twostroke 0.0.3 → 0.0.4
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/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
|