twostroke 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/lib/twostroke/ast/case.rb +1 -1
  2. data/lib/twostroke/ast/function.rb +2 -2
  3. data/lib/twostroke/ast/label.rb +20 -0
  4. data/lib/twostroke/ast/switch.rb +1 -0
  5. data/lib/twostroke/ast/try.rb +3 -3
  6. data/lib/twostroke/ast/unary_operators.rb +1 -1
  7. data/lib/twostroke/ast.rb +1 -0
  8. data/lib/twostroke/compiler/javascript.rb +20 -2
  9. data/lib/twostroke/compiler/tsasm.rb +89 -35
  10. data/lib/twostroke/compiler.rb +1 -1
  11. data/lib/twostroke/error.rb +3 -0
  12. data/lib/twostroke/lexer.rb +3 -2
  13. data/lib/twostroke/parser.rb +309 -227
  14. data/lib/twostroke/runtime/lib/array.js +9 -3
  15. data/lib/twostroke/runtime/lib/array.rb +15 -7
  16. data/lib/twostroke/runtime/lib/console.rb +3 -2
  17. data/lib/twostroke/runtime/lib/error.rb +1 -1
  18. data/lib/twostroke/runtime/lib/etc.js +38 -0
  19. data/lib/twostroke/runtime/lib/etc.rb +29 -0
  20. data/lib/twostroke/runtime/lib/math.rb +31 -0
  21. data/lib/twostroke/runtime/lib/number.rb +7 -1
  22. data/lib/twostroke/runtime/lib/object.rb +1 -1
  23. data/lib/twostroke/runtime/lib/regexp.rb +1 -0
  24. data/lib/twostroke/runtime/lib/string.rb +62 -22
  25. data/lib/twostroke/runtime/scope.rb +23 -2
  26. data/lib/twostroke/runtime/types/array.rb +5 -0
  27. data/lib/twostroke/runtime/types/function.rb +5 -2
  28. data/lib/twostroke/runtime/types/object.rb +3 -4
  29. data/lib/twostroke/runtime/types/regexp.rb +45 -2
  30. data/lib/twostroke/runtime/types.rb +1 -1
  31. data/lib/twostroke/runtime/vm.rb +2 -2
  32. data/lib/twostroke/runtime/vm_frame.rb +47 -8
  33. data/lib/twostroke/tokens.rb +32 -16
  34. metadata +5 -5
  35. data/lib/twostroke/ast/delete.rb +0 -15
  36. data/lib/twostroke/ast/unsorted_binop.rb +0 -97
  37. data/lib/twostroke/runtime/lib/function.js +0 -0
@@ -21,13 +21,19 @@ Array.prototype.reverse = function() {
21
21
  return this;
22
22
  };
23
23
 
