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.
Files changed (80) hide show
  1. data/lib/twostroke/ast/array.rb +8 -0
  2. data/lib/twostroke/ast/assignment.rb +7 -0
  3. data/lib/twostroke/ast/binary_operators.rb +7 -0
  4. data/lib/twostroke/ast/body.rb +6 -0
  5. data/lib/twostroke/ast/break.rb +4 -0
  6. data/lib/twostroke/ast/call.rb +7 -0
  7. data/lib/twostroke/ast/case.rb +9 -1
  8. data/lib/twostroke/ast/continue.rb +11 -0
  9. data/lib/twostroke/ast/declaration.rb +4 -0
  10. data/lib/twostroke/ast/delete.rb +6 -0
  11. data/lib/twostroke/ast/do_while.rb +7 -0
  12. data/lib/twostroke/ast/false.rb +11 -0
  13. data/lib/twostroke/ast/for_in.rb +8 -0
  14. data/lib/twostroke/ast/for_loop.rb +10 -1
  15. data/lib/twostroke/ast/function.rb +6 -0
  16. data/lib/twostroke/ast/if.rb +8 -0
  17. data/lib/twostroke/ast/index.rb +7 -0
  18. data/lib/twostroke/ast/member_access.rb +6 -0
  19. data/lib/twostroke/ast/multi_expression.rb +7 -0
  20. data/lib/twostroke/ast/new.rb +7 -0
  21. data/lib/twostroke/ast/null.rb +4 -0
  22. data/lib/twostroke/ast/number.rb +4 -0
  23. data/lib/twostroke/ast/object_literal.rb +6 -0
  24. data/lib/twostroke/ast/regexp.rb +4 -0
  25. data/lib/twostroke/ast/return.rb +6 -0
  26. data/lib/twostroke/ast/string.rb +4 -0
  27. data/lib/twostroke/ast/switch.rb +7 -0
  28. data/lib/twostroke/ast/ternary.rb +8 -0
  29. data/lib/twostroke/ast/this.rb +4 -0
  30. data/lib/twostroke/ast/throw.rb +6 -0
  31. data/lib/twostroke/ast/true.rb +11 -0
  32. data/lib/twostroke/ast/try.rb +8 -0
  33. data/lib/twostroke/ast/unary_operators.rb +7 -1
  34. data/lib/twostroke/ast/variable.rb +4 -0
  35. data/lib/twostroke/ast/while.rb +8 -1
  36. data/lib/twostroke/ast/with.rb +16 -0
  37. data/lib/twostroke/compiler/javascript.rb +396 -0
  38. data/lib/twostroke/compiler/tsasm.rb +595 -0
  39. data/lib/twostroke/compiler.rb +8 -0
  40. data/lib/twostroke/parser.rb +47 -9
  41. data/lib/twostroke/runtime/lib/array.js +144 -0
  42. data/lib/twostroke/runtime/lib/array.rb +71 -0
  43. data/lib/twostroke/runtime/lib/boolean.rb +23 -0
  44. data/lib/twostroke/runtime/lib/console.rb +13 -0
  45. data/lib/twostroke/runtime/lib/date.rb +65 -0
  46. data/lib/twostroke/runtime/lib/error.rb +36 -0
  47. data/lib/twostroke/runtime/lib/function.js +0 -0
  48. data/lib/twostroke/runtime/lib/function.rb +45 -0
  49. data/lib/twostroke/runtime/lib/math.rb +36 -0
  50. data/lib/twostroke/runtime/lib/number.rb +65 -0
  51. data/lib/twostroke/runtime/lib/object.js +0 -0
  52. data/lib/twostroke/runtime/lib/object.rb +55 -0
  53. data/lib/twostroke/runtime/lib/regexp.rb +28 -0
  54. data/lib/twostroke/runtime/lib/string.rb +86 -0
  55. data/lib/twostroke/runtime/lib/undefined.rb +7 -0
  56. data/lib/twostroke/runtime/lib.rb +42 -0
  57. data/lib/twostroke/runtime/scope.rb +120 -0
  58. data/lib/twostroke/runtime/types/array.rb +79 -0
  59. data/lib/twostroke/runtime/types/boolean.rb +23 -0
  60. data/lib/twostroke/runtime/types/boolean_object.rb +25 -0
  61. data/lib/twostroke/runtime/types/function.rb +83 -0
  62. data/lib/twostroke/runtime/types/null.rb +11 -3
  63. data/lib/twostroke/runtime/types/number.rb +31 -0
  64. data/lib/twostroke/runtime/types/number_object.rb +25 -0
  65. data/lib/twostroke/runtime/types/object.rb +169 -20
  66. data/lib/twostroke/runtime/types/regexp.rb +31 -0
  67. data/lib/twostroke/runtime/types/string.rb +16 -0
  68. data/lib/twostroke/runtime/types/string_object.rb +52 -0
  69. data/lib/twostroke/runtime/types/undefined.rb +11 -3
  70. data/lib/twostroke/runtime/types/value.rb +14 -0
  71. data/lib/twostroke/runtime/types.rb +133 -4
  72. data/lib/twostroke/runtime/vm.rb +25 -0
  73. data/lib/twostroke/runtime/vm_frame.rb +459 -0
  74. data/lib/twostroke/runtime.rb +6 -5
  75. data/lib/twostroke/tokens.rb +20 -8
  76. data/lib/twostroke.rb +3 -1
  77. metadata +41 -7
  78. data/lib/twostroke/runtime/context.rb +0 -33
  79. data/lib/twostroke/runtime/environment.rb +0 -13
  80. data/lib/twostroke/runtime/types/basic_type.rb +0 -5
