new page for the builder dsl
This commit is contained in:
parent
089db053a9
commit
0d44a9376c
2
Gemfile
2
Gemfile
@ -19,6 +19,8 @@ gem "rubyx" , "0.6" , git: "https://github.com/ruby-x/rubyx" , require: false
|
|||||||
gem "rubyx-debugger" , "0.3" , git: "https://github.com/ruby-x/rubyx-debugger" , require: false
|
gem "rubyx-debugger" , "0.3" , git: "https://github.com/ruby-x/rubyx-debugger" , require: false
|
||||||
gem "rx-file" , git: "https://github.com/ruby-x/rx-file"
|
gem "rx-file" , git: "https://github.com/ruby-x/rx-file"
|
||||||
|
|
||||||
|
gem "haml-coderay"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
|
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
|
@ -15,7 +15,7 @@ GIT
|
|||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/ruby-x/rubyx-debugger
|
remote: https://github.com/ruby-x/rubyx-debugger
|
||||||
revision: 24961e2e101d77080a6a038105354e290ec2afc1
|
revision: bba8f787d531d9e9cb1a9ce2f6b93bbb1e22dd3a
|
||||||
specs:
|
specs:
|
||||||
rubyx-debugger (0.3)
|
rubyx-debugger (0.3)
|
||||||
|
|
||||||
@ -104,6 +104,7 @@ GEM
|
|||||||
capybara-screenshot (1.0.19)
|
capybara-screenshot (1.0.19)
|
||||||
capybara (>= 1.0, < 4)
|
capybara (>= 1.0, < 4)
|
||||||
launchy
|
launchy
|
||||||
|
coderay (1.1.2)
|
||||||
concurrent-ruby (1.0.5)
|
concurrent-ruby (1.0.5)
|
||||||
crass (1.0.4)
|
crass (1.0.4)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
@ -116,6 +117,9 @@ GEM
|
|||||||
haml (5.0.4)
|
haml (5.0.4)
|
||||||
temple (>= 0.8.0)
|
temple (>= 0.8.0)
|
||||||
tilt
|
tilt
|
||||||
|
haml-coderay (0.2.0)
|
||||||
|
coderay
|
||||||
|
haml
|
||||||
haml-rails (1.0.0)
|
haml-rails (1.0.0)
|
||||||
actionpack (>= 4.0.1)
|
actionpack (>= 4.0.1)
|
||||||
activesupport (>= 4.0.1)
|
activesupport (>= 4.0.1)
|
||||||
@ -297,6 +301,7 @@ DEPENDENCIES
|
|||||||
capistrano-rails-console
|
capistrano-rails-console
|
||||||
capybara
|
capybara
|
||||||
capybara-screenshot
|
capybara-screenshot
|
||||||
|
haml-coderay
|
||||||
haml-rails
|
haml-rails
|
||||||
high_voltage
|
high_voltage
|
||||||
listen (>= 3.0.5, < 3.2)
|
listen (>= 3.0.5, < 3.2)
|
||||||
|
@ -5,4 +5,5 @@
|
|||||||
%li= link_to "Debugger", "debugger.html"
|
%li= link_to "Debugger", "debugger.html"
|
||||||
%li= link_to "Memory" , "memory.html"
|
%li= link_to "Memory" , "memory.html"
|
||||||
%li= link_to "Calling" , "calling.html"
|
%li= link_to "Calling" , "calling.html"
|
||||||
|
%li= link_to "Assembler Dsl" , "builder.html"
|
||||||
%li= link_to "Method Resolution" , "method_resolution.html"
|
%li= link_to "Method Resolution" , "method_resolution.html"
|
||||||
|
121
app/views/pages/rubyx/builder.html.haml
Normal file
121
app/views/pages/rubyx/builder.html.haml
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
= render "pages/rubyx/menu"
|
||||||
|
|
||||||
|
%h1=title "Generating assembler by dsl"
|
||||||
|
|
||||||
|
%p
|
||||||
|
In the end everything boils down to risc instruction generation. From there on down,
|
||||||
|
it is pretty much mechanics. And everything between ruby and the risc layer is to make
|
||||||
|
the step towards risc easier.
|
||||||
|
%p
|
||||||
|
Risc is after all very much like assembler, and programming any system in assembler is
|
||||||
|
challenging. The simple object model helps, and so does the simple risc instruction set,
|
||||||
|
but for a long time i was struggling to create readable code that generates the risc.
|
||||||
|
%p
|
||||||
|
The main places we find this code is the mom layer, eg in the
|
||||||
|
=link_to "calling convention" , "calling.html"
|
||||||
|
and in the Builtin module, generating code that can not be expressed in ruby.
|
||||||
|
|
||||||
|
%h2 Compiler
|
||||||
|
%p
|
||||||
|
When the code goes from ruby to vool, or vool to mom, methods on the previous (tree)
|
||||||
|
structure return the next structure. But going to risc the code evolved to use a different
|
||||||
|
approach. The unit of compilation is a block or method, and the respective
|
||||||
|
%b Compiler
|
||||||
|
is passed into the generating method.
|
||||||
|
%p
|
||||||
|
The risc code is then added directly to the compiler. If anything is returned it is
|
||||||
|
used by the caller for other things. This change happened as the compiler needed to
|
||||||
|
be passed in as it carries the scope, ie is needed for variable resolution.
|
||||||
|
%p
|
||||||
|
It is also easier to understand (albeit maybe after getting used to it), as the generation
|
||||||
|
of the structure after the return was not intuitive.
|
||||||
|
The Compiler's central method to capture the code, is
|
||||||
|
%b add_code,
|
||||||
|
and the argument must be a risc instruction.
|
||||||
|
%p
|
||||||
|
While this worked (and works) the resulting code is not very concise, cluttered with all
|
||||||
|
kind of namespace issues and details.
|
||||||
|
|
||||||
|
%h2 Builder
|
||||||
|
%p
|
||||||
|
From these difficulties was born the DSL approach that is implemented in the
|
||||||
|
%b Builder.
|
||||||
|
The Builder uses method_missing to resolve names to registers. Those registers, or more
|
||||||
|
precisely RegisterValues, are then used to generate risc instruction by overloading
|
||||||
|
operators.
|
||||||
|
%p
|
||||||
|
Most of the work at the risc level , probably 60% or more, is shuffling data around.
|
||||||
|
This is covered by the four instruction SlotToReg, RegToSlot, Transfer and LoadConstant
|
||||||
|
using the
|
||||||
|
%b <<
|
||||||
|
operator with different arguments. See below:
|
||||||
|
.container_full
|
||||||
|
.half_left
|
||||||
|
:coderay
|
||||||
|
#!ruby
|
||||||
|
def build_message_data( builder )
|
||||||
|
builder.build do
|
||||||
|
space? << Parfait.object_space
|
||||||
|
next_message? << space[:next_message]
|
||||||
|
|
||||||
|
next_message_reg! << next_message[:next_message]
|
||||||
|
space[:next_message] << next_message_reg
|
||||||
|
....
|
||||||
|
end
|
||||||
|
.half_right
|
||||||
|
%ul
|
||||||
|
%li Variable definitions with ? or ! . Variables must be defined before use.
|
||||||
|
%li space becomes a variable, or a named register
|
||||||
|
%li First line generates a constant_load, because the right side is a constant
|
||||||
|
%li second (and third) line generates a SlotToReg , because the left is a register and the right is a slot
|
||||||
|
%li fourth line generates a RegToSlot, as the left side is a slot, and the right a register
|
||||||
|
%li all generated instructions are automatically added to the compiler
|
||||||
|
%p
|
||||||
|
As you can see the code is quite readable. Mapping names to the registers makes them
|
||||||
|
feel like normal variables. (They are not off course, they are instances of RegisterValue
|
||||||
|
stores in a hash in Builder).
|
||||||
|
The overloading of [] , which just creates an intermediate RValue, makes the it look like
|
||||||
|
the result of the array access is transferred to the register. And that is exactly what is
|
||||||
|
happening. The next_message on the right is a register, and the array indexing access the
|
||||||
|
"register" is an object. The "index", also called next_message is an instance variable.
|
||||||
|
The builder magic looks up the index in the type (Message) and does an indexed
|
||||||
|
memory access, which is exactly what a SlotToReg is.
|
||||||
|
|
||||||
|
%p{style: "clear: both;"}
|
||||||
|
There are also other ways to use the Builder: One is by just using the instance
|
||||||
|
methods, ie any code can be added with add_code, also inside the block
|
||||||
|
.container_full
|
||||||
|
.half_left
|
||||||
|
:coderay
|
||||||
|
#!ruby
|
||||||
|
if_zero ok_label
|
||||||
|
...
|
||||||
|
branch while_start_label
|
||||||
|
add_code exit_label
|
||||||
|
.half_right
|
||||||
|
%ul
|
||||||
|
%li Here we use the if_zero method, that generates a IfZero instruction
|
||||||
|
%li The second line is a creates a Branch, much the same way
|
||||||
|
%li The last line adds a label, using the add_code
|
||||||
|
%p{style: "clear: both;"}
|
||||||
|
And in these last examples we see how operators can be called, or indeed any
|
||||||
|
generating function can be called
|
||||||
|
.container_full
|
||||||
|
.half_left
|
||||||
|
:coderay
|
||||||
|
#!ruby
|
||||||
|
builder.build do
|
||||||
|
integer_const! << 1
|
||||||
|
integer_tmp.op :>> , integer_const
|
||||||
|
...
|
||||||
|
Risc::Builtin::Object.emit_syscall( builder , :exit )
|
||||||
|
...
|
||||||
|
.half_right
|
||||||
|
%ul
|
||||||
|
%li The first line load a fixnum, which is LoadData in risc terms
|
||||||
|
%li
|
||||||
|
The second line calls an operator >>, which gets translated to an
|
||||||
|
OperatorInstruction, that leaves the result in the second argument
|
||||||
|
%li
|
||||||
|
The last line invokes some shared code that does an system exit.
|
||||||
|
This is in essence much like inlining the exit code.
|
@ -48,7 +48,7 @@
|
|||||||
But it should definitely be expanded, and there is nothing about Builtin.
|
But it should definitely be expanded, and there is nothing about Builtin.
|
||||||
Builtin is the way to write methods that can not be expressed in ruby. And since
|
Builtin is the way to write methods that can not be expressed in ruby. And since
|
||||||
writing them got so messy i wrote a DSL, which is only documented in
|
writing them got so messy i wrote a DSL, which is only documented in
|
||||||
=link_to "code." , "https://github.com/ruby-x/rubyx/blob/master/lib/risc/builder.rb"
|
=ext_link "code." , "https://github.com/ruby-x/rubyx/blob/master/lib/risc/builder.rb"
|
||||||
%h3 Calling
|
%h3 Calling
|
||||||
%p
|
%p
|
||||||
Since Calling is now done, i documented both the
|
Since Calling is now done, i documented both the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user