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,396 @@
|
|
1
|
+
class Twostroke::Compiler::Javascript
|
2
|
+
attr_accessor :src, :ast
|
3
|
+
|
4
|
+
def initialize(ast)
|
5
|
+
@methods = Hash[self.class.private_instance_methods(false).map { |name| [name, true] }]
|
6
|
+
@ast = ast
|
7
|
+
end
|
8
|
+
|
9
|
+
def compile(node = nil)
|
10
|
+
if node
|
11
|
+
if node.respond_to? :each
|
12
|
+
# hoist named functions to top
|
13
|
+
node.select { |n| n.is_a?(Twostroke::AST::Function) && n.name }.each { |n| compile n }
|
14
|
+
node.reject { |n| n.is_a?(Twostroke::AST::Function) && n.name }.each { |n| compile n }
|
15
|
+
else
|
16
|
+
if @methods[type(node)]
|
17
|
+
send type(node), node if node
|
18
|
+
else
|
19
|
+
error! "#{type node} not implemented"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
@indent = 0
|
24
|
+
@src = ""
|
25
|
+
prologue
|
26
|
+
ast.each { |node| compile node }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# utility methods
|
32
|
+
|
33
|
+
def error!(msg)
|
34
|
+
raise Twostroke::Compiler::CompileError, msg
|
35
|
+
end
|
36
|
+
|
37
|
+
def type(node)
|
38
|
+
node.class.name.split("::").last.intern
|
39
|
+
end
|
40
|
+
|
41
|
+
def indent
|
42
|
+
@indent += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def dedent
|
46
|
+
@indent -= 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def output(line)
|
50
|
+
@src << "#{"\t" * @indent}#{line}\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
def stack
|
54
|
+
"__stack"
|
55
|
+
end
|
56
|
+
|
57
|
+
def prologue
|
58
|
+
output "var #{stack} = [], #{stack}_sizes = [], __tmp;"
|
59
|
+
end
|
60
|
+
|
61
|
+
def binop(op, node)
|
62
|
+
if node.assign_result_left
|
63
|
+
compile node.right
|
64
|
+
mutate node.left, "%s #{op}= #{stack}.pop()"
|
65
|
+
else
|
66
|
+
compile node.right
|
67
|
+
compile node.left
|
68
|
+
output "#{stack}.push(#{stack}.pop() #{op} #{stack}.pop());"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def escape(str)
|
73
|
+
escaped = str.gsub("\\","\\\\").gsub('"','\\"').gsub(/[^\x20-\x7e]/) do |m|
|
74
|
+
if m.ord < 256
|
75
|
+
sprintf '\x%02x', m.ord
|
76
|
+
else
|
77
|
+
sprintf '\u%04x', m.ord
|
78
|
+
end
|
79
|
+
end
|
80
|
+
"\"#{escaped}\""
|
81
|
+
end
|
82
|
+
|
83
|
+
def mutate(left, fmt)
|
84
|
+
lval = nil
|
85
|
+
if type(left) == :Variable
|
86
|
+
lval = "#{left.name}"
|
87
|
+
elsif type(left) == :Declaration
|
88
|
+
compile left
|
89
|
+
lval = "#{left.name}"
|
90
|
+
elsif type(left) == :MemberAccess
|
91
|
+
compile left.object
|
92
|
+
lval = "#{stack}.pop().#{left.member}"
|
93
|
+
elsif type(left) == :Index
|
94
|
+
compile left.index
|
95
|
+
compile left.object
|
96
|
+
lval = "#{stack}.pop()[#{stack}.pop()]"
|
97
|
+
else
|
98
|
+
error! "Invalid left hand side in assignment"
|
99
|
+
end
|
100
|
+
output "#{stack}.push(#{sprintf(fmt, lval)});"
|
101
|
+
end
|
102
|
+
|
103
|
+
# code generation methods
|
104
|
+
|
105
|
+
def Call(node)
|
106
|
+
compile node.arguments.reverse_each
|
107
|
+
callee = nil
|
108
|
+
if type(node.callee) == :MemberAccess
|
109
|
+
compile node.callee.object
|
110
|
+
callee = "#{stack}.pop().#{node.callee.member}"
|
111
|
+
elsif type(node.callee) == :Index
|
112
|
+
compile node.callee.index
|
113
|
+
compile node.callee.object
|
114
|
+
callee = "#{stack}.pop()[#{stack}.pop()]"
|
115
|
+
else
|
116
|
+
compile node.callee
|
117
|
+
callee = "#{stack}.pop()"
|
118
|
+
end
|
119
|
+
args = (["#{stack}.pop()"] * node.arguments.size).join ", "
|
120
|
+
output "#{stack}.push(#{callee}(#{args}));"
|
121
|
+
end
|
122
|
+
|
123
|
+
def Variable(node)
|
124
|
+
output "#{stack}.push(#{node.name});"
|
125
|
+
end
|
126
|
+
|
127
|
+
def String(node)
|
128
|
+
output "#{stack}.push(#{escape node.string});"
|
129
|
+
end
|
130
|
+
|
131
|
+
def MultiExpression(node)
|
132
|
+
output "#{stack}_sizes.push(#{stack}.length);"
|
133
|
+
compile node.left
|
134
|
+
output "#{stack}.length = #{stack}_sizes.pop();" # so we don't unbalance the stack
|
135
|
+
compile node.right
|
136
|
+
end
|
137
|
+
|
138
|
+
def Declaration(node)
|
139
|
+
output "var #{node.name};"
|
140
|
+
end
|
141
|
+
|
142
|
+
def Assignment(node)
|
143
|
+
compile node.right
|
144
|
+
mutate node.left, "%s = #{stack}.pop()"
|
145
|
+
end
|
146
|
+
|
147
|
+
def Function(node)
|
148
|
+
if node.name
|
149
|
+
output "function #{node.name}(#{node.arguments.join ", "}) {"
|
150
|
+
else
|
151
|
+
output "#{stack}.push(function(#{node.arguments.join ", "}) {"
|
152
|
+
end
|
153
|
+
indent
|
154
|
+
prologue
|
155
|
+
compile node.statements
|
156
|
+
dedent
|
157
|
+
if node.name
|
158
|
+
output "}"
|
159
|
+
output "#{stack}.push(#{node.name});"
|
160
|
+
else
|
161
|
+
output "});"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def Return(node)
|
166
|
+
compile node.expression
|
167
|
+
output "return #{stack}.pop();"
|
168
|
+
end
|
169
|
+
|
170
|
+
def Addition(node); binop "+", node; end
|
171
|
+
def Subtraction(node); binop "-", node; end
|
172
|
+
def Multiplication(node); binop "*", node; end
|
173
|
+
def Division(node); binop "/", node; end
|
174
|
+
def Equality(node); binop "==", node; end
|
175
|
+
def Inequality(node); binop "!=", node; end
|
176
|
+
def StrictEquality(node); binop "===", node; end
|
177
|
+
def StrictInequality(node); binop "!==", node; end
|
178
|
+
def LessThan(node); binop "<", node; end
|
179
|
+
def LessThanEqual(node); binop "<=", node; end
|
180
|
+
def GreaterThan(node); binop ">", node; end
|
181
|
+
def GreaterThanEqual(node); binop ">=", node; end
|
182
|
+
|
183
|
+
def And(node)
|
184
|
+
compile node.left
|
185
|
+
output "if(#{stack}[#{stack}.length - 1]) {"
|
186
|
+
indent
|
187
|
+
compile node.right
|
188
|
+
dedent
|
189
|
+
output "}"
|
190
|
+
end
|
191
|
+
def Or(node)
|
192
|
+
compile node.left
|
193
|
+
output "if(!#{stack}[#{stack}.length - 1]) {"
|
194
|
+
indent
|
195
|
+
compile node.right
|
196
|
+
dedent
|
197
|
+
output "}"
|
198
|
+
end
|
199
|
+
|
200
|
+
def If(node)
|
201
|
+
compile node.condition
|
202
|
+
output "if(#{stack}.pop()) {"
|
203
|
+
indent
|
204
|
+
compile node.then
|
205
|
+
dedent
|
206
|
+
output "} else {"
|
207
|
+
indent
|
208
|
+
compile node.else if node.else
|
209
|
+
dedent
|
210
|
+
output "}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def Null(node)
|
214
|
+
output "#{stack}.push(null);"
|
215
|
+
end
|
216
|
+
|
217
|
+
def This(node)
|
218
|
+
output "#{stack}.push(this);"
|
219
|
+
end
|
220
|
+
|
221
|
+
def Array(node)
|
222
|
+
compile node.items.reverse
|
223
|
+
args = ["#{stack}.pop()"] * node.items.size
|
224
|
+
output "#{stack}.push([#{args.join ", "}]);"
|
225
|
+
end
|
226
|
+
|
227
|
+
def ObjectLiteral(node)
|
228
|
+
compile node.items.map(&:last).reverse
|
229
|
+
keys = []
|
230
|
+
node.items.each do |k,v|
|
231
|
+
keys << "#{escape k.val}: #{stack}.pop()"
|
232
|
+
end
|
233
|
+
output "#{stack}.push({ #{keys.join ", "} });"
|
234
|
+
end
|
235
|
+
|
236
|
+
def Number(node)
|
237
|
+
output "#{stack}.push(#{node.number});"
|
238
|
+
end
|
239
|
+
|
240
|
+
def Index(node)
|
241
|
+
compile node.index
|
242
|
+
compile node.object
|
243
|
+
output "#{stack}.push(#{stack}.pop()[#{stack}.pop()]);"
|
244
|
+
end
|
245
|
+
|
246
|
+
def Negation(node)
|
247
|
+
compile node.value
|
248
|
+
output "#{stack}.push(-#{stack}.pop());"
|
249
|
+
end
|
250
|
+
|
251
|
+
def Not(node)
|
252
|
+
compile node.value
|
253
|
+
output "#{stack}.push(!#{stack}.pop());"
|
254
|
+
end
|
255
|
+
|
256
|
+
def Body(node)
|
257
|
+
compile node.statements
|
258
|
+
end
|
259
|
+
|
260
|
+
def Ternary(node)
|
261
|
+
compile node.condition
|
262
|
+
output "if(#{stack}.pop()) {"
|
263
|
+
indent
|
264
|
+
compile node.if_true
|
265
|
+
dedent
|
266
|
+
output "} else {"
|
267
|
+
indent
|
268
|
+
compile node.if_false
|
269
|
+
dedent
|
270
|
+
output "}"
|
271
|
+
end
|
272
|
+
|
273
|
+
def MemberAccess(node)
|
274
|
+
compile node.object
|
275
|
+
output "#{stack}.push(#{stack}.pop().#{node.member});"
|
276
|
+
end
|
277
|
+
|
278
|
+
def ForLoop(node)
|
279
|
+
compile node.initializer
|
280
|
+
output "while(true) {"
|
281
|
+
indent
|
282
|
+
compile node.condition
|
283
|
+
output "if(!#{stack}.pop()) break;"
|
284
|
+
compile node.body
|
285
|
+
compile node.increment
|
286
|
+
dedent
|
287
|
+
output "}"
|
288
|
+
end
|
289
|
+
|
290
|
+
def ForIn(node)
|
291
|
+
compile node.object
|
292
|
+
output "for(var __tmp_lval in #{stack}.pop()) {"
|
293
|
+
indent
|
294
|
+
mutate node.lval, "%s = __tmp_lval"
|
295
|
+
compile node.body
|
296
|
+
dedent
|
297
|
+
output "}"
|
298
|
+
end
|
299
|
+
|
300
|
+
def PostIncrement(node)
|
301
|
+
mutate node.value, "%s++"
|
302
|
+
end
|
303
|
+
def PostDecrement(node)
|
304
|
+
mutate node.value, "%s--"
|
305
|
+
end
|
306
|
+
def PreIncrement(node)
|
307
|
+
mutate node.value, "++%s"
|
308
|
+
end
|
309
|
+
def PreDecrement(node)
|
310
|
+
mutate node.value, "--%s"
|
311
|
+
end
|
312
|
+
|
313
|
+
def While(node)
|
314
|
+
output "while(true) {"
|
315
|
+
indent
|
316
|
+
compile node.condition
|
317
|
+
output "if(!#{stack}.pop()) break;"
|
318
|
+
compile node.body
|
319
|
+
dedent
|
320
|
+
output "}"
|
321
|
+
end
|
322
|
+
|
323
|
+
def Try(node)
|
324
|
+
output "try {"
|
325
|
+
indent
|
326
|
+
compile node.try_statements
|
327
|
+
dedent
|
328
|
+
if node.catch_variable
|
329
|
+
output "} catch(#{node.catch_variable}) {"
|
330
|
+
indent
|
331
|
+
compile node.catch_statements
|
332
|
+
dedent
|
333
|
+
end
|
334
|
+
if node.finally_statements
|
335
|
+
output "} finally {"
|
336
|
+
indent
|
337
|
+
compile node.finally_statements
|
338
|
+
dedent
|
339
|
+
end
|
340
|
+
output "}"
|
341
|
+
end
|
342
|
+
|
343
|
+
def Void(node)
|
344
|
+
compile node.value
|
345
|
+
output "#{stack}.push(void #{stack}.pop());"
|
346
|
+
end
|
347
|
+
|
348
|
+
def TypeOf(node)
|
349
|
+
puts type(node.value)
|
350
|
+
if type(node.value) == :VARIABLE
|
351
|
+
output "#{stack}.push(typeof #{node.value.name});"
|
352
|
+
else
|
353
|
+
compile node.value
|
354
|
+
output "#{stack}.push(typeof #{stack}.pop());"
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def New(node)
|
359
|
+
compile node.arguments.reverse
|
360
|
+
compile node.callee
|
361
|
+
args = ["#{stack}.pop()"] * node.arguments.size
|
362
|
+
output "__tmp = #{stack}.pop();"
|
363
|
+
output "#{stack}.push(new __tmp(#{args.join ", "}));"
|
364
|
+
end
|
365
|
+
|
366
|
+
def Switch(node)
|
367
|
+
compile node.cases.reverse.map { |c| c.expression }.reject(&:nil?)
|
368
|
+
cases = ["#{stack}.pop()"] * node.cases.size
|
369
|
+
output "__tmp = [#{cases.join ", "}]"
|
370
|
+
compile node.expression
|
371
|
+
output "switch(#{stack}.pop()) {"
|
372
|
+
indent
|
373
|
+
i = 0
|
374
|
+
node.cases.each do |c|
|
375
|
+
dedent
|
376
|
+
if c.expression
|
377
|
+
output "case __tmp[#{i}]:"
|
378
|
+
i += 1
|
379
|
+
else
|
380
|
+
output "default:"
|
381
|
+
end
|
382
|
+
indent
|
383
|
+
compile c.statements
|
384
|
+
end
|
385
|
+
dedent
|
386
|
+
output "}"
|
387
|
+
end
|
388
|
+
|
389
|
+
def Break(node)
|
390
|
+
output "break;"
|
391
|
+
end
|
392
|
+
|
393
|
+
def Continue(node)
|
394
|
+
output "continue;"
|
395
|
+
end
|
396
|
+
end
|