@@ -1,33 +1,182 @@
1
1
  module Twostroke::Runtime::Types
2
- class Object < BasicType
3
- attr_accessor :properties, :accessor_properties
4
-
5
- def initialize(env)
2
+ class Object < Value
3
+ attr_accessor :_class, :prototype
4
+ attr_reader :accessors, :properties, :extensible, :data
5
+ private :accessors, :properties
6
+
7
+ def initialize
8
+ @extensible = true
6
9
  @properties = {}
7
- @accessor_properties = {}
8
- properties["__proto__"]
10
+ @accessors = {}
11
+ @prototype ||= defined?(@@_prototype) ? @@_prototype : Null.new
12
+ @_class = self.class.constructor_function if !defined?(@_class) && self.class.respond_to?(:constructor_function)
13
+ @data = {} # for abitrary data not safe to store as a property
14
+ proto_put "constructor", @_class
15
+ end
16
+
17
+ def _class=(c)
18
+ proto_put "constructor", (@_class = c)
19
+ end
20
+
21
+ def self.set_global_prototype(proto)
22
+ @@_prototype = proto
23
+ end
24
+
25
+ def typeof
26
+ "object"
27
+ end
28
+
29
+ def constructing?
30
+ @constructing
31
+ end
32
+
33
+ # this allows us to treat the object like a generic array.
34
+ # the value of this method is not memoized, so call sparingly
35
+ def generic_items
36
+ return items if respond_to? :items
37
+ len = Twostroke::Runtime::Types.to_number(get "length").number
38
+ return [] if (len.is_a?(Float) && (len.nan? || len.infinite?)) || len < 0
39
+ len = len.to_i
40
+ (0...len).map { |i| get i.to_s }
41
+ end
42
+
43
+ def construct(opts = {})
44
+ @constructing = true
45
+ opts.each do |k,v|
46
+ if respond_to? "#{k}="
47
+ send "#{k}=", v
48
+ else
49
+ instance_variable_set "@#{k}", v
50
+ end
51
+ end
52
+ yield
53
+ @constructing = false
54
+ end
55
+
56
+ def get(prop, this = self)
57
+ if accessors.has_key? prop
58
+ accessors[prop][:get].(this)
59
+ elsif properties.has_key? prop
60
+ properties[prop]
61
+ else
62
+ prototype && prototype.is_a?(Object) ? prototype.get(prop, this) : Undefined.new
63
+ end
64
+ end
65
+
66
+ def get_own_property(prop)
67
+ if accessors.has_key? prop
68
+ accessors[prop][:get] ? accessors[prop][:get].(this) : Undefined.new
69
+ elsif properties.has_key? prop
70
+ properties[prop]
71
+ else
72
+ Undefined.new
73
+ end
74
+ end
75
+
76
+ def get_property(prop)
77
+ # @TODO?
9
78
  end
10
79
 
