Compare commits
No commits in common. "master" and "diy-render" have entirely different histories.
master
...
diy-render
47
.gitignore
vendored
47
.gitignore
vendored
@ -1,9 +1,42 @@
|
||||
# database
|
||||
db
|
||||
|
||||
# rdoc generated
|
||||
rdoc
|
||||
|
||||
# yard generated
|
||||
doc
|
||||
.yardoc
|
||||
|
||||
# bundler
|
||||
.bundle
|
||||
.config
|
||||
.yardoc
|
||||
tmp
|
||||
.idea
|
||||
.yardoc
|
||||
.sass-cache
|
||||
*.gem
|
||||
|
||||
# jeweler generated
|
||||
pkg
|
||||
|
||||
#
|
||||
# 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
|
||||
compiled
|
||||
|
||||
# For TextMate
|
||||
#*.tmproj
|
||||
#tmtags
|
||||
|
||||
# For emacs:
|
||||
#*~
|
||||
#\#*
|
||||
#.\#*
|
||||
|
||||
# For vim:
|
||||
#*.swp
|
||||
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
log
|
||||
|
||||
tmp/
|
||||
|
25
Gemfile
25
Gemfile
@ -1,21 +1,24 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "opal"
|
||||
gem 'opal-sprockets'
|
||||
gem "opal" , :github => "opal/opal"
|
||||
gem 'opal-browser'
|
||||
|
||||
gem "rubyx" , "0.6" , path: "../rubyx"
|
||||
|
||||
gem "rx-file" , :git => "https://github.com/ruby-x/rx-file"
|
||||
#gem "salama" , "0.2" , :path => "../salama"
|
||||
|
||||
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 "sass"
|
||||
gem "susy"
|
||||
|
||||
group :test do
|
||||
# Testing dependencies
|
||||
gem "minitest"
|
||||
gem 'rspec'
|
||||
gem 'capybara'
|
||||
gem 'selenium-webdriver'
|
||||
gem 'chromedriver2-helper'
|
||||
gem 'poltergeist'
|
||||
gem 'rspec', '~> 3.2.0'
|
||||
gem 'opal-rspec', '~> 0.4.2'
|
||||
gem 'capybara', '~> 2.4.2'
|
||||
gem 'selenium-webdriver', '~> 2.43.0'
|
||||
gem 'chromedriver2-helper', '~> 0.0.8'
|
||||
gem 'poltergeist', '~> 1.5.0'
|
||||
end
|
||||
|
192
Gemfile.lock
192
Gemfile.lock
@ -1,120 +1,134 @@
|
||||
GIT
|
||||
remote: https://github.com/ruby-x/rx-file
|
||||
revision: 7c4a5546136d1bad065803da91778b209c18cb4d
|
||||
remote: git://github.com/opal/opal.git
|
||||
revision: c604685071f8560d3357044683a99b80b210a99b
|
||||
specs:
|
||||
rx-file (0.3.0)
|
||||
opal (0.9.0.dev)
|
||||
hike (~> 1.2)
|
||||
sourcemap (~> 0.1.0)
|
||||
sprockets (~> 3.1)
|
||||
tilt (>= 1.4)
|
||||
|
||||
PATH
|
||||
remote: ../rubyx
|
||||
GIT
|
||||
remote: git://github.com/salama/parslet.git
|
||||
revision: b8bf8db20a242eb6bd8fec88027e3b2ae19276e8
|
||||
specs:
|
||||
rubyx (0.6.0)
|
||||
parser (~> 2.3.0)
|
||||
rx-file (~> 0.3)
|
||||
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: df95b8b54de103523743cbfd5c46d87af84c6b09
|
||||
specs:
|
||||
salama (0.2.0)
|
||||
salama-object-file (~> 0.2)
|
||||
salama-reader (~> 0.2)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
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)
|
||||
capybara (2.4.4)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
childprocess (0.5.6)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chromedriver2-helper (0.0.10)
|
||||
chromedriver2-helper (0.0.8)
|
||||
nokogiri
|
||||
cliver (0.3.2)
|
||||
concurrent-ruby (1.1.4)
|
||||
diff-lcs (1.3)
|
||||
ffi (1.10.0)
|
||||
diff-lcs (1.2.5)
|
||||
ffi (1.9.10)
|
||||
hike (1.2.3)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.4.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)
|
||||
parser (= 2.3.3.1)
|
||||
sourcemap (~> 0.1.0)
|
||||
mime-types (2.6.1)
|
||||
mini_portile (0.6.2)
|
||||
multi_json (1.11.2)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.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)
|
||||
tilt (>= 1.4)
|
||||
paggio (0.2.6)
|
||||
parser (2.3.3.1)
|
||||
ast (~> 2.2)
|
||||
poltergeist (1.18.1)
|
||||
capybara (>= 2.1, < 4)
|
||||
opal-rspec (0.4.3)
|
||||
opal (>= 0.7.0, < 0.9)
|
||||
paggio (0.2.4)
|
||||
poltergeist (1.5.1)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
public_suffix (3.0.3)
|
||||
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)
|
||||
rack (1.6.4)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rspec (3.2.0)
|
||||
rspec-core (~> 3.2.0)
|
||||
rspec-expectations (~> 3.2.0)
|
||||
rspec-mocks (~> 3.2.0)
|
||||
rspec-core (3.2.3)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-expectations (3.2.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-mocks (3.2.1)
|
||||
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)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-support (3.2.2)
|
||||
rubyzip (1.1.7)
|
||||
sass (3.4.16)
|
||||
selenium-webdriver (2.43.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.2, >= 1.2.2)
|
||||
multi_json (~> 1.0)
|
||||
rubyzip (~> 1.0)
|
||||
websocket (~> 1.0)
|
||||
sourcemap (0.1.1)
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
tilt (2.0.9)
|
||||
websocket-driver (0.7.0)
|
||||
sprockets (3.3.2)
|
||||
rack (~> 1.0)
|
||||
susy (2.2.5)
|
||||
sass (>= 3.3.0, < 3.5)
|
||||
tilt (2.0.1)
|
||||
websocket (1.2.2)
|
||||
websocket-driver (0.5.4)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.3)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
websocket-extensions (0.1.2)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
capybara
|
||||
chromedriver2-helper
|
||||
minitest
|
||||
opal
|
||||
capybara (~> 2.4.2)
|
||||
chromedriver2-helper (~> 0.0.8)
|
||||
opal!
|
||||
opal-browser
|
||||
opal-sprockets
|
||||
poltergeist
|
||||
rspec
|
||||
rubyx (= 0.6)!
|
||||
rx-file!
|
||||
opal-rspec (~> 0.4.2)
|
||||
parslet!
|
||||
poltergeist (~> 1.5.0)
|
||||
rspec (~> 3.2.0)
|
||||
salama!
|
||||
salama-arm!
|
||||
salama-object-file!
|
||||
salama-reader!
|
||||
sass
|
||||
selenium-webdriver
|
||||
selenium-webdriver (~> 2.43.0)
|
||||
susy
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
1.10.5
|
||||
|
128
README.md
128
README.md
@ -1,117 +1,23 @@
|
||||
### statement of intent
|
||||
|
||||
just starting and not quite sure, but here is the direction
|
||||
|
||||
# Debugger
|
||||
|
||||
After some tryouts it ended up being an Opal application. That is ruby as javascript in the browser.
|
||||
Below is a screenshot.
|
||||
I don't want to use gdb anymore, and it would be easier without using the qemu setup, so:
|
||||
|
||||

