56 Commits

Author SHA1 Message Date
6312b0962e minor 2015-07-31 15:52:58 +03:00
79383d89b1 trying to get register view to update 2015-07-29 16:39:15 +03:00
933b64c499 better docs, new pic 2015-07-28 16:36:29 +03:00
62e5761a40 using github gems 2015-07-28 16:35:45 +03:00
07f7945917 move to suzy is done 2015-07-28 16:16:33 +03:00
756ab31a61 add status view and fixes 2015-07-28 16:16:09 +03:00
557c455167 switch to susy
a rewire basically, but much clearer
2015-07-27 21:29:41 +03:00
7e34ae003e integrating the sources to views 2015-07-27 12:09:35 +03:00
e460e0041b finishes the yellow world instructions 2015-07-26 18:27:12 +03:00
87cec0f4bc some read me info 2015-07-26 14:38:07 +03:00
1ae4baf3f7 implement put string and fix set 2015-07-26 13:59:58 +03:00
228b1b5832 slight improvement on the register view
still lagging, but …
2015-07-26 13:24:00 +03:00
add84432b4 removed unused 2015-07-26 12:52:29 +03:00
6ce51c38db better view for registers 2015-07-26 12:51:57 +03:00
6ba3c54690 three more instruction for interpreter 2015-07-26 09:04:57 +03:00
3464e50b2f better block view 2015-07-26 09:04:34 +03:00
515e1b106b better register an instruction view 2015-07-25 21:04:20 +03:00
b693f55a29 showing active instruction with bg 2015-07-25 15:28:59 +03:00
2f66bd9f08 Slot instructions and tests 2015-07-24 21:15:47 +03:00
93ac945dd2 first mini test, remove spec 2015-07-24 18:38:37 +03:00
1526656dfe fix register (sym/ref) 2015-07-24 17:21:13 +03:00
959cb628fa implements fetching of next instruction 2015-07-24 15:48:12 +03:00
5d42da9d03 next instruction working
but no clock yet
2015-07-24 13:56:31 +03:00
f5ece67233 first interpreted instruction, with gui change 2015-07-24 10:15:58 +03:00
246bb5ee4f debug off is faster 2015-07-24 10:15:27 +03:00
0cdd5fdf13 registering events with objects not procs
to make unregistering easier
2015-07-24 10:01:01 +03:00
8183dfd6b1 simple events and triggers in interpreter 2015-07-23 20:09:19 +03:00
950c77b127 react update 2015-07-23 20:08:50 +03:00
a896702880 passing the interpreter around 2015-07-23 20:08:40 +03:00
53d576ef90 showing actual block and actual instruction
time to ponder a little more
2015-07-23 17:26:48 +03:00
1f19a28906 trying to find exception 2015-07-23 16:23:10 +03:00
03c0041255 create separate main
figuring load order
2015-07-23 14:59:29 +03:00
736bff02a5 trying to find .name error 2015-07-23 14:59:15 +03:00
2645f54d5b move registers to hash 2015-07-23 13:20:53 +03:00
aa2303ddaa add lib and interpreter skeleton 2015-07-23 13:15:44 +03:00
f4dfbb5845 move to haml style class names 2015-07-23 13:15:26 +03:00
398d364c02 got vm to boot and classes to show 2015-07-22 23:08:36 +03:00
3d96d59371 sort of noop of version is working 2015-07-22 22:05:31 +03:00
a0721a0cde move to opal from sinatra
also external index
2015-07-22 17:10:50 +03:00
d021816726 move sources to app directory 2015-07-22 16:26:46 +03:00
e8d9000a74 file per class 2015-07-22 16:22:54 +03:00
db7fa423bb a noon start, copied from examples 2015-07-22 16:17:25 +03:00
bd882b7d2e remove volt 2015-07-21 20:25:57 +03:00
5c2f423f58 reinstantiating lib dir 2015-07-18 11:25:11 +03:00
19a0b7f9fe get vm source on screen 2015-07-18 11:22:42 +03:00
4305ddaf1d copied salama in for reload
but have to rethink approach
2015-07-17 22:44:20 +03:00
b5b0630060 rename 2015-07-17 19:27:46 +03:00
c22a4a95d3 add block inspect, status: up to first jump 2015-07-17 15:14:41 +03:00
69414a11f9 fix eval by including parser 2015-07-17 11:42:21 +03:00
40d97dcf87 remove double boot 2015-07-17 11:41:59 +03:00
acd66f66f3 add hint.css for tooltips 2015-07-17 11:37:02 +03:00
f2af076c8e small view change 2015-07-16 17:35:19 +03:00
1efce561d7 basic html skeleton, classes and registers 2015-07-16 10:43:49 +03:00
1d005862ac remove pixi, going back to html 2015-07-15 19:26:11 +03:00
e51d6cb3fa remove hardcoded size and move point code to pixie 2015-07-14 19:59:21 +03:00
d47d8a9e2a still playing luke, has the direction wrong 2015-07-14 16:57:44 +03:00
45 changed files with 1081 additions and 28277 deletions

51
Gemfile
View File

@ -1,44 +1,17 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'volt', :path => "../volt" gem 'opal-react', :path => '../react.rb'
gem 'opal-jquery'
gem 'react-source'
# volt uses mongo as the default data store. gem "parslet" , github: "salama/parslet"
gem 'volt-mongo', '~> 0.1.0' gem "salama" , github: "salama/salama"
gem "salama-reader" , github: "salama/salama-reader"
gem "salama-arm" , github: "salama/salama-arm"
gem "salama-object-file" , github: "salama/salama-object-file"
gem "susy"
# The following gem's are optional for themeing group :development do
# Twitter bootstrap gem "minitest"
gem 'volt-bootstrap', '~> 0.0.10' gem "rubygems-tasks"
# Asset compilation gems, they will be required when needed.
gem 'csso-rails', '~> 0.3.4', require: false
gem 'uglifier', '>= 2.4.0', require: false
gem 'opal-pixi' , :path => "../opal-pixi"
#gem "salama" , "0.2" , :path => "../salama"
gem "parslet" , path: "../parslet"
gem "salama" , path: "../salama"
gem "salama-reader" , path: "../salama-reader"
gem "salama-object-file" , path: "../salama-object-file"
group :test do
# Testing dependencies
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
# Server for MRI
platform :mri, :mingw do
# The implementation of ReadWriteLock in Volt uses concurrent ruby and ext helps performance.
gem 'concurrent-ruby-ext', '~> 0.8.0'
# Thin is the default volt server, Puma is also supported
gem 'thin', '~> 1.6.0'
gem 'bson_ext', '~> 1.9.0'
end end