11
- def [](key)
12
- if accessor_properties.key? key
13
- accessor_properties[key].get self
14
- elsif properties.key? key
15
- properties[key]
16
- elsif properties.key? "__proto__"
17
- properties["__proto__"][key]
80
+ def each_enumerable_property(&bk)
81
+ accessors.select { |k,v| v[:enumerable] }.each { |k,v| yield k }
82
+ properties.reject { |k,v| accessors[k] }.each { |k,v| yield k }
83
+ if prototype && prototype.is_a?(Object)
84
+ prototype.each_enumerable_property &bk
85
+ end
86
+ end
87
+
88
+ def put(prop, value, this = self)
89
+ if accessors.has_key? prop
90
+ accessors[prop][:set].(this, value) if accessors[prop][:set] && accessors[prop][:writable]
91
+ elsif properties.has_key? prop
92
+ properties[prop] = value
93
+ #elsif prototype && prototype.is_a?(Object) && prototype.has_accessor(prop)
94
+ # prototype.put prop, value, this
95
+ else
96
+ properties[prop] = value
97
+ end
98
+ end
99
+
100
+ # puts prop as a non-enumerable property
101
+ def proto_put(prop, value)
102
+ define_own_property prop, value: value, enumerable: false
103
+ end
104
+
105
+ def can_put(prop)
106
+ extensible && (!accessors.has_key?(prop) || accessors[prop][:configurable])
107
+ end
108
+
109
+ def has_property(prop)
110
+ accessors.has_key?(prop) || properties.has_key?(prop) || (prototype && prototype.is_a?(Object) && prototype.has_property(prop))
111
+ end
112
+
113
+ def has_accessor(prop)
114
+ accessors.has_key?(prop)
115
+ end
116
+
117
+ def has_own_property(prop)
118
+ accessors.has_key?(prop) || properties.has_key?(prop)
119
+ end
120
+
121
+ def delete(prop)
122
+ if accessors.has_key? prop
123
+ accessors.delete prop if accessors[prop][:configurable]
124
+ elsif prop != "prototype"
125
+ properties.delete prop
126
+ end
127
+ end
128
+
129
+ def delete!(prop)
130
+ if accessors.has_key? prop
131
+ accessors.delete prop if accessors[prop][:configurable]
18
132
  else
19
- Undefined.undefined
133
+ properties.delete prop
134
+ end
135
+ end
136
+
137
+ def default_value(hint = nil)
138
+ if hint.nil?
139
+ # @TODO
140
+ # hint = is_a?(Date) ? "String" : "Number"
141
+ hint = "Number"
142
+ end
143
+
144
+ if hint == "String"
145
+ toString = get "toString"
146
+ if toString.respond_to? :call
147
+ str = toString.call(nil, self, [])
148
+ return str if str.is_a? Primitive
149
+ end
150
+ valueOf = get "valueOf"
151
+ if valueOf.respond_to? :call
152
+ val = valueOf.call(nil, self, [])
153
+ return val if val.is_a? Primitive
154
+ end
155
+ Twostroke::Runtime::Lib.throw_type_error "could not convert object to string"
156
+ elsif hint == "Number"
157
+ valueOf = get "valueOf"
158
+ if valueOf.respond_to? :call
159
+ val = valueOf.call(nil, self, [])
160
+ return val if val.is_a? Primitive
161
+ end
162
+ toString = get "toString"
163
+ if toString.respond_to? :call
164
+ str = toString.call(nil, self, [])
165
+ return str if str.is_a? Primitive
166
+ end
167
+ Twostroke::Runtime::Lib.throw_type_error "could not convert object to string"
20
168
  end
21
169
  end
22
170
 
23
- def []=(key, val)
24
- if accessor_properties.key? key
25
- accessor_properties[key].get self
26
- elsif properties.key? key
27
- properties[key]
171
+ def define_own_property(prop, descriptor)
172
+ unless descriptor.has_key?(:get) || descriptor.has_key?(:set)
173
+ descriptor[:get] = ->(this) { descriptor[:value] }
174
+ descriptor[:set] = ->(this, value) { descriptor[:value] = value }
175
+ descriptor[:value] ||= Undefined.new
28
176
  else
29
- Undefined.undefined
177
+ descriptor[:writable] = true if descriptor.has_key?(:set)
30
178
  end
179
+ accessors[prop] = descriptor
31
180
  end
32
181
  end
33
182
  end
