Permalink
Browse files

Full docs

  • Loading branch information...
1 parent 2466c7d commit a76bdcccb0100981dc858164ff6929d96eba2193 @txus committed Jun 17, 2011
Showing with 120 additions and 67 deletions.
  1. +2 −0 .gitignore
  2. +7 −3 Readme.md
  3. +5 −10 lib/rexpl.rb
  4. +7 −2 lib/rexpl/environment.rb
  5. +7 −9 lib/rexpl/generator_methods.rb
  6. +58 −41 lib/rexpl/generator_proxy.rb
  7. +33 −2 lib/rexpl/output.rb
  8. +1 −0 lib/rexpl/version.rb
View
@@ -3,3 +3,5 @@
Gemfile.lock
pkg/*
*.rbc
+doc/*
+.yardoc/*
View
@@ -5,17 +5,21 @@ Virtual Machine and its bytecode instructions. It comes wrapped in a REPL
(Read-Eval-Print Loop) à la IRB, so that anytime you can open a terminal,
fire up **rexpl**, and start playing with instant feedback.
+This intends to be a fun tool to use when learning how to use Rubinius
+bytecode instructions, for example when bootstraping a new language targeting
+the Rubinius VM for the first time.
+
Its main feature is **stack introspection**, which means you can inspect
what the stack looks like after each step of your instruction set.
## How to use it?
Needless to say, **rexpl** runs only on Rubinius. Thus, your first step is to
-install it. Go to the [rubinius website](http://rubini.us) to find how, or if
+install it. Go to the [Rubinius website](http://rubini.us) to find how, or if
you are using RVM, just follow [this instructions](http://beginrescueend.com/interpreters/rbx/).
- $ gem install rexpl
- $ rexpl
+ $ gem install rexpl
+ $ rexpl
Now you should see a welcome banner and an IRB-like prompt, and you're good to
go! Just start typing some [VM instructions](http://rubini.us/doc/en/virtual-machine/instructions/)
View
@@ -1,14 +1,9 @@
-# Rexpl is an interactive bytecode console for the Rubinius VM.
+# rexpl is an interactive bytecode console for the Rubinius VM.
#
-# It intends to be a fun tool to use when learning how to use Rubinius
-# bytecodes, for example when bootstraping a new language targeting the
-# Rubinius VM.
-#
-# Implemented as a REPL-like executable (think of it as an IRB for Rubinius
-# bytecode), Rexpl accepts any command that can be issued to an instance of
-# Rubinius::Generator, plus some custom commands to list the current program
-# instructions or print out the state of the stack after each instruction, for
-# example.
+# **rexpl** is a sandbox to experiment and play with the [Rubinius](http:://rubini.us)
+# Virtual Machine and its bytecode instructions. It comes wrapped in a REPL
+# (Read-Eval-Print Loop) à la IRB, so that anytime you can open a terminal,
+# fire up **rexpl**, and start playing with instant feedback.
#
module Rexpl
end
View
@@ -1,20 +1,25 @@
module Rexpl
+ # This class represents the Environment holding a {GeneratorProxy} instance
+ # exposing the main entry point as a class method.
+ #
class Environment
@@generator = GeneratorProxy.new
class << self
- # Rexpl's main entry point. Starts the REPL.
+ # Fires up the REPL. This is the program's main entry point.
def run
Output.print_banner
+
while (Output.print_prompt; input = gets)
@@generator.instance_eval input.chomp
+ # After each new instruction, evaluate all of them.
dynamic_method(:run) do |g|
@@generator.visit(g)
if g.size == 0
g.push_nil
else
- g.show_stack
+ g.print_debug_info
end
g.ret
end
@@ -5,20 +5,18 @@ module Rexpl
module GeneratorMethods
# Prints the topmost element on the stack and the stack's current size.
#
- # Calling #show_stack neither consumes nor produces stack, it's just used
+ # Calling this method neither consumes nor produces stack, it's just used
# for debugging the current state.
- #
- # @param [String] name an optional label to print out, to make debugging
- # easier.
- def show_stack(name = nil)
+ def print_debug_info
+ initial_size = size
dup
- push :self
+ push_const :Rexpl
+ find_const :Output
swap_stack
send :inspect, 0, true
- push_literal "[#{name || 'DEBUG'}] [STACK SIZE: #{size}]: "
+ push_literal initial_size
swap_stack
- push_literal "\n"
- send :print, 3, true
+ send :print_debug_info, 2, true
pop
end
@@ -1,47 +1,83 @@
module Rexpl
+ # This class acts as a Proxy between the user and an actual Generator.
+ #
class GeneratorProxy
protected_methods = %w(class to_s inspect instance_eval tainted?)
- instance_methods.each { |method| undef_method method unless method.to_s =~ /^_/ || protected_methods.include?(method.to_s)}
+ # Undefine every method so that this class acts as an actual proxy.
+ instance_methods.each do |method|
+ unless method.to_s =~ /^_/ || protected_methods.include?(method.to_s)
+ undef_method method
+ end
+ end
+
attr_accessor :instructions
+ # Initializes a new {GeneratorProxy} instance with an empty instruction
+ # set.
def initialize
@instructions = []
end
+ # Swallows every message passed onto the instance and stores it in the
+ # instruction set.
+ def method_missing(method, *args)
+ @instructions << [method, *args]
+ nil
+ end
+
+ # Applies the set of instructions to an actual generator.
+ #
+ # It captures errors in case an instruction is unknown or called with wrong
+ # arguments.
+ #
+ # @param [Rubinius::Generator] generator the generator onto which apply the
+ # instructions.
+ #
+ # @example
+ #
+ # proxy = Rexpl::GeneratorProxy.new
+ # proxy.push 83
+ # proxy.push_literal 'bar'
+ #
+ # dynamic_method(:foo) do |g|
+ # proxy.visit(g)
+ # g.ret
+ # end
+ #
+ def visit(generator)
+ last_instruction = nil
+ begin
+ @instructions.each do |instruction|
+ last_instruction = instruction
+ generator.__send__ *instruction
+ end
+ rescue NameError, ArgumentError=>e
+ puts "[ERROR]: #{e}"
+ @instructions.delete(last_instruction)
+ end
+ end
+
+ # Empties the instruction set.
def reset
- puts 'Reseted!'
+ Output.print_reset
initialize
end
+ # Prints a list of the current instruction set.
def list
puts
@instructions.each_with_index do |instruction, idx|
- puts "[#{idx}]: #{instruction.first} #{instruction[1..-1].map(&:inspect)}"
+ Output.print_instruction instruction, idx
end
puts
end
+ # Visually represents the state of the stack after each instruction is ran.
def draw
-
instructions = @instructions.dup
klass = Class.new do
- def print_stack(arr)
- puts "\n\t#{'Current stack'.center(25, ' ')}"
- puts "\t-------------------------"
- arr.reverse.each do |element|
- puts "\t|#{element.inspect.center(23, ' ')}|"
- puts "\t-------------------------"
- end
- puts "\n"
- nil
- end
dynamic_method(:draw) do |g|
- g.push :self
- g.push_literal "\n"
- g.send :puts, 1, true
- g.pop
-
instructions.each_with_index do |instruction, idx|
# Execute the instruction
before = g.size
@@ -55,15 +91,15 @@ def print_stack(arr)
verb = 'consumed'
produced = produced.abs
end
-
g.push_self
g.push_literal "[#{idx}] [#{instruction.first} #{instruction[1..-1].map(&:inspect).join(', ')}] #{verb} #{produced} stack, size is now #{after}"
g.send :puts, 1, true
g.pop
- # Print the entire stack
+ # Visually print the entire stack
g.return_stack
- g.push_self
+ g.push_const :Rexpl
+ g.find_const :Output
g.swap_stack
g.send :print_stack, 1, true
g.pop
@@ -76,24 +112,5 @@ def print_stack(arr)
klass.new.draw
end
-
- def method_missing(m, *args)
- @instructions << [m, *args]
- nil
- end
-
- def visit(generator)
- last_instruction = nil
- begin
- @instructions.each do |instruction|
- last_instruction = instruction
- generator.__send__ *instruction
- end
- rescue NameError, ArgumentError=>e
- puts "[ERROR]: #{e}"
- @instructions.delete(last_instruction)
- end
- !!@instructions.any?
- end
end
end
View
@@ -17,19 +17,50 @@ def print_banner
print_rationale
end
+ # Prints the console prompt.
def print_prompt
print bold { '> ' }
end
- private
+ # Prints a message indicating that the instruction set has been resetted.
+ def print_reset
+ puts "Reseted!"
+ end
+
+ # Prints the current size of the stack and the topmost value.
+ def print_debug_info(size, value)
+ puts "=> [" + bold { "Stack size: #{size}"} + "] " + "[" + bold { "Topmost value: #{value}" } + "]"
+ end
+
+ # Prints an instruction in a single row.
+ def print_instruction(instruction, idx)
+ puts bold { "[" } + blue { idx.to_s } + bold { "]" } + ": " + green { instruction.first } + " " + bold { instruction[1..-1].map(&:inspect) }
+ end
+ # Prints a stack out of an array of stack cells.
+ #
+ # @param [Array] cells the cells of the stack containing its values.
+ def print_stack(cells)
+ puts
+ puts "\t#{bold { 'Current stack' }.center(33, ' ')}"
+ puts "\t" + blue { "-------------------------" }
+ cells.reverse.each do |element|
+ puts "\t#{blue {"|"} }#{bold { element.inspect.center(23, ' ') }}#{blue {"|"}}"
+ puts "\t" + blue { "-------------------------" }
+ end
+ puts
+ end
+
+ # Prints a little readme to get people started when firing up the
+ # interactive console.
def print_rationale
puts "To start playing, just start typing some Rubinius VM instructions."
puts "You can find a complete list with documentation here:"
puts
puts "\thttp://rubini.us/doc/en/virtual-machine/instructions/"
puts
- puts "To introspect the program you are writing, use the following commands:"
+ puts "To introspect the program you are writing, use the following commands,"
+ puts "or type " + bold { "exit" } + " to exit:"
puts
puts " " + bold { "list" } + " lists the instruction set of the current program."
puts " " + bold { "reset" } + " empties the instruction set and starts a new program."
View
@@ -1,3 +1,4 @@
module Rexpl
+ # The current rexpl version.
VERSION = "0.0.1"
end

0 comments on commit a76bdcc

Please sign in to comment.