View File

@ -1,188 +1,83 @@
PATH GIT
remote: ../opal-pixi remote: git://github.com/salama/parslet.git
specs: revision: beeb9b441a9ade1504f7f0e848d805e36a02c544
opal-pixi (0.1.0)
opal (~> 0.7.0)
PATH
remote: ../parslet
specs: specs:
parslet (1.7.0) parslet (1.7.0)
PATH GIT
remote: ../salama remote: git://github.com/salama/salama-arm.git
revision: 0bd5091e3f284ecf040e0086a41d2449cd5afb7a
specs:
salama-arm (0.0.1)
GIT
remote: git://github.com/salama/salama-object-file.git
revision: fbae6a02764dbe97e01e4833f9ffffe09879b100
specs:
salama-object-file (0.2.0)
GIT
remote: git://github.com/salama/salama-reader.git
revision: 841592c667acea1e796f950851262e6938b231bc
specs:
salama-reader (0.2.0)
parslet (~> 1.7.0)
GIT
remote: git://github.com/salama/salama.git
revision: 3fb08acf3f83aa403b095aa60be1702419d8a66d
specs: specs:
salama (0.2.0) salama (0.2.0)
salama-object-file (~> 0.2) salama-object-file (~> 0.2)
salama-reader (~> 0.2) salama-reader (~> 0.2)
PATH PATH
remote: ../salama-object-file remote: ../react.rb
specs: specs:
salama-object-file (0.2.0) opal-react (0.2.2)
opal
PATH opal-activesupport
remote: ../salama-reader
specs:
salama-reader (0.2.0)
parslet (~> 1.7.0)
PATH
remote: ../volt
specs:
volt (0.9.4)
bcrypt (~> 3.1.9)
bundler (>= 1.5)
concurrent-ruby (= 0.8.0)
configurations (~> 2.0.0.pre)
faye-websocket (~> 0.9.2)
listen (~> 3.0.1)
opal (~> 0.7.2)
pry (~> 0.10.1)
rack (~> 1.5.0)
sass (~> 3.2.5)
sprockets-sass (~> 1.0.0)
thor (~> 0.19.0)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
bcrypt (3.1.10)
bson (1.9.2)
bson_ext (1.9.2)
bson (~> 1.9.2)
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.8)
nokogiri
cliver (0.3.2)
coderay (1.1.0)
concurrent-ruby (0.8.0)
ref (~> 1.0, >= 1.0.5)
concurrent-ruby-ext (0.8.0)
concurrent-ruby (~> 0.8.0)
configurations (2.0.0)
csso-rails (0.3.4)
execjs (>= 1)
daemons (1.2.3)
diff-lcs (1.2.5)
eventmachine (1.0.7)
execjs (2.5.2)
faye-websocket (0.9.2)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
ffi (1.9.10)
hike (1.2.3) hike (1.2.3)
json (1.8.3) minitest (5.7.0)
listen (3.0.2) opal (0.8.0)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
method_source (0.8.2)
mime-types (2.6.1)
mini_portile (0.6.2)
mongo (1.9.2)
bson (~> 1.9.2)
multi_json (1.11.2)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
opal (0.7.2)
hike (~> 1.2) hike (~> 1.2)
sourcemap (~> 0.1.0) sourcemap (~> 0.1.0)
sprockets (>= 2.2.3, < 3.0.0) sprockets (~> 3.1)
tilt (~> 1.4) tilt (>= 1.4)
opal-rspec (0.4.3) opal-activesupport (0.1.0)
opal (>= 0.7.0, < 0.9) opal (>= 0.5.0, < 1.0.0)
poltergeist (1.5.1) opal-jquery (0.4.0)
capybara (~> 2.1) opal (>= 0.7.0, < 0.9.0)
cliver (~> 0.3.1) rack (1.6.4)
multi_json (~> 1.0) react-source (0.13.3)
websocket-driver (>= 0.2.0) rubygems-tasks (0.2.4)
pry (0.10.1) sass (3.4.16)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rack (1.5.5)
rack-test (0.6.3)
rack (>= 1.0)
rb-fsevent (0.9.5)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
ref (1.0.5)
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.2.0)
rspec-mocks (3.2.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.2.0)
rspec-support (3.2.2)
rubyzip (1.1.7)
sass (3.2.19)
selenium-webdriver (2.43.0)
childprocess (~> 0.5)
multi_json (~> 1.0)
rubyzip (~> 1.0)
websocket (~> 1.0)
slop (3.6.0)
sourcemap (0.1.1) sourcemap (0.1.1)
sprockets (2.12.4) sprockets (3.2.0)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) susy (2.2.5)
sprockets-sass (1.0.3) sass (>= 3.3.0, < 3.5)
sprockets (~> 2.0) tilt (2.0.1)
tilt (~> 1.1)
thin (1.6.3)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0)
rack (~> 1.0)
thor (0.19.1)
tilt (1.4.1)
uglifier (2.7.1)
execjs (>= 0.3.0)
json (>= 1.8.0)
volt-bootstrap (0.0.10)
volt-mongo (0.1.1)
mongo (~> 1.9.0)
websocket (1.2.2)
websocket-driver (0.5.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
xpath (2.0.0)
nokogiri (~> 1.3)
PLATFORMS PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
bson_ext (~> 1.9.0) minitest
capybara (~> 2.4.2) opal-jquery
chromedriver2-helper (~> 0.0.8) opal-react!
concurrent-ruby-ext (~> 0.8.0)
csso-rails (~> 0.3.4)
opal-pixi!
opal-rspec (~> 0.4.2)
parslet! parslet!
poltergeist (~> 1.5.0) react-source
rspec (~> 3.2.0) rubygems-tasks
salama! salama!
salama-arm!
salama-object-file! salama-object-file!
salama-reader! salama-reader!
selenium-webdriver (~> 2.43.0) susy
thin (~> 1.6.0)
uglifier (>= 2.4.0) BUNDLED WITH
volt! 1.10.5
volt-bootstrap (~> 0.0.10)
volt-mongo (~> 0.1.0)

View File

@ -1,9 +1,22 @@
### statement of intent
just starting and not quite sure, but here is the direction
# Debugger # Debugger
After some tryouts it ended up being an Opal application. That is ruby as javascript in the browser.
![Debugger](https://raw.githubusercontent.com/salama/salama-debugger/master/static/debugger.png)
- On the left are the classes of the system. Next idea is to have hover info about them.
- Next a source code view (not implemented)
- next a view of the Virtual Instructions
- last section, current block with current Register Instruction highlighted
- step (next) button for single stepping
- status: starting , running , exited
- bottom row are the registers. If the register hold an object the variables are shown.
(also should have hover info) , the first letter indicates the class, the number is the address
So lots to do, but a good start.
I don't want to use gdb anymore, and it would be easier without using the qemu setup, so: I don't want to use gdb anymore, and it would be easier without using the qemu setup, so:
- single step debugging of the register machine level (as close to arm as need be) - single step debugging of the register machine level (as close to arm as need be)

50
app/block_view.rb Normal file
View File

@ -0,0 +1,50 @@
require "instruction_view"
class BlockView
include React::Component
required_param :interpreter
define_state :block => []
define_state :block_name => ""
before_mount do
interpreter.register_event(:instruction_changed, self)
update_block
end
def update_block
return unless interpreter.instruction
block_name! interpreter.block.name
codes = interpreter.block.codes.dup
slice = codes.index(interpreter.instruction) #- 1
codes.shift( slice ) if slice >= 0
codes.pop while(codes.length > 4)
block! codes
end
def instruction_changed
update_block
end
def render
return unless block
div.block_view do
div do
h4 { method_name}
h4 {"Block: #{block_name}"}
end
block.each do |code|
InstructionView :interpreter => interpreter , :instruction => code
end
end
end
def method_name
bl = interpreter.block
return bl.method if bl.method.is_a? String
"#{bl.method.for_class.name}.#{bl.method.name}"
end
end

16
app/class_view.rb Normal file
View File

@ -0,0 +1,16 @@
class ClassView
include React::Component
required_param :classes, type: {}
def render
div.classes do
h4 { "Classes" }
classes.each do |name , clas|
div.one_class do
clas.name
end
end
end
end
end

37
app/debugger.rb Normal file
View File

@ -0,0 +1,37 @@
require "register_view"
require "class_view"
require "source_view"
require "block_view"
require "status_view"
class Debugger
include React::Component
required_param :machine , :type => Virtual::Machine
define_state :interpreter => Interpreter.new
before_mount do
code = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:putstring, [] ,Ast::StringExpression.new("Hello again"))])
Virtual::Compiler.compile( code , machine.space.get_main )
machine.run_before "Register::CallImplementation"
interpreter.start machine.init
end
def render
div.debugger_view do
ClassView classes: machine.space.classes
div.file_view do
"Future Source code view"
end
SourceView :interpreter => interpreter
BlockView :interpreter => interpreter
StatusView :interpreter => interpreter
div.registers_view do
interpreter.registers.each do |r , oid|
RegisterView interpreter: interpreter , register: r
end
end
end
end
end

