Compare commits

..

No commits in common. "master" and "diy-render" have entirely different histories.

48 changed files with 681 additions and 1187 deletions

47
.gitignore vendored
View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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:
![Debugger](https://raw.githubusercontent.com/rubyx/rubyx-debugger/master/static/debugger.png)
## 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

View File

@ -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
View 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;
}

View File

@ -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
View 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;
}

View File

@ -1,5 +0,0 @@
class Object
int main()
return 55.puti()
end
end

View File

@ -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

View File

@ -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

View File

@ -1,6 +0,0 @@
class Object
int main()
"Hello World".putstring()
return 1
end
end

View File

@ -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

View File

@ -1,10 +0,0 @@
class Object
int main()
int n = 14
if_plus( n - 12)
"then".putstring()
else
"else".putstring()
end
end
end

View File

@ -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

View File

@ -1,8 +0,0 @@
class Object
int puts(Word str)
return str
end
int main()
puts("Hello")
end
end

View File

@ -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

View File

@ -1,12 +0,0 @@
class Object
int main()
int i = 5
while_plus(i)
"out ".putstring()
i = i - 1
end
return i
end
end

View File

@ -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)
}

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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
View 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
View 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
View 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
View 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
View 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

View File

@ -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

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe 'sample http controller test', type: :http_controller do
# Specs here
end

View 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

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe 'sample model' do
# Specs here
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe 'sample task', type: :task do
# Specs here
end

14
spec/spec_helper.rb Normal file
View 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