twostroke 0.2.2 → 0.2.3
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.rb +2 -1
- data/lib/twostroke/ast/break.rb +2 -0
- data/lib/twostroke/ast/continue.rb +2 -0
- data/lib/twostroke/compiler/tsasm.rb +45 -18
- data/lib/twostroke/context.rb +58 -0
- data/lib/twostroke/context/object_proxy.rb +42 -0
- data/lib/twostroke/lexer.rb +10 -2
- data/lib/twostroke/parser.rb +36 -8
- data/lib/twostroke/runtime/lib.rb +3 -1
- data/lib/twostroke/runtime/lib/boolean.rb +4 -10
- data/lib/twostroke/runtime/lib/console.rb +1 -1
- data/lib/twostroke/runtime/lib/date.rb +18 -4
- data/lib/twostroke/runtime/lib/etc.rb +1 -15
- data/lib/twostroke/runtime/lib/function.rb +2 -8
- data/lib/twostroke/runtime/lib/math.rb +14 -2
- data/lib/twostroke/runtime/lib/number.js +3 -0
- data/lib/twostroke/runtime/lib/number.rb +12 -5
- data/lib/twostroke/runtime/lib/object.rb +4 -4
- data/lib/twostroke/runtime/lib/regexp.rb +2 -2
- data/lib/twostroke/runtime/scope.rb +1 -5
- data/lib/twostroke/runtime/types.rb +17 -2
- data/lib/twostroke/runtime/types/array.rb +4 -0
- data/lib/twostroke/runtime/types/boolean.rb +5 -1
- data/lib/twostroke/runtime/types/boolean_object.rb +2 -2
- data/lib/twostroke/runtime/types/function.rb +4 -4
- data/lib/twostroke/runtime/types/number.rb +8 -0
- data/lib/twostroke/runtime/types/number_object.rb +2 -2
- data/lib/twostroke/runtime/types/object.rb +5 -7
- data/lib/twostroke/runtime/types/regexp.rb +3 -3
- data/lib/twostroke/runtime/types/string.rb +4 -0
- data/lib/twostroke/runtime/types/string_object.rb +7 -4
- data/lib/twostroke/runtime/types/value.rb +4 -4
- data/lib/twostroke/runtime/vm.rb +12 -0
- data/lib/twostroke/runtime/vm_frame.rb +11 -19
- data/lib/twostroke/tokens.rb +2 -2
- metadata +5 -3
- data/lib/twostroke/compiler/javascript.rb +0 -414
@@ -3,25 +3,11 @@ module Twostroke::Runtime
|
|
3
3
|
evaled = 0
|
4
4
|
eval = Types::Function.new(->(_scope, this, args) {
|
5
5
|
src = Types.to_string(args[0] || Types::Undefined.new).string + ";"
|
6
|
-
|
7
6
|
begin
|
8
|
-
|
9
|
-
parser.parse
|
10
|
-
|
11
|
-
evaled += 1
|
12
|
-
compiler = Twostroke::Compiler::TSASM.new parser.statements, "evaled_#{evaled}_"
|
13
|
-
compiler.compile
|
7
|
+
scope.global_scope.vm.eval src, _scope, this
|
14
8
|
rescue Twostroke::SyntaxError => e
|
15
9
|
Lib.throw_syntax_error e.to_s
|
16
10
|
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
11
|
}, nil, "eval", [])
|
26
12
|
eval.inherits_caller_this = true
|
27
13
|
scope.set_var "eval", eval
|
@@ -4,13 +4,6 @@ module Twostroke::Runtime
|
|
4
4
|
scope.set_var "Function", obj
|
5
5
|
|
6
6
|
proto = Types::Object.new
|
7
|
-
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
8
|
-
if this.is_a?(Types::Function)
|
9
|
-
this.primitive_value
|
10
|
-
else
|
11
|
-
Lib.throw_type_error "Function.prototype.toString is not generic"
|
12
|
-
end
|
13
|
-
}, nil, "toString", [])
|
14
7
|
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this }, nil, "valueOf", [])
|
15
8
|
proto.define_own_property "arity", get: ->(this) { this.arguments.size }, writable: false
|
16
9
|
proto.define_own_property "length", get: ->(this) { this.arguments.size }, writable: false
|
@@ -38,7 +31,8 @@ module Twostroke::Runtime
|
|
38
31
|
end, nil, "call", [])
|
39
32
|
# Function.prototype.toString
|
40
33
|
proto.proto_put "toString", Types::Function.new(->(scope, this, args) do
|
41
|
-
this.
|
34
|
+
Lib.throw_type_error "Function.prototype.toString is not generic" unless this.is_a? Types::Function
|
35
|
+
Types::String.new "function #{this.name}(#{this.arguments.join ","}) { #{this.source || "[native code]"} }"
|
42
36
|
end, nil, "toString", [])
|
43
37
|
obj.proto_put "prototype", proto
|
44
38
|
end
|
@@ -32,9 +32,14 @@ module Twostroke::Runtime
|
|
32
32
|
}, nil, "parseInt", [])
|
33
33
|
|
34
34
|
# one argument functions
|
35
|
-
%w(sqrt sin cos tan).each do |method|
|
35
|
+
%w(sqrt sin cos tan exp).each do |method|
|
36
36
|
obj.proto_put method, Types::Function.new(->(scope, this, args) {
|
37
|
-
|
37
|
+
ans = begin
|
38
|
+
Math.send method, Types.to_number(args[0] || Undefined.new).number
|
39
|
+
rescue Math::DomainError
|
40
|
+
Float::NAN
|
41
|
+
end
|
42
|
+
Types::Number.new(ans)
|
38
43
|
}, nil, method, [])
|
39
44
|
end
|
40
45
|
|
@@ -63,5 +68,12 @@ module Twostroke::Runtime
|
|
63
68
|
obj.proto_put "min", Types::Function.new(->(scope, this, args) {
|
64
69
|
Types::Number.new [Float::INFINITY, *args.map { |a| Types.to_number(a).number }].min
|
65
70
|
}, nil, "min", [])
|
71
|
+
|
72
|
+
obj.proto_put "pow", Types::Function.new(->(scope, this, args) {
|
73
|
+
a = Types.to_number(args[0] || Types::Undefined.new).number
|
74
|
+
b = Types.to_number(args[1] || Types::Undefined.new).number
|
75
|
+
ans = a ** b
|
76
|
+
Types::Number.new(ans.is_a?(Complex) ? Float::NAN : ans)
|
77
|
+
}, nil, "random", [])
|
66
78
|
end
|
67
79
|
end
|
@@ -22,7 +22,7 @@ module Twostroke::Runtime
|
|
22
22
|
proto.proto_put "toExponential", Types::Function.new(->(scope, this, args) do
|
23
23
|
n = Types.to_number(this)
|
24
24
|
if n.nan? || n.infinite?
|
25
|
-
Types::String.new n.to_s
|
25
|
+
Types::String.new n.number.to_s
|
26
26
|
else
|
27
27
|
places = Math.log(n.number, 10).floor
|
28
28
|
significand = n.number / (10 ** places).to_f
|
@@ -41,25 +41,32 @@ module Twostroke::Runtime
|
|
41
41
|
end, nil, "toExponential", [])
|
42
42
|
# Number.prototype.toFixed
|
43
43
|
proto.proto_put "toFixed", Types::Function.new(->(scope, this, args) do
|
44
|
-
digits = Types.to_number(args[0] || Undefined.new)
|
44
|
+
digits = Types.to_number(args[0] || Types::Undefined.new)
|
45
45
|
if digits.nan? || digits.infinite?
|
46
46
|
digits = 0
|
47
47
|
else
|
48
48
|
digits = digits.number
|
49
49
|
end
|
50
|
-
|
50
|
+
digits = [[0,digits].max,20].min
|
51
|
+
Types::String.new sprintf("%.#{digits}f", Types.to_number(this).number.round(digits))
|
51
52
|
end, nil, "toFixed", [])
|
52
53
|
# Number.prototype.toLocaleString
|
53
54
|
proto.proto_put "toLocaleString", proto.get("toString")
|
54
55
|
# Number.prototype.toString
|
55
56
|
proto.proto_put "toPrecision", Types::Function.new(->(scope, this, args) do
|
56
|
-
|
57
|
+
n = Types.to_number(this).number
|
58
|
+
return Types::String.new(n.to_s) unless args[0]
|
59
|
+
digits = Types.to_number(args[0] || Types::Undefined.new)
|
57
60
|
if digits.nan? || digits.infinite?
|
58
61
|
digits = 0
|
59
62
|
else
|
60
63
|
digits = digits.number
|
61
64
|
end
|
62
|
-
|
65
|
+
Lib.throw_range_error "toPrecision() argument must be between 1 and 21" unless (1..21).include? digits
|
66
|
+
fixup = 10 ** Math.log(n, 10).floor
|
67
|
+
n /= fixup.to_f
|
68
|
+
n = n.round digits - fixup
|
69
|
+
Types::String.new (n * fixup).to_s
|
63
70
|
end, nil, "toString", [])
|
64
71
|
obj.proto_put "prototype", proto
|
65
72
|
obj.proto_put "MAX_VALUE", Types::Number.new(Float::MAX)
|
@@ -30,11 +30,11 @@ module Twostroke::Runtime
|
|
30
30
|
proto = args[0].prototype
|
31
31
|
this = Types.to_object(this || Types::Undefined.new)
|
32
32
|
while proto.is_a?(Types::Object)
|
33
|
-
return Types::Boolean.
|
33
|
+
return Types::Boolean.true if this == proto
|
34
34
|
proto = proto.prototype
|
35
35
|
end
|
36
36
|
end
|
37
|
-
Types::Boolean.
|
37
|
+
Types::Boolean.false
|
38
38
|
}, nil, "isPrototypeOf", [])
|
39
39
|
proto.proto_put "propertyIsEnumerable", Types::Function.new(->(scope, this, args) {
|
40
40
|
this = Types.to_object(this || Types::Undefined.new)
|
@@ -42,9 +42,9 @@ module Twostroke::Runtime
|
|
42
42
|
if this.has_accessor(prop)
|
43
43
|
Types::Boolean.new this.accessors[prop][:enumerable]
|
44
44
|
elsif this.has_property(prop)
|
45
|
-
Types::Boolean.
|
45
|
+
Types::Boolean.true
|
46
46
|
else
|
47
|
-
Types::Boolean.
|
47
|
+
Types::Boolean.false
|
48
48
|
end
|
49
49
|
}, nil, "propertyIsEnumerable", [])
|
50
50
|
|
@@ -4,8 +4,8 @@ module Twostroke::Runtime
|
|
4
4
|
scope.set_var "RegExp", regexp
|
5
5
|
proto = Types::Object.new
|
6
6
|
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
7
|
-
Lib.throw_type_error "RegExp.prototype.toString is not generic" unless this.is_a?
|
8
|
-
this.
|
7
|
+
Lib.throw_type_error "RegExp.prototype.toString is not generic" unless this.is_a? Types::RegExp
|
8
|
+
Types::String.new this.regexp.inspect + (this.global ? "g" : "")
|
9
9
|
}, nil, "toString", [])
|
10
10
|
proto.define_own_property "global", get: ->(this) {
|
11
11
|
Types::Boolean.new this.global
|
@@ -34,7 +34,7 @@ module Twostroke::Runtime::Types
|
|
34
34
|
elsif object.is_a?(String)
|
35
35
|
Number.new(Float(object.string)) rescue Number.new(Float::NAN)
|
36
36
|
else # object is Object
|
37
|
-
to_number to_primitive(object)
|
37
|
+
to_number to_primitive(object, "Number")
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -70,7 +70,7 @@ module Twostroke::Runtime::Types
|
|
70
70
|
elsif object.is_a?(String)
|
71
71
|
object
|
72
72
|
else
|
73
|
-
to_string to_primitive(object)
|
73
|
+
to_string to_primitive(object, "String")
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
@@ -128,6 +128,21 @@ module Twostroke::Runtime::Types
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
def self.marshal(ruby_object)
|
132
|
+
case ruby_object
|
133
|
+
when ::String; String.new ruby_object
|
134
|
+
when Fixnum, Float; Number.new ruby_object
|
135
|
+
when ::Array; Array.new ruby_object.map { |el| box el }
|
136
|
+
when Hash; o = Object.new
|
137
|
+
ruby_object.each { |k,v| o.put k.to_s, box(v) }
|
138
|
+
o
|
139
|
+
when nil; Null.new
|
140
|
+
when true; Boolean.true
|
141
|
+
when false; Boolean.false
|
142
|
+
else Undefined.new
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
131
146
|
require File.expand_path("../types/value.rb", __FILE__)
|
132
147
|
require File.expand_path("../types/object.rb", __FILE__)
|
133
148
|
Dir.glob(File.expand_path("../types/*", __FILE__)).each do |f|
|
@@ -4,7 +4,7 @@ module Twostroke::Runtime::Types
|
|
4
4
|
@@true ||= Boolean.new(true)
|
5
5
|
end
|
6
6
|
def self.false
|
7
|
-
@@false ||=
|
7
|
+
@@false ||= Boolean.new(false)
|
8
8
|
end
|
9
9
|
|
10
10
|
attr_reader :boolean
|
@@ -12,6 +12,10 @@ module Twostroke::Runtime::Types
|
|
12
12
|
@boolean = boolean
|
13
13
|
end
|
14
14
|
|
15
|
+
def to_ruby
|
16
|
+
boolean
|
17
|
+
end
|
18
|
+
|
15
19
|
def ===(other)
|
16
20
|
other.is_a?(Boolean) && boolean == other.boolean
|
17
21
|
end
|
@@ -50,6 +50,10 @@ module Twostroke::Runtime::Types
|
|
50
50
|
put "prototype", proto
|
51
51
|
end
|
52
52
|
|
53
|
+
def to_ruby
|
54
|
+
->(this, *args) { call(nil, this, args.map(&Twostroke::Runtime::Types.method(:marshal))).to_ruby }
|
55
|
+
end
|
56
|
+
|
53
57
|
def prototype
|
54
58
|
@prototype ||= Function.constructor_function.get("prototype") if Function.constructor_function
|
55
59
|
end
|
@@ -69,10 +73,6 @@ module Twostroke::Runtime::Types
|
|
69
73
|
"function"
|
70
74
|
end
|
71
75
|
|
72
|
-
def primitive_value
|
73
|
-
String.new "function #{name}(#{arguments.join ","}) { #{source || "[native code]"} }"
|
74
|
-
end
|
75
|
-
|
76
76
|
def call(upper_scope, this, args)
|
77
77
|
retn_val = function.(upper_scope, this || upper_scope.global_scope.root_object, args)
|
78
78
|
# prevent non-Value objects being returned to javascript
|
@@ -5,6 +5,10 @@ module Twostroke::Runtime::Types
|
|
5
5
|
@number = number
|
6
6
|
end
|
7
7
|
|
8
|
+
def to_ruby
|
9
|
+
number
|
10
|
+
end
|
11
|
+
|
8
12
|
def ===(other)
|
9
13
|
if number.zero? && other.is_a?(Number) && other.number.zero?
|
10
14
|
# in javascript, -0 and 0 are not equal
|
@@ -15,15 +19,19 @@ module Twostroke::Runtime::Types
|
|
15
19
|
other.is_a?(Number) && number == other.number
|
16
20
|
end
|
17
21
|
end
|
22
|
+
|
18
23
|
def typeof
|
19
24
|
"number"
|
20
25
|
end
|
26
|
+
|
21
27
|
def zero?
|
22
28
|
number.zero?
|
23
29
|
end
|
30
|
+
|
24
31
|
def nan?
|
25
32
|
number.is_a?(Float) && number.nan?
|
26
33
|
end
|
34
|
+
|
27
35
|
def infinite?
|
28
36
|
number.is_a?(Float) && number.infinite?
|
29
37
|
end
|
@@ -13,6 +13,10 @@ module Twostroke::Runtime::Types
|
|
13
13
|
proto_put "constructor", @_class
|
14
14
|
end
|
15
15
|
|
16
|
+
def to_ruby
|
17
|
+
Twostroke::Context::ObjectProxy.new self
|
18
|
+
end
|
19
|
+
|
16
20
|
def _class=(c)
|
17
21
|
proto_put "constructor", (@_class = c)
|
18
22
|
end
|
@@ -41,13 +45,7 @@ module Twostroke::Runtime::Types
|
|
41
45
|
|
42
46
|
def construct(opts = {})
|
43
47
|
@constructing = true
|
44
|
-
opts.each
|
45
|
-
if respond_to? "#{k}="
|
46
|
-
send "#{k}=", v
|
47
|
-
else
|
48
|
-
instance_variable_set "@#{k}", v
|
49
|
-
end
|
50
|
-
end
|
48
|
+
opts.each { |k,v| send "#{k}=", v }
|
51
49
|
yield if block_given?
|
52
50
|
@constructing = false
|
53
51
|
end
|
@@ -24,8 +24,8 @@ module Twostroke::Runtime::Types
|
|
24
24
|
super()
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
27
|
+
def to_ruby
|
28
|
+
regexp
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.to_ruby_regexp(src)
|
@@ -36,7 +36,7 @@ module Twostroke::Runtime::Types
|
|
36
36
|
gsub(/([^\[]|\A)\^/,"\\1\\A").gsub(/((\]|\A)([^\[]*))\$/,"\\1\\z").
|
37
37
|
|
38
38
|
# javascript supports \cA through \cZ for control characters
|
39
|
-
gsub(/\\c[a-z]/i) { |m| (m.
|
39
|
+
gsub(/\\c[a-z]/i) { |m| (m[-1].downcase.ord - 'a'.ord + 1).chr }
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.exec(scope, this, args)
|
@@ -12,14 +12,17 @@ module Twostroke::Runtime
|
|
12
12
|
@string = string
|
13
13
|
super()
|
14
14
|
end
|
15
|
-
|
16
|
-
def
|
17
|
-
|
15
|
+
|
16
|
+
def to_ruby
|
17
|
+
string
|
18
18
|
end
|
19
19
|
|
20
20
|
def get(prop, this = self)
|
21
21
|
if prop =~ /\A\d+\z/
|
22
|
-
|
22
|
+
idx = prop.to_i
|
23
|
+
unless idx < 0 or idx >= string.length
|
24
|
+
String.new string[idx]
|
25
|
+
end
|
23
26
|
else
|
24
27
|
super prop, this
|
25
28
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Twostroke::Runtime::Types
|
2
2
|
class Value
|
3
|
-
def typeof
|
4
|
-
"VALUE"
|
5
|
-
end
|
6
|
-
|
7
3
|
def has_instance(obj)
|
8
4
|
Twostroke::Runtime::Lib.throw_type_error "Expected a function in instanceof check"
|
9
5
|
end
|
6
|
+
|
7
|
+
def to_ruby
|
8
|
+
nil
|
9
|
+
end
|
10
10
|
end
|
11
11
|
|
12
12
|
class Primitive < Value
|
data/lib/twostroke/runtime/vm.rb
CHANGED
@@ -8,6 +8,7 @@ module Twostroke::Runtime
|
|
8
8
|
@global_scope = GlobalScope.new self
|
9
9
|
@lib = {}
|
10
10
|
@name_args = {}
|
11
|
+
@vm_eval_counter = 0
|
11
12
|
end
|
12
13
|
|
13
14
|
def execute(section = :main, scope = nil, this = nil)
|
@@ -29,6 +30,17 @@ module Twostroke::Runtime
|
|
29
30
|
@name_args[section]
|
30
31
|
end
|
31
32
|
end
|
33
|
+
|
34
|
+
def eval(source, scope = nil, this = nil)
|
35
|
+
parser = Twostroke::Parser.new Twostroke::Lexer.new source
|
36
|
+
parser.parse
|
37
|
+
prefix = "#{@vm_eval_counter += 1}_"
|
38
|
+
compiler = Twostroke::Compiler::TSASM.new parser.statements, prefix
|
39
|
+
compiler.compile
|
40
|
+
compiler.bytecode[:"#{prefix}main"][-2] = [:ret]
|
41
|
+
bytecode.merge! compiler.bytecode
|
42
|
+
execute :"#{prefix}main", scope, this
|
43
|
+
end
|
32
44
|
|
33
45
|
private
|
34
46
|
def error!(msg)
|