28
app/instruction_view.rb Normal file
View File

@ -0,0 +1,28 @@
class InstructionView
include React::Component
required_param :interpreter
required_param :instruction
define_state :active => ""
before_mount do
check_active interpreter.instruction
end
def check_active i
active! instruction == i ? "bright" : ""
end
def instruction_changed old , ins
check_active ins
end
def render
div :class => active do
instruction.to_s if instruction
end
end
end

16
app/main.rb Normal file
View File

@ -0,0 +1,16 @@
require 'opal'
require "opal/parser"
require "salama"
require "interpreter"
require 'opal-react'
require "debugger"
require 'opal-jquery'
Document.ready? do # Document.ready? is a opal-jquery method.
machine = Virtual.machine.boot
React.render( React.create_element( Debugger , :machine => machine ), Element['#content'] )
end

View File

@ -1 +0,0 @@
// Place your apps css here

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 B

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
# Specify which components you wish to include when
# the "home" component loads.
# bootstrap css framework
component 'bootstrap'
Opal.use_gem("salama")

View File

@ -1,10 +0,0 @@
# Place any code you want to run when the component is included on the client
# or server.
# To include code only on the client use:
# if RUBY_PLATFORM == 'opal'
#
# To include code only on the server, use:
# unless RUBY_PLATFORM == 'opal'
# ^^ this will not send compile in code in the conditional to the client.
# ^^ this include code required in the conditional.

View File

@ -1,7 +0,0 @@
# See https://github.com/voltrb/volt#routes for more info on routes
client '/about', action: 'about'
# The main route, this should be last. It will match any params not
# previously matched.
client '/', {}

View File

@ -1,36 +0,0 @@
# By default Volt generates this controller for your Main component
require "salama"
if RUBY_PLATFORM == 'opal'
require "main/lib/main_view"
end
Virtual::Machine.boot
module Main
class MainController < Volt::ModelController
def index
MainView.new()
end
def about
end
private
# The main template contains a #template binding that shows another
# template. This is the path to that template. It may change based
# on the params._component, params._controller, and params._action values.
def main_path
"#{params._component || 'main'}/#{params._controller || 'main'}/#{params._action || 'index'}"
end
# Determine if the current nav component is the active one by looking
# at the first part of the url against the href attribute.
def active_tab?
url.path.split('/')[1] == attrs.href.split('/')[1]
end
end
end

View File

@ -1,46 +0,0 @@
require 'opal/pixi'
require 'native'
require "salama"
require_relative "registers_view"
require_relative "object_view"
require_relative "space_view"
class MainView
def initialize
@container = PIXI::Container.new
height = `window.innerHeight`
width = `window.innerWidth`
renderer = PIXI::WebGLRenderer.new( width - 100 , height - 100, {"backgroundColor" => 0xFFFFFF})
body = Native(`window.document.body`)
# bit of a hack as it assumes index's structure
html_con = body.firstElementChild
html_con.insertBefore renderer.view , html_con.lastElementChild
registers = RegisterView.new(height - 150)
@container.add_child registers
ParseTask.parse(1).then do |result|
is = Ast::Expression.from_basic(result)
Virtual::Compiler.compile( is , Virtual.machine.space.get_main )
Virtual.machine.run_before Virtual::Machine::FIRST_PASS
end.fail do |error|
raise "Error: #{error}"
end
space = SpaceView.new
@container.add_child space
animate = Proc.new do
`requestAnimationFrame(animate)`
registers.draw_me
space.draw_me
renderer.render @container
end
animate.call
end
attr_reader :container
end

View File

