The C-UP Programming Language

Version 0.9, September 2018

Introduction

C-UP is a statically typed procedural programming language in the C style with these major features:

The language has the following design goals:

C-UP is intended to be used as an application programming language, meaning that the bulk of your application should be written using it. It specifically isn’t a systems programming language as it shuns things like threads, inline assembly, direct access to specific areas of memory and processor intrinsics, etc. In fact, you are actively encouraged to keep writing such code in C because C-UP makes it almost trivial to interface with C. Conversely, it also isn’t really designed as a scripting language as it’s big and complicated and presumes its runtime has control over threads, memory, file handles, etc.

Here is “Hello, world” in C-UP:

import System.IO.Console;

public int Main(string[] local args)
{
    Console.WriteLine("Hello, world");
    return 0;
}

Getting Started

In order to compile and run c-up programs you only need these 3 files:

Cuprt.exe contains the compiler internals and the runtime systems (garbage collector, parallel job runtime, debugger hooks, etc.) In the future a separate version without the compiler will be provided so you don’t have to pay the memory cost for it when it’s not required.

System.cuo is the core c-up system libraries which incorporates things like file IO that basically every program requires. Separate libraries exist for other functionality (e.g. graphics and audio). The .cuo format is a proprietary binary file format.

When you launch cuprt.exe you must pass the name of a c-up executable (.cue) file as the first argument. The .cue extension can optionally be omitted. Any subsequent arguments are passed as arguments to the main entry point of the c-up executable. Arguments are strings separated by whitespace. If you need an argument to contain whitespace characters wrap the entire argument in quotes.

The cue file is a proprietary binary format. The code is stored in an intermediate language which is converted to native executable code at load time. JIT compilation isn’t a binding philosophical decision in c-up, it’s just how things are currently implemented because it’s easiest.

Compiling

Although the main bulk of the compiler is built into cuprt.exe, the command line interface to it is implemented as a c-up program called “compile.cue”. Therefore to launch the c-up compiler from the command line type “cuprt compile” on the command line followed by any number of compiler arguments.

Arguments to the compiler take three forms:

Options all start with either the - or / character. Details of all available options can be found below.

Response files start with the @ character, followed by a file name which must be relative to the root directory (see below). A response file is just a text file that contains any number of other compiler arguments separated by whitespace.

Any argument that does not start with one of the above characters is assumed to be the name of a file to be passed to the compiler. If that file has the .cup extension then it’s treated as a c-up source file and is parsed and compiled as such. If it has any other extension then it’s a binary resource which is embedded in the output file without being changed in any way.

All source file names passed to the compiler are specified relative to the root directory. This root directory is the current working directory unless you override it using the –root option (see below)

Here’s a list of compiler options:

-root:folder

Specifies the root folder for source code and resource files. If not specified the current working directory is used. The folder given can be either absolute or relative to the current working directory.

-target:lib

Specifies that the output is a library as opposed to an executable. When a library is saved only the modules corresponding to input source files are written - imported libraries are not written. It’s an error for a main entry point to be found in the source code when this option is found.

-target:exe

Specifies that the output is an executable. When an executable is saved the entire symbol table is written including all libraries that have been imported. This means that c-up executables are entirely self-contained, requiring no additional libraries to be dynamically loaded. A single entry point must be found in the source code when this option is specified.

-out:filename

Specifies the output file name (a .cuo or .cue file.) If this is not specified then all compilation is performed but no output is written. The file name is relative to the current working directory or absolute – it is not relative to the root folder.

-lib:filename

Import a library (.cuo file) into the program being compiled. The file name is relative to the current working directory or absolute – it is not relative to the root folder. You can import the required system.cuo library from any location, but if you don’t specify it then it’s automatically imported from the same location as the compile command.

-define:symbol -define:symbol=value

Define a program wide pre-processor symbol and give it an optional value. Pre-processor symbols are always signed 64-bit integers – if you don’t specify a value then it’s given the value 1.

-noboundscheck -noaligncheck -noheapcheck

Disable the specified runtime checks all of which are enabled by default. Can be used to improve performance of final shipping builds.

-aliascheck

Enable reference aliasing runtime checks. Unlike the tests above this is not enabled by default as it can be quite slow. Should be enabled for fully checked builds or automated tests.

-inline

Enable function inlining. You have no other control over inlining, it’s entirely at the compiler’s discretion.

-parallel

Enable parallel execution of the resulting code.

-optimise

Enable optimisations.

-debug

Controls whether debugging information is embedded in the resulting file. Currently this just controls whether c-up source code is embedded in the resulting .cuo or .cue file.

-system

Passed in when recompiling the system library.

-allowshared

Allow the use of the shared memory feature. See the section on parallelism for more details.

-run

Run the resulting executable immediately if compilation succeeds. This allows you to compile and run in one command. It’s not necessary to write the executable to disk (although you can) as it’s executed directly from memory.

Example

To compile the c-up file c:.cup as an executable, writing out c:.cue:

cuprt compile main.cup –out:test.cue

To execute the resulting exe:

cuprt test.cue

## Debugging

C-UP comes with a debugger, written in C-UP. It’s called debug.cue and it takes 2 command line arguments: the first is the name of the program to debug and the second is an optional root directory for the file system with the current directory being used if it’s omitted.

You invoke the debugger just like the compiler:

cuprt debug programToDebug.cue

As you can see there’s no menu bar so you can’t open and close projects. This means you’re restricted to debugging the same exe (.cue) for an entire session but that’s typically how it’s done anyway.

All the files in the project are shown in their folder structure on the right, which includes all the system libraries (under ‘Runtime’). Double click a .cup file to open it in the main area. All source code is embedded in the .cue file so there’s no risk of seeing out of date source code or any awkward path mappings to set up. The system libraries have source code embedded too so you can browse them freely. If you don’t want source code embedded in your project then don’t pass the –debug option to the compiler.

The re-load button is what you need to click after you recompile your project in order to re-load it into the debugger. All open source windows will refresh automatically. You can only reload when the program is stopped.

Keyboard shortcuts for common debugging operations are:

Clicking in the margin at the left of the source code also toggles breakpoints. You can navigate to breakpoints by double clicking them in the Breakpoints window.

Ctrl+G lets you goto a line number in the current document.

Ctrl+F lets you find a string in the current document. F3 finds the next occurance, Shift+F3 the previous.

Extracting source

Source code is embedded in c-up libraries and executables (if they were compiled with the –debug option.) This is advantageous because there’s no need to distribute multiple files and no possibility of source files being out of sync with the executable code – when you see the source you know for certain it’s exactly what was used to generate that exe.

But it is convenient to be able to extract the source code to separate files for browsing and that’s what the extract command does. To invoke extract use:

cuprt extract library-or-exe-name root-destination-folder