Skip to content
Browse files

AST & compiler: very much WIP.

  • Loading branch information...
1 parent 8e93410 commit c3b563c16e22d3ba37ac5d373fb86f6a4429416a @matthewd committed Nov 20, 2010
Showing with 443 additions and 3 deletions.
  1. +22 −0 LICENSE
  2. +24 −0 ast.rb
  3. +12 −0 ast/block.rb
  4. +24 −0 ast/call.rb
  5. +87 −0 ast/expression.rb
  6. +138 −0 ast/flow.rb
  7. +42 −0 ast/simple.rb
  8. +29 −0 ast/value.rb
  9. +62 −0 compiler.rb
  10. +3 −3 scratch.citrus
View
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2010 Matthew Draper. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
View
24 ast.rb
@@ -0,0 +1,24 @@
+
+module Rasp; module AST
+FalseInt = 0
+TrueInt = ~FalseInt
+
+class Node
+ def graph
+ Rubinius::AST::AsciiGrapher.new(self, Node).print
+ end
+
+ def visit(visitor)
+ # This is a bit magic... but is does avoid loads of repetition
+ self_name = self.class.name.gsub(/(.)([A-Z])/) { $1 + "_" + $2.downcase }.downcase
+ visitor.send(self_name, self)
+ end
+end
+class Container < Node
+ def visit(visitor)
+ child_nodes.each {|c| c.visit visitor }
+ end
+end
+end; end
+
+require 'rasp/ast/value'
View
12 ast/block.rb
@@ -0,0 +1,12 @@
+module Rasp::AST
+
+class Scope < Node
+ include Rubinus::Compiler::LocalVariables
+
+ attr_accessor :statements
+ def initialize(statements)
+ @statements = statements
+ end
+end
+
+end
View
24 ast/call.rb
@@ -0,0 +1,24 @@
+module Rasp::AST
+
+class Call < Node
+ attr_accessor :target, :name, :args
+ def initialize(target, name, args)
+ @target, @name, @args = target, name, args
+ end
+end
+
+class NullCall < Call
+end
+class GetCall < Node
+end
+class LetCall < Node
+ attr_accessor :value
+ def initialize(target, name, args, value)
+ super target, name, args
+ @value = value
+ end
+end
+class SetCall < LetCall
+end
+
+end
View
87 ast/expression.rb
@@ -0,0 +1,87 @@
+module Rasp::AST
+
+class Operator < Node
+end
+class UnaryOp < Node
+ attr_accessor :inner
+ def initialize(inner)
+ @inner = inner
+ end
+end
+class BinaryOp < Node
+ attr_accessor :lhs, :rhs
+ def initialize(lhs, rhs)
+ @lhs, @rhs = lhs, rhs
+ end
+ def bytecode(g)
+ @rhs.bytecode(g)
+ @lhs.bytecode(g)
+ op_bytecode g
+ end
+end
+class ImpOp < BinaryOp
+end
+class EqvOp < BinaryOp
+end
+class XorOp < BinaryOp
+ def op_bytecode(g)
+ g.send :^
+ end
+end
+class OrOp < BinaryOp
+ def op_bytecode(g)
+ g.send :|
+ end
+end
+class AndOp < BinaryOp
+ def op_bytecode(g)
+ g.send :&
+ end
+end
+class NotOp < UnaryOp
+end
+class Comparison < BinaryOp
+ attr_accessor :operator
+ def initialize(operator, lhs, rhs)
+ super(lhs, rhs)
+ @operator = operator
+ end
+ def op_bytecode(g)
+ g.send @operator.to_sym
+ end
+end
+class StringAppend < BinaryOp
+ def op_bytecode(g)
+ g.string_dup
+ g.string_append
+ end
+end
+class MathOp < BinaryOp
+ attr_accessor :operator
+ def initialize(operator, lhs, rhs)
+ super(lhs, rhs)
+ @operator = operator
+ end
+ def op_bytecode(g)
+ g.send @operator.to_sym
+ end
+end
+class UnaryPlus < UnaryOp
+ def op_bytecode(g)
+ g.send_vcall :"@+"
+ end
+end
+class UnaryMinus < UnaryOp
+ def op_bytecode(g)
+ g.send_vcall :"@-"
+ end
+end
+
+class NewObject < Node
+ attr_accessor :class_name
+ def initialize(class_name)
+ @class_name = class_name
+ end
+end
+
+end
View
138 ast/flow.rb
@@ -0,0 +1,138 @@
+module Rasp::AST
+
+class Loop < Node
+ attr_accessor :type, :body
+ def initialize(type, body)
+ @type, @body = type, body
+ end
+
+ def bytecode(g)
+ top = g.new_label
+ top.set!
+
+ @body.bytecode(g)
+ g.goto top
+ end
+end
+class DoWhile < Loop
+ attr_accessor :condition
+ def initialize(type, condition, invert, body)
+ super(type, body)
+ @condition = invert ? NotExpr.new(condition) : condition
+ end
+ def bytecode(g)
+ top = g.new_label
+ done = g.new_label
+ top.set!
+
+ @condition.bytecode(g)
+ g.meta_push_0
+ g.meta_send_op_eq
+ g.giz done
+
+ @body.bytecode(g)
+ g.goto top
+
+ done.set!
+ end
+end
+class LoopWhile < DoWhile
+ def bytecode(g)
+ top = g.new_label
+ top.set!
+
+ @body.bytecode(g)
+
+ @condition.bytecode(g)
+ g.gnz top
+ end
+end
+class ForLoop < Loop
+ 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
+ end
+end
+class ForEachLoop < Loop
+ attr_accessor :var, :collection
+ def initialize(var, collection, body)
+ super(:for, body)
+ @var, @collection = var, collection
+ end
+ def bytecode(g)
+ # TODO
+ end
+end
+
+class If < Node
+ attr_accessor :condition, :true_body, :false_body
+ def initialize(condition, true_body, false_body)
+ @condition, @true_body, @false_body = condition, true_body, false_body
+ end
+
+ def bytecode(g)
+ done = g.new_label
+ false_label = g.new_label
+
+ @condition.bytecode(g)
+ g.giz false_label
+
+ @true_body.bytecode(g)
+ g.goto done
+
+ false_label.set!
+ @false_body.bytecode(g)
+
+ done.set!
+ end
+end
+
+class SelectCase < Container
+ attr_accessor :expr, :cases, :else_body
+ def initialize(expr, cases, else_body)
+ @expr, @cases, @else_body = expr, cases, else_body
+ end
+
+ def bytecode(g)
+ done = new_label
+ @expr.bytecode(g)
+ @cases.each do |c|
+ c.bytecode(g, done)
+ end
+ @else_body.bytecode(g) if @else_body
+ done.set!
+ end
+end
+class Case < Node
+ attr_accessor :matches, :body
+ 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
+
+ matches.each do |m|
+ dup_top
+ m.bytecode(g)
+ meta_send_op_eq find_literal(:==)
+ if m == matches.last
+ giz failed_match
+ else
+ gnz body_label
+ end
+ end
+
+ body_label.set! unless matches.size == 1
+ @body.bytecode(g)
+ goto done
+
+ failed_match.set!
+ end
+end
+
+end
View
42 ast/simple.rb
@@ -0,0 +1,42 @@
+module Rasp::AST
+
+class Statement < Node
+end
+class Assignment < Statement
+ attr_accessor :var, :value
+ def initialize(var, value)
+ @var, @value = var, value
+ end
+end
+class SetAssignment < Assignment
+end
+class ConstAssignment < Assignment
+end
+class Declaration < Statement
+ attr_accessor :var
+ def initialize(var)
+ @name = var
+ end
+end
+class ErrorControl < Statement
+ attr_accessor :continue
+ def initialize(continue)
+ @continue = continue
+ end
+end
+class ExitStatement < Statement
+ attr_accessor :type
+ def initialize(type)
+ @type = type
+ end
+end
+class OptionExplicit < Statement
+end
+class Randomize < Statement
+ attr_accessor :seed
+ def initialize(seed=nil)
+ @seed = seed
+ end
+end
+
+end
View
29 ast/value.rb
@@ -0,0 +1,29 @@
+module Rasp::AST
+
+class Literal < Node
+ attr_accessor :value
+ def initialize(value)
+ @value = value
+ end
+ def bytecode(g)
+ g.push_literal value
+ end
+end
+class String < Literal
+end
+class Integer < Literal
+end
+class Float < Literal
+end
+class TrueValue < Integer
+ def initialize
+ super TrueInt
+ end
+end
+class FalseValue < Integer
+ def initialize
+ super FalseInt
+ end
+end
+
+end
View
62 compiler.rb
@@ -0,0 +1,62 @@
+# Stolen from poison; original code Copyright (c) 2010 Brian Ford.
+module Rasp
+ class Code
+ end
+
+ class Compiler
+ attr_reader :g
+
+ def initialize
+ @g = Generator.new
+ end
+
+ def compile(ast, filename="(rasp)", line_number=1)
+ g.name = :call
+ g.file = filename.intern
+ g.set_line line_number
+
+ g.required_args = 0
+ g.total_args = 0
+ g.splat_index = nil
+
+ g.local_count = 0
+ g.local_names = []
+
+ ast.bytecode self
+ g.ret
+ g.close
+
+ g.encode
+ cm = g.package ::Rubinius::CompiledMethod
+ puts cm.decode if $DEBUG
+
+ code = Rasp::Code.new
+ ss = ::Rubinius::StaticScope.new Object
+ ::Rubinius.attach_method g.name, cm, ss, code
+
+ code
+ end
+ end
+
+ class Generator < Rubinius::Generator
+ def giz(label)
+ dup_top
+ gif label
+
+ meta_push_0
+ meta_send_op_eq find_literal(:==)
+ git label
+ end
+ def gnz(label)
+ f = new_label
+ dup_top
+ gif f # if it's false, don't compare to zero; just continue
+
+ meta_push_0
+ meta_send_op_eq find_literal(:==)
+ gif label
+
+ f.set!
+ end
+ end
+end
View
6 scratch.citrus
@@ -52,8 +52,6 @@ grammar VBScript
|for_each
|select_block
|with_block
- |`option` SP `explicit`
- |`randomize` (SP expression)?
|simple_statement ENDSTATEMENT
|EOL
)
@@ -99,6 +97,8 @@ grammar VBScript
|on_error_statement
|const_assignment
|`call` SP identifier ("(" SP? (expression_list SP?)? ")")?
+ |`option` SP `explicit`
+ |`randomize` (SP expression)?
|objvalue (SP expression_list)?
)
end
@@ -210,7 +210,7 @@ grammar VBScript
end
rule value_expr
(literal
- |`new` SP identifier
+ |`new` SP identifier ("." identifier)*
|"(" SP? expression SP? ")"
|objvalue
)

0 comments on commit c3b563c

Please sign in to comment.
Something went wrong with that request. Please try again.