From 8bb321f8b032dfaeffbe3d1b8dfeb215c12d3642 Mon Sep 17 00:00:00 2001 From: "Juan J. Martinez" Date: Mon, 18 Jul 2022 07:45:58 +0100 Subject: First public release --- docs/tour.md | 488 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 docs/tour.md (limited to 'docs') diff --git a/docs/tour.md b/docs/tour.md new file mode 100644 index 0000000..27c7e14 --- /dev/null +++ b/docs/tour.md @@ -0,0 +1,488 @@ +# 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 later (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. + +### 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. + +Number supports 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 compilation 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 +} +``` + +## Directives + +TODO + +Include a file in current source and it will interpreted / compiled. + +```micro +include "file.micro" +``` + +Include a binary file as an expression list to initialize an array. + +```micro +var arr [64]number = incbin "file.micro"; +``` + +## 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);` | +| `str` | used with arrays of uint8 to convert them as a printable string | `println(str(arr));` | + + +## To do + +See [TODO](todo.md) for features that are planned but not yet implemented. + -- cgit v1.2.3