24
- Array.prototype.concat = function(other) {
24
+ Array.prototype.concat = function() {
25
25
  var x = [];
26
26
  Array.prototype.slice.call(this).forEach(function(el) {
27
27
  x.push(el);
28
28
  });
29
- Array.prototype.slice.call(other).forEach(function(el) {
30
- x.push(el);
29
+ Array.prototype.slice.call(arguments).forEach(function(obj) {
30
+ if(obj instanceof Array) {
31
+ obj.forEach(function(el) {
32
+ x.push(el);
33
+ });
34
+ } else {
35
+ x.push(obj);
36
+ }
31
37
  });
32
38
  return x;
33
39
  };
@@ -50,6 +50,20 @@ module Twostroke::Runtime
50
50
  Types::Array.new this.generic_items[begin_index.number.to_i...end_index.number.to_i]
51
51
  end
52
52
  }, nil, "slice", [])
53
+ # Array.prototype.splice
54
+ proto.proto_put "splice", Types::Function.new(->(scope, this, args) {
55
+ Lib.throw_type_error "Array.prototype.splice is not generic" unless this.is_a? Types::Array
56
+ idx = Types.to_uint32(args[0] || Types::Undefined.new)
57
+ count = args[1] && Types.to_uint32(args[1])
58
+ if count and count >= 0
59
+ retn = this.items[idx...(idx + count)]
60
+ this.items[idx...(idx + count)] = args.drop(2)
61
+ else
62
+ retn = this.items[idx..-1]
63
+ this.items[idx..-1] = args.drop(2)
64
+ end
65
+ Types::Array.new retn
66
+ }, nil, "splice", [])
53
67
  # Array.prototype.sort
54
68
  proto.proto_put "sort", Types::Function.new(->(scope, this, args) {
55
69
  Lib.throw_type_error "Array.prototype.sort is not generic" unless this.is_a? Types::Array
@@ -59,13 +73,7 @@ module Twostroke::Runtime
59
73
  this
60
74
  }, nil, "sort", [])
61
75
  # Array.prototype.length
62
- proto.define_own_property "length", get: ->(this) { Types::Number.new this.items.size }, set: ->(this,val) do
63
- Lib.throw_type_error "Array.prototype.length is not generic" unless this.is_a? Types::Array
64
- len = Types.to_number(val)
65
- Lib.throw_range_error "invalid array length" if len.nan? || len.number < 0 || (len.number % 1) != 0
66
- this.items = this.items[0...len.number.to_i]
67
- len
68
- end, writable: true
76
+ proto.define_own_property "length", get: ->(this) { Types::Number.new this.items.size }
69
77
  ary.proto_put "prototype", proto
70
78
  end
71
79
  end
@@ -1,12 +1,13 @@
1
1
  module Twostroke::Runtime
2
2
  Lib.register do |scope|
3
- log = Types::Function.new(->(scope, this, args) { puts args.map { |o| Types.to_string(o).string }.join(" ") }, nil, "log", ["string"])
3
+ log = Types::Function.new(->(scope, this, args) { print args.map { |o| Types.to_string(o).string }.join(" ") + "\n" }, nil, "log", ["string"])
4
4
 
5
5
  console = Types::Object.new
6
6
  ["log", "info", "warn", "error"].each do |m|
7
7
  console.proto_put m, log
8
8
  end
9
- console.proto_put "_gets", Types::Function.new(->(scope, this, args) { Types::String.new gets }, nil, "_gets", [])
9
+ console.proto_put "_gets", Types::Function.new(->(scope, this, args) { Types::String.new STDIN.gets }, nil, "_gets", [])
10
+ console.proto_put "_readAll", Types::Function.new(->(scope, this, args) { Types::String.new STDIN.read }, nil, "_readAll", [])
10
11
  console.proto_put "_print", Types::Function.new(->(scope, this, args) { print args.map { |o| Types.to_string(o).string }.join(" ") }, nil, "_print", ["string"])
11
12
  scope.set_var "console", console
12
13
  end
@@ -12,7 +12,7 @@ module Twostroke::Runtime
12
12
  }, nil, "toString", [])
13
13
  error.proto_put "prototype", error.prototype
14
14
 
