If you have programmed in an imperative programming language, you might have noticed the absence of
an assignment operator in Motto. This is because the kernel of Motto
is a purely Functional language.
The Motto virtual machine has the capability to update a variable. It is just not exposed to the
outside world. Before I show you how to expose it, let me explain why Motto cannot have an assignment
operator based on its simple stack-based programming model.
A symbol bound to a value, is evaluated to that value:
As a Motto procedure always follow its arguments, we cannot have an “assignment” procedure because
all it will see on the stack is the value, not the symbol. So we need to add a facility to handle
this special case. We need a procedure that precedes its argument or at least one of its arguments.
This can be accomplished by writing a compiler definition, i.e, a procedure executed by the compiler.
This procedure will read the symbol from the input and emit virtual machine instructions to assign
the previous value on the stack to that symbol. As this special procedure is executed by the compiler, it has to be
written in the language the compiler itself is written. The Motto compiler and virtual machine is
written in Scheme, a dialect of Lisp.
Especially, we use a Scheme implementation called Gambit-C
which has the ability to generate highly-optimized executable programs for a variety of platforms.
Let us call our assignment procedure set and add its definition to the Motto compiler:
> (cdef 'set (lambda (compiler)
(list (c-push (get-symbol (compiler-input compiler)))
(c-set))))
cdef maps the Scheme symbol set to a Scheme procedure (a lambda form).
The Motto virtual machine consumes lists of instructions.
Here the compiler will read a symbol from the input stream and emit the instruction to push
that symbol to the stack. This instruction is followed by a set bytecode which will assign
the value already on the stack to the symbol:
In fact, Motto comes pre-packaged with set and a few other useful compiler definitions:
> 20 def b ;; defines a new variable.
> 50 def a ;; re-defines the existing variable `a`.
> a b + def c
> c
70
> 100 c set
> c
100
The use of def and set becomes more evident when we have to deal with non-local variables:
> 100 def x
> 200 def y
> [x y .s .c] !
100 200
> x y
100 200
> ;; In the following code block, `set` will update the global variable `x`, while
;; `def` will declare a new local variable `y`.
> [300 set x
500 def y
x y .s .c
600 set y
x y .s .c] !
300 500
300 600
> ;; `x` was changed by the procedure, while `y` stays intact
;; as the procedure modified its local definition of `y`.
> x y
300 200
There are compiler definitions to load and compile Motto source code files. For example,
here is a simple Motto script – hello.m:
;; file: hello.m
[hello world .s .c] def say-hello
This can be imported to the interpreter:
> import "hello.m"
> say-hello
hello, world
The interpreter will compile the script before executing it.
The script can be explicitly compiled and stored in an object file for faster loading:
> compile "hello.m"
> import "hello.mo"
> say-hello
hello, world
Another useful compiler definition is probe which can be used to look-up a value in an
anonymous closure:
> 100 200 [def x def y []] ! dup probe x swap probe y
200 100
Compiler definitions can be used to optimize some computations by executing them at compile-time
and inserting the result into bytecode. An example can be seen here. Most of the basic operations in Motto are compiler definitions. You can extend the compiler with your own definitions or change
the behavior of existing definitions. For example, arithmetic procedures can be re-defined to make Motto do
infix arithmetic:
> (cdef '+ (lambda (compiler)
(list (c-push (get-number (compiler-input compiler)))
(c-arith '+))))
> 10 + 20
30
Virtual Machine definitions
Just like the compiler, the virtual machine can also be extended with definitions written in Scheme.
These definitions are executed at runtime, like normal Motto procedures. A virtual machine definition
is a lambda expression that takes two arguments:
the current instance of the virtual machine and a reference to the data stack. This lambda should
return a new data stack. A virtual machine definition is introduced using the def Scheme procedure.
The following sample shows a VM definition that squares all numbers on the stack. It first converts
the stack to a list and uses the Lisp map function to produce a list of squares. This list is then converted
to a stack and returned. (Conversions between the data stack and Scheme lists are not expensive,
because the current implementation of Motto uses a Scheme list as its data stack!).
> (def 'sqr-all
(lambda (vm stack)
(list->stack (map (lambda (x) (* x 2))
(stack->list stack)))))
> 1 2 3 4 5 sqr-all
2 4 6 8 10