Zinc Reference Manual
Copyright © 2004 Marc Kerbiquet
Abstract
Zinc is a low level programming language to write small and fast programs.Table of Contents
List of Tables
- 3.1. Basic Types
- 4.1. String Escape Characters
- 4.2. Escape Characters
- 4.3. Bitwise Logical Operators
- 6.1. Operator Names
Zinc is a low level programming language to write small and fast programs. It is a low level language between assembler, C and C++ with a syntax somewhat similar to Ruby.
Important Notice / Disclaimer
This programming language is still experimental, it contains some inconsistencies and missing features that are subject to change in the future. It should not be used for something else than the text editor 'Code Browser'. This documentation is provided only to understand or edit the source code of Code Browser.
Table of Contents
New lines are relevants and are equivalents to semicolons. The following lines:
instr1 instr2 instr3
are equivalents to:
instr1 ; instr2 ; instr3
New lines are ignored after some characters:
( , : = + - * ...
Empty lines are ignored
Single line comments starts with two slashes.
// this is a comment
Multiple line comments start with a /* and finish with */. Nested comments are not permited.
/* This is another comment on several lines */
An identifier is defined as follow:
A word is a sequence of letters, underscores or digits and cannot start with a digit. Words are case sensitive.
abc abc12 ab1c2d
A number is a sequence of digits.
1 23
A blank is a space or a tabulation
An identifier is a sequence of words and numbers separated by blanks. The number of blanks is not relevant. The first item of an identifier cannot be a number or a reserved keyword.
var1 var 1 yet another variable
List of reserved keywords:
def equ func struct union enum typedef sizeof public private const import if elsif while switch case else end return repeat
Special Identifiers
The language has some builtin identifiers that are not reserved word such as nil, true and false. It is possible to use them in different identifiers or with different signature:
true 1 // a valid identifier nil (-> void) // a valid function
Table of Contents
Types are not defined in the same namespace as expressions, so types are not reserved names.
There can be inheritance between types, for instance:
- a structure can inherit from another structure
- a signed integer inherits from an unsigned integer
Pointer to a type:
-> type
Pointers preserves inheritance: if t2 inherits from t1, -> t2 intherits from -> t1.
[n] type
n is a positive integer.
- [*] type is an alias for [0] type.
- [] type is an alias for -> [*] type, i.e. a pointer to an undefined size array of type items.
Examples:
[10] int // an array of 10 integers [10] [20] int // a two dimension array of integer [] char // a pointer to an array of character
[n2] t2 inherits from [n1] t1 if and only if n1=n2 and t2 inherits from t1.
{type1, type2, ...} return-type
This type is a pointer, but the -> is implicit.
Examples:
{int, int} int {int} void // A function without argument: {} int
Anonymous structures (and unions) are defined in the same way as named structures but wihout the name:
def origin: struct x: int y: int end
Attributes of anonymous structures are always private.
Anonymous enums are defined in the same way as regular enums but wihout the name:
def category: enum rock pop folk classic end
Values of anonymous enums are always private.
Table of Contents
- Decimals: (+|-)?[0-9]+
- Hexadecimals: (+|-)?0x[0-9a-fA-F]+
A literal number is 32 bit signed. A cast can be used to get a different type:
123:byte // unsigned byte 12345:short // signed short 0x5f:byte // unsigned byte
"aaaa"
Table 4.1. String Escape Characters
Character | Description |
---|---|
\a | Bell (7) |
\b | Backspace (8) |
\f | Form Feed (12) |
\n | Line Feed (10) |
\r | Cariage Return (13) |
\t | Tab (9) |
\v | Vertical Tab (11) |
\w | Whitespace (32) |
\" | double quotes |
If two literal strings are adjacents, they can be concatened: "abc" "def" is same as "abcdef". It can be useful for long string, since you can join two lines with the '\' character:
"abc" \ "def"
- $c for regular characters
- \c for escape characters, one of:
- \nn for numeric characters
const <pointer type> (<list of arguments>)
<pointer type> is a either a pointer to an array or a pointer to a structure. arguments must match the type. Each argument must be a constant.
^ <function name> [(<list of types>)]
<function name> is the name of the function followed by the types of arguments between parenthesis. Do not put parenthesis if the function has no parameter.
++ --
If suffixed, the expression evaluates to value before increment or decrement. Otherwise it evaluates to value after the operation.
left = right
Assigns the right expression to the left expression.
left := fn (arg1, ...)
Special operator: the right expression must be a function with 'returned argument', the left expression must be a pointer that is passed to the function as the returned argument.
left OP= right
is equivalent to:
left = left OP right
boolean exp -> e1, e2
If the boolean expression is evaluated to true, the expression is evaluated to e1, e2 otherwise.
e[n]
- e is a pointer to an array
- n is an integer
e[] is an alias for e[0]
Notes:
- e can also be of another type if the operator is redefined.
- the operator can take several argument of any type when redefined:
e[e1, e2, ...]
e {e1, e2, ...}
- e is a pointer to a function
- e1, e2, ... are the arguments
When there is no argument:
e {}
Functions, equates and iterators without arguments:
f
Functions, equates and iterators with arguments:
f (arg1, arg2, ...)
Iterator with block without parameters:
f (arg1, arg2, ...) ? instr1 instr2 ... end
Iterator with block with parameters:
f (arg1, arg2, ...) ? x1, x2, ... instr1 instr2 ... end
if cond1 ... elsif cond2 ... elsif ... ... else ... end
Where:
- cond1, cond2 are boolean expressions.
- elsif and else statements are optionals
while cond ... end
Repeat the block as long as the boolean expression cond evaluates to true. The break statement can interrupt the loop.
repeat ... end
A synonym for while true, and endless loop can that only be interrupted by a break.
Table of Contents
The order of declarations is not important except:
- local variables must be declared before use
- when size of a type must be known Ok: struct aa x: bb // pointer: size known (4 bytes) end struct bb ... end Error: struct aa x: local bb // size of bb must be known end struct bb ... end
Each declaration must have a unique name+signature, but the name itself has not to be unique.
Variables are locals or globals. A variable is a value that can be stored in a machine register.
def name : type
if the type is a structure or an array, a variable is always a constant pointer to the element.
def name = e1
The type of the variable if the type of the expression.
def name := fn (e1, e2, ...)
name is a constant reference to a local structure initialized with the function. The type is the type of the returned argument of the function.
def name := "a string" def name := const type (...)
Constants are not lvalues: they cannot be modified. They are constant effective address evaluable by the compiler and are not stored in registers.
Define a function:
func name (arg1: t1, arg2: t2, ...) [: return-type] ... end
The return type is optional. In fact it is required only when there is a circular reference with the function. This can happen when
- the function is recursively called.
- when the function or a callee have a reference to it.
Types of arguments cannot be structures or array, they must be pointers or basic types, i.e. values that can be stored in registers.
To return a value or simply return from the function, just use
return [expr]
There is an alternative syntax to define functions:
func name (arg1: t1, arg2: t2, ...) -> (return-arg: return-type) ... end
It must be a void function, the return-arg behaves like other arguments except that its type must be a pointer to a structure or an array. This kind of function differs by its calling method:
x := name (arg1, arg2, ...)
x is passed as the returned argument.def y := name (arg1, arg2, ...)
A structure or an array is created with the constant reference y pointing to it. then y is passed as the returned argument.name (arg1, arg2, ...)
A structure or an array is created and its effective address is passed as the returned argument and the expression evaluates to it.
There is special function names to redefine operators
Table 6.1. Operator Names
Name | Operation |
---|---|
@add | + |
@sub | - |
@mul | * |
@div | / |
@mod | % |
@shl | << |
@shr | >> |
@equal | == |
@not equal | <> |
@less or equal | <= |
@less than | < |
@greater or equal | >= |
@greater than | > |
@and | & |
@xor | ^ |
@or | | |
@andand | && |
@oror | || |
@assign add | += |
@assign sub | -= |
@assign mul | *= |
@assign div | /= |
@assign mod | %= |
@assign shl | <<= |
@assign shr | >>= |
@assign and | &= |
@assign or | |= |
@assign xor | ^= |
@assign not | ~! |
@assign neg | -! |
@not | ~ |
@preinc | prefixed ++ |
@predec | prefixed -- |
@postinc | postfixed ++ |
@postdec | postfixed -- |
@at | indexed access |
Equates are for macro expansion. There is two kind of equates:
- simple expression substitution
- iterators
equ name (arg1: t1, arg2: t2, ...) = expr
expr is the expression that will replace the equate call.
expr is fully analysed when the equate is defined:
- no need of parenthesis as in C
- function calls in expr are frozen, no matter what the real types of arguments are.
- epxr can use private definition even if the equate is public.
Arguments are expanded as is in the expression, if an argument is a function call, the call will be performed for each occurrence of the argument in the expression. If an argument is not used in the expression, it won't be evaluated. Equates must be used carefully especially when switching a function to an equate or vice-versa.
If an expression passed to the equate is an lvalue, the associate argument becomes an lvalue.
This special kind of equate is called iterator because its main use is for iterators but it can be used for anything else such as allocating and freeing a resource in one step.
equ name (arg1: t1, arg2: t2, ...) ... end
There can be zero, one or more occurrences of yield in the body of the iterator.
-
0 yield: the iterator does not have a block
equ print twice (s: string) puts (s) puts (s) end print twice ("hello world")
-
1 or more yield: the iterator has a block with or without
parameters. If there is more than one yield, all must have
a compatible signature.
equ loop (n: int) def i = 1 while i <= n yield (i) i += 1 end end loop (10) ? j printf("%d\n", j) end
Iterators are still limited now: it is not possible to break an iteration.
Define a structure:
struct name [:parent name] attr1: t1 attr2: t2 public attr3: t3 attr4: t4 end private attr5: t5 public attr6: t6 end
This creates a structure named local name and an alias named name that is a pointer to this structure.
For each attribute attr of the structure name, a declaration is created to read and modifiy it:
attr (t: name)
When the exposition of an attribute is not specified, it inherits the exposition of the structure.
A structure can inherit from another structure, all attributes are inherited: it is equivalent to add them at the beginning. A pointer to a structure is also considered as a pointer to its parent structure.
A pointer to a structure can be converted to a pointer to its parent structure using super(x).
Define an enumeration
enum name item1 [= value1] item2 [= value2] ... end
An enumeration is an integer.
Each item can have a value assigned to it. If the value is not specified, it gets the value of its predecessor plus one or zero for the first item.