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
@@ -0,0 +1,595 @@
1
+ class Twostroke::Compiler::TSASM
2
+ attr_accessor :bytecode, :ast, :prefix
3
+
4
+ def initialize(ast, prefix = nil)
5
+ @methods = Hash[self.class.private_instance_methods(false).map { |name| [name, true] }]
6
+ @ast = ast
7
+ @prefix = prefix
8
+ end
9
+
10
+ def compile(node = nil)
11
+ if node
12
+ if node.respond_to? :each
13
+ # hoist named functions to top
14
+ node.select { |n| n.is_a?(Twostroke::AST::Function) && n.name }.each { |n| compile n }
15
+ node.reject { |n| n.is_a?(Twostroke::AST::Function) && n.name }.each { |n| compile n }
16
+ elsif node.is_a? Symbol
17
+ send node
18
+ else
19
+ if @methods[type(node)]
20
+ send type(node), node if node
21
+ else
22
+ error! "#{type node} not implemented"
23
+ end
24
+ end
25
+ else
26
+ @indent = 0
27
+ @bytecode = Hash.new { |h,k| h[k] = [] }
28
+ @current_section = :"#{prefix}main"
29
+ @auto_inc = 0
30
+ @sections = [:"#{prefix}main"]
31
+ @break_stack = []
32
+ @continue_stack = []
33
+
34
+ ast.each { |node| hoist node }
35
+ ast.each { |node| compile node }
36
+ output :undefined
37
+ output :ret
38
+
39
+ fix_labels
40
+ end
41
+ end
42
+
43
+ private
44
+ # utility methods
45
+
46
+ def fix_labels
47
+ bytecode.each do |k,v|
48
+ labels_at = {}
49
+ offset = 0
50
+ v.each_with_index do |ins,i|
51
+ if ins[0] == :".label"
52
+ labels_at[ins[1]] = i + offset
53
+ offset -= 1
54
+ end
55
+ end
56
+ bytecode[k] = v.select do |ins|
57
+ if [:jmp, :jit, :jif, :pushcatch, :pushfinally, :jiee].include?(ins[0])
58
+ ins[1] = labels_at[ins[1]]
59
+ end
60
+ ins[0] != :".label"
61
+ end
62
+ end
63
+ end
64
+
65
+ def hoist(node)
66
+ node.walk do |node|
67
+ if node.is_a? Twostroke::AST::Declaration
68
+ output :".local", node.name.intern
69
+ elsif node.is_a? Twostroke::AST::Function
70
+ output :".local", node.name.intern if node.name
71
+ false
72
+ else
73
+ true
74
+ end
75
+ end
76
+ end
77
+
78
+ def error!(msg)
79
+ raise Twostroke::Compiler::CompileError, msg
80
+ end
81
+
82
+ def type(node)
83
+ node.class.name.split("::").last.intern
84
+ end
85
+
86
+ def output(*args)
87
+ @bytecode[@current_section] << args
88
+ end
89
+
90
+ def section(sect)
91
+ @sections.push @current_section
92
+ @current_section = sect
93
+ end
94
+
95
+ def pop_section
96
+ @current_section = @sections.pop
97
+ end
98
+
99
+ def uniqid
100
+ @auto_inc += 1
101
+ end
102
+
103
+ def mutate(left, right)
104
+ if type(left) == :Variable || type(left) == :Declaration
105
+ compile right
106
+ output :set, left.name.intern
107
+ elsif type(left) == :MemberAccess
108
+ compile left.object
109
+ compile right
110
+ output :setprop, left.member.intern
111
+ elsif type(left) == :Index
112
+ compile left.object
113
+ compile left.index
114
+ compile right
115
+ output :setindex
116
+ else
117
+ error! "Bad lval in assignment"
118
+ end
119
+ end
120
+
121
+ # code generation
122
+
123
+ { Addition: :add, Subtraction: :sub, Multiplication: :mul, Division: :div,
124
+ Equality: :eq, StrictEquality: :seq, LessThan: :lt, GreaterThan: :gt,
125
+ LessThanEqual: :lte, GreaterThanEqual: :gte, BitwiseAnd: :and,
126
+ BitwiseOr: :or, BitwiseXor: :xor, In: :in, RightArithmeticShift: :sar,
127
+ LeftShift: :sal, RightLogicalShift: :slr, InstanceOf: :instanceof,
128
+ Modulus: :mod
129
+ }.each do |method,op|
130
+ define_method method do |node|
131
+ if node.assign_result_left
132
+ if type(node.left) == :Variable || type(node.left) == :Declaration
133
+ compile node.left
134
+ compile node.right
135
+ output op
136
+ output :set, node.left.name.intern
137
+ elsif type(node.left) == :MemberAccess
138
+ compile node.left.object
139
+ dup
140
+ output :member, node.left.member.intern
141
+ compile node.right
142
+ output op
143
+ output :setprop, node.left.member.intern
144
+ elsif type(node.left) == :Index
145
+ compile node.left.object
146
+ compile node.left.index
147
+ output :dup, 2
148
+ output :index
149
+ compile node.right
150
+ output op
151
+ output :setindex
152
+ else
153
+ error! "Bad lval in combined operation/assignment"
154
+ end
155
+ else
156
+ compile node.left
157
+ compile node.right
158
+ output op
159
+ end
160
+ end
161
+ private method
162
+ end
163
+ def StrictInequality(node)
164
+ StrictEquality(node)
165
+ output :not
166
+ end
167
+ def Inequality(node)
168
+ Equality(node)
169
+ output :not
170
+ end
171
+
172
+ def post_mutate(left, op)
173
+ if type(left) == :Variable || type(left) == :Declaration
174
+ output :push, left.name.intern
175
+ output :dup
176
+ output op
177
+ output :set, left.name.intern
178
+ output :pop
179
+ elsif type(left) == :MemberAccess
180
+ compile left.object
181
+ output :dup
182
+ output :member, left.member.intern
183
+ output op
184
+ output :setprop, left.member.intern
185
+ elsif type(left) == :Index
186
+ compile left.object
187
+ compile left.index
188
+ output :dup, 2
189
+ output :index
190
+ output op
191
+ output :setindex
192
+ else
193
+ error! "Bad lval in post-mutation"
194
+ end
195
+ end
196
+
197
+ def PostIncrement(node)
198
+ post_mutate node.value, :inc
199
+ end
200
+
201
+ def PostDecrement(node)
202
+ post_mutate node.value, :dec
203
+ end
204
+
205
+ def pre_mutate(left, op)
206
+ if type(left) == :Variable || type(left) == :Declaration
207
+ output :push, left.name.intern
208
+ output op
209
+ output :set, left.name.intern
210
+ elsif type(left) == :MemberAccess
211
+ compile left.object
212
+ output :dup
213
+ output :member, left.member.intern
214
+ output op
215
+ output :setprop, left.member.intern
216
+ elsif type(left) == :Index
217
+ error! "pre-mutatation of array index not supported yet" # @TODO
218
+ else
219
+ error! "Bad lval in post-mutation"
220
+ end
221
+ end
222
+
223
+ def PreIncrement(node)
224
+ pre_mutate node.value, :inc
225
+ end
226
+
227
+ def PreDecrement(node)
228
+ pre_mutate node.value, :dec
229
+ end
230
+
231
+ def Call(node)
232
+ if type(node.callee) == :MemberAccess
233
+ compile node.callee.object
234
+ output :dup
235
+ output :member, node.callee.member.intern
236
+ node.arguments.each { |n| compile n }
237
+ output :thiscall, node.arguments.size
238
+ elsif type(node.callee) == :Index
239
+ compile node.callee.object
240
+ output :dup
241
+ output node.callee.index
242
+ output :index
243
+ node.arguments.each { |n| compile n }
244
+ output :thiscall, node.arguments.size
245
+ else
246
+ compile node.callee
247
+ node.arguments.each { |n| compile n }
248
+ output :call, node.arguments.size
249
+ end
250
+ end
251
+
252
+ def New(node)
253
+ compile node.callee
254
+ node.arguments.each { |n| compile n }
255
+ output :newcall, node.arguments.size
256
+ end
257
+
258
+ def Variable(node)
259
+ output :push, node.name.intern
260
+ end
261
+
262
+ def Null(node)
263
+ output :null
264
+ end
265
+
266
+ def True(node)
267
+ output :true
268
+ end
269
+
270
+ def False(node)
271
+ output :false
272
+ end
273
+
274
+ def Regexp(node)
275
+ output :regexp, node.regexp
276
+ end
277
+
278
+ def Function(node)
279
+ fnid = :"#{@prefix}fn_#{uniqid}"
280
+
281
+ section fnid
282
+ output :".name", node.name if node.name
283
+ node.arguments.each do |arg|
284
+ output :".arg", arg.intern
285
+ end
286
+ output :".local", node.name.intern if node.name
287
+ node.statements.each { |s| hoist s }
288
+ if node.name
289
+ output :callee
290
+ output :set, node.name.intern
291
+ end
292
+ node.statements.each { |s| compile s }
293
+ output :undefined
294
+ output :ret
295
+ pop_section
296
+
297
+ output :close, fnid
298
+ output :set, node.name.intern if node.name
299
+ end
300
+
301
+ def Declaration(node)
302
+ # no-op since declarations have already been hoisted
303
+ end
304
+
305
+ def MultiExpression(node)
306
+ output :pushsp
307
+ compile node.left
308
+ output :popsp
309
+ compile node.right
310
+ end
311
+
312
+ def Assignment(node)
313
+ mutate node.left, node.right
314
+ end
315
+
316
+ def String(node)
317
+ output :push, node.string
318
+ end
319
+
320
+ def Return(node)
321
+ if node.expression
322
+ compile node.expression
323
+ else
324
+ output :undefined
325
+ end
326
+ output :ret
327
+ end
328
+
329
+ def Delete(node)
330
+ if node.expression.is_a?(Twostroke::AST::Variable)
331
+ output :deleteg, node.expression.name
332
+ elsif node.expression.is_a?(Twostroke::AST::MemberAccess)
333
+ compile node.expression.object
334
+ output :delete, node.expression.member
335
+ elsif node.expression.is_a?(Twostroke::AST::Index)
336
+ compile node.expression.object
337
+ output :delete, node.expression.index
338
+ else
339
+ output :true
340
+ end
341
+ end
342
+
343
+ def Throw(node)
344
+ compile node.expression
345
+ output :_throw
346
+ end
347
+
348
+ def Try(node)
349
+ if node.catch_variable
350
+ catch_label = uniqid
351
+ output :pushcatch, catch_label
352
+ end
353
+ finally_label = uniqid
354
+ if node.finally_statements
355
+ output :pushfinally, finally_label
356
+ end
357
+ end_label = uniqid
358
+ compile node.try_statements
359
+ # no exceptions? clean up
360
+ output :popcatch if node.catch_variable
361
+ output :popfinally if node.catch_variable
362
+ output :jmp, finally_label
363
+
364
+ if node.catch_variable
365
+ output :".label", catch_label
366
+ output :".catch", node.catch_variable.intern
367
+ output :popcatch
368
+ compile node.catch_statements
369
+ end
370
+ output :".label", finally_label
371
+ if node.finally_statements
372
+ output :popfinally
373
+ compile node.finally_statements
374
+ end
375
+ end
376
+
377
+ def Or(node)
378
+ compile node.left
379
+ output :dup
380
+ end_label = uniqid
381
+ output :jit, end_label
382
+ output :pop
383
+ compile node.right
384
+ output :".label", end_label
385
+ end
386
+
387
+ def And(node)
388
+ compile node.left
389
+ output :dup
390
+ end_label = uniqid
391
+ output :jif, end_label
392
+ output :pop
393
+ compile node.right
394
+ output :".label", end_label
395
+ end
396
+
397
+ def ObjectLiteral(node)
398
+ args = []
399
+ node.items.each do |k,v|
400
+ args << k
401
+ compile v
402
+ end
403
+ output :object, args.map(&:val)
404
+ end
405
+
406
+ def Array(node)
407
+ node.items.each do |item|
408
+ compile item
409
+ end
410
+ output :array, node.items.size
411
+ end
412
+
413
+ def While(node)
414
+ start_label = uniqid
415
+ end_label = uniqid
416
+ @continue_stack.push start_label
417
+ @break_stack.push end_label
418
+ output :".label", start_label
419
+ compile node.condition
420
+ output :jif, end_label
421
+ compile node.body
422
+ output :jmp, start_label
423
+ output :".label", end_label
424
+ @continue_stack.pop
425
+ @break_stack.pop
426
+ end
427
+
428
+ def MemberAccess(node)
429
+ compile node.object
430
+ output :member, node.member.intern
431
+ end
432
+
433
+ def Index(node)
434
+ compile node.object
435
+ compile node.index
436
+ output :index
437
+ end
438
+
439
+ def Number(node)
440
+ output :push, node.number
441
+ end
442
+
443
+ def UnaryPlus(node)
444
+ compile node.value
445
+ output :number
446
+ end
447
+
448
+ def This(node)
449
+ output :this
450
+ end
451
+
452
+ def With(node)
453
+ compile node.object
454
+ output :with
455
+ compile node.statement
456
+ output :popscope
457
+ end
458
+
459
+ def If(node)
460
+ compile node.condition
461
+ else_label = uniqid
462
+ output :jif, else_label
463
+ compile node.then
464
+ if node.else
465
+ end_label = uniqid
466
+ output :jmp, end_label
467
+ output :".label", else_label
468
+ compile node.else
469
+ output :".label", end_label
470
+ else
471
+ output :".label", else_label
472
+ end
473
+ end
474
+
475
+ def Ternary(node)
476
+ compile node.condition
477
+ else_label = uniqid
478
+ end_label = uniqid
479
+ output :jif, else_label
480
+ compile node.if_true
481
+ output :jmp, end_label
482
+ output :".label", else_label
483
+ compile node.if_false
484
+ output :".label", end_label
485
+ end
486
+
487
+ def Switch(node)
488
+ cases = node.cases.map { |c| [uniqid, c] }
489
+ default = cases.select { |l,c| c.expression.nil? }.first
490
+ end_label = uniqid
491
+ compile node.expression
492
+ output :pushsp
493
+
494
+ @break_stack.push end_label
495
+
496
+ cases.select { |l,c| c.expression }.each do |label, c|
497
+ output :popsp
498
+ output :pushsp
499
+ output :dup
500
+ compile c.expression
501
+ output :seq
502
+ output :jit, label
503
+ end
504
+ output :popsp # restore sp stack
505
+ output :jmp, (default ? default[0] : end_label)
506
+
507
+ cases.each do |label, c|
508
+ output :".label", label
509
+ compile c.statements
510
+ end
511
+
512
+ output :".label", end_label
513
+ @break_stack.pop
514
+ end
515
+
516
+ def Body(node)
517
+ node.statements.each { |s| compile s }
518
+ end
519
+
520
+ def ForLoop(node)
521
+ compile node.initializer if node.initializer
522
+ start_label = uniqid
523
+ end_label = uniqid
524
+ @continue_stack.push start_label
525
+ @break_stack.push end_label
526
+ output :".label", start_label
527
+ compile node.condition if node.condition
528
+ output :jif, end_label
529
+ compile node.body if node.body
530
+ compile node.increment if node.increment
531
+ output :jmp, start_label
532
+ output :".label", end_label
533
+ @continue_stack.pop
534
+ @break_stack.pop
535
+ end
536
+
537
+ def _enum_next
538
+ output :enumnext
539
+ end
540
+
541
+ def ForIn(node)
542
+ end_label = uniqid
543
+ loop_label = uniqid
544
+ @break_stack.push end_label
545
+ @continue_stack.push loop_label
546
+ compile node.object
547
+ output :enum
548
+ output :".label", loop_label
549
+ output :jiee, end_label
550
+ mutate node.lval, :_enum_next
551
+ compile node.body
552
+ output :jmp, loop_label
553
+ output :".label", end_label
554
+ output :popenum
555
+ @break_stack.pop
556
+ @continue_stack.pop
557
+ end
558
+
559
+ def Not(node)
560
+ compile node.value
561
+ output :not
562
+ end
563
+
564
+ def Break(node)
565
+ output :jmp, @break_stack.last
566
+ end
567
+
568
+ def Continue(node)
569
+ output :jmp, @continue_stack.last
570
+ end
571
+
572
+ def TypeOf(node)
573
+ if node.value.is_a?(Twostroke::AST::Variable)
574
+ output :typeof, node.value.name.intern
575
+ else
576
+ compile node.value
577
+ output :typeof
578
+ end
579
+ end
580
+
581
+ def BracketedExpression(node)
582
+ compile node.value
583
+ end
584
+
585
+ def Void(node)
586
+ compile node.value
587
+ output :pop
588
+ output :undefined
589
+ end
590
+
591
+ def Negation(node)
592
+ compile node.value
593
+ output :negate
594
+ end
595
+ end
@@ -0,0 +1,8 @@
1
+ module Twostroke::Compiler
2
+ class CompileError < Twostroke::Error
3
+ end
4
+
5
+ Dir.glob File.expand_path("../compiler/*", __FILE__) do |f|
6
+ require f
7
+ end
8
+ end