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