@@ -0,0 +1,31 @@
1
+ module Twostroke::Runtime::Types
2
+ class RegExp < Object
3
+ def self.constructor_function
4
+ @@constructor_function ||=
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
7
+ }, nil, "RegExp", [])
8
+ end
9
+
10
+ attr_reader :regexp
11
+ attr_reader :global
12
+ def initialize(regexp_source, options)
13
+ opts = 0
14
+ (options ||= "").each_char do |opt|
15
+ opts |= case opt
16
+ when "m"; Regexp::MULTILINE
17
+ when "i"; Regexp::IGNORECASE
18
+ else; 0
19
+ end
20
+ end
21
+ @regexp = Regexp.new regexp_source, opts
22
+ @global = options.include? "g"
23
+ @prototype = RegExp.constructor_function.get("prototype")
24
+ super()
25
+ end
26
+
27
+ def primitive_value
28
+ String.new(regexp.inspect + (@global ? "g" : ""))
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module Twostroke::Runtime::Types
2
+ class String < Primitive
3
+ attr_reader :string
4
+ def initialize(string)
5
+ @string = string
6
+ end
7
+
8
+ def ===(other)
9
+ other.is_a?(String) && string == other.string
10
+ end
11
+
12
+ def typeof
13
+ "string"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,52 @@
1
+ module Twostroke::Runtime
2
+ module Types
3
+ class StringObject < Object
4
+ def self.constructor_function
5
+ @@constructor_function ||=
6
+ Function.new(->(scope, this, args) { this.constructing? ? Types.to_object(Types.to_string(args[0] || Undefined.new)) : Types.to_string(args[0]) }, nil, "String", [])
7
+ end
8
+
9
+ attr_reader :string
10
+ def initialize(string)
11
+ @prototype = StringObject.constructor_function.get("prototype")
12
+ @string = string
13
+ super()
14
+ end
15
+
16
+ def primitive_value
17
+ String.new string
18
+ end
19
+
20
+ def get(prop, this = self)
21
+ if prop =~ /\A\d+\z/
22
+ String.new string[prop.to_i]
23
+ else
24
+ super prop, this
25
+ end
26
+ end
27
+
28
+ def has_property(prop)
29
+ if prop =~ /\A\d+\z/
30
+ i = prop.to_i
31
+ i >= 0 && i < string.size
32
+ else
33
+ super prop
34
+ end
35
+ end
36
+
37
+ def has_own_property(prop)
38
+ if prop =~ /\A\d+\z/
39
+ i = prop.to_i
40
+ i >= 0 && i < string.size
41
+ else
42
+ super prop
43
+ end
44
+ end
45
+
46
+ def each_enumerable_property(&bk)
47
+ (0...string.length).map(&:to_s).each &bk
48
+ super &bk
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,7 +1,15 @@
1
1
  module Twostroke::Runtime::Types
2
- class Undefined < BasicType
3
- def self.undefined
4
- @@undefined ||= Undefined.new
2
+ class Undefined < Primitive
3
+ def self.new
4
+ @@undefined ||= Undefined.allocate
5
+ end
6
+
7
+ def ===(other)
8
+ other.is_a?(Undefined)
9
+ end
10
+
11
+ def typeof
12
+ "undefined"
5
13
  end
6
14
  end
7
15
  end
@@ -0,0 +1,14 @@
1
+ module Twostroke::Runtime::Types
2
+ class Value
3
+ def typeof
4
+ "VALUE"
5
+ end
6
+
7
+ def has_instance(obj)
8
+ Twostroke::Runtime::Lib.throw_type_error "Expected a function in instanceof check"
9
+ end
10
+ end
11
+
12
+ class Primitive < Value
13
+ end
14
+ end
@@ -1,7 +1,136 @@
1
- module Twostroke::Runtime
2
- module Types
3
- Dir.glob File.expand_path("../types/*", __FILE__) do |f|
4
- require f
1
+ module Twostroke::Runtime::Types
2
+ def self.to_primitive(object, preferred_type = nil)
3
+ if object.is_a? Primitive
4
+ object
5
+ else
6
+ object.default_value preferred_type
5
7
  end
6
8
  end
