# NanoC > Alpha software. Translates to plain C11. ## Syntax card NanoC card (alpha). NanoC -> plain C11. Indent 2 spaces, no braces/semicolons. Unrecognized lines are plain C. FILE +stdio,stdlib (includes, bare name adds .h) | + | + "x.h" FUNC f name | f name(a: int, b: int) int | f get(int x,lo,hi) int ; result type optional (void; main is int); last expression is the result; < v early return; < alone in void LOCAL n := 0 (literal infers int/double/char/str) | x: Type = expr | int a[] = {1,2} | calls and arithmetic need x: Type = expr FLOW if c / elif c / else | while c | for i := 0; i < n; i++ | ! break | ^ continue TYPES s Name a,b: int (struct) | u Name (union) | e Color red,green=5 (enum) ; pointers *T ; arrays/function pointers: exact C PRE $ N = 1 define | $ F(x) = ((x)*2) | $- N | ?+ N ifdef | ?- N ifndef | ?# expr if | :# expr elif | : else | !! "msg" error | dedent = endif MACRO @m twice($x) newline-indented body ((($x)*2)) ; use @twice(3) ; captures $a, $b:block, $c:ident, variadic ... TT @tt then indented program: x := [] ; push(x,v) ; for i range(n) ; str() join() split() replace() len() slice() find() ; emit("C text") ; emit(quote(int a = $n;)) RAW @c { C code } | @c indented | @c "C string" EXAMPLE +stdio s Point x,y: double f dist2(p: Point) double p.x*p.x + p.y*p.y f main Point p = {3, 4} for i := 0; i < 2; i++ printf("%d %g\n", i, dist2(p)) 0 RULES comments are // ; no classes, generics, exceptions or GC; use malloc/free; compile with: nanoc run file.nc ; check: nanoc check file.nc --json ## Builtins (translate time) emit(text|tokens) str(value) tokens(text) ident(text) fresh(hint) len(value) range([start,]end) replace(text,from,to) rename(text,oldIdentifier,newIdentifier) find(text,needle) contains(text,needle) split(text,separator) join(list,separator) slice(value,start[,end]) push(list,value) lex(text) source() rewrite(text) again() pass() get(key,default) put(key,value) eval(program) fail(message) assert(condition[,message]) log(message) read(path) exists(path) env(name) file() path() line() name() cycle() target() tr.ast([file]) module node of the source being translated, or of a file tr.walk(node,fnName) calls fnName(node) per node, parents first; stops when it returns true tr.find(kind[,fnName[,root]]) nodes of a kind ("*" any) accepted by fnName(node) tr.type_of(exprNode|text) resolved C type text, "" when unknown tr.layout(type|recordNode) [size,align,[[field,type,offset,size]]] on LP64 tr.replace(node,new) replaces a node of the current AST (new may be a module: its kids are spliced) tr.insert(parent,index,new) inserts into parent's body; negative index appends tr.remove(node) removes a node from the current AST tr.get(node,field) kind name type text line column endLine endColumn args kids id trail tr.set(node,field,value) returns an updated copy of a node value tr.print(node) NanoC source of a node (one line for expressions) tr.source() NanoC source of the current AST tr.commit() rewrites the source with the current AST and runs another pass tr.reset() drops AST edits and reparses on next use tr.parse(text[,"expr"]) module node (or expression node) from NanoC text tr.node(kind,name,type,text,args,kids) generic node tr.func(name,params,ret,body) params: [name,type] pairs or declaration text; body: nodes or source text tr.param(name,type) parameter node tr.field(name,type) record field node tr.record(name,fields[,"u"]) record (struct or union) node tr.type(name,"e"|"typedef",members) enum or typedef node tr.call(name,args) call node; args: nodes, numbers or expression text tr.var(name,type[,init]) variable node tr.stmt(text) statement node tr.expr(text|number) expression node tr.ret([expr]) return node tr.ident(name) identifier node tr.lit(number|string) literal node tr.if(header,body) if/elif/else node, header such as "if x>1" tr.loop(header,body) loop node, header such as "for i 0..8" tr.cblock(cText) backtick C block node tr.diagnostic(node,message[,"warning"|"error"]) records a diagnostic at a node tr.info([key]) file language pass check version astVersion import nodes tr.translate(nanocText) C text from NanoC text in a nested context tr.import(file.c) NanoC text from C when the importer is available write(path,text) mkdir(path) remove(path) run(argv[,cwd]) [status,out,err] exec(argv[,cwd]) stdout; fails on nonzero sh(command) [status,out,err] ## Examples ### Hello, world + f main() printf("Hello from NanoC! %d %g\n", 6 * 7, 1.5) 0 ### FizzBuzz + ## Classic FizzBuzz: indentation instead of braces, one expression per branch. f label(n: i32) str if n % 15 == 0 < "FizzBuzz" elif n % 3 == 0 < "Fizz" elif n % 5 == 0 < "Buzz" < NULL f main() for i := 1; i <= 15; i++ text: str = label(i) if text puts(text) else printf("%d\n", i) < 0 ### Loops and Collatz + f main() total:=0 for i := 1; i < 11; i++ total += i printf("sum 1..10 = %d\n", total) n:=27 steps:=0 while n != 1 n = n % 2 ? 3 * n + 1 : n / 2 ++steps printf("collatz(27) takes %d steps\n", steps) < 0 ### Compact C + $ SQUARE(x) = ((x)*(x)) ?+ _WIN32 $ PLATFORM = "Windows" :# defined(__linux__) $ PLATFORM = "Linux" : $ PLATFORM = "Other" s Point x,y: i32 f sum(a: *i32, n: i32) i32 total := 0 for i := 0; i < n; i++ if a[i] < 0 ^ elif a[i] == 0 ! total += a[i] < total f main() nums: i32[] = {1,2,-3,4,0,5} result: i32 = sum(nums, 6) ?# defined(TEST) if result > 5 puts(PLATFORM) else < 1 : result = SQUARE(result) while result > 7 result-- < result != 7 ### Structs and enums + e Kind CIRCLE, SQUARE, TRIANGLE s Shape kind: Kind size: f64 f area(s: Shape) f64 k:=0.0 if s.kind == CIRCLE k = 3.14159 elif s.kind == SQUARE k = 1 else k = 0.433 < k * s.size * s.size f main() shapes: Shape[3] = {{CIRCLE, 1}, {SQUARE, 2}, {TRIANGLE, 3}} for i := 0; i < 3; i++ printf("shape %d area %g\n", i, area(shapes[i])) < 0 ### Fixed-width types + + + s Pair a,b:int f gcd(a: u32, b: u32) u32 < b ? gcd(b, a % b) : a f main() p: Pair = {84, 36} small: uint8_t = 200 big: int64_t = INT64_C(1) << 40 printf("gcd %d %d = %u\n", p.a, p.b, gcd(p.a, p.b)) printf("u8 %u i64 %" PRId64 "\n", (unsigned int)small, big) 0 ### Linked list + + ## A singly linked list: structs, pointers and malloc are plain C. s Node value: i32 next: *Node f push(head: *Node, value: i32) *Node n: *Node = malloc(sizeof(Node)) n->value = value n->next = head < n f total(head: *Node) i32 sum := 0 for p: *Node = head; p; p = p->next sum += p->value < sum f release(head: *Node) while head next: *Node = head->next free(head) head = next f main() list: *Node = NULL for i := 1; i <= 5; i++ list = push(list, i * i) for p: *Node = list; p; p = p->next printf("%d ", p->value) printf("\ntotal %d\n", total(list)) release(list) < 0 ### Sorting with callbacks + + ## Function pointers and qsort: an exact C declarator in a parameter, everything else compact. s Person name: str age: i32 f by_age(a: *const `void`, b: *const `void`) i32 x: *const Person = a y: *const Person = b < x->age - y->age f main() team: Person[4] = {{"Ada", 36}, {"Linus", 28}, {"Grace", 45}, {"Dennis", 31}} qsort(team, 4, sizeof(Person), by_age) for i := 0; i < 4; i++ printf("%-7s %d\n", team[i].name, team[i].age) < 0 ### Bitset sieve + + # Sieve of Eratosthenes on a bitset: preprocessor-style macros and fixed-width types. $ BITS = 128 $ GET(a, i) = (((a)[(i) / 32] >> ((i) % 32)) & 1u) $ SET(a, i) = ((a)[(i) / 32] |= 1u << ((i) % 32)) f main() composite: uint32_t[BITS / 32] = {0} for i := 2; i * i < BITS; i++ if !GET(composite, i) for j: int = i * i; j < BITS; j += i SET(composite, j) count := 0 for i := 2; i < BITS; i++ if !GET(composite, i) printf("%d ", i) count++ printf("\n%d primes below %d\n", count, BITS) < 0 ### Token macros + @m max($a, $b) (($a) > ($b) ? ($a) : ($b)) @m clamp($x, $lo, $hi) @max($lo, ($x) < ($hi) ? ($x) : ($hi)) @m getter($name, $field) f $name(s: *Shape) double < s->$field s Shape w,h:double @getter(width, w) @getter(height, h) f main() s: Shape = {3, 4} printf("max %d clamp %d %d\n", @max(3, 9), @clamp(15, 0, 10), @clamp(-4, 0, 10)) printf("w x h = %g %g\n", width(&s), height(&s)) < 0 ### Generic vector + + # One macro stamps out a typed growable vector per element type: generics without a runtime. @m vec($T, $name, $push) s $name data: *`$ T` len,cap: i32 f $push(v: *$name, x: $T) if v->len == v->cap v->cap = v->cap ? v->cap * 2 : 4 v->data = realloc(v->data, v->cap * sizeof($T)) v->data[v->len++] = x @vec(int, IntVec, IntVec_push) @vec(double, DoubleVec, DoubleVec_push) f main() a: IntVec = {0} b: DoubleVec = {0} for i := 0; i < 10; i++ IntVec_push(&a, i * 3) DoubleVec_push(&b, i / 4.0) printf("ints %d, last %d, cap %d\n", a.len, a.data[a.len - 1], a.cap) printf("doubles %d, last %g\n", b.len, b.data[b.len - 1]) free(a.data) free(b.data) < 0 ### Scoped cleanup + # Cleanup is just a macro: open, run the indented body, always close. @m with_file($f, $path, $body:block) for $f: *FILE = fopen($path, "r"); $f; $f = (fclose($f), (FILE *)0) $body f count(path: str) i32 n:=0 @with_file(f, path) while fgetc(f) != EOF ++n < n f main() printf("bytes: %d\n", count("/dev/null")) printf("missing: %d\n", count("/no/such/file")) < 0 ### Compile-time table + # Runs while compiling: the table below is computed by the compiler, not at run time. @tt vals:=[] for i 0..8 vals = push(vals, str(i * i)) emit("static const int squares[] = {" + join(vals, ", ") + "};") f main() for i := 0; i < 8; i++ printf("square %d = %d\n", i, squares[i]) < 0 ### Prime table at translate time + # A lookup table computed by the compiler: @tt sieves the primes below 60 and emits a constant array. @tt found:=[] names:=[] for n 2..60 prime:=true for p found if n % p == 0 prime = false if prime found = push(found, n) names = push(names, str(n)) emit("static const int primes[] = {" + join(names, ", ") + "};") emit("enum { PRIME_COUNT = " + str(len(found)) + " };") f main() printf("%d primes below 60:", PRIME_COUNT) for i := 0; i < PRIME_COUNT; i++ printf(" %d", primes[i]) printf("\n") < 0 ### Enum and name table + # Enum plus matching name table from one list: the macro removes the "update both places" bug. @m colors($m) $m(RED) $m(GREEN) $m(BLUE) @tt names:=["RED","GREEN","BLUE"] items:=[] for n names items = push(items, "\"" + n + "\"") emit("static const char *color_name[] = {" + join(items, ", ") + "};") emit("enum Color { " + join(names, ", ") + ", COLOR_COUNT };") f main() for i := 0; i < COLOR_COUNT; i++ printf("%d = %s\n", i, color_name[i]) < 0 ### C interop + + @c static double hyp(double a, double b) { return sqrt(a * a + b * b); } f main() printf("hypot(3,4) = %g\n", hyp(3, 4)) printf("cbrt(27) = %g\n", cbrt(27)) < 0