15
- ["Eval", "Range", "Reference", "Syntax", "Type", "URI"].each do |e|
15
+ ["Eval", "Range", "Reference", "Syntax", "Type", "URI", "_Interrupt"].each do |e|
16
16
  obj = Types::Function.new(->(scope, this, args) {
17
17
  this.proto_put "name", Types::String.new("#{e}Error")
18
18
  this.proto_put "message", (args[0] || Types::Undefined.new)
@@ -0,0 +1,38 @@
1
+ JSON = {
2
+ stringify: function(obj) {
3
+ switch(typeof obj) {
4
+ case "string":
5
+ return '"' + obj.replace(/["\\]/g, function(x) { return "\\" + x; }).replace(/[\x00-\x1F]/g, function(x) {
6
+ var s = x.charCodeAt(0).toString(16);
7
+ while(s.length < 4) s = "0" + s;
8
+ return "\\u" + s;
9
+ }) + '"';
10
+ case "number":
11
+ case "boolean":
12
+ return obj.toString();
13
+ case "undefined":
14
+ return "null";
15
+ case "object":
16
+ if(obj === null) {
17
+ return "null";
18
+ } else if(obj instanceof Array) {
19
+ return "[" + obj.map(function(x) { return JSON.stringify(x); }).filter(function(o) { return o !== undefined; }).join(",") + "]";
20
+ } else {
21
+ var keys = [];
22
+ for(var k in obj) {
23
+ if(obj.hasOwnProperty(k)) {
24
+ keys.push(k);
25
+ }
26
+ }
27
+ return "{" + keys.map(function(k) {
28
+ var v = JSON.stringify(obj[k]);
29
+ if(v) {
30
+ return '"' + k + '":' + JSON.stringify(obj[k]);
31
+ }
32
+ }).filter(function(o) { return o !== undefined; }).join(",") + "}";
33
+ }
34
+ default:
35
+ return undefined;
36
+ }
37
+ }
38
+ };
@@ -0,0 +1,29 @@
1
+ module Twostroke::Runtime
2
+ Lib.register do |scope|
3
+ evaled = 0
4
+ eval = Types::Function.new(->(_scope, this, args) {
5
+ src = Types.to_string(args[0] || Types::Undefined.new).string + ";"
6
+
7
+ begin
8
+ parser = Twostroke::Parser.new Twostroke::Lexer.new src
9
+ parser.parse
10
+
11
+ evaled += 1
12
+ compiler = Twostroke::Compiler::TSASM.new parser.statements, "evaled_#{evaled}_"
13
+ compiler.compile
14
+ rescue Twostroke::SyntaxError => e
15
+ Lib.throw_syntax_error e.to_s
16
+ end
17
+
18
+ vm = scope.global_scope.vm
19
+ compiler.bytecode.each do |k,v|
20
+ vm.bytecode[k] = v
21
+ end
22
+
23
+ vm.bytecode[:"evaled_#{evaled}_main"][-2] = [:ret]
24
+ vm.execute :"evaled_#{evaled}_main", _scope, this
25
+ }, nil, "eval", [])
26
+ eval.inherits_caller_this = true
27
+ scope.set_var "eval", eval
28
+ end
29
+ end
@@ -6,6 +6,31 @@ module Twostroke::Runtime
6
6
  scope.set_var "Infinity", Types::Number.new(Float::INFINITY)
7
7
  scope.set_var "NaN", Types::Number.new(Float::NAN)
8
8
 
9
+ scope.set_var "isNaN", Types::Function.new(->(scope, this, args) {
10
+ Types::Boolean.new(args[0].is_a?(Types::Number) && args[0].nan?)
11
+ }, nil, "isNaN", [])
12
+
13
+ scope.set_var "parseInt", Types::Function.new(->(scope, this, args) {
14
+ str = Types.to_string(args[0] || Undefined.new).string.gsub(/\A\s+/,"")
15
+ unless args[1] and (radix = Types.to_uint32(args[1])) != 0
16
+ case str
17
+ when /\A0x/i; radix = 16
18
+ when /\A0/i; radix = 8
19
+ else; radix = 10
20
+ end
21
+ end
22
+ if radix < 2 or radix > 36
23
+ Types::Number.new(Float::NAN)
24
+ else
25
+ begin
26
+ Integer(str[0], radix) # ensure the first character can be converted
27
+ Types::Number.new str.to_i radix
28
+ rescue
29
+ Types::Number.new(Float::NAN)
30
+ end
31
+ end
32
+ }, nil, "parseInt", [])
33
+
9
34
  # one argument functions
10
35
  %w(sqrt sin cos tan).each do |method|
11
36
  obj.proto_put method, Types::Function.new(->(scope, this, args) {
@@ -13,6 +38,8 @@ module Twostroke::Runtime
13
38
  }, nil, method, [])
14
39
  end
15
40
 
41
+ obj.proto_put "PI", Types::Number.new(Math::PI)
42
+
16
43
  obj.proto_put "random", Types::Function.new(->(scope, this, args) {
17
44
  Types::Number.new rand
18
45
  }, nil, "random", [])
@@ -24,6 +51,10 @@ module Twostroke::Runtime
24
51
  obj.proto_put "ceil", Types::Function.new(->(scope, this, args) {
25
52
  Types::Number.new Types.to_number(args[0] || Undefined.new).number.ceil
26
53
  }, nil, "ceil", [])
54
+
55
+ obj.proto_put "abs", Types::Function.new(->(scope, this, args) {
56
+ Types::Number.new Types.to_number(args[0] || Undefined.new).number.abs
57
+ }, nil, "floor", [])
27
58
 
28
59
  obj.proto_put "max", Types::Function.new(->(scope, this, args) {
29
60
  Types::Number.new [-Float::INFINITY, *args.map { |a| Types.to_number(a).number }].max
@@ -6,7 +6,13 @@ module Twostroke::Runtime
6
6
  proto = Types::Object.new
7
7
  proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
8
8
  if this.is_a?(Types::NumberObject)
9
- Types::String.new(this.number.to_s)
9
+ base = args[0] ? Types.to_uint32(args[0]) : 10
10
+ Lib.throw_range_error "toString() radix argument must be between 2 and 36" if base < 2 or base > 36
11
+ if this.number.is_a? Fixnum or this.number.is_a? Bignum
12
+ Types::String.new this.number.to_s base
13
+ else
14
+ Types::String.new this.number.to_s
15
+ end
10
16
  else
11
17
  Lib.throw_type_error "Number.prototype.toString is not generic"
12
18
  end
@@ -41,7 +41,7 @@ module Twostroke::Runtime
41
41
  prop = Types.to_string(args[0] || Types::Undefined.new).string
42
42
  if this.has_accessor(prop)
43
43
  Types::Boolean.new this.accessors[prop][:enumerable]
44
- elsif this.has_property
44
+ elsif this.has_property(prop)
45
45
  Types::Boolean.new true
46
46
  else
47
47
  Types::Boolean.new false
@@ -23,6 +23,7 @@ module Twostroke::Runtime
23
23
  Lib.throw_type_error "RegExp.prototype.test is not generic" unless this.is_a?(Types::RegExp)
24
24
  Types::Boolean.new((Types.to_string(args[0] || Undefined.new).string =~ this.regexp) != nil)
25
25
  }, nil, "test", [])
26
+ proto.proto_put "exec", Types::Function.new(Types::RegExp.method(:exec), nil, "exec", [])
26
27
  regexp.proto_put "prototype", proto
27
28
  end
28
29
  end
@@ -52,31 +52,71 @@ module Twostroke::Runtime
52
52
 
53
53
  Types::String.new retn
54
54
  }, nil, "replace", [])