@ -1,38 +0,0 @@
class ObjectView
attr_accessor :text , :object , :attributes
def initialize o
super()
self.text = PIXI::Text.new("no")
self.text.position = PIXI::Point.new( rand(1000) , rand(550))
puts "NO O " unless o
self.object = o
self.text.text = short
@attributes = {}
end
def short
object.class.name.split("::").last[0 .. 3]
end
def is_parfait
object.class.name.split("::").first == "Parfait"
end
def set name , val
@attributes[name] = val
self.text.text = short + @attributes.length.to_s
end
def get(name)
@attributes[name]
end
def position
#raise "NONAME" unless self.text
self.text.position
end
def distance to
self.position - to.position
end
end

View File

@ -1,21 +0,0 @@
class RegisterView < PIXI::Container
@@register_names = (0..8).collect {|i| "r#{i}"}
def initialize at_y
super()
@registers = {}
x = 0
@@register_names.each do |name|
reg = PIXI::Text.new( name )
reg.position = PIXI::Point.new x , at_y
x += reg.width + 20
@registers[name] = reg
self.add_child reg
end
def draw_me
end
end
end

View File

@ -1,159 +0,0 @@
require "math"
PIXI::Point.class_eval do
alias_native :y=
def add point
self.x += point.x
self.x = 0 if self.x < 0
self.x = 1100 if self.x > 1100
self.y += point.y
self.y = 0 if self.y < 0
self.y = 550 if self.y > 550
end
def scale_by num
min = 0.001
num = min if num <= min
self.x = self.x / num
self.y = self.y / num
end
end
class SpaceView < PIXI::Graphics
include Sof::Util
def initialize
super()
space = Virtual.machine.space
# just a way to get the space into a list. objects is an id => occurence mapping.
# occurence.object is the object
objects = Sof::Members.new(space).objects
@objects = objects
puts "Objects #{objects.length}"
# create a mapping from id to volt models
@view_objects = {}
@objects.each do |i , o|
next unless o.object
view = ObjectView.new o.object
@view_objects[i] = view
add_child view.text
end
fill_attributes
end
# should almost be called draw by now
def draw_me
update_positions
self.clear
@view_objects.each do |i , view|
self.lineStyle(4, 0xffd900, 2)
puts "v" if view.nil?
view.attributes.each do |n , v |
next if n == :id
next unless v.is_a? ObjectView
next unless v.is_parfait
puts "v2" if view.nil?
puts "0" if v.nil?
self.moveTo( view.position.x , view.position.y )
self.lineTo( v.position.x , v.position.y )
end
end
end
def force from , to
dir_x = from.x - to.x - 100
dir_x2 = dir_x * dir_x
dir_y = from.y - to.y - 100
dir_y2 = dir_y * dir_y
if( dir_x2 < 0.1 and dir_y2 < 0.1 )
puts "Were close"
dir_x = rand(10) - 5
dir_y = rand(10) - 5
end
f = dir_x * dir_x + dir_y * dir_y
f = 0.01 if f < 0.01
f = f / 100
#puts "force #{f}"
PIXI::Point.new( dir_x / f , dir_y / f)
end
def update_positions
@view_objects.each do |i , view|
view.attributes.each do |n , v |
next if n == :id
next unless v.is_a? ObjectView
next unless v.is_parfait
puts "v2" if view.nil?
puts "0" if v.nil?
view.position.add force( view.position , v.position )
end
offset = 0.0
view.position.add force( view.position , PIXI::Point.new(view.position.x , -offset) )
view.position.add force( view.position , PIXI::Point.new(-offset , view.position.y) )
view.position.add force( view.position , PIXI::Point.new(view.position.x , 550 + offset) )
view.position.add force( view.position , PIXI::Point.new(1000 + offset , view.position.y) )
end
end
def fill_attributes
@view_objects.each do |i , view|
ob = view.object
next if is_value?(ob)
case ob.class.name
when "Array" , "Parfait::List"
fill_array view
when "Hash" , "Parfait::Dictionary"
fill_hash view
else
next if ob.class.name.include?("::") and !ob.class.name.include?("Parfait")
next if ob.class.name == "Proc"
next if ob.class.name == "String"
next if ob.class.name == "Numeric"
next if ob.class.name == "Class"
#puts "object #{ob.class.name}"
attributes = attributes_for(ob)
attributes.each do |a|
next if a == "html_safe"
next if a == "constructor"
next if a == "toString"
next if a == "position"
val = get_value( ob , a)
if( @view_objects[val.object_id])
val = @view_objects[val.object_id]
end
#puts "set #{a}"
view.set(a , val )
end
superclasses = [ob.class.superclass.name]
if superclasses.include?( "Array") or superclasses.include?( "Parfait::List")
fill_array view
end
if superclasses.include?( "Hash") or superclasses.include?( "Parfait::Dictionary")
fill_hash view
end
end
end
end
# and hash keys/values
def fill_hash hash
return
hash.each do |a,b|
next_level << a
next_level << b
end
end
# and array values
def fill_array array
return
array.each do |a|
next_level << a
end
end
end

View File

@ -1,12 +0,0 @@
# By default Volt generates this User model which inherits from Volt::User,
# you can rename this if you want.
class User < Volt::User
# login_field is set to :email by default and can be changed to :username
# in config/app.rb
field login_field
field :name
validate login_field, unique: true, length: 8
validate :email, email: true
end

View File

@ -1,11 +0,0 @@
require "salama-reader"
class ParseTask < Volt::Task
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

@ -1,7 +0,0 @@
<:Title>
About
<:Body>
<h1>About</h1>
<p>About page...</p>

View File

@ -1,4 +0,0 @@
<:Title>
Home
<:Body>

View File

@ -1,27 +0,0 @@
<:Title>
{{ view main_path, "title", {controller_group: 'main'} }}
<:Body>
<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<:nav href="/">Home</:nav>
<:nav href="/about">About</:nav>
</ul>
<h3 class="text-muted">Salama Debugger</h3>
</div>
<:volt:notices />
{{ view main_path, 'body', {controller_group: 'main'} }}
<div class="footer">
<p> <a href="http://salama-vm.org/" >Salama</a> {{ Time.now.year }}</p>
</div>
</div>
<:Nav>
<li class="{{ if active_tab? }}active{{ end }}">
<a href="{{ attrs.href }}">{{ yield }}</a>
</li>

64
app/register_view.rb Normal file
View File

