Skip to content
Browse files

More work in progress.

  • Loading branch information...
1 parent 14d9d4d commit 1e63e20396ee8dfbe4c149b02547d20876f318b2 @matthewd committed Nov 21, 2010
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
18 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)
+
View
3 lib/rasp.rb
@@ -0,0 +1,3 @@
+require 'rasp/compiler'
+require 'rasp/parser'
+require 'rasp/ast'
View
29 lib/rasp/ast.rb
@@ -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
13 lib/rasp/ast/block.rb
@@ -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
6 lib/rasp/ast/call.rb
@@ -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
104 lib/rasp/ast/flow.rb
@@ -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
20 lib/rasp/ast/simple.rb
@@ -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
10 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
View
21 lib/rasp/compiler.rb
@@ -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
View
293 lib/rasp/parser.citrus
@@ -0,0 +1,293 @@
+
+grammar Rasp::Parser
+ rule vbscript
+ (statement
+ |class_def
+ |sub_def
+ |function_def
+ )* { Rasp::AST::File.new(matches.map {|m| m.value }.compact) }
+ end
+ rule class_def
+ `class` identifier ENDSTATEMENT
+ class_body
+ `end` `class` ENDSTATEMENT
+ end
+ rule scope
+ `public` | `private`
+ end
+ rule class_body
+ ((scope SP)? ident_def ENDSTATEMENT
+ |(scope SP)? sub_def
+ |(scope SP)? function_def
+ |(scope SP)? property_def
+ |EOL
+ )* { matches.map {|m| m.value }.compact }
+ end
+ rule property_def
+ `property` SP (`get` | `let` | `set`) SP identifier SP? arg_def_list? ENDSTATEMENT
+ statement*
+ `end` SP `property` ENDSTATEMENT
+ end
+ rule sub_def
+ `sub` SP identifier SP? arg_def_list? ENDSTATEMENT
+ statement*
+ `end` SP `function` ENDSTATEMENT
+ end
+ rule function_def
+ `function` SP identifier SP? arg_def_list? ENDSTATEMENT
+ statement*
+ `end` SP `function` ENDSTATEMENT
+ end
+ rule arg_def_list
+ ("(" SP? ( list:(arg_def SP? "," SP?)* arg_def SP? )? ")") { list.map {|l| l.arg_def.value } + [arg_def.value] }
+ end
+ rule arg_def
+ ((`byref` SP | `byval` SP)? identifier) { identifier.value }
+ end
+ rule statement
+ (full_if_block
+ |single_line_if
+ |do_loop
+ |for_next
+ |for_each
+ |select_block
+ |with_block
+ |(simple_statement ENDSTATEMENT) { simple_statement.value }
+ |EOL
+ )
+ end
+ rule with_block
+ `with` SP expression ENDSTATEMENT statement* `end` SP `with` ENDSTATEMENT
+ end
+ rule select_block
+ `select` SP `case` SP expression ENDSTATEMENT
+ (`case` SP expression_list ENDSTATEMENT statement*)+
+ (`case` SP `else` statement*)?
+ `end` SP `select` ENDSTATEMENT
+ end
+ rule for_each
+ `for` SP `each` SP identifier SP `in` SP expression ENDSTATEMENT statement* `next` ENDSTATEMENT
+ end
+ rule do_loop
+ (`do` ENDSTATEMENT statement* `loop` ENDSTATEMENT
+ |`do` SP (`while` | `until`) SP expression ENDSTATEMENT statement* `loop` ENDSTATEMENT
+ |`do` ENDSTATEMENT statement* `loop` SP (`while` | `until`) SP expression ENDSTATEMENT
+ |`while` SP expression ENDSTATEMENT statement* `wend` ENDSTATEMENT
+ )
+ end
+ rule for_next
+ (`for` SP identifier SP? "=" SP? start:expression SP `to` SP finish:expression (SP `step` SP step:expression)? ENDSTATEMENT body:(statement*) `next` ENDSTATEMENT)
+ { Rasp::AST::ForLoop.new(identifier.value, start.value, finish.value, first(:step) && first(:step).value, body.matches.map {|m| m.value }) }
+ end
+ rule single_line_if
+ `if` SP expression SP `then` SP (single_line_if | simple_statement_chain (SP `else` SP (single_line_if | simple_statement_chain EOL) | EOL) )
+ end
+ rule full_if_block
+ `if` SP expression SP `then` ENDSTATEMENT statement*
+ (`elseif` SP expression SP `then` ENDSTATEMENT statement*)*
+ (`else` ENDSTATEMENT statement*)?
+ `end` SP `if` ENDSTATEMENT
+ end
+ rule simple_statement_chain
+ (simple_statement SP? ":" SP?)* simple_statement
+ end
+ rule simple_statement
+ (assignment
+ |(`dim` SP identifier list:(SP? "," SP? identifier)*) { Rasp::AST::Declaration.new([identifier.value] + list.matches.map {|x| x.identifier.value }) }
+ |exit_statement
+ |on_error_statement
+ |const_assignment
+ |`call` SP identifier ("(" SP? (expression_list SP?)? ")")?
+ |(`option` SP `explicit`) { Rasp::AST::OptionExplicit.new }
+ |`randomize` (SP expression)? { Rasp::AST::Randomize.new expression.value }
+ |sub_call
+ )
+ end
+ rule on_error_statement
+ (`on` SP `error` SP `resume` SP `next`) { Rasp::AST::ErrorControl.new(true) }
+ | (`on` SP `error` SP `goto` SP "0") { Rasp::AST::ErrorControl.new(false) }
+ end
+ rule exit_statement
+ (`exit` SP type:(`sub` | `function` | `property` | `do` | `for`)) { Rasp::AST::ExitStatement.new type.downcase.to_sym }
+ end
+ rule assignment
+ (objvalue SP? "=" SP? expression
+ |`set` SP objvalue SP? "=" SP? expression
+ )
+ end
+ rule const_assignment
+ (`const` SP identifier SP? "=" SP? literal) { Rasp::AST::ConstAssignment.new(identifier.value, literal.value) }
+ end
+ rule sub_call
+ (
+ subref:(init:((local:identifier | "." withprop:identifier) ("(" SP? (expression_list SP?)? ")")?)
+ { Rasp::AST::Call.new(first(:withprop) ? Rasp::AST::WithRef.new : nil,
+ (first(:local) || first(:withprop)).value,
+ first(:expression_list) && first(:expression_list).value) }
+ list:("." identifier ("(" SP? (expression_list SP?)? ")")?)*)
+ {
+ list.matches.inject(init.value) do |full, x|
+ Rasp::AST::Call.new(full, x.identifier.value, x.first(:expression_list) && x.first(:expression_list).value)
+ end
+ }
+ args:(!")" SP? expression_list)?
+ ) {
+ v = subref.value
+ a = args.matches.first && args.matches.first.first(:expression_list)
+ v.args = a.value if a
+ v
+ }
+ end
+ rule objvalue
+ (init:((local:identifier | "." withprop:identifier) ("(" SP? (expression_list SP?)? ")")?)
+ { Rasp::AST::Call.new(first(:withprop) ? Rasp::AST::WithRef.new : nil,
+ (first(:local) || first(:withprop)).value,
+ first(:expression_list) && first(:expression_list).value) }
+ list:("." identifier ("(" SP? (expression_list SP?)? ")")?)*)
+ {
+ list.matches.inject(init.value) do |full, x|
+ Rasp::AST::Call.new(full, x.identifier.value, x.first(:expression_list) && x.first(:expression_list).value)
+ end
+ }
+ end
+ rule identifier
+ (!reserved ident:/[A-Za-z_][A-Za-z0-9_]*/) { ident.to_sym } | "[" ident:/[^\]]+/ "]" { ident.to_sym }
+ end
+ rule reserved
+ active_reserved | other_reserved
+ end
+ rule active_reserved
+ # Reserved words that are used by the language, either to avoid
+ # ambiguity, as a convenience in the reference parser. Either way,
+ # we must reject them in order to be fully conforming.
+
+ `select` | `case` | `do` | `while` | `until` | `loop` | `end` | `wend`
+ | `if` | `then` | `elseif` | `else` | `byval` | `byref`
+ | `with` | `for` | `each` | `next` | `public` | `private`
+ | `class` | `function` | `sub` | `property` | `get` | `set` | `let`
+ | `imp` | `eqv` | `xor` | `or` | `and` | `not` | `is` | `mod`
+ | `new` | `const` | `dim` | `call` | `exit` | `option` | `randomize`
+
+ # Things you might think are reserved words, but actually aren't:
+ # `in`, `to`, `step`
+ end
+ rule other_reserved
+ # Reserved by spec, though unused in the language. Most likely, a
+ # reserved word in VB proper.
+ `typeof`
+ | `redim` | `preserve`
+ | `erase`
+ end
+ rule expression
+ (lhs:eqv_expr list:(SP op:`imp` SP rhs:eqv_expr
+ )*) { Rasp::AST.binary_op(Rasp::AST::ImpOp, lhs, list) }
+ end
+ rule eqv_expr
+ (lhs:xor_expr list:(SP op:`eqv` SP rhs:xor_expr
+ )*) { Rasp::AST.binary_op(Rasp::AST::EqvOp, lhs, list) }
+ end
+ rule xor_expr
+ (lhs:or_expr list:(SP op:`xor` SP rhs:or_expr
+ )*) { Rasp::AST.binary_op(Rasp::AST::XorOp, lhs, list) }
+ end
+ rule or_expr
+ (lhs:and_expr list:(SP op:`or` SP rhs:and_expr
+ )*) { Rasp::AST.binary_op(Rasp::AST::OrOp, lhs, list) }
+ end
+ rule and_expr
+ (lhs:not_expr list:(SP op:`and` SP rhs:not_expr
+ )*) { Rasp::AST.binary_op(Rasp::AST::AndOp, lhs, list) }
+ end
+ rule not_expr
+ (`not` SP cmp_expr) { Rasp::AST::NotOp.new(cmp_expr.value) } | cmp_expr
+ end
+ rule cmp_expr
+ (lhs:amp_expr list:(SP? op:"=" SP? rhs:amp_expr
+ |SP? op:"<>" SP? rhs:amp_expr
+ |SP? op:"<=" SP? rhs:amp_expr
+ |SP? op:">=" SP? rhs:amp_expr
+ |SP? op:"<" SP? rhs:amp_expr
+ |SP? op:">" SP? rhs:amp_expr
+ |SP op:`is` SP rhs:amp_expr
+ )*) { Rasp::AST.math_op(self) }
+ end
+ rule amp_expr
+ (lhs:add_expr list:(SP? op:"&" SP? rhs:add_expr
+ )*) { Rasp::AST.binary_op(Rasp::AST::StringAppend, lhs, list) }
+ end
+ rule add_expr
+ (lhs:mod_expr list:(SP? op:"+" SP? rhs:mod_expr
+ |SP? op:"-" SP? rhs:mod_expr
+ )*) { Rasp::AST.math_op(self) }
+ end
+ rule mod_expr
+ (lhs:idiv_expr list:(SP op:`mod` SP rhs:idiv_expr
+ )*) { Rasp::AST.math_op(self, :%) }
+ end
+ rule idiv_expr
+ (lhs:mul_expr list:(SP? op:"\\" SP? rhs:mul_expr
+ )*) { Rasp::AST.math_op(self) }
+ end
+ rule mul_expr
+ (lhs:exp_expr list:(SP? op:"*" SP? rhs:exp_expr
+ |SP? op:"/" SP? rhs:exp_expr
+ )*) { Rasp::AST.math_op(self) }
+ end
+ rule exp_expr
+ # Online references conflict on this point, but a manual test shows
+ # that unary minus binds more tightly than '^'
+ (lhs:unary_expr list:(SP? op:"^" SP? unary_expr
+ )*) { Rasp::AST.math_op(self) }
+ end
+ rule unary_expr
+ ("+" SP? value_expr { Rasp::AST::UnaryPlus.new value_expr }
+ |"-" SP? value_expr { Rasp::AST::UnaryMinus.new value_expr }
+ |value_expr
+ )
+ end
+ rule value_expr
+ (literal
+ |(`new` SP class_name:(identifier ("." identifier)*)) { Rasp::AST::NewObject.new class_name }
+ |("(" SP? expression SP? ")") { expression.value }
+ |objvalue
+ )
+ end
+
+ rule literal
+ (float
+ |integer
+ |string
+ |`true` { Rasp::AST::TrueValue.new }
+ |`false` { Rasp::AST::FalseValue.new }
+ )
+ end
+
+ rule integer
+ /[+-]?[0-9]+/ { Rasp::AST::Integer.new to_i }
+ end
+ rule float
+ /[0-9]+.[0-9]+/ { Rasp::AST::Float.new to_f }
+ end
+ rule string
+ /"([^"]|"")*"/ { Rasp::AST::String.new to_s }
+ end
+
+ rule ENDSTATEMENT
+ /[ \t]*(?::|(?:'[^\n]+)?\n)[ \t]*/ { nil }
+ end
+ rule expression_list
+ ((expression SP? "," SP?)* expression) { find(:expression).map {|e| e.value } }
+ end
+
+
+ rule EOL
+ /[ \t]*(?:(?:'[^\n]+)?\n[ \t]*)+/ { nil }
+ end
+ rule comment
+ /'[^\n]+/ { nil }
+ end
+ rule SP
+ /[ \t]+/ { nil }
+ end
+end
+
View
2 lib/rasp/parser.rb
@@ -0,0 +1,2 @@
+require 'citrus'
+Citrus.load( File.dirname(__FILE__) + '/parser' )
View
256 lib/rasp/scratch.citrus
@@ -1,256 +0,0 @@
-
-grammar VBScript
- rule vbscript
- (statement
- |class_def
- |sub_def
- |function_def
- )*
- end
- rule class_def
- `class` identifier ENDSTATEMENT
- class_body_item*
- `end` `class` ENDSTATEMENT
- end
- rule scope
- `public` | `private`
- end
- rule class_body_item
- ((scope SP)? ident_def ENDSTATEMENT
- |(scope SP)? sub_def
- |(scope SP)? function_def
- |(scope SP)? property_def
- |EOL
- )
- end
- rule property_def
- `property` SP (`get` | `let` | `set`) SP identifier SP? arg_def_list? ENDSTATEMENT
- statement*
- `end` SP `property` ENDSTATEMENT
- end
- rule sub_def
- `sub` SP identifier SP? arg_def_list? ENDSTATEMENT
- statement*
- `end` SP `function` ENDSTATEMENT
- end
- rule function_def
- `function` SP identifier SP? arg_def_list? ENDSTATEMENT
- statement*
- `end` SP `function` ENDSTATEMENT
- end
- rule arg_def_list
- "(" SP? ( (arg_def SP? "," SP?)* arg_def SP? )? ")"
- end
- rule arg_def
- (`byref` SP | `byval` SP)? identifier
- end
- rule statement
- (full_if_block
- |single_line_if
- |do_loop
- |for_next
- |for_each
- |select_block
- |with_block
- |simple_statement ENDSTATEMENT
- |EOL
- )
- end
- rule with_block
- `with` SP expression ENDSTATEMENT statement* `end` SP `with` ENDSTATEMENT
- end
- rule select_block
- `select` SP `case` SP expression ENDSTATEMENT
- (`case` SP expression_list ENDSTATEMENT statement*)+
- (`case` SP `else` statement*)?
- `end` SP `select` ENDSTATEMENT
- end
- rule for_each
- `for` SP `each` SP identifier SP `in` SP expression ENDSTATEMENT statement* `next` ENDSTATEMENT
- end
- rule do_loop
- (`do` ENDSTATEMENT statement* `loop` ENDSTATEMENT
- |`do` SP (`while` | `until`) SP expression ENDSTATEMENT statement* `loop` ENDSTATEMENT
- |`do` ENDSTATEMENT statement* `loop` SP (`while` | `until`) SP expression ENDSTATEMENT
- |`while` SP expression ENDSTATEMENT statement* `wend` ENDSTATEMENT
- )
- end
- rule for_next
- `for` SP identifier SP? "=" SP? expression SP `to` SP expression (SP `step` SP expression)? ENDSTATEMENT statement* `next` ENDSTATEMENT
- end
- rule single_line_if
- `if` SP expression SP `then` SP (single_line_if | simple_statement_chain (SP `else` SP (single_line_if | simple_statement_chain EOL) | EOL) )
- end
- rule full_if_block
- `if` SP expression SP `then` ENDSTATEMENT statement*
- (`elseif` SP expression SP `then` ENDSTATEMENT statement*)*
- (`else` ENDSTATEMENT statement*)?
- `end` SP `if` ENDSTATEMENT
- end
- rule simple_statement_chain
- (simple_statement SP? ":" SP?)* simple_statement
- end
- rule simple_statement
- (assignment
- |`dim` SP identifier (SP? "," SP? identifier)*
- |exit_statement
- |on_error_statement
- |const_assignment
- |`call` SP identifier ("(" SP? (expression_list SP?)? ")")?
- |`option` SP `explicit`
- |`randomize` (SP expression)?
- |objvalue (SP expression_list)?
- )
- end
- rule on_error_statement
- `on` SP `error` SP `resume` SP `next` | `on` SP `error` SP `goto` SP "0"
- end
- rule exit_statement
- `exit` SP (`sub` | `function` | `property` | `do` | `for`)
- end
- rule assignment
- (objvalue SP? "=" SP? expression
- |`set` SP objvalue SP? "=" SP? expression
- )
- end
- rule const_assignment
- `const` SP identifier SP? "=" SP? literal
- end
- rule objvalue
- (identifier | "." identifier) ("(" SP? (expression_list SP?)? ")" | "." objmember)*
- end
- rule identifier
- !reserved /[A-Za-z_][A-Za-z0-9_]*/ | /\[[^\]]*\]/
- end
- rule reserved
- active_reserved | other_reserved
- end
- rule active_reserved
- # Reserved words that are used by the language, either to avoid
- # ambiguity, as a convenience in the reference parser. Either way,
- # we must reject them in order to be fully conforming.
-
- `select` | `case` | `do` | `while` | `until` | `loop` | `end` | `wend`
- | `if` | `then` | `elseif` | `else` | `byval` | `byref`
- | `with` | `for` | `each` | `next` | `public` | `private`
- | `class` | `function` | `sub` | `property` | `get` | `set` | `let`
- | `imp` | `eqv` | `xor` | `or` | `and` | `not` | `is` | `mod`
- | `new` | `const` | `dim` | `call` | `exit` | `option` | `randomize`
-
- # Things you might think are reserved words, but actually aren't:
- # `in`, `to`, `step`
- end
- rule other_reserved
- # Reserved by spec, though unused in the language. Most likely, a
- # reserved word in VB proper.
- `typeof`
- | `redim` | `preserve`
- | `erase`
- end
- rule objmember
- identifier
- end
- rule expression
- eqv_expr (SP `imp` SP eqv_expr)*
- end
- rule eqv_expr
- xor_expr (SP `eqv` SP xor_expr)*
- end
- rule xor_expr
- or_expr (SP `xor` SP or_expr)*
- end
- rule or_expr
- and_expr (SP `or` SP and_expr)*
- end
- rule and_expr
- not_expr (SP `and` SP not_expr)*
- end
- rule not_expr
- `not` SP cmp_expr | cmp_expr
- end
- rule cmp_expr
- amp_expr (SP? "=" SP? amp_expr
- |SP? "<>" SP? amp_expr
- |SP? "<=" SP? amp_expr
- |SP? ">=" SP? amp_expr
- |SP? "<" SP? amp_expr
- |SP? ">" SP? amp_expr
- |SP `is` SP amp_expr
- )*
- end
- rule amp_expr
- add_expr (SP? "&" SP? add_expr)*
- end
- rule add_expr
- mod_expr (SP? "+" SP? mod_expr
- |SP? "-" SP? mod_expr
- )*
- end
- rule mod_expr
- idiv_expr (SP `mod` SP idiv_expr)*
- end
- rule idiv_expr
- mul_expr (SP? "\\" SP? mul_expr)*
- end
- rule mul_expr
- exp_expr (SP? "*" SP? exp_expr
- |SP? "/" SP? exp_expr
- )*
- end
- rule exp_expr
- # Online references conflict on this point, but a manual test shows
- # that unary minus binds more tightly than '^'
- unary_expr (SP? "^" SP? unary_expr)*
- end
- rule unary_expr
- ("+" SP? value_expr
- |"-" SP? value_expr
- |value_expr
- )
- end
- rule value_expr
- (literal
- |`new` SP identifier ("." identifier)*
- |"(" SP? expression SP? ")"
- |objvalue
- )
- end
-
- rule literal
- (float
- |integer
- |string
- |`true`
- |`false`
- )
- end
-
- rule integer
- /[+-]?[0-9]+/
- end
- rule float
- /[0-9]+.[0-9]+/
- end
- rule string
- /"([^"]|"")*"/
- end
-
- rule ENDSTATEMENT
- /[ \t]*(?::|(?:'[^\n]+)?\n)[ \t]*/
- end
- rule expression_list
- (expression SP? "," SP?)* expression
- end
-
-
- rule EOL
- /[ \t]*(?:(?:'[^\n]+)?\n[ \t]*)+/
- end
- rule comment
- /'[^\n]+/
- end
- rule SP
- /[ \t]+/
- end
-end
-
View
6 scratch.rb
@@ -1,8 +1,4 @@
-
-require 'citrus'
-Citrus.load 'scratch'
-$CITRUS_DEBUG = true
+require 'rasp/parser'
r = VBScript.parse(File.read('t/math.vbs'), :consume => true)
puts r.dump
-

0 comments on commit 1e63e20

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