Home | Overview | Download | Syntax | Math | Text | Code Samples | Forum | Contact
This overview highlights Agena's main syntax features. For more information,
please consult the Agena
Crash Course or the Primer and
Reference:
To assign the number 1 to a variable called a, type:
> a := 1; |
To print the value of a variable, simply place a colon (:) after its name.
> a: 1 |
Variables can be deleted by assigning them null or using the clear statement. The latter also performs a garbage collection.
> a := null: null > clear a; > a: null |
Agena supports both real and complex arithmetic with the + (addition), - (subtraction), * (multiplication), / (division) and ^ (exponentiation) operators:
> 1+2: 3 > exp(1+2*I): -1.1312043837568+2.4717266720048*I |
There are various other mathematical operators, just have a look at the manual.
You can enclose text in either single or double quotes.
> str := 'a string': a string |
Substrings are extracted as follows:
> str[3]: s > str[3 to 6]: stri |
Concatenation, search, and replace operations:
> str := str & ' and another
one, too.': a string and another one, too > 'another' in str: 14 > replace(str, 'and', '&'): a string & another one, too. > replace(str, 1, 'A'): A string & another one, too. > replace(str, 1, 'One'): One string & another one, too. |
There are various other string operators and functions available.
Agena has the constants true, false, and fail that represent Boolean values. fail usually indicates a failed computation. The operators <, >, =, <>, <=, and >= compare values and return either true or false. The operators and, or, not, and xor combine Boolean values.
> 1 < 2: true > true or false: true |
Tables represent complex data structures. Tables consist of zero, one or more key-value pairs: the key referring to the position of the value in the table, and the value the data itself.
> tbl := [ > 1 ~ ['a', 7.71], > 2 ~ ['b', 7.70], > 3 ~ ['c', 7.59] > ]; |
To get the subtable ['a', 7.71] indexed with key 1, and the second value 7.71 in it, input:
> tbl[1]: [a, 7.71] > tbl[1, 2]: 7.71 |
The insert statement adds values to a table.
> insert ['d', 8.01] into tbl > tbl: [[a, 7.71], [b, 7.7], [c, 7.59], [d, 8.01]] |
Alternatively, you can add values using the indexing method.
> tbl[5] := ['e', 8.04]; > tbl: [[a, 7.71], [b, 7.7], [c, 7.59], [d, 8.01], [e, 8.04]] |
Of course, values can be replaced:
> tbl[3] := ['z', -5]; > tbl: [[a, 7.71], [b, 7.7], [z, -5], [d, 8.01], [e, 8.04]] |
Another type of table is the dictionary, with the indices any kind
of data, not just positive integers. Use the tilde to separate keys and
values.
> dic:= ['donald' ~ 'duck', 'mickey'
~ 'mouse']; > dic['donald']: duck |
Sets are collections of unique items: numbers, strings, and any other data except null. Any item is stored only once and in random order.
> s := {'donald', 'mickey',
'donald'}: {donald, mickey} |
If you want to check whether 'donald' is part of the set s, just index it or use the in operator:
> s['donald']: true > s['daisy']: false > 'donald' in s: true |
The insert statement adds new values to a set, the delete statement deletes them.
> insert 'daisy' into s; > delete 'donald' from s; > s: {daisy, mickey} |
Three operators exist to conduct Cantor set operations: minus, intersect, and union.
Sequences can hold any number of items with the exception of null. All elements are indexed by integers starting from 1. Compared to tables, sequences are twice as fast when adding values. The insert, delete, the indexing and assignment statements as well as the operators previously described, do all work with sequences, too.
> s := seq(1, 1, 'donald',
true): seq(1, 1, donald, true) > s[2]: 1 > s[4] := {1, 2, 2}; > insert [1, 2, 2] into s; > s: seq(1, 1, donald, {1, 2}, [1, 2, 2]) |
Pairs hold exactly two values of any type including null and
other
pairs. You can retrieve values by indexing them or by using the left and right operators. Values can be exchanged by using indexed names.
> p := 10:11; > left(p), right(p), p[1], p[2]: 10 11 10 11 > p[1] := -10; > p: -10:11 |
Conditions can be checked with the if statement. The elif and else clauses are optional. The final fi is obligatory.
> if 1 < 2 then > print('valid') > elif 1 = 2 then > print('invalid') > else > print('invalid, too') > fi; valid |
The case statement facilitates comparing values and executing corresponding statements.
> c := 'agena'; > case c > of 'agena' then > print('Agena!') > of 'lua', 'Lua' then > print('Lua!') > else > print('Another programming language !') > esac; Agena! |
The optional onsuccess clause put right after the last elif or of condition executes statements if at least one of the conditions resulted to true.
> flag := false; > if 1 < 2 then > result := 'valid' > elif 1 = 2 then > result := 'invalid' > onsuccess > flag := true > else > result := 'invalid, too' > fi; > flag: true |
You can combine both an assignment and a condition in an if clause. In all if flavours, instead of a closing fi, you can also use the end token.
> if a := 1, a > 1 then > print('this will never be printed.') > end; > a: 1 |
A for loop iterates over statements.
It starts with an initial numeric value (defined by the from clause) and proceeds up to and including a specified numeric value (the to clause). You can define the step size with the optional by clause. The loop body concludes with either the od keyword or, alternatively, the end keyword.
The from and by clauses are
optional. If the from clause is omitted, the loop
starts 1. If the by clause is
omitted, the step size is 1. Negative step size are accepted, as well.
The current iteration value is stored to a control variable (i in this
example)
which can be used in the loop body.
> for i from 1 to 3 by 1 do > print(i, i^2, i^3) > od; 1 1 1 2 4 8 3 9 27 |
The loop's final iteration value (including the step size increment) is accessible in the enclosing code block.
> i: 4 |
You can also use the downto keyword to count down by 1 or any other positive step.
for/in loops iterate over all elements in
tables, sets, sequences, and strings.
> for i in ['Agena', 'programming',
'language'] do > print(i) > od; Agena programming language |
You can also iterate over the keys of a table or both keys and values:
> for keys i in ['donald' ~ 'duck',
'daisy' ~ 'duck'] do > print(i) > end; daisy donald > for i, j in ['donald' ~ 'duck', 'daisy' ~ 'duck'] do > print(i, j) > end; daisy duck donald duck |
A while loop first checks a condition and if it evaluates to true or any other value except
false, fail or null, it will execute the loop
body again and again as long as the
condition is met. The following statements compute the largest Fibonacci number less than 1000.
> a := 0; b := 1; > while b < 1000 do > c := b; b := a + b; a := c > od; > c: 987 |
As with if statements, you can combine an assignment and a check in the while clause. Note that in this case, the assignment is always redone with the latest value.
> i := 0.1; > while logn := ln(i), logn < -0.9 do > print(i, logn); > i +:= 0.1 > od; 0.1 -2.302585092994 0.2 -1.6094379124341 0.3 -1.2039728043259 0.4 -0.91629073187416 |
do .. as and do .. until loops evaluate their condition at the end of each iteration. This ensures the loop body will always execute at least once.
> c := 0; > do > inc c > as c < 10; > c: 10 > c := 0; > do > inc c > until c >= 10; > c: 10 |
All flavours of for loops can be
combined with a while or until
condition. The loop will continue to iterate as long as the while or until condition remains true.
> for x to 10 while ln(x) <= 1
do > print(x, ln(x)) > od; 1 0 2 0.69314718055995 |
You can also define a conditional for loop. In this case you must explicitly change the loop control variable within the loop body:
> for x := 1 while ln(x) <= 1 and x <= 10
do > print(x, ln(x++)) > od; 1 0 2 0.69314718055995 |
The next statement
immediately starts the next iteration of a for or while loop, skipping all
subsequent statements in the current iteration.
To terminate a for or while loop entirely, use the break statement. Execution will then resume at the code line immediately after the loop's end.
Finally, the redo statement restarts the current iteration of a for/to or for/in loop, the relaunch statement starts a loop all-over again.
Procedures cluster a sequence of statements into abstract units which then can be repeatedly invoked.
A procedure can call itself to generate a result. The return
statement passes the result of a computation.
> factorial := proc(n) is > # computes the factorial of an integer n > if n < 0 then return fail > elif n = 0 then return 1 > else return factorial(n-1)*n > fi > end; > factorial(4): 24 |
Local variables can be declared with the local statement.
If you want to pass a variable number of arguments, use the ? keyword in the parameter list and the procedure body to access them.
The system variable nargs contains the number of
arguments passed in a function call.
Type checking can be done within the body of a procedure with the type,
::, or :-
operators or by optionally giving the required type of an
argument in
the parameter list with the :: token. Optionally, Agena can
check the type of the return. Pass it right after the parameter list.
> f := proc(x :: number, ?) :: table
is > local result; > result := [x, nargs, ?, ?[1]]; > return result > end; > f(10, 'a string', 20): [10, 3, [a string, 20], a string] > f('a string', 20): Error in stdin: invalid type for argument #1: expected number, got string Stack traceback: stdin, in `f` stdin, at line 1 in main chunk |
Use curly brackets if you want to check for more than one type (argument or return).
zero := proc(x :: {number, complex}) ::
{number, complex} is return if type x :: number then 0 else 0 + 0*I fi end; zero(0!0): # the prettyprinter just issues 0 for 0 + 0*I 0 |
Agena remembers results if the remember function is invoked. An optional table of predefined results can be given, as well. This significantly speeds up recursively defined procedures.
> fib := proc(n) is > assume(n >= 0); > return fib(n-2) + fib(n-1) > end; > remember(fib, [0~1, 1~1]); > fib(50): 20365011074 |
You can also assign precomputed results to a procedure by using the rtable.defaults function.
Alternatively, to activate the remember functionality, place the reminisce statement immediately after the is keyword: In this case, you cannot pass defaults and you
need to
define an explicit exit condition, but it may suffice in most cases:
> fib := proc(n) is > feature reminisce; > if n in 0:1 then # n = 0 or n = 1 ? > return 1 > else > return fib(n-2) + fib(n-1) > fi > end; > fib(50): 20365011074 |
A table can be attached to a procedure with the store feature. The table is available during the whole session and you can read or write values to it in subsequent calls:
> add := proc(x) is > feature store; > if x = null then # set default value zero > store[1] := 0 > else > store[1] := store[1] + x > fi; > return store[1] > end; > add(): 0 > add(10); 10 |
There are various composite operators available for assignment: +:=, -:=, *:=, /:=, \:=, %:= and &:=. Likewise, the ++ suffix operator returns the current value of a variable and then increases it by one, and the -- suffix operator decreases it by one.
> a := 0; > a++; > a: 1 > a -:= 1; > a: 0 |
Statements that might raise an exception can be enclosed in a try clause. If an error is triggered (e.g., by an explicit call to error or another expression/function/statement), execution immediately proceeds to the relevant catch clause. If no catch clause exists, control jumps to the end of the block, bypassing any remaining statements in the try clause.
> try > for i to 3 do > print('before', i); > if i = 2 then error('oops') fi; > print('after', i) > od > catch in err then > print('error', i, err); > yrt; before 1 after 1 before 2 error 1 oops |
Alternatively, you may use the protect function along with the lasterror system variable.
allow to automatically run a procedure before quitting or restarting Agena, for example to clean up temporary files, etc. Just assign an exit handler to environ.onexit:
> environ.onexit := proc() is
print('Tschüß !') end; > bye Tschüß ! |
This even works when pressing CTRL + C in the shell.