Permalink
Browse files

More work in progress.

  • Loading branch information...
matthewd committed Nov 20, 2010
1 parent 14d9d4d commit 1e63e20396ee8dfbe4c149b02547d20876f318b2
Showing with 506 additions and 275 deletions.
  1. +18 −0 bin/rasp
  2. +3 −0 lib/rasp.rb
  3. +28 −1 lib/rasp/ast.rb
  4. +11 −2 lib/rasp/ast/block.rb
  5. +6 −0 lib/rasp/ast/call.rb
  6. +97 −7 lib/rasp/ast/flow.rb
  7. +17 −3 lib/rasp/ast/simple.rb
  8. +10 −0 lib/rasp/ast/variable.rb
  9. +20 −1 lib/rasp/compiler.rb
  10. +293 −0 lib/rasp/parser.citrus
  11. +2 −0 lib/rasp/parser.rb
  12. +0 −256 lib/rasp/scratch.citrus
  13. +1 −5 scratch.rb
View
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+$: << "/home/matthew/src/citrus/lib"
+
+$: << File.join(File.dirname(File.dirname(__FILE__)), "lib")
+
+require 'rasp'
+
+filename = 't/math.vbs'
+parse = Rasp::Parser.parse(File.read(filename), :consume => true)
+#puts parse.dump
+
+parse.value.graph
+#p parse.value
+
+$DEBUG = true
+p Rasp::Compiler.new.compile(parse.value, filename)
+
View
@@ -0,0 +1,3 @@
+require 'rasp/compiler'
+require 'rasp/parser'
+require 'rasp/ast'
View
@@ -1,8 +1,30 @@
-
module Rasp; module AST
FalseInt = 0
TrueInt = ~FalseInt
+def self.math_op(match, op=nil)
+ binary_op(AST::MathOp, match.lhs, match.list, op ? [op] : :op)
+end
+
+def self.binary_op(klass, lhs, ary, op_attr=nil, attr=:rhs)
+ lhs = lhs.value
+ ary = ary.matches
+ until ary.empty?
+ curr = ary.shift
+ rhs = curr.send(attr).value
+ if Symbol === op_attr
+ op = curr.send(op_attr).to_sym
+ lhs = klass.new(op, lhs, rhs)
+ elsif op_attr
+ op = op_attr.first
+ lhs = klass.new(op, lhs, rhs)
+ else
+ lhs = klass.new(lhs, rhs)
+ end
+ end
+ lhs
+end
+
class Node
def graph
Rubinius::AST::AsciiGrapher.new(self, Node).print
@@ -21,4 +43,9 @@ def visit(visitor)
end
end; end
+require 'rasp/ast/block'
+require 'rasp/ast/call'
+require 'rasp/ast/expression'
+require 'rasp/ast/flow'
+require 'rasp/ast/simple'
require 'rasp/ast/value'
View
@@ -1,12 +1,21 @@
module Rasp::AST
-class Scope < Node
- include Rubinus::Compiler::LocalVariables
+class File < Node
+ include Rasp::Compiler::LocalVariables
attr_accessor :statements
def initialize(statements)
@statements = statements
end
+ def global; self; end
+ def bytecode(g)
+ g.push_state self
+ statements.each do |s|
+ p s
+ s.bytecode(g)
+ end
+ g.pop_state
+ end
end
end
View
@@ -5,6 +5,12 @@ class Call < Node
def initialize(target, name, args)
@target, @name, @args = target, name, args
end
+ def bytecode(g)
+ if @target.nil? && g.constants.key?(@name)
+ raise "Can't call constant with args" if @args && !@args.empty?
+ g.push_literal g.constants[@name]
+ end
+ end
end
class NullCall < Call
View
@@ -48,13 +48,103 @@ def bytecode(g)
end
end
class ForLoop < Loop
+ # Contrary to my original expectation, not having used VBScript in
+ # ages, all three of (start, finish, step) are fixed at loop start.
+
attr_accessor :var, :start, :finish, :step
def initialize(var, start, finish, step, body)
super(:for, body)
@var, @start, @finish, @step = var, start, finish, step
end
def bytecode(g)
# TODO
+ if g.state.scope.variables.key? var
+ loop_var = g.state.scope.variable(var).reference
+ else
+ loop_var = g.state.scope.global.variable(var).reference
+ end
+ @finish.bytecode(g)
+ @step.bytecode(g) if @step
+ unless Rasp::AST::Literal === @step || @step.nil?
+ g.dup
+ g.meta_push_0
+ g.send_stack :<, 1
+ end
+
+ @start.bytecode(g)
+ loop_var.set_bytecode(g)
+ loop_body = g.new_label
+ loop_body.set!
+ @body.each do |s|
+ #s.bytecode(g)
+ end
+
+ if Rasp::AST::Literal === @step || @step.nil?
+ # Step is constant
+
+ if @step
+ g.dup_many 2 # @step, @finish, ...
+ loop_var.get_bytecode(g)
+ g.send_stack :+, 1
+ else
+ g.dup # @finish, ...
+ loop_var.get_bytecode(g)
+ g.send_vcall :inc
+ end
+ g.dup
+ loop_var.set_bytecode(g)
+
+ if @step.nil? || @step.value > 0
+ # Always working forwards
+ g.send_stack :>, 1
+ elsif @step.value < 0
+ # Always working backwards
+ g.send_stack :<, 1
+ else
+ # Constant step of zero?! Twit.
+ raise "For loop cannot have a zero step"
+ end
+
+ g.gif loop_body
+
+ if @step
+ g.pop_many 2
+ else
+ g.pop
+ end
+ else
+ raise "dynstep!"
+ # Dynamic step; have to work out which way we're going at runtime
+ # Experimentation shows this isn't as scary as it seems; the
+ # reference implementation only evaluates the Step value once, as
+ # the loop starts.
+ #
+ # In theory, we could apply some clever logic based on the size of
+ # @body, to decide whether we're better off paying an extra git
+ # per iteration at runtime vs having two separate copies of the
+ # loop. No idea whether that'd be actually useful.
+
+ check_backwards = g.new_label
+ end_loop = g.new_label
+
+ g.dup_many 3 # loop_backwards, @step, @finish, ...
+ loop_var.get_bytecode(g)
+ g.send_stack :+, 1
+ g.dup
+ loop_var.set_bytecode(g)
+
+ g.git check_backwards
+ g.send_stack :>, 1
+ g.gif loop_body
+ g.goto end_loop
+
+ check_backwards.set!
+ g.send_stack :<, 1
+ g.gif loop_body
+
+ end_loop.set!
+ g.pop_many 3
+ end
end
end
class ForEachLoop < Loop
@@ -113,23 +203,23 @@ def initialize(matches, body)
@matches, @body = matches, body
end
def bytecode(g, done)
- body_label = new_label unless matches.size == 1
- failed_match = new_label
+ body_label = g.new_label unless matches.size == 1
+ failed_match = g.new_label
matches.each do |m|
- dup_top
+ g.dup
m.bytecode(g)
- meta_send_op_eq find_literal(:==)
+ g.meta_send_op_eq find_literal(:==)
if m == matches.last
- giz failed_match
+ g.giz failed_match
else
- gnz body_label
+ g.gnz body_label
end
end
body_label.set! unless matches.size == 1
@body.bytecode(g)
- goto done
+ g.goto done
failed_match.set!
end
View
@@ -11,11 +11,22 @@ def initialize(var, value)
class SetAssignment < Assignment
end
class ConstAssignment < Assignment
+ def bytecode(g)
+ if g.constants.key? @var
+ raise "Duplicate constant: #@var"
+ end
+
+ # @value is syntactically guaranteed to be a Literal.
+ g.constants[@var] = @value.value
+ end
end
class Declaration < Statement
- attr_accessor :var
- def initialize(var)
- @name = var
+ attr_accessor :vars
+ def initialize(vars)
+ @vars = vars
+ end
+ def bytecode(g)
+ # Compiler directive
end
end
class ErrorControl < Statement
@@ -31,6 +42,9 @@ def initialize(type)
end
end
class OptionExplicit < Statement
+ def bytecode(g)
+ # Compile option only
+ end
end
class Randomize < Statement
attr_accessor :seed
View
@@ -0,0 +1,10 @@
+module Rasp::AST
+
+class VarAccess < Node
+ attr_accessor :var
+ def initialize(var)
+ @var = var
+ end
+end
+
+end
View
@@ -4,6 +4,19 @@ class Code
end
class Compiler
+ module LocalVariables
+ include Rubinius::Compiler::LocalVariables
+
+ def new_local(name)
+ variable = Rubinius::Compiler::LocalVariable.new allocate_slot
+ variables[name] = variable
+ end
+
+ def variable(name)
+ variables[name] || new_local(name)
+ end
+ end
+
attr_reader :g
def initialize
@@ -22,7 +35,7 @@ def compile(ast, filename="(rasp)", line_number=1)
g.local_count = 0
g.local_names = []
- ast.bytecode self
+ ast.bytecode g
g.ret
g.close
@@ -39,6 +52,12 @@ def compile(ast, filename="(rasp)", line_number=1)
end
class Generator < Rubinius::Generator
+ attr_accessor :constants
+ def initialize
+ super
+ @constants = {}
+ end
+
def giz(label)
dup_top
gif label
Oops, something went wrong.

0 comments on commit 1e63e20

Please sign in to comment.