This language manual describes all the syntax rules of Axiom.

Blocks

Axiom uses indentation to denote blocks (this is known as "significant whitespace"), which is similar to Python. However, unlike Python, the only valid indentation character is tab. (This is to put an end to the tabs vs spaces debate).

Comments

Comments in Axiom are denoted by # or ##, for line comments and block comments respectively.

x = 1 #this is a line comment y = 2 ## this is is a block comment ## z = ## this is also a block comment ## 3

Statements

Statements each occupy a whole line, and may be followed by an indented block. The types of statements are:

Function Statement

A function is declared using a colon (:) followed by an identifier, followed by a list of parameters in parentheses, followed by an indented block which is the function body. The list of parameters is zero or more identifiers separated by commas. It is an error to define two functions with the same name in the same scope.

However, an exception is made if the function is :main(). This is so that each source file can have its own :main() function, and still be imported into the same scope. The last definition of :main() takes precedence, and is executed automatically at runtime.

#this is a function that takes 2 parameters, a and b :some_function(a,b) #this is the function body print 'a =',a print 'b =',b print 'hello from some function!' #this is how to call the function some_function(1,'xyz')

Class Statement

A class is declared by using an amphora (@) followed by an identifier, followed by an indented block, which contains methods and static variables. Each method implicitly receives a variable named "this", which is the class instance (object).

If a method is named "new", it is considered to be the constructor, and is implicitly called when an object is instantiated. The constructor implicitly returns "this", but can return another value to indicate an error. Declaring a constructor is optional.

If a method never references the "this" variable, it is considered a static method, and can be called on the whole class rather than a class instance.

It is an error to define two classes with the same name in the same scope.

#this is a class @some_class #this is a static variable count = 0 #this is the constructor :new() this.id = count++ #this is a method :print_id() print 'id =',this.id #this is a static method :print_count() print 'There are',count,'objects total' #this instantiates the class and calls the constructor a = some_class() #this is how you call a method a.print_id() #this is how you call a static method some_class.print_count()

If Statement

An if statement is the keyword "if" followed by an expression, followed by an indented block. The block will be executed if the expression is true.

An if statement may be followed by any number of elif statements. An elif statement is the keyword "elif" followed by an expression, followed by an indented block. The block will be executed if the expression is true, and all previous if/elif expressions were false.

An if or elif statement may be followed by an else statement. An else statement is the keyword "else" followed by an indented block. The block will be executed if all previous if/elif expressions were false.

if age<16 print 'You cannot do anything.' elif age<18 print 'You can drive, but not smoke or drink.' elif age<21 print 'You can drive and smoke, but not drink.' else print 'You can drive, smoke, and drink!'

While Statement

A while statement is the keyword "while" followed by an expression, followed by an indented block. The block will run repeatedly as long as the expression is true.

#this will run forever while true print 'going!' #this will run 5 times n = 5 while n-- print 'n =',n

For Statement

A for statement is the keyword "for" followed by an identifier, followed by a comma, followed by an expression, followed by an indented block. The expression should evaluate to a number N. The the block will run N times. For each iteration, the identifier is set to a number starting from 0 and ending at N-1.

#print numbers 0 to 5 for n,5 print n #loop over elements in array a = ['a','b','c'] for n,a.len() print n,'=',a[n] #loop over pairs in object o = { x: 1, y: 2, z: 3 } for n,o.len() print o.key(n),'=',o[n]

Return Statement

A return statement is the keyword "return" optionally followed by an expression. This will end the execution of a function using the expression result as the return value. If no expression is specified, the return value is null. Note that a function body without a return statement will simply execute until the last line and return null.

:square(x) return x*x :nothing() #will return null return :nothing2() #also returns null pass

Continue Statement

A continue statement is simply the keyword "continue". This will exit the current loop iteration and move to the next iteration.

n = 5 while n-- print 'hi' continue print 'bye' #this will never happen for n,5 print 'hi' continue print 'bye' #this will never happen

Break Statement

A break statement is simply the keyword "break". This will exit the current loop iteration and end the loop.

n = 5 while n-- print 'hi' #this will only get printed once break for n,5 print 'hi' #this will only get printed once break

Pass Statement

A pass statement is simply the keyword "pass". This is used to declare an empty block.

#an empty class @nothing pass #an empty function :do_nothing() pass

Print Statement

A print statement is the keyword "print" followed by zero or more expressions, separated by commas. The results of the expressions will be printed to standard output, separated by a space, and terminated by a newline.

#print a blank line print #print one thing print 1 #print some things separated by spaces print 'hello',2,'u'

Standalone Expression

A standalone expression statement is just an expression by itself on a line. See the next section for more details on expressions.

:hello() print 'hello' #these are all standalone expressions n = 5 n++ m = n*2 hello()

Expressions

Expressions are nestable instructions, including literals, references, operations, function calls, and parentheticals. Literals are described in the next section. References are simply identifiers. Function calls are identifiers followed by a list of arguments in parentheses and separated by commas. The list of arguments is zero or more expressions separated by commas. Operations are one or more expressions combined with operators. The operators are described in a subsequent section. A parenthetical is simply an expression in parentheses, for nesting and explicitly ordering operations.

Literals and Types

A literal is a declared value with no identifier. There are literals for each of the six types: null, boolean, number, string, array, and object.

Null literals are just the keyword "null".

null

Boolean literals are either of the keywords "true" or "false".

true false

Number literals are one or more digits, optionally followed by a dot and one or more digits.

1 3.5

String literals are any characters surrounded by either a single quote (') or back tick (`). Single-quoted strings can only span one line. Back-ticked strings can span multiple lines, and the preceding indentation is ignored on each line. (Double-quoted strings are not supported. This is to put an end to the single vs double quote debate.)

'hello there' ` roses are red violets are blue `

Array literals are zero or more expressions in brackets, separated by commas.

[1,2,'a','b']

Object literals are zero or more pairs in curly braces, separated by commas. A pair is an identifier or string, followed by a colon (:), followed by an expression.

{ x: 1, 'y': 23 }

String Escapes

Special characters within strings can be escaped using the backlash (\). These characters are:

Operators

These are all the operators in order of precedence, from high to low. Operators with high precedence will be evaluated before operators with low predecence. Operators with the same precedence will be evaluated from left to right.

Most of these operators should be recognizable to anyone familiar with mathematics. However, to go over the more obscure obscure operators in detail:

These operators have special meaning for arrays:

These operators have special meaning for objects:

The multiplication (*) operator has special meaning for strings and arrays. If a number is on the right-hand side, it can be used to allocate a string or array with the memory initalized to the left-hand side.

' '*10 #allocate a blank string of length 10 'abc'*3 #allocate a string like 'abcabcabc' [0]*10 #allocate an array of zeroes of length 10 [1,2,3]*3 #allocate an array like [1,2,3,1,2,3,1,2,3]

Also the logical operators (&& and ||) exhibit "short-circuit" behavior. This means that if the result can be known from the left-hand side, the right-hand side is not evaluated.

if true || do_something() #do_something() is never called print 'true' if false && do_something() #do_something() is never called print 'this never gets printed'