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