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.
// 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.
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.
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:
? ! - ~
/ * %
&
- +
<< >> | ^
> >= < <=
|| &&
!= ==
=
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:
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).
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.
const K number = 10;
var a number = K;
K = 0;
// error: value cannot be assigned to constant K
Conditionals and flow control
If-else
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.
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).
var i number = 4;
for {
println(i);
i = i - 1;
if i == 0 {
break;
}
}
for ... in
can be used to Iterate over arrays.
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.
// 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.
// 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.
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.
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.
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.
var g [5]number;
def fn() [5]number {
// local is a reference to a non-local variable
var local [5]number = g;
return local;
}
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.
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.
// 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).
// 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.
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.
// 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 for features that are planned but not yet implemented.