Skip to content
This repository has been archived by the owner on May 13, 2019. It is now read-only.

Commit

Permalink
More work in progress.
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewd committed Nov 20, 2010
1 parent 14d9d4d commit 1e63e20
Show file tree
Hide file tree
Showing 13 changed files with 506 additions and 275 deletions.
18 changes: 18 additions & 0 deletions bin/rasp
@@ -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)

3 changes: 3 additions & 0 deletions lib/rasp.rb
@@ -0,0 +1,3 @@
require 'rasp/compiler'
require 'rasp/parser'
require 'rasp/ast'
29 changes: 28 additions & 1 deletion lib/rasp/ast.rb
@@ -1,8 +1,30 @@

module Rasp; module AST module Rasp; module AST
FalseInt = 0 FalseInt = 0
TrueInt = ~FalseInt 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 class Node
def graph def graph
Rubinius::AST::AsciiGrapher.new(self, Node).print Rubinius::AST::AsciiGrapher.new(self, Node).print
Expand All @@ -21,4 +43,9 @@ def visit(visitor)
end end
end; 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' require 'rasp/ast/value'
13 changes: 11 additions & 2 deletions lib/rasp/ast/block.rb
@@ -1,12 +1,21 @@
module Rasp::AST module Rasp::AST


class Scope < Node class File < Node
include Rubinus::Compiler::LocalVariables include Rasp::Compiler::LocalVariables


attr_accessor :statements attr_accessor :statements
def initialize(statements) def initialize(statements)
@statements = statements @statements = statements
end 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


end end
6 changes: 6 additions & 0 deletions lib/rasp/ast/call.rb
Expand Up @@ -5,6 +5,12 @@ class Call < Node
def initialize(target, name, args) def initialize(target, name, args)
@target, @name, @args = target, name, args @target, @name, @args = target, name, args
end 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 end


class NullCall < Call class NullCall < Call
Expand Down
104 changes: 97 additions & 7 deletions lib/rasp/ast/flow.rb
Expand Up @@ -48,13 +48,103 @@ def bytecode(g)
end end
end end
class ForLoop < Loop 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 attr_accessor :var, :start, :finish, :step
def initialize(var, start, finish, step, body) def initialize(var, start, finish, step, body)
super(:for, body) super(:for, body)
@var, @start, @finish, @step = var, start, finish, step @var, @start, @finish, @step = var, start, finish, step
end end
def bytecode(g) def bytecode(g)
# TODO # 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
end end
class ForEachLoop < Loop class ForEachLoop < Loop
Expand Down Expand Up @@ -113,23 +203,23 @@ def initialize(matches, body)
@matches, @body = matches, body @matches, @body = matches, body
end end
def bytecode(g, done) def bytecode(g, done)
body_label = new_label unless matches.size == 1 body_label = g.new_label unless matches.size == 1
failed_match = new_label failed_match = g.new_label


matches.each do |m| matches.each do |m|
dup_top g.dup
m.bytecode(g) m.bytecode(g)
meta_send_op_eq find_literal(:==) g.meta_send_op_eq find_literal(:==)
if m == matches.last if m == matches.last
giz failed_match g.giz failed_match
else else
gnz body_label g.gnz body_label
end end
end end


body_label.set! unless matches.size == 1 body_label.set! unless matches.size == 1
@body.bytecode(g) @body.bytecode(g)
goto done g.goto done


failed_match.set! failed_match.set!
end end
Expand Down
20 changes: 17 additions & 3 deletions lib/rasp/ast/simple.rb
Expand Up @@ -11,11 +11,22 @@ def initialize(var, value)
class SetAssignment < Assignment class SetAssignment < Assignment
end end
class ConstAssignment < Assignment 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 end
class Declaration < Statement class Declaration < Statement
attr_accessor :var attr_accessor :vars
def initialize(var) def initialize(vars)
@name = var @vars = vars
end
def bytecode(g)
# Compiler directive
end end
end end
class ErrorControl < Statement class ErrorControl < Statement
Expand All @@ -31,6 +42,9 @@ def initialize(type)
end end
end end
class OptionExplicit < Statement class OptionExplicit < Statement
def bytecode(g)
# Compile option only
end
end end
class Randomize < Statement class Randomize < Statement
attr_accessor :seed attr_accessor :seed
Expand Down
10 changes: 10 additions & 0 deletions lib/rasp/ast/variable.rb
@@ -0,0 +1,10 @@
module Rasp::AST

class VarAccess < Node
attr_accessor :var
def initialize(var)
@var = var
end
end

end
21 changes: 20 additions & 1 deletion lib/rasp/compiler.rb
Expand Up @@ -4,6 +4,19 @@ class Code
end end


class Compiler 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 attr_reader :g


def initialize def initialize
Expand All @@ -22,7 +35,7 @@ def compile(ast, filename="(rasp)", line_number=1)
g.local_count = 0 g.local_count = 0
g.local_names = [] g.local_names = []


ast.bytecode self ast.bytecode g
g.ret g.ret
g.close g.close


Expand All @@ -39,6 +52,12 @@ def compile(ast, filename="(rasp)", line_number=1)
end end


class Generator < Rubinius::Generator class Generator < Rubinius::Generator
attr_accessor :constants
def initialize
super
@constants = {}
end

def giz(label) def giz(label)
dup_top dup_top
gif label gif label
Expand Down

0 comments on commit 1e63e20

Please sign in to comment.