Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

This code dates from between Nov 2005 and Sep 2006.

And it's a mess.
  • Loading branch information...
commit b1e075a4e668b185b67c63958cb543f6db9ef0da 0 parents
Matthew Draper authored
4 .gitignore
@@ -0,0 +1,4 @@
+*.output
+*.tab.rb
+*.aspc
+*.vbsc
75 convertor.rb
@@ -0,0 +1,75 @@
+require 'syntax/convertors/abstract'
+
+module Syntax
+ module Convertors
+ class Meta < Abstract
+ def type= new_type
+ @tokenizer.type = new_type
+ end
+ def type
+ @tokenizer.type
+ end
+ def virtual_root= new_value
+ @tokenizer.virtual_root = new_value
+ end
+ def virtual_root; @tokenizer.virtual_root; end
+ def follow_includes= new_value
+ @tokenizer.follow_includes = new_value
+ end
+ def swallow= new_value
+ @tokenizer.swallow = new_value
+ end
+ attr_accessor :start_group
+ def convert text, filename
+ @tokenizer.filename = filename
+ @tokenizer.start( text ) do |token|
+ start_group.call token.group, token
+ end
+ @tokenizer.step until @tokenizer.eos?
+ @tokenizer.finish
+ end
+ end
+ class CodeTree < Abstract
+ def type= new_type
+ @tokenizer.type = new_type
+ end
+ def type
+ @tokenizer.type
+ end
+ def virtual_root; @tokenizer.virtual_root; end
+ def virtual_root= new_value
+ @tokenizer.virtual_root = new_value
+ end
+ def follow_includes= new_value
+ @tokenizer.follow_includes = new_value
+ end
+ def swallow= new_value
+ @tokenizer.swallow = new_value
+ end
+ def convert text, filename
+ @text = text
+ @filename = filename
+ VbScriptRaccParser.new.run_parse self, :go
+ end
+ def go
+ @tokenizer.filename = @filename
+ @tokenizer.start( @text ) do |token|
+ if $DEBUG
+ if token.group.is_a? Symbol then
+ puts "Yielding :#{token.group} for '#{token}'"
+ else
+ puts "Yielding characters for '#{token}'"
+ end
+ end
+ yield [token.group, token] unless token.group == :token_sep
+ end
+ @tokenizer.step until @tokenizer.eos?
+ @tokenizer.finish
+ puts "Yielding final :fake_newline" if $DEBUG
+ yield [:fake_newline, nil]
+ puts "Yielding EOF" if $DEBUG
+ yield [false, false]
+ end
+ end
+ end
+end
15 foo.asp
@@ -0,0 +1,15 @@
+<%
+
+Dim o
+Set o = Server.CreateObject("Scripting.Dictionary")
+
+o.Add "foo", "bar"
+o.Add "baz", "quux"
+
+Response.Write o("foo") & vbCrLf & vbCrLf
+
+o("foo") = o("baz")
+
+Response.Write o("foo")
+
+%>
371 parser.rb
@@ -0,0 +1,371 @@
+
+require 'syntax'
+
+class Syntax::Tokenizer
+ def token tok
+ @callback.call( tok )
+ end
+end
+
+class PositionedToken < Syntax::Token
+ attr_accessor :position
+ def initialize data, group, position
+ super data, group
+ self.position = position
+ end
+end
+class VbScriptTokenizer < Syntax::Tokenizer
+ def initialize
+ @type = nil
+ @filename = ""
+ @follow_includes = false
+ @virtual_root = nil
+ @swallow = true
+ end
+ attr_accessor :type
+
+ attr_accessor :filename
+ def setup
+ @mode = nil
+ @file_type = @type
+ @allow_keyword = true
+ @last_space = true
+ @asp_expr = false
+ self.last_pos = 0
+ end
+
+ def start_group(a, b=nil)
+ a, b = map_group(a, b)
+ if a
+ flush_chunk
+ @group = a
+ append b
+ flush_chunk
+ #super :token_sep
+ end
+ end
+
+ attr_accessor :follow_includes
+ attr_accessor :virtual_root
+ attr_accessor :swallow
+
+ attr_accessor :last_pos
+ alias :scan_without_storing_position :scan
+ def scan_after_storing_position pattern
+ self.last_pos = pos
+ scan_without_storing_position pattern
+ end
+ alias :scan :scan_after_storing_position
+
+ def position
+ pre = @text.string.slice( 0, last_pos + 1 )
+ line = pre.count( "\n" ) + 1
+ char = pre.reverse.index( "\n" )
+ char = pre.length if line == 1
+ "#{filename}(#{line}, #{char})"
+ end
+ def append data
+ if @chunk.empty?
+ @chunk = data
+ else
+ @chunk << data
+ end
+ end
+ def flush_chunk
+ tok = @chunk || ""
+ @chunk = ""
+
+ unless tok.empty?
+ tok = PositionedToken.new( tok, @group, position ) unless tok.is_a? Syntax::Token
+ token( tok )
+ end
+ end
+ def start_region(a, b=nil)
+ start_group a, b
+ end
+ def end_region(a, b=nil)
+ start_group(a, b) if b
+ end
+
+ def map_group(group, value)
+ group = :ident if group == :'asp constant'
+ return nil, nil if group == :space || group == :line_continuation || ((group == :comment) && @swallow)
+ return value, value if group == :operator || group == :asp_marker
+ #return :keyword, value if group.to_s.upcase == group.to_s
+ return :ident, value if group.to_s.upcase == group.to_s and @allow_keyword == false
+ return group, value
+ end
+
+ def step
+ allow_next_keyword = true
+ is_space = false
+
+ unless @mode
+ @file_type = check( /\s*</ ) ? :asp : :vbs unless @file_type
+ case @file_type
+ when :asp
+ @mode = :html
+ when :vbs
+ @mode = :normal
+ else
+ @mode = check( /\s*</ ) ? :html : :normal
+ end
+ end
+
+ case @mode
+ when :html
+ @mode = :normal
+ start_group :html, scan_until( /(?=<!-- *#include)|(?=<%)|(?![^X]|X)/i )
+ if scan(/<% *=/)
+ start_group :asp_marker, '<%='
+ @asp_expr = true
+ elsif directive = scan( /<!-- *#include +(virtual|file)="([^"]*)" *-->/i )
+ if follow_includes
+ path = subgroup(2)
+ if subgroup(1).downcase == 'virtual' && subgroup(2)[0] == ?/
+ path = Pathname.new( virtual_root + path.sub( /./, '' ) )
+ else
+ path = Pathname.new( path )
+ current_dir = Pathname.new( filename ).parent
+ path = current_dir + path
+ end
+
+ c = Syntax::Convertors::Meta.for_syntax 'vbscript'
+ c.start_group = lambda {|group, content| start_group group, content }
+ c.type = :asp
+ c.follow_includes = true
+ c.virtual_root = virtual_root
+ puts "Entering #{path}"
+ c.convert File.read( path ), path
+ puts "Leaving #{path}"
+ @mode = :html
+ else
+ start_group :directive, directive
+ @mode = :html
+ end
+ else
+ start_group :asp_marker, scan( /<%/ )
+ @asp_expr = false
+ end
+ is_space = true
+ when :normal
+ if nl = scan(/\r\n?|\n/)
+ start_group :newline, nl
+ is_space = true
+ elsif nl = scan(/:/)
+ start_group :operator, nl
+ is_space = true
+ elsif continue = scan(/_ *(\r\n?|\n)/)
+ start_group :line_continuation, continue
+ is_space = true
+ elsif @file_type == :asp and marker = scan( /%>/ )
+ unless @asp_expr
+ start_group :fake_newline, ' '
+ end
+ @mode = :html
+ start_group :asp_marker, marker
+ elsif comment = scan(/(Rem +|').*/i)
+ start_group :comment, comment
+
+ # FIXME: This is just for testing convenience
+ if comment == "'__END__"
+ # Swallow the whole file
+ scan( /(X|[^X])*/ )
+ end
+ elsif quote = scan(/"/)
+ start_group :operator, quote
+ start_region :string_text, scan(/([^"%]|%[^>])+%?/)
+ @mode = :string
+
+ elsif reserved = scan( /(TypeOf)\b/i )
+ start_group :reserved, reserved
+
+ elsif constant = scan( /True\b/i )
+ start_group :TRUE, constant
+ elsif constant = scan( /False\b/i )
+ start_group :FALSE, constant
+
+ # FIXME
+ elsif constant = scan( /(Empty|Nothing|Null|Err)\b/i )
+ start_group :ident, constant
+
+ # FIXME
+ elsif constant = scan( /(Response|Request|Server)\b/i )
+ start_group :ident, constant
+
+ elsif operator = scan( /With\b/i )
+ start_group :WITH, operator
+ elsif operator = scan( /Class\b/i )
+ start_group :CLASS, operator
+ elsif operator = scan( /Sub\b/i )
+ start_group :SUB, operator
+ elsif operator = scan( /Function\b/i )
+ start_group :FUNCTION, operator
+ elsif operator = scan( /Property\b/i )
+ start_group :PROPERTY, operator
+ elsif operator = scan( /End\b/i )
+ start_group :END, operator
+ elsif operator = scan( /Exit\b/i )
+ start_group :EXIT, operator
+ elsif operator = scan( /Get\b/i )
+ start_group :GET, operator
+
+ elsif keyword = scan( /New\b/i )
+ start_group :NEW, keyword
+
+ elsif operator = scan( /On +Error +Resume +Next\b/i )
+ start_group :IGNORE_ERRORS, operator
+ elsif operator = scan( /On +Error +Goto +0\b/i )
+ start_group :THROW_ERRORS, operator
+ elsif operator = scan( /Option +Explicit\b/i )
+ start_group :EXPLICIT, operator
+ elsif operator = scan( /Call\b/i )
+ start_group :CALL, operator
+ elsif operator = scan( /Randomize\b/i )
+ start_group :RANDOMIZE, operator
+ elsif operator = scan( /Private\b/i )
+ start_group :PRIVATE, operator
+ elsif operator = scan( /Public\b/i )
+ start_group :PUBLIC, operator
+ elsif operator = scan( /Erase\b/i )
+ start_group :ERASE, operator
+ elsif operator = scan( /Const\b/i )
+ start_group :CONST, operator
+ elsif operator = scan( /Dim\b/i )
+ start_group :DIM, operator
+ elsif operator = scan( /ReDim\b/i )
+ start_group :REDIM, operator
+ elsif operator = scan( /Preserve\b/i )
+ start_group :PRESERVE, operator
+ elsif operator = scan( /Let\b/i )
+ start_group :LET, operator
+ elsif operator = scan( /Set\b/i )
+ start_group :SET, operator
+ elsif operator = scan( /Do\b/i )
+ start_group :DO, operator
+ elsif operator = scan( /Loop\b/i )
+ start_group :LOOP, operator
+ elsif operator = scan( /For\b/i )
+ start_group :FOR, operator
+ elsif operator = scan( /In\b/i ) # FIXME: Not actually a reserved word! :(
+ start_group :IN, operator
+ elsif operator = scan( /To\b/i ) # FIXME: Not actually a reserved word! :(
+ start_group :TO, operator
+ elsif operator = scan( /Step\b/i ) # FIXME: Not actually a reserved word! :(
+ start_group :STEP, operator
+ elsif operator = scan( /Next\b/i )
+ start_group :NEXT, operator
+ elsif operator = scan( /Each\b/i )
+ start_group :EACH, operator
+ elsif operator = scan( /If\b/i )
+ start_group :IF, operator
+ elsif operator = scan( /With\b/i )
+ start_group :WITH, operator
+ elsif operator = scan( /Then\b/i )
+ start_group :THEN, operator
+ elsif operator = scan( /Else\b/i )
+ start_group :ELSE, operator
+ elsif operator = scan( /ElseIf\b/i )
+ start_group :ELSEIF, operator
+ elsif operator = scan( /Select\b/i )
+ start_group :SELECT, operator
+ elsif operator = scan( /Case\b/i )
+ start_group :CASE, operator
+ elsif operator = scan( /While\b/i )
+ start_group :WHILE, operator
+ elsif operator = scan( /Until\b/i )
+ start_group :UNTIL, operator
+ elsif operator = scan( /Wend\b/i )
+ start_group :WEND, operator
+
+ # FIXME
+ elsif builtin = scan( /(IsArray|LBound|UBound|Abs|Asc|AscB|AscW|Chr|ChrB|ChrW|CBool|CByte|CDate|CDbl|CInt|CLng|CSng|CStr|DateSerial|DateValue|Hex|Oct|Fix|Int|Sgn|TimeSerial|TimeValue|Date|Time|Day|Month|Weekday|Year|Hour|Minute|Second|Now|Atn|Cos|Sin|Tan|Exp|Log|Sqr|Rnd|CreateObject|IsObject|InStr|InStrB|Len|LenB|LCase|UCase|Left|LeftB|Mid|MidB|Right|RightB|Space|StrComp|String|LTrim|RTrim|Trim|IsDate|IsEmpty|IsNull|IsNumeric|VarType)(?= *\()/i )
+ start_group :ident, builtin
+
+ elsif operator = scan( /ByVal\b/i )
+ start_group :BYVAL, operator
+ elsif operator = scan( /ByRef\b/i )
+ start_group :BYREF, operator
+
+ elsif operator = scan( /Mod\b/i )
+ start_group :MOD, operator
+ elsif operator = scan( /Is\b/i )
+ start_group :IS, operator
+ elsif operator = scan( /Not\b/i )
+ start_group :NOT, operator
+ elsif operator = scan( /And\b/i )
+ start_group :AND, operator
+ elsif operator = scan( /Or\b/i )
+ start_group :OR, operator
+ elsif operator = scan( /Xor\b/i )
+ start_group :XOR, operator
+ elsif operator = scan( /Eqv\b/i )
+ start_group :EQV, operator
+ elsif operator = scan( /Imp\b/i )
+ start_group :IMP, operator
+
+ elsif digits = scan(/\d*\.\d+/)
+ start_group :float, digits
+
+ elsif digits = scan(/\d+/)
+ start_group :integer, digits
+
+ elsif words = scan(/\w+/)
+ start_group :ident, words
+
+ elsif punct = scan(/[,]/)
+ start_group :operator, punct
+ is_space = true
+
+ elsif punct = scan(/[(]/)
+ start_group @last_space ? :operator : :arg_paren, punct
+ is_space = true
+
+ elsif punct = scan(/[.]/)
+ start_group @last_space ? :with_dot : :operator, punct
+ allow_next_keyword = false
+
+ elsif punct = scan(/[)]/)
+ start_group :operator, punct
+ allow_next_keyword = false
+ is_space = false
+
+ elsif punct = scan(/[-+*\\\/^&=]|[<>]=|<>|[<>]/)
+ start_group :operator, punct
+ allow_next_keyword = false
+ is_space = true
+
+ elsif space = scan(/\s/)
+ start_group :space, space
+ is_space = true
+
+ elsif ext_ident = scan(/\[([^\]]*)\]/)
+ start_group :operator, '['
+ start_group :ident, subgroup( 1 )
+ start_group :operator, ']'
+
+ else
+ start_group :unknown, scan(/./)
+ end
+ when :string
+ if @file_type == :asp and marker = scan( /%>/ )
+ raise "Syntax error: Can't have %> in a string!"
+ elsif escaped = scan(/""/)
+ start_group :escaped_atom, escaped
+ start_group :string_text, scan(/([^"%]|%[^>])+%?/)
+ elsif quote = scan(/"/)
+ end_region :string_text
+ start_group :operator, quote
+ @mode = :normal
+ else
+ start_group :string_text, scan(/./)
+ end
+ end
+
+ @allow_keyword = allow_next_keyword
+ @last_space = is_space
+ end
+end
+
+Syntax::SYNTAX['vbscript'] = VbScriptTokenizer
+
1,258 rasp.rb
@@ -0,0 +1,1258 @@
+
+class Hash
+ def self.from_two_arrays keys, values
+ hash = {}
+ keys.size.times { |i| hash[keys[i]] = values[i] }
+ hash
+ end
+end
+class Array
+ def debug_info action
+ puts
+ puts "#{action} code block:"
+ puts
+ p self
+ puts
+ end
+ def token
+ self[0].token rescue self[0]
+ end
+ def append other
+ if other.is_a? Array
+ self + other
+ else
+ self.push other
+ end
+ end
+ def compile context
+ debug_info 'Compiling' if $DEBUG
+ compile_all(context).last
+ end
+ def run context
+ run_all(context).last
+ end
+ def compile_all context
+ map { |item| item.compile context }
+ end
+ def run_all context
+ debug_info 'Running' if $DEBUG
+ map { |item| item.run context }
+ end
+ def context= new_value
+ each { |item| item.context = new_value }
+ end
+end
+class Object
+ def run context; self; end
+ def compile context; self; end
+ def simple; self; end
+ def is_intrinsic?; false; end
+end
+class Array; def is_intrinsic?; true; end; end
+class Number; def is_intrinsic?; true; end; end
+class String; def is_intrinsic?; true; end; end
+
+module Rasp
+ def self.name_of_member_to_call obj, original_name
+ name = original_name.to_s.dup
+ return name if obj.respond_to? name
+ name.gsub!( /([A-Z])/ ) { |letter| "_#{letter.downcase}" }
+ name.gsub!( /^_/, '' )
+ return name if obj.respond_to? name
+ name.gsub!( /(_[a-z])/ ) { |letter| letter.upcase[1,1] }
+ return name if obj.respond_to? name
+ name.gsub!( /^([a-z])/ ) { |letter| letter.upcase }
+ return name if obj.respond_to? name
+ name.downcase!
+ return name if obj.respond_to? name
+ name.gsub!( '_', '' )
+ return name if obj.respond_to? name
+
+ matches = obj.methods.select { |method_name| method_name.downcase.gsub( '_', '' ) == name }
+ return matches[0] if matches.size == 1
+
+ return original_name
+ end
+
+ class Context
+ attr_accessor :local_variables
+ attr_accessor :methods
+ attr_accessor :parent_context
+ attr_accessor :constants
+ attr_accessor :errors_fatal
+ attr_accessor :self_object
+
+ def all_constant?; @all_constant; end
+ def all_constant!; @all_constant = true; end
+
+ def errors_fatal?
+ return errors_fatal unless errors_fatal.nil?
+ return parent_context.errors_fatal? unless parent_context.nil?
+ true
+ end
+
+ def root_context
+ return parent_context.root_context unless parent_context.nil?
+ return self
+ end
+
+ def initialize options=nil
+ @self_object = nil
+ @parent_context = nil
+ @methods = {}
+ @local_variables = {}
+ @constants = []
+ options.each { |key, value| instance_variable_set('@' + key.to_s, value) } if options
+ @all_constant = false
+ @explicit = false
+ end
+
+ def get_method name, failure_fatal = true
+ puts "Getting method #{name} in #{self}" if $DEBUG
+
+ method = nil
+ #name_on_object = Rasp::name_of_member_to_call( self_object, name )
+ name_on_object = name.downcase
+ method ||= self_object.method( name_on_object ) if self_object.respond_to? name_on_object
+ method ||= methods[name.downcase]
+ method ||= parent_context.get_method(name, failure_fatal) if parent_context
+ raise "Unable to find method: #{name}" if method.nil? && failure_fatal
+ method
+ end
+
+ def define_method name, args, body, exit_symbol, returns
+ throw :constant_context if all_constant?
+ throw :variable if variable_exists? name
+ methods[name.downcase] = Method.new(:name => name, :arguments => args, :body => body, :context => self, :exit_symbol => exit_symbol, :returns => returns)
+ end
+ def define_native_method name, &block
+ throw :constant_context if all_constant?
+ throw :variable if variable_exists? name
+ puts "Defining #{name.downcase}" if $DEBUG
+ methods[name.downcase] = RubyMethod.new(:name => name, :block => block, :context => self)
+ end
+
+ def variable_context variable_name
+ return self if local_variables.key? variable_name.downcase
+ return parent_context.variable_context( variable_name ) unless parent_context.nil?
+ end
+
+ def define_variable variable_name, self_value = false
+ return false if all_constant?
+ return false if local_variables.key? variable_name.downcase
+ return false if method_exists? variable_name and !self_value
+ puts "Defining #{variable_name} (#{variable_name.class}) in #{self}" if $DEBUG
+ local_variables[variable_name.downcase] = Empty.get
+ return true
+ end
+
+ def [] variable_name
+ return self_object if variable_name.downcase == 'me' unless self_object.nil?
+ #name_on_object = Rasp::name_of_member_to_call( self_object, variable_name )
+ name_on_object = variable_name.downcase
+ return self_object.__send__( name_on_object ) if self_object.respond_to? name_on_object
+ context = variable_context(variable_name)
+ if context.nil?
+ if method = get_method(variable_name, false)
+ raise "#{variable_name} does not return a value" if method.respond_to?( :returns ) && !method.returns
+ raise "Incorrect number of arguments for #{variable_name}; got 0, expected #{method.arity}" unless method.arity.zero? or method.arity == -1
+ return method.call
+ end
+
+ if explicit?
+ raise "Variable undefined: #{variable_name} (known: #{known_variables}); current context: #{self}"
+ else
+ define_variable variable_name
+ context = self
+ end
+ end
+ context.local_variables[variable_name.downcase]
+ end
+
+ def known_variables
+ l_known = local_variables.keys.join(', ')
+ p_known = parent_context.known_variables if parent_context
+ return "#{l_known}; #{p_known}"
+ end
+
+ def []= variable_name, new_value
+ throw :constant_context if all_constant?
+ throw :constant if is_constant? variable_name
+ context = variable_context(variable_name)
+ unless context
+ throw :variable_undefined if explicit?
+ context = self
+ end
+ context.local_variables[variable_name.downcase] = new_value
+ end
+
+ def explicit?
+ return parent_context.explicit? if parent_context
+ @explicit
+ end
+ def explicit!
+ @explicit = true
+ end
+
+ def exists? name
+ variable_exists?(name) || method_exists?(name)
+ end
+
+ def variable_exists? name
+ !variable_context(name).nil?
+ end
+
+ def method_exists? name
+ !get_method(name, false).nil?
+ end
+
+ def is_constant? name
+ constants.include? name.downcase
+ end
+
+ def define_constant name, value
+ self[name] = value
+ constants.push name.downcase
+ value
+ end
+ end
+
+ class MetaObject
+ attr_accessor :__context__
+ def simple
+ throw :no_default_property
+ end
+ def method_missing target, *args
+ name = Rasp::name_of_member_to_call( self, target )
+ if name != target
+ __send__ name, *args
+ else
+ super
+ end
+ end
+ def generous_respond_to? method
+ name = Rasp::name_of_member_to_call( self, method )
+ respond_to? name
+ end
+ def initialize
+ class_initialize if generous_respond_to? :class_initialize
+ end
+ def self.create_finalizer obj
+ proc {|id| obj.class_terminate if obj.generous_respond_to? :class_terminate }
+ end
+ end
+
+ # SYNTAX ERRORS
+ # 1052 Cannot have multiple default property/method in a Class
+ # 1044 Cannot use parentheses when calling a Sub
+ # 1053 Class initialize or terminate do not have arguments
+ # 1058 'Default' specification can only be on Property Get
+ # 1057 'Default' specification must also specify 'Public'
+ # 1005 Expected '('
+ # 1006 Expected ')'
+ # 1011 Expected '='
+ # 1021 Expected 'Case'
+ # 1047 Expected 'Class'
+ # 1025 Expected end of statement
+ # 1014 Expected 'End'
+ # 1023 Expected expression
+ # 1015 Expected 'Function'
+ # 1010 Expected identifier
+ # 1012 Expected 'If'
+ # 1046 Expected 'In'
+ # 1026 Expected integer constant
+ # 1049 Expected Let or Set or Get in property declaration
+ # 1045 Expected literal constant
+ # 1019 Expected 'Loop'
+ # 1020 Expected 'Next'
+ # 1050 Expected 'Property'
+ # 1022 Expected 'Select'
+ # 1024 Expected statement
+ # 1016 Expected 'Sub'
+ # 1017 Expected 'Then'
+ # 1013 Expected 'To'
+ # 1018 Expected 'Wend'
+ # 1027 Expected 'While' or 'Until'
+ # 1028 Expected 'While,' 'Until,' or end of statement
+ # 1029 Expected 'With'
+ # 1030 Identifier too long
+ # 1014 Invalid character
+ # 1039 Invalid 'exit' statement
+ # 1040 Invalid 'for' loop control variable
+ # 1013 Invalid number
+ # 1037 Invalid use of 'Me' keyword
+ ## 1038 'loop' without 'do'
+ # 1048 Must be defined inside a Class
+ # 1042 Must be first statement on the line
+ ## 1041 Name redefined
+ # 1051 Number of arguments must be consistent across properties specification
+ # 1001 Out of Memory
+ # 1054 Property Set or Let must have at least one argument
+ ## 1002 Syntax error
+ # 1055 Unexpected 'Next'
+ # 1015 Unterminated string constant
+ #
+ # RUNTIME ERRORS
+ # 429 ActiveX component can't create object
+ # 507 An exception occurred
+ # 449 Argument not optional
+ # 17 Can't perform requested operation
+ # 430 Class doesn't support Automation
+ # 506 Class not defined
+ ## 11 Division by zero
+ # 48 Error in loading DLL
+ # 5020 Expected ')' in regular expression
+ # 5019 Expected ']' in regular expression
+ # 432 File name or class name not found during Automation operation
+ # 92 For loop not initialized
+ # 5008 Illegal assignment
+ ## 51 Internal error
+ # 505 Invalid or unqualified reference
+ # 481 Invalid picture
+ # 5 Invalid procedure call or argument
+ # 5021 Invalid range in character set
+ # 94 Invalid use of Null
+ # 448 Named argument not found
+ # 447 Object doesn't support current locale setting
+ # 445 Object doesn't support this action
+ # 438 Object doesn't support this property or method
+ # 451 Object not a collection
+ # 504 Object not safe for creating
+ # 503 Object not safe for initializing
+ # 502 Object not safe for scripting
+ # 424 Object required
+ # 91 Object variable not set
+ # 7 Out of Memory
+ # 28 Out of stack space
+ # 14 Out of string space
+ # 6 Overflow
+ # 35 Sub or function not defined
+ # 9 Subscript out of range
+ # 5017 Syntax error in regular expression
+ # 462 The remote server machine does not exist or is unavailable
+ # 10 This array is fixed or temporarily locked
+ # 13 Type mismatch
+ # 5018 Unexpected quantifier
+ # 500 Variable is undefined
+ # 458 Variable uses an Automation type not supported in VBScript
+ # 450 Wrong number of arguments or invalid property assignment
+ #
+ class ScriptError < RuntimeError
+ def exception; self; end
+ attr_accessor :error_symbol
+ attr_accessor :statement
+ attr_accessor :token
+ attr_accessor :inner
+ def initialize error_symbol, statement, token=nil
+ self.error_symbol = error_symbol
+ self.statement = statement
+ self.token = token.nil? ? (statement.nil? ? nil : statement.token) : token
+
+ @number, @description = \
+ case error_symbol
+ when :divide_by_zero
+ [11, 'Division by zero']
+ when :syntax_error
+ [1002, 'Syntax error']
+ when :name_redefined
+ [1041, 'Name redefined']
+ when :loop_without_do
+ [1038, "'loop' without 'do'"]
+ else
+ [51, 'Internal error']
+ end
+ end
+ attr_reader :number
+ attr_accessor :description
+ def location
+ puts "token nil!" if token.nil?
+ puts "token #{token.class} can't position!" unless token.nil? || token.respond_to?( :position )
+ unless token.nil? || !token.respond_to?( :position )
+ token.position + ' '
+ end
+ end
+ def to_s
+ "#{location}Microsoft VBScript runtime error: #{description}"
+ end
+ end
+
+ class Statement
+ def initialize options
+ @token = nil
+ options.each { |key, value| instance_variable_set('@' + key.to_s, value) }
+ end
+ attr_writer :token
+ def token
+ t = @token
+ t = t.token while t.respond_to? :token
+ t
+ end
+ def compile context
+ end
+ def run context
+ begin
+ run_statement context
+ rescue ScriptError => error
+ error.statement = self
+ error.token = self.token # Maybe?
+ raise error if context.errors_fatal?
+ context.root_context['err'].load_from_ruby_exception error
+ rescue StandardError => error
+ wrapper = ScriptError.new :internal_error, self
+ wrapper.inner = error
+# FIXME
+ raise error
+ raise wrapper if context.errors_fatal?
+ context.root_context['err'].load_from_ruby_exception wrapper
+# ensure
+# GC.start
+ end
+ end
+ end
+
+ class ControlStructure < Statement
+ def initialize options
+ super options
+ end
+ end
+
+ # For, Do, While
+ class Loop < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :init
+ attr_accessor :pre
+ attr_accessor :body
+ attr_accessor :post
+
+ def run_statement context
+ init.run context
+ loop do
+ pre.run(context) or return
+ body.run context
+ post.run(context) or return
+ end
+ end
+ end
+
+ # For Each
+ class Each < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :variable
+ attr_accessor :collection
+ attr_accessor :body
+
+ def run_statement context
+ collection.run(context).each do |item|
+ context[variable] = item
+ body.run context
+ end
+ end
+ end
+
+ # On Error ...
+ class OnError < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :fatal
+
+ def run_statement context
+ context.errors_fatal = fatal
+ end
+ end
+
+ # Option Explicit
+ class Explicit < ControlStructure
+ def compile context
+ context.explicit!
+ end
+ def run_statement context
+ end
+ end
+
+ # If
+ class Conditional < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :condition
+ attr_accessor :true_part
+ attr_accessor :false_part
+
+ def run_statement context
+ if condition.run(context).simple
+ true_part.run context
+ else
+ false_part.run context
+ end
+ end
+ end
+
+ # Select Case
+ class Select < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :expression
+ attr_accessor :alternatives
+
+ def run_statement context
+ value = expression.run(context).simple
+ for alt in alternatives
+ if alt[0] == :else || alt[0].find {|a| Rasp::Expression::Operator::Equal.new(:lvalue => value, :rvalue => a).run(context).simple }
+ return alt[1].run(context)
+ end
+ end
+ end
+ end
+
+ # Dim
+ class Define < Statement
+ def initialize options
+ super options
+ end
+
+ attr_accessor :variable_name
+
+ def compile context
+ unless context.define_variable(variable_name)
+ error = ScriptError.new(:name_redefined, self)
+ raise error if context.errors_fatal?
+ context.root_context['err'].load_from_ruby_exception error
+ end
+ end
+ def run_statement context
+ end
+ end
+
+ # Function
+ class MethodDefine < Statement
+ def initialize options
+ super options
+ end
+
+ attr_accessor :name
+ attr_accessor :arguments
+ attr_accessor :body
+ attr_accessor :exit_symbol
+ attr_accessor :returns
+ attr_accessor :scope
+
+ def run_statement context
+ context.define_method name, arguments, body, exit_symbol, returns
+ end
+ end
+
+ # Class
+ class ClassDefine < Statement
+ def initialize options
+ super options
+ end
+
+ attr_accessor :name
+ attr_accessor :content
+
+ def compile context
+ klass = Class.new( MetaObject )
+ class_context = Context.new(:parent_context => context)
+ class_context.define_variable 'Me'
+ get_destructor = nil
+ content.flatten.each do |item|
+ if item.is_a? Rasp::MethodDefine
+ method = Method.new(:name => item.name, :arguments => item.arguments, :body => item.body, :context => context, :exit_symbol => item.exit_symbol, :returns => item.returns)
+ # FIXME: Really shouldn't play with the name they're trying to define :(
+ klass.send :define_method, item.name.downcase do |*args|
+ method.call_with_context( self.__context__, *args )
+ end
+ if item.name.downcase == 'class_terminate'
+ get_destructor = lambda do |context|
+ lambda do |id|
+ method.call_with_context( context )
+ end
+ end
+ end
+ end
+ end
+ # FIXME: This finalizer won't ever run because I've created a
+ # circular reference :(
+ klass.send :define_method, :initialize do
+ #super
+ self.__context__ = class_context.clone
+ self.__context__.self_object = self
+ self.__context__['Me'] = self
+ ObjectSpace.define_finalizer( self, get_destructor.call( self.__context__ ) ) unless get_destructor.nil?
+ #puts "Defining finalizer" unless get_destructor.nil?
+ #get_destructor.call( self.__context ).call( 0 ) unless get_destructor.nil?
+ end
+
+ context[self.name] = klass
+ end
+
+ def run_statement context
+ end
+ end
+
+ # =, Let, Set
+ class Assignment < Statement
+ def initialize options
+ @object = nil
+ @constant = nil
+ super options
+ end
+
+ attr_accessor :lvalue
+ attr_accessor :expression
+ attr_accessor :constant
+ attr_accessor :object
+
+ def run_statement context
+# FIXME: Why are the next two lines required?!
+ @object = nil unless defined? @object
+ @constant = nil unless defined? @constant
+
+ value = expression.run(context)
+ value = value.simple unless object
+
+ if constant
+ context.define_constant lvalue, value
+ elsif lvalue.is_a? String
+ context[lvalue] = value
+ else
+ if lvalue.is_a? Rasp::Expression::MethodCall
+ # Convert it to a member-call on the default method
+ name = if lvalue.respond_to? :method_name
+ if lvalue.method_name.respond_to? :variable_name
+ lvalue.method_name.variable_name
+ else
+ lvalue.method_name
+ end
+ elsif lvalue.respond_to? :variable_name
+ lvalue.variable_name
+ end.downcase
+ args = lvalue.respond_to?(:arguments) ? lvalue.arguments : []
+
+ puts "lvalue is a method call... this must be an assignment to the default property" if $DEBUG
+
+ lvalue = Rasp::Expression::MemberCall.new(
+ :object => Rasp::Expression::Variable.new(:variable_name => name),
+ :member => Rasp::Expression::MethodCall.new(:method_name => 'ASP_Default', :arguments => args))
+ end
+
+ args = lvalue.member.arguments.run_all(context) if lvalue.member.respond_to?(:arguments)
+ args.push value
+ name = if lvalue.member.respond_to? :method_name
+ lvalue.member.method_name
+ elsif lvalue.member.respond_to? :variable_name
+ lvalue.member.variable_name
+ end.downcase
+
+ o = lvalue.object.run( context )
+ name = Rasp::name_of_member_to_call( o, name )
+
+ if o.respond_to?( :ole_methods ) && name == 'asp_default'
+ name = o.ole_methods.find {|m| m.dispid == 0 }.name
+ end
+ name = '[]' if name == 'asp_default' and !o.respond_to?( :asp_default= ) and o.respond_to?( :[]= )
+ if o.is_a? WIN32OLE
+ o.setproperty( name, *args )
+ else
+ o.__send__(name + '=', *args)
+ end
+ end
+ end
+ end
+ class Exit < Statement
+ def initialize options
+ super options
+ end
+ attr_accessor :symbol
+ def run_statement context
+ throw symbol
+ end
+ end
+
+ # Response.Write
+ class Print < Statement
+ def initialize options
+ super options
+ end
+
+ attr_accessor :expression
+
+ def run_statement context
+ puts expression.run(context).simple
+ end
+ end
+
+ class Randomize < Statement
+ def initialize options
+ super options
+ end
+
+ attr_accessor :expression
+
+ def run_statement context
+ ignore = expression.run(context).simple
+ end
+ end
+
+ class RubyMethod < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :name
+ attr_accessor :block
+ attr_accessor :context
+
+ def arity
+ block.arity
+ end
+
+ def call *argument_values
+ block.call( *argument_values )
+ end
+
+ def returns; true; end
+ end
+ class Method < ControlStructure
+ def initialize options
+ super options
+ end
+
+ attr_accessor :name
+ attr_accessor :body
+ attr_accessor :arguments
+ attr_accessor :context
+ attr_accessor :exit_symbol
+ attr_accessor :returns
+
+ def arity
+ arguments.length
+ end
+
+ def call *argument_values
+ call_with_context context, *argument_values
+ end
+ def call_with_context context, *argument_values
+ c = Context.new(:parent_context => context)
+ c.errors_fatal = true
+
+ # The name of the method is also a variable, through which the
+ # return value will be retrieved
+ c.define_variable name, true
+
+ # The method's arguments are defined as locally-scoped
+ # variables
+ args = Hash.from_two_arrays arguments, argument_values
+ args.each { |name, value| c.define_variable(name); c[name] = value }
+
+ catch exit_symbol do
+ body.compile(c)
+ body.run(c)
+ end
+
+ # The return value is the current value of the variable sharing
+ # its name with this method
+ c[self.name]
+ end
+ end
+
+ module Expression
+ class Atom
+ def initialize options
+ options.each { |key, value| instance_variable_set('@' + key.to_s, value) }
+ end
+ attr_writer :token
+ def token
+ t = @token
+ t = t.token while t.respond_to? :token
+ t
+ end
+ def compile context
+ end
+ end
+
+ class Literal < Atom
+ def initialize options
+ super options
+ end
+
+ attr_accessor :value
+
+ def run context
+ value
+ end
+ end
+
+ class NewObject < Atom
+ def initialize options
+ super options
+ end
+
+ attr_accessor :class_name
+
+ def run context
+ context[class_name].new
+ end
+ end
+
+ class Variable < Atom
+ def initialize options
+ super options
+ end
+
+ attr_accessor :variable_name
+
+ def run context
+ puts "Getting variable #{variable_name}" if $DEBUG
+ context[variable_name]
+ end
+ end
+
+ class MemberCall < Atom
+ def initialize options
+ super options
+ end
+
+ attr_accessor :object
+ attr_accessor :member
+
+ def run context, *arguments
+ puts "Calling member #{member.to_s} on #{object.to_s}" if $DEBUG
+
+ name = if member.respond_to? :method_name
+ member.method_name
+ elsif member.respond_to? :variable_name
+ member.variable.name
+ else
+ member
+ end.downcase
+ o = object.run( context )
+ name = Rasp::name_of_member_to_call( o, name )
+ args = member.respond_to?(:arguments) ? member.arguments : arguments
+
+ args = args.run_all( context )
+
+ if o.respond_to?( :ole_methods ) && name == 'asp_default'
+ name = o.ole_methods.find {|m| m.dispid == 0 }.name
+ end
+ name = '[]' if name == 'asp_default' and !o.respond_to?( :asp_default ) and o.respond_to?( :[] )
+ o.__send__( name, *args )
+ end
+ end
+ class MemberSet < Atom
+ def initialize options
+ super options
+ end
+
+ attr_accessor :object
+ attr_accessor :name
+ attr_accessor :arguments
+ attr_accessor :expression
+ attr_accessor :action
+
+ def run context
+ value = expression.run(context)
+ value = value.simple if :action == :let
+ args = arguments.run_all(context)
+ args.push value
+ object.run.__send__ name + '=', *args
+ end
+ end
+
+ class MethodCall < Atom
+ def initialize options
+ super options
+ end
+
+ attr_accessor :method_name
+ attr_accessor :arguments
+ attr_accessor :expect_return
+
+ def run context
+ self.method_name = method_name.variable_name if method_name.respond_to?( :variable_name )
+ if method_name.is_a? Rasp::Expression::MemberCall
+ return method_name.run( context, *arguments )
+ #puts "method_name.object"
+ #p method_name.object
+ #puts "-- "
+ #puts "method_name.member"
+ #p method_name.member
+ #puts "-- "
+ #puts "arguments"
+ #p arguments
+ #puts "-- "
+ end
+ if method = context.get_method(method_name, false)
+ args = arguments.run_all(context)
+ raise "#{method_name} does not return a value" if expect_return && !method.returns
+ unless method.arity.zero? && args.length > 0
+ required = method.arity
+ required = -required - 1 if required < 0
+ expected = required
+ expected = "at least #{expected}" if method.arity < 0
+ raise "Incorrect number of arguments for #{method_name}; got #{args.length}, expected #{expected}" unless required == args.length or (args.length > required and method.arity < 0)
+ return method.call(*args)
+ else
+ object = Rasp::Expression::MethodCall.new(:method_name => method_name, :arguments => [], :expect_return => true)
+ end
+ elsif context.exists? method_name
+ object = Rasp::Expression::Variable.new(:variable_name => method_name)
+ else
+ raise "No such method: #{method_name}"
+ end
+
+ puts "Didn't find a method #{method_name}, maybe it's a default method call on an object... (#{context})" if $DEBUG
+
+ # Convert it to a member-call on the default method
+ return Rasp::Expression::MemberCall.new(
+ :object => object,
+ :member => Rasp::Expression::MethodCall.new(:method_name => 'ASP_Default', :arguments => arguments)
+ ).run(context)
+ end
+ end
+
+ module Operator
+ class Operator < Rasp::Expression::Atom
+ def initialize options
+ super options
+ end
+ end
+
+ class BinaryOperator < Operator
+ def initialize options
+ super options
+ end
+
+ attr_accessor :lvalue
+ attr_accessor :rvalue
+ end
+
+ class UnaryOperator < Operator
+ def initialize options
+ super options
+ end
+
+ attr_accessor :value
+ end
+
+ class And < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple & rvalue.run(context).simple
+ end
+ end
+ class Or < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple | rvalue.run(context).simple
+ end
+ end
+ class Xor < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple ^ rvalue.run(context).simple
+ end
+ end
+ class Eqv < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple ^ ~rvalue.run(context).simple
+ end
+ end
+ class Imp < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ ~lvalue.run(context).simple | rvalue.run(context).simple
+ end
+ end
+
+ class TypeOf < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).class.to_s == rvalue
+ end
+ end
+
+ class Equal < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple == rvalue.run(context).simple
+ end
+ end
+ class ObjEqual < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ #left = lvalue.run( context )
+ #right = rvalue.run( context )
+
+ #left.__id__ == right.__id__
+ lvalue.run(context).__id__ == rvalue.run(context).__id__
+ end
+ end
+ class NotEqual < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple != rvalue.run(context).simple
+ end
+ end
+ class GreaterThan < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple > rvalue.run(context).simple
+ end
+ end
+ class GreaterThanEqual < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple >= rvalue.run(context).simple
+ end
+ end
+ class LessThan < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple < rvalue.run(context).simple
+ end
+ end
+ class LessThanEqual < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple <= rvalue.run(context).simple
+ end
+ end
+
+ class Concat < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple.to_s + rvalue.run(context).simple.to_s
+ end
+ end
+
+ class Plus < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple + rvalue.run(context).simple
+ end
+ end
+ class Minus < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple - rvalue.run(context).simple
+ end
+ end
+ class Multiply < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple * rvalue.run(context).simple
+ end
+ end
+ class Divide < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ begin
+ lvalue.run(context).simple / rvalue.run(context).simple
+ rescue ZeroDivisionError => error
+ raise ScriptError.new(:divide_by_zero, nil, self.token)
+ end
+ end
+ end
+ class IntDivide < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ (lvalue.run(context).simple / rvalue.run(context).simple).to_i
+ end
+ end
+ class Modulo < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple % rvalue.run(context).simple
+ end
+ end
+ class Exponent < BinaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ lvalue.run(context).simple ** rvalue.run(context).simple
+ end
+ end
+
+ class UnaryMinus < UnaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ -value.run(context).simple
+ end
+ end
+ class UnaryPlus < UnaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ value.run(context).simple
+ end
+ end
+ class Not < UnaryOperator
+ def initialize options
+ super options
+ end
+
+ def run context
+ ~value.run(context).simple
+ end
+ end
+ end
+ end
+end
+
+def test1
+ c = Rasp::Context.new
+ a_expr = Rasp::Expression::Literal.new(:value => 1)
+ assignment = Rasp::Assignment.new(:lvalue => :a, :expression => a_expr)
+ a_ref = Rasp::Expression::Variable.new(:variable_name => :a)
+ comp = Rasp::Expression::Operator::GreaterThan.new(:lvalue => a_ref, :rvalue => 2)
+ truepart = Rasp::Print.new(:expression => 'Foo')
+ falsepart = Rasp::Print.new(:expression => 'Bar')
+ if_stmt = Rasp::Conditional.new(:condition => comp, :true_part => truepart, :false_part => falsepart)
+
+ file = [assignment, if_stmt]
+ file.run c
+
+ # a = 1
+ # If a > 2 Then
+ # Response.Write "Foo"
+ # Else
+ # Response.Write "Bar"
+ # End If
+end
+
+def test2
+ a_expr = Rasp::Expression::Literal.new(:value => 4)
+ a_expr_2 = Rasp::Expression::Literal.new(:value => 23)
+ a_assignment = Rasp::Assignment.new(:lvalue => :a, :expression => a_expr)
+ a_assignment_2 = Rasp::Assignment.new(:lvalue => :a, :expression => a_expr_2)
+ a_def = Rasp::Define.new(:variable_name => :a)
+ a_ref = Rasp::Expression::Variable.new(:variable_name => :a)
+ b_expr = Rasp::Expression::Literal.new(:value => 51)
+ b_expr_2 = Rasp::Expression::Literal.new(:value => 17)
+ b_assignment = Rasp::Assignment.new(:lvalue => :b, :expression => b_expr)
+ b_assignment_2 = Rasp::Assignment.new(:lvalue => :b, :expression => b_expr_2)
+ b_ref = Rasp::Expression::Variable.new(:variable_name => :b)
+ method_call = Rasp::Expression::MethodCall.new(:method_name => :frob, :arguments => [])
+
+ a_out = Rasp::Print.new(:expression => a_ref.dup)
+ b_out = Rasp::Print.new(:expression => b_ref.dup)
+ sep_out = Rasp::Print.new(:expression => '-')
+
+ method_body = [a_def, a_assignment_2, b_assignment_2, a_out.dup, b_out.dup, sep_out.dup]
+
+ method_def = Rasp::MethodDefine.new(:name => :frob, :arguments => [], :body => method_body)
+
+ file = [method_def, a_assignment, b_assignment, a_out, b_out, sep_out, method_call, a_out, b_out]
+ file.run Rasp::Context.new
+
+ # Sub frob
+ # Dim a
+ # a = 23
+ # b = 17
+ #
+ # Response.Write a
+ # Response.Write b
+ # Response.Write "-"
+ # End Sub
+ #
+ # a = 4
+ # b = 51
+ #
+ # Response.Write a
+ # Response.Write b
+ # Response.Write "-"
+ #
+ # frob
+ #
+ # Response.Write a
+ # Response.Write b
+end
+
+#puts 'test1:'
+#test1
+#puts '-- '
+
+#puts 'test2:'
+#test2
+#puts '-- '
+
79 reformat.rb
@@ -0,0 +1,79 @@
+require 'convertor'
+require 'parser'
+require 'reformat.tab'
+require 'rasp'
+
+require 'pathname'
+
+$BuiltIns = %w(
+Rnd Round CLng CInt CDbl CStr CBool oApplication Trim Write ID
+Request Response Server Now Array DatePart WeekDayName Day Month Year Right Left Mid Replace IsNumeric IsNull IsEmpty
+Null Nothing Empty Err TypeName FormatNumber Abs LBound UBound Split
+)
+# elsif builtin = scan( /(IsArray|LBound|UBound|Abs|Asc|AscB|AscW|Chr|ChrB|ChrW|CBool|CByte|CDate|CDbl|CInt|CLng|CSng|CStr|DateSerial|DateValue|Hex|Oct|Fix|Int|Sgn|TimeSerial|TimeValue|Date|Time|Day|Month|Weekday|Year|Hour|Minute|Second|Now|Atn|Cos|Sin|Tan|Exp|Log|Sqr|Rnd|CreateObject|IsObject|InStr|InStrB|Len|LenB|LCase|UCase|Left|LeftB|Mid|MidB|Right|RightB|Space|StrComp|String|LTrim|RTrim|Trim|IsDate|IsEmpty|IsNull|IsNumeric|VarType)(?= *\()/i )
+
+$Rename = { }
+
+$seen = {}
+
+def Bracket(s)
+ (s.nil? || s.empty?) ? '' : "(#{s})"
+end
+def NL(s)
+ s << "\n" unless s =~ /\n\z/
+ s
+end
+def I(s)
+ return '' if s.empty?
+
+ lines = s.split("\n")
+ lines.pop while lines.last.empty?
+
+ lines.map {|l| l.empty? ? '' : (' ' + l) }.join("\n") + "\n"
+end
+def check_ident(s)
+ builtin = $BuiltIns.find {|b| b.downcase == s.downcase }
+ return builtin if builtin
+
+ rename = $Rename.keys.find {|r| r.downcase == s.downcase }
+ return $Rename[rename] if rename
+
+ # TODO: Try to split it into words (possibly with an ID suffix, and
+ # possibly with a type prefix), and re-capitalize appropriately.
+
+ $seen[s.downcase] = [] unless $seen.include? s.downcase
+ $seen[s.downcase] << s
+
+ s
+end
+def check_member(s)
+ # This function is passed an already-checked identifier, for further
+ # checking, now that we know it's an object member.
+
+ s[0, 1].upcase + s[1, s.size]
+end
+
+filename = ARGV[0] || 'samples/test2.vbs'
+
+convertor = Syntax::Convertors::CodeTree.for_syntax 'vbscript'
+convertor.follow_includes = true
+convertor.follow_includes = false ## XXX
+convertor.swallow = false ## XXX
+convertor.virtual_root = Pathname.new( filename ).parent
+#convertor.type = :asp
+proper_filename = File.expand_path(filename).gsub('/', '\\')
+proper_filename = File.expand_path(filename) unless File.exists?( proper_filename )
+
+begin
+ print convertor.convert(File.read(filename), proper_filename).gsub(/\r/, '')
+
+ $seen.each do |dc, list|
+ if list.uniq.size > 1
+ $stderr.puts "#{dc}: #{list.uniq.inspect}"
+ end
+ end
+rescue Rasp::ScriptError => error
+ $stderr.puts "#{error.token.inspect}\n#{error.location}Microsoft VBScript compilation error: #{error.description}"
+ exit 1
+end
+
391 reformat.y
@@ -0,0 +1,391 @@
+class VbScriptRaccParser
+prechigh
+ left arg_paren
+ left '.'
+
+ # http://msdn.microsoft.com/library/en-us/dnasdj01/html/asp1200.asp
+ # and
+ # http://www.ronshardwebapps.com/tips/febtutorialoperators1.asp
+ # both claim that '^' comes before UMINUS, yet
+ # http://msdn.microsoft.com/library/en-us/script56/html/vsgrpoperatorprecedence.asp
+ # claims the opposite :/
+ #
+ # A manual test shows that UMINUS does actually bind more tightly
+ # than '^'.
+
+ nonassoc UMINUS UPLUS
+ left '^'
+ left '*' '/'
+ left '\\\\'
+ left MOD
+ left '+' '-'
+ left '&'
+
+ left COMPARISON '<>' '<' '>' '<=' '>=' IS
+
+ left NOT
+ left AND
+ left OR
+ left XOR
+ left EQV
+ left IMP
+
+ left '(' ')' ','
+
+ left '='
+
+ left TO IN STEP
+ left ELSEIF ELSE
+ left ':'
+ left newline fake_newline
+ left '%>'
+preclow
+options no_result_var
+
+#
+# TODO
+#
+
+#
+# * Re-order rules to provide some logical groupings
+#
+#
+# * With block
+#
+# * With operator
+#
+# * Arrays
+#
+# * Default method
+#
+# * Defining a default method
+#
+# * Set value on a method - Property Set/Let with params
+#
+# * Imp/Eqv operators
+#
+# * ASP environment - Request, Response, Server
+#
+# * VBScript environment - WScript
+#
+# * CreateObject for native implementation (dictionary, fso)
+#
+# * Randomize - Can I seed ruby's generator?
+#
+
+#
+# * Booleans happily cast to integers and back
+#
+
+rule
+ rootfile
+ : file
+ | html_block
+ opt_html
+ : {''}
+ | html_block
+ html_block
+ : opt_html directive opt_newline { val[0] << val[1] << val[2] }
+ | opt_html html opt_newline { val[0] << val[1] << val[2] }
+ | opt_html '<%' file '%>' opt_newline { val[0] << '<%' << val[2] << (val[2] =~ /\n\z/ ? '' : "\n") << '%>' << val[4] }
+ opt_newline
+ : /* nothing */ {''}
+ | opt_newline any_newline { val[0] + (val[1] || '') }
+ file
+ : {''}
+ | file filecontent { val[0] << val[1] }
+ filecontent
+ : statement
+ | class_def
+ | method_def
+ class_def
+ : CLASS identifier end_statement
+ class_body
+ END CLASS end_statement { "Class #{val[1]}#{NL val[2]}#{I val[3]}End Class#{val[6]}" }
+ class_body
+ : /* nothing */ {''}
+ | class_body class_body_item { val[0] << val[1] }
+ class_body_item
+ : class_member
+ | class_method_def
+ | class_property_def
+ | any_newline
+ class_member
+ : scope ident_def end_statement { "#{val[0]} #{val[1]}#{val[2]}" }
+ ident_def_list
+ : ident_def
+ | ident_def_list ',' ident_def { "#{val[0]}, #{val[2]}" }
+ ident_def
+ : identifier { val[0] }
+ | ident_def arg_paren ')' { "#{val[0]}()" }
+ | ident_def arg_paren integer ')' { "#{val[0]}(#{val[2]})" }
+ class_method_def
+ : scope method_def { "#{val[0]} #{val[1]}" }
+ | method_def
+ method_def
+ : sub_def
+ | function_def
+ class_property_def
+ : scope property_def { "#{val[0]} #{val[1]}" }
+ | property_def
+ scope
+ : PUBLIC { 'Public' }
+ | PRIVATE { 'Private' }
+ property_stmt
+ : PROPERTY GET { 'Property Get' }
+ | PROPERTY LET { 'Property Let' }
+ | PROPERTY SET { 'Property Set' }
+ property_def
+ : property_stmt identifier full_arg_def_list end_statement
+ method_body
+ END PROPERTY end_statement
+ { "#{val[0]} #{val[1]}#{Bracket val[2]}#{NL val[3]}#{I val[4]}End Property#{val[7]}" }
+ sub_def
+ : SUB identifier full_arg_def_list end_statement
+ method_body
+ END SUB end_statement
+ { "Sub #{val[1]}#{Bracket val[2]}#{NL val[3]}#{I val[4]}End Sub#{val[7]}" }
+ function_def
+ : FUNCTION identifier full_arg_def_list end_statement
+ method_body
+ END FUNCTION end_statement
+ { "Function #{val[1]}#{Bracket val[2]}#{NL val[3]}#{I val[4]}End Function#{val[7]}" }
+ full_arg_def_list
+ : arg_paren ')' {''}
+ | arg_paren arg_def_list ')' { val[1] }
+ | {''}
+ arg_def_list
+ : arg_def { val[0] }
+ | arg_def_list ',' arg_def { "#{val[0]}, #{val[2]}" }
+ arg_def
+ : identifier
+ | BYREF identifier { "ByRef #{val[1]}" }
+ | BYVAL identifier { "ByVal #{val[1]}" }
+ statements
+ : /* nothing */ {''}
+ | statements statement { val[0] << val[1] }
+ | statements '%>' html_values '<%' { val[0] << '%>' << val[2] << '<%' }
+ method_body
+ : statements
+ statement
+ : end_statement { val[0].gsub(/^ */, '') }
+ | simple_statement end_statement { val[0] + val[1] }
+ | full_if_block
+ | single_line_if
+ | do_loop
+ | for_next
+ | for_each
+ | select_block
+ | with_block
+ | EXPLICIT { "Option Explicit\n" }
+ html_values
+ : /* nothing */ {''}
+ | html_values html { val[0] << val[1] }
+ | html_values '<%=' expression '%>' { val[0] << '<%=' << val[2] << '%>' }
+ with_block
+ : WITH expression end_statement statements END WITH end_statement
+ { "With #{val[1]}#{NL val[2]}#{I val[3]}End With#{val[6]}" }
+ select_block
+ : SELECT CASE expression end_statement
+ case_or_case_else
+ END SELECT end_statement
+ { "Select Case #{val[2]}#{NL val[3]}#{I val[4]}End Select#{val[7]}" }
+ case_or_case_else
+ : optional_case case_block { val[0] << val[1] }
+ | optional_case CASE ELSE end_statement statements { val[0] << "Case Else#{NL val[3]}#{I val[4]}" }
+ case_block
+ : CASE expression_list end_statement statements { "Case #{val[1]}#{NL val[2]}#{I val[3]}" }
+ optional_case
+ : optional_case case_block { val[0] << val[1] }
+ | /* nothing */ {''}
+ optional_step
+ : STEP expression { " Step #{val[1]}" }
+ | /* nothing */ { '' }
+ for_each
+ : FOR EACH identifier IN expression end_statement statements NEXT end_statement
+ { "For Each #{val[2]} In #{val[4]}#{NL val[5]}#{I val[6]}Next#{val[8]}" }
+ do_loop
+ : DO end_statement statements LOOP end_statement
+ { "Do#{NL val[1]}#{I val[2]}Loop#{val[4]}" }
+ | DO WHILE expression end_statement statements LOOP end_statement
+ { "Do While #{val[2]}#{NL val[3]}#{I val[4]}Loop#{val[6]}" }
+ | DO end_statement statements LOOP WHILE expression end_statement
+ { "Do#{NL val[1]}#{I val[2]}Loop While #{val[5]}#{val[6]}" }
+ | DO UNTIL expression end_statement statements LOOP end_statement
+ { "Do Until #{val[2]}#{NL val[3]}#{I val[4]}Loop#{val[6]}" }
+ | DO end_statement statements LOOP UNTIL expression end_statement
+ { "Do#{NL val[1]}#{I val[2]}Loop Until #{val[5]}#{val[6]}" }
+ | WHILE expression end_statement statements WEND end_statement
+ { "While #{val[1]}#{NL val[2]}#{I val[3]}Wend#{val[5]}" }
+ for_next
+ : FOR identifier '=' expression TO expression optional_step end_statement statements NEXT end_statement
+ { "For #{val[1]} = #{val[3]} To #{val[5]}#{val[6]}#{NL val[7]}#{I val[8]}Next#{val[10]}" }
+ single_line_if
+ : IF expression THEN simple_statement_chain opt_comment any_newline
+ { a = "If #{val[1]} Then"; b = "#{val[3]}#{val[4]}\n"; (a.size + b.size) > 70 ? "#{a} _\n#{I b}" : "#{a} #{b}" }
+ | IF expression THEN single_line_if
+ { "If #{val[1]} Then _\n#{I val[3]}" }
+ | IF expression THEN simple_statement_chain ELSE single_line_if
+ { "If #{val[1]} Then #{val[3]} Else #{val[5]}" }
+ | IF expression THEN simple_statement_chain ELSE simple_statement_chain opt_comment any_newline
+ { "If #{val[1]} Then #{val[3]} Else #{val[5]}#{val[6]}\n" }
+ full_if_block
+ : IF expression THEN end_statement
+ statements
+ optional_else_or_elseif
+ END IF end_statement
+ { "If #{val[1]} Then#{NL val[3]}#{I val[4]}#{val[5]}End If#{val[8]}" }
+ optional_else_or_elseif
+ : ELSEIF expression THEN end_statement statements optional_else_or_elseif
+ { "ElseIf #{val[1]} Then#{NL val[3]}#{I val[4]}#{val[5]}" }
+ | else_block
+ | /* nothing */ {''}
+ else_block
+ : ELSE end_statement statements { "Else#{NL val[1]}#{I val[2]}" }
+ simple_statement_chain
+ : simple_statement_chain ':' simple_statement { "#{val[0]}: #{val[2]}" }
+ | simple_statement
+ simple_statement
+ : assignment
+ | objvalue
+ | const_assignment
+ | sub_call
+ | exit_statement
+ | on_error_statement
+ | DIM ident_def_list { "Dim #{val[1]}" }
+ | RANDOMIZE opt_expression { val[1] ? "Randomize #{val[1]}" : "Randomize" }
+ on_error_statement
+ : IGNORE_ERRORS { "On Error Resume Next" }
+ | THROW_ERRORS { "On Error Goto 0" }
+ exitable
+ : SUB { 'Sub' }
+ | FUNCTION { 'Function' }
+ | PROPERTY { 'Property' }
+ | DO { 'Do' }
+ | FOR { 'For' }
+ exit_statement
+ : EXIT exitable { "Exit #{val[1]}" }
+ assignment
+ : objvalue '=' expression { "#{val[0]} = #{val[2]}" }
+ | SET objvalue '=' expression { "Set #{val[1]} = #{val[3]}" }
+ const_assignment
+ : CONST identifier '=' literal { "Const #{val[1]} = #{val[3]}" }
+ | CONST identifier '=' '-' literal { "Const #{val[1]} = -#{val[4]}" }
+ objvalue
+ : objvalue arg_paren opt_expression_list ')' { "#{val[0]}#{Bracket val[2]}" }
+ | objvalue '.' objmember { "#{val[0]}.#{val[2]}" }
+ | with_dot objmember { ".#{val[1]}" }
+ | identifier
+ identifier
+ : ident { check_ident val[0] }
+ | '[' ident ']' { "[#{check_ident val[1]}]" }
+ objmember
+ : identifier { check_member val[0] }
+ opt_expression
+ : /* nothing */ { nil }
+ | expression
+ expression
+ : objexpression
+ | intrinsic_expression
+ # Note that this doesn't actually cover all intrinsic expressions...
+ # specifically, a variable reference is always treated as an
+ # objexpression (via objvalue).
+ intrinsic_expression
+ : literal
+
+ | '+' expression =UPLUS { val[1] }
+ | '-' expression =UMINUS { "-#{val[1]}" }
+ | expression '^' expression { "#{val[0]} ^ #{val[2]}" }
+ | expression '*' expression { "#{val[0]} * #{val[2]}" }
+ | expression '/' expression { "#{val[0]} / #{val[2]}" }
+ | expression '\\\\' expression { "#{val[0]} \\ #{val[2]}" }
+ | expression MOD expression { "#{val[0]} Mod #{val[2]}" }
+ | expression '+' expression { "#{val[0]} + #{val[2]}" }
+ | expression '-' expression { "#{val[0]} - #{val[2]}" }
+ | expression '&' expression { "#{val[0]} & #{val[2]}" }
+
+ | expression '=' expression =COMPARISON { "#{val[0]} = #{val[2]}" }
+ | expression '<>' expression { "#{val[0]} <> #{val[2]}" }
+ | expression '<=' expression { "#{val[0]} <= #{val[2]}" }
+ | expression '>=' expression { "#{val[0]} >= #{val[2]}" }
+ | expression '<' expression { "#{val[0]} < #{val[2]}" }
+ | expression '>' expression { "#{val[0]} > #{val[2]}" }
+
+ | NOT expression { "Not #{val[1]}" }
+ | expression AND expression { "#{val[0]} And #{val[2]}" }
+ | expression OR expression { "#{val[0]} Or #{val[2]}" }
+ | expression XOR expression { "#{val[0]} Xor #{val[2]}" }
+ | expression EQV expression { "#{val[0]} Eqv #{val[2]}" }
+ | expression IMP expression { "#{val[0]} Imp #{val[2]}" }
+
+ | objexpression IS objexpression { "#{val[0]} Is #{val[2]}" }
+
+ | '(' intrinsic_expression ')' { "(#{val[1]})" }
+ objexpression
+ : objvalue
+ | NEW identifier { "New #{val[1]}" }
+ | '(' objexpression ')' { "(#{val[1]})" }
+ literal
+ : integer { val[0].to_i.to_s }
+ | float { val[0].to_f.to_s }
+ | string { '"' + val[0].to_s.gsub('"', '""') + '"' }
+ | TRUE { 'True' }
+ | FALSE { 'False' }
+ string
+ : '\"' string_contents '\"' { val[1] }
+ | '\"' '\"' { '' }
+ string_contents
+ : string_atom
+ | string_contents string_atom { val[0] + val[1] }
+ string_atom
+ : escaped_atom { '"' }
+ | string_text
+ sub_call
+ : objvalue '.' objmember opt_expression_list { val[3] ? "#{val[0]}.#{val[2]} #{val[3]}" : "#{val[0]}.#{val[2]}" }
+ | identifier opt_expression_list { val[1] ? "#{val[0]} #{val[1]}" : val[0] }
+ | with_dot identifier opt_expression_list { val[2] ? ".#{val[1]} #{val[2]}" : ".#{val[1]}" }
+ | CALL function_call { "Call #{val[1]}" }
+ function_call
+ : identifier arg_paren opt_expression_list ')' { "#{val[0]}#{Bracket val[2]}" }
+ | identifier
+ any_newline
+ : newline { "\n" }
+ | fake_newline { '' }
+ end_statement
+ : ':' { ': ' }
+ | comment any_newline { " #{val[0]}#{val[1]}" }
+ | any_newline { val[0] }
+ opt_comment
+ : /* nothing */ { '' }
+ | comment { " #{val[0]}" }
+ opt_expression_list
+ : /* nothing */ { nil }
+ | expression_list
+ expression_list
+ : expression { val[0] }
+ | expression_list ',' expression { "#{val[0]}, #{val[2]}" }
+
+end
+
+---- inner
+def run_parse object, method
+ @yydebug = true
+
+ yyparse object, method
+end
+def on_error error_token_id, error_value, value_stack
+ $stderr.puts "on_error: " + [error_token_id, error_value.group, error_value, value_stack.size, value_stack.last(5)].inspect
+
+ errno = case token_to_str(error_token_id).to_sym
+ when :LOOP
+ :loop_without_do
+ when :TYPEOF, :reserved, :error
+ :syntax_error
+ else
+ :unknown
+ end
+ exception = Rasp::ScriptError.new( errno == :unknown ? :parse_error : errno, nil )
+ exception.token = error_value
+ exception.description = "Parse error on value \"#{error_value}\" (#{token_to_str error_token_id})" if errno == :unknown
+ raise exception
+end
168 run.rb
@@ -0,0 +1,168 @@
+
+require 'convertor'
+require 'parser'
+require 'rasp'
+require 'vbscript.tab'
+
+require 'parsedate'
+#require 'win32ole'
+require 'pathname'
+
+filename = ARGV[0] || 'samples/test2.vbs'
+
+Root = Rasp::Context.new
+
+class AspNativeObject
+ def run context; self; end
+end
+class Err < AspNativeObject
+ attr_accessor :number
+ attr_accessor :description
+ def clear
+ self.number = 0
+ self.description = ''
+ end
+ def load_from_ruby_exception error
+ self.number = error.number
+ self.description = error.description
+ end
+end
+class WScript < AspNativeObject
+ def echo string
+ puts string
+ end
+end
+class Object
+ def asp_default *a
+ self[*a]
+ end
+ def asp_default= *a
+ self.[]=(*a)
+ end
+end
+
+class AspSession < AspNativeObject
+ def initialize; @data = {}; end
+ def asp_default= key, value
+ @data[key] = value
+ end
+ def asp_default key; @data.key?(key) ? @data[key] : Empty.get; end
+end
+class AspRequest < AspNativeObject
+end
+class AspResponse < AspNativeObject
+ def write string
+ print string
+ end
+end
+class AspServer < AspNativeObject
+ #def create_object name
+ # WIN32OLE.new name
+ #end
+end
+class AspNil
+ def run; self; end
+ def nil?; true; end
+end
+class Empty < AspNil
+ def simple; self; end
+ def to_int; 0; end
+ alias :to_i :to_int
+ def to_str; ''; end
+ alias :to_s :to_str
+ def self.handle_as_zero( *sym )
+ sym.each do |s|
+ define_method( s ) { |*a| to_int.__send__( s, *a ) }
+ end
+ end
+ handle_as_zero :+, :-, :*, :/, :&, :|, :^, :%, :<, :>, :<=, :>=
+ def self.get; @@instance; end
+ @@instance = self.new
+end
+class Null < Empty
+ def self.get; @@instance; end
+ @@instance = self.new
+end
+class Nothing < AspNil
+ def self.get; @@instance; end
+ @@instance = self.new
+end
+
+# Common constants
+Root['vbCrLf'] = "\r\n"
+Root['vbCr'] = "\r"
+Root['vbLf'] = "\n"
+Root['vbNewline'] = "\r\n"
+Root['Empty'] = Empty.get
+Root['Null'] = Null.get
+Root['Nothing'] = Nothing.get
+Root['True'] = true
+Root['False'] = false
+Root['Err'] = Err.new
+
+# ASP
+Root['Response'] = AspResponse.new
+Root['Request'] = AspRequest.new
+Root['Server'] = AspServer.new
+Root['Session'] = AspSession.new
+
+# WSH
+Root['WScript'] = WScript.new
+
+Root.define_native_method( 'Rnd' ) { || rand }
+Root.define_native_method( 'CLng' ) { |v| v.to_i }
+Root.define_native_method( 'CStr' ) { |v| v.to_s }
+Root.define_native_method( 'CDbl' ) { |v| v.to_f }
+Root.define_native_method( 'CBool' ) { |v| v.to_b }
+Root.define_native_method( 'CDate' ) { |v| Time.local( *ParseDate.parsedate( v ) ) }
+
+Root.define_native_method( 'IsEmpty' ) { |v| v.is_a? Empty }
+Root.define_native_method( 'IsNull' ) { |v| v.is_a? Null }
+
+#Root.define_native_method( 'CreateObject' ) { |v| WIN32OLE.new( v ) }
+
+Root.all_constant!
+
+convertor = Syntax::Convertors::CodeTree.for_syntax 'vbscript'
+convertor.follow_includes = true
+convertor.follow_includes = false ## XXX
+convertor.virtual_root = Pathname.new( filename ).parent
+#convertor.type = :asp
+proper_filename = File.expand_path(filename).gsub('/', '\\')
+proper_filename = File.expand_path(filename) unless File.exists?( proper_filename )
+ast = nil
+if File.exists?( filename + 'c' )
+ data = nil
+ File.open( filename + 'c', 'r' ) do |file|
+ file.binmode
+ data = file.gets( nil )
+ end
+ ast = Marshal.load( data )
+end
+begin
+ if ast.nil?
+ ast = convertor.convert(File.read(filename), proper_filename)
+ File.open( filename + 'c', 'w' ) do |out|
+ out.binmode
+ out.write Marshal.dump( ast )
+ end
+ end
+rescue Rasp::ScriptError => error
+ puts "#{error.location}Microsoft VBScript compilation error: #{error.description}"
+else
+ require 'pp'; pp ast; exit ## XXX
+ begin
+ context = Rasp::Context.new(:parent_context => Root)
+ ast.compile context
+ rescue Rasp::ScriptError => error
+ puts "#{error.location}Microsoft VBScript compilation error: #{error.description}"
+ else
+ begin
+ ast.run context
+ rescue Rasp::ScriptError => error
+ puts "#{error.location}Microsoft VBScript runtime error: #{error.description}"
+ end
+ end
+end
+puts ""
+
20 t/class.vbs
@@ -0,0 +1,20 @@
+
+Class Foo
+ Public Sub DoStuff
+ Out Something
+ End Sub
+ Public Sub Out(s)
+ WScript.Echo s
+ End Sub
+ Public Function Something
+ Something = "Some return value"
+ End Function
+ Public Sub Class_Terminate
+ WScript.Echo "Terminating!"
+ End Sub
+End Class
+
+Dim o: Set o = New Foo
+
+o.DoStuff
+
18 t/math.vbs
@@ -0,0 +1,18 @@
+Option Explicit
+
+Dim i,j
+
+Const MIN = -32
+Const MAX = 32
+
+For i = MIN To MAX
+ For j = MIN To MAX
+ WScript.Echo i & " And " & j & " = " & (i And j)
+ WScript.Echo i & " Eqv " & j & " = " & (i Eqv j)
+ WScript.Echo i & " Imp " & j & " = " & (i Imp j)
+ WScript.Echo i & " Or " & j & " = " & (i Or j)
+ WScript.Echo i & " Xor " & j & " = " & (i Xor j)
+ Next
+ WScript.Echo "Not " & i & " = " & (Not i)
+Next
+
20 t/math2.vbs
@@ -0,0 +1,20 @@
+Option Explicit
+
+Dim i,j
+Dim a
+
+Const MIN = -32
+Const MAX = 32
+
+For i = MIN To MAX
+ For j = MIN To MAX
+ a = i & " And " & j & " = " & (i And j)
+ a = i & " Eqv " & j & " = " & (i Eqv j)
+ a = i & " Imp " & j & " = " & (i Imp j)
+ a = i & " Or " & j & " = " & (i Or j)
+ a = i & " Xor " & j & " = " & (i Xor j)
+ Next
+ a = "Not " & i & " = " & (Not i)
+Next
+Wscript.Echo ".."
+
4 t/meta/error.vbs
@@ -0,0 +1,4 @@
+Dim abb
+'Dim a, b, c, d, e, f, g, h, i, zx, abb
+Dim zx, abb
+Dim zz
1  t/meta/happy.vbs
@@ -0,0 +1 @@
+WScript.Echo "Whee!"
1  t/meta/loop.vbs
@@ -0,0 +1 @@
+Loop
4 t/meta/zero.vbs
@@ -0,0 +1,4 @@
+Dim a: a = 7
+Dim b: b = 0
+Dim c: c = b / a
+Dim d: d = a / c
5 t/perf/rand1.vbs
@@ -0,0 +1,5 @@
+
+Dim i, a
+For i = 1 To 100000
+ a = Rnd() * (127 - 32) + 32
+Next
8 t/perf/str.vbs
@@ -0,0 +1,8 @@
+
+Dim i,s
+s = "abcdefgh"
+For i = 1 To 27
+ s = s & s
+WScript.Echo i
+Next
+
32 t/samples/style.css
@@ -0,0 +1,32 @@
+.normal {}
+.comment { color: #070; font-style: italic; }
+.keyword { color: #A00; font-weight: bold; }
+.method { color: #077; }
+.class { color: #074; }
+.module { color: #050; }
+.punct { color: #447; font-weight: bold; }
+.symbol { color: #099; }
+.string { color: #944; }
+.char { color: #F07; }
+.ident { color: #004; }
+.constant { color: #07F; }
+.regex { color: #B66; }
+.number { color: #D55; }
+.attribute { color: #377; }
+.global { color: #3B7; }
+.expr { color: #227; }
+.asp_marker { color: black; background: yellow; font-weight: bold; }
+.operator { color: silver; }
+.builtin { color: orange; }
+
+.paren { color: green; }
+.expression .paren { color: red; }
+.expression .expression .paren { color: blue; }
+.expression .expression .expression .paren { color: orange; }
+.expression .expression .expression .expression .paren { color: purple; }
+.expression .expression .expression .expression .expression .paren { color: green; }
+.expression .expression .expression .expression .expression .expression .paren { color: red; }
+
+.operator_assignment { background: red; }
+.operator_equals { background: green; }
+
58 t/samples/test.vbs
@@ -0,0 +1,58 @@
+
+Dim a, s: s = "This is a ""string"""
+WScript.Echo s
+WScript.Echo a
+WScript.Echo ""
+
+' I hope you like it! :)
+
+'Session("foo") = "xyzzy"
+'wscript.echo "--"
+'wscript.echo session("bar")
+'wscript.echo "**"
+'wscript.echo session("foo")
+'wscript.echo "--"
+
+Sub frob
+ Dim a
+ a = 23: b = 17 + _
+ 2
+
+ WScript.Echo a
+ WScript.Echo b
+ WScript.Echo "-"
+ Exit Sub
+ WScript.Echo "***"
+End Sub
+
+Function foo(a, b, ByrEf z)
+ foo = a + b * z
+ Exit Function
+ foo = 99
+End Function
+
+a = 4
+b = 51
+
+WScript.Echo a
+WScript.Echo b
+WScript.Echo "-"
+
+Call frob
+
+'Dim x: x = foo
+'Dim x: x = foo(12)
+'Dim x: x = foo(12, 1, 1, 17)
+
+WScript.Echo a
+WScript.Echo b
+
+WScript.Echo "=="
+
+a = Empty
+b = -17.3
+
+WScript.Echo a & " | " & b & " | " & s
+
+WScript.Echo foo(12, 8, 3)
+
32 t/samples/test2.vbs
@@ -0,0 +1,32 @@
+Sub w(s)
+ 'Response.Write s & vbCrLf
+ WScript.Echo s
+End Sub
+Dim a
+'For a = 5 To -2 Step -1
+For a = -2 To 5
+ w ""
+ w "a = " & a
+ Select Case a
+ Case 1
+ w " Foo"
+ Case 2, 3
+ w " Bar"
+ Case 4
+ w " Quux"
+ Case Else
+ w " Baz"
+ End Select
+ If a > 2 Then
+ w " X-Foo"
+ Else
+ w " X-Bar"
+ End If
+
+ If a < 4 Then w " a less 4": w " lesslessless" Else w " a gte 4": w " gtgtgt"
+
+ If a > 4 Then w " a gt 4" Else If a > 2 Then w " a gt 2 but le 4" Else w " a le 2"
+
+ w ""
+Next
+
37 t/samples/test3.asp
@@ -0,0 +1,37 @@
+<%
+
+Const zx = 73
+
+Sub w(s)
+ Response.Write s & vbCrLf
+End Sub
+Dim a
+'For a = 5 To -2 Step -1
+For a = -2 To 5
+ w ""
+ w "a = " & a &