twostroke 0.0.1 → 0.0.2
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/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
|