twostroke 0.1.0 → 0.2.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 (37) hide show
  1. data/lib/twostroke/ast/case.rb +1 -1
  2. data/lib/twostroke/ast/function.rb +2 -2
  3. data/lib/twostroke/ast/label.rb +20 -0
  4. data/lib/twostroke/ast/switch.rb +1 -0
  5. data/lib/twostroke/ast/try.rb +3 -3
  6. data/lib/twostroke/ast/unary_operators.rb +1 -1
  7. data/lib/twostroke/ast.rb +1 -0
  8. data/lib/twostroke/compiler/javascript.rb +20 -2
  9. data/lib/twostroke/compiler/tsasm.rb +89 -35
  10. data/lib/twostroke/compiler.rb +1 -1
  11. data/lib/twostroke/error.rb +3 -0
  12. data/lib/twostroke/lexer.rb +3 -2
  13. data/lib/twostroke/parser.rb +309 -227
  14. data/lib/twostroke/runtime/lib/array.js +9 -3
  15. data/lib/twostroke/runtime/lib/array.rb +15 -7
  16. data/lib/twostroke/runtime/lib/console.rb +3 -2
  17. data/lib/twostroke/runtime/lib/error.rb +1 -1
  18. data/lib/twostroke/runtime/lib/etc.js +38 -0
  19. data/lib/twostroke/runtime/lib/etc.rb +29 -0
  20. data/lib/twostroke/runtime/lib/math.rb +31 -0
  21. data/lib/twostroke/runtime/lib/number.rb +7 -1
  22. data/lib/twostroke/runtime/lib/object.rb +1 -1
  23. data/lib/twostroke/runtime/lib/regexp.rb +1 -0
  24. data/lib/twostroke/runtime/lib/string.rb +62 -22
  25. data/lib/twostroke/runtime/scope.rb +23 -2
  26. data/lib/twostroke/runtime/types/array.rb +5 -0
  27. data/lib/twostroke/runtime/types/function.rb +5 -2
  28. data/lib/twostroke/runtime/types/object.rb +3 -4
  29. data/lib/twostroke/runtime/types/regexp.rb +45 -2
  30. data/lib/twostroke/runtime/types.rb +1 -1
  31. data/lib/twostroke/runtime/vm.rb +2 -2
  32. data/lib/twostroke/runtime/vm_frame.rb +47 -8
  33. data/lib/twostroke/tokens.rb +32 -16
  34. metadata +5 -5
  35. data/lib/twostroke/ast/delete.rb +0 -15
  36. data/lib/twostroke/ast/unsorted_binop.rb +0 -97
  37. data/lib/twostroke/runtime/lib/function.js +0 -0
@@ -12,7 +12,7 @@ module Twostroke::AST
12
12
 
13
13
  def walk(&bk)
14
14
  if yield self
15
- expression.walk &bk
15
+ expression.walk &bk if expression
16
16
  statements.each { |s| s.walk &bk }
17
17
  end
18
18
  end
@@ -1,6 +1,6 @@
1
1
  module Twostroke::AST
2
2
  class Function < Base
3
- attr_accessor :name, :arguments, :statements
3
+ attr_accessor :name, :arguments, :statements, :fnid
4
4
 
5
5
  def initialize(*args)
6
6
  @arguments = []
@@ -9,7 +9,7 @@ module Twostroke::AST
9
9
  end
10
10
 
11
11
  def collapse
12
- self.class.new name: name, arguments: arguments, statements: statements.reject(&:nil?).map(&:collapse)
12
+ self.class.new name: name, arguments: arguments, statements: statements.reject(&:nil?).map(&:collapse), fnid: fnid
13
13
  end
14
14
 
15
15
  def walk(&bk)
@@ -0,0 +1,20 @@
1
+ module Twostroke::AST
2
+ class Label < Base
3
+ attr_accessor :name, :statement
4
+
5
+ def initialize(*args)
6
+ @statements = []
7
+ super *args
8
+ end
9
+
10
+ def collapse
11
+ self.class.new name: name, statement: statement
12
+ end
13
+
14
+ def walk(&bk)
15
+ if yield self
16
+ statement.walk &bk
17
+ end
18
+ end
19
+ end
20
+ end
@@ -11,6 +11,7 @@ module Twostroke::AST
11
11
 
