twostroke 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/twostroke/ast/binary_operators.rb +10 -1
- data/lib/twostroke/ast/for_in.rb +9 -0
- data/lib/twostroke/ast/if.rb +0 -1
- data/lib/twostroke/ast/new.rb +14 -0
- data/lib/twostroke/ast/null.rb +7 -0
- data/lib/twostroke/ast/this.rb +5 -0
- data/lib/twostroke/ast/try.rb +11 -0
- data/lib/twostroke/ast/unary_operators.rb +1 -1
- data/lib/twostroke/ast/unsorted_binop.rb +0 -2
- data/lib/twostroke/ast/while.rb +9 -0
- data/lib/twostroke/parser.rb +138 -19
- data/lib/twostroke/tokens.rb +2 -2
- metadata +8 -2
@@ -6,7 +6,16 @@ module Twostroke::AST
|
|
6
6
|
StrictInequality BitwiseAnd BitwiseXor BitwiseOr
|
7
7
|
And Or).each do |op|
|
8
8
|
klass = Class.new Base do
|
9
|
-
attr_accessor :left, :right
|
9
|
+
attr_accessor :left, :right, :assign_result_left
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
@assign_result_left = false
|
13
|
+
super *args
|
14
|
+
end
|
15
|
+
|
16
|
+
def collapse
|
17
|
+
self.class.new left: left.collapse, right: right.collapse, assign_result_left: assign_result_left
|
18
|
+
end
|
10
19
|
end
|
11
20
|
const_set op, klass
|
12
21
|
end
|
data/lib/twostroke/ast/if.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Twostroke::AST
|
2
|
+
class New < Base
|
3
|
+
attr_accessor :callee, :arguments
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@arguments = []
|
7
|
+
super *args
|
8
|
+
end
|
9
|
+
|
10
|
+
def collapse
|
11
|
+
self.class.new callee: callee.collapse, arguments: arguments.map(&:collapse)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Twostroke::AST
|
2
|
+
class Try < Base
|
3
|
+
attr_accessor :try_statements, :catch_variable, :catch_statements, :finally_statements
|
4
|
+
|
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))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Twostroke::AST
|
2
2
|
[ :PostIncrement, :PreIncrement, :PostDecrement, :PreDecrement,
|
3
|
-
:BinaryNot, :UnaryPlus, :Negation, :TypeOf, :Not ].each do |op|
|
3
|
+
:BinaryNot, :UnaryPlus, :Negation, :TypeOf, :Not, :Void ].each do |op|
|
4
4
|
klass = Class.new Base do
|
5
5
|
attr_accessor :value
|
6
6
|
|
@@ -27,7 +27,6 @@ module Twostroke::AST
|
|
27
27
|
:PIPE => BitwiseOr,
|
28
28
|
:AND => And,
|
29
29
|
:OR => Or
|
30
|
-
|
31
30
|
}
|
32
31
|
end
|
33
32
|
|
@@ -83,7 +82,6 @@ module Twostroke::AST
|
|
83
82
|
if token.is_a? Symbol
|
84
83
|
r = stack.pop
|
85
84
|
l = stack.pop
|
86
|
-
puts token
|
87
85
|
stack.push UnsortedBinop.operator_class[token].new(left: l, right: r)
|
88
86
|
else
|
89
87
|
stack.push token
|
data/lib/twostroke/parser.rb
CHANGED
@@ -56,7 +56,10 @@ module Twostroke
|
|
56
56
|
when :VAR; var
|
57
57
|
when :IF; consume_semicolon = false; send :if
|
58
58
|
when :FOR; consume_semicolon = false; send :for
|
59
|
+
when :WHILE; consume_semicolon = false; send :while
|
60
|
+
when :TRY; consume_semicolon = false; try
|
59
61
|
when :OPEN_BRACE; consume_semicolon = false; body
|
62
|
+
when :FUNCTION; consume_semicolon = false; function
|
60
63
|
when :SEMICOLON; nil
|
61
64
|
else; expression
|
62
65
|
end
|
@@ -67,29 +70,58 @@ module Twostroke
|
|
67
70
|
st
|
68
71
|
end
|
69
72
|
|
70
|
-
def expression(no_comma = false)
|
73
|
+
def expression(no_comma = false, no_in = false, no_ternary = false)
|
71
74
|
expr = expression_after_unary no_comma
|
72
|
-
if [:PLUS, :MINUS, :ASTERISK, :SLASH, :
|
73
|
-
:
|
74
|
-
:
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
new_expr = if [:PLUS, :MINUS, :ASTERISK, :SLASH, :MOD,
|
76
|
+
:LEFT_SHIFT, :RIGHT_SHIFT, :RIGHT_TRIPLE_SHIFT,
|
77
|
+
:AMPERSAND, :CARET, :PIPE ].include? peek_token.type
|
78
|
+
# these operators can be combined with assignment
|
79
|
+
if look_ahead(2).type == :EQUALS
|
80
|
+
# combination assignment
|
81
|
+
op = next_token.type
|
82
|
+
assert_type next_token, :EQUALS
|
83
|
+
AST::UnsortedBinop.operator_class[op].new left: expr, assign_result_left: true, right: expression
|
84
|
+
else
|
85
|
+
binop expr
|
86
|
+
end
|
87
|
+
elsif [ :GT, :LT, :GTE, :LTE, :DOUBLE_EQUALS,
|
88
|
+
:TRIPLE_EQUALS, :NOT_EQUALS, :NOT_DOUBLE_EQUALS,
|
89
|
+
:AND, :OR, :LEFT_SHIFT, :RIGHT_SHIFT,
|
90
|
+
:RIGHT_TRIPLE_SHIFT, :INSTANCEOF,
|
91
|
+
*(no_in ? [] : [:IN]) ].include? peek_token.type
|
92
|
+
expr = binop expr
|
93
|
+
# this has a higher precedence than the ternary
|
94
|
+
# so we'll hackily check for a ternary after this
|
95
|
+
if try_peek_token && peek_token.type == :QUESTION
|
96
|
+
ternary(expr)
|
97
|
+
else
|
98
|
+
expr
|
99
|
+
end
|
78
100
|
elsif peek_token.type == :EQUALS
|
79
101
|
next_token
|
80
|
-
AST::Assignment.new left: expr, right: expression(
|
81
|
-
elsif peek_token.type == :QUESTION
|
102
|
+
AST::Assignment.new left: expr, right: expression(true)
|
103
|
+
elsif !no_ternary && peek_token.type == :QUESTION
|
82
104
|
ternary(expr)
|
83
105
|
else
|
84
106
|
expr
|
85
107
|
end
|
108
|
+
|
109
|
+
if !no_comma && peek_token.type == :COMMA
|
110
|
+
next_token
|
111
|
+
AST::MultiExpression.new left: new_expr, right: expression
|
112
|
+
else
|
113
|
+
new_expr
|
114
|
+
end
|
86
115
|
end
|
87
116
|
|
88
|
-
def expression_after_unary(no_comma = false)
|
117
|
+
def expression_after_unary(no_comma = false, no_call = false)
|
89
118
|
expr = case peek_token.type
|
90
119
|
when :FUNCTION; function
|
91
120
|
when :STRING; string
|
92
121
|
when :NUMBER; number
|
122
|
+
when :THIS; this
|
123
|
+
when :NULL; null
|
124
|
+
when :NEW; send :new
|
93
125
|
when :BAREWORD; bareword
|
94
126
|
when :OPEN_PAREN; parens
|
95
127
|
when :OPEN_BRACE; object_literal
|
@@ -98,13 +130,14 @@ module Twostroke
|
|
98
130
|
when :TILDE; tilde
|
99
131
|
when :INCREMENT; pre_increment
|
100
132
|
when :DECREMENT; pre_decrement
|
133
|
+
when :VOID; void
|
101
134
|
when :PLUS; unary_plus
|
102
135
|
when :MINUS; unary_minus
|
103
136
|
when :TYPEOF; typeof
|
104
137
|
else error! "Unexpected #{peek_token.type}"
|
105
138
|
end
|
106
139
|
loop do
|
107
|
-
if peek_token.type == :OPEN_PAREN
|
140
|
+
if !no_call && peek_token.type == :OPEN_PAREN
|
108
141
|
expr = call expr
|
109
142
|
elsif peek_token.type == :OPEN_BRACKET
|
110
143
|
expr = index expr
|
@@ -125,7 +158,7 @@ module Twostroke
|
|
125
158
|
|
126
159
|
def binop(left)
|
127
160
|
next_token
|
128
|
-
AST::UnsortedBinop.new left: left, op: token.type, right: expression
|
161
|
+
AST::UnsortedBinop.new left: left, op: token.type, right: expression(false, false, true)
|
129
162
|
end
|
130
163
|
|
131
164
|
def body
|
@@ -138,6 +171,16 @@ module Twostroke
|
|
138
171
|
body
|
139
172
|
end
|
140
173
|
|
174
|
+
def this
|
175
|
+
assert_type next_token, :THIS
|
176
|
+
AST::This.new
|
177
|
+
end
|
178
|
+
|
179
|
+
def null
|
180
|
+
assert_type next_token, :NULL
|
181
|
+
AST::Null.new
|
182
|
+
end
|
183
|
+
|
141
184
|
def bareword
|
142
185
|
assert_type next_token, :BAREWORD
|
143
186
|
AST::Variable.new name: token.val
|
@@ -169,11 +212,29 @@ module Twostroke
|
|
169
212
|
assert_type next_token, :FOR
|
170
213
|
assert_type next_token, :OPEN_PAREN
|
171
214
|
# decide if this is a for(... in ...) or a for(;;) loop
|
172
|
-
|
173
|
-
|
174
|
-
if
|
175
|
-
|
176
|
-
|
215
|
+
for_in = false
|
216
|
+
saved_i = @i
|
217
|
+
if @i + 3 < @tokens.length && look_ahead(1).type == :VAR &&
|
218
|
+
look_ahead(2).type == :BAREWORD && look_ahead(3).type == :IN
|
219
|
+
for_in = true
|
220
|
+
else
|
221
|
+
stmt = statement(false)
|
222
|
+
assert_type next_token, :SEMICOLON, :CLOSE_PAREN
|
223
|
+
for_in = (token.type == :CLOSE_PAREN)
|
224
|
+
end
|
225
|
+
if for_in
|
226
|
+
@i = saved_i # no luck parsing for(;;), reparse as for(..in..)
|
227
|
+
if peek_token.type == :VAR
|
228
|
+
next_token
|
229
|
+
assert_type next_token, :BAREWORD
|
230
|
+
lval = AST::Declaration.new name: token.val
|
231
|
+
else
|
232
|
+
lval = expression(false, true)
|
233
|
+
end
|
234
|
+
assert_type next_token, :IN
|
235
|
+
obj = expression
|
236
|
+
assert_type next_token, :CLOSE_PAREN
|
237
|
+
AST::ForIn.new lval: lval, object: obj, body: statement
|
177
238
|
else
|
178
239
|
initializer = stmt
|
179
240
|
condition = statement(false)
|
@@ -184,6 +245,48 @@ module Twostroke
|
|
184
245
|
end
|
185
246
|
end
|
186
247
|
|
248
|
+
def while
|
249
|
+
assert_type next_token, :WHILE
|
250
|
+
assert_type next_token, :OPEN_PAREN
|
251
|
+
node = AST::While.new condition: expression
|
252
|
+
assert_type next_token, :CLOSE_PAREN
|
253
|
+
node.body = statement
|
254
|
+
node
|
255
|
+
end
|
256
|
+
|
257
|
+
def try
|
258
|
+
try = AST::Try.new try_statements: []
|
259
|
+
assert_type next_token, :TRY
|
260
|
+
assert_type next_token, :OPEN_BRACE
|
261
|
+
while peek_token.type != :CLOSE_BRACE
|
262
|
+
try.try_statements << statement
|
263
|
+
end
|
264
|
+
assert_type next_token, :CLOSE_BRACE
|
265
|
+
assert_type next_token, :CATCH, :FINALLY
|
266
|
+
if token.type == :CATCH
|
267
|
+
try.catch_statements = []
|
268
|
+
assert_type next_token, :OPEN_PAREN
|
269
|
+
assert_type next_token, :BAREWORD
|
270
|
+
try.catch_variable = token.val
|
271
|
+
assert_type next_token, :CLOSE_PAREN
|
272
|
+
assert_type next_token, :OPEN_BRACE
|
273
|
+
while peek_token.type != :CLOSE_BRACE
|
274
|
+
try.catch_statements << statement
|
275
|
+
end
|
276
|
+
assert_type next_token, :CLOSE_BRACE
|
277
|
+
end
|
278
|
+
if try_peek_token && peek_token.type == :FINALLY
|
279
|
+
try.finally_statements = []
|
280
|
+
assert_type next_token, :FINALLY
|
281
|
+
assert_type next_token, :OPEN_BRACE
|
282
|
+
while peek_token.type != :CLOSE_BRACE
|
283
|
+
try.finally_statements << statement
|
284
|
+
end
|
285
|
+
assert_type next_token, :CLOSE_BRACE
|
286
|
+
end
|
287
|
+
try
|
288
|
+
end
|
289
|
+
|
187
290
|
def member_access(obj)
|
188
291
|
assert_type next_token, :MEMBER_ACCESS
|
189
292
|
assert_type next_token, :BAREWORD
|
@@ -199,6 +302,17 @@ module Twostroke
|
|
199
302
|
end
|
200
303
|
end
|
201
304
|
|
305
|
+
def new
|
306
|
+
assert_type next_token, :NEW
|
307
|
+
node = AST::New.new
|
308
|
+
node.callee = expression_after_unary(false, true)
|
309
|
+
if try_peek_token && peek_token.type == :OPEN_PAREN
|
310
|
+
call = call(node.callee)
|
311
|
+
node.arguments = call.arguments
|
312
|
+
end
|
313
|
+
node
|
314
|
+
end
|
315
|
+
|
202
316
|
def call(callee)
|
203
317
|
assert_type next_token, :OPEN_PAREN
|
204
318
|
c = AST::Call.new callee: callee
|
@@ -341,6 +455,11 @@ module Twostroke
|
|
341
455
|
AST::BinaryNot.new value: expression_after_unary
|
342
456
|
end
|
343
457
|
|
458
|
+
def void
|
459
|
+
assert_type next_token, :VOID
|
460
|
+
AST::Void.new value: expression_after_unary
|
461
|
+
end
|
462
|
+
|
344
463
|
def unary_plus
|
345
464
|
assert_type next_token, :PLUS
|
346
465
|
AST::UnaryPlus.new value: expression_after_unary
|
@@ -357,7 +476,7 @@ module Twostroke
|
|
357
476
|
end
|
358
477
|
|
359
478
|
def post_decrement(obj)
|
360
|
-
assert_type next_token, :
|
479
|
+
assert_type next_token, :DECREMENT
|
361
480
|
AST::PostDecrement.new value: obj
|
362
481
|
end
|
363
482
|
|
@@ -367,7 +486,7 @@ module Twostroke
|
|
367
486
|
end
|
368
487
|
|
369
488
|
def pre_decrement(obj)
|
370
|
-
assert_type next_token, :
|
489
|
+
assert_type next_token, :DECREMENT
|
371
490
|
AST::PreDecrement.new value: obj
|
372
491
|
end
|
373
492
|
|
data/lib/twostroke/tokens.rb
CHANGED
@@ -8,8 +8,8 @@ module Twostroke
|
|
8
8
|
[ :WHITESPACE, /\s+/ ],
|
9
9
|
[ :NUMBER, /\d+(\.\d*(e[+-]?\d+)?)?/, ->m { m[0].to_f } ],
|
10
10
|
|
11
|
-
*%w(function var if instanceof in else for while do this return throw typeof try catch).map do |w|
|
12
|
-
[ w.upcase.intern, /#{w}/ ]
|
11
|
+
*%w(function var if instanceof in else for while do this return throw typeof try catch finally void null new).map do |w|
|
12
|
+
[ w.upcase.intern, /#{w}(?=[^a-zA-Z_0-9])/ ]
|
13
13
|
end,
|
14
14
|
[ :BAREWORD, /[a-zA-Z_][a-zA-Z_0-9]*/, ->m { m[0] } ],
|
15
15
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twostroke
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-05 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: An implementation of Javascript written in pure Ruby. This project currently
|
15
15
|
includes a WIP parser, and will include a runtime.
|
@@ -25,20 +25,26 @@ files:
|
|
25
25
|
- lib/twostroke/ast/body.rb
|
26
26
|
- lib/twostroke/ast/call.rb
|
27
27
|
- lib/twostroke/ast/declaration.rb
|
28
|
+
- lib/twostroke/ast/for_in.rb
|
28
29
|
- lib/twostroke/ast/for_loop.rb
|
29
30
|
- lib/twostroke/ast/function.rb
|
30
31
|
- lib/twostroke/ast/if.rb
|
31
32
|
- lib/twostroke/ast/index.rb
|
32
33
|
- lib/twostroke/ast/member_access.rb
|
33
34
|
- lib/twostroke/ast/multi_expression.rb
|
35
|
+
- lib/twostroke/ast/new.rb
|
36
|
+
- lib/twostroke/ast/null.rb
|
34
37
|
- lib/twostroke/ast/number.rb
|
35
38
|
- lib/twostroke/ast/object_literal.rb
|
36
39
|
- lib/twostroke/ast/return.rb
|
37
40
|
- lib/twostroke/ast/string.rb
|
38
41
|
- lib/twostroke/ast/ternary.rb
|
42
|
+
- lib/twostroke/ast/this.rb
|
43
|
+
- lib/twostroke/ast/try.rb
|
39
44
|
- lib/twostroke/ast/unary_operators.rb
|
40
45
|
- lib/twostroke/ast/unsorted_binop.rb
|
41
46
|
- lib/twostroke/ast/variable.rb
|
47
|
+
- lib/twostroke/ast/while.rb
|
42
48
|
- lib/twostroke/ast.rb
|
43
49
|
- lib/twostroke/error.rb
|
44
50
|
- lib/twostroke/lexer.rb
|