twostroke 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|