mostly comments
This commit is contained in:
parent
8d7b353f33
commit
7df1490da8
63
lib/ast/README.md
Normal file
63
lib/ast/README.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
### Ast
|
||||||
|
|
||||||
|
The Ast (abstract syntax tree) is created by kide-reader gem and the classes defined there
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- the dynamic part is the code, which is stored as streams of instructions in the MethodDefinition
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The first argument to the compile method is the MethodDefinition. All code is encoded as a stream of Instructions in the
|
||||||
|
MethodDefinition. In fact Instructions are a linked list and so the MethodDefinition only hold the head, and the current
|
||||||
|
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.
|
||||||
|
|
||||||
|
Anf interestingly we can partly use ruby to find the method, so in a way it is not just a top down transformation. but
|
||||||
|
the sending goes back up and then down again.
|
||||||
|
|
||||||
|
The Message object is the second parameter to the compile method, the run-time part as it were.
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
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.
|
@ -3,7 +3,7 @@ module Ast
|
|||||||
|
|
||||||
class CallSiteExpression < Expression
|
class CallSiteExpression < Expression
|
||||||
# attr_reader :name, :args , :receiver
|
# attr_reader :name, :args , :receiver
|
||||||
@@counter = 0
|
|
||||||
def compile method , message
|
def compile method , message
|
||||||
me = receiver.compile( method, message )
|
me = receiver.compile( method, message )
|
||||||
with = args.collect{|a| a.compile( method,message)}
|
with = args.collect{|a| a.compile( method,message)}
|
||||||
|
@ -11,7 +11,7 @@ module Virtual
|
|||||||
# - low level means it's basic instructions are realively easily implemented in a register machine. ie send is not
|
# - low level means it's basic instructions are realively easily implemented in a register machine. ie send is not
|
||||||
# a an instruction but a function.
|
# a an instruction but a function.
|
||||||
#
|
#
|
||||||
# So the memory model of the machine allows for indexed access into and "object" . A fixed number of objects exist
|
# So the memory model of the machine allows for indexed access into an "object" . A fixed number of objects exist
|
||||||
# (ie garbage collection is reclaming, not destroying and recreating) although there may be a way to increase that number.
|
# (ie garbage collection is reclaming, not destroying and recreating) although there may be a way to increase that number.
|
||||||
#
|
#
|
||||||
# The ast is transformed to virtaul-machine objects, some of which represent code, some data.
|
# The ast is transformed to virtaul-machine objects, some of which represent code, some data.
|
||||||
@ -19,12 +19,12 @@ module Virtual
|
|||||||
# The next step transforms to the register machine layer, which is what actually executes.
|
# The next step transforms to the register machine layer, which is what actually executes.
|
||||||
#
|
#
|
||||||
|
|
||||||
# More concretely, an virtual machine is a sort of oo turing machine, it has a current instruction, executes the
|
# More concretely, a virtual machine is a sort of oo turing machine, it has a current instruction, executes the
|
||||||
# instructions, fetches the next one and so on.
|
# instructions, fetches the next one and so on.
|
||||||
# Off course the instructions are not soo simple, but in oo terms quite so.
|
# Off course the instructions are not soo simple, but in oo terms quite so.
|
||||||
#
|
#
|
||||||
# The machine is virtual in the sense that it is completely
|
# The machine is virtual in the sense that it is completely modeled in software,
|
||||||
# modeled in software, it's complete state explicitly available (not implicitly by walking stacks or something)
|
# it's complete state explicitly available (not implicitly by walking stacks or something)
|
||||||
|
|
||||||
# The machine has a no register, but local variables, a scope at each point in time.
|
# The machine has a no register, but local variables, a scope at each point in time.
|
||||||
# Scope changes with calls and blocks, but is saved at each level. In terms of lower level implementation this means
|
# Scope changes with calls and blocks, but is saved at each level. In terms of lower level implementation this means
|
||||||
@ -34,9 +34,9 @@ module Virtual
|
|||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
the_end = Halt.new
|
the_end = Halt.new
|
||||||
@frame = Message.new(the_end , the_end , :Object)
|
@message = Message.new(the_end , the_end , :Object)
|
||||||
end
|
end
|
||||||
attr_reader :frame
|
attr_reader :message
|
||||||
|
|
||||||
# run the instruction stream given. Instructions are a graph and executing means traversing it.
|
# run the instruction stream given. Instructions are a graph and executing means traversing it.
|
||||||
# If there is no next instruction the machine stops
|
# If there is no next instruction the machine stops
|
||||||
|
@ -13,7 +13,7 @@ module VirtualHelper
|
|||||||
parts = Parser::Transform.new.apply(syntax)
|
parts = Parser::Transform.new.apply(syntax)
|
||||||
machine = Virtual::Machine.new
|
machine = Virtual::Machine.new
|
||||||
main = Virtual::MethodDefinition.main
|
main = Virtual::MethodDefinition.main
|
||||||
expressions = parts.compile( main, machine.frame )
|
expressions = parts.compile( main, machine.message )
|
||||||
should = YAML.load(@output.gsub("RETURN_MARKER" , "\n"))
|
should = YAML.load(@output.gsub("RETURN_MARKER" , "\n"))
|
||||||
assert_equal should , expressions , expressions.to_yaml.gsub("\n" , "RETURN_MARKER") + "\n" + expressions.to_yaml
|
assert_equal should , expressions , expressions.to_yaml.gsub("\n" , "RETURN_MARKER") + "\n" + expressions.to_yaml
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user