1
Front Matter
1.1.1
The Micron Programming Language Specification
1.2
Version
1.2.1
2025-12-04, work in progress
1.3
Author
1.3.1
me@rochus-keller.ch
1.4
License
1.4.1
This file may be used under the terms of the GNU General Public License (GPL) versions 2.0 or 3.0 as published by the Free Software Foundation and appearing in the file LICENSE.GPL included in the packaging of this file. Please review the following information to ensure GNU General Public Licensing requirements will be met: http://www.fsf.org/licensing/licenses/info/GPLv2.html and http://www.gnu.org/copyleft/gpl.html.
1.5
Additional Credits
1.5.1
A lot of text is derived from the Oberon+ language specification
[OBX], which again is derived from the Oberon-2 language specification
[Mo91].
1.6
Table of Contkents
2
Language Specification
2.1
Introduction
2.1.1
Micron is a systems programming language with a syntax similar to Oberon+ and the flexibility of C. It is designed to be capable enough to represent the TBD (Lua, etc.) system, and thus to be an adequate alternative to C.
2.1.2
The name "Micron" is an abbreviation of "MicroOberon".
2.1.3
The most important features of Micron are block structure, modularity, separate compilation, static typing with strong type checking, and generic programming. Micron is a value-oriented language in the tradition of C-like systems languages, rather than reference-oriented language.
2.1.4
Like Oberon+, the language allows several simplifications compared to previous Oberon versions: reserved words can be written in lower case, all semicolons and commas are optional, and for some reserved words there are shorter variants; a declaration sequence can contain more than one CONST, TYPE and VAR section in arbitrary order, interleaved with procedures.
2.1.5
INVAR and INLINE procedures, together with generic modules offer most of the capabilities of C preprocessor macros: running calculations at compile time, and substituting procedure calls by inline code, or creating code depending on compile time parametrization.
2.1.6
The language is designed to work with a single-pass compiler. ABI compatibility with C on the target platform is assumed.
2.1.7
Main differences to Oberon+
2.1.7.1
In contrast to Oberon+, Micron has no garbage collector in the default language level. Data allocated with NEW has to be explicitly deleted with DISPOSE.
2.1.7.2
Record embedding (similar to Go) using the INLINE keyword is supported.
2.1.7.3
Type extension (inheritance) is optional and only available for object types (not record types).
2.1.7.4
Type-bound procedures are supported for record and object types, but only the latter support dynamic dispatch.
2.1.7.5
A pointer can also point to basic types, enums and to objects on the stack, or to record fields or array elements.
2.1.7.6
The address of an designated value can be taken with the @ operator and assigned to a pointer.
2.1.7.7
INC and DEC can also be applied to pointers. The difference of two pointers can be calculated using ORD().
2.1.7.8
There are no VAR parameters; instead a pointer to the variable has to be passed as a parameter.
2.1.7.9
No DEFINITION modules; separate compilation fully depends on compiler-specific symbol files.
2.1.7.11
Predeclared signed and unsigned types, INT8, UINT8, etc.; SIGNED() and UNSIGNED() cast built-in procedures.
2.1.7.12
Integer literals are unsigned, and there are different suffix types.
2.1.7.13
Only one-dimensional arrays (but array x of array y of T is still possible).
2.1.7.14
There is a goto statement (mostly to easy porting existing C code).
2.1.7.15
String concatenation only allowed for string and character literals.
2.1.7.16
The WHILE statement has no ELSIF part in Micron.
2.1.7.17
Non-local access is not supported. Nested procedures only have access to the objects declared on module level, but not to declarations in the outer (nesting) procedures.
2.1.7.18
The initial values of module and local variables are undefined in Micron.
2.1.7.19
AND and NOT are supported in addition to & and ~.
2.2.1
An extended Backus-Naur Formalism (EBNF) is used to describe the syntax of Micron:
2.2.1.1
Alternatives are separated by
|.
2.2.1.2
Brackets
[ and
] denote optionality of the enclosed expression.
2.2.1.3
Braces
{ and
} denote its repetition (possibly 0 times).
2.2.1.4
Syntactic entities (non-terminal symbols) are denoted by English words expressing their intuitive meaning.
2.2.1.5
Symbols of the language vocabulary (terminal symbols) are denoted by strings formatted in bold face.
2.3
Vocabulary and Representation
2.3.1
Micron source code is a string of characters encoded using the UTF-8 variable-width encoding as defined in ISO/IEC 10646.
2.3.2
Identifiers, numbers, operators, and delimiters are represented using the ASCII character set; strings and comments can use characters from the Latin-1 (as defined in ISO/IEC 8859-1) character set (UTF-8 encoded in the source code).
2.3.3
The following lexical rules apply: blanks and line breaks must not occur within symbols (except in block comments, and blanks in strings); they are ignored unless they are essential to separate two consecutive symbols. Capital and lower-case letters are considered as distinct.
2.3.4
Identifiers
2.3.4.1
Identifiers are sequences of letters, digits and underscore. The first character must be a letter or an underscore.
2.3.4.2.1
ident = ( letter | '_' ) { letter | digit | '_' }
letter = 'A' ... 'Z' | 'a' ... 'z'
digit = '0' ... '9'
2.3.5.1
Number literals are integer or real constants.
2.3.5.2
The type of an integer constant is the minimal unsigned integer type to which the constant value belongs (see
2.6.4 Basic types).
2.3.5.3
If the literal is specified with the suffix 'H', the representation is hexadecimal, if it is specified with suffix 'O', the representation is ocal, or if it is specified with suffic 'B', the representation is binary, otherwise the representation is decimal.
2.3.5.4
The type of an integer constant can be explicitly set with a suffix. The U1, U2, U4 or U8 suffices correspond to the UINT8, UINT16, UINT32 or UINT64 unsigned integer types. The I1, I2, I4 or I8 suffices correspond to the INT8, INT16, INT32 or INT64 signed integer types. If the given constant value cannot be represented by the explicit type, the compiler reports an error. Alternatively, the suffix U or I can be used without explicit byte width, in which case the compiler assigns the minimal unsigned or signed integer type to which the value belongs.
2.3.5.5NOTE
Number literals can be prefixed with a sign. But the sign is not part of the literal, but of a simple expression including the literal, see
2.8.3 Operators. Use the SIGNED() predeclared function for a signed integer reinterpretation of the unsigned integer (e.g. SIGNED(0FFH) is an INT8 with value -1). Otherwise the term -0FFH becomes an INT16 with value -255.
2.3.5.6
Signed integer types are represented in two’s complement form.
2.3.5.7
A real number always contains a decimal point and at least one digit before the point. Optionally it may also contain a decimal scale factor. The letter 'E' means
times ten to the power of.
2.3.5.9
A real number is of type
LONGREAL, if it has a scale factor containing the letter 'D', or of type
REAL, if it has a scale factor containing the letter 'F'. If the scale factor contains the letter 'D', the type is
LONGREAL if the mantissa or exponent are too large to be represented by
REAL.
2.3.5.10
Numbers can be interspersed with underscores for better readability, but the first character is always a digit.
2.3.5.11.1
number = integer | real
integer = (digit {digit|'_'} ['O'|'B'] | digit {hexDigit|'_'} 'H')
['U'|'U8'|'U16'|'U32'|'U64'|'I'|'I8'|'I16'|'I32'|'I64']
real = digit {digit|'_'} '.' {digit|'_'} [Exponent]
Exponent = ('E' | 'D' | 'F' ) ['+' | '-'] digit {digit}
hexDigit = digit | 'A' ... 'F'
digit = '0' ... '9'
2.3.5.12
Even though only the upper-case version is shown here, also the lower-case versions of the suffices and scale factor letters are supported.
2.3.5.13.1
1234
0dh 0DH
12.3
4.567e8 4.567E8
0.57712566d-6 0.57712566D-6
2.3.6
Character literals
2.3.6.1
Character literals are denoted by the ordinal number of the character in hexadecimal notation followed by the letter
X (or
x).
2.3.6.2.1
character = digit {hexDigit} ('X' | 'x')
2.3.6.3
A character is encoded as a 8-bit code value using the ISO/IEC 8859-1 Latin-1 encoding scheme.
2.3.7
String literals
2.3.7.1
String literals are sequences of printable characters enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. A string must not extend over the end of a line. The number of characters in a string is called its length.
2.3.7.2
A string of length 1 can be used wherever a character literal is allowed and vice versa.
2.3.7.3.1
string = ''' {character} ''' | '"' {character} '"'
2.3.8
Operators and Delimiters
2.3.8.1
Operators and delimiters are the special characters, or character pairs listed below.
2.3.8.2
|
- |
, |
; |
: |
:= |
. |
.. |
( |
) |
[ |
] |
{ |
} |
* |
/ |
# |
^ |
+ |
<= |
= |
>= |
| |
~ |
|
2.3.9
Reserved Words
2.3.9.1
The reserved words consist of either all capital or all lower case letters and cannot be used as identifiers. All words listed below are reserved (only capital letter versions shown).
2.3.9.2
|
AND |
ARRAY |
BEGIN |
BITS |
BY |
CASE |
CONST |
DIV |
DO |
ELSE |
ELSIF |
END |
EXIT |
EXTERN |
FALSE |
FINALLY |
FOR |
IF |
IMPORT |
IN |
INLINE |
INTERFACE |
INVAR |
IS |
LOOP |
MOD |
MODULE |
NIL |
NOT |
OBJECT |
OF |
OR |
POINTER |
PROC |
PROCEDURE |
RECORD |
REPEAT |
RETURN |
THEN |
TO |
TRUE |
TYPE |
UNTIL |
VAR |
WHILE |
2.3.10.1
Comments are arbitrary character sequences opened by the bracket
(* and closed by
*). Comments may be nested. They do not affect the meaning of a program. Micron also supports line comments; text starting with
// up to a line break is considered a comment.
2.4
Declarations and scope rules
2.4.1
Every identifier occurring in a program must be introduced by a declaration, unless it is a predeclared identifier. Declarations also specify certain permanent properties of an object, such as whether it is a constant, a type, a variable, or a procedure. The identifier is then used to refer to the associated object.
2.4.2
The
scope of an object
x extends textually from the point of its declaration to the end of the block (module, procedure, record, or object) to which the declaration belongs and hence to which the object is
local. It excludes the scopes of equally named objects which are declared in nested blocks. The scope rules are:
2.4.2.1
No identifier may denote more than one object within a given scope (i.e. no identifier may be declared twice in a block);
2.4.2.2
An object may only be referenced within its scope;
2.4.2.3
A type
T of the form POINTER TO
T1 (see
2.6.8 Pointer types) can be declared at a point where
T1 is still unknown. The declaration of
T1 must follow before the base type of T is accessed for the first time;
2.4.3
An identifier declared in a module block may be followed by an export mark (" * " or " - ") in its declaration to indicate that it is exported. An identifier
x exported by a module
M may be used in other modules, if they import
M (see
2.12 Modules). The identifier is then denoted as
M.x in these modules and is called a
qualified identifier. Identifiers marked with " - " in their declaration are
read-only in importing modules.
2.4.4.1
qualident = [ident '.'] ident
identdef = ident ['*' | '-']
2.4.5
The following identifiers are predeclared; their meaning is defined in the indicated sections; either all capital or all lower case identifiers are supported (only capital versions shown).
2.4.6
|
ABS |
ANY |
ASSERT |
BITAND |
BITASR |
BITNOT |
BITOR |
BITS |
BITSHL |
BITSHR |
BITXOR |
BOOLEAN |
BYTE |
CAP |
CAST |
CHAR |
CHR |
DEC |
DEFAULT |
DISPOSE |
EXCL |
FLT |
FLT32 |
FLT64 |
FLOOR |
HALT |
INC |
INCL |
INT16 |
INT32 |
INT64 |
INT8 |
INTEGER |
LEN |
LONG |
LONGINT |
LONGREAL |
MAX |
MIN |
NEW |
ODD |
ORD |
PCALL |
PRINT |
PRINTLN |
RAISE |
REAL |
SET |
SHORT |
SHORTINT |
SIGNED |
SIZE |
STRLEN |
UINT16 |
UINT32 |
UINT64 |
UINT8 |
UNSIGNED |
|
|
2.5
Constant declarations
2.5.1
A constant declaration associates an identifier with a constant value.
2.5.2.1
ConstDeclaration = identdef '=' ConstExpression
ConstExpression = expression
2.5.3
A constant expression is an expression that can be evaluated by a mere textual scan without actually executing the program. Its operands are constants (see
2.8.2 Operands) or predeclared functions (see
6 Predeclared Procedure Reference) that can be evaluated at compile time. Examples of constant declarations are:
2.5.4.1
N = 100
limit = 2*N - 1
fullSet = {min(set) .. max(set)}
2.5.5NOTE
For compile time calculations of values the same rules as for runtime calculation apply. The ConstExpression of ConstDeclaration behaves as if each use of the constant identifier was replaced by the ConstExpression. An expression like
MAX(INT32)+1 thus causes an overflow of the INT32 range. To avoid this either
LONG(MAX(INT32))+1 or
MAX(INT32)+1I8 has to be used.
2.6
Type declarations
2.6.1
A data type determines the set of values which variables of that type may assume, and the operators that are applicable. A type declaration associates an identifier with a type. In the case of structured types (arrays, records, and objects) it also defines the structure of variables of this type. A structured type cannot contain itself.
2.6.2.1
TypeDeclaration = identdef '=' type
type = NamedType | ArrayType | RecordType | PointerType | ProcedureType
| enumeration | ObjectType | InterfaceType
NamedType = qualident
2.6.3.1
Table = array N of real
Tree = pointer to Node
Node = record
key: integer
left, right: Tree
end
CenterTree = pointer to CenterNode
CenterNode = record (Node)
width: integer
subnode: Tree
end
Function = procedure(x: integer): integer
2.6.4
Basic types
2.6.4.1
The basic types are denoted by predeclared identifiers. The associated operators are defined in
2.8.3 Operators and the predeclared function procedures in
6 Predeclared Procedure Reference. All basic types support alternative identifiers. Either all capital or all lower case identifiers are supported (only capital versions shown).
2.6.4.2
The values of the basic types are the following:
2.6.4.3
|
BOOL, BOOLEAN |
1 byte |
the truth values true and false |
CHAR |
1 byte |
the characters of the Latin-1 set (0x .. 0ffx) |
UINT8, U8, BYTE |
1 byte |
the integers between 0 and 255 |
INT8, I8 |
1 byte |
the integers between -128 and 127 |
INT16, I16, SHORTINT |
2 byte |
the integers between -32'768 and 32'767 |
UINT16, U16 |
2 byte |
the integers between 0 and 65'535 |
INT32, I32, INTEGER |
4 byte |
the integers between -2'147'483'648 and 2'147'483'647 |
UINT32, U32 |
4 byte |
the integers between 0 and 4'294'967'295 |
INT64, I64, LONGINT |
8 byte |
the integers between -9'223'372'036'854'775'808 and 9'223'372'036'854'775'807 |
UINT64, U64 |
8 byte |
the integers between 0 and 18'446'744'073'709'551'615 |
FLT32, F32, REAL |
32 bit |
an IEEE 754 floating point number |
FLT64, F64, LONGREAL |
64 bit |
an IEEE 754 floating point number |
SET |
4 byte |
the sets of integers between 0 and 31 |
2.6.4.4
INT64, INT32, INT16, INT8, are signed integer types, UIN64, UINT32, UINT16, and UINT8, are unsigned integer types, FLT32 and FLT64 are
Floating-point types, and together they are called numeric types. The larger type includes (the values of) the smaller type according to the following relations:
2.6.4.5
INT64 >= INT32 >= INT16 >= INT8
UINT64 >= UINT32 >= UINT16 >= UINT8
FLT64 >= FLT32
2.6.4.6NOTE
Oberon and Oberon+ both support implicit type casts from integer to floating point and vice versa. Micron requres an explicit cast. To convert an integer to a floating point type, the FLT() built-in function must be used. To convert a numeric type to a signed integer, the SIGNED() function must be used. To convert a numeric type to an unsigned integer, the UNSIGNED() function must be used.
2.6.5.1
An array is a structure consisting of a number of elements which are all of the same type, called the element type. The number of elements of an array is called its length. The length is a positive integer. The elements of the array are designated by indices, which are integers between 0 and the length minus 1.
2.6.5.2.1
ArrayType = ARRAY [ length ] OF type | '[' [ length ] ']' type
length = ConstExpression | VAR varlength
varlength = expression
2.6.5.3
Arrays declared without length are called
open arrays. They are restricted to pointer base types (see
2.6.8 Pointer types).
2.6.5.4NOTE
In contrast to Oberon and Oberon+, in Micron only the first dimension of a multi-dimensional array can be an open array.
2.6.5.5
Arrays of arrays are stored in row-major order.
2.6.5.6.1
array 10 of integer
pointer to array of char
[N] T
2.6.5.7
Local variables of array type can have variable lengths calculated at runtime; in this case the LengthList is prefixed with the VAR reserved word; the expression cannot reference other local variables of the same scope.
2.6.5.8NOTE
In contrast to array pointers allocated with NEW(), variable length arrays (VLA) can be allocated on the stack instead of the heap (depending on the compiler and supported options), which makes them attractive to low-resource embedded applications where dynamic memory allocation is not feasible.
2.6.5.9
Array lengths of at least MAX(UINT32) shall be accepted by a compiler, for constant, dynamic and variable lengths.
2.6.6
Record types
2.6.6.1
A record type is a structure consisting of a fixed number of elements, called fields, with possibly different types. The record type declaration specifies the name and type of each field. The scope of the field identifiers extends from the point of their declaration to the end of the record type, but they are also visible within designators referring to elements of record variables (see
2.8.2 Operands). If a record type is exported, field identifiers that are to be visible outside the declaring module must be marked. They are called public fields; unmarked elements are called private fields.
2.6.6.2
The syntax for a record type also makes provisions for an optional variant part, started by the CASE reserved word. The variant part implies that a record type may be specified as consisting of several variants. This means that different variables, although said to be of the same type, may assume structures which differ in a certain manner. The differences may consist of a different number and different types of components. Each variant is characterised by a field of record type, according to which the variants are discriminated. No case of a variant part can be of object type (see
2.6.7 Object types).
2.6.6.3.1
RecordType = RECORD [FixedPart][VariantPart] END
FixedPart = FieldList { [';'] FieldList}
FieldList = IdentList ':' type [ BITS integer ]
| '..' BITS integer
| INLINE identdef ':' type
VariantPart = CASE { ['|'] [INLINE] identdef ':' type }
IdentList = identdef { [','] identdef }
2.6.6.4.1
record
day, month, year: integer
end
RECORD
name, firstname: ARRAY 32 of CHAR
age: INTEGER
salary: REAL
END
2.6.6.5
Fields of record type of the fixed part or the fields of the variant part can be prefixed by the INLINE (or abbreviated only IN) reserved word, in which case the name of the field or variant becomes transparent, and the fields of the corresponding record type are directly embedded in the enclosing record.
2.6.6.6
Fields of boolean or integer type within the fixed part of a record can be packed at the bit level for memory optimization. This is achieved by explicitly specifying the number of bits each field occupies. Use the BITS keyword followed by an integer to declare the bit width of a field. Sequences of fields with BITS specifications are grouped into packing units. Each unit is sized to the nearest integer type that can contain the specified bits. Explicit padding can be inserted using '..' followed by BITS and a number. This allows for precise control over field alignment within a unit. A new packing unit begins when either the sum of bits in the current sequence exceeds the maximum supported integer size, or padding of 0 bits is encountered. The type of a field with BITS specification determines how the field value is interpreted (e.g., signed vs unsigned overflow behavior); the number of bits of a signed integer type includes the sign bit.
2.6.6.7
Each field or variant of a record must have a name which is unique within the record; if a field of a record embedded with the INLINE prefix has the same name as a field or variant of the embedding record, then the name of the prefixed field or variant must be used to access it.
2.6.6.8
TODO: open array as last field (no variant part) with new(ptr,len)
2.6.6.10NOTE
Self-referential records or objects are not directly supported. The name of the record type declaration is not known at the time the record fields are declared. By the deferred pointer type resolution (see
2.4 Declarations and scope rules), a pointer field to the currently declared record is still legal. In case of a pointer-to-record declaration, the following approach can be used (use the extra ItemDesc declaration so the Item type is already known when "next" is declared):
Item = POINTER TO ItemDesc
ItemDesc = RECORD value: INTEGER; next: Item END
2.6.7
Object types
2.6.7.1
An object type, similar to a record type, is a structure consisting of a fixed number of elements, called fields, with possibly different types. The object type declaration specifies the name and type of each field. The scope of the field identifiers extends from the point of their declaration to the end of the object type, but they are also visible within designators referring to elements of object variables (see
2.8.2 Operands). If an object type is exported, field identifiers that are to be visible outside the declaring module must be marked. They are called public fields; unmarked elements are called private fields.
2.6.7.2.1
ObjectType = OBJECT ['(' BaseType ')'] { IdentList ':' type [ ';' ] } END
BaseType = NamedType
2.6.7.3
Object types are extensible, i.e. an object type can be declared as an extension of another object type. In the example
2.6.7.3.1
T0 = record x: integer end
T1 = record (T0) y: real end
2.6.7.4
T1 is a (direct) extension of T0 and T0 is the (direct) base type of T1 (see
3 Definition of terms). An extended type T1 consists of the fields of its base type and of the fields which are declared in T1. Fields declared in the extended object shadow equally named fields declared in a base type.
2.6.7.5
Alternatively, a pointer to object type can be used as the base type; in this case the object base type of the pointer is used as the base type of the declared object.
2.6.7.6NOTE
Object types and type-bound procedures are an extended feature of the language and not available in all language levels (see
7 Language levels).
2.6.8
Pointer types
2.6.8.1
A variable of a pointer type P assumes as value the memory address of a value of some type T. T is called the pointer base type of P and can be of any type.
2.6.8.2
The pointer creates a level of indirection, allowing access to the value at that memory location through a process called dereferencing. Pointers can reference:
2.6.8.2.1
dynamically allocated memory on the heap,
2.6.8.2.2
local variables or parameters on the stack,
2.6.8.3.1
PointerType = ( POINTER TO | '^' ) type
2.6.8.4
If p is a variable of type
P = POINTER TO T, a call of the predeclared procedure
NEW(p) (see
6 Predeclared Procedure Reference) allocates a variable of type T on heap storage. If T is a record, object, or an array type with fixed length, the allocation is done with
NEW(p); if T is an open array type, the allocation has to be done with
NEW(p, e) where T is allocated with the length given by the expression e. In either case the pointer to the allocated memory is assigned to
p.
p is of type P. The dereferenced variable
p^ is of type T. Any pointer variable may assume the value NIL, which points to no memory at all. If an allocated record, object or array is no longer used, it can be explicitly deallocated with
DISPOSE(p). A pointer variable can also be set using the address operator (see
2.8.3.8 Address operator).
2.6.9
Procedure types
2.6.9.1
Variables of a procedure type T have a procedure (or NIL) as value. If a procedure P is assigned to a variable of type T, the formal parameter lists and result types (see
2.11.11 Formal parameters) of P and T must
match (see
3 Definition of terms). A procedure P assigned to a variable or a formal parameter must not be a predeclared, nor an inlined procedure.
2.6.9.2.1
ProcedureType = PROCEDURE [FormalParameters]
2.6.9.3NOTE
Traditional Oberon versions don’t support assignment of procedures local to another procedure to a procedure type variable. Oberon+ as well as Micron don’t make this restriction.
2.6.10
Type-bound procedure types
2.6.10.1
Variables of a type-bound procedure type T have a type-bound procedure (see
2.11.13 Type-bound procedures) or NIL as value. To assign a type-bound procedure P to a variable of a type-bound procedure type T, the right side of the assignment must be a designator of the form
v^.P or
v.P, where
v is a pointer to record or object, and
P is a procedure bound to this record or object. Note, that in case of procedures bound to an object type, the dynamic type of
v determines which procedure is assigned; this may be a different procedure than the one bound to the static type of
v. The formal parameter lists and result types (see
2.11.11 Formal parameters) of P and T must
match (see
3 Definition of terms). The same rules apply when passing a type-bound procedure to a formal argument of a type-bound procedure type.
2.6.10.2.1
ProcedureType = ( PROCEDURE | PROC ) '(' ( POINTER | '^' ) ')' [FormalParameters]
2.6.10.3NOTE
Type-bound procedure types support both, procedures bound to records and to objects, but the latter are an extended feature of the language and not available in all language levels (see
7 Language levels).
2.6.11
Interface types
2.6.11.2
A variable of interface type may hold NIL or a pointer to any record or object type whose type-bound procedures satisfy the interface. Satisfaction is determined structurally: a pointer type satisfies an interface if it provides type-bound procedures with signatures that are parameter-compatible with all procedures declared in the interface. Both record types and object types may satisfy interfaces through their type-bound procedures. No explicit declaration of intent to implement an interface is required.
2.6.11.3
If an interface type is exported, all procedure identifiers of the interface are visible outside the declaring module. Satisfaction determination considers whether the procedures bound to the candidate record or object type are exported (see
2.4 Declarations and scope rules).
2.6.11.4
Assignment of a pointer value to an interface variable is valid if the pointed-to type satisfies the interface. The special value NIL is assignable to any interface variable.
2.6.11.5
In case of an object type, the dynamic type of the pointer determines the set of procedures; this may be a different set than the one bound to the static object type of the pointer.
2.6.11.6.1
InterfaceType = INTERFACE {InterfaceProc} END
InterfaceProc = [PROCEDURE | PROC] identdef [FormalParameters] [';']
2.6.11.7NOTE
Micron interfaces are adopted from Go and follow similar semantics.
2.6.12
Enumeration types
2.6.12.1
An enumeration is a list of identifiers that denote the elements which constitute the type. They, and no other values, belong to this type. These identifiers are constants.
2.6.12.2
The export mark of the enumeration type declaration also applies to the elements of the enumeration.
2.6.12.3.1
enumeration ::= '(' constEnum ')'
constEnum ::= ident [ '=' ConstExpression ] { [','] ident }
2.6.12.4
The values of an enumeration are ordered, and the ordering relation is defined by their sequence in the enumeration. If T is an enumeration type then
MIN(T) returns the first and
MAX(T) the last element of the enumeration.
2.6.12.5
The ordinal number of a constant enumeration element can be obtained using the
ORD predeclared procedure.
CAST is the reverse operation. The ordinal number of the first element is determined by the optional constant expression, or 0 by default.
INC returns the next and
DEC the previous element.
INC(MAX(T)) and
DEC(MIN(T)) are undefined and terminate the program.
2.6.12.6.1
(red, green, blue)
(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
2.6.13.1
A type can be an alias of another type. The other type is referenced with a qualident. If the other type is in another module, it must be exported. A type is considered the same type as its alias.
2.6.13.2NOTE
If the other type is an enumeration type in another module M, the elements e of this enumeration type still have to be denoted by M.e.
2.7
Variable declarations
2.7.1
Variable declarations introduce variables by defining an identifier and a data type for them. The initial value of a variable is undefined.
2.7.2.1
VariableDeclaration = IdentList ':' type
2.7.3.1
i, j, k: integer
x, y: real
p, q: bool
s: set
F: Function
a: array 100 of real
w: array 16 of record
name: arra 32 of char
count: integer
end
t, c: Tree
2.8
Expressions
2.8.1
Expressions are constructs denoting rules of computation whereby constants and current values of variables are combined to compute other values by the application of operators and function procedures. Expressions consist of operands and operators. Parentheses may be used to express specific associations of operators and operands.
2.8.2.1
With the exception of constructors and literal constants (numbers, character constants, or strings), operands are denoted by designators. A designator consists of an identifier referring to a constant, variable, or procedure. This identifier may possibly be qualified by a module identifier (see
2.4 Declarations and scope rules and
2.12 Modules) and may be followed by selectors if the designated object is an element of a structure.
2.8.2.2.1
designator = qualident {selector}
selector = '.' ident | '[' expression ']' | '^' | '(' qualident ')'
ExpList = expression {[','] expression}
2.8.2.3
If
a designates an array, then
a[e] denotes that element of
a whose index is the current value of the expression
e. The type of
e must be an
integer type.
2.8.2.4
If
r designates a record or object, then
r.f denotes the field
f of
r. If
p designates a pointer,
p^ denotes the variable which is referenced by
p. The designators
p^.f and
p^[e] may be abbreviated as
p.f and
p[e], i.e. record, object and array selectors imply dereferencing.
2.8.2.5
Dereferencing is also implied if a pointer is assigned to a variable of a record, object or array type, if a pointer is used as actual parameter for a formal parameter of a record, object or array type, or if a pointer is used as argument of the predeclared procedure LEN().
2.8.2.6
If
a or
r are read-only, then also
a[e] and
r.f are read-only.
2.8.2.7
A type cast
v(T) converts the original type of
v to T. Within the designator,
v is then regarded as having the type T instead of the original type. The cast is applicable, if
v is a pointer type.
2.8.2.8
If the designated object is a constant or a variable, then the designator refers to its current value. If it is a procedure, the designator refers to that procedure unless it is followed by a (possibly empty) parameter list in which case it implies an activation of that procedure and stands for the value resulting from its execution. The actual parameters must correspond to the formal parameters as in proper procedure calls (see
2.11.11 Formal parameters).
2.8.2.9.1
i
a[i]
w[3].name[i]
t.left.right
t(CenterTree).subnode
2.8.3.1
Different classes of operators with different precedences (binding strengths) are syntactically distinguished in expressions. The operator
~ has the highest precedence, followed by multiplication operators, addition operators, and relations. Operators of the same precedence associate from left to right. For example,
x-y-z stands for
(x-y)-z.
2.8.3.2.1
expression = SimpleExpression [ relation SimpleExpression ]
relation = '=' | '#' | '<' | '<=' | '>' | '>=' | IN
SimpleExpression = ['+' | '-'] term { AddOperator term }
AddOperator = '+' | '-' | OR
term = factor {MulOperator factor}
MulOperator = '*' | '/' | DIV | MOD | '&' | AND
literal = number | string | hexstring | hexchar
| NIL | TRUE | FALSE | set
factor = literal | designator [ActualParameters]
| '(' expression ')' | ('~'|NOT) factor
| '@' designator | constructor
ActualParameters = '(' [ ExpList ] ')'
set = '{' [ element {[','] element} ] '}'
element = expression ['..' expression]
2.8.3.3
Logical operators
2.8.3.3.1
|
OR |
logical disjunction |
p or q |
if p then TRUE, else q |
&, AND |
logical conjunction |
p & q |
if p then q, else FALSE |
~, NOT |
negation |
~p |
not p |
2.8.3.3.2
These operators apply to BOOLEAN operands and yield a BOOLEAN result.
2.8.3.3.3NOTE
The OR and AND operator do short-circuit evaluation like C, i.e. in case of AND the second operand is not evaluated if the first operand results to FALSE.
2.8.3.3.4NOTE
In addition to & and ~, Micron supports the AND and NOT operators with the same meaning, for compatibility with Pascal and Modula-2.
2.8.3.4
Arithmetic operators
2.8.3.4.1
|
+ |
sum |
- |
difference |
* |
product |
/ |
real quotient |
DIV |
integer quotient |
MOD |
modulus |
2.8.3.4.2
The operators
+,
-,
*, and
/ apply to operands of numeric types.
2.8.3.4.3
When used as a binary operator, both operands must be of either of signed integer, unsigned integer or floating point type.
2.8.3.4.4NOTE
This is the same rule as in Modula-2,
where both operands must be either of type CARDINAL [...], in which case the result is of the type CARDINAL, or they must both be of type INTEGER [...], in which case the result is of type INTEGER. The Ada language has a similar rule. The rules of the C language in contrast are much more complicated (see e.g.
here), but it is always possible to add explicit conversions when migrating C to Micron.
2.8.3.4.5
As an exception, if one of the operands is an unsigned integer constant and the other operand is of type integer, the unsigned integer constant is converted to the smallest integer type which includes the constant value.
2.8.3.4.6
The type of the result of the operation is the type of that operand which includes the type of the other operand (see
2.6.4.5). In case of overflow, the result of an integer expression is truncated to this type by taking the appropriate number of low-order bits (corresponding to an implicit modulo 2^n operation, where n is the bit width of the integer type). An implementation of the language may decide to also truncate the results of intermediate operations.
2.8.3.4.7
The operator '
/' applies to real operands only.
2.8.3.4.8
The operators
DIV and
MOD apply to integer operands only. They are related by the following formulas defined for any
x and positive divisors
y:
2.8.3.4.8.1
x = (x DIV y) * y + (x MOD y)
0 <= (x MOD y) < y
2.8.3.4.9
When used as monadic (unary) operators, '-' denotes sign inversion and '+' denotes the identity operation. If the unary '-' or '+' is applied to an operand of unsigned integer type, the result is the next larger signed integer type, or an error if the operand is of type UIN64. Otherwise the type of the result is the same as the type of the operand.
2.8.3.4.11NOTE
Micron doesn’t do overflow checks. If the representation of the result of an arithmetic operation would require a wider integer type than provided by the type of the expression, the behaviour is undefined; e.g.
MAX(INT32)+1 causes an overflow, i.e. the result could be MIN(INT32) or anything else.
2.8.3.5.1
|
+ |
union |
- |
difference (x - y = x * (-y)) |
* |
intersection |
/ |
symmetric set difference (x / y = (x-y) + (y-x)) |
2.8.3.5.2
Set operators apply to operands of type SET and yield a result of type SET. The monadic minus sign denotes the complement of
x, i.e.
-x denotes the set of integers between 0 and
MAX(SET) which are not elements of
x. Set operators are not associative (
(a+b)-c # a+(b-c)).
2.8.3.5.3
A set constructor defines the value of a set by listing its elements between curly brackets. The elements must be integers in the range
0..MAX(SET). A range
a..b denotes all integers in the interval [a, b].
2.8.3.6.1
|
= |
equal |
#, <> |
unequal |
< |
less |
<= |
less or equal |
> |
greater |
>= |
greater or equal |
IN |
membership |
IS |
type test |
2.8.3.6.3
The relations
=,
#,
<,
<=,
>, and
>= apply to the numeric types, as well as constant enumerations, pointer types, CHAR, string literals, and CHAR arrays containing
0x as a terminator.
2.8.3.6.4
The relations
= and
# also apply to BOOLEAN, and SET, as well procedure types.
2.8.3.6.5NOTE
Micron supports '<>' as an alternative '#' syntax for compatibility with Pascal.
2.8.3.6.7
In general, both operands must be of the same type. If the operand are of numeric type, both must be either of signed integer, unsigned integer or floating point type.
2.8.3.6.8
If one of the operands is an unsigned integer constant and the other operand is of type integer, the unsigned integer constant is converted to the smallest integer type which includes the constant value.
2.8.3.6.9
String literals, CHAR and CHAR array operands can be mixed.
2.8.3.6.10
If both operands are pointers, the specific type doesn't matter; the same applies to procedure types.
2.8.3.6.11
x IN s stands for
x is an element of s. If
s is of type SET,
x must be of an integer type.
2.8.3.6.12
v IS T stands for
the dynamic type of v is T (or an extension of T ) and is called a type test. It is applicable if
2.8.3.6.12.1
v is a pointer to object type (which can be NIL), and if
2.8.3.6.13NOTE
The IS operator is an extended feature of the language and not available in all language levels (see
7 Language levels).
2.8.3.6.14.1
1991 // integer
i div 3 // integer
~p or q // boolean
(i+j) * (i-j) // integer
s - {8, 9, 13} // set
i + x // real
a[i+j] * a[i-j] // real
(0<=i) & (i<100) // boolean
t.key = 0 // boolean
k in {i..j-1} // boolean
w[i].name <= "John" // boolean
t is CenterTree // boolean
2.8.3.7
String operators
2.8.3.7.2
The concatenation operator applies to string and character literals. The resulting string consists of the characters of the first operand followed by the characters of the second operand.
2.8.3.8
Address operator
2.8.3.8.1
The address operator '@' applies to a variable, parameter, record field, object field, or array element, which is visible and not read-only; the operator yields the address of the value.
2.8.3.8.2
If
v designates a variable or parameter of type T, then
@v denotes the address of
v with the type
POINTER TO T.
2.8.3.8.3
If
r.f designates a record or object field
f of type T, then
@r.f denotes the address of field
f with the type
POINTER TO T.
2.8.3.8.4
If
a[e] designates an array element of type T at index
e, then
@a[e] denotes the address of the element at index
e; the address is of type
POINTER TO T.
2.8.3.9.1
A function call is a factor in an expression. In contrast to
2.9.5 Procedure calls in a function call the actual parameter list is mandatory. Each expression in the actual parameters list (if any) is used to initialize a corresponding formal parameter. The number of expressions in the actual parameter list must correspond the number of formal parameters. See also
2.11.11 Formal parameters.
2.8.4
Constructors
2.8.4.1
Constructors can be used to initialize records, objects, arrays and pointers via assignments, or to create an anonymous variable of the constructed type.
2.8.4.2.1
constructor = ['@'][NamedType] '{' [ component {[','] component} ] '}'
component = ident ':' expression
| '[' ConstExpression ']' ':' expression
| expression ['..' expression]
2.8.4.3
A constructor consists of an optional NamedType and a list of either named, indexed or anonymous components. Named, Indexed and anonymous components can be mixed in the list.
2.8.4.4
When the type of a constructor can be inferred (e.g. by the left side of an assignment), the NamedType prefix can be left out.
2.8.4.5
A constructor is a literal and thus can appear in a constant expression, if all expressions appearing in its components are constant expressions.
2.8.4.6
When a constructor is prefixed with the address-of operator ('@'), then it represents an anonymous, local variable in the current scope.
2.8.4.7
If NamedType is a record or object type, then anonymous or named components can be used to assign field values; anonymous components are associated with fields in the order of declaration (in case of object types, inherited fields come first in the order). If the record type has a variant part, only named component can be used, and only one option of the variant part can be initialized in the constructor. If an object type has a base type imported from another module where one or more fields are not public or read-only, then a constructor is not available.
2.8.4.8
If NamedType is an array type, then anonymous or indexed components can be used to assign element values; anonymous components are associated with elements in the order of indizes. The array type may be an open array in which case the number of elements is determined by the number of components or the largest index component.
2.8.4.9
If NamedType is a pointer type, then there is exactly one anonymous component which is an unsigned integer type constant representing the address.
2.8.4.10
For each field or element which is of record, object, array or pointer type, an embedded constructor is required.
2.8.4.12.1
Hex strings are sequences of bytes encoded in hexadecimal format and enclosed in dollar signs. The number of hex digits in the string must be even, two hex digits per byte. The number of bytes encoded in a hex string is called its length. Line breaks and other white space between the dollar signs is ignored. Both upper and lower-case hex digits are supported.
2.8.4.12.3.1
const arrow = $0F0F 0060 0070 0038 001C 000E 0007 8003 C101 E300
7700 3F00 1F00 3F00 7F00 FF00$
2.8.4.12.4NOTE
Hex strings are provided for compatibility with Oberon+; they are essentially an alternative syntax for an open byte array constructor.
2.9
Statements
2.9.1
Statements denote actions. There are elementary and structured statements. Elementary statements are not composed of any parts that are themselves statements. They are the assignment, the procedure call, the return, and the
exit statement. Structured statements are composed of parts that are themselves statements. They are used to express sequencing and conditional, selective, and repetitive execution.
2.9.2.1
statement = [ assignment | ProcedureCall | IfStatement
| CaseStatement | WithStatement | LoopStatement
| ExitStatement | GotoStatement | gotoLabel
| ReturnStatement | RepeatStatement | ForStatement ]
2.9.3
Statement sequences
2.9.3.1
Statement sequences denote the sequence of actions specified by the component statements which are optionally separated by semicolons.
2.9.3.2.1
StatementSequence = statement { [";"] statement}
2.9.4
Assignments
2.9.4.1
Assignments replace the current value of a variable by a new value specified by an expression. The expression must be
assignment compatible with the variable (see
3 Definition of terms). The assignment operator is written as
:= and pronounced as
becomes.
2.9.4.2.1
assignment = designator ':=' expression
2.9.4.3
If an expression
e of type T
e is assigned to a variable
v of type T
v, the following happens:
2.9.4.3.1
if T
v and T
e are the same record type, all fields of T
e are assigned to the corresponding fields of T
v.
2.9.4.3.2
if T
v and T
e are the same object type, all fields of T
e are assigned to the corresponding fields of T
v, unless T
v has a base type imported from another module where not all fields are public (in which case assignment is not available).
2.9.4.3.3
if T
v is an
ARRAY n OF CHAR and
e is a string literal, or an array of CHAR (open or fixed length), then
v[i] becomes e
i for i = 0..m-1 and
v[m] becomes 0X, for m = MIN(n-1, STRLEN(e)).
2.9.4.4.1
i := 0
p := i = j
x := i + 1
k := log2(i+j)
F := log2
s := {2, 3, 5, 7, 11, 13}
a[i] := (x+y) * (x-y)
t.key := i
w[i+1].name := "John"
t := c
2.9.5
Procedure calls
2.9.5.1
A procedure call activates a procedure. It may contain a list of actual parameters which replace the corresponding formal parameter list defined in the procedure declaration (see
2.11 Procedure declarations). The correspondence is established by the positions of the parameters in the actual and formal parameter lists.
2.9.5.2
Each actual parameter must be an expression. This expression is evaluated before the procedure activation, and the resulting value is assigned to the formal parameter (see also
2.11.11 Formal parameters). The order of evaluation of the actual parameter expressions is undefined.
2.9.5.3.1
ProcedureCall = designator [ ActualParameters ]
2.9.5.4.1
WriteInt(i*2+1)
inc(w[k].count)
t.Insert("John")
2.9.6
If statements
2.9.6.1
If statements specify the conditional execution of guarded statement sequences. The boolean expression preceding a statement sequence is called its guard. The guards are evaluated in sequence of occurrence, until one evaluates to TRUE, whereafter its associated statement sequence is executed. If no guard is satisfied, the statement sequence following ELSE is executed, if there is one.
2.9.6.2.1
IfStatement = IF expression THEN StatementSequence
{ElsifStatement} [ElseStatement] END
ElsifStatement = ELSIF expression THEN StatementSequence
ElseStatement = ELSE StatementSequence
2.9.6.3.1
if (ch >= "A") & (ch <= "Z") then ReadIdentifier
elsif (ch >= "0") & (ch <= "9") then ReadNumber
elsif (ch = "'") OR (ch = '"') then ReadString
else SpecialCharacter
end
2.9.7
Case statements
2.9.7.1
Case statements specify the selection and execution of a statement sequence according to the value of an expression. First the case expression is evaluated, then that statement sequence is executed whose case label list contains the obtained value. The case expression must either be of an integer type that includes tjhe types of all case labels, or a constant enumeration type with all case labels being valid members of this type, or both the case expression and the case labels must be of type CHAR. Case labels are constants, and no value must occur more than once. If the value of the expression does not occur as a label of any case, the statement sequence following ELSE is selected, if there is one, otherwise the program is aborted.
2.9.7.2
The type T of the case expression (case variable) may also be a pointer to object type. Then each case consists of exactly one case label which must be an
extension of T (see
3 Definition of terms), and in the statements S
i labelled by T
i, the case variable is considered as of type T
i. If the case variable is of POINTER type, then one case label can also be NIL. The evaluation order corresponds to the case label order; the first statement sequence is executed whose case label meets the condition.
2.9.7.3.1
CaseStatement = CASE expression OF ['|'] Case { '|' Case }
[ ELSE StatementSequence ] END
Case = [ CaseLabelList ':' StatementSequence ]
CaseLabelList = LabelRange { [','] LabelRange }
LabelRange = label [ '..' label ]
label = ConstExpression
2.9.7.4.1
case ch of
"A" .. "Z": ReadIdentifier
| "0" .. "9": ReadNumber
| "'", '"': ReadString
else SpecialCharacter
end
2.9.7.4.2
type R = object a: integer end
R0 = object (R) b: integer end
R1 = object (R) b: real end
R2 = object (R) b: set end
P = ^R
P0 = ^R0
P1 = ^R1
P2 = ^R2
var p: P
case p of
| P0: p.b := 10
| P1: p.b := 2.5
| P2: p.b := {0, 2}
| NIL: p.b := {}
end
2.9.8
While statements
2.9.8.1
While statements specify the repeated execution of a statement sequence while the Boolean expression (its guard) yields TRUE. The guard is checked before every execution of the statement sequence.
2.9.8.2.1
WhileStatement = WHILE expression DO StatementSequence END
2.9.9
Repeat statements
2.9.9.1
A repeat statement specifies the repeated execution of a statement sequence until a condition specified by a Boolean expression is satisfied. The statement sequence is executed at least once.
2.9.9.2.1
RepeatStatement = REPEAT StatementSequence UNTIL expression
2.9.10
For statements
2.9.10.1
A FOR statement specifies the repeated execution of a statement sequence while a progression of values is assigned to a control variable of the for statement. Control variables can be of integer or enumeration types. An explicit BY expression cannot be zero and is only supported for integer control variables.
2.9.10.2.1
ForStatement = FOR ident ':=' expression TO expression
[BY ConstExpression]
DO StatementSequence END
2.9.10.3.1
for v := first to last by step do statements end
2.9.10.4.1
temp := last; v := first
if step > 0 then
while v <= temp do statements; INC(v,step) end
else
while v >= temp do statements; DEC(v,-step) end
end
2.9.10.5
temp has the same type as
v.
step must be a nonzero constant expression; if
step is not specified, it is assumed to be 1.
2.9.10.6NOTE
A FOR statement supposed to run downwards therefore needs an explicit negative BY expression.
2.9.10.7
If
first and
last are elements of an enumeration type, then the statement is equivalent to
2.9.10.7.1
temp := last; v := first
if ORD(first) <= ORD(last) then
while v <= temp do statements; INC(v) end
else
while v >= temp do statements; DEC(v) end
end
2.9.10.8.1
for i := 0 to 79 do k := k + a[i] end
for i := 79 to 1 by -1 do a[i] := a[i-1] end
2.9.11
Loop statements
2.9.11.2.1
LoopStatement = LOOP StatementSequence END
ExitStatement = EXIT
2.9.11.3.1
loop
ReadInt(i)
if i < 0 then exit end
WriteInt(i)
end
2.9.11.4
Loop statements are useful to express repetitions with several exit points or cases where the exit condition is in the middle of the repeated statement sequence.
2.9.12
Return, exit and goto statements
2.9.12.1
A return statement indicates the termination of a procedure. It is denoted by RETURN, followed by an expression if the procedure is a function procedure. The type of the expression must be assignment compatible (see
3 Definition of terms) with the result type specified in the procedure heading (see
2.11 Procedure declarations).
2.9.12.2
Return statements are illegal in module bodies and FINALLY sections.
2.9.12.3.1
ReturnStatement = RETURN [ expression ]
2.9.12.4
Function procedures require the presence of a return statement indicating the result value. In proper procedures, a return statement is implied by the end of the procedure body. Any explicit return statement therefore appears as an additional (probably exceptional) termination point.
2.9.12.5NOTE
The optional expression causes an LL(k) ambiguity which can be resolved in that the parser expects a return expression if the procedure has a return type and vice versa.
2.9.12.6
An exit statement is denoted by EXIT. It specifies termination of the enclosing loop statement and continuation with the statement following that loop statement. Exit statements are contextually, although not syntactically associated with the loop statement which contains them.
2.9.12.8
The goto statement causes an unconditional jump to another statement in the same statement sequence. The destination of the jump is specified by the name of a label. Goto labels are declared in the statement sequence and are referenced in the goto statements. It is illegal to define a label that is never used. Goto labels do not conflict with identifiers that are not labels. The scope of a goto label is the statement sequence where it is declared and all nested statement sequences.
2.9.12.9
A goto statement cannot jump into a structured statement or outside of the enclosing BEGIN or FINALLY section.
2.9.12.10.1
GotoStatement = 'GOTO' ident
gotoLabel = ident ':'
2.9.12.11NOTE
Goto statements are considered bad practice and should be avoided as much as possible. They were mostly added to the language to ease migration of existing C code. It is always possible to replace a goto statement by an alternative implementation that doesn’t need a goto.
2.10
Exception handling
2.10.1
Exception handling in Micron is implemented using the predeclared procedures PCALL and RAISE (see
6 Predeclared Procedure Reference), without any special syntax. There are no predefined exceptions.
2.10.2
An exception is just a value of type POINTER TO ANY. The pointer representing the exception is passed as an actual argument to RAISE. RAISE may be called without an argument in which case the compiler provides a random internal pointer. RAISE never returns, but control is transferred from the place where RAISE is called to the nearest dynamically-enclosing call of PCALL. When calling RAISE without a dynamically-enclosing call of PCALL the program execution is aborted.
2.10.3
PCALL executes a protected call of the procedure or procedure type P. P is passed as the second argument to PCALL. P cannot have a return type. P can be a nested procedure. If P has formal parameters the corresponding actual parameters are passed to PCALL immediately after P. The actual parameters must be
parameter compatible with the formal parameters of P (see
3 Definition of terms). The first parameter R of PCALL is a variable of type POINTER TO ANY; if RAISE(E) is called in the course of P, then R is set to E; otherwise R is set to NIL. The state of VAR parameters of P or local variables or parameters of an outer procedure accessed by P is non-deterministic in case RAISE is called in the course of P.
2.10.4.1
module ExceptionExample
var ex1, ex2: integer // type doesn't matter
proc Print(in str: array of char)
begin
println(str)
raise(@ex1)
println("this is not printed")
end Print
var res: pointer to any
begin
pcall(res, Print, "Hello World")
if res = @ex1 then println("got ex1")
elsif res = nil then println("no exception")
else println("unknown exception")
// could call raise(res) here to propagate the exception
end
end ExceptionExample
2.11
Procedure declarations
2.11.1
A procedure declaration consists of a procedure heading and a procedure body. The heading specifies the procedure identifier and the formal parameters (see
2.11.11 Formal parameters). The body contains declarations and statements. The procedure identifier must be repeated at the end of the procedure declaration.
2.11.2
There are two kinds of procedures: proper procedures and function procedures. The latter are activated by a function designator as a constituent of an expression and yield a result that is an operand of the expression. Proper procedures are activated by a procedure call. A procedure is a function procedure if its formal parameters specify a result type. Each control path of a function procedure must return a value.
2.11.3
All constants, variables, types, and procedures declared within a procedure body are local to the procedure. The values of local variables are undefined upon entry to the procedure. Since procedures may be declared as local objects too, procedure declarations may be nested. The call of a procedure within its declaration implies recursive activation.
2.11.4
In addition to its formal parameters and locally declared objects, the objects declared globally are also visible in the procedure.
2.11.5
A procedure can be declared INLINE in which case the code of the procedure is embedded at the call site, and no call actually happens. Procedures declared INLINE don't support recursion, neither directly nor indirectly. INLINE procedures neither support variable length array local variables. Nested procedures of an INLINE procedure must also be INLINE.
2.11.6
A procedure can be declared INVAR (invariable) in which case it can be used in CONST declarations to derive a constant value at compile time. INVAR procedures can only call other INVAR procedures or predeclared procedures not depending on runtime information. INVAR procedures cannot access module variables, but they can access CONST declarations in the same or other modules.
2.11.7
A procedure can be declared EXTERN to indicate that it is implemented elsewhere and the source code is not available. If EXTERN is followed by the ident C, then the procedure is implemented in an external library with C calling convention and called via the foreign function interface, otherwise it is assumed that the procedure is implemented in Micron. EXTERN C can be followed by a string literal with the name of the procedure in the external library, otherwise the same name as in the identdef is assumed.
2.11.8NOTE
EXTERN is regarded as an alternative to ProcedureBody, why it follows the (optional) semicolon.
2.11.9
In case of a forward declaration the formal parameters must be identical with the actual procedure declaration. Forward declarations are marked by '^'.
2.11.10.1
ProcedureDeclaration = ProcedureHeading ( [ INLINE | INVAR ] [';'] ProcedureBody
| [';'] EXTERN ['C' [string]] )
| ForwardDeclaration
ProcedureHeading = ( PROCEDURE | PROC ) identdef [ FormalParameters ]
ForwardDeclaration = ( PROCEDURE | PROC ) '^' identdef [ FormalParameters ]
ProcedureBody = DeclarationSequence BEGIN StatementSequence
[FINALLY StatementSequence] END ident
DeclarationSequence = { CONST { ConstDeclaration [';'] }
| TYPE { TypeDeclaration [';']
| VAR { VariableDeclaration [';'] }
| ProcedureDeclaration [';'] }
2.11.11
Formal parameters
2.11.11.1
Formal parameters are identifiers declared in the formal parameter list of a procedure. They correspond to actual parameters specified in the procedure call. The correspondence between formal and actual parameters is established when the procedure is called.
2.11.11.2NOTE
Oberon and Oberon+ support VAR parameters, which are not necessary in Micron, because the formal parameter can be a pointer instead and the '@' operator can be applied to the designator passed as actual parameter.
2.11.11.3
The scope of a formal parameter extends from its declaration to the end of the procedure block in which it is declared. A function procedure without parameters must have an empty parameter list. It must be called by a function designator whose actual parameter list is empty too.
2.11.11.4.1
FormalParameters = '(' [ FPSection { [';'] FPSection } ] ')'
[ ':' ReturnType ]
ReturnType = [ POINTER TO | '^' ] NamedType
FPSection = [CONST] ident { [','] ident } ':' FormalType
FormalType = type
2.11.11.5
Let T
f be the type of a formal parameter
f and T
a the type of the corresponding actual parameter
a. T
a must be
parameter compatible to
f (see
3 Definition of terms).
2.11.11.6
CONST parameters are read-only in the procedure body. The read-only feature is transitive, i.e. also the variables, elements or fields designated from a CONST parameter are read-only.
2.11.11.7NOTE
In contrast to C, Micron doesn't support variable (variadic) parameters, because there are viable alternatives, and the impact on compilers and runtime systems is much greater than the potential benefits. When passing a variable number of arguments of the same type to a procedure, use an pointer to array parameter with an explicit count; pass both the array pointer and the number of elements to ensure bounds safety. When passing a variable number of arguments of different types along with a format specification string, use an array of pointer to ANY; the procedure should parse the format string to determine the type of each argument and perform appropriate type casting. In both cases, constructors can be used to create the array on the fly.
2.11.11.9NOTE
In contrast to Oberon and Oberon-2, Micron allows array and record return types.
2.11.11.10.1
proc ReadInt(x: pointer to integer)
var i: integer; ch: char
begin i := 0; Read(ch)
while ("0" <= ch) & (ch <= "9") do
i := 10*i + (ord(ch)-ord("0")); Read(ch)
end
x^ := i
end ReadInt
2.11.12
Finally section
2.11.12.1
The statement sequence after the symbol BEGIN can optionally be followed by the symbol FINALLY and yet another statement sequence.
2.11.12.2
The statements in this FINALLY section are executed whenever the corresponding BEGIN section terminates.
2.11.12.3
A FINALLY section cannot include a RETURN statement.
2.11.12.4NOTE
The expression of a RETURN statement is evaluated before the FINALLY section is run; it therefore cannot be modified in the FINALLY section.
2.11.12.5
The statements in the FINALLY section are also executed if a nested BEGIN section is terminated with RAISE (see
6.2 Predeclared proper procedures). If a nested BEGIN section is terminated with RAISE, all FINALLY sections on the call chain up to (but not including) PCALL are executed in the reverse order of the call chain.
2.11.12.6NOTE
Note that both, FINALLY sections and also exceptions are an extended feature of the language and not available in all language levels (see
7 Language levels) and compiler configurations.
2.11.13
Type-bound procedures
2.11.13.1
Procedures may be associated with a record or object type declared in the same scope. The procedures are said to be bound to the type. The binding is expressed by the receiver syntax in the heading of a procedure declaration. The receiver consists of a type name and the optional receiver parameter name. The type name can either reference the record or object type T, or a pointer to T. The procedure is bound to the type T and is considered local to it. If the optional receiver parameter name is left out, the compiler automatically creates a hidden parameter which can be accessed from within the procedure body by the name "self".
2.11.13.2NOTE
Regardless of whether the receiver is an object, record or pointer type, the receiver parameter is always a pointer to record or object type.
2.11.13.3
Type-bound procedures cannot be INVAR. Procedures bound to object types cannot be INLINE.
2.11.13.4.1
ProcedureHeading = ( PROCEDURE | PROC ) [Receiver] identdef [ FormalParameters ]
ForwardDeclaration = ( PROCEDURE | PROC ) '^' [Receiver] identdef [ FormalParameters ]
Receiver = '(' ReceiverParam ':' ReceiverType ')' | ReceiverType '.'
ReceiverParam = ident
ReceiverType = ident
2.11.13.5
If a procedure P is bound to an object type T0, it is implicitly also bound to any object type T1 which is an extension of T0. However, a procedure P' (with the same name as P) may be explicitly bound to T1 in which case it overrides the binding of P. P' is considered a redefinition of P for T1. The formal parameters of P and P' must
match (see
3 Definition of terms). If P and T1 are exported (see
2.4 Declarations and scope rules), P' must be exported too.
2.11.13.6NOTE
The name of a type-bound procedure must be unique within the type to which it is bound, not within the scope in which it is declared.
2.11.13.7
If
v is a designator and
P is a type-bound procedure, then
v.P denotes that procedure
P which is bound to the dynamic type of
v. Note, that this may be a different procedure than the one bound to the static type of
v. Depending on wheter
v designates a pointer or a value,
v or
@v is passed to P’s receiver parameter.
2.11.13.8NOTE
As in Object Pascal or Go, you can direcly call
v.P in case v is e.g. a stack-allocated object, instead of
(@v).P. The compiler takes care that a pointer is passed to the receiver parameter.
2.11.13.9
If
r is the receiver parameter of P declared with type T,
r.P^ denotes the (redefined, sometimes calles
super) procedure P bound to a base type of T.
2.11.13.10.1
proc (t: Tree) Insert (node: Tree)
var p, father: Tree
begin p := t
repeat father := p
if node.key = p.key then return end
if node.key < p.key then
p := p.left
else
p := p.right
end
until p = nil
if node.key < father.key then
father.left := node
else
father.right := node
end
node.left := nil; node.right := nil
end Insert
proc CenterTree.Insert (node: Tree) // redefinition
begin
WriteInt(node(CenterTree).width)
self.Insert^(node) // calls the Insert procedure bound to Tree
end Insert
2.11.13.11NOTE
Object types and type-bound procedures are an extended feature of the language and not available in all language levels (see
7 Language levels). Record types also support type-bound procedures, but in contrast to object types, these procedures are not virtual (since records don't support inheritance).
2.11.14
Predeclared procedures
2.11.14.1
Predeclared procedures are provided by the compiler and accessible without extra imports. Some are generic procedures, i.e. they apply to several types of operands.
2.12.1
A module is a collection of declarations of constants, types, variables, and procedures, together with a sequence of statements for the purpose of assigning initial values to the variables. A module constitutes a text that is compilable as a unit (compilation unit).
2.12.2.1
module = MODULE ident [ MetaParams ] [';']
{ ImportList | WhereDecls | DeclarationSequence }
[ BEGIN StatementSequence [FINALLY StatementSequence] ]
END ident ['.']
ImportList = IMPORT import { [','] import } [';']
import = [ ident ':=' ] ImportPath ident [ MetaActuals ]
ImportPath = { ident '.' }
2.12.3
The import list specifies the names of the imported modules. If a module A is imported by a module M and A exports an identifier
x, then
x is referred to as
A.x within M.
2.12.4
If A is imported as
B := A, the object
x must be referenced as
B.x. This allows short alias names in qualified identifiers.
2.12.5
In Micron, as in Oberon+, the import can refer to a module by means of a module name optionally prefixed with an import path. There is no requirement that the import path actually exists in the file system, or that the source files corresponding to an import path are in the same file system directory. It is up to the compiler how source files are mapped to import paths. An imported module with no import path is first looked up in the import path of the importing module.
2.12.6
A module must not import itself (neither directly nor indirectly).
2.12.7
Identifiers that are to be exported (i.e. that are to be visible in client modules) must be marked by an export mark in their declaration (see Chapter
2.4 Declarations and scope rules).
2.12.8
The statement sequence following the symbol BEGIN is executed when the module is loaded, which is done after the imported modules have been loaded. It follows that cyclic import of modules is illegal.
2.13
Generics
2.13.1
Micron supports generic programming. Modules can be made generic by adding formal meta parameters. Meta parameters represent types or constants; the latter include procedures. Meta parameters default to types, but can be explicitly prefixed with the TYPE reserved word; the CONST prefix designates a constant meta parameter.
2.13.2
Meta parameters can be constrained with a type using a WHERE declaration, in which case the actual meta parameter must correspond to this type. The correspondence between a WHERE declaration and a meta parameter is established by name equivalence; there can only be one WHERE declaration per meta parameter. The type of the actual meta parameter must be assignment compatible with the constraint type (see
3 Definition of terms), which is checked when the generic module is instantiated. The special form of the ConstDeclaration may only be used for a CONST meta parameter with the same ident.
2.13.3
Generic modules can be instantiated with different sets of meta actuals which enables the design of reusable algorithms and data structures. The instantiation of a generic module occurs when importing it. A generic module can be instantiated more than once in the same module with different actual meta parameters. See also
2.12 Modules.
2.13.4.1
MetaParams = '(' MetaSection { [';'] MetaSection } ')'
MetaSection = ['CONST' | 'TYPE'] ident { [','] ident }
WhereDecls = WHERE { WhereDeclaration [';'] }
WhereDeclaration = ident ':' type
MetaActuals = '(' ConstExpression { [','] ConstExpression } ')'
2.13.5
Meta parameters can be used within the generic module like normal types or constants. If no type constraint is present, the types and constants can be used wherever no information about the actual type is required; otherwise the type constraint determines the permitted operations. The rules for
same types and
equal types apply analogously to meta parameters, and subsequently also the corresponding assignment, parameter and array compatibility rules.
2.13.6NOTE
A type constraint can be an interface type which is a special but legal use of interface types; if an interface type is used as a type constraint, then the type checker has information about what methods the generic type parameter supports which can be assured at compile time; there is no need that the actual type parameter is indeed an interface type, just that the type satisfies (see
2.6.11 Interface types) the interface.
2.14
Source code directives
2.14.2
Configuration Variables
2.14.2.1
Configuration variables can be set or unset in the source code using the following syntax:
2.14.2.2.1
directive = '<*' ident ( '+' | '-' ) '*>'
2.14.2.3
Each variable is named by an ident which follows the syntax specified in
2.3.4 Identifiers. Variable names have compilation unit scope which is separate from all other scopes of the program. Configuration variable directives can be placed anywhere in the source code. The directive only affects the present compilation unit, starting from its position in the source code.
2.14.2.5NOTE
Usually the compiler provides the possibility to set configuration variables, e.g. via command line interface.
2.14.2.6
TBD: import configuration files with a list of flags for project wide configuration
2.14.3
Conditional compilation
2.14.3.1
Conditional compilation directives can be placed anywhere in the source code. The following syntax applies:
2.14.3.2.1
directive = '<*' [ scIf | scElsif | scElse | scEnd ] '*>'
scIf = IF scExpr THEN
scElsif = ELSIF condition THEN
scElse = ELSE
scEnd = END
condition = scTerm { OR scTerm }
scTerm = scFactor {'&' scFactor}
scFactor = ident | '(' condition ')' | '~' scFactor
2.14.3.3
An ELSIF or ELSE directive must be preceded by an IF or another ELSIF directive. Each IF directive must be ended by an END directive. The directives form sections of the source code. Only the section the condition of which is TRUE (or the section framed by ELSE and END directive otherwise) is visible to the compiler. Conditions are boolean expressions. Ident refers to a configuration variable. When a configuration variable is not explicitly set it is assumed to be FALSE. Each section can contain nested conditional compilation directives.
2.14.3.4.1
<* if A then *>
println("A")
<* elsif B & ~C then *>
println("B & ~C")
<* else *>
println("D")
<* end *>
3
Definition of terms
3.1
Integer types
3.2
Signed integer types
3.3
Unsigned integer types
3.4
Floating-point types
3.5
Numeric types
3.6
Same types
3.6.1
Two variables a and b with types T
a and T
b are of the same type if
3.6.1.1
T
a and T
b are both denoted by the same type identifier, or
3.6.1.2
T
a is declared to equal T
b in a type declaration of the form T
a = T
b, or
3.6.1.3
a and b appear in the same identifier list in a variable, record field, or formal parameter declaration and are not open arrays.
3.7
Equal types
3.7.1
Two types T
a and T
b are equal if
3.7.1.1
T
a and T
b are the
same type, or
3.7.1.2
T
a and T
b are open array types with
equal element types, or
3.7.1.3
T
a and T
b are non-open array types with same length and
equal element types, or
3.7.1.4
T
a and T
b are pointer types with
equal base types, or
3.7.1.5
T
a and T
b are procedure types whose formal parameters
match
3.8
Type inclusion
3.8.1
Numeric types include (the values of) smaller numeric types. There is a separate type inclusion hierarchy for signed integeres, unsigned integers and floating point types. See
2.6.4 Basic types for more information.
3.9
Type extension (object)
3.9.1
Given a type declaration T
b = OBJECT(T
a)…END, T
b is a direct extension of T
a, and T
a is a direct base type of T
b. A type T
b is an extension of a type T
a (T
a is a base type of T
b) if
3.9.1.1
T
a and T
b are the
same types, or
3.9.1.2
T
b is a direct extension of T
a
3.10
Type extension (pointer)
3.10.1
If P
a = POINTER TO T
a and P
b = POINTER TO T
b , P
b is an extension of P
a (P
a is a base type of P
b) if T
b is an extension of T
a.
3.10.2NOTE
The extension relation is between object types or between pointer to object types; there is no extension relation between a pointer to object and a object type or between a object and a pointer to object type.
3.11
Assignment compatible
3.11.1
An expression e of type T
e is assignment compatible with a variable v of type T
v if one of the following conditions hold:
3.11.1.2
T
e and T
v are numeric types and T
v includes T
e;
3.11.1.3
T
e and T
v are pointer types and T
e is a
type extension of T
v or the pointers have
equal base types;
3.11.1.4
T
e and T
v are non-open array types with the same length and have
equal base types;
3.11.1.5
T
v is a pointer to a one dimensional open array, T
e is a pointer to any one or more dimensional array, and their element types are
equal;
3.11.1.6
T
v is a pointer to a record T
R and T
e is a pointer to a record the first field of which is of type T
R, or is again a record the first field of which is of type T
R.
3.11.1.7
T
e and T
v are object types and T
e is a
type extension of T
v and the dynamic type of v is T
v
3.11.1.8
T
v is a pointer or a procedure type and
e is NIL;
3.11.1.9
T
e and T
v are procedure types whose formal parameters
match;
3.11.1.10
T
e and T
v are pointer types and T
v is a pointer to ANY;
3.11.1.11
T
v is an enumeration type and
e is a valid element of the enumeration;
3.11.1.12
T
v is a non-open array of CHAR, T
e is a string literal, or an open array of CHAR;
3.11.1.13
T
v is a procedure type and
e is the name of a procedure whose formal parameters
match those of T
v;
3.11.1.14
T
v is a signed integer and
e is an unsigned integer constant, and T
v includes the smallest integer type necessary to represent
e;
3.11.1.15
T
v is an interface type and T
e is a pointer to a record or object type, and their bound procedures
satisfy (see
2.6.11 Interface types) the interface type.
3.12
Parameter compatible
3.12.1
An actual parameter
a of type T
a is parameter compatible with a formal parameter
f of type T
f if
3.12.1.2
T
f is a pointer to an open array of CHAR,
f is CONST, and
a is string literal, or
3.12.1.3
T
a is
assignment compatible with T
f
3.13
Expression compatible
3.13.1
For a given operator, the types of its operands are expression compatible if they conform to the following table (which shows also the result type of the expression). CHAR arrays that are to be compared must contain 0X as a terminator.
3.13.2
|
operator |
first operand |
second operand |
result type |
+ - * |
signed integer |
signed integer |
smallest signed integer type including both operands |
|
unsigned integer |
unsigned integer |
smallest unsigned integer type including both operands |
|
floating point |
floating point |
smallest floating point type including both operands |
/ |
numeric |
numeric |
smallest floating point type type including both operands |
+ - * / |
SET |
SET |
SET |
DIV MOD |
signed integer |
signed integer |
smallest signed integer type type including both operands |
|
unsigned integer |
unsigned integer |
smallest unsigned integer type type including both operands |
OR & ~ |
BOOLEAN |
BOOLEAN |
BOOLEAN |
= # < ⇐ > >= |
signed integer |
signed integer |
BOOLEAN |
|
unsignednteger |
unsigned integer |
BOOLEAN |
|
floating point |
floating point |
BOOLEAN |
|
CHAR |
CHAR |
BOOLEAN |
|
CHAR array, string |
CHAR array, string |
BOOLEAN |
= # |
BOOLEAN |
BOOLEAN |
BOOLEAN |
|
SET |
SET |
BOOLEAN |
|
NIL, pointer type |
NIL, pointer type |
BOOLEAN |
|
procedure type, NIL |
procedure type, NIL |
BOOLEAN |
IN |
integer |
SET |
BOOLEAN |
3.14
Matching formal parameter lists
3.14.1
Two formal parameter lists match if
3.14.1.1
they have the same number of parameters, and
3.14.1.2
parameters at corresponding positions have
equal types, and
3.14.1.3
parameters at corresponding positions are both either CONST.
3.15
Matching result types
3.15.1
The result types of two procedures match if they are either of
equal type or none.
4
Complete Micron Syntax
4.1↳
ident = ( letter | '_' ) { letter | digit | '_' }
letter = 'A' ... 'Z' | 'a' ... 'z'
digit = '0' ... '9'
4.2↳
number = integer | real
integer = (digit {digit|'_'} ['O'|'B'] | digit {hexDigit|'_'} 'H')
['U'|'U8'|'U16'|'U32'|'U64'|'I'|'I8'|'I16'|'I32'|'I64']
real = digit {digit|'_'} '.' {digit|'_'} [Exponent]
Exponent = ('E' | 'D' | 'F' ) ['+' | '-'] digit {digit}
hexDigit = digit | 'A' ... 'F'
digit = '0' ... '9'
4.3↳
character = digit {hexDigit} ('X' | 'x')
4.4↳
string = ''' {character} ''' | '"' {character} '"'
4.5↳
qualident = [ident '.'] ident
identdef = ident ['*' | '-']
4.6↳
ConstDeclaration = identdef '=' ConstExpression
ConstExpression = expression
4.7↳
TypeDeclaration = identdef '=' type
type = NamedType | ArrayType | RecordType | PointerType | ProcedureType
| enumeration | ObjectType | InterfaceType
NamedType = qualident
4.8↳
ArrayType = ARRAY [ length ] OF type | '[' [ length ] ']' type
length = ConstExpression | VAR varlength
varlength = expression
4.9↳
RecordType = RECORD [FixedPart][VariantPart] END
FixedPart = FieldList { [';'] FieldList}
FieldList = IdentList ':' type [ BITS integer ]
| '..' BITS integer
| INLINE identdef ':' type
VariantPart = CASE { ['|'] [INLINE] identdef ':' type }
IdentList = identdef { [','] identdef }
4.10↳
ObjectType = OBJECT ['(' BaseType ')'] { IdentList ':' type [ ';' ] } END
BaseType = NamedType
4.11↳
PointerType = ( POINTER TO | '^' ) type
4.12↳
ProcedureType = PROCEDURE [FormalParameters]
4.13↳
ProcedureType = ( PROCEDURE | PROC ) '(' ( POINTER | '^' ) ')' [FormalParameters]
4.14↳
InterfaceType = INTERFACE {InterfaceProc} END
InterfaceProc = [PROCEDURE | PROC] identdef [FormalParameters] [';']
4.15↳
enumeration ::= '(' constEnum ')'
constEnum ::= ident [ '=' ConstExpression ] { [','] ident }
4.16↳
VariableDeclaration = IdentList ':' type
4.17↳
designator = qualident {selector}
selector = '.' ident | '[' expression ']' | '^' | '(' qualident ')'
ExpList = expression {[','] expression}
4.18↳
expression = SimpleExpression [ relation SimpleExpression ]
relation = '=' | '#' | '<' | '<=' | '>' | '>=' | IN
SimpleExpression = ['+' | '-'] term { AddOperator term }
AddOperator = '+' | '-' | OR
term = factor {MulOperator factor}
MulOperator = '*' | '/' | DIV | MOD | '&' | AND
literal = number | string | hexstring | hexchar
| NIL | TRUE | FALSE | set
factor = literal | designator [ActualParameters]
| '(' expression ')' | ('~'|NOT) factor
| '@' designator | constructor
ActualParameters = '(' [ ExpList ] ')'
set = '{' [ element {[','] element} ] '}'
element = expression ['..' expression]
4.19↳
FunctionCall = designator ActualParameters
4.20↳
constructor = ['@'][NamedType] '{' [ component {[','] component} ] '}'
component = ident ':' expression
| '[' ConstExpression ']' ':' expression
| expression ['..' expression]
4.21↳
hexstring = '$' {hexDigit} '$'
4.22↳
statement = [ assignment | ProcedureCall | IfStatement
| CaseStatement | WithStatement | LoopStatement
| ExitStatement | GotoStatement | gotoLabel
| ReturnStatement | RepeatStatement | ForStatement ]
4.23↳
StatementSequence = statement { [";"] statement}
4.24↳
assignment = designator ':=' expression
4.25↳
ProcedureCall = designator [ ActualParameters ]
4.26↳
IfStatement = IF expression THEN StatementSequence
{ElsifStatement} [ElseStatement] END
ElsifStatement = ELSIF expression THEN StatementSequence
ElseStatement = ELSE StatementSequence
4.27↳
CaseStatement = CASE expression OF ['|'] Case { '|' Case }
[ ELSE StatementSequence ] END
Case = [ CaseLabelList ':' StatementSequence ]
CaseLabelList = LabelRange { [','] LabelRange }
LabelRange = label [ '..' label ]
label = ConstExpression
4.28↳
WhileStatement = WHILE expression DO StatementSequence END
4.29↳
RepeatStatement = REPEAT StatementSequence UNTIL expression
4.30↳
ForStatement = FOR ident ':=' expression TO expression
[BY ConstExpression]
DO StatementSequence END
4.31↳
LoopStatement = LOOP StatementSequence END
ExitStatement = EXIT
4.32↳
ReturnStatement = RETURN [ expression ]
4.33↳
ExitStatement = EXIT
4.34↳
GotoStatement = 'GOTO' ident
gotoLabel = ident ':'
4.35↳
ProcedureDeclaration = ProcedureHeading ( [ INLINE | INVAR ] [';'] ProcedureBody
| [';'] EXTERN ['C' [string]] )
| ForwardDeclaration
ProcedureHeading = ( PROCEDURE | PROC ) identdef [ FormalParameters ]
ForwardDeclaration = ( PROCEDURE | PROC ) '^' identdef [ FormalParameters ]
ProcedureBody = DeclarationSequence BEGIN StatementSequence
[FINALLY StatementSequence] END ident
DeclarationSequence = { CONST { ConstDeclaration [';'] }
| TYPE { TypeDeclaration [';']
| VAR { VariableDeclaration [';'] }
| ProcedureDeclaration [';'] }
4.36↳
FormalParameters = '(' [ FPSection { [';'] FPSection } ] ')'
[ ':' ReturnType ]
ReturnType = [ POINTER TO | '^' ] NamedType
FPSection = [CONST] ident { [','] ident } ':' FormalType
FormalType = type
4.37↳
ProcedureHeading = ( PROCEDURE | PROC ) [Receiver] identdef [ FormalParameters ]
ForwardDeclaration = ( PROCEDURE | PROC ) '^' [Receiver] identdef [ FormalParameters ]
Receiver = '(' ReceiverParam ':' ReceiverType ')' | ReceiverType '.'
ReceiverParam = ident
ReceiverType = ident
4.38↳
module = MODULE ident [ MetaParams ] [';']
{ ImportList | WhereDecls | DeclarationSequence }
[ BEGIN StatementSequence [FINALLY StatementSequence] ]
END ident ['.']
ImportList = IMPORT import { [','] import } [';']
import = [ ident ':=' ] ImportPath ident [ MetaActuals ]
ImportPath = { ident '.' }
4.39↳
MetaParams = '(' MetaSection { [';'] MetaSection } ')'
MetaSection = ['CONST' | 'TYPE'] ident { [','] ident }
WhereDecls = WHERE { WhereDeclaration [';'] }
WhereDeclaration = ident ':' type
MetaActuals = '(' ConstExpression { [','] ConstExpression } ')'
4.40↳
directive = '<*' ident ( '+' | '-' ) '*>'
4.41↳
directive = '<*' [ scIf | scElsif | scElse | scEnd ] '*>'
scIf = IF scExpr THEN
scElsif = ELSIF condition THEN
scElse = ELSE
scEnd = END
condition = scTerm { OR scTerm }
scTerm = scFactor {'&' scFactor}
scFactor = ident | '(' condition ')' | '~' scFactor
5
Predeclared Types
5.1.1
Opaque type only usable as a pointer base type which is assignment compatible with all pointer types, like void* in C.
5.3
BYTE, UINT8, UINT16, UINT32, UINT64, U8, U16, U32, U64
5.5
INT8, INT16, INT32, INT64, SHORTINT, INTEGER, LONGINT, I8, I16, I32, I64
5.6
REAL, LONGREAL, F32, F64
6
Predeclared Procedure Reference
6.1
Predeclared function procedures
6.1.1
|
Name |
Argument type |
Result type |
Function |
ABS(x) |
numeric type |
type of x |
absolute value |
CAP(x) |
CHAR |
CHAR |
corresponding capital letter (only for the ASCII subset of the CHAR type) |
BITAND(x,y) |
x, y: UINT32 or UINT64 |
UINT32 or UINT64 |
bitwise AND; result is UINT64 if x or y is UINT64, else UINT32 |
BITASR(x,n) |
x: UINT32 or UINT64, n: UINT32 |
UINT32 or UINT64 |
arithmetic shift right by n bits, where n >= 0 and n < SIZE(x)*8; result is UINT64 if x is UINT64, else UINT32 |
BITNOT(x) |
x: UINT32 or UINT64 |
UNT32 or UINT64 |
bitwise NOT; result is UINT64 if x or y is UINT64, else UINT32 |
BITOR(x,y) |
x, y: UINT32 or UINT64 |
UINT32 or UINT64 |
bitwise OR; result is UINT64 if x or y is UINT64, else UINT32 |
BITS(x) |
x: UINT32 |
SET |
set corresponding to the integer; the first element corresponds to the least significant digit of the integer and the last element to the most significant digit. |
BITSHL(x,n) |
x: UINT32 or UINT64, n: UINT32 |
UINT32 or UINT64 |
logical shift left by n bits, where n >= 0 and n < SIZE(x)*8; result is UINT64 if x is UINT64, else UINT32 |
BITSHR(x,n) |
x: UINT32 or UINT64, n: UINT32 |
UINT32 or UINT64 |
logical shift right by n bits, where n >= 0 and n < SIZE(x)*8; result is UINT64 if x is UINT64, else UINT32 |
BITXOR(x,y) |
x, y: UINT32 or UINT64 |
UINT32 or UINT64 |
bitwise XOR; result is UINT64 if x or y is UINT64, else UINT32 |
CAST(T,x) |
T:enumeration type x:ordinal number |
enumeration type |
the enum item with the ordinal number x; halt if no match |
|
T,x: integer type |
T |
convert integer types; re-interprets the raw bytes as T, with truncation or sign/zero extension as required. |
CHR(x) |
integer type |
CHAR |
Latin-1 character with ordinal number x |
DEFAULT(T) |
T = basic type |
T |
zero for numeric and character types, false for boolean, empty set |
|
T = enumeration type |
T |
same as MIN(T) |
|
T = pointer/proc type |
T |
nil |
|
T = record/object/array type |
T |
all fields/elements set to their DEFAULT type |
FLOOR(x) |
x: REAL or LONGREAL |
INT32 or INT64 |
largest integer not greater than x; result is INT64 if x is LONGREAL, else INT32 |
FLT(x) |
x: INT32 or INT64 |
REAL or LONGREAL |
Convert integer to real type; result is LONGREAL if x was INT64, else REAL, accepting potential loss of information |
|
x: UINT32 or UINT64 |
REAL or LONGREAL |
Convert integer to real type; result is LONGREAL if x was UINT64, else REAL, accepting potential loss of information |
GETENV(T,n) |
T: basic, enumeration or char array type |
T |
get the value of the environment variable with name n or the default value |
|
n: name string |
|
|
GETENV(T,n,d) |
d: default of T |
T |
like GETENV(T,n), but with explicit default value if not set |
LEN(v) |
v: non-open array |
UINT32 |
length of array v |
LONG(x) |
x: INT8 or UINT8 |
INT16 or UINT16 |
identity (with sign extension in case of signed types) |
|
x: INT16 or UINT16 |
INT32 or UINT32 |
dito |
|
x: INT32 or UINT32 |
INT64 or UINT64 |
dito |
|
x: REAL |
LONGREAL |
identity |
MAX(T) |
T = basic type |
T |
maximum value of type T |
|
T = SET |
INT32 |
maximum element of a set |
|
T = enumeration type |
T |
last element of the enumeration |
MAX(x,y) |
x,y: numeric type |
numeric type |
greater of x and y, returns smallest numeric type including both arguments |
|
x,y: character type |
character type |
greater of x and y, returns smallest character type including both arguments |
MIN(T) |
T = basic type |
T |
minimum value of type T |
|
T = SET |
UINT32 |
0 |
|
T = enumeration type |
T |
first element of the enumeration |
MIN(x,y) |
x,y: numeric type |
numeric type |
smaller of x and y, returns smallest numeric type including both arguments |
|
x,y: character type |
character type |
smaller of x and y, returns smallest character type including both arguments |
ODD(x) |
integer type |
BOOLEAN |
x MOD 2 = 1 |
ORD(x) |
x: CHAR |
BYTE |
ordinal number of x |
|
x: enumeration type |
UINT32 |
ordinal number of the given identifier |
|
x: BOOLEAN |
BYTE |
TRUE = 1, FALSE = 0 |
|
x: set type |
UINT32 |
number representing the set; the first element corresponds to the least significant digit of the number and the last element to the most significant digit. |
SHORT(x) |
x: INT64 or UINT64 |
INT32 or UINT32 |
truncation to modulo 2^n, where n is the bit width of the target type; negative sign is only preserved if the value fits the range of the signed target type. |
|
x: INT32 or UINT32 |
INT16 or UINT16 |
dito |
|
x: INT16 or UINT16 |
INT8 or UINT8 |
dito |
|
x: LONGREAL |
REAL |
identity with loss of precision |
SIGNED(x) |
x: unsigned integer T |
signed integer T |
interprets the raw bytes of x as signed integer of the same byte width |
SIZE(T) |
any type |
UINT32 |
number of bytes required by T |
STRLEN(s) |
s: array of char |
UINT32 |
dynamic length of the string up to and not including the terminating 0X |
|
s: string literal |
|
|
UNSIGNED(x) |
x: signed integer T |
unsigned integer T |
interprets the raw bytes of x as unsigned integer of the same byte width |
6.2
Predeclared proper procedures
6.2.1
|
Name |
Argument types |
Function |
ASSERT(x) |
x: Boolean expression |
terminate program execution if not x |
ASSERT(x, n) |
x: Boolean expression |
terminate program execution if not x |
|
n: integer constant |
|
DEC(v) |
integer type |
v := v - 1 |
|
const enumeration type |
previous ident in enumeration |
|
pointer to T |
decrement address by size of T |
DEC(v, n) |
v, n: integer type |
v := v - n |
|
v: pointer to T |
decrement address by n times size of T |
DISPOSE(p) |
p: pointer |
free the memory allocated before using NEW() |
EXCL(v, x) |
v: SET; x: integer type |
v := v - {x} |
HALT(n) |
integer constant |
terminate program execution |
INC(v) |
integer type |
v := v + 1 |
|
const enumeration type |
next ident in enumeration |
|
pointer to T |
increment address by size of T |
INC(v, n) |
v, n: integer type |
v := v + n |
|
v: pointer to T |
increment address by n times size of T |
INCL(v, x) |
v: SET; x: integer type |
v := v + {x} |
NEW(p) |
p: pointer to T |
allocate p^ of type T, uninitialized. |
NEW(p,n) |
p: pointer to open array |
allocate p^ with length n, uninitialized. |
|
n: integer type |
|
PCALL(e,p,a0,…,an) |
e: POINTER TO ANY; p: proper procedure type; ai: actual parameters |
call procedure type p with arguments a0…an corresponding to the parameter list of p; e is set to nil in the normal case, and to the pointer passed to RAISE() otherwise |
PRINT(a) |
a: basic type, string or char array |
prints the argument to standard output, not terminating the line; for debugging and logging purpose only |
PRINTLN(a) |
a: basic type, string or char array |
prints the argument to the standard output and terminates the line; for debugging and logging purpose only |
RAISE(e) |
e: POINTER TO ANY |
terminates the last protected function called and returns e as the exception value; RAISE() never returns |
SETENV(n,v) |
n: name string, v: basic or const enum type or array of char |
set the environment value |
7
Language levels
7.2
The Micron language explicitly defines different language levels, starting at level 0 with a minimal language and adding more features with each level.
7.3
Level 0 is the minimal core language supposed to work in an environment where there is no stack and no heap. All procedures are implicitly INLINE and optionally INVAR. EXTERN is not supported. Recursion is not supported. NEW and DISPOSE are not supported. PCALL and RAISE are not supported. VLA not supported.
7.4
Level 1 in addition supports normal procedures and recursion, and FINALLY, and VLA, and INTERFACE, bound procedures
7.5
Level 2 in addition supports heap allocation and deallocation.
7.6
Level 3 in addition supports object types, type case and the IS operation.
7.7
Level 4 in addition supports garbage collection of records, objects or arrays allocated with GCNEW().
7.7.1
We assume a non-moving GC, i.e. pointers are guaranteed to remain stable even after GC runs
7.7.2
We assume that the GC correctly traces interior pointers
7.8
TODO: support PCALL and RAISE not below level 2 (?) and only if explicitly enabled
7.9
TODO: the inverse approach where we remove the unsafe, low-level features of the language, which ends up as an alternative Oberon+ which we could call "Macron" ;-) or better "Helion" or "Aeon"
7.9.1
There is no Helion programming language so far, but Aeon and Macron are in use.
8
References