@ -0,0 +1,64 @@
class RegisterView
include React::Component
required_param :interpreter
required_param :register
define_state :objects_id
define_state :fields => []
before_mount do
interpreter.register_event(:register_changed, self)
interpreter.register_event(:object_changed, self)
register_changed( register , nil , interpreter.registers[register])
end
def register_changed reg , old , value
reg = reg.symbol unless reg.is_a? Symbol
return unless reg == register
objects_id! value
calc_fields
end
def object_changed reg
reg = reg.symbol unless reg.is_a? Symbol
return unless reg == register
puts "Object changed in #{reg}"
calc_fields
end
def calc_fields
#puts "My id #{objects_id} , #{objects_id.class}"
object = Virtual.machine.objects[objects_id]
if object and ! object.is_a?(String)
has_fields = []
clazz = object.class.name.split("::").last
#puts "found #{clazz}"
has_fields << clazz
object.get_instance_variables.each do |variable|
f = object.get_instance_variable(variable)
has_fields << f
end
fields! has_fields
end
end
def render
div.register_view do
div do
objects_id.to_s
end
fields.each do |attribute|
div.col_md_12 do
"#{marker(attribute)} - #{attribute.object_id}".span
end
end
end
end
def marker var
return "W" if var.is_a? String
var.class.name.split("::").last[0]
end
end

38
app/source_view.rb Normal file
View File

@ -0,0 +1,38 @@
class SourceView
include React::Component
required_param :interpreter
define_state :sources => []
before_mount do
interpreter.register_event(:instruction_changed, self)
instruction_changed nil , interpreter.instruction
end
def instruction_changed old , ins
text = ins ? source_text(ins.source) : "exit"
return if sources.last == text
sources << text
sources.shift if sources.length > 5
sources! sources
end
def render
div.source_view do
h4 {"Virtual Machine Instruction"}
sources.each do |s|
s.br
end
end
end
def source_text source
if source.is_a? Virtual::Instruction
return source.class.name
else
return "Method: #{source.name}"
end
end
end

41
app/status_view.rb Normal file
View File

@ -0,0 +1,41 @@
class StatusView
include React::Component
required_param :interpreter
define_state :state => "starting"
define_state :stdout
before_mount do
interpreter.register_event(:instruction_changed, self)
end
def update_state
state! interpreter.state
stdout! interpreter.stdout
end
def instruction_changed old , nex
update_state
end
def render
div.status_view do
div do
button.bright { "next" }.on(:click) { interpreter.tick }
" ".br
end
div do
h4 {"Status:"}
state.to_s.br
end
div do
h4 {"Stdout:"}
end
div do
interpreter.stdout.br
end
end
end
end

40
app/styles.scss Normal file
View File

@ -0,0 +1,40 @@
@import "susy";
$susy: (
columns: 24 ,
gutter-position: split ,
);
.debugger-view { @include container(90%); }
.classes { @include span(3); }
.file-view {
@include span(4);
margin: span(1);
}
.source-view { @include span(6); }
.block-view {
@include span(4);
margin-right: span(3);
height: 200px;
}
.status-view {
@include span(2 at 22);
}
.registers-view {
@include span(20 at 3);
}
.register-view {
@include gallery(3);
margin-top: 10px;
}
.bright {
background-color: orange ;
}

View File

@ -1,4 +1,42 @@
# Run via rack server # config.ru
require 'bundler/setup' require 'bundler'
require 'volt/server' Bundler.require
run Volt::Server.new.app
Opal.use_gem "salama"
Opal.use_gem "salama-arm"
require "tilt/erb"
require "susy"
require "json"
require "react/source"
class DebugServer < Opal::Server
def parse(num)
string_input = '"Hello again".putstring()'
parser = Parser::Salama.new
out = parser.parse(string_input)
parts = Parser::Transform.new.apply(out)
parts.to_basic
end
def call(env)
if env['PATH_INFO'].include? "/parse.json"
parse_out = parse(1).to_s
[200, { 'Content-Type' => 'text/json' }, [parse_out]]
else
super
end
end
end
run DebugServer.new {|s|
s.append_path 'app'
s.append_path 'lib'
s.append_path File.dirname(::React::Source.bundled_path_for("react-with-addons.js"))
s.main = 'main'
s.debug = false
s.source_map = true
s.index_path = "index.html.erb"
}

View File

@ -1,132 +0,0 @@
# app.rb is used to configure your app. This code is only run on the server,
# then any config options in config.public are passed to the client as well.
Volt.configure do |config|
# Setup your global app config here.
#######################################
# Basic App Info (stuff you should set)
#######################################
config.domain = 'salama-vm.org'
config.app_name = 'Salama-debugger'
config.mailer.from = 'Salama-debugger <no-reply@salama-debugger.com>'
############
# App Secret
############
# Your app secret is used for signing things like the user cookie so it can't
# be tampered with. A random value is generated on new projects that will work
# without the need to customize. Make sure this value doesn't leave your server.
#
# For added security we recommend moving the app secret into an environment. You can
# setup that like so:
#
# config.app_secret = ENV['APP_SECRET']
#
config.app_secret = 'pKDKhKInTaI5EVH9WmyN-cJSYnJt8PmDogwW63Zqr_ieUNqYvh1KybeWJoslylzFgsU'
###############
# Log Filtering
###############
# Data updates from the client come in via Tasks. The task dispatcher logs all calls to tasks.
# By default hashes in the arguments can be filtered based on keys. So any hash with a key of
# password will be filtered. You can add more fields to filter below:
config.filter_keys = [:password]
##########
# Database
##########
# Database config all start with db_ and can be set either in the config
# file or with an environment variable (DB_NAME for example).
# config.db_driver = 'mongo'
# config.db_name = (config.app_name + '_' + Volt.env.to_s)
# config.db_host = 'localhost'
# config.db_port = 27017
#####################
# Compression options
#####################
# If you are not running behind something like nginx in production, you can
# have rack deflate all files.
# config.deflate = true
#######################
# Public configurations
#######################
# Anything under config.public will be sent to the client as well as the server,
# so be sure no private data ends up under public
# Use username instead of email as the login
# config.public.auth.use_username = true
#####################
# Compression Options
#####################
# Disable or enable css/js compression. Default is to only run in production.
# if Volt.env.production?
# config.compress_javascript = true
# config.compress_css = true
# end
################
# Mailer options
################
# The volt-mailer gem uses pony (https://github.com/benprew/pony) to deliver e-mail. Any
# options you would pass to pony can be setup below.
# NOTE: The from address is setup at the top
# Normally pony uses /usr/sbin/sendmail if one is installed. You can specify smtp below:
# config.mailer.via = :smtp
# config.mailer.via_options = {
# :address => 'smtp.yourserver.com',
# :port => '25',
# :user_name => 'user',
# :password => 'password',
# :authentication => :plain, # :plain, :login, :cram_md5, no auth by default
# :domain => "localhost.localdomain" # the HELO domain provided by the client to the server
# }
#############
# Message Bus
#############
# Volt provides a "Message Bus" out of the box. The message bus provides
# a pub/sub service between any volt instance (server, client, runner, etc..)
# that share the same database. The message bus can be used by app code. It
# is also used internally to push data to any listening clients.
#
# The default message bus (called "peer_to_peer") uses the database to sync
# socket ip's/ports.
# config.message_bus.bus_name = 'peer_to_peer'
#
# Encrypt message bus - messages on the message bus are encrypted by default
# but this is meant to be used locally by a developer
config.message_bus.disable_encryption = true
#
# ## MessageBus Server -- the message bus binds to a port and ip which the
# other volt instances need to be able to connect to. You can customize
# the server below:
#
# Port range - you can specify a range of ports that an instance can bind the
# message bus on. You can specify a range, an array of Integers, or an array
# of ranges.
# config.message_bus.bind_port_ranges = (58000..61000)
#
# Bind Ip - specifies the ip address the message bus server should bind on.
# config.message_bus.bind_ip = '127.0.0.1'
#############
# Concurrency
#############
# Volt provides a thread worker pool for incoming task requests (and all
# database requests, since those use tasks to do their work.) The following
# lets you control the size of the worker pool. Threads are only created as
# needed, and are removed after a certain amount of inactivity.
# config.min_worker_threads = 1
# config.max_worker_threads = 10
#
# You can also specify the amount of time a Task should run for before it
# timeout's. Setting this to short can cause unexpected results, currently
# we recomend it be at least 10 seconds.
# config.worker_timeout = 60
end