55
- =begin
56
- find = args[0] || Types::Undefined.new
57
- find = Types.to_string(find).string unless find.is_a?(Types::RegExp)
58
- replace = args[1] || Types::Undefined.new
59
- replace = Types.to_string(replace).string unless replace.respond_to? :call
60
- Types::String.new(if find.is_a?(String)
61
- if replace.is_a?(String)
62
- s.sub find, replace
63
- else
64
- s.sub(find) { |m| Types.to_string(replace.call(scope, nil, [Types::String.new(m), Types::Number.new(s.index m), sobj])).string }
65
- end
55
+ # String.prototype.slice
56
+ proto.proto_put "slice", Types::Function.new(->(scope, this, args) {
57
+ sobj = Types.to_string(this)
58
+ s = sobj.string
59
+ if args[0].nil?
60
+ sobj
66
61
  else
67
- m = s.method(find.global ? :gsub : :sub)
68
- if replace.is_a?(String)
69
- m.(find.regexp, replace)
62
+ start = Types.to_int32 args[0]
63
+ if args[1]
64
+ fin = Types.to_int32 args[1]
65
+ Types::String.new s[start...fin]
70
66
  else
71
- offset = 0
72
- m.(find.regexp) do |m|
73
- idx = s.index m, offset
74
- offset = idx + m.size
75
- Types.to_string(replace.call(scope, nil, [Types::String.new(m), Types::Number.new(idx), sobj])).string
76
- end
67
+ Types::String.new s[start..-1]
77
68
  end
78
- end)
79
- =end
69
+ end
70
+ }, nil, "slice", [])
71
+ # String.prototype.indexOf
72
+ proto.proto_put "indexOf", Types::Function.new(->(scope, this, args) {
73
+ idx = args[1] ? Types.to_int32(args[1]) : 0
74
+ Types::Number.new(Types.to_string(this).string.index(Types.to_string(args[0] || Types::Undefined.new).string, idx) || -1)
75
+ }, nil, "indexOf", [])
76
+ # String.prototype.lastIndexOf
77
+ proto.proto_put "lastIndexOf", Types::Function.new(->(scope, this, args) {
78
+ idx = args[1] ? Types.to_int32(args[1]) : -1
79
+ Types::Number.new(Types.to_string(this).string.rindex(Types.to_string(args[0] || Types::Undefined.new).string, idx) || -1)
80
+ }, nil, "lastIndexOf", [])
81
+ # String.prototype.charAt
82
+ proto.proto_put "charAt", Types::Function.new(->(scope, this, args) {
83
+ idx = args[0] ? Types.to_int32(args[0]) : 0
84
+ str = Types.to_string(this).string
85
+ if idx < 0 or idx >= str.length
86
+ Types::String.new ""
87
+ else
88
+ Types::String.new str[idx]
89
+ end
90
+ }, nil, "charAt", [])
91
+ # String.prototype.charAt
92
+ proto.proto_put "charCodeAt", Types::Function.new(->(scope, this, args) {
93
+ idx = args[0] ? Types.to_int32(args[0]) : 0
94
+ str = Types.to_string(this).string
95
+ if idx < 0 or idx >= str.length
96
+ Types::Number.new Float::NAN
97
+ else
98
+ Types::Number.new str[idx].ord
99
+ end
100
+ }, nil, "charCodeAt", [])
101
+ # String.prototype.match
102
+ proto.proto_put "match", Types::Function.new(->(scope, this, args) {
103
+ re = args[0] || Types::Undefined.new
104
+ re = Types::RegExp.constructor_function.(nil, nil, re) unless re.is_a?(Types::RegExp)
105
+ unless re.global
106
+ # same as re.exec(str) in this case
107
+ Types::RegExp.exec(nil, re, [this])
108
+ else
109
+ Types::RegExp.all_matches(nil, re, [this])
110
+ end
111
+ }, nil, "match", [])
112
+ # String.prototype.toUpperCase
113
+ proto.proto_put "toUpperCase", Types::Function.new(->(scope, this, args) {
114
+ Types::String.new Types.to_string(this).string.upcase
115
+ }, nil, "toUpperCase", [])
116
+ # String.prototype.toUpperCase
117
+ proto.proto_put "toLowerCase", Types::Function.new(->(scope, this, args) {
118
+ Types::String.new Types.to_string(this).string.downcase
119
+ }, nil, "toLowerCase", [])
80
120
  obj.proto_put "prototype", proto
