twostroke 0.0.1

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.
@@ -0,0 +1,14 @@
1
+ module Twostroke::AST
2
+ class Array < Base
3
+ attr_accessor :items
4
+
5
+ def initialize(*args)
6
+ @items = []
7
+ super *args
8
+ end
9
+
10
+ def collapse
11
+ self.class.new items: items.map(&:collapse)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class Assignment < Base
3
+ attr_accessor :left, :right
4
+
5
+ def collapse
6
+ self.class.new left: left.collapse, right: right.collapse
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Twostroke::AST
2
+ %w( Addition Subtraction Multiplication Division Modulus
3
+ LeftShift RightArithmeticShift RightLogicalShift
4
+ LessThan LessThanEqual GreaterThan GreaterThanEqual
5
+ In InstanceOf Equality Inequality StrictEquality
6
+ StrictInequality BitwiseAnd BitwiseXor BitwiseOr
7
+ And Or).each do |op|
8
+ klass = Class.new Base do
9
+ attr_accessor :left, :right
10
+ end
11
+ const_set op, klass
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Twostroke::AST
2
+ class Body < Base
3
+ attr_accessor :statements
4
+
5
+ def initialize(*args)
6
+ @statements = []
7
+ super *args
8
+ end
9
+
10
+ def collapse
11
+ self.class.new statements: statements.reject(&:nil?).map(&:collapse)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Twostroke::AST
2
+ class Call < 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,9 @@
1
+ module Twostroke::AST
2
+ class Declaration < Base
3
+ attr_accessor :name
4
+
5
+ def collapse
6
+ self
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class ForLoop < Base
3
+ attr_accessor :initializer, :condition, :increment, :body
4
+
5
+ def collapse
6
+ self.class.new initializer: initializer.collapse, condition: condition.collapse, increment: increment.collapse, body: body.collapse
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Twostroke::AST
2
+ class Function < Base
3
+ attr_accessor :name, :arguments, :statements
4
+
5
+ def initialize(*args)
6
+ @arguments = []
7
+ @statements = []
8
+ super *args
9
+ end
10
+
11
+ def collapse
12
+ self.class.new name: name, arguments: arguments, statements: statements.reject(&:nil?).map(&:collapse)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'pry'
2
+ module Twostroke::AST
3
+ class If < Base
4
+ attr_accessor :condition, :then, :else
5
+
6
+ def collapse
7
+ self.class.new condition: condition.collapse, then: @then.collapse, else: @else && @else.collapse
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class Index < Base
3
+ attr_accessor :object, :index
4
+
5
+ def collapse
6
+ self.class.new object: object.collapse, index: index.collapse
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class MemberAccess < Base
3
+ attr_accessor :object, :member
4
+
5
+ def collapse
6
+ self.class.new object: object.collapse, member: member
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class MultiExpression < Base
3
+ attr_accessor :left, :right
4
+
5
+ def collapse
6
+ self.class.new left: left.collapse, right: right.collapse
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class Number < Base
3
+ attr_accessor :number
4
+
5
+ def collapse
6
+ self
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Twostroke::AST
2
+ class ObjectLiteral < Base
3
+ attr_accessor :items
4
+
5
+ def initialize(*args)
6
+ @items = []
7
+ super *args
8
+ end
9
+
10
+ def collapse
11
+ collapsed = items.map { |k,v| [k, v.collapse] }
12
+ self.class.new items: collapsed
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class Return < Base
3
+ attr_accessor :expression
4
+
5
+ def collapse
6
+ self.class.new expression: expression.collapse
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class String < Base
3
+ attr_accessor :string
4
+
5
+ def collapse
6
+ self
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class Ternary < Base
3
+ attr_accessor :condition, :if_true, :if_false
4
+
5
+ def collapse
6
+ self.class.new condition: condition.collapse, if_true: if_true.collapse, if_false: if_false.collapse
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Twostroke::AST
2
+ [ :PostIncrement, :PreIncrement, :PostDecrement, :PreDecrement,
3
+ :BinaryNot, :UnaryPlus, :Negation, :TypeOf, :Not ].each do |op|
4
+ klass = Class.new Base do
5
+ attr_accessor :value
6
+
7
+ def collapse
8
+ self.class.new value: value.collapse
9
+ end
10
+ end
11
+ const_set op, klass
12
+ end
13
+ end
@@ -0,0 +1,99 @@
1
+ module Twostroke::AST
2
+ class UnsortedBinop < Base
3
+ attr_accessor :left, :op, :right
4
+
5
+ def self.operator_class
6
+ @@classes ||= {
7
+ :ASTERISK => Multiplication,
8
+ :SLASH => Division,
9
+ :MOD => Modulus,
10
+ :PLUS => Addition,
11
+ :MINUS => Subtraction,
12
+ :LEFT_SHIFT => LeftShift,
13
+ :RIGHT_SHIFT => RightArithmeticShift,
14
+ :RIGHT_TRIPLE_SHIFT => RightLogicalShift,
15
+ :LT => LessThan,
16
+ :LTE => LessThanEqual,
17
+ :GT => GreaterThan,
18
+ :GTE => GreaterThanEqual,
19
+ :IN => In,
20
+ :INSTANCE_OF => InstanceOf,
21
+ :DOUBLE_EQUALS => Equality,
22
+ :NOT_EQUALS => Inequality,
23
+ :TRIPLE_EQUALS => StrictEquality,
24
+ :NOT_DOUBLE_EQUALS => StrictInequality,
25
+ :AMPERSAND => BitwiseAnd,
26
+ :CARET => BitwiseXor,
27
+ :PIPE => BitwiseOr,
28
+ :AND => And,
29
+ :OR => Or
30
+
31
+ }
32
+ end
33
+
34
+ def self.operator_precedence
35
+ @precedences ||= {
36
+ :ASTERISK => 5,
37
+ :SLASH => 5,
38
+ :MOD => 5,
39
+ :PLUS => 6,
40
+ :MINUS => 6,
41
+ :LEFT_SHIFT => 7,
42
+ :RIGHT_SHIFT => 7,
43
+ :RIGHT_TRIPLE_SHIFT => 7,
44
+ :LT => 8,
45
+ :LTE => 8,
46
+ :GT => 8,
47
+ :GTE => 8,
48
+ :IN => 8,
49
+ :INSTANCE_OF => 8,
50
+ :DOUBLE_EQUALS => 9,
51
+ :NOT_EQUALS => 9,
52
+ :TRIPLE_EQUALS => 9,
53
+ :NOT_DOUBLE_EQUALS => 9,
54
+ :AMPERSAND => 10,
55
+ :CARET => 11,
56
+ :PIPE => 12,
57
+ :AND => 13,
58
+ :OR => 14
59
+ }
60
+ end
61
+
62
+ def collapse(called_by_binop = false)
63
+ left_collapsed = left.is_a?(UnsortedBinop) ? left.collapse(true) : left.collapse
64
+ right_collapsed = right.is_a?(UnsortedBinop) ? right.collapse(true) : right.collapse
65
+ input = [*left_collapsed, op, *right_collapsed]
66
+
67
+ unless called_by_binop
68
+ stack = []
69
+ output = []
70
+ input.each do |token|
71
+ if token.is_a? Symbol
72
+ while stack.size > 0 && UnsortedBinop.operator_precedence[stack.last] <= UnsortedBinop.operator_precedence[token]
73
+ output.push stack.pop
74
+ end
75
+ stack.push token
76
+ else
77
+ output.push token
78
+ end
79
+ end
80
+ output.push stack.pop until stack.empty?
81
+
82
+ output.each do |token|
83
+ if token.is_a? Symbol
84
+ r = stack.pop
85
+ l = stack.pop
86
+ puts token
87
+ stack.push UnsortedBinop.operator_class[token].new(left: l, right: r)
88
+ else
89
+ stack.push token
90
+ end
91
+ end
92
+
93
+ stack.last
94
+ else
95
+ input
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,9 @@
1
+ module Twostroke::AST
2
+ class Variable < Base
3
+ attr_accessor :name
4
+
5
+ def collapse
6
+ self
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Twostroke
2
+ module AST
3
+ class Base
4
+ def initialize(hash = {})
5
+ hash.each do |k,v|
6
+ send "#{k}=", v
7
+ end
8
+ end
9
+ end
10
+
11
+ Dir.glob File.expand_path("../ast/*", __FILE__) do |f|
12
+ require f
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module Twostroke
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,51 @@
1
+ module Twostroke
2
+ class LexError < Twostroke::Error
3
+ end
4
+
5
+ class Token
6
+ attr_accessor :type, :val, :line, :col
7
+ def initialize(hash = {})
8
+ hash.each do |k,v|
9
+ send "#{k}=", v
10
+ end
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
+ end
20
+
21
+ class Lexer
22
+ attr_reader :str, :col, :line, :tokens
23
+ def initialize(str)
24
+ @str = str
25
+ @col = 1
26
+ @line = 1
27
+ @tokens = []
28
+ end
29
+ def lex
30
+ until @str.empty?
31
+ read_token
32
+ end
33
+ end
34
+ private
35
+ def read_token
36
+ TOKENS.each do |token|
37
+ m = token[1].match @str
38
+ if m
39
+ @tokens.push Token.new(:type => token[0], :val => token[2] ? token[2].call(m) : nil, :line => @line, :col => @col) unless [:WHITESPACE, :MULTI_COMMENT, :SINGLE_COMMENT].include? token[0]
40
+ @str = m.post_match
41
+ newlines = m[0].count "\n"
42
+ @col = 1 if !newlines.zero?
43
+ @line += newlines
44
+ @col += m[0].length - (m[0].rindex("\n") || 0)
45
+ return
46
+ end
47
+ end
48
+ raise LexError, "Illegal character '#{@str[0]}' at line #{@line}, col #{@col}. (read #{@tokens.count} tokens)"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,379 @@
1
+ module Twostroke
2
+ class ParseError < Error
3
+ end
4
+
5
+ class Parser
6
+ attr_reader :statements
7
+
8
+ def initialize(tokens)
9
+ @i = -1
10
+ @tokens = tokens + [Token.new(type: :SEMICOLON)]
11
+ @statements = []
12
+ end
13
+
14
+ def parse
15
+ while @i + 1 < @tokens.length
16
+ st = statement
17
+ statements.push st.collapse if st
18
+ end
19
+ end
20
+
21
+ private
22
+ def error!(msg)
23
+ raise ParseError, "Syntax error at line #{token.line}, col #{token.col}. #{msg}"
24
+ end
25
+ def assert_type(tok, *types)
26
+ error! "Found #{tok.type}#{"<#{tok.val}>" if tok.val}, expected #{types.join ", "}" unless types.include? tok.type
27
+ end
28
+ def stack
29
+ @stack
30
+ end
31
+ def stack_top
32
+ @stack.last
33
+ end
34
+ def token
35
+ @tokens[@i] or raise ParseError, "unexpected end of input"
36
+ end
37
+ def next_token
38
+ @i += 1
39
+ token
40
+ end
41
+ def try_peek_token
42
+ @i + 1 < @tokens.length ? peek_token : nil
43
+ end
44
+ def peek_token
45
+ @tokens[@i + 1] or raise ParseError, "unexpected end of input"
46
+ end
47
+ def look_ahead(n = 1)
48
+ @tokens[@i + n]
49
+ end
50
+
51
+ ####################
52
+
53
+ def statement(consume_semicolon = true)
54
+ st = case peek_token.type
55
+ when :RETURN; send :return
56
+ when :VAR; var
57
+ when :IF; consume_semicolon = false; send :if
58
+ when :FOR; consume_semicolon = false; send :for
59
+ when :OPEN_BRACE; consume_semicolon = false; body
60
+ when :SEMICOLON; nil
61
+ else; expression
62
+ end
63
+ if consume_semicolon
64
+ #next_token if try_peek_token && peek_token.type == :SEMICOLON
65
+ assert_type next_token, :SEMICOLON
66
+ end
67
+ st
68
+ end
69
+
70
+ def expression(no_comma = false)
71
+ expr = expression_after_unary no_comma
72
+ if [:PLUS, :MINUS, :ASTERISK, :SLASH, :GT, :LT,
73
+ :GTE, :LTE, :DOUBLE_EQUALS, :TRIPLE_EQUALS,
74
+ :NOT_EQUALS, :NOT_DOUBLE_EQUALS, :AND, :OR,
75
+ :AMPERSAND, :PIPE, :CARET, :MOD, :LEFT_SHIFT,
76
+ :RIGHT_SHIFT, :RIGHT_TRIPLE_SHIFT, ].include? peek_token.type
77
+ binop expr
78
+ elsif peek_token.type == :EQUALS
79
+ next_token
80
+ AST::Assignment.new left: expr, right: expression(no_comma)
81
+ elsif peek_token.type == :QUESTION
82
+ ternary(expr)
83
+ else
84
+ expr
85
+ end
86
+ end
87
+
88
+ def expression_after_unary(no_comma = false)
89
+ expr = case peek_token.type
90
+ when :FUNCTION; function
91
+ when :STRING; string
92
+ when :NUMBER; number
93
+ when :BAREWORD; bareword
94
+ when :OPEN_PAREN; parens
95
+ when :OPEN_BRACE; object_literal
96
+ when :OPEN_BRACKET; array
97
+ when :NOT; send :not
98
+ when :TILDE; tilde
99
+ when :INCREMENT; pre_increment
100
+ when :DECREMENT; pre_decrement
101
+ when :PLUS; unary_plus
102
+ when :MINUS; unary_minus
103
+ when :TYPEOF; typeof
104
+ else error! "Unexpected #{peek_token.type}"
105
+ end
106
+ loop do
107
+ if peek_token.type == :OPEN_PAREN
108
+ expr = call expr
109
+ elsif peek_token.type == :OPEN_BRACKET
110
+ expr = index expr
111
+ elsif peek_token.type == :MEMBER_ACCESS
112
+ expr = member_access expr
113
+ elsif !no_comma && peek_token.type == :COMMA
114
+ expr = comma(expr)
115
+ elsif peek_token.type == :INCREMENT
116
+ expr = post_increment expr
117
+ elsif peek_token.type == :DECREMENT
118
+ expr = post_decrement expr
119
+ else
120
+ return expr
121
+ end
122
+ end
123
+ expr
124
+ end
125
+
126
+ def binop(left)
127
+ next_token
128
+ AST::UnsortedBinop.new left: left, op: token.type, right: expression
129
+ end
130
+
131
+ def body
132
+ assert_type next_token, :OPEN_BRACE
133
+ body = AST::Body.new
134
+ while peek_token.type != :CLOSE_BRACE
135
+ body.statements.push statement
136
+ end
137
+ assert_type next_token, :CLOSE_BRACE
138
+ body
139
+ end
140
+
141
+ def bareword
142
+ assert_type next_token, :BAREWORD
143
+ AST::Variable.new name: token.val
144
+ end
145
+
146
+ def ternary(cond)
147
+ assert_type next_token, :QUESTION
148
+ ternary = AST::Ternary.new condition: cond
149
+ ternary.if_true = expression
150
+ assert_type next_token, :COLON
151
+ ternary.if_false = expression
152
+ ternary
153
+ end
154
+
155
+ def if
156
+ assert_type next_token, :IF
157
+ assert_type next_token, :OPEN_PAREN
158
+ node = AST::If.new condition: expression
159
+ assert_type next_token, :CLOSE_PAREN
160
+ node.then = statement
161
+ if try_peek_token && peek_token.type == :ELSE
162
+ assert_type next_token, :ELSE
163
+ node.else = statement
164
+ end
165
+ node
166
+ end
167
+
168
+ def for
169
+ assert_type next_token, :FOR
170
+ assert_type next_token, :OPEN_PAREN
171
+ # decide if this is a for(... in ...) or a for(;;) loop
172
+ stmt = statement(false)
173
+ assert_type next_token, :SEMICOLON, :CLOSE_PAREN
174
+ if token.type == :CLOSE_PAREN
175
+ # this is a for(... in ...) loop. we'll figure out how to deal with that in a sec
176
+ error! "for..in loops not implemented yet!" # @TODO
177
+ else
178
+ initializer = stmt
179
+ condition = statement(false)
180
+ assert_type next_token, :SEMICOLON
181
+ increment = statement(false)
182
+ assert_type next_token, :CLOSE_PAREN
183
+ AST::ForLoop.new initializer: initializer, condition: condition, increment: increment, body: statement
184
+ end
185
+ end
186
+
187
+ def member_access(obj)
188
+ assert_type next_token, :MEMBER_ACCESS
189
+ assert_type next_token, :BAREWORD
190
+ access = AST::MemberAccess.new object: obj, member: token.val
191
+ if peek_token.type == :MEMBER_ACCESS
192
+ member_access access
193
+ elsif peek_token.type == :OPEN_PAREN
194
+ call access
195
+ elsif peek_token.type == :EQUALS
196
+ assignment access
197
+ else
198
+ access
199
+ end
200
+ end
201
+
202
+ def call(callee)
203
+ assert_type next_token, :OPEN_PAREN
204
+ c = AST::Call.new callee: callee
205
+ while peek_token.type != :CLOSE_PAREN
206
+ c.arguments.push expression(true)
207
+ if peek_token.type == :COMMA
208
+ next_token
209
+ redo
210
+ end
211
+ end
212
+ next_token
213
+ c
214
+ end
215
+
216
+ def index(obj)
217
+ assert_type next_token, :OPEN_BRACKET
218
+ ind = expression
219
+ assert_type next_token, :CLOSE_BRACKET
220
+ AST::Index.new object: obj, index: ind
221
+ end
222
+
223
+ def return
224
+ assert_type next_token, :RETURN
225
+ AST::Return.new expression: expression
226
+ end
227
+
228
+ def var
229
+ assert_type next_token, :VAR
230
+ var_rest
231
+ end
232
+
233
+ def var_rest
234
+ assert_type next_token, :BAREWORD
235
+ decl = AST::Declaration.new(name: token.val)
236
+ return decl if peek_token.type == :SEMICOLON
237
+
238
+ assert_type next_token, :COMMA, :EQUALS
239
+
240
+ if token.type == :COMMA
241
+ AST::MultiExpression.new left: decl, right: var_rest
242
+ else
243
+ assignment = AST::Assignment.new left: decl, right: expression(true)
244
+ if peek_token.type == :SEMICOLON
245
+ assignment
246
+ elsif peek_token.type == :COMMA
247
+ next_token
248
+ AST::MultiExpression.new left: assignment, right: var_rest
249
+ else
250
+ error! "Unexpected #{peek_token.type}"
251
+ end
252
+ end
253
+ end
254
+
255
+ def assignment(lval)
256
+ assert_type next_token, :EQUALS
257
+ AST::Assignment.new left: lval, right: expression
258
+ end
259
+
260
+ def comma(left)
261
+ assert_type next_token, :COMMA
262
+ AST::MultiExpression.new left: left, right: expression
263
+ end
264
+
265
+ def number
266
+ assert_type next_token, :NUMBER
267
+ AST::Number.new number: token.val
268
+ end
269
+
270
+ def string
271
+ assert_type next_token, :STRING
272
+ AST::String.new string: token.val
273
+ end
274
+
275
+ def object_literal
276
+ assert_type next_token, :OPEN_BRACE
277
+ obj = AST::ObjectLiteral.new
278
+ while peek_token.type != :CLOSE_BRACE
279
+ assert_type next_token, :BAREWORD, :STRING, :NUMBER
280
+ key = token
281
+ assert_type next_token, :COLON
282
+ obj.items.push [key, expression(true)]
283
+ if peek_token.type == :COMMA
284
+ next_token
285
+ redo
286
+ end
287
+ end
288
+ next_token
289
+ obj
290
+ end
291
+
292
+ def array
293
+ assert_type next_token, :OPEN_BRACKET
294
+ ary = AST::Array.new
295
+ while peek_token.type != :CLOSE_BRACKET
296
+ ary.items.push expression(true)
297
+ if peek_token.type == :COMMA
298
+ next_token
299
+ redo
300
+ end
301
+ end
302
+ next_token
303
+ ary
304
+ end
305
+
306
+ def parens
307
+ assert_type next_token, :OPEN_PAREN
308
+ expr = expression
309
+ assert_type next_token, :CLOSE_PAREN
310
+ expr
311
+ end
312
+
313
+ def function
314
+ assert_type next_token, :FUNCTION
315
+ fn = AST::Function.new arguments: [], statements: []
316
+ error! unless [:BAREWORD, :OPEN_PAREN].include? next_token.type
317
+ if token.type == :BAREWORD
318
+ fn.name = token.val
319
+ assert_type next_token, :OPEN_PAREN
320
+ end
321
+ while peek_token.type == :BAREWORD
322
+ fn.arguments.push next_token.val
323
+ next_token if peek_token.type == :COMMA
324
+ end
325
+ assert_type next_token, :CLOSE_PAREN
326
+ assert_type next_token, :OPEN_BRACE
327
+ while peek_token.type != :CLOSE_BRACE
328
+ fn.statements.push statement
329
+ end
330
+ assert_type next_token, :CLOSE_BRACE
331
+ fn
332
+ end
333
+
334
+ def not
335
+ assert_type next_token, :NOT
336
+ AST::Not.new value: expression_after_unary
337
+ end
338
+
339
+ def tilde
340
+ assert_type next_token, :TILDE
341
+ AST::BinaryNot.new value: expression_after_unary
342
+ end
343
+
344
+ def unary_plus
345
+ assert_type next_token, :PLUS
346
+ AST::UnaryPlus.new value: expression_after_unary
347
+ end
348
+
349
+ def unary_minus
350
+ assert_type next_token, :MINUS
351
+ AST::Negation.new value: expression_after_unary
352
+ end
353
+
354
+ def post_increment(obj)
355
+ assert_type next_token, :INCREMENT
356
+ AST::PostIncrement.new value: obj
357
+ end
358
+
359
+ def post_decrement(obj)
360
+ assert_type next_token, :INCREMENT
361
+ AST::PostDecrement.new value: obj
362
+ end
363
+
364
+ def pre_increment(obj)
365
+ assert_type next_token, :INCREMENT
366
+ AST::PreIncrement.new value: obj
367
+ end
368
+
369
+ def pre_decrement(obj)
370
+ assert_type next_token, :INCREMENT
371
+ AST::PreDecrement.new value: obj
372
+ end
373
+
374
+ def typeof
375
+ assert_type next_token, :TYPEOF
376
+ AST::TypeOf.new value: expression_after_unary
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,78 @@
1
+ module Twostroke
2
+ class Lexer
3
+ TOKENS = [
4
+
5
+ [ :MULTI_COMMENT, %r{/\*.*?\*/} ],
6
+ [ :SINGLE_COMMENT, /\/\/.*?$/ ],
7
+
8
+ [ :WHITESPACE, /\s+/ ],
9
+ [ :NUMBER, /\d+(\.\d*(e[+-]?\d+)?)?/, ->m { m[0].to_f } ],
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}/ ]
13
+ end,
14
+ [ :BAREWORD, /[a-zA-Z_][a-zA-Z_0-9]*/, ->m { m[0] } ],
15
+
16
+ [ :STRING, /(["'])((\\.|[^\1])*?[^\1\\]?)\1/, ->m do
17
+ m[2].gsub(/\\([bfnrt])/) { |m|
18
+ case m[1]
19
+ when "b"; "\b"
20
+ when "n"; "\n"
21
+ when "f"; "\f"
22
+ when "r"; "\r"
23
+ when "t"; "\t"
24
+ end
25
+ }
26
+ .gsub(/\\([0-6]{1,3})/) { |m| m[1].to_i(7).chr }
27
+ .gsub(/\\x([a-f0-9]{2})/i) { |m| m[1].to_i(16).chr }
28
+ .gsub(/\\u([a-f0-9]{4})/i) { |m| m[1].to_i(16).chr }
29
+ .gsub(/\\(.)/) { |m| m[1] }
30
+ end ],
31
+
32
+ [ :OPEN_PAREN, /\(/ ],
33
+ [ :CLOSE_PAREN, /\)/ ],
34
+ [ :OPEN_BRACKET, /\[/ ],
35
+ [ :CLOSE_BRACKET, /\]/ ],
36
+ [ :OPEN_BRACE, /\{/ ],
37
+ [ :CLOSE_BRACE, /\}/ ],
38
+
39
+ [ :MEMBER_ACCESS, /\./ ],
40
+
41
+ [ :INCREMENT, /\+\+/ ],
42
+ [ :DECREMENT, /--/ ],
43
+ [ :PLUS, /\+/ ],
44
+ [ :MINUS, /-/ ],
45
+ [ :ASTERISK, /\*/ ],
46
+ [ :SLASH, /\// ],
47
+ [ :MOD, /%/ ],
48
+ [ :QUESTION, /\?/ ],
49
+ [ :COMMA, /,/ ],
50
+ [ :SEMICOLON, /;/ ],
51
+ [ :COLON, /:/ ],
52
+
53
+ [ :AND, /&&/ ],
54
+ [ :AMPERSAND, /&/ ],
55
+ [ :OR, /\|\|/ ],
56
+ [ :PIPE, /\|/ ],
57
+ [ :TRIPLE_EQUALS, /===/ ],
58
+ [ :DOUBLE_EQUALS, /==/ ],
59
+ [ :EQUALS, /=/ ],
60
+ [ :NOT_DOUBLE_EQUALS, /!==/ ],
61
+ [ :NOT_EQUALS, /!=/ ],
62
+ [ :NOT, /!/ ],
63
+ [ :TILDE, /~/ ],
64
+ [ :CARET, /\^/ ],
65
+
66
+ [ :LEFT_SHIFT, /<</ ],
67
+ [ :RIGHT_TRIPLE_SHIFT, />>>/ ],
68
+ [ :RIGHT_SHIFT, />>/ ],
69
+ [ :LTE, /<=/ ],
70
+ [ :GTE, />=/ ],
71
+ [ :LT, /</ ],
72
+ [ :GT, />/ ],
73
+
74
+ ].map do |a|
75
+ [a[0], Regexp.new("\\A#{a[1].source}", Regexp::MULTILINE), a[2]]
76
+ end
77
+ end
78
+ end
data/lib/twostroke.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "twostroke/error"
2
+ require "twostroke/tokens"
3
+ require "twostroke/lexer"
4
+ require "twostroke/parser"
5
+ require "twostroke/ast"
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twostroke
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Charlie Somerville
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-04 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: An implementation of Javascript written in pure Ruby. This project currently
15
+ includes a WIP parser, and will include a runtime.
16
+ email:
17
+ - charlie@charliesomerville.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/twostroke/ast/array.rb
23
+ - lib/twostroke/ast/assignment.rb
24
+ - lib/twostroke/ast/binary_operators.rb
25
+ - lib/twostroke/ast/body.rb
26
+ - lib/twostroke/ast/call.rb
27
+ - lib/twostroke/ast/declaration.rb
28
+ - lib/twostroke/ast/for_loop.rb
29
+ - lib/twostroke/ast/function.rb
30
+ - lib/twostroke/ast/if.rb
31
+ - lib/twostroke/ast/index.rb
32
+ - lib/twostroke/ast/member_access.rb
33
+ - lib/twostroke/ast/multi_expression.rb
34
+ - lib/twostroke/ast/number.rb
35
+ - lib/twostroke/ast/object_literal.rb
36
+ - lib/twostroke/ast/return.rb
37
+ - lib/twostroke/ast/string.rb
38
+ - lib/twostroke/ast/ternary.rb
39
+ - lib/twostroke/ast/unary_operators.rb
40
+ - lib/twostroke/ast/unsorted_binop.rb
41
+ - lib/twostroke/ast/variable.rb
42
+ - lib/twostroke/ast.rb
43
+ - lib/twostroke/error.rb
44
+ - lib/twostroke/lexer.rb
45
+ - lib/twostroke/parser.rb
46
+ - lib/twostroke/tokens.rb
47
+ - lib/twostroke.rb
48
+ homepage: http://github.com/charliesome/twostroke
49
+ licenses: []
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.10
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: A Ruby implementation of Javascript
72
+ test_files: []