Calqit language specification

Keywords (as of Feb 2023; switch is planned)
break clear const continue else end for funcdec function global if persistent return unless use while

Statements finish with either new line or a semi-colon or a double-semicolon (;;). A double-semicolon adds a display instruction to show the outcome. A condition (in if, while, unless statements) terminates with either a colon or a line break.

Comments
A single hash # means everything to the end of the line is a comment. A double hash ## at the start of a line means everything until the next ## at the start of a line is a (multi-line) comment. A line beginning '##end_input' means ignore the rest of the file.

Braces and indentation
You can write Python-style:
if a > 1
   dothis
   andthis
else
   dothat
disp('done')   
or use braces, C-style:
if a > 1
{ 
   dothis
   andthis
}
else
{
   dothat
}
disp('done')   
The C-style allows you to write more succinctly, e.g.:
if a > 1: { dothis; andthis } else dothat

Iteration
while condition: statement
for i = expr; statement
for x in values; statement
for i,x in values; statement
In the last form, i starts at 1 and is incremented with each iteration

Assignment

Examples:
x = 1  
x.2 = 1   # assign to the second element of x
x += 3    # add 3 to all the elements of x
          # (also -= *= /= ,= ;=)
[sn,cs,tn] = [sin t, cos t, tan t]  # multiple assignment
++x       # increment
--x       # decrement 
s = 'a string'
s = "another string"

Operators

level  operators                  comments
-1   ++  --  !  '  ~  unary-  ??  inc, dec, factorial, transpose, not)
0    ^  :  ^^                     power, sequence, matrix power 
1    *  .  /  _/  //  @  vec      (see below)
2    +  -  mod  @+  @-
3    @&                           bitand
4    @|  @!                       bitor, bitxor  
5    <  <=  ==  ===  ~=  >=  >  % comparison and whole-division
6    &&  &                        and
7    ||  |  xor  ?  ::            or, xor, query
8    ,                            next item or column
9    ;                            next row
10   0
11    <<  >>  ;;                  input, output, display
12   /.  ...                      such that, continuation
*    multiplication (element-wise)
.    (preceded by a space) inner product
/    division
_/   integer division
//   divide item by everything to the right (e.g.  1+2 // 3+4  evaluates to 9/7)
@    tensor product
vec  vector product
%    divide left expr by right expr (e.g.  1+2 % 3+4  evaluates to 3/7)

Some special operators

The operator === means 'equal in all respects'. For example, use this to compare a vector of unknown length with another vector.

The operator /. can be read 'such that'. The statement to the right of /. is evaluated first, before the statement to the left.

The query operator is like in C++ but you must use a double-colon (::) to separate the expressions:

s = (a>2)? 'bigger' :: 'smaller'

Implied operators

If the compiler determines that an operator ought to be present between two items and none has been given, then * (multiplication) is assumed unless the expression is in square brackets, in which case a comma is assumed. Thus the following pairs give two ways to write the same expression

2x+1,      2*x+1       # the first version is clear and more convenient
[a b c],   [a,b,c]     # the first version is moderately more convenient
[a (b c)], [a, b*c]    # putting the explicit operator is clearer here

Indexing

The following pairs are synonyms:

m[i],   m.i     # i'th element
m[i,:], m\i     # i'th row
m[:,i], m\\i    # i'th column
You can index by element number or by boolean mask.

Types

Calqit works with 4 'base-types': integer, double, complex, list. All are matrix-like. Within integer and double types there are sub-types char and boolean. A 'list' is a matrix whose elements can be of any type (like cell array in Matlab).

The base-type of any given variable is set at compile-time, so most type checking happens at compile-time. If the compiler cannot determine a type (for example, because it is an element in the list) then is it set as 'unknown' and a type-check will happen at run-time.

A variable is declared (implicitly) and has its type set by the first assignment statement the compiler encounters for the variable, or, in the case of function arguments, by the function declaration. To declare a list, use one of the built-in functions such as list, split, dir, or set it equal to an existing list. To declare a complex number use complex or the built-in constants 1i, 0i, 1j, 0j. Examples:

x = 2
z = 2 + 1i
stuff = list('a',1:3)

Calqit only handles matrices up to dimension 2 in the first instance. You can get some of the functionality of higher-dimensional arrays by using a list of matrices.

User-defined functions

There are two basic forms:

f(x) := expr
f(x) := { statement; statement; expr }
and
function [output] = name(arg,arg)
   body
end
You can have multiple input and output arguments. Arguments of type complex or list have to be announced as such, using * or [], as in:
function [out1, out2*] = name(arg1, arg2[], arg3*)
   body
end
Here arg1 is ordinary, arg2 is a list, arg3 is complex, out1 is ordinary and out2 is complex. A function does not necessarily require a return statement since the return value is specified Matlab-style by providing an output argument in the function declaration. However you can optionally do it C-style, so the following are both valid:
function [s] = innerProdv1(g,a,b)
   s = a' . g . b
end

function innerProdv2(g,a,b)
   return a' . g . b
end

A function must be declared before it is called. Usually this is done by providing the complete declaration and definition before invoking the function. However when this is inconvenient or impossible (e.g. when a pair of functions call each other) then use the keyword funcdec to declare a function. The syntax is like function but there is no function body and no end.

Doc strings

It is optional, but good practice, to provide a comment with every user-defined function, explaining what the function does and how it is used. The interpreter will search for such a comment if the user poses the query '?func'. Calqit searches the script in the following order:
1. Multiple lines bracketed by double-hashes (##) immediately before the function definition.
2. A single-# comment immediately after the function definition
2. A single-# comment immediately before the function definition

Lambda-functions (small anonymous functions)

When calling a built-in function such as plot or fmin or integ you can specify a function by using a semicolon in the argument list. The syntax is

func(expr; varname, arg)
where there may be further arguments. The logic can be deduced from the following examples:
plot(1-x^2/3; x, -2,5)       # a graph of 1 - x^2/3
fzero(1-x^2/3; x, [0,4])     # find the zero-crossing
y=sum( sin(k x)/k; k, 1:10 ) # sum a set of sin functions

Default argument values and pass-by-reference

You can provide default argument values much as in C++, but use a colon not an equals sign. At time of writing (version 5.3a) only scalar double or integer default values are allowed.

Arguments are ordinarily passed by value (i.e. the functionality is equivalent to this) but you can specify pass-by-reference by inserting a ^ before the argument name in the function declaration.