bit of line wrapping
This commit is contained in:
parent
d758a23eb6
commit
aee36d9f5f
13
README.md
13
README.md
@ -57,8 +57,8 @@ Parse simple code, using Parslet. This has been seperated out as it's own gem, s
|
|||||||
Parsing is a surprisingly fiddly process, very space and order sensitive. But Parslet is great and simple
|
Parsing is a surprisingly fiddly process, very space and order sensitive. But Parslet is great and simple
|
||||||
expressions (including function definitions and calls) are starting to work.
|
expressions (including function definitions and calls) are starting to work.
|
||||||
|
|
||||||
I spent some time on the parse testing framework, so it is safe to fiddle and add. In fact it is very modular and
|
I spent some time on the parse testing framework, so it is safe to fiddle and add.
|
||||||
so ot is easy to add.
|
In fact it is very modular and so ot is easy to add.
|
||||||
|
|
||||||
### Virtual: Compile the Ast
|
### Virtual: Compile the Ast
|
||||||
|
|
||||||
@ -94,8 +94,8 @@ So the current staus is that i can
|
|||||||
|
|
||||||
#### Blocks
|
#### Blocks
|
||||||
|
|
||||||
Implement ruby Blocks, and make new vm classes to deal with that. This is in fact a little open, but i have a general
|
Implement ruby Blocks, and make new vm classes to deal with that. This is in fact a little open,
|
||||||
notion that blocks are "just" methods with even more implicit arguments.
|
but i have a general notion that blocks are "just" methods with even more implicit arguments.
|
||||||
|
|
||||||
#### Exceptions
|
#### Exceptions
|
||||||
|
|
||||||
@ -103,8 +103,9 @@ Implement Exceptions. Conceptionally this is not so difficult in an oo machine a
|
|||||||
|
|
||||||
I have a post about it http://salama.github.io/2014/06/27/an-exceptional-though.html
|
I have a post about it http://salama.github.io/2014/06/27/an-exceptional-though.html
|
||||||
|
|
||||||
which boild down to the fact that we can treat the address to return to in an exception quite like a return address
|
which boild down to the fact that we can treat the address to return to in an exception quite
|
||||||
from a function. Ie just another implicit parameter (as return is really an implicit parameter, a little like self for oo)
|
like a return address from a function. Ie just another implicit parameter
|
||||||
|
(as return is really an implicit parameter, a little like self for oo)
|
||||||
|
|
||||||
### C linking
|
### C linking
|
||||||
|
|
||||||
|
@ -6,10 +6,11 @@ The Ast (abstract syntax tree) is created by salama-reader gem and the classes d
|
|||||||
|
|
||||||
The code in this directory compiles the AST to the virtual machine code.
|
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
|
If this were an intrepreter, we would just walk the tree and do what it says.
|
||||||
difficult, especially in time.
|
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.*
|
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.
|
Similarly, the result of compiling is two-fold: a static and a dynamic part.
|
||||||
|
|
||||||
@ -23,27 +24,29 @@ Each ast class gets a compile method that does the compilation.
|
|||||||
|
|
||||||
#### Compiled Method and Instructions
|
#### Compiled Method and Instructions
|
||||||
|
|
||||||
The first argument to the compile method is the CompiledMethod. All code is encoded as a stream of Instructions in the
|
The first argument to the compile method is the CompiledMethod.
|
||||||
CompiledMethod. Instructions are stored as a list of Blocks, and Blocks are the smallest unit of code, which is
|
All code is encoded as a stream of Instructions in the CompiledMethod.
|
||||||
always linear.
|
Instructions are stored as a list of Blocks, and Blocks are the smallest unit of code,
|
||||||
|
which is always linear.
|
||||||
|
|
||||||
Code is added to the method (using add_code), rather than working with the actual instructions. This is so each
|
Code is added to the method (using add_code), rather than working with the actual instructions.
|
||||||
compiling method can just do it's bit and be unaware of the larger structure that is being created.
|
This is so each compiling 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),
|
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).
|
but we build it to have one start and *one* end (return).
|
||||||
|
|
||||||
|
|
||||||
#### Messages and frames
|
#### Messages and frames
|
||||||
|
|
||||||
The virtual machine instructions obviously operate on the virtual machine. Since the machine is virtual,
|
The virtual machine instructions obviously operate on the virtual machine.
|
||||||
we have to define it, and since it is oo we define it in objects.
|
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 operate, which is is in a physical machine would be by changing
|
Also it is important to define how instructions operate, which is is in a physical machine would
|
||||||
the contents of registers or some stack.
|
be 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 seperat
|
Our machine is ot a register machine, but an object machine: it operates directly on objects and
|
||||||
stack, only objects. There are a number of objects which are accessible, and one can think of these (their addresses)
|
also has no seperate stack, only objects. There are a number of objects which are accessible,
|
||||||
as register contents. And one wouldn't be far off as that is the implementation
|
and one can think of these (their addresses) as register contents.
|
||||||
|
And one wouldn't be far off as that is the implementation
|
||||||
|
|
||||||
The objects the machine works on are:
|
The objects the machine works on are:
|
||||||
|
|
||||||
@ -52,12 +55,14 @@ The objects the machine works on are:
|
|||||||
- Self
|
- Self
|
||||||
- NewMessage
|
- NewMessage
|
||||||
|
|
||||||
and working on means, these are the only objects which the machine accesses. Ie all others would have to be moved first.
|
and working on means, these are the only objects which the machine accesses.
|
||||||
|
Ie all others would have to be moved first.
|
||||||
|
|
||||||
When a Method needs to make a call, or send a Message, it creates a NewMessage object.
|
When a Method needs to make a call, or send a Message, it creates a NewMessage object.
|
||||||
Messages contain return addresses and arguments.
|
Messages contain return addresses and arguments.
|
||||||
|
|
||||||
Then the machine must find the method to call. This is a function of the virtual machine and is implemented in ruby.
|
Then the machine must find the method to call.
|
||||||
|
This is a function of the virtual machine and is implemented in ruby.
|
||||||
|
|
||||||
Then a new Method receives the Message, creates a Frame for local and temporary variables and continues execution.
|
Then a new Method receives the Message, creates a Frame for local and temporary variables and continues execution.
|
||||||
|
|
||||||
@ -66,13 +71,15 @@ The important thing here is that Messages and Frames are normal objects.
|
|||||||
And interestingly we can partly use ruby to find the method, so in a way it is not just a top down transformation.
|
And interestingly we can partly use ruby to find the method, so in a way it is not just a top down transformation.
|
||||||
Instead the sending goes back up and then down again.
|
Instead 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. Why? Since it only
|
The Message object is the second parameter to the compile method, the run-time part as it were.
|
||||||
exists at runtime: to make compile time analysis possible (it is after all the Virtual version, not Parfait. ie
|
Why? Since it only exists at runtime: to make compile time analysis possible
|
||||||
compile-time, not run-time). Especially for those times when we can resolve the method
|
(it is after all the Virtual version, not Parfait. ie compile-time, not run-time).
|
||||||
at compile time.
|
Especially for those times when we can resolve the method at compile time.
|
||||||
|
|
||||||
|
|
||||||
*
|
*
|
||||||
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
|
As ruby is a dynamic language, it also compiles at run-time. This line of thought does not help
|
||||||
the seperate times up, even they are not. Even in a running ruby programm the stages of compile and run are seperate.
|
though as it sort of mixes the seperate times up, even they are not.
|
||||||
Similarly it does not help to argue that the code is static too, not dynamic, as that leaves us with a worse working model.
|
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.
|
@ -1,7 +1,7 @@
|
|||||||
### Builtin module
|
### Builtin module
|
||||||
|
|
||||||
The Builtin module contains functions that can not be coded in ruby. It is the other side of the parfait coin, part of
|
The Builtin module contains functions that can not be coded in ruby.
|
||||||
the runtime.
|
It is the other side of the parfait coin, part of the runtime.
|
||||||
|
|
||||||
The functions are organized by their respective class and get loaded in boot_classes! , right at the start.
|
The functions are organized by their respective class and get loaded in boot_classes! , right at the start.
|
||||||
|
|
||||||
|
@ -18,27 +18,27 @@ And thus parfait can be used at run-time.
|
|||||||
|
|
||||||
It's too simple: just slips off the mind like a fish into water.
|
It's too simple: just slips off the mind like a fish into water.
|
||||||
|
|
||||||
Parfait has a brother, the Builtin module. Builtin contains everything that can not be coded in ruby, but we stil need
|
Parfait has a brother, the Builtin module. Builtin contains everything that can not be coded in ruby,
|
||||||
(things like array access).
|
but we stil need (things like array access).
|
||||||
|
|
||||||
#### Example: Message send
|
#### Example: Message send
|
||||||
|
|
||||||
It felt a little stupid that it took me so long to notice that sending a message is very closely related to the
|
It felt a little stupid that it took me so long to notice that sending a message is very closely related to the
|
||||||
existing ruby method Object.send
|
existing ruby method Object.send
|
||||||
|
|
||||||
Off course Object.send takes symbol and the arguments and has the receiver, so all the elements of our Messaage are there.
|
Off course Object.send takes symbol and the arguments and has the receiver, so all the elements of our
|
||||||
And the process that Object.send needs to do is exactly that: send that message, ie find the correct method according to
|
Messaage are there. And the process that Object.send needs to do is exactly that:
|
||||||
the old walk up the inheritance tree rules and dispatch it.
|
send that message, ie find the correct method according to the old walk up the inheritance tree rules and dispatch it.
|
||||||
|
|
||||||
And as all this happens at runtime, "all" we have to do is code this logic. And since it is at runtime,
|
And as all this happens at runtime, "all" we have to do is code this logic. And since it is at runtime,
|
||||||
we can do it in ruby (as i said, this get's compiled and run, just like the program).
|
we can do it in ruby (as i said, this get's compiled and run, just like the program).
|
||||||
|
|
||||||
But what about the infinite loop problem:
|
But what about the infinite loop problem:
|
||||||
|
|
||||||
There was a little step left out: Off course the method gets compiled at compile-time and so we don't just blindly dispatch:
|
There was a little step left out: Off course the method gets compiled at compile-time and so
|
||||||
we catch the simple cases that we know about: layout, type instance variables and compile time known functions. Part of
|
we don't just blindly dispatch: we catch the simple cases that we know about:
|
||||||
those are some that we just don't allow to be overridden.
|
layout, type instance variables and compile time known functions.
|
||||||
|
Part of those are some that we just don't allow to be overridden.
|
||||||
Also what in ruby is object.send is Message.send in salama, as it is the message we are sending and which defines all the
|
|
||||||
data we need (not the object). The object receives, it does not send.
|
|
||||||
|
|
||||||
|
Also what in ruby is object.send is Message.send in salama, as it is the message we are sending and
|
||||||
|
which defines all the data we need (not the object). The object receives, it does not send.
|
||||||
|
@ -23,27 +23,28 @@ There are four virtual objects that are accessible (we can access their variable
|
|||||||
- Frame (local and tmp variables)
|
- Frame (local and tmp variables)
|
||||||
- NewMessage ( to build the next message sent)
|
- NewMessage ( to build the next message sent)
|
||||||
|
|
||||||
These are pretty much the first four registers. When the code goes from virtual to register, we use register instructions
|
These are pretty much the first four registers. When the code goes from virtual to register,
|
||||||
to replace virtual ones.
|
we use register instructions to replace virtual ones.
|
||||||
|
|
||||||
Eg: A Virtual::Set can move data around inside those objects. And since in Arm this can not be done in one instruciton,
|
Eg: A Virtual::Set can move data around inside those objects.
|
||||||
we use two, one to move to an unused register and then into the destination. And then we need some fiddling of bits
|
And since in Arm this can not be done in one instruction, we use two, one to move to an unused register
|
||||||
to shift the type info.
|
and then into the destination. And then we need some fiddling of bits to shift the type info.
|
||||||
|
|
||||||
Another simple example is a Call. A simple case of a Class function call resolves the class object, and with the
|
Another simple example is a Call. A simple case of a Class function call resolves the class object,
|
||||||
method name the function to be found at compile-time. And so this results in a Register::Call, which is an Arm
|
and with the method name the function to be found at compile-time.
|
||||||
instruction.
|
And so this results in a Register::Call, which is an Arm instruction.
|
||||||
|
|
||||||
A C call
|
A C call
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Ok, there are no c calls. But syscalls are very similar. This is not at all as simple as the nice Class call described
|
Ok, there are no c calls. But syscalls are very similar.
|
||||||
above.
|
This is not at all as simple as the nice Class call described above.
|
||||||
|
|
||||||
For syscall in Arm (linux) you have to load registers 0-x (depending on call), load R7 with the syscall number and then
|
For syscall in Arm (linux) you have to load registers 0-x (depending on call), load R7 with the
|
||||||
issue the software interupt instruction. If you get back something back, it's in R0.
|
syscall number and then issue the software interupt instruction.
|
||||||
|
If you get back something back, it's in R0.
|
||||||
|
|
||||||
In short, lots of shuffling. And to make it fit with our four object architecture, we need the Message to hold the data
|
In short, lots of shuffling. And to make it fit with our four object architecture,
|
||||||
for the call and Sys (module) to be self. And then the actual functions do the shuffle, saving the data and restoring it.
|
we need the Message to hold the data for the call and Sys (module) to be self.
|
||||||
|
And then the actual functions do the shuffle, saving the data and restoring it.
|
||||||
And setting type information according to kernel documentation (as there is no runtime info)
|
And setting type information according to kernel documentation (as there is no runtime info)
|
||||||
|
|
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
Knowing what's going on while coding salama is not so easy: Hence the need to look at code dumps
|
Knowing what's going on while coding salama is not so easy: Hence the need to look at code dumps
|
||||||
|
|
||||||
Hence the need for a code/object file format (remember an oo program is just objects, some data, some code, all objects)
|
Hence the need for a code/object file format
|
||||||
|
(remember an oo program is just objects, some data, some code, all objects)
|
||||||
|
|
||||||
I started with yaml, which is nice in that it has a solid implementation, reads and writes, handles arbitrary objects, handles graphs and is a sort of readable text format.
|
I started with yaml, which is nice in that it has a solid implementation, reads and writes,
|
||||||
|
handles arbitrary objects, handles graphs and is a sort of readable text format.
|
||||||
|
|
||||||
But the "sort of" started to get to me, because
|
But the "sort of" started to get to me, because
|
||||||
|
|
||||||
@ -26,7 +28,8 @@ The main starting goal was quite like yaml, but with
|
|||||||
Ok, so we all heard about object files, it's the things compilers create so we don't have to have
|
Ok, so we all heard about object files, it's the things compilers create so we don't have to have
|
||||||
huge compiles and can link them later.
|
huge compiles and can link them later.
|
||||||
|
|
||||||
Much fewer know what they include, and that is not because they are not very useful, but rather very complicated.
|
Much fewer know what they include, and that is not because they are not very useful,
|
||||||
|
but rather very complicated.
|
||||||
|
|
||||||
An object machine must off course have it's own object files, because:
|
An object machine must off course have it's own object files, because:
|
||||||
|
|
||||||
@ -44,11 +47,13 @@ And so this is a little start, just some outputter.
|
|||||||
|
|
||||||
#### Direction
|
#### Direction
|
||||||
|
|
||||||
The way this is meant to go (planned for 2020+) was a salama core with only a sof parser (as that is soo much simpler).
|
The way this is meant to go (planned for 2020+) was a salama core with only a sof parser
|
||||||
|
(as that is soo much simpler).
|
||||||
|
|
||||||
Then to_ruby for all the ast classes to be able to roundtrip ruby code.
|
Then to_ruby for all the ast classes to be able to roundtrip ruby code.
|
||||||
|
|
||||||
Then go to storing sof in git, rather than ruby.
|
Then go to storing sof in git, rather than ruby.
|
||||||
|
|
||||||
Then write a python/java parser and respective runtime conversion. Extracting common features. With the respective
|
Then write a python/java parser and respective runtime conversion. Extracting common features.
|
||||||
to_python on the ast's to roundtrip that too. Have to since by now we work on sof's. Etc . ..
|
With the respective to_python on the ast's to roundtrip that too.
|
||||||
|
Have to since by now we work on sof's. Etc . ..
|
||||||
|
@ -8,30 +8,31 @@ Symbols have similar properties and those are:
|
|||||||
- equality means identity
|
- equality means identity
|
||||||
- no change over lifetime
|
- no change over lifetime
|
||||||
|
|
||||||
It's like with Atoms: they used to be the smallest possible physical unit. Now we have electrons, proton and neutrons.
|
It's like with Atoms: they used to be the smallest possible physical unit. Now we have electrons,
|
||||||
And so objects are made up of Values (not objects), integers, floats , references and possibly more.
|
proton and neutrons. And so objects are made up of Values (not objects), integers, floats ,
|
||||||
|
references and possibly more.
|
||||||
|
|
||||||
Values have type in the same way objects have a class. We keep track of the type of a value at runtime, also in an
|
Values have type in the same way objects have a class. We keep track of the type of a value at runtime,
|
||||||
similar way that objects have their classes at runtime.
|
also in an similar way that objects have their classes at runtime.
|
||||||
|
|
||||||
### Layers
|
### Layers
|
||||||
|
|
||||||
*Ast* instances get created by the salama-reader gem from source. Here we add compile functions to ast classes and
|
*Ast* instances get created by the salama-reader gem from source.
|
||||||
comile the ast layer into Virtual:: objects
|
Here we add compile functions to ast classes and comile the ast layer into Virtual:: objects
|
||||||
|
|
||||||
The main objects are BootSpace (lots of objects), BootClass (represents a class),
|
The main objects are BootSpace (lots of objects), BootClass (represents a class),
|
||||||
CompiledMethod (with Blocks and Instruction).
|
CompiledMethod (with Blocks and Instruction).
|
||||||
|
|
||||||
**Virtual** Instructions get further transformed into **register** instructions. This is done by an abstractly defined
|
**Virtual** Instructions get further transformed into **register** instructions.
|
||||||
Register Machine with basic Intructions. A concrete implementation (like Arm) derives and creates derived
|
This is done by an abstractly defined Register Machine with basic Intructions.
|
||||||
Instructions.
|
A concrete implementation (like Arm) derives and creates derived Instructions.
|
||||||
|
|
||||||
The transformation is implemented as **passes** to make it easier to understand what is going on. Also this makes it
|
The transformation is implemented as **passes** to make it easier to understand what is going on.
|
||||||
easier to add functionality and optimisations from external (to the gem) sources.
|
Also this makes it easier to add functionality and optimisations from external (to the gem) sources.
|
||||||
|
|
||||||
The final transformation assigns Positions to all boot objects (Linker) and assembles them into a binary representation.
|
The final transformation assigns Positions to all boot objects (Linker) and assembles them into a
|
||||||
The data- part is then a representation of classes in the **parfait** runtime. And the instrucions make up the
|
binary representation. The data- part is then a representation of classes in the **parfait** runtime.
|
||||||
funtions.
|
And the instrucions make up the funtions.
|
||||||
|
|
||||||
### Accessible Objects
|
### Accessible Objects
|
||||||
|
|
||||||
@ -42,7 +43,8 @@ Object oriented systems have data hiding. So we have access to the inner state o
|
|||||||
- Frame (local and tmp variables)
|
- Frame (local and tmp variables)
|
||||||
- NewMessage ( to build the next message sent)
|
- NewMessage ( to build the next message sent)
|
||||||
|
|
||||||
A single instructions (Set) allows movement of data between these. There are compare, branch and call intructions too.
|
A single instructions (Set) allows movement of data between these.
|
||||||
|
There are compare, branch and call intructions too.
|
||||||
|
|
||||||
### Micro
|
### Micro
|
||||||
|
|
||||||
@ -53,7 +55,8 @@ As such we are aiming for integer and reference (type) support, and a minimal cl
|
|||||||
(object/class/aray/hash/string). It is possible to add types to the system in a similar way as we add classes,
|
(object/class/aray/hash/string). It is possible to add types to the system in a similar way as we add classes,
|
||||||
and also implement very machine dependent functionality which nevertheless is fully wrapped as OO.
|
and also implement very machine dependent functionality which nevertheless is fully wrapped as OO.
|
||||||
|
|
||||||
**Parfait** is that part of the runtime that can be coded in ruby. It is parsed, like any other code and always included
|
**Parfait** is that part of the runtime that can be coded in ruby.
|
||||||
in the resulting binary. **Builtin** is the part of the runtime that can not be coded in ruby (but is still needed). This
|
It is parsed, like any other code and always included in the resulting binary.
|
||||||
is coded by construction CompiledMethods in code and neccesarily machine dependant.
|
**Builtin** is the part of the runtime that can not be coded in ruby (but is still needed).
|
||||||
|
This is coded by construction CompiledMethods in code and neccesarily machine dependant.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user