twostroke 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/twostroke/ast/case.rb +1 -1
- data/lib/twostroke/ast/function.rb +2 -2
- data/lib/twostroke/ast/label.rb +20 -0
- data/lib/twostroke/ast/switch.rb +1 -0
- data/lib/twostroke/ast/try.rb +3 -3
- data/lib/twostroke/ast/unary_operators.rb +1 -1
- data/lib/twostroke/ast.rb +1 -0
- data/lib/twostroke/compiler/javascript.rb +20 -2
- data/lib/twostroke/compiler/tsasm.rb +89 -35
- data/lib/twostroke/compiler.rb +1 -1
- data/lib/twostroke/error.rb +3 -0
- data/lib/twostroke/lexer.rb +3 -2
- data/lib/twostroke/parser.rb +309 -227
- data/lib/twostroke/runtime/lib/array.js +9 -3
- data/lib/twostroke/runtime/lib/array.rb +15 -7
- data/lib/twostroke/runtime/lib/console.rb +3 -2
- data/lib/twostroke/runtime/lib/error.rb +1 -1
- data/lib/twostroke/runtime/lib/etc.js +38 -0
- data/lib/twostroke/runtime/lib/etc.rb +29 -0
- data/lib/twostroke/runtime/lib/math.rb +31 -0
- data/lib/twostroke/runtime/lib/number.rb +7 -1
- data/lib/twostroke/runtime/lib/object.rb +1 -1
- data/lib/twostroke/runtime/lib/regexp.rb +1 -0
- data/lib/twostroke/runtime/lib/string.rb +62 -22
- data/lib/twostroke/runtime/scope.rb +23 -2
- data/lib/twostroke/runtime/types/array.rb +5 -0
- data/lib/twostroke/runtime/types/function.rb +5 -2
- data/lib/twostroke/runtime/types/object.rb +3 -4
- data/lib/twostroke/runtime/types/regexp.rb +45 -2
- data/lib/twostroke/runtime/types.rb +1 -1
- data/lib/twostroke/runtime/vm.rb +2 -2
- data/lib/twostroke/runtime/vm_frame.rb +47 -8
- data/lib/twostroke/tokens.rb +32 -16
- metadata +5 -5
- data/lib/twostroke/ast/delete.rb +0 -15
- data/lib/twostroke/ast/unsorted_binop.rb +0 -97
- 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(
|
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(
|
30
|
-
|
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 }
|
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) {
|
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
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
68
|
-
if
|
69
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
94
|
-
|
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]
|
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
|
data/lib/twostroke/runtime/vm.rb
CHANGED
@@ -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)
|