View File

@ -1,20 +0,0 @@
<!DOCTYPE html>
<html>
<%# IMPORTANT: Please read before changing! %>
<%# This file is rendered on the server using ERB, so it does NOT use Volt's %>
<%# normal template system. You can add to it, but keep in mind the template %>
<%# language difference. This file handles auto-loading all JS/Opal and CSS. %>
<head>
<meta charset="UTF-8" />
<% javascript_files.each do |javascript_file| %>
<script src="<%= javascript_file %>"></script>
<% end %>
<% css_files.each do |css_file| %>
<link href="<%= css_file %>" media="all" rel="stylesheet" type="text/css" />
<% end %>
</head>
<body>
</body>
</html>

View File

@ -1,3 +0,0 @@
#require "salama"
Virtual::Machine.boot

12
index.html.erb Normal file
View File

@ -0,0 +1,12 @@
<html>
<head>
<title>Salama Debugger</title>
<link rel="stylesheet" href="/assets/styles.css">
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="/assets/react-with-addons.js"></script>
<%= javascript_include_tag 'main' %>
</head>
<body>
<div id="content"></div>
</body>
</html>

37
lib/eventable.rb Normal file
View File

@ -0,0 +1,37 @@
# A simple event registering/triggering module to mix into classes.
# Events are stored in the `@events` ivar.
module Eventable
# Register a handler for the given event name.
# The event name is the method name called on the handler object
#
# obj.on(:foo , some_object_that_implements foo( whateverargs)
#
# @param [String, Symbol] name event name
# @param [Object] object handling the event, ie implement the function name
# @return handler
def register_event(name, handler)
event_table[name] << handler
handler
end
def unregister_event(name, handler)
event_table[name].delete handler
end
def event_table
return @event_table if @event_table
@event_table = Hash.new { |hash, key| hash[key] = [] }
end
# Trigger the given event name and passes all args to each handler
# for this event.
#
# obj.trigger(:foo)
# obj.trigger(:foo, 1, 2, 3)
#
# @param [String, Symbol] name event name to trigger
def trigger(name, *args)
event_table[name].each { |handler| handler.send( name.to_sym , *args) }
end
end

166
lib/interpreter.rb Normal file
View File

@ -0,0 +1,166 @@
require "eventable"
class Interpreter
# fire events for changed pc and register contents
include Eventable
# current instruction or pc
attr_reader :instruction
# an (arm style) link register. store the return address to return to
attr_reader :link
# current executing block. since this is not a hardware simulator this is luxury
attr_reader :block
# the registers, 12
attr_reader :registers
# collect the output
attr_reader :stdout
attr_reader :state
def initialize
@state = "runnnig"
@stdout = ""
@registers = {}
(0...16).each do |r|
set_register "r#{r}".to_sym , "r#{r}:unknown"
end
end
def start bl
set_block bl
end
def set_block bl
return if @block == bl
raise "Error, nil block" unless bl
old = @block
@block = bl
trigger(:block_changed , old , bl)
set_instruction bl.codes.first
end
def set_instruction i
@state = "exited" unless i
return if @instruction == i
old = @instruction
@instruction = i
trigger(:instruction_changed, old , i)
end
def get_register( reg )
reg = reg.symbol if reg.is_a? Register::RegisterReference
raise "Not a register #{reg}" unless Register::RegisterReference.look_like_reg(reg)
@registers[reg]
end
def set_register reg , val
old = get_register( reg ) # also ensures format
return if old === val
reg = reg.symbol if reg.is_a? Register::RegisterReference
@registers[reg] = val
trigger(:register_changed, reg , old , val)
end
def tick
return unless @instruction
name = @instruction.class.name.split("::").last
fetch = send "execute_#{name}"
return unless fetch
fetch_next_intruction
end
def fetch_next_intruction
if(@instruction != @block.codes.last)
set_instruction @block.codes[ @block.codes.index(@instruction) + 1]
else
next_b = @block.method.source.blocks.index(@block) + 1
set_block @block.method.source.blocks[next_b]
end
end
def object_for reg
id = get_register(reg)
Virtual.machine.objects[id]
end
# Instruction interpretation starts here
def execute_Branch
target = @instruction.block
set_block target
false
end
def execute_LoadConstant
to = @instruction.register
value = @instruction.constant.object_id
set_register( to , value )
true
end
def execute_GetSlot
object = object_for( @instruction.array )
value = object.internal_object_get( @instruction.index )
value = value.object_id unless value.is_a? Integer
set_register( @instruction.register , value )
true
end
def execute_SetSlot
value = object_for( @instruction.register )
object = object_for( @instruction.array )
object.internal_object_set( @instruction.index , value )
trigger(:object_changed, @instruction.register )
true
end
def execute_RegisterTransfer
value = get_register @instruction.from
set_register @instruction.to , value
true
end
def execute_FunctionCall
@link = [@block , @instruction]
next_block = @instruction.method.source.blocks.first
set_block next_block
false
end
def execute_SaveReturn
object = object_for @instruction.register
raise "save return has nothing to save" unless @link
trigger(:object_changed, @instruction.register )
object.internal_object_set @instruction.index , @link
true
end
def execute_Syscall
name = @instruction.name
case name
when :putstring
str = object_for( :r1 ) # should test length, ie r2
raise "NO string for putstring #{str}" unless str.is_a? Symbol
@stdout += str.to_s
when :exit
set_instruction(nil)
return false
else
raise "un-implemented syscall #{name}"
end
true
end
def execute_FunctionReturn
object = object_for( @instruction.register )
#wouldn't need to assign to link, but makes tsting easier
@link = object.internal_object_get( @instruction.index )
@block , @instruction = @link
# we jump back to the call instruction. so it is as if the call never happened and we continue
true
end
end

