2018-04-11 09:45:50 +02:00
|
|
|
|
= render "pages/rubyx/menu"
|
|
|
|
|
|
2018-04-11 18:44:01 +02:00
|
|
|
|
%h1=title "RubyX architectural layers"
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
|
|
|
|
To implement an object system to execute object oriented languages takes a large system.
|
|
|
|
|
The parts or abstraction layers are detailed below.
|
|
|
|
|
%p
|
|
|
|
|
It is important to understand the approach first though, as it differs from the normal
|
|
|
|
|
interpretation. The idea is to
|
|
|
|
|
%strong compile
|
|
|
|
|
ruby. The argument is often made that
|
|
|
|
|
typed languages are faster, but i don’t believe in that. I think dynamic languages
|
|
|
|
|
just push more functionality into the “virtual machine” and it is in fact only the
|
2018-07-25 11:10:08 +02:00
|
|
|
|
compilation to binaries that gives static languages their speed. This is the reason
|
2018-04-10 18:50:07 +02:00
|
|
|
|
to compile ruby.
|
2018-04-11 09:45:50 +02:00
|
|
|
|
%p.center
|
|
|
|
|
= image_tag "layers.jpg" , alt: "Architectural layers"
|
|
|
|
|
|
2018-07-25 11:10:08 +02:00
|
|
|
|
%h3#ruby Ast + Ruby
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
|
|
|
|
To compile and run ruby, we first need to parse ruby. While parsing ruby is quite
|
|
|
|
|
a difficult task, it has already been implemented in pure ruby
|
2018-04-11 09:45:50 +02:00
|
|
|
|
= succeed "." do
|
|
|
|
|
=ext_link "here" ,"https://github.com/whitequark/parser"
|
|
|
|
|
The output of the parser is
|
2018-04-10 18:50:07 +02:00
|
|
|
|
an ast, which holds information about the code in instances of a single
|
|
|
|
|
%em Node
|
|
|
|
|
class.
|
2018-04-11 09:45:50 +02:00
|
|
|
|
Nodes have a type attribute (which you sometimes see in s-expressions) and a list of children.
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
2018-07-25 11:10:08 +02:00
|
|
|
|
The first layer in RubyX is the
|
|
|
|
|
%b Ruby Layer
|
|
|
|
|
that basically models the ast in a concrete syntax tree. This means there is one class
|
|
|
|
|
per node type.
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
2018-07-25 11:10:08 +02:00
|
|
|
|
The Ruby layer is then used to transform the data into the Vool layer. Eg: Implicit
|
|
|
|
|
block passing is made explicit, conditions are simplified, arguments are simplified,
|
|
|
|
|
and syntactic sugar is removed.
|
2018-04-11 09:45:50 +02:00
|
|
|
|
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%h3#virtual-object-oriented-language Virtual Object Oriented Language
|
|
|
|
|
%p
|
|
|
|
|
Virtual, in this context, means that there is no syntax for this language; it is an
|
|
|
|
|
intermediate representation which
|
|
|
|
|
%em could
|
2018-04-11 09:45:50 +02:00
|
|
|
|
be targeted by several real languages.
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
|
|
|
|
The main purpose is to simplify existing oo languages down to it’s core components: mostly
|
2018-07-25 11:10:08 +02:00
|
|
|
|
calling, assignment, continuations and exceptions. Typed classes for each language
|
|
|
|
|
construct exist and are responsible to transform a statement into Mom level below.
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
|
|
|
|
Examples for things that exist in ruby but are broken down in Vool are
|
|
|
|
|
%em unless
|
2018-04-11 09:45:50 +02:00
|
|
|
|
,
|
|
|
|
|
%em ternary operator
|
|
|
|
|
,
|
|
|
|
|
%em do while
|
|
|
|
|
or
|
|
|
|
|
%em for
|
|
|
|
|
loops and other similar syntactic sugar.
|
|
|
|
|
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%h3#minimal-object-machine Minimal Object machine
|
|
|
|
|
%p
|
|
|
|
|
We compile Vool statements into Mom instructions. Mom is a machine, which means it has
|
|
|
|
|
instructions. But unlike a cpu (or the risc layer below) it does not have memory, only objects.
|
|
|
|
|
It also has no registers, and together these two things mean that all information is stored in
|
|
|
|
|
objects. Also the calling convention is object based and uses Frame and Message instances to
|
|
|
|
|
save state.
|
|
|
|
|
%p
|
|
|
|
|
Objects are typed, and are in fact the same objects the language operates on. Just the
|
2018-07-25 11:10:08 +02:00
|
|
|
|
functionality is expressed through instructions. While source methods are in defined on
|
|
|
|
|
classes as Vool, when they are compiled to binary, they are made type specific. These
|
|
|
|
|
TypedMethods hold the binary and are stored in the Type.
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
2018-04-11 09:45:50 +02:00
|
|
|
|
The Mom level exists to make the transition to Risc easier. It has a very abstract,
|
2018-07-25 11:10:08 +02:00
|
|
|
|
high level instruction set, where each single instruction may resolve to many (even tens of)
|
|
|
|
|
lower level instructions. But it breaks down Vool's tree into an instruction
|
2018-04-11 09:45:50 +02:00
|
|
|
|
list, which is conceptually a much easier input for the next layer.
|
|
|
|
|
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%h3#risc Risc
|
|
|
|
|
%p
|
|
|
|
|
The Register machine layer is a relatively close abstraction of risc hardware, but without the
|
|
|
|
|
quirks.
|
|
|
|
|
%p
|
|
|
|
|
The Risc machine has registers, indexed addressing, operators, branches and everything
|
2018-04-11 09:45:50 +02:00
|
|
|
|
needed to implement Mom. It does not try to abstract every possible machine feature
|
2018-04-10 18:50:07 +02:00
|
|
|
|
(like llvm), but rather “objectifies” the general risc view to provide what is needed for
|
2018-07-25 11:10:08 +02:00
|
|
|
|
the Mom layer, the next layer up (and actually Builtin functions).
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%p
|
|
|
|
|
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).
|
|
|
|
|
%p
|
|
|
|
|
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 the same objects will be used at runtime and compile time.
|
|
|
|
|
%p
|
|
|
|
|
Since working with at this low machine level (essentially assembler) is not easy to follow for
|
|
|
|
|
everyone (me :-), an interpreter was created (by me:-). Later a graphical interface, a kind of
|
|
|
|
|
%a{:href => "https://github.com/ruby-x/rubyx-debugger"} 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.
|
2018-04-11 09:45:50 +02:00
|
|
|
|
|
2018-04-10 18:50:07 +02:00
|
|
|
|
%h3#binary--arm-and-elf Binary , Arm and Elf
|
|
|
|
|
%p
|
|
|
|
|
A physical machine will run binaries containing instructions that the cpu understands, in a
|
|
|
|
|
format the operating system understands (elf). Arm and elf subdirectories hold the code for
|
|
|
|
|
these layers.
|
|
|
|
|
%p
|
|
|
|
|
Arm is a risc architecture, but anyone who knows it will attest, with it’s own quirks.
|
2018-04-11 09:45:50 +02:00
|
|
|
|
For example any instruction may be executed conditionally, ie
|
|
|
|
|
%em every
|
|
|
|
|
instruction carries bits to make it check the status register. Or the fact that 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.
|
|
|
|
|
%p
|
|
|
|
|
The Arm::Translator translates that translates RegisterInstructions to ArmInstructions,
|
|
|
|
|
and the Elf::ObjectWriter creates Linux binaries.
|