Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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
@matthewd 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 @@