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
@@ -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