12
12
  def walk(&bk)
13
13
  if yield self
14
+
14
15
  expression.walk &bk
15
16
  cases.each { |c| c.walk &bk }
16
17
  end
@@ -3,9 +3,9 @@ module Twostroke::AST
3
3
  attr_accessor :try_statements, :catch_variable, :catch_statements, :finally_statements
4
4
 
5
5
  def collapse
6
- self.class.new try_statements: try_statements.map(&:collapse), catch_variable: catch_variable,
7
- catch_statements: (catch_statements && catch_statements.map(&:collapse)),
8
- finally_statements: (finally_statements && finally_statements.map(&:collapse))
6
+ self.class.new try_statements: try_statements.reject(&:nil?).map(&:collapse), catch_variable: catch_variable,
7
+ catch_statements: (catch_statements && catch_statements.reject(&:nil?).map(&:collapse)),
8
+ finally_statements: (finally_statements && finally_statements.reject(&:nil?).map(&:collapse))
9
9
  end
10
10
 
11
11
  def walk(&bk)
@@ -1,5 +1,5 @@
1
1
  module Twostroke::AST
2
- [ :PostIncrement, :PreIncrement, :PostDecrement, :PreDecrement,
2
+ [ :PostIncrement, :PreIncrement, :PostDecrement, :PreDecrement, :Delete,
3
3
  :BinaryNot, :UnaryPlus, :Negation, :TypeOf, :Not, :Void, :BracketedExpression ].each do |op|
4
4
  klass = Class.new Base do
5
5
  attr_accessor :value
data/lib/twostroke/ast.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Twostroke
2
2
  module AST
3
3
  class Base
4
+ attr_accessor :line
4
5
  def initialize(hash = {})
5
6
  hash.each do |k,v|
6
7
  send "#{k}=", v
@@ -80,13 +80,31 @@ private
80
80
  "\"#{escaped}\""
81
81
  end
82
82
 
83
+ def lval(node)
84
+ if type(left) == :Variable
85
+ left.name
86
+ elsif type(left) == :Declaration
87
+ left.name
88
+ elsif type(left) == :MemberAccess
89
+ compile left.object
90
+ "#{stack}.pop().#{left.member}"
91
+ elsif type(left) == :Index
92
+ compile left.object
93
+ compile left.index
94
+ output "var __A = stack.pop(), __B = stack.pop();"
95
+ "__B[__A]"
96
+ else
97
+ "bad lval!"
98
+ end
99
+ end
100
+
83
101
  def mutate(left, fmt)
84
102
  lval = nil
85
103
  if type(left) == :Variable
86
- lval = "#{left.name}"
104
+ lval = left.name
87
105
  elsif type(left) == :Declaration
88
106
  compile left
89
- lval = "#{left.name}"
107
+ lval = left.name
90
108
  elsif type(left) == :MemberAccess
91
109
  compile left.object
92
110
  lval = "#{stack}.pop().#{left.member}"
@@ -17,7 +17,10 @@ class Twostroke::Compiler::TSASM
17
17
  send node
18
18
  else
19
19
  if @methods[type(node)]
20
+ output :".line", @current_line = node.line if node.line and node.line > @current_line
21
+ @node_stack.push node
20
22
  send type(node), node if node
23
+ @node_stack.pop
21
24
  else
22
25
  error! "#{type node} not implemented"
23
26
  end
@@ -30,6 +33,8 @@ class Twostroke::Compiler::TSASM
30
33
  @sections = [:"#{prefix}main"]
31
34
  @break_stack = []
32
35
  @continue_stack = []
36
+ @node_stack = []
37
+ @current_line = 0
33
38
 
34
39
  ast.each { |node| hoist node }
35
40
  ast.each { |node| compile node }
@@ -66,8 +71,11 @@ private
66
71
  node.walk do |node|
67
72
  if node.is_a? Twostroke::AST::Declaration
68
73
  output :".local", node.name.intern
74
+ false
69
75
  elsif node.is_a? Twostroke::AST::Function
70
76
  output :".local", node.name.intern if node.name
77
+ # because javascript is odd, entire function bodies need to be hoisted, not just their declarations
78
+ Function(node, true)
71
79
  false
72
80
  else
73
81
  true
@@ -76,7 +84,7 @@ private
76
84
  end
77
85
 
78
86
  def error!(msg)
79
- raise Twostroke::Compiler::CompileError, msg
87
+ raise Twostroke::Compiler::CompileError, "#{msg} at line #{@current_line}"
80
88
  end
81
89
 
82
90
  def type(node)
@@ -108,7 +116,7 @@ private
108
116
  compile left.object
109
117
  compile right
110
118
  output :setprop, left.member.intern
111
- elsif type(left) == :Index
119
+ elsif type(left) == :Index
112
120
  compile left.object
113
121
  compile left.index
114
122
  compile right
@@ -136,7 +144,7 @@ private
136
144
  output :set, node.left.name.intern
137
145
  elsif type(node.left) == :MemberAccess
138
146
  compile node.left.object
139
- dup
147
+ output :dup
140
148
  output :member, node.left.member.intern
141
149
  compile node.right
142
150
  output op
@@ -180,15 +188,23 @@ private
180
188
  compile left.object
181
189
  output :dup
182
190
  output :member, left.member.intern
191
+ output :dup
192
+ output :tst
183
193
  output op
184
194
  output :setprop, left.member.intern
195
+ output :pop
196
+ output :tld
185
197
  elsif type(left) == :Index
186
198
  compile left.object
187
199
  compile left.index
188
200
  output :dup, 2
189
201
  output :index
202
+ output :dup
203
+ output :tst
190
204
  output op
191
205
  output :setindex
206
+ output :pop
207
+ output :tld
192
208
  else
193
209
  error! "Bad lval in post-mutation"
194
210
  end
@@ -214,7 +230,12 @@ private
214
230
  output op
215
231
  output :setprop, left.member.intern
216
232
  elsif type(left) == :Index
217
- error! "pre-mutatation of array index not supported yet" # @TODO
233
+ compile left.object
234
+ compile left.index
235
+ output :dup, 2
236
+ output :index
237
+ output op
238
+ output :setindex
218
239
  else
219
240
  error! "Bad lval in post-mutation"
220
241
  end
@@ -238,7 +259,7 @@ private
238
259
  elsif type(node.callee) == :Index
239
260
  compile node.callee.object
240
261
  output :dup
241
- output node.callee.index
262
+ compile node.callee.index
242
263
  output :index
243
264
  node.arguments.each { |n| compile n }
244
265
  output :thiscall, node.arguments.size
@@ -275,27 +296,30 @@ private
275
296
  output :regexp, node.regexp
276
297
  end
277
298
 
278
- def Function(node)
279
- fnid = :"#{@prefix}fn_#{uniqid}"
299
+ def Function(node, in_hoist_stage = false)
300
+ fnid = node.fnid ||= :"#{@prefix}fn_#{uniqid}"
280
301
 
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
302
+ if !node.name or in_hoist_stage
303
+ section fnid
304
+ output :".name", node.name if node.name
305
+ node.arguments.each do |arg|
306
+ output :".arg", arg.intern
307
+ end
308
+ output :".local", node.name.intern if node.name
309
+ node.statements.each { |s| hoist s }
310
+ if node.name
311
+ output :callee
312
+ output :set, node.name.intern
313
+ end
314
+ node.statements.each { |s| compile s }
315
+ output :undefined
316
+ output :ret
317
+ pop_section
318
+ output :close, fnid
319
+ output :set, node.name.intern if node.name
320
+ else
321
+ output :close, fnid
291
322
  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
323
  end
300
324
 
301
325
  def Declaration(node)
@@ -327,15 +351,18 @@ private
327
351
  end
328
352
 
329
353
  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
354
+ if node.value.is_a?(Twostroke::AST::Variable)
355
+ output :deleteg, node.value.name.intern
356
+ elsif node.value.is_a?(Twostroke::AST::MemberAccess)
357
+ compile node.value.object
358
+ output :delete, node.value.member
359
+ elsif node.value.is_a?(Twostroke::AST::Index)
360
+ compile node.value.index
361
+ compile node.value.object
362
+ output :deleteindex
338
363
  else
364
+ compile node.value
365
+ output :pop
339
366
  output :true
340
367
  end
341
368
  end
@@ -358,7 +385,6 @@ private
358
385
  compile node.try_statements
359
386
  # no exceptions? clean up
360
387
  output :popcatch if node.catch_variable
361
- output :popfinally if node.catch_variable
362
388
  output :jmp, finally_label
363
389
 
364
390
  if node.catch_variable
@@ -371,6 +397,7 @@ private
371
397
  if node.finally_statements
372
398
  output :popfinally
373
399
  compile node.finally_statements
400
+ output :endfinally
374
401
  end
375
402
  end
376
403
 
@@ -437,7 +464,11 @@ private
437
464
  end
438
465
 
439
466
  def Number(node)
440
- output :push, node.number
467
+ if node.number.is_a? Bignum
468
+ output :push, node.number.to_f
469
+ else
470
+ output :push, node.number
471
+ end
441
472
  end
442
473
 
443
474
  def UnaryPlus(node)
@@ -517,16 +548,32 @@ private
517
548
  node.statements.each { |s| compile s }
518
549
  end
519
550
 
551
+ def DoWhile(node)
552
+ start_label = uniqid
553
+ next_label = uniqid
554
+ end_label = uniqid
555
+ @continue_stack.push next_label
556
+ @break_stack.push end_label
557
+ output :".label", start_label
558
+ compile node.body
559
+ output :".label", next_label
560
+ compile node.condition
561
+ output :jit, start_label
562
+ output :".label", end_label
563
+ end
564
+
520
565
  def ForLoop(node)
521
566
  compile node.initializer if node.initializer
522
567
  start_label = uniqid
568
+ next_label = uniqid
523
569
  end_label = uniqid
524
- @continue_stack.push start_label
570
+ @continue_stack.push next_label
525
571
  @break_stack.push end_label
526
572
  output :".label", start_label
527
573
  compile node.condition if node.condition
528
574
  output :jif, end_label
529
575
  compile node.body if node.body
576
+ output :".label", next_label
530
577
  compile node.increment if node.increment
531
578
  output :jmp, start_label
532
579
  output :".label", end_label
@@ -556,16 +603,23 @@ private
556
603
  @continue_stack.pop
557
604
  end
558
605
 
606
+ def BinaryNot(node)
607
+ compile node.value
608
+ output :bnot
609
+ end
610
+
559
611
  def Not(node)
560
612
  compile node.value
561
613
  output :not
562
614
  end
563
615
 
564
616
  def Break(node)
617
+ raise Twostroke::Compiler::CompileError, "Break not allowed outside of loop" unless @break_stack.any?
565
618
  output :jmp, @break_stack.last
566
619
  end
567
620
 
568
621
  def Continue(node)
622
+ raise Twostroke::Compiler::CompileError, "Continue not allowed outside of loop" unless @continue_stack.any?
569
623
  output :jmp, @continue_stack.last
570
624
  end
571
625
 
@@ -1,5 +1,5 @@
1
1
  module Twostroke::Compiler
2
- class CompileError < Twostroke::Error
2
+ class CompileError < Twostroke::SyntaxError
3
3
  end
4
4
 
5
5
  Dir.glob File.expand_path("../compiler/*", __FILE__) do |f|
@@ -1,4 +1,7 @@
1
1
  module Twostroke
2
2
  class Error < StandardError
3
3
  end
4
+
5
+ class SyntaxError < Error
6
+ end
4
7
  end
@@ -1,5 +1,5 @@
1
1
  module Twostroke
2
- class LexError < Twostroke::Error
2
+ class LexError < SyntaxError
3
3
  end
4
4
 
5
5
  class Token
@@ -27,6 +27,7 @@ module Twostroke
27
27
  @str = str
28
28
  @col = 1
29
29
  @line = 1
30
+ @line_terminator = false
30
31
  end
31
32
 
32
33
  def read_token(allow_regexp = true)
@@ -39,7 +40,7 @@ module Twostroke
39
40
  @col = 1 if !newlines.zero?
40
41
  @line += newlines
41
42
  @col += m[0].length - (m[0].rindex("\n") || 0)
42
- if [:WHITESPACE, :MULTI_COMMENT, :SINGLE_COMMENT].include? token[0]
43
+ if token[0] == :LINE_TERMINATOR or [:WHITESPACE, :MULTI_COMMENT, :SINGLE_COMMENT].include? token[0]
43
44
  return read_token(allow_regexp)
44
45
  else
45
46
  return tok