twostroke 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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