twostroke 0.0.4 → 0.1.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/array.rb +8 -0
- data/lib/twostroke/ast/assignment.rb +7 -0
- data/lib/twostroke/ast/binary_operators.rb +7 -0
- data/lib/twostroke/ast/body.rb +6 -0
- data/lib/twostroke/ast/break.rb +4 -0
- data/lib/twostroke/ast/call.rb +7 -0
- data/lib/twostroke/ast/case.rb +9 -1
- data/lib/twostroke/ast/continue.rb +11 -0
- data/lib/twostroke/ast/declaration.rb +4 -0
- data/lib/twostroke/ast/delete.rb +6 -0
- data/lib/twostroke/ast/do_while.rb +7 -0
- data/lib/twostroke/ast/false.rb +11 -0
- data/lib/twostroke/ast/for_in.rb +8 -0
- data/lib/twostroke/ast/for_loop.rb +10 -1
- data/lib/twostroke/ast/function.rb +6 -0
- data/lib/twostroke/ast/if.rb +8 -0
- data/lib/twostroke/ast/index.rb +7 -0
- data/lib/twostroke/ast/member_access.rb +6 -0
- data/lib/twostroke/ast/multi_expression.rb +7 -0
- data/lib/twostroke/ast/new.rb +7 -0
- data/lib/twostroke/ast/null.rb +4 -0
- data/lib/twostroke/ast/number.rb +4 -0
- data/lib/twostroke/ast/object_literal.rb +6 -0
- data/lib/twostroke/ast/regexp.rb +4 -0
- data/lib/twostroke/ast/return.rb +6 -0
- data/lib/twostroke/ast/string.rb +4 -0
- data/lib/twostroke/ast/switch.rb +7 -0
- data/lib/twostroke/ast/ternary.rb +8 -0
- data/lib/twostroke/ast/this.rb +4 -0
- data/lib/twostroke/ast/throw.rb +6 -0
- data/lib/twostroke/ast/true.rb +11 -0
- data/lib/twostroke/ast/try.rb +8 -0
- data/lib/twostroke/ast/unary_operators.rb +7 -1
- data/lib/twostroke/ast/variable.rb +4 -0
- data/lib/twostroke/ast/while.rb +8 -1
- data/lib/twostroke/ast/with.rb +16 -0
- data/lib/twostroke/compiler/javascript.rb +396 -0
- data/lib/twostroke/compiler/tsasm.rb +595 -0
- data/lib/twostroke/compiler.rb +8 -0
- data/lib/twostroke/parser.rb +47 -9
- data/lib/twostroke/runtime/lib/array.js +144 -0
- data/lib/twostroke/runtime/lib/array.rb +71 -0
- data/lib/twostroke/runtime/lib/boolean.rb +23 -0
- data/lib/twostroke/runtime/lib/console.rb +13 -0
- data/lib/twostroke/runtime/lib/date.rb +65 -0
- data/lib/twostroke/runtime/lib/error.rb +36 -0
- data/lib/twostroke/runtime/lib/function.js +0 -0
- data/lib/twostroke/runtime/lib/function.rb +45 -0
- data/lib/twostroke/runtime/lib/math.rb +36 -0
- data/lib/twostroke/runtime/lib/number.rb +65 -0
- data/lib/twostroke/runtime/lib/object.js +0 -0
- data/lib/twostroke/runtime/lib/object.rb +55 -0
- data/lib/twostroke/runtime/lib/regexp.rb +28 -0
- data/lib/twostroke/runtime/lib/string.rb +86 -0
- data/lib/twostroke/runtime/lib/undefined.rb +7 -0
- data/lib/twostroke/runtime/lib.rb +42 -0
- data/lib/twostroke/runtime/scope.rb +120 -0
- data/lib/twostroke/runtime/types/array.rb +79 -0
- data/lib/twostroke/runtime/types/boolean.rb +23 -0
- data/lib/twostroke/runtime/types/boolean_object.rb +25 -0
- data/lib/twostroke/runtime/types/function.rb +83 -0
- data/lib/twostroke/runtime/types/null.rb +11 -3
- data/lib/twostroke/runtime/types/number.rb +31 -0
- data/lib/twostroke/runtime/types/number_object.rb +25 -0
- data/lib/twostroke/runtime/types/object.rb +169 -20
- data/lib/twostroke/runtime/types/regexp.rb +31 -0
- data/lib/twostroke/runtime/types/string.rb +16 -0
- data/lib/twostroke/runtime/types/string_object.rb +52 -0
- data/lib/twostroke/runtime/types/undefined.rb +11 -3
- data/lib/twostroke/runtime/types/value.rb +14 -0
- data/lib/twostroke/runtime/types.rb +133 -4
- data/lib/twostroke/runtime/vm.rb +25 -0
- data/lib/twostroke/runtime/vm_frame.rb +459 -0
- data/lib/twostroke/runtime.rb +6 -5
- data/lib/twostroke/tokens.rb +20 -8
- data/lib/twostroke.rb +3 -1
- metadata +41 -7
- data/lib/twostroke/runtime/context.rb +0 -33
- data/lib/twostroke/runtime/environment.rb +0 -13
- data/lib/twostroke/runtime/types/basic_type.rb +0 -5
@@ -0,0 +1,55 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
proto = Types::Object.new
|
4
|
+
|
5
|
+
obj = Types::Function.new(->(scope, this, args) {
|
6
|
+
if args.length.zero? || args[0].is_a?(Types::Null) || args[0].is_a?(Types::Undefined)
|
7
|
+
Types::Object.new
|
8
|
+
else
|
9
|
+
Types.to_object(args[0])
|
10
|
+
end
|
11
|
+
}, nil, "Object", [])
|
12
|
+
#obj.prototype is Function, lets set its prototype to proto
|
13
|
+
obj.prototype.prototype = proto
|
14
|
+
obj.proto_put "prototype", proto
|
15
|
+
scope.set_var "Object", obj
|
16
|
+
|
17
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
18
|
+
if this.is_a? Types::Primitive
|
19
|
+
Types.to_string(this).string
|
20
|
+
else
|
21
|
+
Types::String.new "[object #{this._class ? this._class.name : "Object"}]"
|
22
|
+
end
|
23
|
+
}, nil, "toString", [])
|
24
|
+
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this }, nil, "valueOf", [])
|
25
|
+
proto.proto_put "hasOwnProperty", Types::Function.new(->(scope, this, args) {
|
26
|
+
Types::Boolean.new Types.to_object(this || Types::Undefined.new).has_own_property(Types.to_string(args[0] || Types::Undefined.new).string)
|
27
|
+
}, nil, "hasOwnProperty", [])
|
28
|
+
proto.proto_put "isPrototypeOf", Types::Function.new(->(scope, this, args) {
|
29
|
+
if args[0].is_a? Types::Object
|
30
|
+
proto = args[0].prototype
|
31
|
+
this = Types.to_object(this || Types::Undefined.new)
|
32
|
+
while proto.is_a?(Types::Object)
|
33
|
+
return Types::Boolean.new(true) if this == proto
|
34
|
+
proto = proto.prototype
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Types::Boolean.new false
|
38
|
+
}, nil, "isPrototypeOf", [])
|
39
|
+
proto.proto_put "propertyIsEnumerable", Types::Function.new(->(scope, this, args) {
|
40
|
+
this = Types.to_object(this || Types::Undefined.new)
|
41
|
+
prop = Types.to_string(args[0] || Types::Undefined.new).string
|
42
|
+
if this.has_accessor(prop)
|
43
|
+
Types::Boolean.new this.accessors[prop][:enumerable]
|
44
|
+
elsif this.has_property
|
45
|
+
Types::Boolean.new true
|
46
|
+
else
|
47
|
+
Types::Boolean.new false
|
48
|
+
end
|
49
|
+
}, nil, "propertyIsEnumerable", [])
|
50
|
+
|
51
|
+
Types::Object.set_global_prototype proto
|
52
|
+
Types::Object.define_singleton_method(:constructor_function) { obj }
|
53
|
+
scope.global_scope.root_object.prototype = proto
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
regexp = Types::RegExp.constructor_function
|
4
|
+
scope.set_var "RegExp", regexp
|
5
|
+
proto = Types::Object.new
|
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?(Types::RegExp)
|
8
|
+
this.primitive_value
|
9
|
+
}, nil, "toString", [])
|
10
|
+
proto.define_own_property "global", get: ->(this) {
|
11
|
+
Types::Boolean.new this.global
|
12
|
+
}, writable: false, enumerable: false, configurable: false
|
13
|
+
proto.define_own_property "ignoreCase", get: ->(this) {
|
14
|
+
Types::Boolean.new((this.regexp.options & Regexp::IGNORECASE) != 0)
|
15
|
+
}, writable: false, enumerable: false, configurable: false
|
16
|
+
proto.define_own_property "multiline", get: ->(this) {
|
17
|
+
Types::Boolean.new((this.regexp.options & Regexp::MULTILINE) != 0)
|
18
|
+
}, writable: false, enumerable: false, configurable: false
|
19
|
+
proto.define_own_property "source", get: ->(this) {
|
20
|
+
Types::String.new this.regexp.source
|
21
|
+
}, writable: false, enumerable: false, configurable: false
|
22
|
+
proto.proto_put "test", Types::Function.new(->(scope, this, args) {
|
23
|
+
Lib.throw_type_error "RegExp.prototype.test is not generic" unless this.is_a?(Types::RegExp)
|
24
|
+
Types::Boolean.new((Types.to_string(args[0] || Undefined.new).string =~ this.regexp) != nil)
|
25
|
+
}, nil, "test", [])
|
26
|
+
regexp.proto_put "prototype", proto
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
obj = Types::StringObject.constructor_function
|
4
|
+
scope.set_var "String", obj
|
5
|
+
|
6
|
+
proto = Types::Object.new
|
7
|
+
# String.prototype.toString
|
8
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
9
|
+
if this.is_a?(Types::StringObject)
|
10
|
+
Types::String.new(this.string)
|
11
|
+
else
|
12
|
+
Lib.throw_type_error "String.prototype.toString is not generic"
|
13
|
+
end
|
14
|
+
}, nil, "toString", [])
|
15
|
+
# String.prototype.valueOf
|
16
|
+
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this.is_a?(Types::StringObject) ? Types::String.new(this.string) : Types.to_primitive(this) }, nil, "valueOf", [])
|
17
|
+
# String.prototype.split
|
18
|
+
proto.proto_put "split", Types::Function.new(->(scope, this, args) {
|
19
|
+
sep = Types.to_string(args[0] || Types::Undefined.new).string
|
20
|
+
str = Types.to_string(this).string
|
21
|
+
Types::Array.new (if args[1]
|
22
|
+
str.split sep, Types.to_uint32(args[1])
|
23
|
+
else
|
24
|
+
str.split sep
|
25
|
+
end).map { |s| Types::String.new s }
|
26
|
+
}, nil, "split", [])
|
27
|
+
# String.prototype.length
|
28
|
+
proto.define_own_property "length", get: ->(this) { Types::Number.new this.string.size }, writable: false, enumerable: false
|
29
|
+
# String.prototype.replace
|
30
|
+
proto.proto_put "replace", Types::Function.new(->(scope, this, args) {
|
31
|
+
sobj = Types.to_string(this)
|
32
|
+
s = sobj.string
|
33
|
+
|
34
|
+
find = args[0] || Types::Undefined.new
|
35
|
+
re = find.is_a?(Types::RegExp) ? find.regexp : Regexp.new(Regexp.escape Types.to_string(find).string)
|
36
|
+
global = find.is_a?(Types::RegExp) && find.global
|
37
|
+
|
38
|
+
replace = args[1] || Types::Undefined.new
|
39
|
+
callback = replace.respond_to?(:call) ? replace : ->(*_) { replace }
|
40
|
+
|
41
|
+
retn = ""
|
42
|
+
offset = 0
|
43
|
+
loop do
|
44
|
+
md = re.match s, offset
|
45
|
+
break unless md && (offset.zero? || global)
|
46
|
+
retn << md.pre_match[offset..-1]
|
47
|
+
retn << Types.to_string(callback.(scope, nil, [*md.to_a.map { |c| Types::String.new c }, Types::Number.new(md.begin 0), sobj])).string
|
48
|
+
offset = md.end 0
|
49
|
+
end
|
50
|
+
|
51
|
+
retn << s[offset..-1]
|
52
|
+
|
53
|
+
Types::String.new retn
|
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
|
66
|
+
else
|
67
|
+
m = s.method(find.global ? :gsub : :sub)
|
68
|
+
if replace.is_a?(String)
|
69
|
+
m.(find.regexp, replace)
|
70
|
+
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
|
77
|
+
end
|
78
|
+
end)
|
79
|
+
=end
|
80
|
+
obj.proto_put "prototype", proto
|
81
|
+
|
82
|
+
obj.proto_put "fromCharCode", Types::Function.new(->(scope, this, args) {
|
83
|
+
Types::String.new args.map { |a| Types.to_number(a).number.to_i.chr }.join
|
84
|
+
}, nil, "fromCharCode", [])
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
module Lib
|
3
|
+
INITIALIZERS = []
|
4
|
+
|
5
|
+
def self.setup_environment(vm)
|
6
|
+
INITIALIZERS.each { |i| i.arity == 1 ? i.(vm.global_scope) : i.(vm.global_scope, vm) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.register(&bk)
|
10
|
+
INITIALIZERS << bk
|
11
|
+
end
|
12
|
+
|
13
|
+
require File.expand_path("../lib/object.rb", __FILE__)
|
14
|
+
Dir.glob File.expand_path("../lib/*.rb", __FILE__) do |f|
|
15
|
+
require f
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
Dir.glob File.expand_path("../lib/*.js", __FILE__) do |f|
|
20
|
+
INITIALIZERS << ->(global_scope, vm) {
|
21
|
+
privileged_scope = global_scope.close
|
22
|
+
privileged_scope.declare :twostroke_lib_set
|
23
|
+
privileged_scope.set_var :twostroke_lib_set, Types::Function.new(->(scope, this, args) {
|
24
|
+
vm.lib[Types.to_string(args[0] || Types::Undefined.new).string.intern] = args[1] || Types::Undefined.new
|
25
|
+
}, nil, "twostroke_lib_set", [])
|
26
|
+
|
27
|
+
parser = Twostroke::Parser.new(Twostroke::Lexer.new(File.read f))
|
28
|
+
parser.parse
|
29
|
+
|
30
|
+
compiler = Twostroke::Compiler::TSASM.new parser.statements, "lib_#{f}_"
|
31
|
+
compiler.compile
|
32
|
+
|
33
|
+
compiler.bytecode.each do |k,v|
|
34
|
+
vm.bytecode[k] = v
|
35
|
+
end
|
36
|
+
|
37
|
+
vm.execute :"lib_#{f}_main", privileged_scope
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
class Scope
|
3
|
+
attr_reader :parent
|
4
|
+
|
5
|
+
def initialize(parent = nil)
|
6
|
+
@locals = {}
|
7
|
+
@parent = parent
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_var(var)
|
11
|
+
if @locals.has_key? var
|
12
|
+
@locals[var]
|
13
|
+
else
|
14
|
+
@parent.get_var(var)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_var(var, value)
|
19
|
+
if @locals.has_key? var
|
20
|
+
@locals[var] = value
|
21
|
+
else
|
22
|
+
@parent.set_var(var, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_var(var)
|
27
|
+
@locals.has_key?(var) || parent.has_var(var)
|
28
|
+
end
|
29
|
+
|
30
|
+
def declare(var)
|
31
|
+
@locals[var] = Types::Undefined.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
Scope.new self
|
36
|
+
end
|
37
|
+
|
38
|
+
def global_scope
|
39
|
+
@global_scope ||= parent.global_scope
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ObjectScope
|
44
|
+
attr_reader :object, :parent
|
45
|
+
|
46
|
+
def initialize(object, parent)
|
47
|
+
@parent, @object = parent, object
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_var(var)
|
51
|
+
if object.has_property var.to_s
|
52
|
+
object.get var.to_s
|
53
|
+
else
|
54
|
+
parent.get_var var
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_var(var, value)
|
59
|
+
if object.has_property var.to_s
|
60
|
+
object.put var.to_s, value
|
61
|
+
else
|
62
|
+
parent.set_var var.to_s, value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_var
|
67
|
+
object.has_property(var.to_s) || parent.has_var(var)
|
68
|
+
end
|
69
|
+
|
70
|
+
def declare(var)
|
71
|
+
parent.declare var
|
72
|
+
end
|
73
|
+
|
74
|
+
def close
|
75
|
+
Scope.new self
|
76
|
+
end
|
77
|
+
|
78
|
+
def global_scope
|
79
|
+
@global_scope ||= parent.global_scope
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class GlobalScope
|
84
|
+
attr_reader :root_object, :root_name, :vm
|
85
|
+
|
86
|
+
def initialize(vm, root_name = "window", root_object = nil)
|
87
|
+
@root_name = root_name
|
88
|
+
@root_object = root_object || Types::Object.new
|
89
|
+
@root_object.put root_name.to_s, @root_object
|
90
|
+
@vm = vm
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_var(var)
|
94
|
+
if @root_object.has_property var.to_s
|
95
|
+
@root_object.get var.to_s
|
96
|
+
else
|
97
|
+
Lib.throw_reference_error "undefined variable #{var}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_var(var)
|
102
|
+
@root_object.has_property var.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
def declare(var)
|
106
|
+
end
|
107
|
+
|
108
|
+
def close
|
109
|
+
Scope.new self
|
110
|
+
end
|
111
|
+
|
112
|
+
def set_var(var, value)
|
113
|
+
@root_object.put var.to_s, value
|
114
|
+
end
|
115
|
+
|
116
|
+
def global_scope
|
117
|
+
self
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
module Types
|
3
|
+
class Array < Object
|
4
|
+
def self.constructor_function
|
5
|
+
@@constructor_function ||=
|
6
|
+
Function.new(->(scope, this, args) do
|
7
|
+
if args.length.zero?
|
8
|
+
Array.new
|
9
|
+
elsif args.length == 1
|
10
|
+
Array.new([nil] * Twostroke::Runtime::Types.to_uint32(args[0]))
|
11
|
+
else
|
12
|
+
Array.new args
|
13
|
+
end
|
14
|
+
end, nil, "Array", [])
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :items
|
18
|
+
def initialize(items = [])
|
19
|
+
@prototype = Array.constructor_function.get("prototype")
|
20
|
+
super()
|
21
|
+
@items = items
|
22
|
+
end
|
23
|
+
|
24
|
+
def length
|
25
|
+
items.size
|
26
|
+
end
|
27
|
+
|
28
|
+
def get(prop, this = self)
|
29
|
+
if prop =~ /\A\d+\z/
|
30
|
+
items[prop.to_i] || Undefined.new
|
31
|
+
else
|
32
|
+
super prop, this
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(prop, val, this = self)
|
37
|
+
if prop =~ /\A\d+\z/
|
38
|
+
items[prop.to_i] = val
|
39
|
+
else
|
40
|
+
super prop, val, this
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def has_property(prop)
|
45
|
+
if prop =~ /\A\d+\z/
|
46
|
+
i = prop.to_i
|
47
|
+
i >= 0 && i < items.size && !items[i].nil?
|
48
|
+
else
|
49
|
+
super prop
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def has_own_property(prop)
|
54
|
+
if prop =~ /\A\d+\z/
|
55
|
+
i = prop.to_i
|
56
|
+
i >= 0 && i < items.size && !items[i].nil?
|
57
|
+
else
|
58
|
+
super prop
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(prop)
|
63
|
+
if prop =~ /\A\d+\z/
|
64
|
+
i = prop.to_i
|
65
|
+
if i >= 0 && i < items.size && !items[i].nil?
|
66
|
+
items[i] = nil
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
super prop
|
71
|
+
end
|
72
|
+
|
73
|
+
def each_enumerable_property(&bk)
|
74
|
+
(0...items.size).map(&:to_s).each &bk
|
75
|
+
super &bk
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Twostroke::Runtime::Types
|
2
|
+
class Boolean < Primitive
|
3
|
+
def self.true
|
4
|
+
@@true ||= Boolean.new(true)
|
5
|
+
end
|
6
|
+
def self.false
|
7
|
+
@@false ||= Null.new(false)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :boolean
|
11
|
+
def initialize(boolean)
|
12
|
+
@boolean = boolean
|
13
|
+
end
|
14
|
+
|
15
|
+
def ===(other)
|
16
|
+
other.is_a?(Boolean) && boolean == other.boolean
|
17
|
+
end
|
18
|
+
|
19
|
+
def typeof
|
20
|
+
"boolean"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Twostroke::Runtime::Types
|
2
|
+
class BooleanObject < Object
|
3
|
+
def self.constructor_function
|
4
|
+
@@constructor_function ||=
|
5
|
+
Function.new(->(scope, this, args) {
|
6
|
+
if this.constructing?
|
7
|
+
Twostroke::Runtime::Types.to_object(Twostroke::Runtime::Types.to_boolean(args[0] || Undefined.new))
|
8
|
+
else
|
9
|
+
Twostroke::Runtime::Types.to_boolean(args[0])
|
10
|
+
end
|
11
|
+
}, nil, "Boolean", [])
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :boolean
|
15
|
+
def initialize(boolean)
|
16
|
+
@prototype = BooleanObject.constructor_function.get("prototype")
|
17
|
+
super()
|
18
|
+
@boolean = boolean
|
19
|
+
end
|
20
|
+
|
21
|
+
def primitive_value
|
22
|
+
Boolean.new boolean
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Twostroke::Runtime::Types
|
2
|
+
class Function < Object
|
3
|
+
def self.constructor_function
|
4
|
+
unless defined?(@@constructor_function)
|
5
|
+
@@constructor_function = nil # lock the Function constructor out from here...
|
6
|
+
@@created_funcs = 0
|
7
|
+
@@constructor_function = Function.new(->(scope, this, args) {
|
8
|
+
formal_parameters = (args.size > 1 ? args[0...-1] : []).map { |a| Twostroke::Runtime::Types.to_string(a).string }
|
9
|
+
src = Twostroke::Runtime::Types.to_string(args[-1] || Undefined.new).string
|
10
|
+
|
11
|
+
parser = Twostroke::Parser.new(Twostroke::Lexer.new(src))
|
12
|
+
parser.parse
|
13
|
+
|
14
|
+
@@created_funcs += 1
|
15
|
+
fun_ast = Twostroke::AST::Function.new name: "anonymous", arguments: formal_parameters, statements: parser.statements
|
16
|
+
compiler = Twostroke::Compiler::TSASM.new [fun_ast], "runtime_created_#{@@created_funcs}_"
|
17
|
+
compiler.compile
|
18
|
+
|
19
|
+
vm = scope.global_scope.vm
|
20
|
+
compiler.bytecode.each do |k,v|
|
21
|
+
vm.bytecode[k] = v
|
22
|
+
end
|
23
|
+
|
24
|
+
global_scope = vm.global_scope
|
25
|
+
fun = Function.new(->(_scope, _this, _args) {
|
26
|
+
Twostroke::Runtime::VM::Frame.new(vm, :"runtime_created_#{@@created_funcs}_fn_1", fun).execute(global_scope, _this, _args)
|
27
|
+
}, src, "anonymous", formal_parameters)
|
28
|
+
}, nil, "Function", [])
|
29
|
+
@@constructor_function.proto_put "constructor", @@constructor_function
|
30
|
+
@@constructor_function._class = @@constructor_function
|
31
|
+
end
|
32
|
+
@@constructor_function
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :arguments, :name, :source, :function
|
36
|
+
def initialize(function, source, name, arguments)
|
37
|
+
@function = function
|
38
|
+
@source = source
|
39
|
+
@name = name
|
40
|
+
@arguments = arguments
|
41
|
+
# setting @_class to Function's constructor would result in a stack overflow,
|
42
|
+
# so we'll set it to nil and patch things up after @@constructor_function has
|
43
|
+
# been set
|
44
|
+
@_class = nil unless defined?(@@constructor_function)
|
45
|
+
super()
|
46
|
+
@prototype = nil
|
47
|
+
proto_put "prototype", Object.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def prototype
|
51
|
+
@prototype ||= Function.constructor_function.get("prototype")
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_instance(obj)
|
55
|
+
return false unless obj.is_a? Object
|
56
|
+
o = get "prototype"
|
57
|
+
Twostroke::Runtime::Lib.throw_type_error "Function prototype not an object" unless o.is_a? Object
|
58
|
+
loop do
|
59
|
+
obj = obj.prototype
|
60
|
+
return false unless obj.is_a?(Object)
|
61
|
+
return true if obj == o
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def typeof
|
66
|
+
"function"
|
67
|
+
end
|
68
|
+
|
69
|
+
def primitive_value
|
70
|
+
String.new "function #{name}(#{arguments.join ","}) { #{source || "[native code]"} }"
|
71
|
+
end
|
72
|
+
|
73
|
+
def call(upper_scope, this, args)
|
74
|
+
retn_val = function.(upper_scope, this || upper_scope.global_scope.root_object, args)
|
75
|
+
# prevent non-Value objects being returned to javascript
|
76
|
+
if retn_val.is_a? Value
|
77
|
+
retn_val
|
78
|
+
else
|
79
|
+
Undefined.new
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -1,7 +1,15 @@
|
|
1
1
|
module Twostroke::Runtime::Types
|
2
|
-
class Null <
|
3
|
-
def self.
|
4
|
-
@@null ||= Null.
|
2
|
+
class Null < Primitive
|
3
|
+
def self.new
|
4
|
+
@@null ||= Null.allocate
|
5
|
+
end
|
6
|
+
|
7
|
+
def ===(other)
|
8
|
+
other.is_a?(Null)
|
9
|
+
end
|
10
|
+
|
11
|
+
def typeof
|
12
|
+
"object"
|
5
13
|
end
|
6
14
|
end
|
7
15
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Twostroke::Runtime::Types
|
2
|
+
class Number < Primitive
|
3
|
+
attr_reader :number
|
4
|
+
def initialize(number)
|
5
|
+
@number = number
|
6
|
+
end
|
7
|
+
|
8
|
+
def ===(other)
|
9
|
+
if number.zero? && other.is_a?(Number) && other.number.zero?
|
10
|
+
# in javascript, -0 and 0 are not equal
|
11
|
+
# in ruby they are, and the only way to check if a number is -0 is with #to_s
|
12
|
+
# please correct me if there's a better way
|
13
|
+
number.to_s[0] == other.number.to_s[0]
|
14
|
+
else
|
15
|
+
other.is_a?(Number) && number == other.number
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def typeof
|
19
|
+
"number"
|
20
|
+
end
|
21
|
+
def zero?
|
22
|
+
number.zero?
|
23
|
+
end
|
24
|
+
def nan?
|
25
|
+
number.is_a?(Float) && number.nan?
|
26
|
+
end
|
27
|
+
def infinite?
|
28
|
+
number.is_a?(Float) && number.infinite?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Twostroke::Runtime::Types
|
2
|
+
class NumberObject < Object
|
3
|
+
def self.constructor_function
|
4
|
+
@@constructor_function ||=
|
5
|
+
Function.new(->(scope, this, args) {
|
6
|
+
if this.constructing?
|
7
|
+
Twostroke::Runtime::Types.to_object(Twostroke::Runtime::Types.to_number(args[0] || Undefined.new))
|
8
|
+
else
|
9
|
+
Twostroke::Runtime::Types.to_number(args[0])
|
10
|
+
end
|
11
|
+
}, nil, "Number", [])
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :number
|
15
|
+
def initialize(number)
|
16
|
+
@number = number
|
17
|
+
@prototype = NumberObject.constructor_function.get("prototype")
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
|
21
|
+
def primitive_value
|
22
|
+
Number.new number
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|