Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6312b0962e |
47
.gitignore
vendored
47
.gitignore
vendored
@ -1,9 +1,42 @@
|
|||||||
|
# database
|
||||||
|
db
|
||||||
|
|
||||||
|
# rdoc generated
|
||||||
|
rdoc
|
||||||
|
|
||||||
|
# yard generated
|
||||||
|
doc
|
||||||
|
.yardoc
|
||||||
|
|
||||||
|
# bundler
|
||||||
.bundle
|
.bundle
|
||||||
.config
|
*.gem
|
||||||
.yardoc
|
|
||||||
tmp
|
# jeweler generated
|
||||||
.idea
|
pkg
|
||||||
.yardoc
|
|
||||||
.sass-cache
|
#
|
||||||
|
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
||||||
|
#
|
||||||
|
# For MacOS:
|
||||||
|
#
|
||||||
.DS_Store
|
.DS_Store
|
||||||
compiled
|
|
||||||
|
# For TextMate
|
||||||
|
#*.tmproj
|
||||||
|
#tmtags
|
||||||
|
|
||||||
|
# For emacs:
|
||||||
|
#*~
|
||||||
|
#\#*
|
||||||
|
#.\#*
|
||||||
|
|
||||||
|
# For vim:
|
||||||
|
#*.swp
|
||||||
|
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
log
|
||||||
|
|
||||||
|
tmp/
|
||||||
|
26
Gemfile
26
Gemfile
@ -1,21 +1,17 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem "opal"
|
gem 'opal-react', :path => '../react.rb'
|
||||||
gem 'opal-sprockets'
|
gem 'opal-jquery'
|
||||||
gem 'opal-browser'
|
gem 'react-source'
|
||||||
|
|
||||||
gem "rubyx" , "0.6" , path: "../rubyx"
|
gem "parslet" , github: "salama/parslet"
|
||||||
|
gem "salama" , github: "salama/salama"
|
||||||
|
gem "salama-reader" , github: "salama/salama-reader"
|
||||||
|
gem "salama-arm" , github: "salama/salama-arm"
|
||||||
|
gem "salama-object-file" , github: "salama/salama-object-file"
|
||||||
|
gem "susy"
|
||||||
|
|
||||||
gem "rx-file" , :git => "https://github.com/ruby-x/rx-file"
|
group :development do
|
||||||
|
|
||||||
gem "sass"
|
|
||||||
|
|
||||||
group :test do
|
|
||||||
# Testing dependencies
|
|
||||||
gem "minitest"
|
gem "minitest"
|
||||||
gem 'rspec'
|
gem "rubygems-tasks"
|
||||||
gem 'capybara'
|
|
||||||
gem 'selenium-webdriver'
|
|
||||||
gem 'chromedriver2-helper'
|
|
||||||
gem 'poltergeist'
|
|
||||||
end
|
end
|
||||||
|
157
Gemfile.lock
157
Gemfile.lock
@ -1,120 +1,83 @@
|
|||||||
GIT
|
GIT
|
||||||
remote: https://github.com/ruby-x/rx-file
|
remote: git://github.com/salama/parslet.git
|
||||||
revision: 7c4a5546136d1bad065803da91778b209c18cb4d
|
revision: beeb9b441a9ade1504f7f0e848d805e36a02c544
|
||||||
specs:
|
specs:
|
||||||
rx-file (0.3.0)
|
parslet (1.7.0)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/salama/salama-arm.git
|
||||||
|
revision: 0bd5091e3f284ecf040e0086a41d2449cd5afb7a
|
||||||
|
specs:
|
||||||
|
salama-arm (0.0.1)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/salama/salama-object-file.git
|
||||||
|
revision: fbae6a02764dbe97e01e4833f9ffffe09879b100
|
||||||
|
specs:
|
||||||
|
salama-object-file (0.2.0)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/salama/salama-reader.git
|
||||||
|
revision: 841592c667acea1e796f950851262e6938b231bc
|
||||||
|
specs:
|
||||||
|
salama-reader (0.2.0)
|
||||||
|
parslet (~> 1.7.0)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/salama/salama.git
|
||||||
|
revision: 3fb08acf3f83aa403b095aa60be1702419d8a66d
|
||||||
|
specs:
|
||||||
|
salama (0.2.0)
|
||||||
|
salama-object-file (~> 0.2)
|
||||||
|
salama-reader (~> 0.2)
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: ../rubyx
|
remote: ../react.rb
|
||||||
specs:
|
specs:
|
||||||
rubyx (0.6.0)
|
opal-react (0.2.2)
|
||||||
parser (~> 2.3.0)
|
opal
|
||||||
rx-file (~> 0.3)
|
opal-activesupport
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
addressable (2.6.0)
|
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
|
||||||
ast (2.4.0)
|
|
||||||
capybara (3.13.2)
|
|
||||||
addressable
|
|
||||||
mini_mime (>= 0.1.3)
|
|
||||||
nokogiri (~> 1.8)
|
|
||||||
rack (>= 1.6.0)
|
|
||||||
rack-test (>= 0.6.3)
|
|
||||||
regexp_parser (~> 1.2)
|
|
||||||
xpath (~> 3.2)
|
|
||||||
childprocess (0.9.0)
|
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
|
||||||
chromedriver2-helper (0.0.10)
|
|
||||||
nokogiri
|
|
||||||
cliver (0.3.2)
|
|
||||||
concurrent-ruby (1.1.4)
|
|
||||||
diff-lcs (1.3)
|
|
||||||
ffi (1.10.0)
|
|
||||||
hike (1.2.3)
|
hike (1.2.3)
|
||||||
mini_mime (1.0.1)
|
minitest (5.7.0)
|
||||||
mini_portile2 (2.4.0)
|
opal (0.8.0)
|
||||||
minitest (5.11.3)
|
|
||||||
nokogiri (1.10.1)
|
|
||||||
mini_portile2 (~> 2.4.0)
|
|
||||||
opal (0.11.4)
|
|
||||||
ast (>= 2.3.0)
|
|
||||||
hike (~> 1.2)
|
hike (~> 1.2)
|
||||||
parser (= 2.3.3.1)
|
|
||||||
sourcemap (~> 0.1.0)
|
sourcemap (~> 0.1.0)
|
||||||
opal-browser (0.2.0)
|
|
||||||
opal
|
|
||||||
paggio
|
|
||||||
opal-sprockets (0.4.2.0.11.0.3.1)
|
|
||||||
opal (~> 0.11.0)
|
|
||||||
sprockets (~> 3.1)
|
sprockets (~> 3.1)
|
||||||
tilt (>= 1.4)
|
tilt (>= 1.4)
|
||||||
paggio (0.2.6)
|
opal-activesupport (0.1.0)
|
||||||
parser (2.3.3.1)
|
opal (>= 0.5.0, < 1.0.0)
|
||||||
ast (~> 2.2)
|
opal-jquery (0.4.0)
|
||||||
poltergeist (1.18.1)
|
opal (>= 0.7.0, < 0.9.0)
|
||||||
capybara (>= 2.1, < 4)
|
rack (1.6.4)
|
||||||
cliver (~> 0.3.1)
|
react-source (0.13.3)
|
||||||
websocket-driver (>= 0.2.0)
|
rubygems-tasks (0.2.4)
|
||||||
public_suffix (3.0.3)
|
sass (3.4.16)
|
||||||
rack (2.0.6)
|
|
||||||
rack-test (1.1.0)
|
|
||||||
rack (>= 1.0, < 3)
|
|
||||||
rb-fsevent (0.10.3)
|
|
||||||
rb-inotify (0.10.0)
|
|
||||||
ffi (~> 1.0)
|
|
||||||
regexp_parser (1.3.0)
|
|
||||||
rspec (3.8.0)
|
|
||||||
rspec-core (~> 3.8.0)
|
|
||||||
rspec-expectations (~> 3.8.0)
|
|
||||||
rspec-mocks (~> 3.8.0)
|
|
||||||
rspec-core (3.8.0)
|
|
||||||
rspec-support (~> 3.8.0)
|
|
||||||
rspec-expectations (3.8.2)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.8.0)
|
|
||||||
rspec-mocks (3.8.0)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.8.0)
|
|
||||||
rspec-support (3.8.0)
|
|
||||||
rubyzip (1.2.2)
|
|
||||||
sass (3.7.3)
|
|
||||||
sass-listen (~> 4.0.0)
|
|
||||||
sass-listen (4.0.0)
|
|
||||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
|
||||||
rb-inotify (~> 0.9, >= 0.9.7)
|
|
||||||
selenium-webdriver (3.141.0)
|
|
||||||
childprocess (~> 0.5)
|
|
||||||
rubyzip (~> 1.2, >= 1.2.2)
|
|
||||||
sourcemap (0.1.1)
|
sourcemap (0.1.1)
|
||||||
sprockets (3.7.2)
|
sprockets (3.2.0)
|
||||||
concurrent-ruby (~> 1.0)
|
rack (~> 1.0)
|
||||||
rack (> 1, < 3)
|
susy (2.2.5)
|
||||||
tilt (2.0.9)
|
sass (>= 3.3.0, < 3.5)
|
||||||
websocket-driver (0.7.0)
|
tilt (2.0.1)
|
||||||
websocket-extensions (>= 0.1.0)
|
|
||||||
websocket-extensions (0.1.3)
|
|
||||||
xpath (3.2.0)
|
|
||||||
nokogiri (~> 1.8)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
capybara
|
|
||||||
chromedriver2-helper
|
|
||||||
minitest
|
minitest
|
||||||
opal
|
opal-jquery
|
||||||
opal-browser
|
opal-react!
|
||||||
opal-sprockets
|
parslet!
|
||||||
poltergeist
|
react-source
|
||||||
rspec
|
rubygems-tasks
|
||||||
rubyx (= 0.6)!
|
salama!
|
||||||
rx-file!
|
salama-arm!
|
||||||
sass
|
salama-object-file!
|
||||||
selenium-webdriver
|
salama-reader!
|
||||||
|
susy
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.17.2
|
1.10.5
|
||||||
|
127
README.md
127
README.md
@ -1,117 +1,36 @@
|
|||||||
# Debugger
|
# Debugger
|
||||||
|
|
||||||
After some tryouts it ended up being an Opal application. That is ruby as javascript in the browser.
|
After some tryouts it ended up being an Opal application. That is ruby as javascript in the browser.
|
||||||
Below is a screenshot.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Views
|
- On the left are the classes of the system. Next idea is to have hover info about them.
|
||||||
|
- Next a source code view (not implemented)
|
||||||
|
- next a view of the Virtual Instructions
|
||||||
|
- last section, current block with current Register Instruction highlighted
|
||||||
|
- step (next) button for single stepping
|
||||||
|
- status: starting , running , exited
|
||||||
|
- bottom row are the registers. If the register hold an object the variables are shown.
|
||||||
|
(also should have hover info) , the first letter indicates the class, the number is the address
|
||||||
|
|
||||||
From left to right there are several views showing different data and controls.
|
So lots to do, but a good start.
|
||||||
All of the green boxes are in fact pop-up menus and can show more information.
|
|
||||||
Most of these are implemented as a single class with the name reflecting what part.
|
|
||||||
I wrote 2 base classes that handle element generation (ie there is hardly any html involved, just elements)
|
|
||||||
|
|
||||||
### Switch view
|
|
||||||
|
|
||||||
Top left at the top is a little control to switch files.
|
|
||||||
The files need to be in the repository, but at least one can have several and switch between
|
|
||||||
them without stopping the debugger.
|
|
||||||
|
|
||||||
Parsing is the only thing that opal chokes on, so the files are parsed by a server script and the
|
|
||||||
ast is sent to the browser.
|
|
||||||
|
|
||||||
### Classes View
|
|
||||||
|
|
||||||
The first column on the left is a list of classes in the system. Like on all boxes one can hover
|
|
||||||
over a name to look at the class and it's instance variables (recursively)
|
|
||||||
|
|
||||||
|
|
||||||
### Source View
|
|
||||||
|
|
||||||
Next is a view of the Soml source. The Source is reconstructed from the ast as html.
|
I don't want to use gdb anymore, and it would be easier without using the qemu setup, so:
|
||||||
Soml (Salama object machine language) is is a statically typed language,
|
|
||||||
maybe in spirit close to c++ (without the c). In the future Salama will compile ruby to soml.
|
|
||||||
|
|
||||||
While stepping through the code, those parts of the code that are active get highlighted in blue.
|
- single step debugging of the register machine level (as close to arm as need be)
|
||||||
|
- visual transitions for steps
|
||||||
Currently stepping is done only in register instructions, which means that depending on the source
|
- visualisation of data in registers (some kind of link to the object)
|
||||||
constructs it may take many steps for the cursor to move on.
|
- show the current instruction and a few around
|
||||||
|
- show vm object (message etc)
|
||||||
Each step will show progress on the register level though (next view)
|
- show effect of register transitions on vm objects
|
||||||
|
- visualize vm object content (again some links)
|
||||||
|
|
||||||
|
|
||||||
### Register Instruction view
|
# Space
|
||||||
|
|
||||||
Salama defines a register machine level which is quite close to the arm machine, but with more
|
- Visualise the object space in some way
|
||||||
sensible names. It has 16 registers (below) and an instruction set that is useful for Soml.
|
- Visualise single object, bit like atoms
|
||||||
|
- values immediate
|
||||||
Data movement related instruction implement an indexed get and set. There is also Constant load and
|
- objects as link
|
||||||
integer operators and off course branches.
|
|
||||||
Instructions print their name and used registers r0-r15.
|
|
||||||
|
|
||||||
The next instruction to be executed is highlighted in blue. A list of previous instructions is shown.
|
|
||||||
|
|
||||||
One can follow the effect of instruction in the register view below.
|
|
||||||
|
|
||||||
### Status View
|
|
||||||
|
|
||||||
The last view at the top right show the status of the machine (interpreter to be precise), the
|
|
||||||
instruction count and any stdout
|
|
||||||
|
|
||||||
Current controls include stepping and three speeds of running the program.
|
|
||||||
|
|
||||||
- Next (green button) will execute exactly one instruction when clicked. Mostly useful when
|
|
||||||
debugging the compiler, ie inspecting the generated code.
|
|
||||||
- Crawl (first blue button) will execute at a moderate speed. One can still follow the
|
|
||||||
logic at the register level
|
|
||||||
- Run (second blue button) runs the program at a higher speed where register instruction just
|
|
||||||
whizz by, but one can still follow the source view. Mainly used to verify that the source executes
|
|
||||||
as expected and also to get to a specific place in the program (in the absence of breakpoints)
|
|
||||||
- Wizz (third blue button) makes the program run so fast that it's only useful function is to
|
|
||||||
fast forward in the code (while debugging)
|
|
||||||
|
|
||||||
### Register view
|
|
||||||
|
|
||||||
The bottom part of the screen is taken up by the 16 register. As we execute an object oriented
|
|
||||||
language, we show the object contents if it is an object (not an integer) in a register.
|
|
||||||
|
|
||||||
The (virtual) machine only uses objects, and specifically a linked list of Message objects to
|
|
||||||
make calls. The current message is always in register 0 (analgous to a stack pointer).
|
|
||||||
All other registers are scratch for statement use.
|
|
||||||
|
|
||||||
In Soml expressions compile to the register that holds the expressions value and statements may use
|
|
||||||
all registers and may not rely on anything other than the message in register 0.
|
|
||||||
|
|
||||||
|
|
||||||
The Register view is now greatly improved, especially in it's dynamic features:
|
|
||||||
|
|
||||||
- when the contents update the register obviously updates
|
|
||||||
- when the object that the register holds updates, the new value is shown immediately
|
|
||||||
- hovering over a variable will **expand that variable** .
|
|
||||||
- the hovering works recursively, so it is possible to drill down into objects for several levels
|
|
||||||
|
|
||||||
The last feature of inspecting objects is show in the screenshot. This makes it possible
|
|
||||||
to very quickly verify the programs behaviour. As it is a pure object system , all data is in
|
|
||||||
objects, and all objects can be inspected.
|
|
||||||
|
|
||||||
### Debugging the debugger
|
|
||||||
|
|
||||||
Opal is pre 1.0 and is a wip. While current source map support is quite good, one only gets
|
|
||||||
real lines when switching debug on. Debug make it load every single file seperately, slooows it
|
|
||||||
down in other words. Set DEBUG environment to switch it on.
|
|
||||||
|
|
||||||
I set the sprockets cache to mem-cache and that increase load time from 12s to 1 , so it's quite
|
|
||||||
usable and restarting a debug is fine.
|
|
||||||
|
|
||||||
## Todos
|
|
||||||
|
|
||||||
Breakpoints would be nice at some point. Both in step count and variable value.
|
|
||||||
|
|
||||||
## Trying it out
|
|
||||||
|
|
||||||
Clone
|
|
||||||
|
|
||||||
Bundle
|
|
||||||
|
|
||||||
bundle exec rackup
|
|
||||||
|
50
app/block_view.rb
Normal file
50
app/block_view.rb
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
require "instruction_view"
|
||||||
|
|
||||||
|
class BlockView
|
||||||
|
|
||||||
|
|
||||||
|
include React::Component
|
||||||
|
required_param :interpreter
|
||||||
|
|
||||||
|
define_state :block => []
|
||||||
|
define_state :block_name => ""
|
||||||
|
|
||||||
|
before_mount do
|
||||||
|
interpreter.register_event(:instruction_changed, self)
|
||||||
|
update_block
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_block
|
||||||
|
return unless interpreter.instruction
|
||||||
|
block_name! interpreter.block.name
|
||||||
|
codes = interpreter.block.codes.dup
|
||||||
|
slice = codes.index(interpreter.instruction) #- 1
|
||||||
|
codes.shift( slice ) if slice >= 0
|
||||||
|
codes.pop while(codes.length > 4)
|
||||||
|
block! codes
|
||||||
|
end
|
||||||
|
|
||||||
|
def instruction_changed
|
||||||
|
update_block
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
return unless block
|
||||||
|
div.block_view do
|
||||||
|
div do
|
||||||
|
h4 { method_name}
|
||||||
|
h4 {"Block: #{block_name}"}
|
||||||
|
end
|
||||||
|
block.each do |code|
|
||||||
|
InstructionView :interpreter => interpreter , :instruction => code
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_name
|
||||||
|
bl = interpreter.block
|
||||||
|
return bl.method if bl.method.is_a? String
|
||||||
|
"#{bl.method.for_class.name}.#{bl.method.name}"
|
||||||
|
end
|
||||||
|
end
|
16
app/class_view.rb
Normal file
16
app/class_view.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class ClassView
|
||||||
|
include React::Component
|
||||||
|
|
||||||
|
required_param :classes, type: {}
|
||||||
|
|
||||||
|
def render
|
||||||
|
div.classes do
|
||||||
|
h4 { "Classes" }
|
||||||
|
classes.each do |name , clas|
|
||||||
|
div.one_class do
|
||||||
|
clas.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
37
app/debugger.rb
Normal file
37
app/debugger.rb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
require "register_view"
|
||||||
|
require "class_view"
|
||||||
|
require "source_view"
|
||||||
|
require "block_view"
|
||||||
|
require "status_view"
|
||||||
|
|
||||||
|
class Debugger
|
||||||
|
|
||||||
|
include React::Component
|
||||||
|
required_param :machine , :type => Virtual::Machine
|
||||||
|
define_state :interpreter => Interpreter.new
|
||||||
|
|
||||||
|
before_mount do
|
||||||
|
code = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:putstring, [] ,Ast::StringExpression.new("Hello again"))])
|
||||||
|
Virtual::Compiler.compile( code , machine.space.get_main )
|
||||||
|
machine.run_before "Register::CallImplementation"
|
||||||
|
interpreter.start machine.init
|
||||||
|
end
|
||||||
|
def render
|
||||||
|
div.debugger_view do
|
||||||
|
ClassView classes: machine.space.classes
|
||||||
|
div.file_view do
|
||||||
|
"Future Source code view"
|
||||||
|
end
|
||||||
|
SourceView :interpreter => interpreter
|
||||||
|
BlockView :interpreter => interpreter
|
||||||
|
StatusView :interpreter => interpreter
|
||||||
|
div.registers_view do
|
||||||
|
interpreter.registers.each do |r , oid|
|
||||||
|
RegisterView interpreter: interpreter , register: r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
28
app/instruction_view.rb
Normal file
28
app/instruction_view.rb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class InstructionView
|
||||||
|
|
||||||
|
include React::Component
|
||||||
|
required_param :interpreter
|
||||||
|
required_param :instruction
|
||||||
|
|
||||||
|
define_state :active => ""
|
||||||
|
|
||||||
|
before_mount do
|
||||||
|
check_active interpreter.instruction
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_active i
|
||||||
|
active! instruction == i ? "bright" : ""
|
||||||
|
|
||||||
|
end
|
||||||
|
def instruction_changed old , ins
|
||||||
|
check_active ins
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
div :class => active do
|
||||||
|
instruction.to_s if instruction
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
app/main.rb
Normal file
16
app/main.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
require 'opal'
|
||||||
|
require "opal/parser"
|
||||||
|
|
||||||
|
require "salama"
|
||||||
|
require "interpreter"
|
||||||
|
|
||||||
|
require 'opal-react'
|
||||||
|
|
||||||
|
require "debugger"
|
||||||
|
|
||||||
|
require 'opal-jquery'
|
||||||
|
|
||||||
|
Document.ready? do # Document.ready? is a opal-jquery method.
|
||||||
|
machine = Virtual.machine.boot
|
||||||
|
React.render( React.create_element( Debugger , :machine => machine ), Element['#content'] )
|
||||||
|
end
|
64
app/register_view.rb
Normal file
64
app/register_view.rb
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
class RegisterView
|
||||||
|
|
||||||
|
include React::Component
|
||||||
|
required_param :interpreter
|
||||||
|
required_param :register
|
||||||
|
|
||||||
|
define_state :objects_id
|
||||||
|
define_state :fields => []
|
||||||
|
|
||||||
|
before_mount do
|
||||||
|
interpreter.register_event(:register_changed, self)
|
||||||
|
interpreter.register_event(:object_changed, self)
|
||||||
|
register_changed( register , nil , interpreter.registers[register])
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_changed reg , old , value
|
||||||
|
reg = reg.symbol unless reg.is_a? Symbol
|
||||||
|
return unless reg == register
|
||||||
|
objects_id! value
|
||||||
|
calc_fields
|
||||||
|
end
|
||||||
|
|
||||||
|
def object_changed reg
|
||||||
|
reg = reg.symbol unless reg.is_a? Symbol
|
||||||
|
return unless reg == register
|
||||||
|
puts "Object changed in #{reg}"
|
||||||
|
calc_fields
|
||||||
|
end
|
||||||
|
|
||||||
|
def calc_fields
|
||||||
|
#puts "My id #{objects_id} , #{objects_id.class}"
|
||||||
|
object = Virtual.machine.objects[objects_id]
|
||||||
|
if object and ! object.is_a?(String)
|
||||||
|
has_fields = []
|
||||||
|
clazz = object.class.name.split("::").last
|
||||||
|
#puts "found #{clazz}"
|
||||||
|
has_fields << clazz
|
||||||
|
object.get_instance_variables.each do |variable|
|
||||||
|
f = object.get_instance_variable(variable)
|
||||||
|
has_fields << f
|
||||||
|
end
|
||||||
|
fields! has_fields
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
div.register_view do
|
||||||
|
div do
|
||||||
|
objects_id.to_s
|
||||||
|
end
|
||||||
|
fields.each do |attribute|
|
||||||
|
div.col_md_12 do
|
||||||
|
"#{marker(attribute)} - #{attribute.object_id}".span
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def marker var
|
||||||
|
return "W" if var.is_a? String
|
||||||
|
var.class.name.split("::").last[0]
|
||||||
|
end
|
||||||
|
end
|
38
app/source_view.rb
Normal file
38
app/source_view.rb
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
class SourceView
|
||||||
|
|
||||||
|
include React::Component
|
||||||
|
|
||||||
|
required_param :interpreter
|
||||||
|
|
||||||
|
define_state :sources => []
|
||||||
|
|
||||||
|
before_mount do
|
||||||
|
interpreter.register_event(:instruction_changed, self)
|
||||||
|
instruction_changed nil , interpreter.instruction
|
||||||
|
end
|
||||||
|
|
||||||
|
def instruction_changed old , ins
|
||||||
|
text = ins ? source_text(ins.source) : "exit"
|
||||||
|
return if sources.last == text
|
||||||
|
sources << text
|
||||||
|
sources.shift if sources.length > 5
|
||||||
|
sources! sources
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
div.source_view do
|
||||||
|
h4 {"Virtual Machine Instruction"}
|
||||||
|
sources.each do |s|
|
||||||
|
s.br
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_text source
|
||||||
|
if source.is_a? Virtual::Instruction
|
||||||
|
return source.class.name
|
||||||
|
else
|
||||||
|
return "Method: #{source.name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
app/status_view.rb
Normal file
41
app/status_view.rb
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
class StatusView
|
||||||
|
|
||||||
|
include React::Component
|
||||||
|
required_param :interpreter
|
||||||
|
|
||||||
|
define_state :state => "starting"
|
||||||
|
define_state :stdout
|
||||||
|
|
||||||
|
before_mount do
|
||||||
|
interpreter.register_event(:instruction_changed, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_state
|
||||||
|
state! interpreter.state
|
||||||
|
stdout! interpreter.stdout
|
||||||
|
end
|
||||||
|
|
||||||
|
def instruction_changed old , nex
|
||||||
|
update_state
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
div.status_view do
|
||||||
|
div do
|
||||||
|
button.bright { "next" }.on(:click) { interpreter.tick }
|
||||||
|
" ".br
|
||||||
|
end
|
||||||
|
div do
|
||||||
|
h4 {"Status:"}
|
||||||
|
state.to_s.br
|
||||||
|
end
|
||||||
|
div do
|
||||||
|
h4 {"Stdout:"}
|
||||||
|
end
|
||||||
|
div do
|
||||||
|
interpreter.stdout.br
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
40
app/styles.scss
Normal file
40
app/styles.scss
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
@import "susy";
|
||||||
|
|
||||||
|
$susy: (
|
||||||
|
columns: 24 ,
|
||||||
|
gutter-position: split ,
|
||||||
|
);
|
||||||
|
|
||||||
|
.debugger-view { @include container(90%); }
|
||||||
|
|
||||||
|
.classes { @include span(3); }
|
||||||
|
|
||||||
|
.file-view {
|
||||||
|
@include span(4);
|
||||||
|
margin: span(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.source-view { @include span(6); }
|
||||||
|
|
||||||
|
.block-view {
|
||||||
|
@include span(4);
|
||||||
|
margin-right: span(3);
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-view {
|
||||||
|
@include span(2 at 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.registers-view {
|
||||||
|
@include span(20 at 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-view {
|
||||||
|
@include gallery(3);
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bright {
|
||||||
|
background-color: orange ;
|
||||||
|
}
|
@ -1,98 +0,0 @@
|
|||||||
|
|
||||||
body
|
|
||||||
background-color: #1a1a1a
|
|
||||||
color: white
|
|
||||||
|
|
||||||
.one_class, .classes, .vool_view ,.mom_view , .risc_view, .status_view ,.vool_view
|
|
||||||
position: relative
|
|
||||||
float: left
|
|
||||||
.mom_view , .risc_view , .status_view , .registers_view, .register_view
|
|
||||||
position: relative
|
|
||||||
float: left
|
|
||||||
|
|
||||||
.one_class
|
|
||||||
margin: 10px
|
|
||||||
|
|
||||||
.classes
|
|
||||||
width: 16%
|
|
||||||
margin-left: 1%
|
|
||||||
|
|
||||||
.vool_view
|
|
||||||
width: 12%
|
|
||||||
margin-left: 1%
|
|
||||||
|
|
||||||
.mom_view
|
|
||||||
width: 16%
|
|
||||||
margin-left: 1%
|
|
||||||
overflow-x: hidden
|
|
||||||
|
|
||||||
.risc_view
|
|
||||||
width: 34%
|
|
||||||
margin-left: 1%
|
|
||||||
overflow-x: hidden
|
|
||||||
|
|
||||||
.status_view
|
|
||||||
width: 14%
|
|
||||||
margin-left: 1%
|
|
||||||
line-height : 1.25em
|
|
||||||
.header_state
|
|
||||||
color: #53a5ff
|
|
||||||
|
|
||||||
.labels_view
|
|
||||||
width: 18%
|
|
||||||
margin-right: 2%
|
|
||||||
height: 200px
|
|
||||||
|
|
||||||
|
|
||||||
.registers_view
|
|
||||||
width: 79%
|
|
||||||
margin-left: 1%
|
|
||||||
.register_view
|
|
||||||
width: 19%
|
|
||||||
margin-right: 1%
|
|
||||||
margin-top: 10px
|
|
||||||
&:nth-child(5n + 1)
|
|
||||||
clear: left
|
|
||||||
|
|
||||||
.field
|
|
||||||
width: 80%
|
|
||||||
|
|
||||||
.value
|
|
||||||
width: 80%
|
|
||||||
|
|
||||||
.value_head
|
|
||||||
background-color: #C5FFD9
|
|
||||||
|
|
||||||
.statement
|
|
||||||
margin-left: 10px
|
|
||||||
|
|
||||||
.ticker
|
|
||||||
text-align: right
|
|
||||||
|
|
||||||
.next , .run , .wizz
|
|
||||||
border-radius: 7px
|
|
||||||
font-size: 1em
|
|
||||||
|
|
||||||
.next
|
|
||||||
background-color: #00FF66
|
|
||||||
|
|
||||||
.run
|
|
||||||
background-color: #00CC33
|
|
||||||
|
|
||||||
.wizz
|
|
||||||
background-color: #009900
|
|
||||||
|
|
||||||
.risc_bright , .mom_bright
|
|
||||||
background-color: black
|
|
||||||
color: #53a5ff
|
|
||||||
border-radius: 7px
|
|
||||||
|
|
||||||
.fade_in
|
|
||||||
transition: background-color 100ms linear
|
|
||||||
padding-right: 6px
|
|
||||||
padding-left: 6px
|
|
||||||
background-color: #00E3FF
|
|
||||||
border-radius: 7px
|
|
||||||
|
|
||||||
h4.select
|
|
||||||
z-index: 20
|
|
@ -1,45 +0,0 @@
|
|||||||
body
|
|
||||||
font-family: arial, helvetica, serif
|
|
||||||
|
|
||||||
#nav , #nav ul
|
|
||||||
padding: 0
|
|
||||||
margin: 0
|
|
||||||
list-style: none
|
|
||||||
float: left
|
|
||||||
width: 100%
|
|
||||||
border-radius: 8px
|
|
||||||
background: black
|
|
||||||
border: 2px solid #C3D46A
|
|
||||||
|
|
||||||
#nav
|
|
||||||
li
|
|
||||||
position: relative
|
|
||||||
float: left
|
|
||||||
line-height: 1.25em
|
|
||||||
width: 99%
|
|
||||||
a , span
|
|
||||||
width: 99%
|
|
||||||
display: block
|
|
||||||
text-decoration: none
|
|
||||||
background-color: black
|
|
||||||
border-radius: 7px
|
|
||||||
ul
|
|
||||||
position: absolute
|
|
||||||
left: -999em
|
|
||||||
margin-left: 99%
|
|
||||||
margin-top: -2.7em
|
|
||||||
ul
|
|
||||||
left: -999em
|
|
||||||
|
|
||||||
#nav li a:hover
|
|
||||||
color: white
|
|
||||||
background-color: #98CC1F
|
|
||||||
|
|
||||||
|
|
||||||
#nav li:hover ul ul, #nav li:hover ul ul ul , #nav li:hover ul ul ul ul , #nav li:hover ul ul ul ul ul
|
|
||||||
left: -999em
|
|
||||||
|
|
||||||
|
|
||||||
/* lists nested under hovered list items */
|
|
||||||
#nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li li li li:hover ul , #nav li li li li li:hover ul
|
|
||||||
left: auto
|
|
@ -1,5 +0,0 @@
|
|||||||
class Object
|
|
||||||
int main()
|
|
||||||
return 55.puti()
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,22 +0,0 @@
|
|||||||
class Object
|
|
||||||
int fibonaccir( int n )
|
|
||||||
if_plus( n - 1 )
|
|
||||||
int tmp
|
|
||||||
tmp = n - 1
|
|
||||||
int a = fibonaccir( tmp )
|
|
||||||
tmp = n - 2
|
|
||||||
int b = fibonaccir( tmp )
|
|
||||||
return a + b
|
|
||||||
else
|
|
||||||
return n
|
|
||||||
end
|
|
||||||
end
|
|
||||||
int fib_print(int n)
|
|
||||||
int fib = fibonaccir( n )
|
|
||||||
fib.putint()
|
|
||||||
return fib
|
|
||||||
end
|
|
||||||
int main()
|
|
||||||
return fib_print(7)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,20 +0,0 @@
|
|||||||
class Object
|
|
||||||
int fibonaccit(int n)
|
|
||||||
int a = 0
|
|
||||||
int b = 1
|
|
||||||
n = n - 1
|
|
||||||
while_plus( n )
|
|
||||||
int tmp = a
|
|
||||||
a = b
|
|
||||||
b = tmp + b
|
|
||||||
n = n - 1
|
|
||||||
end
|
|
||||||
b.putint()
|
|
||||||
return b
|
|
||||||
end
|
|
||||||
|
|
||||||
int main()
|
|
||||||
int f = fibonaccit( 10 )
|
|
||||||
return f.puti()
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,6 +0,0 @@
|
|||||||
class Object
|
|
||||||
int main()
|
|
||||||
"Hello World".putstring()
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,13 +0,0 @@
|
|||||||
class Object
|
|
||||||
int itest(int n)
|
|
||||||
if_zero( n - 12)
|
|
||||||
"then".putstring()
|
|
||||||
else
|
|
||||||
"else".putstring()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
int main()
|
|
||||||
itest(20)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,10 +0,0 @@
|
|||||||
class Object
|
|
||||||
int main()
|
|
||||||
int n = 14
|
|
||||||
if_plus( n - 12)
|
|
||||||
"then".putstring()
|
|
||||||
else
|
|
||||||
"else".putstring()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,23 +0,0 @@
|
|||||||
class Layout < Object
|
|
||||||
|
|
||||||
Class object_class()
|
|
||||||
return get_internal_word(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class Object
|
|
||||||
|
|
||||||
Layout get_layout()
|
|
||||||
return get_internal_word(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
Class get_class()
|
|
||||||
Layout l = get_layout()
|
|
||||||
return l.object_class()
|
|
||||||
end
|
|
||||||
|
|
||||||
int main()
|
|
||||||
return get_class()
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,8 +0,0 @@
|
|||||||
class Object
|
|
||||||
int puts(Word str)
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
int main()
|
|
||||||
puts("Hello")
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,23 +0,0 @@
|
|||||||
class Object
|
|
||||||
|
|
||||||
int times(int a, int b)
|
|
||||||
if_zero( b )
|
|
||||||
a = 0
|
|
||||||
else
|
|
||||||
int m = b - 1
|
|
||||||
int t = times(a, m)
|
|
||||||
a = a + t
|
|
||||||
end
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
int t_seven()
|
|
||||||
int tim = times(5,3)
|
|
||||||
tim.putint()
|
|
||||||
return tim
|
|
||||||
end
|
|
||||||
|
|
||||||
int main()
|
|
||||||
return t_seven()
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,12 +0,0 @@
|
|||||||
class Object
|
|
||||||
|
|
||||||
int main()
|
|
||||||
int i = 5
|
|
||||||
while_plus(i)
|
|
||||||
"out ".putstring()
|
|
||||||
i = i - 1
|
|
||||||
end
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
44
config.ru
44
config.ru
@ -1,16 +1,42 @@
|
|||||||
|
# config.ru
|
||||||
require 'bundler'
|
require 'bundler'
|
||||||
Bundler.require
|
Bundler.require
|
||||||
require 'tilt/erb'
|
|
||||||
require "opal"
|
|
||||||
require 'opal-sprockets'
|
|
||||||
|
|
||||||
Opal.use_gem("rubyx")
|
Opal.use_gem "salama"
|
||||||
|
Opal.use_gem "salama-arm"
|
||||||
|
|
||||||
run Opal::Sprockets::Server.new { |s|
|
require "tilt/erb"
|
||||||
s.main = 'debugger.js.rb'
|
require "susy"
|
||||||
|
require "json"
|
||||||
|
require "react/source"
|
||||||
|
|
||||||
|
class DebugServer < Opal::Server
|
||||||
|
|
||||||
|
def parse(num)
|
||||||
|
string_input = '"Hello again".putstring()'
|
||||||
|
parser = Parser::Salama.new
|
||||||
|
out = parser.parse(string_input)
|
||||||
|
parts = Parser::Transform.new.apply(out)
|
||||||
|
parts.to_basic
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
if env['PATH_INFO'].include? "/parse.json"
|
||||||
|
parse_out = parse(1).to_s
|
||||||
|
[200, { 'Content-Type' => 'text/json' }, [parse_out]]
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
run DebugServer.new {|s|
|
||||||
|
s.append_path 'app'
|
||||||
s.append_path 'lib'
|
s.append_path 'lib'
|
||||||
s.append_path 'assets'
|
s.append_path File.dirname(::React::Source.bundled_path_for("react-with-addons.js"))
|
||||||
s.debug = !ENV["DEBUG"].nil?
|
s.main = 'main'
|
||||||
|
s.debug = false
|
||||||
|
s.source_map = true
|
||||||
s.index_path = "index.html.erb"
|
s.index_path = "index.html.erb"
|
||||||
s.sprockets.cache = Sprockets::Cache::MemoryStore.new(50000)
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Debugger</title>
|
<title>Salama Debugger</title>
|
||||||
<meta charset="utf-8"/>
|
<link rel="stylesheet" href="/assets/styles.css">
|
||||||
<link rel="stylesheet" href="/assets/css/menu.css" type="text/css" charset="utf-8">
|
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/css/app.css" type="text/css" charset="utf-8">
|
<script src="/assets/react-with-addons.js"></script>
|
||||||
|
<%= javascript_include_tag 'main' %>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<%= javascript_include_tag "debugger" %>
|
<div id="content"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
require_relative "element_view"
|
|
||||||
|
|
||||||
# A very simple ElementView with constant text and class. It uses the ElementView.div function
|
|
||||||
# to generate the html, see there for details
|
|
||||||
#
|
|
||||||
class ConstantView < ElementView
|
|
||||||
|
|
||||||
# store the class and text
|
|
||||||
def initialize class_or_id , text = nil
|
|
||||||
@class_or_id = class_or_id
|
|
||||||
@text = text
|
|
||||||
end
|
|
||||||
|
|
||||||
# use ElementView.div to create html from the class and text
|
|
||||||
def draw
|
|
||||||
@element = div(@class_or_id , @text)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,90 +0,0 @@
|
|||||||
# The basic idea is somewhat that of a shadow dom.
|
|
||||||
#
|
|
||||||
# ElementView wraps a single div with whatever content you want (derive to implement the view)
|
|
||||||
#
|
|
||||||
# It must have an element, which is drawn. Draw returns the div or whatever. An ElementView
|
|
||||||
# does not draw itself, but rather is drawn.
|
|
||||||
#
|
|
||||||
# Listviews provide structure
|
|
||||||
#
|
|
||||||
class ElementView
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@element = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
#abstract function that should return the single element that is being represented
|
|
||||||
# the element is also stored in @element
|
|
||||||
def draw
|
|
||||||
raise "implement me to return an Element"
|
|
||||||
end
|
|
||||||
|
|
||||||
# helper function to create an element with possible classes, id and text
|
|
||||||
# The first argument is a bit haml inspired, so "tagname.classname" is the format
|
|
||||||
# but if tagname is ommited it will default to div
|
|
||||||
# also several classnames may be given
|
|
||||||
# if one of the names ends in a ! (bang) it will be assigned as the id
|
|
||||||
# second argument is optional, but if given will be added as text (content) to the newly
|
|
||||||
# created Element
|
|
||||||
# return the new Element, which is not linked into the dom at that point (see << and add*)
|
|
||||||
def div(name_class = "div" , text = nil)
|
|
||||||
name , clazz = name_class.split(".")
|
|
||||||
name = "div" if name.empty?
|
|
||||||
element = $document.create_element(name)
|
|
||||||
element.text = text if text
|
|
||||||
return element unless clazz
|
|
||||||
if( clazz.is_a? Array )
|
|
||||||
clazz.each { |c| add_class_or_id( element , cl )}
|
|
||||||
else
|
|
||||||
add_class_or_id element , clazz
|
|
||||||
end
|
|
||||||
element
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_class_or_id element , class_or_id
|
|
||||||
return element unless class_or_id
|
|
||||||
if class_or_id[-1] == "!"
|
|
||||||
element.id = class_or_id[0 ... -1]
|
|
||||||
else
|
|
||||||
element.add_class class_or_id
|
|
||||||
end
|
|
||||||
element
|
|
||||||
end
|
|
||||||
|
|
||||||
# wrap the @element variable with the given element
|
|
||||||
# so if wrapper == <h4/> the new @element will be <h4> old @element </h4>
|
|
||||||
# return the new @element, which is wrapper
|
|
||||||
def wrap_element wrapper
|
|
||||||
@element = wrap_node_with @element , wrapper
|
|
||||||
end
|
|
||||||
|
|
||||||
# wrap the given node with the wappper, so for a div wrapper and a button node
|
|
||||||
# the result will be <div> <button>whatever was in there</button> <div>
|
|
||||||
def wrap_node_with node , wrapper
|
|
||||||
node.replace_with(wrapper) if node.parent
|
|
||||||
wrapper << node
|
|
||||||
end
|
|
||||||
|
|
||||||
# add the given element to the @element, at the end
|
|
||||||
# return the div that was passed in (use << to return the @element)
|
|
||||||
def append_element div
|
|
||||||
@element << div
|
|
||||||
div
|
|
||||||
end
|
|
||||||
|
|
||||||
# add the given element to the @element , at the front
|
|
||||||
# return the div that was passed in (use >> to return the @element)
|
|
||||||
def prepend_element div
|
|
||||||
@element >> div
|
|
||||||
div
|
|
||||||
end
|
|
||||||
|
|
||||||
# create a new element with class and possibly text
|
|
||||||
# add that new element to the @element
|
|
||||||
# return the newly created element
|
|
||||||
def add class_or_id , tex = nil
|
|
||||||
append_element div( class_or_id , tex)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
@ -1,74 +0,0 @@
|
|||||||
require_relative "element_view"
|
|
||||||
|
|
||||||
# Listviews hold an array of elements and are responsible for drawing (and re-drawing them)
|
|
||||||
#
|
|
||||||
# A ListView hold the elements, but also the drawn html divs. You can change the element
|
|
||||||
# structure by adding/removing/replacing and the ListView will take care of redrawing the html
|
|
||||||
#
|
|
||||||
# A ListView is itself an ElementView so one can build recursive structures.
|
|
||||||
#
|
|
||||||
# Also one can easily change the root html element, or by deriving wrap or edit the resulting html
|
|
||||||
#
|
|
||||||
class ListView < ElementView
|
|
||||||
|
|
||||||
def initialize children
|
|
||||||
@children = children
|
|
||||||
@elements = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def length
|
|
||||||
@children.length
|
|
||||||
end
|
|
||||||
|
|
||||||
# create a root node acording to the tag given (default div)
|
|
||||||
# The tag name will be passed to the div function, so class and id may be set as well (see there)
|
|
||||||
# draw all children and keep the elements as @elements
|
|
||||||
# return (as per base class) the single root of the collection
|
|
||||||
def draw root = "div"
|
|
||||||
@element = div(root)
|
|
||||||
@elements = @children.collect do | c |
|
|
||||||
append_element c.draw
|
|
||||||
end
|
|
||||||
@element
|
|
||||||
end
|
|
||||||
|
|
||||||
# replace the child at index with the given one (second arg)
|
|
||||||
# The child must be an ElementView , which will be rendered and
|
|
||||||
# the old node will be replaced in the live dom
|
|
||||||
def replace_at( index , node)
|
|
||||||
old = @elements[index]
|
|
||||||
@children[index] = node
|
|
||||||
rendered = node.draw
|
|
||||||
@elements[index] = rendered
|
|
||||||
old.replace_with(rendered) if old
|
|
||||||
end
|
|
||||||
|
|
||||||
# remove the first child and element (from view)
|
|
||||||
def remove_first
|
|
||||||
remove_at 0
|
|
||||||
end
|
|
||||||
|
|
||||||
# remove both child and element at given position
|
|
||||||
def remove_at index
|
|
||||||
raise "index out of bounds #{index} => #{@children.length}" if(index >= @children.length or index < 0)
|
|
||||||
@children.delete_at( index )
|
|
||||||
element = @elements.delete_at(index)
|
|
||||||
element.remove if element
|
|
||||||
end
|
|
||||||
|
|
||||||
# remove all elements and views, basically resetting the list to empty
|
|
||||||
def clear_view
|
|
||||||
remove_first while( ! @children.empty? )
|
|
||||||
end
|
|
||||||
|
|
||||||
# append a View instnace to the children array
|
|
||||||
# render it and append it to the html element
|
|
||||||
# and keep a copy in @elements
|
|
||||||
def append_view view
|
|
||||||
@children << view
|
|
||||||
rendered = view.draw
|
|
||||||
@elements << rendered # add to internal array
|
|
||||||
@element << rendered # add to html children
|
|
||||||
rendered
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
require "opal"
|
|
||||||
require "opal-parser"
|
|
||||||
require 'opal/compiler'
|
|
||||||
|
|
||||||
require 'browser'
|
|
||||||
require 'browser/http'
|
|
||||||
require 'native'
|
|
||||||
require "rubyx"
|
|
||||||
require "ast"
|
|
||||||
require "util/eventable"
|
|
||||||
require "risc/interpreter"
|
|
||||||
# the base, our own mini framework, allows for child and parent views and handles updates
|
|
||||||
require "base/list_view"
|
|
||||||
# each seperate view is in it's own class.
|
|
||||||
require "views/left_view"
|
|
||||||
require "views/status_view"
|
|
||||||
require "views/vool_view"
|
|
||||||
require "views/mom_view"
|
|
||||||
require "views/risc_view"
|
|
||||||
require "views/registers_view"
|
|
||||||
class Bignum
|
|
||||||
end
|
|
||||||
class String
|
|
||||||
def codepoints
|
|
||||||
arr = []
|
|
||||||
one = nil
|
|
||||||
self.each_byte do |c|
|
|
||||||
if( one )
|
|
||||||
arr << (one + c * 256)
|
|
||||||
one = nil
|
|
||||||
else
|
|
||||||
one = c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
arr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
module RubyX
|
|
||||||
def self.debugger_options
|
|
||||||
{ parfait: {factory: 50} }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class MainView < ListView
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
compiler = RubyX::RubyXCompiler.new(RubyX.debugger_options)
|
|
||||||
input = "class Space;def main(arg); return 1; end; end"
|
|
||||||
linker = compiler.ruby_to_binary(input , :interpreter)
|
|
||||||
@interpreter = Risc::Interpreter.new(linker)
|
|
||||||
super( [LeftView.new(@interpreter) ,
|
|
||||||
VoolView.new(@interpreter) ,
|
|
||||||
MomView.new(@interpreter) ,
|
|
||||||
RiscView.new(@interpreter) ,
|
|
||||||
StatusView.new(@interpreter) ,
|
|
||||||
RegistersView.new(@interpreter) ] )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
view = MainView.new()
|
|
||||||
view.draw.append_to($document.body)
|
|
37
lib/eventable.rb
Normal file
37
lib/eventable.rb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# A simple event registering/triggering module to mix into classes.
|
||||||
|
# Events are stored in the `@events` ivar.
|
||||||
|
module Eventable
|
||||||
|
|
||||||
|
# Register a handler for the given event name.
|
||||||
|
# The event name is the method name called on the handler object
|
||||||
|
#
|
||||||
|
# obj.on(:foo , some_object_that_implements foo( whateverargs)
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] name event name
|
||||||
|
# @param [Object] object handling the event, ie implement the function name
|
||||||
|
# @return handler
|
||||||
|
def register_event(name, handler)
|
||||||
|
event_table[name] << handler
|
||||||
|
handler
|
||||||
|
end
|
||||||
|
|
||||||
|
def unregister_event(name, handler)
|
||||||
|
event_table[name].delete handler
|
||||||
|
end
|
||||||
|
|
||||||
|
def event_table
|
||||||
|
return @event_table if @event_table
|
||||||
|
@event_table = Hash.new { |hash, key| hash[key] = [] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Trigger the given event name and passes all args to each handler
|
||||||
|
# for this event.
|
||||||
|
#
|
||||||
|
# obj.trigger(:foo)
|
||||||
|
# obj.trigger(:foo, 1, 2, 3)
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] name event name to trigger
|
||||||
|
def trigger(name, *args)
|
||||||
|
event_table[name].each { |handler| handler.send( name.to_sym , *args) }
|
||||||
|
end
|
||||||
|
end
|
166
lib/interpreter.rb
Normal file
166
lib/interpreter.rb
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
|
||||||
|
require "eventable"
|
||||||
|
|
||||||
|
class Interpreter
|
||||||
|
# fire events for changed pc and register contents
|
||||||
|
include Eventable
|
||||||
|
|
||||||
|
# current instruction or pc
|
||||||
|
attr_reader :instruction
|
||||||
|
|
||||||
|
# an (arm style) link register. store the return address to return to
|
||||||
|
attr_reader :link
|
||||||
|
|
||||||
|
# current executing block. since this is not a hardware simulator this is luxury
|
||||||
|
attr_reader :block
|
||||||
|
|
||||||
|
# the registers, 12
|
||||||
|
attr_reader :registers
|
||||||
|
|
||||||
|
# collect the output
|
||||||
|
attr_reader :stdout
|
||||||
|
|
||||||
|
attr_reader :state
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@state = "runnnig"
|
||||||
|
@stdout = ""
|
||||||
|
@registers = {}
|
||||||
|
(0...16).each do |r|
|
||||||
|
set_register "r#{r}".to_sym , "r#{r}:unknown"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def start bl
|
||||||
|
set_block bl
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_block bl
|
||||||
|
return if @block == bl
|
||||||
|
raise "Error, nil block" unless bl
|
||||||
|
old = @block
|
||||||
|
@block = bl
|
||||||
|
trigger(:block_changed , old , bl)
|
||||||
|
set_instruction bl.codes.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_instruction i
|
||||||
|
@state = "exited" unless i
|
||||||
|
return if @instruction == i
|
||||||
|
old = @instruction
|
||||||
|
@instruction = i
|
||||||
|
trigger(:instruction_changed, old , i)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_register( reg )
|
||||||
|
reg = reg.symbol if reg.is_a? Register::RegisterReference
|
||||||
|
raise "Not a register #{reg}" unless Register::RegisterReference.look_like_reg(reg)
|
||||||
|
@registers[reg]
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_register reg , val
|
||||||
|
old = get_register( reg ) # also ensures format
|
||||||
|
return if old === val
|
||||||
|
reg = reg.symbol if reg.is_a? Register::RegisterReference
|
||||||
|
@registers[reg] = val
|
||||||
|
trigger(:register_changed, reg , old , val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tick
|
||||||
|
return unless @instruction
|
||||||
|
name = @instruction.class.name.split("::").last
|
||||||
|
fetch = send "execute_#{name}"
|
||||||
|
return unless fetch
|
||||||
|
fetch_next_intruction
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_next_intruction
|
||||||
|
if(@instruction != @block.codes.last)
|
||||||
|
set_instruction @block.codes[ @block.codes.index(@instruction) + 1]
|
||||||
|
else
|
||||||
|
next_b = @block.method.source.blocks.index(@block) + 1
|
||||||
|
set_block @block.method.source.blocks[next_b]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def object_for reg
|
||||||
|
id = get_register(reg)
|
||||||
|
Virtual.machine.objects[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instruction interpretation starts here
|
||||||
|
def execute_Branch
|
||||||
|
target = @instruction.block
|
||||||
|
set_block target
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_LoadConstant
|
||||||
|
to = @instruction.register
|
||||||
|
value = @instruction.constant.object_id
|
||||||
|
set_register( to , value )
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_GetSlot
|
||||||
|
object = object_for( @instruction.array )
|
||||||
|
value = object.internal_object_get( @instruction.index )
|
||||||
|
value = value.object_id unless value.is_a? Integer
|
||||||
|
set_register( @instruction.register , value )
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_SetSlot
|
||||||
|
value = object_for( @instruction.register )
|
||||||
|
object = object_for( @instruction.array )
|
||||||
|
object.internal_object_set( @instruction.index , value )
|
||||||
|
trigger(:object_changed, @instruction.register )
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_RegisterTransfer
|
||||||
|
value = get_register @instruction.from
|
||||||
|
set_register @instruction.to , value
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_FunctionCall
|
||||||
|
@link = [@block , @instruction]
|
||||||
|
next_block = @instruction.method.source.blocks.first
|
||||||
|
set_block next_block
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_SaveReturn
|
||||||
|
object = object_for @instruction.register
|
||||||
|
raise "save return has nothing to save" unless @link
|
||||||
|
trigger(:object_changed, @instruction.register )
|
||||||
|
object.internal_object_set @instruction.index , @link
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_Syscall
|
||||||
|
name = @instruction.name
|
||||||
|
case name
|
||||||
|
when :putstring
|
||||||
|
str = object_for( :r1 ) # should test length, ie r2
|
||||||
|
raise "NO string for putstring #{str}" unless str.is_a? Symbol
|
||||||
|
@stdout += str.to_s
|
||||||
|
when :exit
|
||||||
|
set_instruction(nil)
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
raise "un-implemented syscall #{name}"
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_FunctionReturn
|
||||||
|
object = object_for( @instruction.register )
|
||||||
|
#wouldn't need to assign to link, but makes tsting easier
|
||||||
|
@link = object.internal_object_get( @instruction.index )
|
||||||
|
@block , @instruction = @link
|
||||||
|
# we jump back to the call instruction. so it is as if the call never happened and we continue
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
@ -1,44 +0,0 @@
|
|||||||
require_relative "ref_view"
|
|
||||||
|
|
||||||
class ClassesView < ListView
|
|
||||||
|
|
||||||
def initialize interpreter
|
|
||||||
@interpreter = interpreter
|
|
||||||
@interpreter.register_event(:state_changed, self)
|
|
||||||
super( class_views )
|
|
||||||
end
|
|
||||||
|
|
||||||
def class_views
|
|
||||||
classes = []
|
|
||||||
Parfait.object_space.classes.each do |name , claz|
|
|
||||||
classes << claz
|
|
||||||
end
|
|
||||||
classes.sort! {|a,b| a.name <=> b.name }
|
|
||||||
classes.collect{|c| ClassView.new(c)}
|
|
||||||
end
|
|
||||||
|
|
||||||
def state_changed old , new_s
|
|
||||||
return unless new_s == :running
|
|
||||||
class_views.each_with_index do |v, i|
|
|
||||||
replace_at i , v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
super()
|
|
||||||
wrap_element div("ul.nav!")
|
|
||||||
wrap_element( div("h4" , "Classes") )
|
|
||||||
return @element
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class ClassView < RefView
|
|
||||||
def initialize clazz
|
|
||||||
super(clazz.name , clazz , 20 )
|
|
||||||
end
|
|
||||||
|
|
||||||
def ref_text
|
|
||||||
@name
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,120 +0,0 @@
|
|||||||
class HtmlConverter < AST::Processor
|
|
||||||
|
|
||||||
alias :old_process :process
|
|
||||||
def process s
|
|
||||||
return "" unless s
|
|
||||||
#puts s.type
|
|
||||||
old_process(s)
|
|
||||||
end
|
|
||||||
def handler_missing s
|
|
||||||
puts "Missing: " + s.type
|
|
||||||
"Missing #{s.type}"
|
|
||||||
end
|
|
||||||
def div( statement , html)
|
|
||||||
"<div class='statement' id='i#{statement.object_id.to_s(16)}'>" + html + "</div>"
|
|
||||||
end
|
|
||||||
def span( statement , html)
|
|
||||||
"<span class='expression' id='i#{statement.object_id.to_s(16)}'>" + html + "</span>"
|
|
||||||
end
|
|
||||||
def on_function( statement)
|
|
||||||
return_type , name , parameters, kids , receiver = *statement
|
|
||||||
str = return_type + " "
|
|
||||||
str += receiver + "." if receiver
|
|
||||||
str += name.to_a.first + "("
|
|
||||||
str += process(parameters) + ")<br>"
|
|
||||||
str += process(kids) + "end<br>"
|
|
||||||
div(statement,str)
|
|
||||||
end
|
|
||||||
def on_parameters statement
|
|
||||||
process_all(statement.children).join(",")
|
|
||||||
end
|
|
||||||
def on_parameter p
|
|
||||||
type , name = *p
|
|
||||||
span(type,type) + " " + span(name,name)
|
|
||||||
end
|
|
||||||
def on_string s
|
|
||||||
span(s, "'" + s.first + "'")
|
|
||||||
end
|
|
||||||
def on_receiver expression
|
|
||||||
span expression , process(expression.first)
|
|
||||||
end
|
|
||||||
def on_field expression
|
|
||||||
span expression , process(expression.first)
|
|
||||||
end
|
|
||||||
def on_field_access statement
|
|
||||||
receiver_ast , field_ast = *statement
|
|
||||||
receiver = process(receiver_ast)
|
|
||||||
field = process(field_ast)
|
|
||||||
span( statement , receiver + "." + field)
|
|
||||||
end
|
|
||||||
def on_field_def statement
|
|
||||||
type , name , value = *statement
|
|
||||||
str = span(type, type) + " " + process(name)
|
|
||||||
str += " = #{process(value)}" if value
|
|
||||||
div(statement,str)
|
|
||||||
end
|
|
||||||
def on_return statement
|
|
||||||
str = "return " + process(statement.first )
|
|
||||||
div(statement,str)
|
|
||||||
end
|
|
||||||
def on_false_statements s
|
|
||||||
on_statements s
|
|
||||||
end
|
|
||||||
def on_true_statements s
|
|
||||||
on_statements s
|
|
||||||
end
|
|
||||||
def on_statements s
|
|
||||||
str = ""
|
|
||||||
s.children.each do |c|
|
|
||||||
str += process(c).to_s
|
|
||||||
end
|
|
||||||
div(s,str)
|
|
||||||
end
|
|
||||||
def on_while_statement statement
|
|
||||||
branch_type , condition , statements = *statement
|
|
||||||
condition = condition.first
|
|
||||||
ret = "while_#{branch_type}(" + process(condition) + ")<br>"
|
|
||||||
ret += process(statements)
|
|
||||||
ret += "end"
|
|
||||||
div(statement,ret)
|
|
||||||
end
|
|
||||||
def on_if_statement statement
|
|
||||||
branch_type , condition , if_true , if_false = *statement
|
|
||||||
condition = condition.first
|
|
||||||
ret = "if_#{branch_type}(" + process(condition) + ")<br>" + process(if_true)
|
|
||||||
ret += "else" + "<br>" + process(if_false) if if_false
|
|
||||||
ret += "end"
|
|
||||||
div(statement,ret)
|
|
||||||
end
|
|
||||||
def on_assignment statement
|
|
||||||
name , value = *statement
|
|
||||||
name = process(name)
|
|
||||||
v = process(value)
|
|
||||||
str = name + " = " + v
|
|
||||||
div(statement,str)
|
|
||||||
end
|
|
||||||
def on_call c
|
|
||||||
name , arguments , receiver = *c
|
|
||||||
ret = process(name)
|
|
||||||
ret = process(receiver.first) + "." + ret if receiver
|
|
||||||
ret += "("
|
|
||||||
ret += process(arguments).join(",")
|
|
||||||
ret += ")"
|
|
||||||
span(c,ret)
|
|
||||||
end
|
|
||||||
def on_operator_value statement
|
|
||||||
operator , left_e , right_e = *statement
|
|
||||||
left_reg = process(left_e)
|
|
||||||
right_reg = process(right_e)
|
|
||||||
span(statement , left_reg + " " + operator + " " + right_reg )
|
|
||||||
end
|
|
||||||
def on_arguments args
|
|
||||||
args.children.collect{|c| process(c)}
|
|
||||||
end
|
|
||||||
def on_name name
|
|
||||||
span(name,name.first)
|
|
||||||
end
|
|
||||||
def on_int i
|
|
||||||
span(i , i.first.to_s)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,89 +0,0 @@
|
|||||||
require_relative "classes_view"
|
|
||||||
|
|
||||||
# the whole of the left, ie selection, space and classes
|
|
||||||
class LeftView < ListView
|
|
||||||
def initialize( interpreter )
|
|
||||||
@interpreter = interpreter
|
|
||||||
super([ SelectView.new(interpreter) ,
|
|
||||||
ObjectView.new( Parfait.object_space , @interpreter , 26),
|
|
||||||
ClassesView.new(interpreter) ])
|
|
||||||
interpreter.register_event(:state_changed, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
# need to re-init when we go to running, as the objects (and the actual space) change
|
|
||||||
# we replace space and class view with new instances
|
|
||||||
def state_changed( old , new_s )
|
|
||||||
return unless new_s == :running
|
|
||||||
space = ObjectView.new( Parfait.object_space , @interpreter , 26)
|
|
||||||
replace_at( 1 , space )
|
|
||||||
replace_at( 2 , ClassesView.new(@interpreter) )
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
super(".classes")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# view for the little code select, implemented as a normal expandable menu
|
|
||||||
#
|
|
||||||
# on click calls select method
|
|
||||||
#
|
|
||||||
# derive from element, meaning we draw
|
|
||||||
# # TODO: make into listview, so code can be the next level expansion
|
|
||||||
class SelectView < ElementView
|
|
||||||
|
|
||||||
def initialize( interpreter )
|
|
||||||
super
|
|
||||||
@interpreter = interpreter
|
|
||||||
@codes = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
@element = div("h4.select", "Code") << (list = div("ul.nav!"))
|
|
||||||
list << (div("li.code_list") << div("a.selected" , "none selected"))
|
|
||||||
selection_codes unless @codes
|
|
||||||
@element << div("br")
|
|
||||||
@element << div("br")
|
|
||||||
end
|
|
||||||
|
|
||||||
def selection_codes
|
|
||||||
@codes = get_codes.keys
|
|
||||||
list = div "ul"
|
|
||||||
@codes.each do |c|
|
|
||||||
code = div("li") << div("a" , c )
|
|
||||||
code.style["z-index"] = 10
|
|
||||||
code.on("click"){ select(c) }
|
|
||||||
list << code
|
|
||||||
end
|
|
||||||
@element.at_css(".code_list") << list
|
|
||||||
end
|
|
||||||
|
|
||||||
# select method set up as click handler for the codes
|
|
||||||
# restart the interpreter after compiling
|
|
||||||
def select( code )
|
|
||||||
puts "selecting #{code}"
|
|
||||||
@interpreter.set_state :stopped
|
|
||||||
@element.at_css(".selected").text = code
|
|
||||||
ruby = as_main(get_codes[code])
|
|
||||||
compiler = RubyX::RubyXCompiler.new(RubyX.debugger_options)
|
|
||||||
linker = compiler.ruby_to_binary(ruby, :interpreter)
|
|
||||||
@interpreter.start_program(linker)
|
|
||||||
end
|
|
||||||
|
|
||||||
def as_main(statements)
|
|
||||||
"class Space ;def yielder; return yield ; end;def main(arg) ; #{statements}; end; end"
|
|
||||||
end
|
|
||||||
def get_codes
|
|
||||||
{ while_with_calls: 'a = 2; while( 0 < a); a = a - 1;end;return a',
|
|
||||||
set_internal_byte: "return 'Hello'.set_internal_byte(1,75)" ,
|
|
||||||
basic_if: 'if( 10 ); return "then";else;return "else";end' ,
|
|
||||||
plus: 'return 5 + 7' ,
|
|
||||||
yield: "a = yielder {return 15} ; return a" ,
|
|
||||||
return: 'return 5' ,
|
|
||||||
hello_world: "h = 'Hello World'.putstring;return h",
|
|
||||||
dynamic_call: "a = 150 ; return a.div10",
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,43 +0,0 @@
|
|||||||
require "base/constant_view"
|
|
||||||
require "base/list_view"
|
|
||||||
|
|
||||||
class MomView < ListView
|
|
||||||
|
|
||||||
def initialize interpreter
|
|
||||||
@interpreter = interpreter
|
|
||||||
@current = nil
|
|
||||||
super([start_view])
|
|
||||||
@interpreter.register_event(:instruction_changed, self)
|
|
||||||
@interpreter.register_event(:state_changed, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_view
|
|
||||||
ConstantView.new( "span.mom_bright" , "starting" )
|
|
||||||
end
|
|
||||||
|
|
||||||
def instruction_changed
|
|
||||||
i = @interpreter.instruction
|
|
||||||
return unless i && i.source.is_a?(Mom::Instruction)
|
|
||||||
return if i.source == @current
|
|
||||||
@current = i.source
|
|
||||||
@element.at_css(".mom_bright").remove_class("mom_bright")
|
|
||||||
instruction = append_view( ConstantView.new( "span.mom_bright" , @current.to_s ) )
|
|
||||||
wrap_node_with( instruction , div )
|
|
||||||
remove_first if( @elements.length > 6)
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
super()
|
|
||||||
wrap_node_with @elements.first , div
|
|
||||||
wrap_element div(".mom_view") << div("h4" ,"Mom::Instruction")
|
|
||||||
@element
|
|
||||||
end
|
|
||||||
|
|
||||||
def state_changed(old , new_s)
|
|
||||||
return unless new_s == :running
|
|
||||||
clear_view
|
|
||||||
@current = nil
|
|
||||||
append_view start_view
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,73 +0,0 @@
|
|||||||
require_relative "ref_view"
|
|
||||||
|
|
||||||
class ObjectView < ListView
|
|
||||||
|
|
||||||
# z is the z-index
|
|
||||||
|
|
||||||
def initialize object , interpreter = nil , z = nil
|
|
||||||
@object = object
|
|
||||||
@z = z
|
|
||||||
@interpreter = interpreter
|
|
||||||
@interpreter.register_event(:object_changed, self) if interpreter
|
|
||||||
super( content_elements )
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
@element = super(@interpreter ? "ul.nav!" : "ul")
|
|
||||||
prepend_element div( "li" ) << div("span" , class_header )
|
|
||||||
return @element
|
|
||||||
end
|
|
||||||
|
|
||||||
def object_changed( reg , at)
|
|
||||||
#puts "Object changed in #{reg} , at #{at}"
|
|
||||||
for_object = @interpreter.get_register( reg )
|
|
||||||
return unless for_object == @object
|
|
||||||
#puts "Object changed #{for_object} , at #{at}"
|
|
||||||
variable = @object.get_instance_variables.get(at)
|
|
||||||
if(variable)
|
|
||||||
f = @object.get_instance_variable(variable)
|
|
||||||
else
|
|
||||||
variable = at.to_s
|
|
||||||
f = @object.get_internal_word(at)
|
|
||||||
end
|
|
||||||
#puts "got var name #{variable}#{variable.class} for #{at}, #{f}"
|
|
||||||
view = RefView.new( variable , f , @z )
|
|
||||||
if( @children[at + 1] )
|
|
||||||
replace_at(at + 1, view)
|
|
||||||
else
|
|
||||||
append_view(view)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def class_header
|
|
||||||
str = Risc::Position.set?(@object).to_s
|
|
||||||
clazz = @object.class.name.split("::").last
|
|
||||||
[clazz, str].join " : "
|
|
||||||
end
|
|
||||||
|
|
||||||
def content_elements
|
|
||||||
fields = [ConstantView.new("li" , "------------------------------")]
|
|
||||||
object = @object
|
|
||||||
if object and ! object.is_a?(String)
|
|
||||||
object.get_instance_variables.each do |variable|
|
|
||||||
f = object.get_instance_variable(variable)
|
|
||||||
fields << RefView.new( variable , f , @z )
|
|
||||||
end
|
|
||||||
if( object.is_a?(Parfait::List) )
|
|
||||||
index = 0
|
|
||||||
object.each do | o|
|
|
||||||
fields << RefView.new( index.to_s , o , @z )
|
|
||||||
index += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if( object.is_a?(Parfait::Integer) )
|
|
||||||
fields << RefView.new( 3.to_s , object.value , @z )
|
|
||||||
end
|
|
||||||
if( object.is_a?(Parfait::Word) )
|
|
||||||
fields << RefView.new( 3.to_s , object.to_string , @z )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
fields
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,69 +0,0 @@
|
|||||||
class RefView < ListView
|
|
||||||
|
|
||||||
def initialize name , value , z = nil
|
|
||||||
@name = name
|
|
||||||
@value = value
|
|
||||||
@z = z
|
|
||||||
super []
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :value
|
|
||||||
|
|
||||||
def value= val
|
|
||||||
@value = val
|
|
||||||
add_hover
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
@element = div("li") << div("a" , ref_text )
|
|
||||||
add_hover
|
|
||||||
@element.style["z-index"] = @z if @z
|
|
||||||
@element
|
|
||||||
end
|
|
||||||
|
|
||||||
def ref_text
|
|
||||||
"#{@name} : #{marker()}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_hover
|
|
||||||
return if is_string?
|
|
||||||
@element.on("hover"){ hover } if is_object?(@value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_object?( )
|
|
||||||
return false if @value.is_a?(Fixnum)
|
|
||||||
return false unless @value
|
|
||||||
! is_label?
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_string?()
|
|
||||||
@value.is_a? String
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_label?
|
|
||||||
@value.is_a?(Risc::Label)
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_nil?()
|
|
||||||
@value.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def hover
|
|
||||||
#puts "hovering #{@name}"
|
|
||||||
append_view ObjectView.new(@value)
|
|
||||||
@element.off("hover")
|
|
||||||
end
|
|
||||||
|
|
||||||
def marker
|
|
||||||
if is_string?
|
|
||||||
str = @value
|
|
||||||
elsif is_object?
|
|
||||||
str = Risc::Position.get(@value).to_s
|
|
||||||
elsif is_label?
|
|
||||||
str = "Label"
|
|
||||||
else
|
|
||||||
str = @value.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,54 +0,0 @@
|
|||||||
require_relative "object_view"
|
|
||||||
|
|
||||||
class RegistersView < ListView
|
|
||||||
|
|
||||||
def initialize interpreter
|
|
||||||
@interpreter = interpreter
|
|
||||||
@interpreter.register_event(:register_changed, self)
|
|
||||||
kids = []
|
|
||||||
@interpreter.registers.each do |reg , val|
|
|
||||||
kids << ValueView.new( val )
|
|
||||||
end
|
|
||||||
super(kids)
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
super( "div.registers_view" )
|
|
||||||
@element.children.each_with_index do |reg, index|
|
|
||||||
elem = div("div.register_view")
|
|
||||||
wrap_node_with reg , elem
|
|
||||||
end
|
|
||||||
@element
|
|
||||||
end
|
|
||||||
|
|
||||||
def register_changed( reg , old , value )
|
|
||||||
reg = reg.symbol unless reg.is_a? Symbol
|
|
||||||
index = reg.to_s[1 .. -1 ].to_i
|
|
||||||
has = Risc::Position.set?(value)
|
|
||||||
if( has )
|
|
||||||
if has.object.is_a?(Risc::Label)
|
|
||||||
swap = ValueView.new "Label: #{has.object.name}"
|
|
||||||
else
|
|
||||||
swap = ObjectView.new( value , @interpreter , 16 - index )
|
|
||||||
end
|
|
||||||
else
|
|
||||||
swap = ValueView.new value
|
|
||||||
end
|
|
||||||
replace_at index , swap
|
|
||||||
# @elements[index].style["z-index"] = -index
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class ValueView < ElementView
|
|
||||||
|
|
||||||
def initialize value
|
|
||||||
@value = value
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
li = div("li")
|
|
||||||
li << div("span", @value)
|
|
||||||
@element = div("ul.nav!") << li
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,42 +0,0 @@
|
|||||||
require "base/constant_view"
|
|
||||||
require "base/list_view"
|
|
||||||
|
|
||||||
class RiscView < ListView
|
|
||||||
|
|
||||||
def initialize interpreter
|
|
||||||
@interpreter = interpreter
|
|
||||||
super([start_view])
|
|
||||||
@interpreter.register_event(:instruction_changed, self)
|
|
||||||
@interpreter.register_event(:state_changed, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_view
|
|
||||||
ConstantView.new( "span.risc_bright" , "starting" )
|
|
||||||
end
|
|
||||||
|
|
||||||
def instruction_changed
|
|
||||||
@element.at_css(".risc_bright").remove_class("risc_bright")
|
|
||||||
instruction = append_view( ConstantView.new( "span.risc_bright" , instruction_text ) )
|
|
||||||
wrap_node_with instruction , div
|
|
||||||
remove_first if( @elements.length > 6)
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
super()
|
|
||||||
wrap_node_with @elements.first , div
|
|
||||||
wrap_element div(".risc_view") << div("h4" ,"Register Machine Instruction")
|
|
||||||
@element
|
|
||||||
end
|
|
||||||
|
|
||||||
def state_changed old , new_s
|
|
||||||
return unless new_s == :running
|
|
||||||
clear_view
|
|
||||||
append_view start_view
|
|
||||||
end
|
|
||||||
|
|
||||||
def instruction_text
|
|
||||||
return "" unless @interpreter.instruction
|
|
||||||
@interpreter.instruction.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,85 +0,0 @@
|
|||||||
require "browser/delay"
|
|
||||||
|
|
||||||
class StatusView < ElementView
|
|
||||||
|
|
||||||
def initialize interpreter
|
|
||||||
@interpreter = interpreter
|
|
||||||
@running = false
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
header = div("h4" , "Interpreter" )
|
|
||||||
header << div("span.header_state" , state_text)
|
|
||||||
@element = div(".status_view") <<
|
|
||||||
header <<
|
|
||||||
div("button.next" , "Next") <<
|
|
||||||
div("button.run" , "Run") <<
|
|
||||||
div("button.wizz" , "Wizz") <<
|
|
||||||
div( "br") <<
|
|
||||||
div("span.clock" , clock_text) <<
|
|
||||||
div( "br") <<
|
|
||||||
div("span.flags" , flags_text) <<
|
|
||||||
div( "br") <<
|
|
||||||
div( "span.stdout" , "Stdout") <<
|
|
||||||
div( "br") <<
|
|
||||||
div( "span.status" , "Status")
|
|
||||||
# set up event handler
|
|
||||||
@element.at_css(".next").on("click") { self.update }
|
|
||||||
@element.at_css(".run").on("mousedown") { self.start( 0.1 ) }
|
|
||||||
@element.at_css(".wizz").on("mousedown") { self.start( 0.0 ) }
|
|
||||||
@element.at_css(".run").on("mouseup") { self.stop }
|
|
||||||
@element.at_css(".wizz").on("mouseup") { self.stop }
|
|
||||||
return @element
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def start(speed)
|
|
||||||
@running = speed
|
|
||||||
run
|
|
||||||
end
|
|
||||||
def stop
|
|
||||||
@running = false
|
|
||||||
end
|
|
||||||
def run
|
|
||||||
return unless @running
|
|
||||||
proc = Proc.new do
|
|
||||||
self.update
|
|
||||||
self.run
|
|
||||||
end
|
|
||||||
proc.after( @running )
|
|
||||||
end
|
|
||||||
def update
|
|
||||||
@interpreter.tick
|
|
||||||
@element.at_css(".clock").text = clock_text
|
|
||||||
@element.at_css(".header_state").text = state_text
|
|
||||||
@element.at_css(".flags").text = flags_text
|
|
||||||
@element.at_css(".stdout").text = @interpreter.stdout
|
|
||||||
@element.at_css(".status").text = status_text
|
|
||||||
end
|
|
||||||
|
|
||||||
def status_text
|
|
||||||
return unless @interpreter.instruction
|
|
||||||
return "#{@interpreter.instruction.to_s}" unless @interpreter.instruction.source
|
|
||||||
source = @interpreter.instruction.source
|
|
||||||
s = "#{source.to_s}"
|
|
||||||
if( source.respond_to?(:source) and source.source )
|
|
||||||
s += @interpreter.instruction.source.source.to_s
|
|
||||||
end
|
|
||||||
s
|
|
||||||
end
|
|
||||||
def state_text
|
|
||||||
" (#{@interpreter.state})"
|
|
||||||
end
|
|
||||||
|
|
||||||
def flags_text
|
|
||||||
flags = []
|
|
||||||
@interpreter.flags.each do |name,value|
|
|
||||||
flags << name if value
|
|
||||||
end
|
|
||||||
"Flags #{flags.join(':')}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def clock_text
|
|
||||||
"Instruction #{@interpreter.clock}-0x#{@interpreter.pc.to_s(16)}"
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,46 +0,0 @@
|
|||||||
require_relative "html_converter"
|
|
||||||
|
|
||||||
class VoolView < ElementView
|
|
||||||
|
|
||||||
def initialize interpreter
|
|
||||||
@interpreter = interpreter
|
|
||||||
@interpreter.register_event(:instruction_changed, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw
|
|
||||||
@text = div(".text")
|
|
||||||
@ticker = div(".ticker")
|
|
||||||
@element = div(".vool_view") << div("h4.source" , "Class.Method") << @ticker << @text
|
|
||||||
@element
|
|
||||||
end
|
|
||||||
|
|
||||||
def instruction_changed
|
|
||||||
i = @interpreter.instruction
|
|
||||||
return "" unless i
|
|
||||||
if i.is_a?(Risc::FunctionReturn)
|
|
||||||
link = @interpreter.get_register( i.register )
|
|
||||||
puts "Link #{link}:#{link.class}"
|
|
||||||
raise "No link method" unless link
|
|
||||||
end
|
|
||||||
method = nil
|
|
||||||
case i.source
|
|
||||||
when Mom::Instruction
|
|
||||||
if(i.source.is_a?(Mom::SimpleCall))
|
|
||||||
method = i.source.method
|
|
||||||
end
|
|
||||||
#TODO, give mom a source
|
|
||||||
when Parfait::Callable
|
|
||||||
method = i.source
|
|
||||||
when String
|
|
||||||
return
|
|
||||||
else
|
|
||||||
raise "Unrecognized source #{i.source.class.name} for #{i}"
|
|
||||||
end
|
|
||||||
update_method(method) if method
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_method(method)
|
|
||||||
@text.inner_html = method.name
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
Binary file not shown.
Before Width: | Height: | Size: 375 KiB After Width: | Height: | Size: 198 KiB |
307
static/hint.css
Normal file
307
static/hint.css
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*! Hint.css - v1.3.5 - 2015-06-16
|
||||||
|
* http://kushagragour.in/lab/hint/
|
||||||
|
* Copyright (c) 2015 Kushagra Gour; Licensed MIT */
|
||||||
|
|
||||||
|
/*-------------------------------------*\
|
||||||
|
HINT.css - A CSS tooltip library
|
||||||
|
\*-------------------------------------*/
|
||||||
|
/**
|
||||||
|
* HINT.css is a tooltip library made in pure CSS.
|
||||||
|
*
|
||||||
|
* Source: https://github.com/chinchang/hint.css
|
||||||
|
* Demo: http://kushagragour.in/lab/hint/
|
||||||
|
*
|
||||||
|
* Release under The MIT License
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* source: hint-core.scss
|
||||||
|
*
|
||||||
|
* Defines the basic styling for the tooltip.
|
||||||
|
* Each tooltip is made of 2 parts:
|
||||||
|
* 1) body (:after)
|
||||||
|
* 2) arrow (:before)
|
||||||
|
*
|
||||||
|
* Classes added:
|
||||||
|
* 1) hint
|
||||||
|
*/
|
||||||
|
.hint, [data-hint] {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
/**
|
||||||
|
* tooltip arrow
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* tooltip body
|
||||||
|
*/ }
|
||||||
|
.hint:before, .hint:after, [data-hint]:before, [data-hint]:after {
|
||||||
|
position: absolute;
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
-moz-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1000000;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-transition: 0.3s ease;
|
||||||
|
-moz-transition: 0.3s ease;
|
||||||
|
transition: 0.3s ease;
|
||||||
|
-webkit-transition-delay: 0ms;
|
||||||
|
-moz-transition-delay: 0ms;
|
||||||
|
transition-delay: 0ms; }
|
||||||
|
.hint:hover:before, .hint:hover:after, .hint:focus:before, .hint:focus:after, [data-hint]:hover:before, [data-hint]:hover:after, [data-hint]:focus:before, [data-hint]:focus:after {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1; }
|
||||||
|
.hint:hover:before, .hint:hover:after, [data-hint]:hover:before, [data-hint]:hover:after {
|
||||||
|
-webkit-transition-delay: 100ms;
|
||||||
|
-moz-transition-delay: 100ms;
|
||||||
|
transition-delay: 100ms; }
|
||||||
|
.hint:before, [data-hint]:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background: transparent;
|
||||||
|
border: 6px solid transparent;
|
||||||
|
z-index: 1000001; }
|
||||||
|
.hint:after, [data-hint]:after {
|
||||||
|
content: attr(data-hint);
|
||||||
|
background: #383838;
|
||||||
|
color: white;
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
white-space: nowrap; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source: hint-position.scss
|
||||||
|
*
|
||||||
|
* Defines the positoning logic for the tooltips.
|
||||||
|
*
|
||||||
|
* Classes added:
|
||||||
|
* 1) hint--top
|
||||||
|
* 2) hint--bottom
|
||||||
|
* 3) hint--left
|
||||||
|
* 4) hint--right
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* set default color for tooltip arrows
|
||||||
|
*/
|
||||||
|
.hint--top:before {
|
||||||
|
border-top-color: #383838; }
|
||||||
|
|
||||||
|
.hint--bottom:before {
|
||||||
|
border-bottom-color: #383838; }
|
||||||
|
|
||||||
|
.hint--left:before {
|
||||||
|
border-left-color: #383838; }
|
||||||
|
|
||||||
|
.hint--right:before {
|
||||||
|
border-right-color: #383838; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* top tooltip
|
||||||
|
*/
|
||||||
|
.hint--top:before {
|
||||||
|
margin-bottom: -12px; }
|
||||||
|
.hint--top:after {
|
||||||
|
margin-left: -18px; }
|
||||||
|
.hint--top:before, .hint--top:after {
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%; }
|
||||||
|
.hint--top:hover:after, .hint--top:hover:before, .hint--top:focus:after, .hint--top:focus:before {
|
||||||
|
-webkit-transform: translateY(-8px);
|
||||||
|
-moz-transform: translateY(-8px);
|
||||||
|
transform: translateY(-8px); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bottom tooltip
|
||||||
|
*/
|
||||||
|
.hint--bottom:before {
|
||||||
|
margin-top: -12px; }
|
||||||
|
.hint--bottom:after {
|
||||||
|
margin-left: -18px; }
|
||||||
|
.hint--bottom:before, .hint--bottom:after {
|
||||||
|
top: 100%;
|
||||||
|
left: 50%; }
|
||||||
|
.hint--bottom:hover:after, .hint--bottom:hover:before, .hint--bottom:focus:after, .hint--bottom:focus:before {
|
||||||
|
-webkit-transform: translateY(8px);
|
||||||
|
-moz-transform: translateY(8px);
|
||||||
|
transform: translateY(8px); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* right tooltip
|
||||||
|
*/
|
||||||
|
.hint--right:before {
|
||||||
|
margin-left: -12px;
|
||||||
|
margin-bottom: -6px; }
|
||||||
|
.hint--right:after {
|
||||||
|
margin-bottom: -14px; }
|
||||||
|
.hint--right:before, .hint--right:after {
|
||||||
|
left: 100%;
|
||||||
|
bottom: 50%; }
|
||||||
|
.hint--right:hover:after, .hint--right:hover:before, .hint--right:focus:after, .hint--right:focus:before {
|
||||||
|
-webkit-transform: translateX(8px);
|
||||||
|
-moz-transform: translateX(8px);
|
||||||
|
transform: translateX(8px); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* left tooltip
|
||||||
|
*/
|
||||||
|
.hint--left:before {
|
||||||
|
margin-right: -12px;
|
||||||
|
margin-bottom: -6px; }
|
||||||
|
.hint--left:after {
|
||||||
|
margin-bottom: -14px; }
|
||||||
|
.hint--left:before, .hint--left:after {
|
||||||
|
right: 100%;
|
||||||
|
bottom: 50%; }
|
||||||
|
.hint--left:hover:after, .hint--left:hover:before, .hint--left:focus:after, .hint--left:focus:before {
|
||||||
|
-webkit-transform: translateX(-8px);
|
||||||
|
-moz-transform: translateX(-8px);
|
||||||
|
transform: translateX(-8px); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source: hint-theme.scss
|
||||||
|
*
|
||||||
|
* Defines basic theme for tooltips.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.hint, [data-hint] {
|
||||||
|
/**
|
||||||
|
* tooltip body
|
||||||
|
*/ }
|
||||||
|
.hint:after, [data-hint]:after {
|
||||||
|
text-shadow: 0 -1px 0px black;
|
||||||
|
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source: hint-color-types.scss
|
||||||
|
*
|
||||||
|
* Contains tooltips of various types based on color differences.
|
||||||
|
*
|
||||||
|
* Classes added:
|
||||||
|
* 1) hint--error
|
||||||
|
* 2) hint--warning
|
||||||
|
* 3) hint--info
|
||||||
|
* 4) hint--success
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Error
|
||||||
|
*/
|
||||||
|
.hint--error:after {
|
||||||
|
background-color: #b34e4d;
|
||||||
|
text-shadow: 0 -1px 0px #592726; }
|
||||||
|
.hint--error.hint--top:before {
|
||||||
|
border-top-color: #b34e4d; }
|
||||||
|
.hint--error.hint--bottom:before {
|
||||||
|
border-bottom-color: #b34e4d; }
|
||||||
|
.hint--error.hint--left:before {
|
||||||
|
border-left-color: #b34e4d; }
|
||||||
|
.hint--error.hint--right:before {
|
||||||
|
border-right-color: #b34e4d; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning
|
||||||
|
*/
|
||||||
|
.hint--warning:after {
|
||||||
|
background-color: #c09854;
|
||||||
|
text-shadow: 0 -1px 0px #6c5328; }
|
||||||
|
.hint--warning.hint--top:before {
|
||||||
|
border-top-color: #c09854; }
|
||||||
|
.hint--warning.hint--bottom:before {
|
||||||
|
border-bottom-color: #c09854; }
|
||||||
|
.hint--warning.hint--left:before {
|
||||||
|
border-left-color: #c09854; }
|
||||||
|
.hint--warning.hint--right:before {
|
||||||
|
border-right-color: #c09854; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info
|
||||||
|
*/
|
||||||
|
.hint--info:after {
|
||||||
|
background-color: #3986ac;
|
||||||
|
text-shadow: 0 -1px 0px #193b4d; }
|
||||||
|
.hint--info.hint--top:before {
|
||||||
|
border-top-color: #3986ac; }
|
||||||
|
.hint--info.hint--bottom:before {
|
||||||
|
border-bottom-color: #3986ac; }
|
||||||
|
.hint--info.hint--left:before {
|
||||||
|
border-left-color: #3986ac; }
|
||||||
|
.hint--info.hint--right:before {
|
||||||
|
border-right-color: #3986ac; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success
|
||||||
|
*/
|
||||||
|
.hint--success:after {
|
||||||
|
background-color: #458746;
|
||||||
|
text-shadow: 0 -1px 0px #1a321a; }
|
||||||
|
.hint--success.hint--top:before {
|
||||||
|
border-top-color: #458746; }
|
||||||
|
.hint--success.hint--bottom:before {
|
||||||
|
border-bottom-color: #458746; }
|
||||||
|
.hint--success.hint--left:before {
|
||||||
|
border-left-color: #458746; }
|
||||||
|
.hint--success.hint--right:before {
|
||||||
|
border-right-color: #458746; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source: hint-always.scss
|
||||||
|
*
|
||||||
|
* Defines a persisted tooltip which shows always.
|
||||||
|
*
|
||||||
|
* Classes added:
|
||||||
|
* 1) hint--always
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.hint--always:after, .hint--always:before {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible; }
|
||||||
|
.hint--always.hint--top:after, .hint--always.hint--top:before {
|
||||||
|
-webkit-transform: translateY(-8px);
|
||||||
|
-moz-transform: translateY(-8px);
|
||||||
|
transform: translateY(-8px); }
|
||||||
|
.hint--always.hint--bottom:after, .hint--always.hint--bottom:before {
|
||||||
|
-webkit-transform: translateY(8px);
|
||||||
|
-moz-transform: translateY(8px);
|
||||||
|
transform: translateY(8px); }
|
||||||
|
.hint--always.hint--left:after, .hint--always.hint--left:before {
|
||||||
|
-webkit-transform: translateX(-8px);
|
||||||
|
-moz-transform: translateX(-8px);
|
||||||
|
transform: translateX(-8px); }
|
||||||
|
.hint--always.hint--right:after, .hint--always.hint--right:before {
|
||||||
|
-webkit-transform: translateX(8px);
|
||||||
|
-moz-transform: translateX(8px);
|
||||||
|
transform: translateX(8px); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source: hint-rounded.scss
|
||||||
|
*
|
||||||
|
* Defines rounded corner tooltips.
|
||||||
|
*
|
||||||
|
* Classes added:
|
||||||
|
* 1) hint--rounded
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.hint--rounded:after {
|
||||||
|
border-radius: 4px; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* source: hint-effects.scss
|
||||||
|
*
|
||||||
|
* Defines various transition effects for the tooltips.
|
||||||
|
*
|
||||||
|
* Classes added:
|
||||||
|
* 1) hint--no-animate
|
||||||
|
* 2) hint--bounce
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.hint--no-animate:before, .hint--no-animate:after {
|
||||||
|
-webkit-transition-duration: 0ms;
|
||||||
|
-moz-transition-duration: 0ms;
|
||||||
|
transition-duration: 0ms; }
|
||||||
|
|
||||||
|
.hint--bounce:before, .hint--bounce:after {
|
||||||
|
-webkit-transition: opacity 0.3s ease, visibility 0.3s ease, -webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);
|
||||||
|
-moz-transition: opacity 0.3s ease, visibility 0.3s ease, -moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);
|
||||||
|
transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24); }
|
21
test/helper.rb
Normal file
21
test/helper.rb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
require 'rubygems'
|
||||||
|
require 'bundler'
|
||||||
|
begin
|
||||||
|
Bundler.setup(:default, :development)
|
||||||
|
rescue Bundler::BundlerError => e
|
||||||
|
$stderr.puts e.message
|
||||||
|
$stderr.puts "Run `bundle install` to install missing gems"
|
||||||
|
exit e.status_code
|
||||||
|
end
|
||||||
|
if ENV['CODECLIMATE_REPO_TOKEN']
|
||||||
|
require "codeclimate-test-reporter"
|
||||||
|
CodeClimate::TestReporter.start
|
||||||
|
end
|
||||||
|
|
||||||
|
require "minitest/autorun"
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
||||||
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'test'))
|
||||||
|
|
||||||
|
require "salama"
|
||||||
|
require "interpreter"
|
82
test/interpreter_test.rb
Normal file
82
test/interpreter_test.rb
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
require_relative "helper"
|
||||||
|
|
||||||
|
class InterpreterTest < MiniTest::Test
|
||||||
|
|
||||||
|
def setup
|
||||||
|
Virtual.machine.boot
|
||||||
|
code = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:putstring, [] ,Ast::StringExpression.new("Hello again"))])
|
||||||
|
Virtual::Compiler.compile( code , Virtual.machine.space.get_main )
|
||||||
|
Virtual.machine.run_before "Register::CallImplementation"
|
||||||
|
@interpreter = Interpreter.new
|
||||||
|
@interpreter.start Virtual.machine.init
|
||||||
|
end
|
||||||
|
|
||||||
|
def ticks num
|
||||||
|
last = nil
|
||||||
|
num.times do
|
||||||
|
last = @interpreter.instruction
|
||||||
|
@interpreter.tick
|
||||||
|
end
|
||||||
|
return last
|
||||||
|
end
|
||||||
|
def test_branch
|
||||||
|
was = @interpreter.block
|
||||||
|
assert_equal Register::Branch , ticks(1).class
|
||||||
|
assert was != @interpreter.block
|
||||||
|
end
|
||||||
|
def test_load
|
||||||
|
assert_equal Register::LoadConstant , ticks(2).class
|
||||||
|
assert_equal Parfait::Space , Virtual.machine.objects[ @interpreter.get_register(:r1)].class
|
||||||
|
assert_equal :r1, @interpreter.instruction.array.symbol
|
||||||
|
end
|
||||||
|
def test_get
|
||||||
|
assert_equal Register::GetSlot , ticks(3).class
|
||||||
|
assert @interpreter.get_register( :r3 )
|
||||||
|
assert @interpreter.get_register( :r3 ).is_a? Integer
|
||||||
|
end
|
||||||
|
def test_transfer
|
||||||
|
transfer = ticks 5
|
||||||
|
assert_equal Register::RegisterTransfer , transfer.class
|
||||||
|
assert_equal @interpreter.get_register(transfer.to) , @interpreter.get_register(transfer.from)
|
||||||
|
end
|
||||||
|
def test_call
|
||||||
|
assert_equal Register::FunctionCall , ticks(7).class
|
||||||
|
assert @interpreter.link
|
||||||
|
end
|
||||||
|
def test_save
|
||||||
|
done = ticks(8)
|
||||||
|
assert_equal Register::SaveReturn , done.class
|
||||||
|
assert @interpreter.get_register done.register.symbol
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_chain
|
||||||
|
["Branch" , "LoadConstant" , "GetSlot" , "SetSlot" , "RegisterTransfer" ,
|
||||||
|
"GetSlot" , "FunctionCall" , "SaveReturn" , "LoadConstant" , "SetSlot" ,
|
||||||
|
"GetSlot" , "GetSlot" , "SetSlot" , "LoadConstant" , "SetSlot" ,
|
||||||
|
"RegisterTransfer" , "GetSlot" , "FunctionCall" , "SaveReturn" , "RegisterTransfer" ,
|
||||||
|
"Syscall" , "RegisterTransfer" , "RegisterTransfer" , "SetSlot" , "GetSlot" ,
|
||||||
|
"GetSlot" , "RegisterTransfer" ,"GetSlot" , "GetSlot","GetSlot",
|
||||||
|
"FunctionReturn" , "RegisterTransfer" , "Syscall" , "NilClass"].each_with_index do |name , index|
|
||||||
|
got = ticks(1)
|
||||||
|
assert got.class.name.index(name) , "Wrong class for #{index+1}, expect #{name} , got #{got}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_putstring
|
||||||
|
done = ticks(21)
|
||||||
|
assert_equal Register::Syscall , done.class
|
||||||
|
assert_equal "Hello again" , @interpreter.stdout
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_return
|
||||||
|
done = ticks(31)
|
||||||
|
assert_equal Register::FunctionReturn , done.class
|
||||||
|
assert @interpreter.block.is_a?(Virtual::Block)
|
||||||
|
assert @interpreter.instruction.is_a?(Register::Instruction) , "not instruction #{@interpreter.instruction}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_exit
|
||||||
|
done = ticks(34)
|
||||||
|
assert_equal NilClass , done.class
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user