9
+
10
+ def self.to_boolean(object)
11
+ b = if object.is_a?(Boolean)
12
+ object.boolean
13
+ elsif object.is_a?(Undefined) || object.is_a?(Null)
14
+ false
15
+ elsif object.is_a?(Number)
16
+ !object.zero? && !object.nan?
17
+ elsif object.is_a?(String)
18
+ object.string != ""
19
+ else
20
+ true
21
+ end
22
+ Boolean.new b
23
+ end
24
+
25
+ def self.to_number(object)
26
+ if object.is_a?(Undefined)
27
+ Number.new Float::NAN
28
+ elsif object.is_a?(Null)
29
+ Number.new 0
30
+ elsif object.is_a?(Boolean)
31
+ Number.new(object.boolean ? 1 : 0)
32
+ elsif object.is_a?(Number)
33
+ object
34
+ elsif object.is_a?(String)
35
+ Number.new(Float(object.string)) rescue Number.new(Float::NAN)
36
+ else # object is Object
37
+ to_number to_primitive(object)
38
+ end
39
+ end
40
+
41
+ def self.to_int32(object)
42
+ num = to_number object
43
+ if num.nan? || num.infinite?
44
+ 0
45
+ else
46
+ int32 = num.number.to_i & 0xffff_ffff
47
+ int32 -= 2 ** 31 if int32 >= 2 ** 31
48
+ int32
49
+ end
50
+ end
51
+
52
+ def self.to_uint32(object)
53
+ num = to_number object
54
+ if num.nan? || num.infinite?
55
+ 0
56
+ else
57
+ num.number.to_i & 0xffff_ffff
58
+ end
59
+ end
60
+
61
+ def self.to_string(object)
62
+ if object.is_a?(Undefined)
63
+ String.new "undefined"
64
+ elsif object.is_a?(Null)
65
+ String.new "null"
66
+ elsif object.is_a?(Boolean)
67
+ String.new object.boolean.to_s
68
+ elsif object.is_a?(Number)
69
+ String.new object.number.to_s
70
+ elsif object.is_a?(String)
71
+ object
72
+ else
73
+ to_string to_primitive(object)
74
+ end
75
+ end
76
+
77
+ def self.to_object(object)
78
+ if object.is_a?(Undefined) || object.is_a?(Null)
79
+ Twostroke::Runtime::Lib.throw_type_error "cannot convert null or undefined to object"
80
+ elsif object.is_a?(Boolean)
81
+ BooleanObject.new object.boolean
82
+ elsif object.is_a?(Number)
83
+ NumberObject.new object.number
84
+ elsif object.is_a?(String)
85
+ StringObject.new object.string
86
+ else
87
+ object
88
+ end
89
+ end
90
+
91
+ def self.is_falsy(object)
92
+ !is_truthy(object)
93
+ end
94
+
95
+ def self.is_truthy(object)
96
+ to_boolean(object).boolean
97
+ end
98
+
99
+ def self.eq(a, b)
100
+ if a.class == b.class
101
+ a === b
102
+ elsif a.is_a?(Null) && b.is_a?(Undefined)
103
+ true
104
+ elsif a.is_a?(Undefined) && b.is_a?(Null)
105
+ true
106
+ elsif a.is_a?(Number) && b.is_a?(String)
107
+ eq(a, to_number(b))
108
+ elsif a.is_a?(String) && b.is_a?(Number)
109
+ eq(to_number(a), b)
110
+ elsif a.is_a?(Boolean)
111
+ eq(to_number(a), b)
112
+ elsif b.is_a?(Boolean)
113
+ eq(a, to_number(b))
114
+ elsif (a.is_a?(String) || b.is_a?(Number)) && b.is_a?(Object)
115
+ eq(a, to_primitive(b))
116
+ elsif a.is_a?(Object) && (b.is_a?(String) || b.is_a?(Number))
117
+ eq(to_primitive(a), b)
118
+ else
119
+ false
120
+ end
121
+ end
122
+
123
+ def self.seq(a, b)
124
+ if a.class == b.class
125
+ a === b
126
+ else
127
+ false
128
+ end
129
+ end
130
+
131
+ require File.expand_path("../types/value.rb", __FILE__)
132
+ require File.expand_path("../types/object.rb", __FILE__)
133
+ Dir.glob(File.expand_path("../types/*", __FILE__)).each do |f|
134
+ require f
135
+ end
7
136
  end
@@ -0,0 +1,25 @@
1
+ module Twostroke::Runtime
2
+ class VM
3
+ attr_accessor :bytecode
4
+ attr_reader :global_scope, :lib
5
+
6
+ def initialize(bytecode)
7
+ @bytecode = bytecode
8
+ @global_scope = GlobalScope.new self
9
+ @lib = {}
10
+ end
11
+
12
+ def execute(section = :main, scope = nil)
13
+ Frame.new(self, section).execute scope
14
+ end
15
+
16
+ def throw_error(type, message)
17
+ throw :exception, lib[type].(nil, global_scope.root_object, [Types::String.new(message)])
18
+ end
19
+
20
+ private
21
+ def error!(msg)
22
+ raise RuntimeError, msg
23
+ end
24
+ end
25
+ end