twostroke 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/twostroke/ast/break.rb +7 -0
- data/lib/twostroke/ast/case.rb +12 -0
- data/lib/twostroke/ast/delete.rb +9 -0
- data/lib/twostroke/ast/do_while.rb +9 -0
- data/lib/twostroke/ast/for_loop.rb +4 -1
- data/lib/twostroke/ast/regexp.rb +9 -0
- data/lib/twostroke/ast/return.rb +1 -1
- data/lib/twostroke/ast/switch.rb +12 -0
- data/lib/twostroke/ast/throw.rb +9 -0
- data/lib/twostroke/ast/unsorted_binop.rb +2 -2
- data/lib/twostroke/lexer.rb +25 -20
- data/lib/twostroke/parser.rb +136 -66
- data/lib/twostroke/runtime/context.rb +33 -0
- data/lib/twostroke/runtime/environment.rb +13 -0
- data/lib/twostroke/runtime/types/basic_type.rb +5 -0
- data/lib/twostroke/runtime/types/null.rb +7 -0
- data/lib/twostroke/runtime/types/object.rb +33 -0
- data/lib/twostroke/runtime/types/undefined.rb +7 -0
- data/lib/twostroke/runtime/types.rb +7 -0
- data/lib/twostroke/runtime.rb +7 -0
- data/lib/twostroke/tokens.rb +5 -2
- metadata +17 -2
@@ -0,0 +1,12 @@
|
|
1
|
+
module Twostroke::AST
|
2
|
+
class Case < Base
|
3
|
+
attr_accessor :expression, :statements
|
4
|
+
def initialize(*args)
|
5
|
+
@statements = []
|
6
|
+
super *args
|
7
|
+
end
|
8
|
+
def collapse
|
9
|
+
self.class.new expression: expression.collapse, statements: statements.collect(&:collapse)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -3,7 +3,10 @@ module Twostroke::AST
|
|
3
3
|
attr_accessor :initializer, :condition, :increment, :body
|
4
4
|
|
5
5
|
def collapse
|
6
|
-
self.class.new initializer: initializer
|
6
|
+
self.class.new initializer: initializer && initializer.collapse,
|
7
|
+
condition: condition && condition.collapse,
|
8
|
+
increment: increment && increment.collapse,
|
9
|
+
body: body.collapse
|
7
10
|
end
|
8
11
|
end
|
9
12
|
end
|
data/lib/twostroke/ast/return.rb
CHANGED
@@ -17,7 +17,7 @@ module Twostroke::AST
|
|
17
17
|
:GT => GreaterThan,
|
18
18
|
:GTE => GreaterThanEqual,
|
19
19
|
:IN => In,
|
20
|
-
:
|
20
|
+
:INSTANCEOF => InstanceOf,
|
21
21
|
:DOUBLE_EQUALS => Equality,
|
22
22
|
:NOT_EQUALS => Inequality,
|
23
23
|
:TRIPLE_EQUALS => StrictEquality,
|
@@ -45,7 +45,7 @@ module Twostroke::AST
|
|
45
45
|
:GT => 8,
|
46
46
|
:GTE => 8,
|
47
47
|
:IN => 8,
|
48
|
-
:
|
48
|
+
:INSTANCEOF => 8,
|
49
49
|
:DOUBLE_EQUALS => 9,
|
50
50
|
:NOT_EQUALS => 9,
|
51
51
|
:TRIPLE_EQUALS => 9,
|
data/lib/twostroke/lexer.rb
CHANGED
@@ -9,43 +9,48 @@ module Twostroke
|
|
9
9
|
send "#{k}=", v
|
10
10
|
end
|
11
11
|
end
|
12
|
-
def is?(t)
|
13
|
-
if t.is_a? Array
|
14
|
-
t.include? type
|
15
|
-
else
|
16
|
-
t == type
|
17
|
-
end
|
18
|
-
end
|
19
12
|
end
|
20
13
|
|
21
14
|
class Lexer
|
22
|
-
|
15
|
+
attr_accessor :str, :col, :line
|
16
|
+
|
17
|
+
def state
|
18
|
+
{ str: str, col: col, line: line }
|
19
|
+
end
|
20
|
+
def state=(state)
|
21
|
+
@str = state[:str]
|
22
|
+
@col = state[:col]
|
23
|
+
@line = state[:line]
|
24
|
+
end
|
25
|
+
|
23
26
|
def initialize(str)
|
24
27
|
@str = str
|
25
28
|
@col = 1
|
26
29
|
@line = 1
|
27
|
-
@tokens = []
|
28
30
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
private
|
35
|
-
def read_token
|
36
|
-
TOKENS.each do |token|
|
31
|
+
|
32
|
+
def read_token(allow_regexp = true)
|
33
|
+
TOKENS.select { |t| allow_regexp || t[0] != :REGEXP }.each do |token|
|
37
34
|
m = token[1].match @str
|
38
35
|
if m
|
39
|
-
|
36
|
+
tok = Token.new(:type => token[0], :val => token[2] ? token[2].call(m) : nil, :line => @line, :col => @col)
|
40
37
|
@str = m.post_match
|
41
38
|
newlines = m[0].count "\n"
|
42
39
|
@col = 1 if !newlines.zero?
|
43
40
|
@line += newlines
|
44
41
|
@col += m[0].length - (m[0].rindex("\n") || 0)
|
45
|
-
|
42
|
+
if [:WHITESPACE, :MULTI_COMMENT, :SINGLE_COMMENT].include? token[0]
|
43
|
+
return read_token(allow_regexp)
|
44
|
+
else
|
45
|
+
return tok
|
46
|
+
end
|
46
47
|
end
|
47
48
|
end
|
48
|
-
|
49
|
+
if @str.size > 0
|
50
|
+
raise LexError, "Illegal character '#{@str[0]}' at line #{@line}, col #{@col}."
|
51
|
+
else
|
52
|
+
nil
|
53
|
+
end
|
49
54
|
end
|
50
55
|
end
|
51
56
|
end
|
data/lib/twostroke/parser.rb
CHANGED
@@ -5,14 +5,14 @@ module Twostroke
|
|
5
5
|
class Parser
|
6
6
|
attr_reader :statements
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(lexer)
|
9
9
|
@i = -1
|
10
|
-
@
|
10
|
+
@lexer = lexer
|
11
11
|
@statements = []
|
12
12
|
end
|
13
13
|
|
14
14
|
def parse
|
15
|
-
while
|
15
|
+
while try_peek_token
|
16
16
|
st = statement
|
17
17
|
statements.push st.collapse if st
|
18
18
|
end
|
@@ -25,27 +25,29 @@ module Twostroke
|
|
25
25
|
def assert_type(tok, *types)
|
26
26
|
error! "Found #{tok.type}#{"<#{tok.val}>" if tok.val}, expected #{types.join ", "}" unless types.include? tok.type
|
27
27
|
end
|
28
|
-
|
29
|
-
|
28
|
+
|
29
|
+
def save_state
|
30
|
+
{ cur_token: @cur_token, peek_token: @peek_token, lexer_state: @lexer.state }
|
30
31
|
end
|
31
|
-
def
|
32
|
-
@
|
32
|
+
def load_state(state)
|
33
|
+
@cur_token = state[:cur_token]
|
34
|
+
@peek_token = state[:peek_token]
|
35
|
+
@lexer.state = state[:lexer_state]
|
33
36
|
end
|
37
|
+
|
34
38
|
def token
|
35
|
-
@
|
39
|
+
@cur_token or raise ParseError, "unexpected end of input"
|
36
40
|
end
|
37
|
-
def next_token
|
38
|
-
@
|
41
|
+
def next_token(allow_regexp = true)
|
42
|
+
@cur_token = @peek_token || @lexer.read_token(allow_regexp)
|
43
|
+
@peek_token = nil
|
39
44
|
token
|
40
45
|
end
|
41
|
-
def try_peek_token
|
42
|
-
@
|
43
|
-
end
|
44
|
-
def peek_token
|
45
|
-
@tokens[@i + 1] or raise ParseError, "unexpected end of input"
|
46
|
+
def try_peek_token(allow_regexp = true)
|
47
|
+
@peek_token ||= @lexer.read_token(allow_regexp)
|
46
48
|
end
|
47
|
-
def
|
48
|
-
@
|
49
|
+
def peek_token(allow_regexp = true)
|
50
|
+
@peek_token ||= @lexer.read_token(allow_regexp) or raise ParseError, "unexpected end of input"
|
49
51
|
end
|
50
52
|
|
51
53
|
####################
|
@@ -53,9 +55,13 @@ module Twostroke
|
|
53
55
|
def statement(consume_semicolon = true)
|
54
56
|
st = case peek_token.type
|
55
57
|
when :RETURN; send :return
|
58
|
+
when :BREAK; send :break
|
59
|
+
when :THROW; send :throw
|
56
60
|
when :VAR; var
|
57
61
|
when :IF; consume_semicolon = false; send :if
|
58
62
|
when :FOR; consume_semicolon = false; send :for
|
63
|
+
when :SWITCH; consume_semicolon = false; send :switch
|
64
|
+
when :DO; send :do
|
59
65
|
when :WHILE; consume_semicolon = false; send :while
|
60
66
|
when :TRY; consume_semicolon = false; try
|
61
67
|
when :OPEN_BRACE; consume_semicolon = false; body
|
@@ -64,64 +70,72 @@ module Twostroke
|
|
64
70
|
else; expression
|
65
71
|
end
|
66
72
|
if consume_semicolon
|
67
|
-
|
68
|
-
assert_type next_token, :SEMICOLON
|
73
|
+
next_token if try_peek_token && peek_token.type == :SEMICOLON
|
74
|
+
#assert_type next_token, :SEMICOLON
|
69
75
|
end
|
70
76
|
st
|
71
77
|
end
|
72
|
-
|
78
|
+
|
73
79
|
def expression(no_comma = false, no_in = false, no_ternary = false)
|
74
80
|
expr = expression_after_unary no_comma
|
75
|
-
|
81
|
+
expr = if [:PLUS, :MINUS, :ASTERISK, :SLASH, :MOD,
|
76
82
|
:LEFT_SHIFT, :RIGHT_SHIFT, :RIGHT_TRIPLE_SHIFT,
|
77
|
-
:AMPERSAND, :CARET, :PIPE ].include? peek_token.type
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
:AMPERSAND, :CARET, :PIPE ].include? peek_token(false).type
|
84
|
+
state = save_state
|
85
|
+
next_token
|
86
|
+
combined = (peek_token(false).type == :EQUALS)
|
87
|
+
load_state state
|
88
|
+
if combined
|
89
|
+
# combination assignment
|
90
|
+
op = next_token(false).type
|
91
|
+
assert_type next_token(false), :EQUALS
|
92
|
+
AST::UnsortedBinop.operator_class[op].new left: expr, assign_result_left: true, right: expression(true, false, true)
|
93
|
+
else
|
94
|
+
binop expr
|
95
|
+
end
|
87
96
|
elsif [ :GT, :LT, :GTE, :LTE, :DOUBLE_EQUALS,
|
88
97
|
:TRIPLE_EQUALS, :NOT_EQUALS, :NOT_DOUBLE_EQUALS,
|
89
98
|
:AND, :OR, :LEFT_SHIFT, :RIGHT_SHIFT,
|
90
99
|
:RIGHT_TRIPLE_SHIFT, :INSTANCEOF,
|
91
|
-
*(no_in ? [] : [:IN]) ].include? peek_token.type
|
100
|
+
*(no_in ? [] : [:IN]) ].include? peek_token(false).type
|
92
101
|
expr = binop expr
|
93
102
|
# this has a higher precedence than the ternary
|
94
103
|
# so we'll hackily check for a ternary after this
|
95
|
-
if try_peek_token && peek_token.type == :QUESTION
|
104
|
+
if try_peek_token && peek_token(false).type == :QUESTION
|
96
105
|
ternary(expr)
|
97
106
|
else
|
98
107
|
expr
|
99
108
|
end
|
100
|
-
|
109
|
+
else
|
110
|
+
expr
|
111
|
+
end
|
112
|
+
expr = if peek_token(false).type == :EQUALS
|
101
113
|
next_token
|
102
114
|
AST::Assignment.new left: expr, right: expression(true)
|
103
|
-
elsif !no_ternary && peek_token.type == :QUESTION
|
115
|
+
elsif !no_ternary && peek_token(false).type == :QUESTION
|
104
116
|
ternary(expr)
|
105
117
|
else
|
106
118
|
expr
|
107
119
|
end
|
108
120
|
|
109
|
-
if !no_comma && peek_token.type == :COMMA
|
121
|
+
if !no_comma && peek_token(false).type == :COMMA
|
110
122
|
next_token
|
111
|
-
AST::MultiExpression.new left:
|
123
|
+
AST::MultiExpression.new left: expr, right: expression
|
112
124
|
else
|
113
|
-
|
125
|
+
expr
|
114
126
|
end
|
115
127
|
end
|
116
128
|
|
117
|
-
def expression_after_unary(no_comma =
|
129
|
+
def expression_after_unary(no_comma = true, no_call = false)
|
118
130
|
expr = case peek_token.type
|
119
131
|
when :FUNCTION; function
|
120
132
|
when :STRING; string
|
121
133
|
when :NUMBER; number
|
134
|
+
when :REGEXP; regexp
|
122
135
|
when :THIS; this
|
123
136
|
when :NULL; null
|
124
137
|
when :NEW; send :new
|
138
|
+
when :DELETE; delete
|
125
139
|
when :BAREWORD; bareword
|
126
140
|
when :OPEN_PAREN; parens
|
127
141
|
when :OPEN_BRACE; object_literal
|
@@ -137,17 +151,17 @@ module Twostroke
|
|
137
151
|
else error! "Unexpected #{peek_token.type}"
|
138
152
|
end
|
139
153
|
loop do
|
140
|
-
if !no_call && peek_token.type == :OPEN_PAREN
|
154
|
+
if !no_call && peek_token(false).type == :OPEN_PAREN
|
141
155
|
expr = call expr
|
142
|
-
elsif peek_token.type == :OPEN_BRACKET
|
156
|
+
elsif peek_token(false).type == :OPEN_BRACKET
|
143
157
|
expr = index expr
|
144
|
-
elsif peek_token.type == :MEMBER_ACCESS
|
158
|
+
elsif peek_token(false).type == :MEMBER_ACCESS
|
145
159
|
expr = member_access expr
|
146
|
-
elsif !no_comma && peek_token.type == :COMMA
|
160
|
+
elsif !no_comma && peek_token(false).type == :COMMA
|
147
161
|
expr = comma(expr)
|
148
|
-
elsif peek_token.type == :INCREMENT
|
162
|
+
elsif peek_token(false).type == :INCREMENT
|
149
163
|
expr = post_increment expr
|
150
|
-
elsif peek_token.type == :DECREMENT
|
164
|
+
elsif peek_token(false).type == :DECREMENT
|
151
165
|
expr = post_decrement expr
|
152
166
|
else
|
153
167
|
return expr
|
@@ -157,8 +171,8 @@ module Twostroke
|
|
157
171
|
end
|
158
172
|
|
159
173
|
def binop(left)
|
160
|
-
next_token
|
161
|
-
AST::UnsortedBinop.new left: left, op:
|
174
|
+
op = next_token.type
|
175
|
+
AST::UnsortedBinop.new left: left, op: op, right: expression(true, false, true)
|
162
176
|
end
|
163
177
|
|
164
178
|
def body
|
@@ -189,9 +203,9 @@ module Twostroke
|
|
189
203
|
def ternary(cond)
|
190
204
|
assert_type next_token, :QUESTION
|
191
205
|
ternary = AST::Ternary.new condition: cond
|
192
|
-
ternary.if_true = expression
|
206
|
+
ternary.if_true = expression(true)
|
193
207
|
assert_type next_token, :COLON
|
194
|
-
ternary.if_false = expression
|
208
|
+
ternary.if_false = expression(true)
|
195
209
|
ternary
|
196
210
|
end
|
197
211
|
|
@@ -207,23 +221,24 @@ module Twostroke
|
|
207
221
|
end
|
208
222
|
node
|
209
223
|
end
|
210
|
-
|
224
|
+
|
211
225
|
def for
|
212
226
|
assert_type next_token, :FOR
|
213
227
|
assert_type next_token, :OPEN_PAREN
|
214
228
|
# decide if this is a for(... in ...) or a for(;;) loop
|
215
|
-
|
216
|
-
|
217
|
-
if @i + 3 < @tokens.length && look_ahead(1).type == :VAR &&
|
218
|
-
look_ahead(2).type == :BAREWORD && look_ahead(3).type == :IN
|
229
|
+
saved_state = save_state
|
230
|
+
if next_token.type == :VAR && next_token.type == :BAREWORD && next_token.type == :IN
|
219
231
|
for_in = true
|
232
|
+
load_state saved_state
|
220
233
|
else
|
221
|
-
|
234
|
+
load_state saved_state
|
235
|
+
stmt = statement(false) unless peek_token.type == :SEMICOLON
|
222
236
|
assert_type next_token, :SEMICOLON, :CLOSE_PAREN
|
223
237
|
for_in = (token.type == :CLOSE_PAREN)
|
224
238
|
end
|
239
|
+
load_state saved_state
|
225
240
|
if for_in
|
226
|
-
|
241
|
+
# no luck parsing for(;;), reparse as for(..in..)
|
227
242
|
if peek_token.type == :VAR
|
228
243
|
next_token
|
229
244
|
assert_type next_token, :BAREWORD
|
@@ -236,15 +251,39 @@ module Twostroke
|
|
236
251
|
assert_type next_token, :CLOSE_PAREN
|
237
252
|
AST::ForIn.new lval: lval, object: obj, body: statement
|
238
253
|
else
|
239
|
-
initializer =
|
240
|
-
condition = statement(false)
|
254
|
+
initializer = statement(false) unless peek_token.type == :SEMICOLON
|
241
255
|
assert_type next_token, :SEMICOLON
|
242
|
-
|
256
|
+
condition = statement(false) unless peek_token.type == :SEMICOLON
|
257
|
+
assert_type next_token, :SEMICOLON
|
258
|
+
increment = statement(false) unless peek_token.type == :CLOSE_PAREN
|
243
259
|
assert_type next_token, :CLOSE_PAREN
|
244
260
|
AST::ForLoop.new initializer: initializer, condition: condition, increment: increment, body: statement
|
245
261
|
end
|
246
262
|
end
|
247
263
|
|
264
|
+
def switch
|
265
|
+
assert_type next_token, :SWITCH
|
266
|
+
assert_type next_token, :OPEN_PAREN
|
267
|
+
sw = AST::Switch.new expression: expression
|
268
|
+
assert_type next_token, :CLOSE_PAREN
|
269
|
+
assert_type next_token, :OPEN_BRACE
|
270
|
+
current_case = nil
|
271
|
+
while ![:CLOSE_BRACE].include? peek_token.type
|
272
|
+
if peek_token.type == :CASE
|
273
|
+
assert_type next_token, :CASE
|
274
|
+
expr = expression
|
275
|
+
current_case = AST::Case.new expression: expr
|
276
|
+
assert_type next_token, :COLON
|
277
|
+
sw.cases << current_case
|
278
|
+
else
|
279
|
+
error! "statements may only appear under a case" if current_case.nil?
|
280
|
+
current_case.statements << statement
|
281
|
+
end
|
282
|
+
end
|
283
|
+
assert_type next_token, :CLOSE_BRACE
|
284
|
+
sw
|
285
|
+
end
|
286
|
+
|
248
287
|
def while
|
249
288
|
assert_type next_token, :WHILE
|
250
289
|
assert_type next_token, :OPEN_PAREN
|
@@ -254,6 +293,16 @@ module Twostroke
|
|
254
293
|
node
|
255
294
|
end
|
256
295
|
|
296
|
+
def do
|
297
|
+
assert_type next_token, :DO
|
298
|
+
node = AST::DoWhile.new body: body
|
299
|
+
assert_type next_token, :WHILE
|
300
|
+
assert_type next_token, :OPEN_PAREN
|
301
|
+
node.condition = expression(false)
|
302
|
+
assert_type next_token, :CLOSE_PAREN
|
303
|
+
node
|
304
|
+
end
|
305
|
+
|
257
306
|
def try
|
258
307
|
try = AST::Try.new try_statements: []
|
259
308
|
assert_type next_token, :TRY
|
@@ -336,7 +385,23 @@ module Twostroke
|
|
336
385
|
|
337
386
|
def return
|
338
387
|
assert_type next_token, :RETURN
|
339
|
-
|
388
|
+
expr = expression unless peek_token.type == :SEMICOLON || peek_token.type == :CLOSE_BRACE
|
389
|
+
AST::Return.new expression: expr
|
390
|
+
end
|
391
|
+
|
392
|
+
def break
|
393
|
+
assert_type next_token, :BREAK
|
394
|
+
AST::Break.new
|
395
|
+
end
|
396
|
+
|
397
|
+
def throw
|
398
|
+
assert_type next_token, :THROW
|
399
|
+
AST::Throw.new expression: expression
|
400
|
+
end
|
401
|
+
|
402
|
+
def delete
|
403
|
+
assert_type next_token, :DELETE
|
404
|
+
AST::Delete.new expression: expression
|
340
405
|
end
|
341
406
|
|
342
407
|
def var
|
@@ -347,7 +412,7 @@ module Twostroke
|
|
347
412
|
def var_rest
|
348
413
|
assert_type next_token, :BAREWORD
|
349
414
|
decl = AST::Declaration.new(name: token.val)
|
350
|
-
return decl if peek_token.type == :SEMICOLON
|
415
|
+
return decl if peek_token.type == :SEMICOLON || peek_token.type == :CLOSE_BRACE
|
351
416
|
|
352
417
|
assert_type next_token, :COMMA, :EQUALS
|
353
418
|
|
@@ -355,7 +420,7 @@ module Twostroke
|
|
355
420
|
AST::MultiExpression.new left: decl, right: var_rest
|
356
421
|
else
|
357
422
|
assignment = AST::Assignment.new left: decl, right: expression(true)
|
358
|
-
if peek_token.type == :SEMICOLON
|
423
|
+
if peek_token.type == :SEMICOLON || peek_token.type == :CLOSE_BRACE
|
359
424
|
assignment
|
360
425
|
elsif peek_token.type == :COMMA
|
361
426
|
next_token
|
@@ -386,6 +451,11 @@ module Twostroke
|
|
386
451
|
AST::String.new string: token.val
|
387
452
|
end
|
388
453
|
|
454
|
+
def regexp
|
455
|
+
assert_type next_token, :REGEXP
|
456
|
+
AST::Regexp.new regexp: token.val
|
457
|
+
end
|
458
|
+
|
389
459
|
def object_literal
|
390
460
|
assert_type next_token, :OPEN_BRACE
|
391
461
|
obj = AST::ObjectLiteral.new
|
@@ -480,14 +550,14 @@ module Twostroke
|
|
480
550
|
AST::PostDecrement.new value: obj
|
481
551
|
end
|
482
552
|
|
483
|
-
def pre_increment
|
553
|
+
def pre_increment
|
484
554
|
assert_type next_token, :INCREMENT
|
485
|
-
AST::PreIncrement.new value:
|
555
|
+
AST::PreIncrement.new value: expression_after_unary
|
486
556
|
end
|
487
557
|
|
488
|
-
def pre_decrement
|
558
|
+
def pre_decrement
|
489
559
|
assert_type next_token, :DECREMENT
|
490
|
-
AST::PreDecrement.new value:
|
560
|
+
AST::PreDecrement.new value: expression_after_unary
|
491
561
|
end
|
492
562
|
|
493
563
|
def typeof
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
class Context
|
3
|
+
attr_accessor :variables, :parent
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
variables = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](var)
|
10
|
+
if variables.key? var
|
11
|
+
variables[var]
|
12
|
+
elsif parent
|
13
|
+
parent[var]
|
14
|
+
else
|
15
|
+
raise "ReferenceError" # TODO
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(var, val)
|
20
|
+
if variables.key?(var) || parent.nil?
|
21
|
+
variables[var] = val
|
22
|
+
else
|
23
|
+
parent[var] = val
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_context
|
28
|
+
context = Context.new
|
29
|
+
context.parent = self
|
30
|
+
context
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Twostroke::Runtime::Types
|
2
|
+
class Object < BasicType
|
3
|
+
attr_accessor :properties, :accessor_properties
|
4
|
+
|
5
|
+
def initialize(env)
|
6
|
+
@properties = {}
|
7
|
+
@accessor_properties = {}
|
8
|
+
properties["__proto__"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
if accessor_properties.key? key
|
13
|
+
accessor_properties[key].get self
|
14
|
+
elsif properties.key? key
|
15
|
+
properties[key]
|
16
|
+
elsif properties.key? "__proto__"
|
17
|
+
properties["__proto__"][key]
|
18
|
+
else
|
19
|
+
Undefined.undefined
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, val)
|
24
|
+
if accessor_properties.key? key
|
25
|
+
accessor_properties[key].get self
|
26
|
+
elsif properties.key? key
|
27
|
+
properties[key]
|
28
|
+
else
|
29
|
+
Undefined.undefined
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/twostroke/tokens.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Twostroke
|
2
2
|
class Lexer
|
3
|
+
RESERVED = %w(function var if instanceof in else for while do this return throw typeof try catch finally void null new delete switch case break)
|
3
4
|
TOKENS = [
|
4
5
|
|
5
6
|
[ :MULTI_COMMENT, %r{/\*.*?\*/} ],
|
6
7
|
[ :SINGLE_COMMENT, /\/\/.*?$/ ],
|
7
8
|
|
8
9
|
[ :WHITESPACE, /\s+/ ],
|
9
|
-
[ :NUMBER,
|
10
|
+
[ :NUMBER, /((?<oct>0[0-7]+)|(?<hex>0x[A-Fa-f0-9]+)|(?<to_f>(\d+(\.?\d*([eE][+-]?\d+)?)?|\.\d+([eE][+-]?\d+)?)))/, ->m { m[0].send m.names.first } ],
|
10
11
|
|
11
|
-
|
12
|
+
*RESERVED.map do |w|
|
12
13
|
[ w.upcase.intern, /#{w}(?=[^a-zA-Z_0-9])/ ]
|
13
14
|
end,
|
14
15
|
[ :BAREWORD, /[a-zA-Z_\$][\$a-zA-Z_0-9]*/, ->m { m[0] } ],
|
@@ -28,6 +29,8 @@ module Twostroke
|
|
28
29
|
.gsub(/\\u([a-f0-9]{4})/i) { |m| m[1].to_i(16).chr }
|
29
30
|
.gsub(/\\(.)/) { |m| m[1] }
|
30
31
|
end ],
|
32
|
+
|
33
|
+
[ :REGEXP, %r{/(?<src>(\\.|[^\1])*?[^\1\\]?)/(?<opts>[gim]+)?}, ->m { [m[:src], m[:opts]] } ],
|
31
34
|
|
32
35
|
[ :OPEN_PAREN, /\(/ ],
|
33
36
|
[ :CLOSE_PAREN, /\)/ ],
|
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.4
|
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-15 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.
|
@@ -23,8 +23,12 @@ files:
|
|
23
23
|
- lib/twostroke/ast/assignment.rb
|
24
24
|
- lib/twostroke/ast/binary_operators.rb
|
25
25
|
- lib/twostroke/ast/body.rb
|
26
|
+
- lib/twostroke/ast/break.rb
|
26
27
|
- lib/twostroke/ast/call.rb
|
28
|
+
- lib/twostroke/ast/case.rb
|
27
29
|
- lib/twostroke/ast/declaration.rb
|
30
|
+
- lib/twostroke/ast/delete.rb
|
31
|
+
- lib/twostroke/ast/do_while.rb
|
28
32
|
- lib/twostroke/ast/for_in.rb
|
29
33
|
- lib/twostroke/ast/for_loop.rb
|
30
34
|
- lib/twostroke/ast/function.rb
|
@@ -36,10 +40,13 @@ files:
|
|
36
40
|
- lib/twostroke/ast/null.rb
|
37
41
|
- lib/twostroke/ast/number.rb
|
38
42
|
- lib/twostroke/ast/object_literal.rb
|
43
|
+
- lib/twostroke/ast/regexp.rb
|
39
44
|
- lib/twostroke/ast/return.rb
|
40
45
|
- lib/twostroke/ast/string.rb
|
46
|
+
- lib/twostroke/ast/switch.rb
|
41
47
|
- lib/twostroke/ast/ternary.rb
|
42
48
|
- lib/twostroke/ast/this.rb
|
49
|
+
- lib/twostroke/ast/throw.rb
|
43
50
|
- lib/twostroke/ast/try.rb
|
44
51
|
- lib/twostroke/ast/unary_operators.rb
|
45
52
|
- lib/twostroke/ast/unsorted_binop.rb
|
@@ -49,6 +56,14 @@ files:
|
|
49
56
|
- lib/twostroke/error.rb
|
50
57
|
- lib/twostroke/lexer.rb
|
51
58
|
- lib/twostroke/parser.rb
|
59
|
+
- lib/twostroke/runtime/context.rb
|
60
|
+
- lib/twostroke/runtime/environment.rb
|
61
|
+
- lib/twostroke/runtime/types/basic_type.rb
|
62
|
+
- lib/twostroke/runtime/types/null.rb
|
63
|
+
- lib/twostroke/runtime/types/object.rb
|
64
|
+
- lib/twostroke/runtime/types/undefined.rb
|
65
|
+
- lib/twostroke/runtime/types.rb
|
66
|
+
- lib/twostroke/runtime.rb
|
52
67
|
- lib/twostroke/tokens.rb
|
53
68
|
- lib/twostroke.rb
|
54
69
|
homepage: http://github.com/charliesome/twostroke
|