--- layout: salama title: Salama architectural layers ---

Main Layers

To implement an object system to execute object oriented languages takes a large system. The parts or abstraction layers are detailed below.
It is important to undrstand the approach first though, as it differs from the normal interpretation. The idea is to compile (eg) ruby. It may be easiest to compare to a static object oriented language like c++. When c++ was created c++ code was translated into c, which then gets translated into assembler, which gets translated to binary code, which is linked and executed. Compiling to binaries is what gives these languages speed, and is one reason to compile ruby.
In a similar way to the c++ example, we need language between ruby and assembler, as it is too big a mental step from ruby to assembler. Off course course one could try to compile to c, but since c is not object oriented that would mean dealing with all off c's non oo heritance, like linking model, memory model, calling convention etc. (more on this in the book)
The layers are:

Binary , Arm and Elf

A physical machine will run binaries containing intructions that the cpu understands. With arm being our main target, this means we need code to produce binary, which is contained in a seperate module salama-arm .
To be able to run code on a unix based operating system, binaries need to be packaged in a way that the os understands, so minimal elf support is included in the package.
Arm is a risc architecture, but anyone who knows it will attest, with it's own quirks. For example any instruction may be executed conditionally in arm. Or there is no 32bit register load instruction. It is possible to create very dense code using all the arm special features, but this is not implemented yet.

Register Machine

The Register machine layer is a relatively close abstraction of risc hardware, but without the quirks.
The register machine has registers, indexed addressing, operators, branches and everything needed for the next layer. It doesn not try to abstract every possible machine leature (like llvm), but rather "objectifies" the risc view to provide what is needed for soml, the next layer up.
The machine has it's own (abstract) instruction set, and the mapping to arm is quite straightforward. Since the instruction set is implemented as derived classes, additional instructions may be defined and used later, as long as translation is provided for them too. In other words the instruction set is extensible (unlike cpu instruction sets).

Basic object oriented concepts are needed already at this level, to be able to generate a whole self contained system. Ie what an object is, a class, a method etc. This minimal runtime is called parfait and will be coded in soml eventually. But since it is the same objects at runtime and compile time, it will then be translated back to ruby for use at compile time. Currenty there are two versions of the code, in ruby and soml, being hand synchronized. More about parfait below.

Since working with at this low machine level (essentially assembler) is not easy to follow for everyone, an interpreter was created. Later a graphical interface, a kind of visual debugger was added. Visualizing the control flow and being able to see values updated immediately helped tremendously in creating this layer. And the interpreter helps in testing, ie keeping it working in the face of developer change.

Soml, Salama object machine language

Soml is probably the larest single part of the system and much more information can be found here .
Before soml, a more traditional virtual machine approach was taken and abandoned. The language is easy to understand and provides a good abstraction, both in terms of object orienteation, and in terms of how this is expressed in the register model.
It is like ruby with out the dynamic aspects, but typed.
In broad strokes it consists off:

Just to summarize a few of soml features that are maybe unusual:

Salama

To compile and run ruby, we need to parse and compile ruby code. To compile ruby to soml a clear mapping has to be achieved. Particularly the dynamic aspects, and typing need to be addressed.
While parsing ruby is quite a difficult task, it has already been implemented in pure ruby here . The output of the parser is again an ast, which needs to be compiled to soml.
The dynamic aspects of ruby are actually realtively easy to handle, once the whole system is in place, because the whole system is written in ruby without external dependencies. Since (when finished) it can compile ruby, it can do so to produce a binary. This binary can then contain the whole of the system, and so the resulting binary will be able to produce binary code when it runs. With small changes to the linking process (easy in ruby!) it can then extend itself.

The type aspect is more tricky: Ruby is not typed and soml is after all. And if everything were objects (as we like to pretend in ruby) we could just do a lot of dynamic checking, possibly later introduce some caching. But everything is not an object, minimally integers are not, but maybe also floats and other values. The destinction between what is an integer and what an object has sprouted an elaborate type system, which is (by necessity) present in soml (see there).

The idea (because it hasn't been implemented yet) is to have different functions for different types. The soml layer defines object layout and types and also lets us return to different places from a function (in effect a soml function call is like an if). By using this, we can compile a single ruby method into several soml methods. Each such method is typed, ie all arguments and variables are of known type. According to these types we can call methods according to their signatures. Also we can autognerate error methods for unhandled types, and predict that only a fraction of the possible combinations will actually be needed.