twostroke 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/twostroke/ast/case.rb +1 -1
- data/lib/twostroke/ast/function.rb +2 -2
- data/lib/twostroke/ast/label.rb +20 -0
- data/lib/twostroke/ast/switch.rb +1 -0
- data/lib/twostroke/ast/try.rb +3 -3
- data/lib/twostroke/ast/unary_operators.rb +1 -1
- data/lib/twostroke/ast.rb +1 -0
- data/lib/twostroke/compiler/javascript.rb +20 -2
- data/lib/twostroke/compiler/tsasm.rb +89 -35
- data/lib/twostroke/compiler.rb +1 -1
- data/lib/twostroke/error.rb +3 -0
- data/lib/twostroke/lexer.rb +3 -2
- data/lib/twostroke/parser.rb +309 -227
- data/lib/twostroke/runtime/lib/array.js +9 -3
- data/lib/twostroke/runtime/lib/array.rb +15 -7
- data/lib/twostroke/runtime/lib/console.rb +3 -2
- data/lib/twostroke/runtime/lib/error.rb +1 -1
- data/lib/twostroke/runtime/lib/etc.js +38 -0
- data/lib/twostroke/runtime/lib/etc.rb +29 -0
- data/lib/twostroke/runtime/lib/math.rb +31 -0
- data/lib/twostroke/runtime/lib/number.rb +7 -1
- data/lib/twostroke/runtime/lib/object.rb +1 -1
- data/lib/twostroke/runtime/lib/regexp.rb +1 -0
- data/lib/twostroke/runtime/lib/string.rb +62 -22
- data/lib/twostroke/runtime/scope.rb +23 -2
- data/lib/twostroke/runtime/types/array.rb +5 -0
- data/lib/twostroke/runtime/types/function.rb +5 -2
- data/lib/twostroke/runtime/types/object.rb +3 -4
- data/lib/twostroke/runtime/types/regexp.rb +45 -2
- data/lib/twostroke/runtime/types.rb +1 -1
- data/lib/twostroke/runtime/vm.rb +2 -2
- data/lib/twostroke/runtime/vm_frame.rb +47 -8
- data/lib/twostroke/tokens.rb +32 -16
- metadata +5 -5
- data/lib/twostroke/ast/delete.rb +0 -15
- data/lib/twostroke/ast/unsorted_binop.rb +0 -97
- data/lib/twostroke/runtime/lib/function.js +0 -0
data/lib/twostroke/ast/case.rb
CHANGED
@@ -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
|
data/lib/twostroke/ast/switch.rb
CHANGED
data/lib/twostroke/ast/try.rb
CHANGED
@@ -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
@@ -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 =
|
104
|
+
lval = left.name
|
87
105
|
elsif type(left) == :Declaration
|
88
106
|
compile left
|
89
|
-
lval =
|
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
|
-
|
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
|
-
|
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
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
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.
|
331
|
-
output :deleteg, node.
|
332
|
-
elsif node.
|
333
|
-
compile node.
|
334
|
-
output :delete, node.
|
335
|
-
elsif node.
|
336
|
-
compile node.
|
337
|
-
|
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
|
-
|
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
|
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
|
|
data/lib/twostroke/compiler.rb
CHANGED
data/lib/twostroke/error.rb
CHANGED
data/lib/twostroke/lexer.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Twostroke
|
2
|
-
class LexError <
|
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
|