# Overview TODO This is an example of a program: ``` def fib(n: u16): u16 { if n < 2 { return n; } else { return fib(n - 1) + fib(n - 2); } } fib(20); # 6765 ``` ## Programs A program is a sequence of: * function declarations * statements * TODO: if-else, for, for-in, etc; with no semicolon Statements are delimited with semicolons (`;`), and are one of: * expression (e.g. `1+2`) * function call * lambda definition * variable * variable declaration * return statement XXX: do we really want expressions without effects? TODO: entry point; `main` to be C compatible? ## Modules A Micro file must start with the module name. ``` # our main module (this is a comment) module main ``` The module name is used by the linker. TODO: "import" and use modules. ## Variables Variable declaration: ``` var a: u8 = 123; ``` Group declaration: ``` var ( a: u8 = 123, b: u16 = 1234 ); ``` Variables must be initialized, there are not default values, with the exception of structures that is optional. Variables can refer to a memory address with `@` operator and the data on that address will be used as initialization: ``` var p: u8 = @0x8000; p; # whatever byte is in address 0x8000 (peek) p = 0; # byte at 0x8000 is now 0 (poke) ``` Global variables are exported by default, unless they are defined as private: ``` private val local: u8 = 123; ``` ### Constants Constant are immutable values and may not have memory allocated to them: ``` # won't allocate memory const K: u8 = 10; # will allocate 10 bytes const V: [10]u8 = 255; ``` Group declaration: ``` const ( A: u8 = 128, B: u16 = 4096 ); ``` Must be resolved at compilation time. ``` var a: u8 = 1; const A: u8 = a + 1; # error: unresolved value ``` ## Built-in types ### Integers | Type | Description | Samples | | --- | --- | --- | | u8 | unsigned 8-bit | `123; 0xce; 0b10000;` | | s8 | signed 8-bit | `-123;` | | u16 | unsigned 16-bit | `65535; 0xffff;` | | s16 | signed 16-bit | `-4096;` | Built-in functions: | Function | Description | Samples | | --- | --- | --- | | hi | Get the MSB on a 16-bit number | `hi(0xaabb); # 0xaa` | | lo | Get the LSB on a 16-bit number | `lo(0xaabb); # 0xbb` | `hi` and `lo` can also be used with references (functions, structures and arrays): ``` def fn() { return; } hi(fn); # MSB of fn address ``` Type conversion is explicit: ``` var a: u8 = 10; var b: u16 = 10; a + b; # error: type mismatch u16(a) + b; # 20: u16 ``` ### Booleans Logic operators result on a boolean type. | Type | Description | Samples | | --- | --- | --- | | bool | boolean | `true; false; 1 == 1;` | ### Functions | Type | Description | Samples | | --- | --- | --- | | ([parameters]) -> [return] | function | `(a: u8) -> u8 { return a + 1; }` | | ([parameters]) | function (no return value) | `() { return; }` | Functions can be declared with `def` when they have a name, or as anonymous using the lambda syntax. ``` def add(a: u8, b: u8): u8 { return a + b; } add(2, 8); # 10 ``` Anonymous function: ``` # increment the value passed as argument (a: u8): u8 { return a + 1; }(10); # 11 ``` Function are higher order functions: ``` def apply(fn: (u8) -> u8, a: u8, b: u8): u8 { return fn(a, b); } apply(add, 2, 10); # 8 # using a lambda apply((a: u8, b: u8): u8 { return a + b; }, 2, 8); # 10 # lambdas can be assigned to variables var double: (u8) -> u8 = (a: u8): u8 { return a + a; }; double(10); # 20 ``` Anonymous functions can only access local variables (closures aren't supported): ``` def closure(a: u8): () -> u8 { return (): u8 { return a; # invalid return, undefined a }; } ``` Functions are exported by default, unless they are defined as private: ``` # not exported (exported is the default) private def dec(a: u8): u8 { return a - 1; } ``` Variables of type function use references: ``` def fn() { return; } # fn and fn2 refer to the same function var fn2: () = fn; ``` ### Special value: nil `nil` is a reference not pointing to a value, used for example on variables with type function or structure. Using a *nil* reference will result in a runtime error. ``` # fn doesn't hold a reference var fn: (u8) -> u8 = nil; fn(10); # runtime error ``` ### Structures Structures can be used to group data and functions. Structure can be declared with `def` and provide values for the grouped data. Those values will be used when allocating an instance. Any variable or function declared in the structure can be accessed like a local variable inside the structure, and in the instances using the dot (`.`) operator. ``` def A { # constants can be part of an structure as well const INC: u8 = 1; var ( n: u8 = 100, m: bool = false, dec: (u8) -> u8 = nil ); # it is possible to define functions local # to a structure def inc(): u8 { n = n + INC; return n; } } # allocate memory for structure A var a: A; a.n; # 100 a.m; # false a.dec; # nil a.inc(); # 101 a.inc(); # 102 a.n; # 102 ``` Variables of type structure handle a reference: ``` var b: A = a; # b points to the same data as a b.inc(); # 103 a.n; # 103 # c doesn't hold a reference to an instance of A var c: A = nil; ``` Recursive structures are not supported and local structures can't be used as return value in a function. ### Arrays Arrays are zero based and are supported for all types. XXX: including arrays? e.g. `[10][10]u8`. Array size is a numeric literal or a constant expression (must be known at compilation type), and all the elements on an array must be of the same type. Arrays are initialised to literals with `[` and `]` providing a list of values. ``` # array of 5 u8 var arr: [5]u8 = [0, 0, 0, 0, 0]; ``` It is possible so initialize the array providing one single value that will be used for all the elements: ``` # this is equivalent to the previous example using [ and ] var arr: [5]u8 = 0; ``` The array size is optional when initializing using literals: ``` var xs: []bool = [true, true, false]; len(xs); # 3 var xs2: []bool = true; # error: missing array size ``` Array elements can be accessed using `[]`: ``` var arr: [5]u8 = 0; arr[0]; # 0 arr[0] = 100; arr[0]; # 100 ``` Variables of type array handle a reference. ``` # arr and arr2 refer to the same array var arr2: [5]u8 = arr; ``` Local arrays can't be used as return value in a function. ``` def fn(): [5]u8 { var local [5]u8 = 0; return local; # error: returning a local value of type array } ``` Built-in functions: | Function | Description | Samples | | --- | --- | --- | | len | get the length of an array as u16 | `len(arr); # 5` | | incbin | load a binary file as []u8 | `const f: []u8 = incbin("file.bin");` | ### Strings TODO: a zero ended array of u8 with special initializers ## Operators Micro uses the same operators and almost the same operator precedence as C, from more to less priority: ``` ! - ~ / * % & - + << >> | ^ > >= < <= || && != == = ``` Arithmetic operators only apply to integers. Comparison operators only apply to integers, with the exception of `==` and `!=` that also apply to other types (if both operands are of the same type). Logic operators apply to booleans and they are lazy operators (e.g. in `a && b`, `b` won't be evaluated if `a` is false). ## Flow control TODO * if-else * loops (while-like, infinite, break, continue) * for-in ## External functions TODO * is this "import"? * calling conventions? * namespace support? ## In-line ASM TODO