Name | Reference type | Index type |
---|---|---|
Nil | no | no |
C Function | no | no |
Number | no | no |
Symbol | yes | no |
String | yes | no |
List | yes | yes |
Weak reference * | no | no |
* weak references are only used internally by the interpreter
The mlisp interpreter doesn't manage every object through reference count, some values are only copied, and only the reference types are cared by the garbage collector. This is intended to save memory, since non-reference types are usually of similar size as pointers.
Based on everything above, types can be put in the following hierarchy:
value
object
(reference types)list
symbol
string
c function
number
weak
(weak reference)nil
The terms above will be used in next section.
This section describes behaviour of built-in functions. The following conventions will be used here:
Example | Name | Meaning |
---|---|---|
(f) → type |
Returns | function f returns value of type type |
argument... |
Repeatable | argument may be repeated |
[argument] |
Optional | argument is optional |
[argument argument] |
Group | both arguments are optional, but have to be passed together |
[argument argument]... |
Repeatable group | like above, but the arguments can be repeated |
A|B |
One of | the argument has to be of type or name A or B |
Beware, the behaviour for most of these functions is still unspecified if they are given wrong type of arguments, however, at worst it only causes forever loops and null-pointer dereference, otherwise the functions just return wrong results without any other side effects.
This sub-section describes the most basic functions in the language, without which the intepreter couldn't work correctly.
eval
takes value of any type, tries to evaluate it, and returns the result.
read
reads the standard input, parses it as mlisp data, and returns it without evaluation.
import
runs another mlisp script in the same context as the main script. It can be used for accessing data structures and functions defined in another file.
The return value is the value of the last expression in the imported file.
print
prints its arguments to the output, it doesn't dive recursively into indexed types. It returns nil
after it finishes its task.
quote
returns its only argument as a literal, unevaluated token.
def
creates new variable in the local scope with the name of the first argument, then evaluates its second argument and assigns the result to the variable.
The function returns nil
.
set
evaluates it's arguments, and assigns the second one, to the first one. The first argument might be a variable name or a reference to a list or map field, for instance:
(def l (list "a" "b" "c" "d")) ; creates a new list
(print (list-get l 2)) ; prints 'c'
(set (list-get l 2) "g") ; sets the "c" inside the list to "g"
(print (list-get l 2)) ; prints 'g'
The difference between set
and def
is that set
will look for an existing value rather than create one, if it doesn't find it, it will look for it in the global scope. But since the interpeter treats every non-declared variable as it were containing nil
, the set
function -- given a symbol as its first argument -- can be actually used to declare global variables from inside functions.
The function returns nil
.
fn
creates and returns a new lambda, its first argument is assumed to be a list of symbols to be used as argument names, the next arguments are the lambda's body.
Every lambda, if called, returns the value of its last expression.
The idiom to declare and call functions in mlisp is:
(def twice (fn (n)
(mul 2 n))) ; declares function 'twice'
(print (twice 9)) ; prints '18'
Since lambdas are first-class values, and actually just lists, they can be passed as arguments to other lambdas or returned by them. The user can use this property to create solutions like the following one:
(def double-op (fn (op n)
(op n n)))
(def twice (fn (n)
(double-op add n)))
(def squared (fn (n)
(double-op mul n)))
(print (twice 4) (squared 4)) ; prints '8 16'
This section describes pars of the language that are helpful when some internal actions of the intepreter are to be figured out.
debug
prints information on number of objects currently allocated in the memory, notice it doesn't include non-reference types.
The function returns nil
.
write
prints it's only argument as an s-expression, if it's not given any arguments, it will print the contents of a scope it is run in.
The function returns nil
.
This section describes functions used to manage maps and lists.
list
creates list of its evaluated arguments and returns it.
list-get
returns value of n
th element inside the given index
.
Remember that lambdas are just lists, so they can be manipulated with some help of this function.
The arguments for map
have to come in key-value pairs, the function puts them inside a newly created map, with the values evaluated, and returns it.
The returned value is a list
understanable by map
family of functions, but since it's a list
, it can be managed by list
functions as well.
map-get
returns value of the element assigned to the given key (the second argument) in the given map (the first argument).
This section describes functions used for evaluating mathematical and comparement expressions as well as functions used for control flow.
For these functions only nil
is treated and returned as a false value, all the other values, including empty strings or 0s are truths.
len
returns length of given objects, if it's a string or a symbol, it returns the number of its characters, if it's a list, it returns the number of values inside it.
while
evaluates all its arguments as long as the first one is true,
it returns the last value of the last expression in its body.
if
evaluates its first argument and checks whether its true, if it is, it evaluates and returns its second argument, otherwise it returns the evaluated value of the third argument. If the third argument isn't given, it is assumed to be nil
.
do
evaluates every each of its arguments once, and returns the value of the last one.
This function is obviously not directly related with mathematical expressions, but it can be used to group expressions; that might be especially handy inside ifs.
All of the functions above take their first argument, and then apply the appropriate mathematical operation to it -- using the next argument; if there are more arguments, the operation is repeated with the current result and the next arguments.
>
checks whether it's arguments are sorted in decreasing order.
If so, it returns the last value on the list, otherwise it returns nil
.
<
checks whether it's arguments are sorted in rising order.
If so, it returns the last value on the list, otherwise it returns nil
.
>=
checks whether it's arguments are sorted in non-rising order.
If so, it returns the last value on the list, otherwise it returns nil
.
<=
checks whether it's arguments are sorted in non-decreasing order.
If so, it returns the last value on the list, otherwise it returns nil
.
=
checks whether all of its arguments are of the same numerical value.
If so, it returns the last value on the list, otherwise it returns nil
.
!=
checks whether any neighbouring pair of its arguments is of different value.
If so, it returns the last value on the list, otherwise it returns nil
.
or
checks if any of arguments passed to it are of type other than nil
, if so, it returns the first non-nil value met, otherwise it returns nil
.
and
checks if any of arguments passed to it are of type nil
, if so, it returns nil
, otherwise it returns the last value passed to it.
not
checks if the only argument passed to it is nil
, if so, it returns a number, otherwise it returns nil
.