![]() |
A quick introduction to |
![]() |
This document is a tutorial introduction to the
To start the interpretor, type mmx-light. The
instructions that you enter will be evaluated once the key [
] is pressed. Several instructions are
separated by ;. If an instruction ends by a ;,
its evaluation is not printed. To exit the session, type quit.
1+2
Mmx]
If you use the interpretor in a textual context, several shortcuts can be very usefull such as
,
for the
previous and next instruction line,
The default mode is a quiet mode. It does not print any additional information. If you want to turn the type mode on or off, use the function type_mode.
type_mode()
Mmx]
: Boolean
23432*2
Mmx]
: Int
type_mode()
Mmx]
a :=1
Mmx]
The other modes are
Mmx]
Identifiers of variables are formed using letters, digits and the special characters _ and ?. Identifiers must start with a letter or _. Identifiers should match the regular expression [a-zA-Z_]+[a-zA-Z0-9_?]*.
To assign a value to a variable, we use the operator := :
a1 := 1
Mmx]
Constant values can be assigned by the operator ==, and cannot be modified hereafter:
a1_? == 2; a1_?
Mmx]
Mmx]
The operator == will be used for instance to define functions which definition should be fixed (see section ?) or constant values.
In this section, we describe the type, which exists by default in the interpreter. For the other types provided by the extension packages, see their documentation.
The default type of an object is Generic and the corresponding variable type is Alias Generic:
a := x
Mmx]
type a
Mmx]
Mmx]
The usual boolean constant are true and false. The equality and inequality tests are = and !=.
a = a
Mmx]
a != a
Mmx]
To build boolean expression, we use the operators and, or and the negation operator ! :
a = b and a != c
Mmx]
a = b or a != c
Mmx]
!( a = b or a != c)
Mmx]
Mmx]
Strings can be braced into double quotes " ... ". Inside such a string a double quote must be blackslashed. In order to avoid blackslashing one can use the stronger string delimiters /" ... "/.
s1 := "This is a string"
Mmx]
s2 : String := "Print \"foo\" "
Mmx]
Not that in this definition, we specify that the variable s2 is of type String.
s2 := /"Print "foo" "/
Mmx]
The concatenation of strings can be done by the operator ><
s3 := s2 ><" and \"fii\" "
Mmx]
It is also possible to use the in place concatenation operator << on a variable:
s2 <</" and "fuu" "/
Mmx]
The length of a string is given by the operator #
#s1
Mmx]
contains? (s2, "Print")
Mmx]
replace(s2, "fuu", "haha")
Mmx]
Mmx]
An integer literal is a sequence of digits, possible preceded by a minus sign. It matches the regular expression [-]?[0-9]+. Examples: 123456789123456789, -123.
a := 2
Mmx]
The usual arithmetic operators +, -, * are available:
a+3; a-5; a*a
Mmx]
as well as the inplace operators +=, -=, *= :
a += 1; a *= 2; a -= 3
Mmx]
5 div 2
Mmx]
5 mod 2
Mmx]
The default type for integers is Int. It corresponds to machine type int.
type(1)
Mmx]
In order to have access to the extended arithmetic, one can
use the package numerix. In this case, the
integer literals will yield numbers of type Integer,
based on
use "numerix"; type(1)
Mmx]
For this type of extended integers, the same operations are available and there is no size restriction:
2343312351435131235124354123*2116514651624164612456124
Mmx]
2234000000000000000000 div 2234
Mmx]
2234000000000000000002 mod 2234
Mmx]
Machine precision integers can still be used by specifying explicitly the type, but with the caution due to their limited size:
i : Int := 12155461246541265421
Mmx]
1233444343433434345 :> Int
Mmx]
Notice the conversion operator :>, used to cast a integer litteral into a machine integer Int.
Mmx]
A floating literal is a sequence of digits with a decimal point inside and an optional exponent. It matches the regular expression [-]?[0-9]+[.][0-9]+[[eE][-]?[0-9]+]?.
z :=0.0
Mmx]
w := -3.14159
Mmx]
x := 1.11e07
Mmx]
type(1.1)
Mmx]
a := 1; a/=2
Mmx]
Mmx]
Note that 0. is not permitted, one must write 0.0;
Vectors are sequence of elements, stored in an array and with a direct access through their index. Their type is parametrized by the type of the elements. The default type is Vector Generic.
v := [1,2,3]
Mmx]
The indices start from 0 :
v[0]+v[1]+v[2]
Mmx]
The length of a vector is given by the prefix operator #:
#v
Mmx]
The concatenation of vectors is performed by the operator ><:
w := v >< [4,5]
Mmx]
The inplace concatenation of vectors is done by the operator <<:
v << [1,2]
Mmx]
The classical operations car, cdr, cons on lists are available also on vectors:
[car(v), cdr(v), cons(3,v)]
Mmx]
reverse v
Mmx]
contains?(v,1)
Mmx]
Mmx]
Tuples are written inside (...). Elements are separated by ,.
v := (1,2,3)
Mmx]
Note the associativity of tuples:
(1,(2,3))
Mmx]
(v,((v)))
Mmx]
Mmx]
a..b means the range [a,b], while a::b stands for the half open range [a,b).
(1..4)
Mmx]
1 to 4
Mmx]
Note that 1..4 or 1 to 4 are not tuples but iterators, that can be used to produce sequences:
[1..6]
Mmx]
[i*i | i in 1 to 10]
Mmx]
[i*j | i in 1 to 10 || j in 1 to 10]
Mmx]
Mmx]
Tables allow to store the association between keys of one type and values of another type. They are defined by providing a default value. In the following example, the default value is 1:
t := table(1);
Mmx]
t[1] := -3; t[34] := 2
Mmx]
t[0]
Mmx]
contains? (t,2)
Mmx]
Mmx]
The default type for tables used in the interpreter is Table(Generic,Generic).
A function definition is done as follows:
name (arg1: type1, arg2: type2, ...): returned_type == block
The block is either one instruction or several
instructions in between { }.
lambda (arg1: type1, arg2: type2, ...): returned_type do ...
Notice that any of the type specifications can be omitted, in which case the type is assumed to be Generic.
f (x: String) :String == return x><x
Mmx]
f("ab")
Mmx]
In the following definition, no type are specified so that the input and output type Generic are assumed:
f (x) == { a := 2*x; return a }
Mmx]
When a function has only one argument, the parenthesis () can be omitted.
f 3.1
Mmx]
The same name f is used for the definition
of two functions of types lambda: Int
Int, lambda: Generic
Generic. The polymorphism of f
is resolved by the type of its argument:
f "xx "
Mmx]
f 2.1
Mmx]
The definition of a postfix function is preceded by the keyword postfix. The name of the function
should start with a .:
postfix .sq(i : Int) : Int == { return i*i }
Mmx]
3.sq
Mmx]
operator .m (a: Int , b: Int) : Double == { return
1.0*a*b }
Mmx]
3 .m 3
Mmx]
Mmx]
Any function definition can be preceded by quantifiers forall(...) or exists(...).
forall (R) gcd (p: Polynomial(R), q: Polynomial(R)): Polynomial(R) == {...}
A function is called in the usual way: foo(arg1, arg2,...).
If foo is unitary then () can be omitted, but note that foo a b c is equivalent to foo(a(b(c))). Function call is always by value.
A macro corresponds to a syntactic definition. No type is needed:
square x ==> x*x
Mmx]
square 2.1
Mmx]
square 2
Mmx]
disc(a,b,c) ==> b*b - 4*a*c
Mmx]
disc (1,b,3*x)
Mmx]
Macros can be usefull to define short and convenient names for types for instance:
VG ==> Vector Generic
Mmx]
f(a : VG) == car a
Mmx]
f ([1,2])
Mmx]
Mmx]
The condition construction is as follows:
if condition then block1 [else block2]
where condition is any expression that evaluates to a boolean. block1 and block2 are either one instruction or a block of instructions braced into {...}. The [...] (here the else part) means that the expression is optional.
if true then 1;
Mmx]
if a = b then 1 else 2;
Mmx]
if i = 0 then { x := 1; y := 2} else { z := 3; mmerr
<< "error" };
Mmx]
Mmx]
Loops are constructed as follows:
[for E1] [while E2] [until E3] [step E4] do block
The block is an instruction or a sequence of instructions delimited by {...}. break exits the loop, and continue goes to the next step of the loop.
for i in 1..3 do mmout << i << "
";
Mmx]
1 2
i := 0; while i < 5 do { mmout << i <<
" "; i := i + 1 }
Mmx]
0 1 2 3 4
// do mmout << "no end "; // This loop
has no end
Mmx]
As in
i := 3; mmout<<"The square of
"<<i<<" is
"<<i*i<<"\n";
Mmx]
The square of 3 is 9
The error stream, used to catch error message, is mmerr:
mmerr <<"An error message";
Mmx]
Mmx]
Here are some usefull command to read and save contents in files. The
command to read a
include "example.mmx"
Mmx]
The command to save a String in a file is save:
save ("tmp.txt", "A string is stored in
the file \n in two lines");
Mmx]
If now, you want to recover the content of a file, you can use the command load:
load "tmp.txt"
Mmx]
The files in a directory can be recovered by the command load_directory. The result is a vector of strings, which corresponds to the name of a file or a subdirectory:
load_directory "."
Mmx]
To check if a file or a directory exists, one can use the predicate readable?
readable? "../example.mmx"
Mmx]
The function use allows to load and use the types and functions exported in an external dynamic library:
use "numerix"
Mmx]
If this example, the library libmmxnumerix.so should be in the loading path.
Mmx]
Several functions are available to interact with the environment. To get the value of a variable defined in the environment, one can use get_env:
get_env "PWD"
Mmx]
set_env ("DISPLAY", "viviane:0")
Mmx]
To run a command in this environement, one can use the function system:
system("ls")
Mmx]
example.mmx
index.en.tm quick_start.en.tm shell.en.tm syntax.en.tm tmp.txt
Mmx]
Comments starting with // extend to the end of the physical line. Such a comment may appear at the start of a line or following whitespace or code, but not within a string literal. Multi-line comments must be braced into /{ ... }/ and can be nested.
x := 1; // Assign 1 to x. y := 2; /{ This is multi-line comment about y. }/