twostroke 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/twostroke/ast/array.rb +8 -0
- data/lib/twostroke/ast/assignment.rb +7 -0
- data/lib/twostroke/ast/binary_operators.rb +7 -0
- data/lib/twostroke/ast/body.rb +6 -0
- data/lib/twostroke/ast/break.rb +4 -0
- data/lib/twostroke/ast/call.rb +7 -0
- data/lib/twostroke/ast/case.rb +9 -1
- data/lib/twostroke/ast/continue.rb +11 -0
- data/lib/twostroke/ast/declaration.rb +4 -0
- data/lib/twostroke/ast/delete.rb +6 -0
- data/lib/twostroke/ast/do_while.rb +7 -0
- data/lib/twostroke/ast/false.rb +11 -0
- data/lib/twostroke/ast/for_in.rb +8 -0
- data/lib/twostroke/ast/for_loop.rb +10 -1
- data/lib/twostroke/ast/function.rb +6 -0
- data/lib/twostroke/ast/if.rb +8 -0
- data/lib/twostroke/ast/index.rb +7 -0
- data/lib/twostroke/ast/member_access.rb +6 -0
- data/lib/twostroke/ast/multi_expression.rb +7 -0
- data/lib/twostroke/ast/new.rb +7 -0
- data/lib/twostroke/ast/null.rb +4 -0
- data/lib/twostroke/ast/number.rb +4 -0
- data/lib/twostroke/ast/object_literal.rb +6 -0
- data/lib/twostroke/ast/regexp.rb +4 -0
- data/lib/twostroke/ast/return.rb +6 -0
- data/lib/twostroke/ast/string.rb +4 -0
- data/lib/twostroke/ast/switch.rb +7 -0
- data/lib/twostroke/ast/ternary.rb +8 -0
- data/lib/twostroke/ast/this.rb +4 -0
- data/lib/twostroke/ast/throw.rb +6 -0
- data/lib/twostroke/ast/true.rb +11 -0
- data/lib/twostroke/ast/try.rb +8 -0
- data/lib/twostroke/ast/unary_operators.rb +7 -1
- data/lib/twostroke/ast/variable.rb +4 -0
- data/lib/twostroke/ast/while.rb +8 -1
- data/lib/twostroke/ast/with.rb +16 -0
- data/lib/twostroke/compiler/javascript.rb +396 -0
- data/lib/twostroke/compiler/tsasm.rb +595 -0
- data/lib/twostroke/compiler.rb +8 -0
- data/lib/twostroke/parser.rb +47 -9
- data/lib/twostroke/runtime/lib/array.js +144 -0
- data/lib/twostroke/runtime/lib/array.rb +71 -0
- data/lib/twostroke/runtime/lib/boolean.rb +23 -0
- data/lib/twostroke/runtime/lib/console.rb +13 -0
- data/lib/twostroke/runtime/lib/date.rb +65 -0
- data/lib/twostroke/runtime/lib/error.rb +36 -0
- data/lib/twostroke/runtime/lib/function.js +0 -0
- data/lib/twostroke/runtime/lib/function.rb +45 -0
- data/lib/twostroke/runtime/lib/math.rb +36 -0
- data/lib/twostroke/runtime/lib/number.rb +65 -0
- data/lib/twostroke/runtime/lib/object.js +0 -0
- data/lib/twostroke/runtime/lib/object.rb +55 -0
- data/lib/twostroke/runtime/lib/regexp.rb +28 -0
- data/lib/twostroke/runtime/lib/string.rb +86 -0
- data/lib/twostroke/runtime/lib/undefined.rb +7 -0
- data/lib/twostroke/runtime/lib.rb +42 -0
- data/lib/twostroke/runtime/scope.rb +120 -0
- data/lib/twostroke/runtime/types/array.rb +79 -0
- data/lib/twostroke/runtime/types/boolean.rb +23 -0
- data/lib/twostroke/runtime/types/boolean_object.rb +25 -0
- data/lib/twostroke/runtime/types/function.rb +83 -0
- data/lib/twostroke/runtime/types/null.rb +11 -3
- data/lib/twostroke/runtime/types/number.rb +31 -0
- data/lib/twostroke/runtime/types/number_object.rb +25 -0
- data/lib/twostroke/runtime/types/object.rb +169 -20
- data/lib/twostroke/runtime/types/regexp.rb +31 -0
- data/lib/twostroke/runtime/types/string.rb +16 -0
- data/lib/twostroke/runtime/types/string_object.rb +52 -0
- data/lib/twostroke/runtime/types/undefined.rb +11 -3
- data/lib/twostroke/runtime/types/value.rb +14 -0
- data/lib/twostroke/runtime/types.rb +133 -4
- data/lib/twostroke/runtime/vm.rb +25 -0
- data/lib/twostroke/runtime/vm_frame.rb +459 -0
- data/lib/twostroke/runtime.rb +6 -5
- data/lib/twostroke/tokens.rb +20 -8
- data/lib/twostroke.rb +3 -1
- metadata +41 -7
- data/lib/twostroke/runtime/context.rb +0 -33
- data/lib/twostroke/runtime/environment.rb +0 -13
- data/lib/twostroke/runtime/types/basic_type.rb +0 -5
@@ -0,0 +1,459 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
class VM::Frame
|
3
|
+
attr_reader :vm, :insns, :stack, :sp_stack, :catch_stack, :finally_stack, :enum_stack, :exception, :ip, :scope
|
4
|
+
|
5
|
+
def initialize(vm, section, callee = nil)
|
6
|
+
@vm = vm
|
7
|
+
@section = section
|
8
|
+
@insns = vm.bytecode[section]
|
9
|
+
@callee = callee
|
10
|
+
end
|
11
|
+
|
12
|
+
def arguments_object
|
13
|
+
arguments = Types::Object.new
|
14
|
+
@args.each_with_index { |arg,i| arguments.put i.to_s, arg }
|
15
|
+
arguments.put "length", Types::Number.new(@args.size)
|
16
|
+
arguments.put "callee", @callee
|
17
|
+
arguments
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute(scope, this = nil, args = [])
|
21
|
+
@scope = scope || Scope.new(vm.global_scope)
|
22
|
+
@stack = []
|
23
|
+
@sp_stack = []
|
24
|
+
@catch_stack = []
|
25
|
+
@finally_stack = []
|
26
|
+
@enum_stack = []
|
27
|
+
@ip = 0
|
28
|
+
@return = false
|
29
|
+
@this = this || @scope.global_scope.root_object
|
30
|
+
@args = args
|
31
|
+
if @callee
|
32
|
+
# the arguments object is only available within functions
|
33
|
+
scope.declare :arguments
|
34
|
+
scope.set_var :arguments, arguments_object
|
35
|
+
end
|
36
|
+
|
37
|
+
until @return
|
38
|
+
ins, arg = *insns[ip]
|
39
|
+
st = @stack.size
|
40
|
+
@ip += 1
|
41
|
+
if respond_to? ins
|
42
|
+
if @exception = catch(:exception) { public_send ins, arg; nil }
|
43
|
+
throw :exception, @exception if catch_stack.empty?
|
44
|
+
@ip = catch_stack.last
|
45
|
+
end
|
46
|
+
else
|
47
|
+
error! "unknown instruction #{ins}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
stack.last
|
52
|
+
end
|
53
|
+
|
54
|
+
define_method ".name" do |arg|
|
55
|
+
scope.declare arg.intern
|
56
|
+
scope.set_var arg.intern, @callee
|
57
|
+
end
|
58
|
+
|
59
|
+
define_method ".local" do |arg|
|
60
|
+
scope.declare arg.intern
|
61
|
+
end
|
62
|
+
|
63
|
+
define_method ".arg" do |arg|
|
64
|
+
scope.declare arg.intern
|
65
|
+
scope.set_var arg.intern, @args.shift || Types::Undefined.new
|
66
|
+
end
|
67
|
+
|
68
|
+
define_method ".catch" do |arg|
|
69
|
+
scope.declare arg.intern
|
70
|
+
scope.set_var arg.intern, @exception
|
71
|
+
end
|
72
|
+
|
73
|
+
## instructions
|
74
|
+
|
75
|
+
def push(arg)
|
76
|
+
if arg.is_a? Symbol
|
77
|
+
stack.push scope.get_var(arg)
|
78
|
+
elsif arg.is_a?(Fixnum) || arg.is_a?(Float)
|
79
|
+
stack.push Types::Number.new(arg)
|
80
|
+
elsif arg.is_a?(Bignum)
|
81
|
+
stack.push Types::Number.new(arg.to_f)
|
82
|
+
elsif arg.is_a?(String)
|
83
|
+
stack.push Types::String.new(arg)
|
84
|
+
else
|
85
|
+
error! "bad argument to push instruction"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def call(arg)
|
90
|
+
args = []
|
91
|
+
arg.times { args.unshift @stack.pop }
|
92
|
+
fun = stack.pop
|
93
|
+
Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
|
94
|
+
stack.push fun.call(scope, scope.global_scope.root_object, args)
|
95
|
+
end
|
96
|
+
|
97
|
+
def thiscall(arg)
|
98
|
+
args = []
|
99
|
+
arg.times { args.unshift stack.pop }
|
100
|
+
fun = stack.pop
|
101
|
+
Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
|
102
|
+
stack.push fun.call(scope, Types.to_object(stack.pop), args)
|
103
|
+
end
|
104
|
+
|
105
|
+
def newcall(arg)
|
106
|
+
args = []
|
107
|
+
arg.times { args.unshift @stack.pop }
|
108
|
+
fun = stack.pop
|
109
|
+
Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
|
110
|
+
obj = Types::Object.new
|
111
|
+
obj.construct prototype: fun.get("prototype"), _class: fun do
|
112
|
+
retn = fun.call(scope, obj, args)
|
113
|
+
if retn.is_a?(Types::Undefined)
|
114
|
+
stack.push obj
|
115
|
+
else
|
116
|
+
stack.push retn
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def dup(arg)
|
122
|
+
n = arg || 1
|
123
|
+
stack.push *stack[-n..-1]
|
124
|
+
end
|
125
|
+
|
126
|
+
def member(arg)
|
127
|
+
stack.push Types.to_object(stack.pop).get(arg.to_s)
|
128
|
+
end
|
129
|
+
|
130
|
+
def deleteg(arg)
|
131
|
+
scope.global_scope.root_object.delete arg.to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
def delete(arg)
|
135
|
+
Types.to_object(stack.pop).delete arg.to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
def in(arg)
|
139
|
+
obj = Types.to_object stack.pop
|
140
|
+
idx = Types.to_string stack.pop
|
141
|
+
stack.push Types::Boolean.new(obj.has_property idx.string)
|
142
|
+
end
|
143
|
+
|
144
|
+
def enum(arg)
|
145
|
+
props = []
|
146
|
+
obj = stack.pop
|
147
|
+
Types.to_object(obj).each_enumerable_property { |p| props.push p } unless obj.is_a?(Types::Null) || obj.is_a?(Types::Undefined)
|
148
|
+
@enum_stack.push [props, 0]
|
149
|
+
end
|
150
|
+
|
151
|
+
def enumnext(arg)
|
152
|
+
enum = @enum_stack.last
|
153
|
+
stack.push Types::String.new(enum[0][enum[1]])
|
154
|
+
enum[1] += 1
|
155
|
+
end
|
156
|
+
|
157
|
+
def jiee(arg)
|
158
|
+
enum = @enum_stack.last
|
159
|
+
@ip = arg if enum[1] >= enum[0].size
|
160
|
+
end
|
161
|
+
|
162
|
+
def popenum(arg)
|
163
|
+
@enum_stack.pop
|
164
|
+
end
|
165
|
+
|
166
|
+
def set(arg)
|
167
|
+
scope.set_var arg, stack.last
|
168
|
+
end
|
169
|
+
|
170
|
+
def setprop(arg)
|
171
|
+
val = stack.pop
|
172
|
+
obj = stack.pop
|
173
|
+
obj.put arg.to_s, val
|
174
|
+
stack.push val
|
175
|
+
end
|
176
|
+
|
177
|
+
def ret(arg)
|
178
|
+
if finally_stack.empty?
|
179
|
+
@return = true
|
180
|
+
else
|
181
|
+
@ip = finally_stack.last
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def _throw(arg)
|
186
|
+
throw :exception, stack.pop
|
187
|
+
end
|
188
|
+
|
189
|
+
def eq(arg)
|
190
|
+
b = stack.pop
|
191
|
+
a = stack.pop
|
192
|
+
stack.push Types::Boolean.new(Types.eq(a, b))
|
193
|
+
end
|
194
|
+
|
195
|
+
def seq(arg)
|
196
|
+
b = stack.pop
|
197
|
+
a = stack.pop
|
198
|
+
stack.push Types::Boolean.new(Types.seq(a, b))
|
199
|
+
end
|
200
|
+
|
201
|
+
def null(arg)
|
202
|
+
stack.push Types::Null.new
|
203
|
+
end
|
204
|
+
|
205
|
+
def true(arg)
|
206
|
+
stack.push Types::Boolean.new(true)
|
207
|
+
end
|
208
|
+
|
209
|
+
def false(arg)
|
210
|
+
stack.push Types::Boolean.new(false)
|
211
|
+
end
|
212
|
+
|
213
|
+
def jmp(arg)
|
214
|
+
@ip = arg.to_i
|
215
|
+
end
|
216
|
+
|
217
|
+
def jif(arg)
|
218
|
+
if Types.is_falsy stack.pop
|
219
|
+
@ip = arg.to_i
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def jit(arg)
|
224
|
+
if Types.is_truthy stack.pop
|
225
|
+
@ip = arg.to_i
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def not(arg)
|
230
|
+
stack.push Types::Boolean.new(Types.is_falsy(stack.pop))
|
231
|
+
end
|
232
|
+
|
233
|
+
def inc(arg)
|
234
|
+
stack.push Types::Number.new(Types.to_number(stack.pop).number + 1)
|
235
|
+
end
|
236
|
+
|
237
|
+
def dec(arg)
|
238
|
+
stack.push Types::Number.new(Types.to_number(stack.pop).number - 1)
|
239
|
+
end
|
240
|
+
|
241
|
+
def pop(arg)
|
242
|
+
stack.pop
|
243
|
+
end
|
244
|
+
|
245
|
+
def index(arg)
|
246
|
+
index = Types.to_string(stack.pop).string
|
247
|
+
stack.push(Types.to_object(stack.pop).get(index) || Types::Undefined.new)
|
248
|
+
end
|
249
|
+
|
250
|
+
def array(arg)
|
251
|
+
args = []
|
252
|
+
arg.times { args.unshift stack.pop }
|
253
|
+
stack.push Types::Array.new(args)
|
254
|
+
end
|
255
|
+
|
256
|
+
def undefined(arg)
|
257
|
+
stack.push Types::Undefined.new
|
258
|
+
end
|
259
|
+
|
260
|
+
def number(arg)
|
261
|
+
stack.push Types.to_number(stack.pop)
|
262
|
+
end
|
263
|
+
|
264
|
+
def regexp(arg)
|
265
|
+
stack.push Types::RegExp.new(*arg)
|
266
|
+
end
|
267
|
+
|
268
|
+
def sal(arg)
|
269
|
+
r = Types.to_uint32(stack.pop) & 31
|
270
|
+
l = Types.to_int32 stack.pop
|
271
|
+
stack.push Types::Number.new(l << r)
|
272
|
+
end
|
273
|
+
|
274
|
+
def sar(arg)
|
275
|
+
r = Types.to_uint32(stack.pop) & 31
|
276
|
+
l = Types.to_int32 stack.pop
|
277
|
+
stack.push Types::Number.new(l >> r)
|
278
|
+
end
|
279
|
+
|
280
|
+
def slr(arg)
|
281
|
+
r = Types.to_uint32(stack.pop) & 31
|
282
|
+
l = Types.to_uint32 stack.pop
|
283
|
+
stack.push Types::Number.new(l >> r)
|
284
|
+
end
|
285
|
+
|
286
|
+
def add(arg)
|
287
|
+
r = stack.pop
|
288
|
+
l = stack.pop
|
289
|
+
right = Types.to_primitive r
|
290
|
+
left = Types.to_primitive l
|
291
|
+
|
292
|
+
if left.is_a?(Types::String) || right.is_a?(Types::String)
|
293
|
+
stack.push Types::String.new(Types.to_string(left).string + Types.to_string(right).string)
|
294
|
+
else
|
295
|
+
stack.push Types::Number.new(Types.to_number(left).number + Types.to_number(right).number)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def sub(arg)
|
300
|
+
right = Types.to_number(stack.pop).number
|
301
|
+
left = Types.to_number(stack.pop).number
|
302
|
+
stack.push Types::Number.new(left - right)
|
303
|
+
end
|
304
|
+
|
305
|
+
def mul(arg)
|
306
|
+
right = Types.to_number(stack.pop).number
|
307
|
+
left = Types.to_number(stack.pop).number
|
308
|
+
stack.push Types::Number.new(left * right)
|
309
|
+
end
|
310
|
+
|
311
|
+
def div(arg)
|
312
|
+
right = Types.to_number(stack.pop).number
|
313
|
+
left = Types.to_number(stack.pop).number
|
314
|
+
stack.push Types::Number.new(left / right.to_f)
|
315
|
+
end
|
316
|
+
|
317
|
+
def mod(arg)
|
318
|
+
right = Types.to_number(stack.pop).number
|
319
|
+
left = Types.to_number(stack.pop).number
|
320
|
+
stack.push Types::Number.new(left % right)
|
321
|
+
end
|
322
|
+
|
323
|
+
def and(arg)
|
324
|
+
right = Types.to_int32 stack.pop
|
325
|
+
left = Types.to_int32 stack.pop
|
326
|
+
stack.push Types::Number.new(left & right)
|
327
|
+
end
|
328
|
+
|
329
|
+
def or(arg)
|
330
|
+
right = Types.to_int32 stack.pop
|
331
|
+
left = Types.to_int32 stack.pop
|
332
|
+
stack.push Types::Number.new(left | right)
|
333
|
+
end
|
334
|
+
|
335
|
+
def xor(arg)
|
336
|
+
right = Types.to_int32 stack.pop
|
337
|
+
left = Types.to_int32 stack.pop
|
338
|
+
stack.push Types::Number.new(left ^ right)
|
339
|
+
end
|
340
|
+
|
341
|
+
def setindex(arg)
|
342
|
+
val = stack.pop
|
343
|
+
index = Types.to_string(stack.pop).string
|
344
|
+
Types.to_object(stack.pop).put index, val
|
345
|
+
stack.push val
|
346
|
+
end
|
347
|
+
|
348
|
+
def lt(arg)
|
349
|
+
comparison_oper :<
|
350
|
+
end
|
351
|
+
|
352
|
+
def lte(arg)
|
353
|
+
comparison_oper :<=
|
354
|
+
end
|
355
|
+
|
356
|
+
def gt(arg)
|
357
|
+
comparison_oper :>
|
358
|
+
end
|
359
|
+
|
360
|
+
def gte(arg)
|
361
|
+
comparison_oper :>=
|
362
|
+
end
|
363
|
+
|
364
|
+
def typeof(arg)
|
365
|
+
if arg
|
366
|
+
stack.push Types::String.new(scope.has_var(arg) ? scope.get_var(arg).typeof : "undefined")
|
367
|
+
else
|
368
|
+
stack.push Types::String.new(stack.pop.typeof)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def instanceof(arg)
|
373
|
+
r = stack.pop
|
374
|
+
l = stack.pop
|
375
|
+
stack.push Types::Boolean.new(r.has_instance l)
|
376
|
+
end
|
377
|
+
|
378
|
+
def close(arg)
|
379
|
+
name = vm.bytecode[arg].select { |ins,arg| ins == :".name" }.map { |ins,arg| arg }.first
|
380
|
+
arguments = vm.bytecode[arg].select { |ins,arg| ins == :".arg" }.map(&:last).map(&:to_s)
|
381
|
+
scope = @scope
|
382
|
+
fun = Types::Function.new(->(outer_scope, this, args) { VM::Frame.new(vm, arg, fun).execute(scope.close, this, args) }, "...", name || "", arguments)
|
383
|
+
stack.push fun
|
384
|
+
end
|
385
|
+
|
386
|
+
def callee(arg)
|
387
|
+
stack.push @callee
|
388
|
+
end
|
389
|
+
|
390
|
+
def object(arg)
|
391
|
+
obj = Types::Object.new
|
392
|
+
kvs = []
|
393
|
+
arg.reverse_each { |a| kvs << [a, stack.pop] }
|
394
|
+
kvs.reverse_each { |kv| obj.put kv[0].to_s, kv[1] }
|
395
|
+
stack.push obj
|
396
|
+
end
|
397
|
+
|
398
|
+
def negate(arg)
|
399
|
+
n = Types.to_number(stack.pop).number
|
400
|
+
if n.zero?
|
401
|
+
stack.push Types::Number.new(-n.to_f) # to preserve javascript's 0/-0 semantics
|
402
|
+
else
|
403
|
+
stack.push Types::Number.new(-n)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
def with(arg)
|
408
|
+
@scope = ObjectScope.new stack.pop, @scope
|
409
|
+
end
|
410
|
+
|
411
|
+
def popscope(arg)
|
412
|
+
@scope = @scope.parent
|
413
|
+
end
|
414
|
+
|
415
|
+
def pushsp(arg)
|
416
|
+
sp_stack.push stack.size
|
417
|
+
end
|
418
|
+
|
419
|
+
def popsp(arg)
|
420
|
+
@stack = stack[0...sp_stack.pop]
|
421
|
+
end
|
422
|
+
|
423
|
+
def pushcatch(arg)
|
424
|
+
catch_stack.push arg
|
425
|
+
end
|
426
|
+
|
427
|
+
def popcatch(arg)
|
428
|
+
catch_stack.pop
|
429
|
+
end
|
430
|
+
|
431
|
+
def pushfinally(arg)
|
432
|
+
finally_stack.push arg
|
433
|
+
end
|
434
|
+
|
435
|
+
def popfinally(arg)
|
436
|
+
finally_stack.pop
|
437
|
+
end
|
438
|
+
|
439
|
+
def this(arg)
|
440
|
+
stack.push @this
|
441
|
+
end
|
442
|
+
|
443
|
+
private
|
444
|
+
def comparison_oper(op)
|
445
|
+
right = Types.to_primitive stack.pop
|
446
|
+
left = Types.to_primitive stack.pop
|
447
|
+
|
448
|
+
if left.is_a?(Types::String) && right.is_a?(Types::String)
|
449
|
+
stack.push Types::Boolean.new(left.string.send op, right.string)
|
450
|
+
else
|
451
|
+
stack.push Types::Boolean.new(Types.to_number(left).number.send op, Types.to_number(right).number)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def error!(msg)
|
456
|
+
vm.send :error!, "#{msg} (at #{@section}+#{@ip - 1})"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
data/lib/twostroke/runtime.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
module Twostroke
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Twostroke::Runtime
|
2
|
+
class RuntimeError < Twostroke::Error
|
3
|
+
end
|
4
|
+
|
5
|
+
Dir.glob File.expand_path("../runtime/*.rb", __FILE__) do |f|
|
6
|
+
require f
|
6
7
|
end
|
7
8
|
end
|
data/lib/twostroke/tokens.rb
CHANGED
@@ -1,33 +1,45 @@
|
|
1
1
|
module Twostroke
|
2
2
|
class Lexer
|
3
|
-
RESERVED = %w(
|
3
|
+
RESERVED = %w(
|
4
|
+
function var if instanceof in else for while do this return
|
5
|
+
throw typeof try catch finally void null new delete switch
|
6
|
+
case break continue default true false with)
|
4
7
|
TOKENS = [
|
5
8
|
|
6
9
|
[ :MULTI_COMMENT, %r{/\*.*?\*/} ],
|
7
10
|
[ :SINGLE_COMMENT, /\/\/.*?$/ ],
|
8
11
|
|
9
12
|
[ :WHITESPACE, /\s+/ ],
|
10
|
-
[ :NUMBER, /((?<oct>0[0-7]+)|(?<hex>0x[A-Fa-f0-9]+)|(?<to_f>(\d+(\.?\d*([eE][+-]?\d+)?)?|\.\d+([eE][+-]?\d+)?)))/, ->m
|
13
|
+
[ :NUMBER, /((?<oct>0[0-7]+)|(?<hex>0x[A-Fa-f0-9]+)|(?<to_f>(\d+(\.?\d*([eE][+-]?\d+)?)?|\.\d+([eE][+-]?\d+)?)))/, ->m do
|
14
|
+
method, number = m.names.zip(m.captures).select { |k,v| v }.first
|
15
|
+
n = number.send method
|
16
|
+
if (n % 1).zero?
|
17
|
+
n.to_i
|
18
|
+
else
|
19
|
+
n
|
20
|
+
end
|
21
|
+
end ],
|
11
22
|
|
12
23
|
*RESERVED.map do |w|
|
13
24
|
[ w.upcase.intern, /#{w}(?=[^a-zA-Z_0-9])/ ]
|
14
25
|
end,
|
15
26
|
[ :BAREWORD, /[a-zA-Z_\$][\$a-zA-Z_0-9]*/, ->m { m[0] } ],
|
16
27
|
|
17
|
-
[ :STRING, /(["'])((
|
18
|
-
m[2]
|
28
|
+
[ :STRING, /(["'])((\\\n|\\.|[^\1])*?[^\1\\]?)\1/, ->m do
|
29
|
+
m[2]
|
30
|
+
.gsub(/\\([0-6]{1,3})/) { |m| m[1].to_i(7).chr }
|
31
|
+
.gsub(/\\x([a-f0-9]{2})/i) { |m| m[1].to_i(16).chr }
|
32
|
+
.gsub(/\\u([a-f0-9]{4})/i) { |m| m[1].to_i(16).chr }
|
33
|
+
.gsub(/\\(.)/m) { |m|
|
19
34
|
case m[1]
|
20
35
|
when "b"; "\b"
|
21
36
|
when "n"; "\n"
|
22
37
|
when "f"; "\f"
|
23
38
|
when "r"; "\r"
|
24
39
|
when "t"; "\t"
|
40
|
+
else; m[1]
|
25
41
|
end
|
26
42
|
}
|
27
|
-
.gsub(/\\([0-6]{1,3})/) { |m| m[1].to_i(7).chr }
|
28
|
-
.gsub(/\\x([a-f0-9]{2})/i) { |m| m[1].to_i(16).chr }
|
29
|
-
.gsub(/\\u([a-f0-9]{4})/i) { |m| m[1].to_i(16).chr }
|
30
|
-
.gsub(/\\(.)/) { |m| m[1] }
|
31
43
|
end ],
|
32
44
|
|
33
45
|
[ :REGEXP, %r{/(?<src>(\\.|[^\1])*?[^\1\\]?)/(?<opts>[gim]+)?}, ->m { [m[:src], m[:opts]] } ],
|
data/lib/twostroke.rb
CHANGED
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.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,10 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-09 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description: An implementation of Javascript written in pure Ruby.
|
15
|
-
|
14
|
+
description: An implementation of Javascript written in pure Ruby. Twostroke contains
|
15
|
+
a parser, a bytecode compiler, a VM, and a minimal implementation of the Javascript
|
16
|
+
standard library.
|
16
17
|
email:
|
17
18
|
- charlie@charliesomerville.com
|
18
19
|
executables: []
|
@@ -26,9 +27,11 @@ files:
|
|
26
27
|
- lib/twostroke/ast/break.rb
|
27
28
|
- lib/twostroke/ast/call.rb
|
28
29
|
- lib/twostroke/ast/case.rb
|
30
|
+
- lib/twostroke/ast/continue.rb
|
29
31
|
- lib/twostroke/ast/declaration.rb
|
30
32
|
- lib/twostroke/ast/delete.rb
|
31
33
|
- lib/twostroke/ast/do_while.rb
|
34
|
+
- lib/twostroke/ast/false.rb
|
32
35
|
- lib/twostroke/ast/for_in.rb
|
33
36
|
- lib/twostroke/ast/for_loop.rb
|
34
37
|
- lib/twostroke/ast/function.rb
|
@@ -47,22 +50,53 @@ files:
|
|
47
50
|
- lib/twostroke/ast/ternary.rb
|
48
51
|
- lib/twostroke/ast/this.rb
|
49
52
|
- lib/twostroke/ast/throw.rb
|
53
|
+
- lib/twostroke/ast/true.rb
|
50
54
|
- lib/twostroke/ast/try.rb
|
51
55
|
- lib/twostroke/ast/unary_operators.rb
|
52
56
|
- lib/twostroke/ast/unsorted_binop.rb
|
53
57
|
- lib/twostroke/ast/variable.rb
|
54
58
|
- lib/twostroke/ast/while.rb
|
59
|
+
- lib/twostroke/ast/with.rb
|
55
60
|
- lib/twostroke/ast.rb
|
61
|
+
- lib/twostroke/compiler/javascript.rb
|
62
|
+
- lib/twostroke/compiler/tsasm.rb
|
63
|
+
- lib/twostroke/compiler.rb
|
56
64
|
- lib/twostroke/error.rb
|
57
65
|
- lib/twostroke/lexer.rb
|
58
66
|
- lib/twostroke/parser.rb
|
59
|
-
- lib/twostroke/runtime/
|
60
|
-
- lib/twostroke/runtime/
|
61
|
-
- lib/twostroke/runtime/
|
67
|
+
- lib/twostroke/runtime/lib/array.js
|
68
|
+
- lib/twostroke/runtime/lib/array.rb
|
69
|
+
- lib/twostroke/runtime/lib/boolean.rb
|
70
|
+
- lib/twostroke/runtime/lib/console.rb
|
71
|
+
- lib/twostroke/runtime/lib/date.rb
|
72
|
+
- lib/twostroke/runtime/lib/error.rb
|
73
|
+
- lib/twostroke/runtime/lib/function.js
|
74
|
+
- lib/twostroke/runtime/lib/function.rb
|
75
|
+
- lib/twostroke/runtime/lib/math.rb
|
76
|
+
- lib/twostroke/runtime/lib/number.rb
|
77
|
+
- lib/twostroke/runtime/lib/object.js
|
78
|
+
- lib/twostroke/runtime/lib/object.rb
|
79
|
+
- lib/twostroke/runtime/lib/regexp.rb
|
80
|
+
- lib/twostroke/runtime/lib/string.rb
|
81
|
+
- lib/twostroke/runtime/lib/undefined.rb
|
82
|
+
- lib/twostroke/runtime/lib.rb
|
83
|
+
- lib/twostroke/runtime/scope.rb
|
84
|
+
- lib/twostroke/runtime/types/array.rb
|
85
|
+
- lib/twostroke/runtime/types/boolean.rb
|
86
|
+
- lib/twostroke/runtime/types/boolean_object.rb
|
87
|
+
- lib/twostroke/runtime/types/function.rb
|
62
88
|
- lib/twostroke/runtime/types/null.rb
|
89
|
+
- lib/twostroke/runtime/types/number.rb
|
90
|
+
- lib/twostroke/runtime/types/number_object.rb
|
63
91
|
- lib/twostroke/runtime/types/object.rb
|
92
|
+
- lib/twostroke/runtime/types/regexp.rb
|
93
|
+
- lib/twostroke/runtime/types/string.rb
|
94
|
+
- lib/twostroke/runtime/types/string_object.rb
|
64
95
|
- lib/twostroke/runtime/types/undefined.rb
|
96
|
+
- lib/twostroke/runtime/types/value.rb
|
65
97
|
- lib/twostroke/runtime/types.rb
|
98
|
+
- lib/twostroke/runtime/vm.rb
|
99
|
+
- lib/twostroke/runtime/vm_frame.rb
|
66
100
|
- lib/twostroke/runtime.rb
|
67
101
|
- lib/twostroke/tokens.rb
|
68
102
|
- lib/twostroke.rb
|
@@ -1,33 +0,0 @@
|
|
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
|