81
121
 
82
122
  obj.proto_put "fromCharCode", Types::Function.new(->(scope, this, args) {
@@ -31,6 +31,14 @@ module Twostroke::Runtime
31
31
  @locals[var] = Types::Undefined.new
32
32
  end
33
33
 
34
+ def delete(var)
35
+ if has_var var
36
+ @locals.delete var
37
+ else
38
+ parent.delete var
39
+ end
40
+ end
41
+
34
42
  def close
35
43
  Scope.new self
36
44
  end
@@ -59,11 +67,11 @@ module Twostroke::Runtime
59
67
  if object.has_property var.to_s
60
68
  object.put var.to_s, value
61
69
  else
62
- parent.set_var var.to_s, value
70
+ parent.set_var var, value
63
71
  end
64
72
  end
65
73
 
66
- def has_var
74
+ def has_var(var)
67
75
  object.has_property(var.to_s) || parent.has_var(var)
68
76
  end
69
77
 
@@ -71,6 +79,14 @@ module Twostroke::Runtime
71
79
  parent.declare var
72
80
  end
73
81
 
82
+ def delete(var)
83
+ if has_var var
84
+ object.delete var.to_s
85
+ else
86
+ parent.delete var
87
+ end
88
+ end
89
+
74
90
  def close
75
91
  Scope.new self
76
92
  end
@@ -103,6 +119,11 @@ module Twostroke::Runtime
103
119
  end
104
120
 
105
121
  def declare(var)
122
+ @root_object.put var.to_s, Types::Undefined.new
123
+ end
124
+
125
+ def delete(var)
126
+ @root_object.delete var.to_s
106
127
  end
107
128
 
108
129
  def close
@@ -19,6 +19,11 @@ module Twostroke::Runtime
19
19
  @prototype = Array.constructor_function.get("prototype")
20
20
  super()
21
21
  @items = items
22
+ define_own_property "length", get: ->(this) { Types::Number.new this.items.size }, set: ->(this,val) do
23
+ Lib.throw_type_error "Array.prototype.length is not generic" unless this.is_a? Types::Array
24
+ len = Types.to_uint32(val)
25
+ this.items = this.items[0...len]
26
+ end, writable: true, enumerable: false
22
27
  end
23
28
 
24
29
  def length
@@ -33,6 +33,7 @@ module Twostroke::Runtime::Types
33
33
  end
34
34
 
35
35
  attr_reader :arguments, :name, :source, :function
36
+ attr_accessor :inherits_caller_this
36
37
  def initialize(function, source, name, arguments)
37
38
  @function = function
38
39
  @source = source
@@ -44,11 +45,13 @@ module Twostroke::Runtime::Types
44
45
  @_class = nil unless defined?(@@constructor_function)
45
46
  super()
46
47
  @prototype = nil
47
- proto_put "prototype", Object.new
48
+ proto = Object.new
49
+ proto.construct _class: self
50
+ put "prototype", proto
48
51
  end
49
52
 
50
53
  def prototype
51
- @prototype ||= Function.constructor_function.get("prototype")
54
+ @prototype ||= Function.constructor_function.get("prototype") if Function.constructor_function
52
55
  end
53
56
 
54
57
  def has_instance(obj)
@@ -2,7 +2,6 @@ module Twostroke::Runtime::Types
2
2
  class Object < Value
3
3
  attr_accessor :_class, :prototype
4
4
  attr_reader :accessors, :properties, :extensible, :data
5
- private :accessors, :properties
6
5
 
7
6
  def initialize
8
7
  @extensible = true
@@ -49,7 +48,7 @@ module Twostroke::Runtime::Types
49
48
  instance_variable_set "@#{k}", v
50
49
  end
51
50
  end
52
- yield
51
+ yield if block_given?
53
52
  @constructing = false
54
53
  end
55
54
 
@@ -90,8 +89,8 @@ module Twostroke::Runtime::Types
90
89
  accessors[prop][:set].(this, value) if accessors[prop][:set] && accessors[prop][:writable]
91
90
  elsif properties.has_key? prop
92
91
  properties[prop] = value
93
- #elsif prototype && prototype.is_a?(Object) && prototype.has_accessor(prop)
94
- # prototype.put prop, value, this
92
+ # elsif prototype && prototype.is_a?(Object) && prototype.has_accessor(prop)
93
+ # prototype.put prop, value, this
95
94
  else
96
95
  properties[prop] = value
97
96
  end
@@ -3,7 +3,7 @@ module Twostroke::Runtime::Types
3
3
  def self.constructor_function
4
4
  @@constructor_function ||=
5
5
  Function.new(->(scope, this, args) {
6
- RegExp.new Twostroke::Runtime::Types.to_string(args[0] || Undefined.new).string, args[1] && Twostroke::Runtime::Types.to_string(args[1]).string
6
+ RegExp.new((args[0] && !args[0].is_a?(Undefined)) ? Twostroke::Runtime::Types.to_string(args[0]).string : "", args[1] && Twostroke::Runtime::Types.to_string(args[1]).string)
7
7
  }, nil, "RegExp", [])
8
8
  end
9
9
 
@@ -18,7 +18,7 @@ module Twostroke::Runtime::Types
18
18
  else; 0
19
19
  end
20
20
  end
21
- @regexp = Regexp.new regexp_source, opts
21
+ @regexp = Regexp.new RegExp.to_ruby_regexp(regexp_source), opts
22
22
  @global = options.include? "g"
23
23
  @prototype = RegExp.constructor_function.get("prototype")
24
24
  super()
@@ -27,5 +27,48 @@ module Twostroke::Runtime::Types
27
27
  def primitive_value
28
28
  String.new(regexp.inspect + (@global ? "g" : ""))
29
29
  end
30
+
31
+ def self.to_ruby_regexp(src)
32
+ src.
33
+
34
+ # javascript's ^$ match the start and end of the entire string
35
+ # ruby's ^$ are line-based, so convert to \A and \z
36
+ gsub(/([^\[]|\A)\^/,"\\1\\A").gsub("$","\\z").
37
+
38
+ # javascript supports \cA through \cZ for control characters
39
+ gsub(/\\c[a-z]/i) { |m| (m.last.downcase.ord - 'a'.ord).chr }
40
+ end
41
+
42
+ def self.exec(scope, this, args)
43
+ re = this.is_a?(RegExp) ? this : constructor_function.(nil, nil, this)
44
+ str = Twostroke::Runtime::Types.to_string(args[0] || Twostroke::Runtime::Types::Undefined.new).string
45
+ if md = re.regexp.match(str)
46
+ result = Twostroke::Runtime::Types::Array.new md.to_a.map { |s| s ? Twostroke::Runtime::Types::String.new(s) : Twostroke::Runtime::Types::Undefined.new }
47
+ result.put "index", Twostroke::Runtime::Types::Number.new(md.offset(0).first)
48
+ result.put "input", Twostroke::Runtime::Types::String.new(str)
49
+ result
50
+ else
51
+ Twostroke::Runtime::Types::Null.new
52
+ end
53
+ end
54
+
55
+ def self.all_matches(scope, this, args)
56
+ re = this.is_a?(RegExp) ? this : constructor_function.(nil, nil, this)
57
+ str = Twostroke::Runtime::Types.to_string(args[0] || Twostroke::Runtime::Types::Undefined.new).string
58
+ arr = []
59
+ idx = 0
60
+ while md = re.regexp.match(str, idx)
61
+ arr << Twostroke::Runtime::Types::String.new(md[0])
62
+ idx = md.offset(0).last
63
+ end
64
+ if arr.any?
65
+ result = Twostroke::Runtime::Types::Array.new arr
66
+ result.put "index", Twostroke::Runtime::Types::Number.new(re.regexp.match(str).offset(0).first)
67
+ result.put "input", Twostroke::Runtime::Types::String.new(str)
68
+ result
69
+ else
70
+ Twostroke::Runtime::Types::Null.new
71
+ end
72
+ end
30
73
  end
31
74
  end
@@ -44,7 +44,7 @@ module Twostroke::Runtime::Types
44
44
  0
45
45
  else
46
46
  int32 = num.number.to_i & 0xffff_ffff
47
- int32 -= 2 ** 31 if int32 >= 2 ** 31
47
+ int32 -= 2 ** 32 if int32 >= 2 ** 31
48
48
  int32
49
49
  end
50
50
  end
@@ -9,8 +9,8 @@ module Twostroke::Runtime
9
9
  @lib = {}
10
10
  end
11
11
 
12
- def execute(section = :main, scope = nil)
13
- Frame.new(self, section).execute scope
12
+ def execute(section = :main, scope = nil, this = nil)
13
+ Frame.new(self, section).execute scope, this
14
14
  end
15
15
 
16
16
  def throw_error(type, message)