c0
c0 compiles to freestanding binaries, code that assumes no runtime and no standard library beneath it,which is the only way a language can be used to write a kernel because the thing it is meant to write is a kernel. the feature set grows only when moonshot actually needs it. that is why c0 was pushed past the point of merely compiling itself, and on toward being a real, capable language.
one type
c0 has a single value type, int, a 64-bit signed
integer.there is no unsigned type and no array type. a consequence worth knowing is that division is signed and truncating, so packed bit patterns are kept below 32 bits where the sign bit cannot interfere.
numbers, characters, truth values and addresses are all an int.
memory is handled through an address value, and the builtins below read and write through it.
the shape of a program
the syntax is a small, familiar subset of C. functions (up to sixteen parameters, the
first six in registers, the rest on the stack, with
recursion), global and local variables, if/else,
while, for, the usual arithmetic and comparison operators, character
literals with escapes, string literals, and // comments. a program
starts at main. source can be split across files with the
include directive, which coff resolves
by plain textual substitution.
include "file.c0";
// this is a comment.
int main() {
int i = 0;
int sum = 0;
while (i < 5) {
sum = sum + i;
i = i + 1;
}
return sum;
}
naming memory instead of counting it
c0 has no structs, but it has layout. a
layout declaration does not introduce a new value type. it
just gives names to a run of 8-byte offsets, so code that would
otherwise be store64(e + 16, v) can read e.vx = v
instead. everything is still an int and a layout
is a naming convention over the load/store builtins, not a second type
system next to them.
layout Entity {
int x;
int y;
int vx;
int vy;
}
int main() {
int e;
e = alloc(sizeof(Entity));
e.x = 10;
e.vx = 1;
return e.x + e.vx;
}
sizeof(Name) resolves at compile time to that layout's
field count times 8. the result is a plain integer, usable anywhere one is, most
often right inside alloc(...).
field names live in one flat table shared by every layout
in the program, not one scoped per layout. c0 has no type system, so
e.x cannot be checked against "the layout e
was allocated as." the first layout to declare a field name fixes its
offset and any other layout reusing that name must declare it at the same
position, otherwise it is a compile error. two layouts can each have their own
x and y as long as both agree x
comes first. this is the same "trust the programmer, one flat namespace"
spirit as c0's single function/global namespace.
builtins instead of a standard library
there is no standard library. a small set of builtins covers what the language
itself cannot express. alloc hands out zeroed memory, and
load8/store8/load64/store64
read and write it a byte or eight bytes at a time. print and
write produce output, open and read take
files in, and exit ends the program. hosted programs (like coff
itself) get argc/argv. the kernel uses none of the hosted
builtins and talks to hardware through the load/store pair instead.
the set is not frozen, a builtin is added when moonshot needs one the language cannot otherwise reach, the same rule the language itself grows by. a general standard library, though, is not a goal, for now, the kernel is meant to build what it needs out of the primitives above.
the whole language
this is the entire vocabulary, c0 is small enough that it fits in a list.
- types
intonly, a 64-bit signed integer, which also stands in for characters, truth values and addresses.- keywords
int,extern,if,else,while,for,return,break,continue,layout,sizeof.- directives
include "file.c0";splices another source file in as text, resolved by coff before compilation.- structure
- functions of up to sixteen parameters (recursion allowed), global and local variables,
layoutdeclarations (named offsets, not a real aggregate type, see above), andmainas the entry point. - operators
- the arithmetic
+ - * /, the comparisons== != < <= > >=, and=for assignment. - literals
- integers, character literals with escapes, and string literals.
- comments
//to the end of the line.- builtins
alloc,load8,store8,load64,store64,print,write,open,read,exit, and (hosted only)argc,argv, described just above.
things are missing on purpose
a feature is added when moonshot produces a concrete need for it, and not before, because every feature the language gains is also code the compiler must carry, correctly, forever. c0 has to stay small enough that its compiler can be held in your head and re-verified against the bootstrap oracle.