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