# A tour of Micro Micro is a small statically typed toy programming language. ## Syntax Micro statements are: * Variable declarations and expressions, delimited by semicolons. * Function and structure definitions. ### Comments Only line comments, starting with `//` and ending at the end of the line. ```micro // This is a comment ``` ### Reserved words These are the reserved words in micro: > var const def func bool true false return if else for in break continue See also the built-in functions. ### Identifiers Identifiers can be any string starting with a letter (upper or lower case) or underscore (`_`), followed by the same type of character or a numeric value. An identifier can't be a reserved word. ```micro var example number; var _example number; var example2 number; ``` ### Blocks and scope Micro has series of statements in blocks delimited with `{` and `}`, and a block defines a lexical scope. A variable declaration can shadow another from a parent scope. ```micro var a number = 1; println(a); { // block, new scope var a string = "one"; println(a); } println(a); // output: // 1 // one // 1 ``` ### Operators Micro uses the same operators and almost the same operator precedence as C, from more to less priority: ```micro ? ! - ~ / * % & - + << >> | ^ > >= < <= || && != == = ``` 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 to the following: * An empty string is false, true otherwise. * A numeric value of 0 is false, true otherwise. * A function reference is always true. The result of these operators is a boolean and they are lazy operators (e.g. in `a && b`, `b` won't be evaluated if `a` is false). ## Values | Type | Keyword | Sample | | --- | --- | --- | | Boolean | `bool` | `true; false; ` | | Number | `number` (int64) | `123;` | | String | `string` | `"example";` | There is also a "none" type referred when a function doesn't return a value. It is not a available to use but it will appear in error reporting. For example, the interpreter running this code: ```micro def fn() { } var a number = fn(); // error: type mismatch expected number found none ``` See functions for more information about the "func" type. Numbers support the following formats: * 0 decimal * 0x00 hexadecimal * 0b00 binary * 'x' for "ASCII character" ## Variables Variables are declared with `var`. If they are not initialized, a default value is assigned; with the exception of functions, that can't be used until they have a value (or there will be a runtime error). ```micro var a number = 123; var b number; // initialises default to 0 var c string; // empty string var d bool; // false // e was not declared: error e = 123; var fn func (); fn(); // error: value is not callable ``` ### Constants Constants are immutable values. Constants can be literals or expressions that get evaluated at parsing time. ```micro const K number = 10; var a number = K; K = 0; // error: value cannot be assigned to constant K ``` ## Conditionals and flow control ### If-else ```micro var a number; if a > 0 { println("it is true"); } else { println("it is false"); } // output: // it is false ``` ### For and for-in The block will loop while the expression evaluates to true. ```micro var i number = 4; for i > 0 { println(i); i = i - 1; } // output: // 4 // 3 // 2 // 1 ``` Loop flow can be altered with: - `continue` to jump to next iteration. - `break` to exit the loop. The expression is optional (infinite loop). ```micro var i number = 4; for { println(i); i = i - 1; if i == 0 { break; } } ``` `for ... in` can be used to Iterate over arrays. ```micro var arr [3]string = ["one", "two", "three"]; for i in arr { // i is of type string println(i); } // output: // one // two // three ``` ## Functions Functions can be defined using `def` and they are assigned a "func" type describing parameters and return type. Recursion is supported and recursive tail calls will be optimized. ```micro // recursive factorial // (the tail call will be optimized) def fact(n number, acc number) number { if n == 1 { return acc; } else { return fact(n - 1, acc * n); } } // type of fact is: func (number, number) number println(fact(20, 1)); // output: // 2432902008176640000 def fn(a number, b number) { // implicit return with no value // it is an error if a value is expected } ``` Functions are higher-order. ```micro // b is a function that takes one number and returns a number def fn(a number, b func (number) number) number { return b(a); } ``` Closures are supported. ```micro def makeCounter() func (number) number { // defaults to 0 var c number; def count() number { c = c + 1; return c; } return count; } var c func = makeCounter(); println(c()); // 1 println(c()); // 2 println(c()); // 3 ``` Arrays and structures are always passed as reference. ```micro var arr [5]number; def fn(a [5]number) { a[0] = 10; } fn(arr); arr[0]; // 10 ``` Local arrays and "struct" can't be returned from a function. ```micro def fn() [5]number { var local [5]number; return local; } // error: returning a local value of type array [5]number ``` But references to variables in scope are allowed. ```micro var g [5]number; def fn() [5]number { // local is a reference to a non-local variable var local [5]number = g; return local; } // error: returning a local value of type array [5]number ``` ### Returning error For cases when the return value can't be used to inform of an error, a function can tag a return value as "error" using `!?`. That tag can be tested with the `?` unary operator. Even if a return value is tagged, it must be a valid return value for the function definition. ```micro def safeDiv(a number, b number) number { // a / 0 would result on a runtime error! if b == 0 { return !? 0; } return a / b; } var result number; // checks if the error tag is set while keeping the return value if ? (result = safeDiv(1, 0)) { println("safeDiv returned error"); } // it can also be used with functions returning no values def fn() { return !?; } if ? fn() { println("fn always fails!"); } ``` `?` (test error) operator resets the error flag before evaluating the right expression. ```micro // our previous "always fails" function fn(); // error is not set if ? true { println("Not run"); } ``` ## Arrays Arrays are zero based and are supported for all types. Array size is a numeric literal or a constant expression (must be known at compilation type). Arrays can be initialised to literals with: * `[` and `]` providing a list of values. * A literal string using double quotes for arrays of unit8; the array size must be at least the length of the string plus 1 (a zero terminating the string). ```micro // array of 5 numbers; the size is only needed // when no initializing values are provided var arr [5]number; // initialized to specific values var arr2 [5]bool = [true, false, true, false, true]; arr[0]; // 0 arr2[0]; // true // iterate over members for i in arr { println(i); } len(arr); // 5 var i number; // arrays indexes are zero based for i < len(arr) { println(arr[i]); i = i + 1; } ``` Arrays can be constants. ```micro const arr [10]number; arr[0] = 10; // error: can't modify a constant variable // but can change references const ref [10]number = arr; const arr2 [10]number; ref = arr2; ``` Arrays are passed by reference to functions. ## Structures Structures allow grouping data and functions. In a structure definition only variable declarations, and definitions of functions and other structures are allowed. ```micro // declare struct A def A { var n number; def fn() number { // n is in scope n = n + 1; return n; } } // memory is allocated for obj var obj A; // obj2 is a reference to obj var obj2 A = obj; prinln(obj2.n); // output: 0 println(obj.fn()); // output: 1 prinln(obj2.n); // output: 1 def B { var n bool; } var b B = obj; // error: type mismatch: expected struct B found struct A // recursive structures are not supported (micro doesn't have pointers) def B { var next B; // error: recursive definition of struct B } def C { println("error"); // error: expected definition in a struct block } ``` ## Interpreter built-in functions These are available on the interpreter. | Function | Description | Example | | --- | --- | --- | | `len` | returns the size in elements of a variable of type array | `len(arr);` | | `panic` | ends execution with a message and a runtime error | `panic("message");` | | `println` | writes to standard output a variable list of expressions, followed by an end of line; returns number of bytes written | `println("value: ", a);` | ## To do See [TODO](../TODO.md) for features that are planned but not yet implemented.