2014-07-25 10:48:06 +02:00
|
|
|
### Ast
|
|
|
|
|
2014-07-29 17:33:11 +02:00
|
|
|
The Ast (abstract syntax tree) is created by salama-reader gem and the classes defined there
|
2014-07-25 10:48:06 +02:00
|
|
|
|
|
|
|
### Compiling
|
|
|
|
|
|
|
|
The code in this directory compiles the AST to the virtual machine code.
|
|
|
|
|
|
|
|
If this were an intrepreter, we would just walk the tree and do what it says. Since it's not things are a little more
|
|
|
|
difficult, especially in time.
|
|
|
|
|
|
|
|
When compiling we deal with two times, compile-time and run-time. All the headache comes from mixing those two up.*
|
|
|
|
|
|
|
|
Similarly, the result of compiling is two-fold: a static and a dynamic part.
|
|
|
|
|
|
|
|
- the static part are objects like the constants, but also defined classes and their methods
|
2014-08-28 07:10:33 +02:00
|
|
|
- the dynamic part is the code, which is stored as streams of instructions in the CompiledMethod
|
2014-07-25 10:48:06 +02:00
|
|
|
|
|
|
|
Too make things a little simpler, we create a very high level instruction stream at first and then run
|
|
|
|
transformation and optimisation passes on the stream to improve it.
|
|
|
|
|
|
|
|
Each ast class gets a compile method that does the compilation.
|
|
|
|
|
|
|
|
#### Method Definition and Instructions
|
|
|
|
|
2014-08-28 07:10:33 +02:00
|
|
|
The first argument to the compile method is the CompiledMethod. All code is encoded as a stream of Instructions in the
|
|
|
|
CompiledMethod. In fact Instructions are a linked list and so the CompiledMethod only hold the head, and the current
|
2014-07-25 10:48:06 +02:00
|
|
|
insertion point.
|
|
|
|
|
|
|
|
Code is added to the method (using add()), rather than working with the actual instructions. This is so each compile method
|
|
|
|
can just do it's bit and be unaware of the larger structure that is being created. The genearal structure of the instructions
|
|
|
|
is a graph (what with if's and whiles and breaks and what), but we build it to have one start and *one* end (return).
|
|
|
|
|
|
|
|
|
|
|
|
#### Messages and frames
|
|
|
|
|
|
|
|
The virtual machine instructions obviously operate on the virtual machine. Since the machine is virtual, we have to define
|
|
|
|
it, and since it is oo we define it in objects.
|
|
|
|
|
|
|
|
Also it is important to define how instructions, which is is in a ohysical machine by changing the contents of registers or
|
|
|
|
some stack.
|
|
|
|
|
|
|
|
Our machine is ot a register machine, but an object machine: it operates directly on objects and also has no stack.
|
|
|
|
|
|
|
|
When a Method needs to make a call, or send a message, it creates a Message object. Messages contain return addresses and
|
|
|
|
arguemnts.
|
|
|
|
|
|
|
|
Then the machine must find the method to call.
|
|
|
|
|
|
|
|
Then a new Method receives the message, creates a Frame for local and temporary variables and continues execution.
|
|
|
|
|
|
|
|
The important thing here is that Messages and Frames are normal objects.
|
|
|
|
|
2014-07-25 19:28:38 +02:00
|
|
|
And interestingly we can partly use ruby to find the method, so in a way it is not just a top down transformation. but
|
2014-07-25 10:48:06 +02:00
|
|
|
the sending goes back up and then down again.
|
|
|
|
|
2014-07-28 11:59:43 +02:00
|
|
|
The Message object is the second parameter to the compile method, the run-time part as it were. Why? Since it only
|
|
|
|
exists at runtime: to make compile time analysis possible. Especially for those times when we can resolve the method
|
|
|
|
at compile time. (Which is for all vm code)
|
2014-07-25 10:48:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
*
|
|
|
|
As ruby is a dynamic language, it also compiles at run-time. This line of thought does not help though as it sort of mixes
|
|
|
|
the seperate times up, even they are not. Even in a running ruby programm the stages of compile and run are seperate.
|
|
|
|
Similarly it does not help to argue that the code is static too, not dynamic, as that leaves us with a worse working model.
|