View File

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

View File

@ -1,11 +0,0 @@
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

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

View File

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

View File

@ -1,14 +0,0 @@
# 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

BIN
static/debugger.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

307
static/hint.css Normal file
View File

@ -0,0 +1,307 @@
/*! Hint.css - v1.3.5 - 2015-06-16
* http://kushagragour.in/lab/hint/
* Copyright (c) 2015 Kushagra Gour; Licensed MIT */
/*-------------------------------------*\
HINT.css - A CSS tooltip library
\*-------------------------------------*/
/**
* HINT.css is a tooltip library made in pure CSS.
*
* Source: https://github.com/chinchang/hint.css
* Demo: http://kushagragour.in/lab/hint/
*
* Release under The MIT License
*
*/
/**
* source: hint-core.scss
*
* Defines the basic styling for the tooltip.
* Each tooltip is made of 2 parts:
* 1) body (:after)
* 2) arrow (:before)
*
* Classes added:
* 1) hint
*/
.hint, [data-hint] {
position: relative;
display: inline-block;
/**
* tooltip arrow
*/
/**
* tooltip body
*/ }
.hint:before, .hint:after, [data-hint]:before, [data-hint]:after {
position: absolute;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
visibility: hidden;
opacity: 0;
z-index: 1000000;
pointer-events: none;
-webkit-transition: 0.3s ease;
-moz-transition: 0.3s ease;
transition: 0.3s ease;
-webkit-transition-delay: 0ms;
-moz-transition-delay: 0ms;
transition-delay: 0ms; }
.hint:hover:before, .hint:hover:after, .hint:focus:before, .hint:focus:after, [data-hint]:hover:before, [data-hint]:hover:after, [data-hint]:focus:before, [data-hint]:focus:after {
visibility: visible;
opacity: 1; }
.hint:hover:before, .hint:hover:after, [data-hint]:hover:before, [data-hint]:hover:after {
-webkit-transition-delay: 100ms;
-moz-transition-delay: 100ms;
transition-delay: 100ms; }
.hint:before, [data-hint]:before {
content: '';
position: absolute;
background: transparent;
border: 6px solid transparent;
z-index: 1000001; }
.hint:after, [data-hint]:after {
content: attr(data-hint);
background: #383838;
color: white;
padding: 8px 10px;
font-size: 12px;
line-height: 12px;
white-space: nowrap; }
/**
* source: hint-position.scss
*
* Defines the positoning logic for the tooltips.
*
* Classes added:
* 1) hint--top
* 2) hint--bottom
* 3) hint--left
* 4) hint--right
*/
/**
* set default color for tooltip arrows
*/
.hint--top:before {
border-top-color: #383838; }
.hint--bottom:before {
border-bottom-color: #383838; }
.hint--left:before {
border-left-color: #383838; }
.hint--right:before {
border-right-color: #383838; }
/**
* top tooltip
*/
.hint--top:before {
margin-bottom: -12px; }
.hint--top:after {
margin-left: -18px; }
.hint--top:before, .hint--top:after {
bottom: 100%;
left: 50%; }
.hint--top:hover:after, .hint--top:hover:before, .hint--top:focus:after, .hint--top:focus:before {
-webkit-transform: translateY(-8px);
-moz-transform: translateY(-8px);
transform: translateY(-8px); }
/**
* bottom tooltip
*/
.hint--bottom:before {
margin-top: -12px; }
.hint--bottom:after {
margin-left: -18px; }
.hint--bottom:before, .hint--bottom:after {
top: 100%;
left: 50%; }
.hint--bottom:hover:after, .hint--bottom:hover:before, .hint--bottom:focus:after, .hint--bottom:focus:before {
-webkit-transform: translateY(8px);
-moz-transform: translateY(8px);
transform: translateY(8px); }
/**
* right tooltip
*/
.hint--right:before {
margin-left: -12px;
margin-bottom: -6px; }
.hint--right:after {
margin-bottom: -14px; }
.hint--right:before, .hint--right:after {
left: 100%;
bottom: 50%; }
.hint--right:hover:after, .hint--right:hover:before, .hint--right:focus:after, .hint--right:focus:before {
-webkit-transform: translateX(8px);
-moz-transform: translateX(8px);
transform: translateX(8px); }
/**
* left tooltip
*/
.hint--left:before {
margin-right: -12px;
margin-bottom: -6px; }
.hint--left:after {
margin-bottom: -14px; }
.hint--left:before, .hint--left:after {
right: 100%;
bottom: 50%; }
.hint--left:hover:after, .hint--left:hover:before, .hint--left:focus:after, .hint--left:focus:before {
-webkit-transform: translateX(-8px);
-moz-transform: translateX(-8px);
transform: translateX(-8px); }
/**
* source: hint-theme.scss
*
* Defines basic theme for tooltips.
*
*/
.hint, [data-hint] {
/**
* tooltip body
*/ }
.hint:after, [data-hint]:after {
text-shadow: 0 -1px 0px black;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3); }
/**
* source: hint-color-types.scss
*
* Contains tooltips of various types based on color differences.
*
* Classes added:
* 1) hint--error
* 2) hint--warning
* 3) hint--info
* 4) hint--success
*
*/
/**
* Error
*/
.hint--error:after {
background-color: #b34e4d;
text-shadow: 0 -1px 0px #592726; }
.hint--error.hint--top:before {
border-top-color: #b34e4d; }
.hint--error.hint--bottom:before {
border-bottom-color: #b34e4d; }
.hint--error.hint--left:before {
border-left-color: #b34e4d; }
.hint--error.hint--right:before {
border-right-color: #b34e4d; }
/**
* Warning
*/
.hint--warning:after {
background-color: #c09854;
text-shadow: 0 -1px 0px #6c5328; }
.hint--warning.hint--top:before {
border-top-color: #c09854; }
.hint--warning.hint--bottom:before {
border-bottom-color: #c09854; }
.hint--warning.hint--left:before {
border-left-color: #c09854; }
.hint--warning.hint--right:before {
border-right-color: #c09854; }
/**
* Info
*/
.hint--info:after {
background-color: #3986ac;
text-shadow: 0 -1px 0px #193b4d; }
.hint--info.hint--top:before {
border-top-color: #3986ac; }
.hint--info.hint--bottom:before {
border-bottom-color: #3986ac; }
.hint--info.hint--left:before {
border-left-color: #3986ac; }
.hint--info.hint--right:before {
border-right-color: #3986ac; }
/**
* Success
*/
.hint--success:after {
background-color: #458746;
text-shadow: 0 -1px 0px #1a321a; }
.hint--success.hint--top:before {
border-top-color: #458746; }
.hint--success.hint--bottom:before {
border-bottom-color: #458746; }
.hint--success.hint--left:before {
border-left-color: #458746; }
.hint--success.hint--right:before {
border-right-color: #458746; }
/**
* source: hint-always.scss
*
* Defines a persisted tooltip which shows always.
*
* Classes added:
* 1) hint--always
*
*/
.hint--always:after, .hint--always:before {
opacity: 1;
visibility: visible; }
.hint--always.hint--top:after, .hint--always.hint--top:before {
-webkit-transform: translateY(-8px);
-moz-transform: translateY(-8px);
transform: translateY(-8px); }
.hint--always.hint--bottom:after, .hint--always.hint--bottom:before {
-webkit-transform: translateY(8px);
-moz-transform: translateY(8px);
transform: translateY(8px); }
.hint--always.hint--left:after, .hint--always.hint--left:before {
-webkit-transform: translateX(-8px);
-moz-transform: translateX(-8px);
transform: translateX(-8px); }
.hint--always.hint--right:after, .hint--always.hint--right:before {
-webkit-transform: translateX(8px);
-moz-transform: translateX(8px);
transform: translateX(8px); }
/**
* source: hint-rounded.scss
*
* Defines rounded corner tooltips.
*
* Classes added:
* 1) hint--rounded
*
*/
.hint--rounded:after {
border-radius: 4px; }
/**
* source: hint-effects.scss
*
* Defines various transition effects for the tooltips.
*
* Classes added:
* 1) hint--no-animate
* 2) hint--bounce
*
*/
.hint--no-animate:before, .hint--no-animate:after {
-webkit-transition-duration: 0ms;
-moz-transition-duration: 0ms;
transition-duration: 0ms; }
.hint--bounce:before, .hint--bounce:after {
-webkit-transition: opacity 0.3s ease, visibility 0.3s ease, -webkit-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);
-moz-transition: opacity 0.3s ease, visibility 0.3s ease, -moz-transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24);
transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s cubic-bezier(0.71, 1.7, 0.77, 1.24); }

21
test/helper.rb Normal file
View File

@ -0,0 +1,21 @@
require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
if ENV['CODECLIMATE_REPO_TOKEN']
require "codeclimate-test-reporter"
CodeClimate::TestReporter.start
end
require "minitest/autorun"
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'test'))
require "salama"
require "interpreter"

82
test/interpreter_test.rb Normal file
View File

@ -0,0 +1,82 @@
require_relative "helper"
class InterpreterTest < MiniTest::Test
def setup
Virtual.machine.boot
code = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:putstring, [] ,Ast::StringExpression.new("Hello again"))])
Virtual::Compiler.compile( code , Virtual.machine.space.get_main )
Virtual.machine.run_before "Register::CallImplementation"
@interpreter = Interpreter.new
@interpreter.start Virtual.machine.init
end
def ticks num
last = nil
num.times do
last = @interpreter.instruction
@interpreter.tick
end
return last
end
def test_branch
was = @interpreter.block
assert_equal Register::Branch , ticks(1).class
assert was != @interpreter.block
end
def test_load
assert_equal Register::LoadConstant , ticks(2).class
assert_equal Parfait::Space , Virtual.machine.objects[ @interpreter.get_register(:r1)].class
assert_equal :r1, @interpreter.instruction.array.symbol
end
def test_get
assert_equal Register::GetSlot , ticks(3).class
assert @interpreter.get_register( :r3 )
assert @interpreter.get_register( :r3 ).is_a? Integer
end
def test_transfer
transfer = ticks 5
assert_equal Register::RegisterTransfer , transfer.class
assert_equal @interpreter.get_register(transfer.to) , @interpreter.get_register(transfer.from)
end
def test_call
assert_equal Register::FunctionCall , ticks(7).class
assert @interpreter.link
end
def test_save
done = ticks(8)
assert_equal Register::SaveReturn , done.class
assert @interpreter.get_register done.register.symbol
end
def test_chain
["Branch" , "LoadConstant" , "GetSlot" , "SetSlot" , "RegisterTransfer" ,
"GetSlot" , "FunctionCall" , "SaveReturn" , "LoadConstant" , "SetSlot" ,
"GetSlot" , "GetSlot" , "SetSlot" , "LoadConstant" , "SetSlot" ,
"RegisterTransfer" , "GetSlot" , "FunctionCall" , "SaveReturn" , "RegisterTransfer" ,
"Syscall" , "RegisterTransfer" , "RegisterTransfer" , "SetSlot" , "GetSlot" ,
"GetSlot" , "RegisterTransfer" ,"GetSlot" , "GetSlot","GetSlot",
"FunctionReturn" , "RegisterTransfer" , "Syscall" , "NilClass"].each_with_index do |name , index|
got = ticks(1)
assert got.class.name.index(name) , "Wrong class for #{index+1}, expect #{name} , got #{got}"
end
end
def test_putstring
done = ticks(21)
assert_equal Register::Syscall , done.class
assert_equal "Hello again" , @interpreter.stdout
end
def test_return
done = ticks(31)
assert_equal Register::FunctionReturn , done.class
assert @interpreter.block.is_a?(Virtual::Block)
assert @interpreter.instruction.is_a?(Register::Instruction) , "not instruction #{@interpreter.instruction}"
end
def test_exit
done = ticks(34)
assert_equal NilClass , done.class
end
end