|
||||
|
||||
## Views
|
||||
|
||||
From left to right there are several views showing different data and controls.
|
||||
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)
|
||||
- single step debugging of the register machine level (as close to arm as need be)
|
||||
- visual transitions for steps
|
||||
- visualisation of data in registers (some kind of link to the object)
|
||||
- show the current instruction and a few around
|
||||
- show vm object (message etc)
|
||||
- show effect of register transitions on vm objects
|
||||
- visualize vm object content (again some links)
|
||||
|
||||
|
||||
### Source View
|
||||
# Space
|
||||
|
||||
Next is a view of the Soml source. The Source is reconstructed from the ast as html.
|
||||
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.
|
||||
|
||||
Currently stepping is done only in register instructions, which means that depending on the source
|
||||
constructs it may take many steps for the cursor to move on.
|
||||
|
||||
Each step will show progress on the register level though (next view)
|
||||
|
||||
|
||||
### Register Instruction view
|
||||
|
||||
Salama defines a register machine level which is quite close to the arm machine, but with more
|
||||
sensible names. It has 16 registers (below) and an instruction set that is useful for Soml.
|
||||
|
||||
Data movement related instruction implement an indexed get and set. There is also Constant load and
|
||||
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
|
||||
- Visualise the object space in some way
|
||||
- Visualise single object, bit like atoms
|
||||
- values immediate
|
||||
- objects as link
|
||||
|
@ -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
|
71
assets/css/app.css.scss
Normal file
71
assets/css/app.css.scss
Normal file
@ -0,0 +1,71 @@
|
||||
// Place your apps css here
|
||||
@import "susy";
|
||||
|
||||
|
||||
$susy: (
|
||||
columns: 24 ,
|
||||
gutter-position: split ,
|
||||
);
|
||||
|
||||
.debugger_view { @include container(90%); }
|
||||
|
||||
.classes {
|
||||
@include span(3);
|
||||
}
|
||||
|
||||
.one_class {
|
||||
margin: 10px;
|
||||
}
|
||||
.file_view {
|
||||
@include span(4);
|
||||
margin-left: span(1);
|
||||
}
|
||||
|
||||
.source_view { @include span(6); }
|
||||
|
||||
.block_view {
|
||||
@include span(4);
|
||||
margin-right: span(2);
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.status_view {
|
||||
@include span(2 at 22);
|
||||
line-height : 1.25em;
|
||||
}
|
||||
|
||||
.registers_view {
|
||||
@include span(20 at 3);
|
||||
}
|
||||
|
||||
.register_view {
|
||||
@include gallery(4);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.field {
|
||||
@include span(5)
|
||||
}
|
||||
|
||||
.value {
|
||||
@include span(18)
|
||||
}
|
||||
|
||||
.value_head {
|
||||
background-color: #C5FFD9;
|
||||
}
|
||||
|
||||
.act{
|
||||
background-color: #00B3FF;
|
||||
-moz-border-radius: 7px;
|
||||
-webkit-border-radius: 7px;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.bright {
|
||||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
background-color: #00E3FF ;
|
||||
-moz-border-radius: 7px;
|
||||
-webkit-border-radius: 7px;
|
||||
}
|
@ -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
|
60
assets/css/menu.css.scss
Normal file
60
assets/css/menu.css.scss
Normal file
@ -0,0 +1,60 @@
|
||||
body {
|
||||
font-family: arial, helvetica, serif;
|
||||
}
|
||||
|
||||
#nav, #nav ul { /* all lists */
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
float : left;
|
||||
width : 10em;
|
||||
/*border around submenu goes here*/
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
background:#fff;
|
||||
border:1px solid #C3D46A
|
||||
}
|
||||
|
||||
#nav li { /* all list items */
|
||||
position : relative;
|
||||
float : left;
|
||||
line-height : 1.25em;
|
||||
width: 9em;
|
||||
}
|
||||
|
||||
#nav li ul { /* second-level lists */
|
||||
position : absolute;
|
||||
left: -999em;
|
||||
margin-left : 10.0em;
|
||||
margin-top : -2.7em;
|
||||
}
|
||||
|
||||
#nav li ul ul { /* third-and-above-level lists */
|
||||
left: -999em;
|
||||
}
|
||||
|
||||
#nav li a , li span {
|
||||
padding-left: 0.5em;
|
||||
width : 9.5em;
|
||||
display : block;
|
||||
color : black;
|
||||
font-weight : bold;
|
||||
text-decoration : none;
|
||||
background-color : white;
|
||||
-moz-border-radius: 7px;
|
||||
-webkit-border-radius: 7px;
|
||||
}
|
||||
|
||||
#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
|
25
config.ru
25
config.ru
@ -1,16 +1,31 @@
|
||||
require 'bundler'
|
||||
Bundler.require
|
||||
require 'tilt/erb'
|
||||
|
||||
require_relative "lib/parse_task"
|
||||
|
||||
require "opal"
|
||||
require 'opal-sprockets'
|
||||
require 'opal-browser'
|
||||
|
||||
Opal.use_gem("rubyx")
|
||||
Opal.use_gem("salama")
|
||||
Opal.use_gem("salama-arm")
|
||||
|
||||
run Opal::Sprockets::Server.new { |s|
|
||||
s.main = 'debugger.js.rb'
|
||||
class DebugServer < Opal::Server
|
||||
def call(env)
|
||||
if( env["REQUEST_PATH"] == "/tasks.json")
|
||||
[200, { 'Content-Type' => 'text/json' }, [ParseTask.new.parse(1).to_json]]
|
||||
else
|
||||
super(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
run DebugServer.new { |s|
|
||||
s.main = 'debugger'
|
||||
s.append_path 'lib'
|
||||
s.append_path 'assets'
|
||||
# s.source_map = true
|
||||
s.debug = !ENV["DEBUG"].nil?
|
||||
|
||||
s.index_path = "index.html.erb"
|
||||
s.sprockets.cache = Sprockets::Cache::MemoryStore.new(50000)
|
||||
s.sprockets.cache = Sprockets::Cache::MemoryStore.new(5000)
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Debugger</title>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="/assets/css/menu.css" type="text/css" charset="utf-8">
|
||||
<link rel="stylesheet" href="/assets/css/app.css" type="text/css" charset="utf-8">
|
||||
</head>
|
||||
|
@ -1,17 +1,12 @@
|
||||
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
|
||||
|
@ -1,12 +1,3 @@
|
||||
# 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
|
||||
@ -27,7 +18,7 @@ class ElementView
|
||||
# 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)
|
||||
def div name_class = "div" , text = nil
|
||||
name , clazz = name_class.split(".")
|
||||
name = "div" if name.empty?
|
||||
element = $document.create_element(name)
|
||||
@ -58,8 +49,8 @@ class ElementView
|
||||
@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>
|
||||
#wrap the given node with the wappper, so for a div wrapper and a button node
|
||||
# the result will be <div> <button>hwatever was in there</button> <div>
|
||||
def wrap_node_with node , wrapper
|
||||
node.replace_with(wrapper) if node.parent
|
||||
wrapper << node
|
||||
|
@ -1,14 +1,5 @@
|
||||
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
|
||||
@ -16,10 +7,6 @@ class ListView < ElementView
|
||||
@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
|
||||
@ -34,13 +21,13 @@ class ListView < ElementView
|
||||
|
||||
# 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)
|
||||
# the old node will be replaces in the live dom
|
||||
def replace_at index , with
|
||||
old = @elements[index]
|
||||
@children[index] = node
|
||||
rendered = node.draw
|
||||
@children[index] = with
|
||||
rendered = with.draw
|
||||
@elements[index] = rendered
|
||||
old.replace_with(rendered) if old
|
||||
old.replace_with rendered
|
||||
end
|
||||
|
||||
# remove the first child and element (from view)
|
||||
@ -50,15 +37,10 @@ class ListView < ElementView
|
||||
|
||||
# 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)
|
||||
raise "insex 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? )
|
||||
element.remove
|
||||
end
|
||||
|
||||
# append a View instnace to the children array
|
||||
|
58
lib/blocks_view.rb
Normal file
58
lib/blocks_view.rb
Normal file
@ -0,0 +1,58 @@
|
||||
|
||||
class BlocksView < ListView
|
||||
|
||||
def initialize interpreter
|
||||
@interpreter = interpreter
|
||||
@interpreter.register_event(:instruction_changed, self)
|
||||
super([BlockView.new(@interpreter.block)])
|
||||
@method_name = method_name
|
||||
end
|
||||
|
||||
def draw
|
||||
super()
|
||||
wrap_element div("div.block_view") << div("h4" , "Method + Block " ) << div("h4.method" , @method_name)
|
||||
return @element
|
||||
end
|
||||
|
||||
def instruction_changed
|
||||
new_name = method_name
|
||||
unless new_name == @method_name
|
||||
@method_name = new_name
|
||||
@element.at_css(".method").text = method_name
|
||||
end
|
||||
return if @interpreter.block.object_id == @children.last.block.object_id
|
||||
@elements.last.at_css(".bright").remove_class("bright")
|
||||
append_view( BlockView.new(@interpreter.block) )
|
||||
remove_first if( @elements.length > 6)
|
||||
end
|
||||
|
||||
def method_name
|
||||
bl = @interpreter.block
|
||||
return "" unless bl
|
||||
return bl.method if bl.method.is_a? String
|
||||
"#{bl.method.for_class.name}.#{bl.method.name}"
|
||||
end
|
||||
end
|
||||
|
||||
class BlockView < ElementView
|
||||
|
||||
def initialize block
|
||||
@block = block
|
||||
end
|
||||
attr_reader :block
|
||||
|
||||
def draw
|
||||
@element = div("div") << div("span.bright" , block_name )
|
||||
end
|
||||
|
||||
def method_name
|
||||
return @block.method if @block.method.is_a? String
|
||||
@block.method.name
|
||||
end
|
||||
|
||||
def block_name
|
||||
return @block if @block.is_a? String
|
||||
"#{method_name}.#{@block.name}"
|
||||
end
|
||||
|
||||
end
|
37
lib/classes_view.rb
Normal file
37
lib/classes_view.rb
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
class ClassesView < ListView
|
||||
|
||||
def initialize interpreter
|
||||
@interpreter = interpreter
|
||||
classes = []
|
||||
Virtual.machine.space.classes.each do |name , claz|
|
||||
next if [:Kernel,:Module,:MetaClass,:BinaryCode].index name
|
||||
classes << claz
|
||||
end
|
||||
classes.sort! {|a,b| a.name <=> b.name }
|
||||
super( classes.collect{|c| ClassView.new(c)})
|
||||
end
|
||||
|
||||
def draw
|
||||
super()
|
||||
wrap_element div("ul.nav!")
|
||||
wrap_element( div(".classes") << div("h4" , "Classes") )
|
||||
return @element
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ClassView < ElementView
|
||||
def initialize clazz
|
||||
@clazz = clazz
|
||||
end
|
||||
|
||||
def draw
|
||||
@element = div("li") << div( "a" , @clazz.name ) << (ul = div("ul"))
|
||||
@clazz.object_layout.object_instance_names.each do |name|
|
||||
ul << (div("li") << div("a", name ))
|
||||
end
|
||||
@element.style["z-index"] = 20
|
||||
@element
|
||||
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)
|
47
lib/debugger.rb
Normal file
47
lib/debugger.rb
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
require "opal"
|
||||
require "opal-parser"
|
||||
|
||||
|
||||
require 'browser'
|
||||
require 'native'
|
||||
require "salama"
|
||||
require "interpreter/interpreter"
|
||||
require "base/list_view"
|
||||
require_relative "classes_view"
|
||||
require_relative "status_view"
|
||||
require_relative "file_view"
|
||||
require_relative "blocks_view"
|
||||
require_relative "instruction_view"
|
||||
require_relative "registers_view"
|
||||
|
||||
class MainView < ListView
|
||||
|
||||
def initialize
|
||||
machine = Virtual.machine.boot
|
||||
|
||||
# compile_main includes the parse
|
||||
# parsing generates an ast as seen below and then compiles it.
|
||||
# machine.compile_main "2 + 5"
|
||||
|
||||
# so the code above is functionally equivalent to the one below, minus the parse
|
||||
# When the ast expression is given all works, so pretty sure it is the parse that fails
|
||||
|
||||
code = Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))
|
||||
Virtual::Compiler.compile( code , machine.space.get_main )
|
||||
|
||||
machine.run_before "Register::CallImplementation"
|
||||
@interpreter = Interpreter::Interpreter.new
|
||||
@interpreter.start machine.init
|
||||
super( [ClassesView.new(@interpreter) ,
|
||||
FileView.new ,
|
||||
BlocksView.new(@interpreter) ,
|
||||
InstructionView.new(@interpreter) ,
|
||||
StatusView.new(@interpreter) ,
|
||||
RegistersView.new(@interpreter) ] )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
view = MainView.new()
|
||||
view.draw.append_to($document.body)
|
8
lib/file_view.rb
Normal file
8
lib/file_view.rb
Normal file
@ -0,0 +1,8 @@
|
||||
class FileView < ElementView
|
||||
|
||||
|
||||
def draw
|
||||
@element = div(".file_view") << div("h4" ,"Future")
|
||||
end
|
||||
|
||||
end
|
30
lib/instruction_view.rb
Normal file
30
lib/instruction_view.rb
Normal file
@ -0,0 +1,30 @@
|
||||
require "base/constant_view"
|
||||
require "base/list_view"
|
||||
|
||||
class InstructionView < ListView
|
||||
|
||||
def initialize interpreter
|
||||
@interpreter = interpreter
|
||||
super([ConstantView.new( "span.bright" , "starting" )])
|
||||
@interpreter.register_event(:instruction_changed, self)
|
||||
end
|
||||
|
||||
def instruction_changed
|
||||
@element.at_css(".bright").remove_class("bright")
|
||||
instruction = append_view( ConstantView.new( "span.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(".source_view") << div("h4" ,"Virtual Machine Instruction")
|
||||
@element
|
||||
end
|
||||
|
||||
def instruction_text
|
||||
return "" unless @interpreter.instruction
|
||||
@interpreter.instruction.to_s
|
||||
end
|
||||
end
|
54
lib/object_view.rb
Normal file
54
lib/object_view.rb
Normal file
@ -0,0 +1,54 @@
|
||||
require_relative "ref_view"
|
||||
|
||||
class ObjectView < ListView
|
||||
|
||||
def initialize object_id , interpreter = nil , z = nil
|
||||
@object_id = object_id
|
||||
@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" , "-------------------------")
|
||||
prepend_element div( "li" ) << div("span" , class_header(@object_id) )
|
||||
return @element
|
||||
end
|
||||
|
||||
def object_changed reg , at
|
||||
puts "Object changed in #{reg}"
|
||||
for_object = @interpreter.get_register( reg )
|
||||
return unless for_object == @object_id
|
||||
puts "Object changed #{for_object} , at #{at}"
|
||||
|
||||
end
|
||||
|
||||
def class_header(id)
|
||||
object = Virtual.machine.objects[id]
|
||||
clazz = object.class.name.split("::").last
|
||||
[clazz, id].join " : "
|
||||
end
|
||||
|
||||
def content_elements
|
||||
object = Virtual.machine.objects[@object_id]
|
||||
fields = []
|
||||
if object and ! object.is_a?(String)
|
||||
fields << RefView.new( "layout" , object.get_layout.object_id , @z )
|
||||
object.get_instance_variables.each do |variable|
|
||||
f = object.get_instance_variable(variable)
|
||||
fields << RefView.new( variable , f.object_id , @z )
|
||||
end
|
||||
if( object.is_a?(Parfait::List) )
|
||||
index = 1
|
||||
object.each do | o , i|
|
||||
fields << RefView.new( index.to_s , o.object_id , @z )
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
fields
|
||||
end
|
||||
|
||||
end
|
11
lib/parse_task.rb
Normal file
11
lib/parse_task.rb
Normal file
@ -0,0 +1,11 @@
|
||||
require "salama-reader"
|
||||
|
||||
class ParseTask
|
||||
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
|
||||
end
|
@ -15,54 +15,44 @@ class RefView < ListView
|
||||
end
|
||||
|
||||
def draw
|
||||
@element = div("li") << div("a" , ref_text )
|
||||
@element = div("li") << div("a" , "#{@name} : #{marker(@value)}" )
|
||||
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?
|
||||
Virtual.machine.objects[@value] != nil
|
||||
end
|
||||
|
||||
def is_string?()
|
||||
@value.is_a? String
|
||||
end
|
||||
|
||||
def is_label?
|
||||
@value.is_a?(Risc::Label)
|
||||
Virtual.machine.objects[@value].is_a? String
|
||||
end
|
||||
|
||||
def is_nil?()
|
||||
@value.nil?
|
||||
Virtual.machine.objects[@value].nil?
|
||||
end
|
||||
|
||||
def hover
|
||||
#puts "hovering #{@name}"
|
||||
puts "hovering #{@name}"
|
||||
append_view ObjectView.new(@value)
|
||||
@element.off("hover")
|
||||
end
|
||||
|
||||
def marker
|
||||
def marker id
|
||||
if is_string?
|
||||
str = @value
|
||||
elsif is_object?
|
||||
str = Risc::Position.get(@value).to_s
|
||||
elsif is_label?
|
||||
str = "Label"
|
||||
elsif is_nil?
|
||||
str = "nil"
|
||||
else
|
||||
str = @value.to_s
|
||||
var = Virtual.machine.objects[id]
|
||||
str = var.class.name.split("::").last[0,2]
|
||||
str + " : #{id.to_s}"
|
||||
end
|
||||
end
|
||||
|
@ -21,16 +21,11 @@ class RegistersView < ListView
|
||||
@element
|
||||
end
|
||||
|
||||
def register_changed( reg , old , value )
|
||||
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
|
||||
if( is_object? value )
|
||||
swap = ObjectView.new( value , @interpreter , 16 - index )
|
||||
else
|
||||
swap = ValueView.new value
|
||||
end
|
||||
@ -38,6 +33,10 @@ class RegistersView < ListView
|
||||
# @elements[index].style["z-index"] = -index
|
||||
end
|
||||
|
||||
def is_object?( id )
|
||||
Virtual.machine.objects[id] != nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ValueView < ElementView
|
||||
@ -47,8 +46,6 @@ class ValueView < ElementView
|
||||
end
|
||||
|
||||
def draw
|
||||
li = div("li")
|
||||
li << div("span", @value)
|
||||
@element = div("ul.nav!") << li
|
||||
@element = div("ul.nav!") << div("li") << div("span", @value)
|
||||
end
|
||||
end
|
42
lib/status_view.rb
Normal file
42
lib/status_view.rb
Normal file
@ -0,0 +1,42 @@
|
||||
class StatusView < ElementView
|
||||
|
||||
def initialize interpreter
|
||||
@interpreter = interpreter
|
||||
end
|
||||
|
||||
def draw
|
||||
@element = div(".status_view") <<
|
||||
div("h4" , "Interpreter" ) <<
|
||||
div("button.act" , "Next") <<
|
||||
div( "br") <<
|
||||
div("span.clock" , clock_text) <<
|
||||
div( "br") <<
|
||||
div("span.state" , state_text) <<
|
||||
div( "br") <<
|
||||
div( "span.link" , link_text) <<
|
||||
div( "br" , "Stdout") <<
|
||||
div("span.stdout")
|
||||
# set up event handler
|
||||
@element.at_css(".act").on("click") { self.update }
|
||||
return @element
|
||||
end
|
||||
|
||||
def update
|
||||
@interpreter.tick
|
||||
@element.at_css(".clock").text = clock_text
|
||||
@element.at_css(".link").text = link_text
|
||||
@element.at_css(".stdout").text = @interpreter.stdout
|
||||
end
|
||||
|
||||
def link_text
|
||||
"Link #{@interpreter.link}"
|
||||
end
|
||||
|
||||
def state_text
|
||||
"State #{@interpreter.state}"
|
||||
end
|
||||
|
||||
def clock_text
|
||||
"Instruction #{@interpreter.clock}"
|
||||
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,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
|
@ -0,0 +1,5 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'sample http controller test', type: :http_controller do
|
||||
# Specs here
|
||||
end
|
11
spec/app/main/integration/sample_integration_spec.rb
Normal file
11
spec/app/main/integration/sample_integration_spec.rb
Normal file
@ -0,0 +1,11 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'sample integration test', type: :feature do
|
||||
# An example integration spec, this will only be run if ENV['BROWSER'] is
|
||||
# specified. Current values for ENV['BROWSER'] are 'firefox' and 'phantom'
|
||||
it 'should load the page' do
|
||||
visit '/'
|
||||
|
||||
expect(page).to have_content('Home')
|
||||
end
|
||||
end
|
5
spec/app/main/models/sample_model_spec.rb
Normal file
5
spec/app/main/models/sample_model_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'sample model' do
|
||||
# Specs here
|
||||
end
|
5
spec/app/main/tasks/sample_task_spec.rb
Normal file
5
spec/app/main/tasks/sample_task_spec.rb
Normal file
@ -0,0 +1,5 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'sample task', type: :task do
|
||||
# Specs here
|
||||
end
|
14
spec/spec_helper.rb
Normal file
14
spec/spec_helper.rb
Normal file
@ -0,0 +1,14 @@
|
||||
# Volt sets up rspec and capybara for testing.
|
||||
require 'volt/spec/setup'
|
||||
Volt.spec_setup
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.filter_run :focus
|
||||
|
||||
# Run specs in random order to surface order dependencies. If you find an
|
||||
# order dependency and want to debug it, you can fix the order by providing
|
||||
# the seed, which is printed after each run.
|
||||
# --seed 1234
|
||||
config.order = 'random'
|
||||
end
|
Binary file not shown.
Before Width: | Height: | Size: 375 KiB |
Loading…
x
Reference in New Issue
Block a user