twostroke 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/twostroke/ast/case.rb +1 -1
- data/lib/twostroke/ast/function.rb +2 -2
- data/lib/twostroke/ast/label.rb +20 -0
- data/lib/twostroke/ast/switch.rb +1 -0
- data/lib/twostroke/ast/try.rb +3 -3
- data/lib/twostroke/ast/unary_operators.rb +1 -1
- data/lib/twostroke/ast.rb +1 -0
- data/lib/twostroke/compiler/javascript.rb +20 -2
- data/lib/twostroke/compiler/tsasm.rb +89 -35
- data/lib/twostroke/compiler.rb +1 -1
- data/lib/twostroke/error.rb +3 -0
- data/lib/twostroke/lexer.rb +3 -2
- data/lib/twostroke/parser.rb +309 -227
- data/lib/twostroke/runtime/lib/array.js +9 -3
- data/lib/twostroke/runtime/lib/array.rb +15 -7
- data/lib/twostroke/runtime/lib/console.rb +3 -2
- data/lib/twostroke/runtime/lib/error.rb +1 -1
- data/lib/twostroke/runtime/lib/etc.js +38 -0
- data/lib/twostroke/runtime/lib/etc.rb +29 -0
- data/lib/twostroke/runtime/lib/math.rb +31 -0
- data/lib/twostroke/runtime/lib/number.rb +7 -1
- data/lib/twostroke/runtime/lib/object.rb +1 -1
- data/lib/twostroke/runtime/lib/regexp.rb +1 -0
- data/lib/twostroke/runtime/lib/string.rb +62 -22
- data/lib/twostroke/runtime/scope.rb +23 -2
- data/lib/twostroke/runtime/types/array.rb +5 -0
- data/lib/twostroke/runtime/types/function.rb +5 -2
- data/lib/twostroke/runtime/types/object.rb +3 -4
- data/lib/twostroke/runtime/types/regexp.rb +45 -2
- data/lib/twostroke/runtime/types.rb +1 -1
- data/lib/twostroke/runtime/vm.rb +2 -2
- data/lib/twostroke/runtime/vm_frame.rb +47 -8
- data/lib/twostroke/tokens.rb +32 -16
- metadata +5 -5
- data/lib/twostroke/ast/delete.rb +0 -15
- data/lib/twostroke/ast/unsorted_binop.rb +0 -97
- data/lib/twostroke/runtime/lib/function.js +0 -0
@@ -18,12 +18,13 @@ module Twostroke::Runtime
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def execute(scope, this = nil, args = [])
|
21
|
-
@scope = scope ||
|
21
|
+
@scope = scope || vm.global_scope
|
22
22
|
@stack = []
|
23
23
|
@sp_stack = []
|
24
24
|
@catch_stack = []
|
25
25
|
@finally_stack = []
|
26
26
|
@enum_stack = []
|
27
|
+
@temp_slot = nil
|
27
28
|
@ip = 0
|
28
29
|
@return = false
|
29
30
|
@this = this || @scope.global_scope.root_object
|
@@ -35,13 +36,18 @@ module Twostroke::Runtime
|
|
35
36
|
end
|
36
37
|
|
37
38
|
until @return
|
38
|
-
ins, arg =
|
39
|
+
ins, arg = insns[ip]
|
39
40
|
st = @stack.size
|
40
41
|
@ip += 1
|
41
42
|
if respond_to? ins
|
42
43
|
if @exception = catch(:exception) { public_send ins, arg; nil }
|
43
|
-
|
44
|
-
@
|
44
|
+
# puts "--> #{Types.to_string(exception).string} #{@name || "(anonymous function)"}:#{@line} <#{@section}+#{@ip}>"
|
45
|
+
throw :exception, @exception if catch_stack.empty? && finally_stack.empty?
|
46
|
+
if catch_stack.any?
|
47
|
+
@ip = catch_stack.last
|
48
|
+
else
|
49
|
+
@ip = finally_stack.last
|
50
|
+
end
|
45
51
|
end
|
46
52
|
else
|
47
53
|
error! "unknown instruction #{ins}"
|
@@ -51,7 +57,12 @@ module Twostroke::Runtime
|
|
51
57
|
stack.last
|
52
58
|
end
|
53
59
|
|
60
|
+
define_method ".line" do |arg|
|
61
|
+
@line = arg
|
62
|
+
end
|
63
|
+
|
54
64
|
define_method ".name" do |arg|
|
65
|
+
@name = arg
|
55
66
|
scope.declare arg.intern
|
56
67
|
scope.set_var arg.intern, @callee
|
57
68
|
end
|
@@ -68,6 +79,7 @@ module Twostroke::Runtime
|
|
68
79
|
define_method ".catch" do |arg|
|
69
80
|
scope.declare arg.intern
|
70
81
|
scope.set_var arg.intern, @exception
|
82
|
+
@exception = nil
|
71
83
|
end
|
72
84
|
|
73
85
|
## instructions
|
@@ -91,7 +103,7 @@ module Twostroke::Runtime
|
|
91
103
|
arg.times { args.unshift @stack.pop }
|
92
104
|
fun = stack.pop
|
93
105
|
Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
|
94
|
-
stack.push fun.call(scope, scope.global_scope.root_object, args)
|
106
|
+
stack.push fun.call(scope, fun.inherits_caller_this ? @this : scope.global_scope.root_object, args)
|
95
107
|
end
|
96
108
|
|
97
109
|
def thiscall(arg)
|
@@ -99,7 +111,8 @@ module Twostroke::Runtime
|
|
99
111
|
arg.times { args.unshift stack.pop }
|
100
112
|
fun = stack.pop
|
101
113
|
Lib.throw_type_error "called non callable" unless fun.respond_to?(:call)
|
102
|
-
|
114
|
+
this_arg = Types.to_object stack.pop
|
115
|
+
stack.push fun.call(scope, fun.inherits_caller_this ? @this : this_arg, args)
|
103
116
|
end
|
104
117
|
|
105
118
|
def newcall(arg)
|
@@ -123,16 +136,33 @@ module Twostroke::Runtime
|
|
123
136
|
stack.push *stack[-n..-1]
|
124
137
|
end
|
125
138
|
|
139
|
+
def tst(arg)
|
140
|
+
@temp_slot = stack.pop
|
141
|
+
end
|
142
|
+
|
143
|
+
def tld(arg)
|
144
|
+
stack.push @temp_slot
|
145
|
+
end
|
146
|
+
|
126
147
|
def member(arg)
|
127
148
|
stack.push Types.to_object(stack.pop).get(arg.to_s)
|
128
149
|
end
|
129
150
|
|
130
151
|
def deleteg(arg)
|
131
|
-
scope.
|
152
|
+
scope.delete arg
|
153
|
+
stack.push Types::Boolean.true
|
132
154
|
end
|
133
155
|
|
134
156
|
def delete(arg)
|
135
157
|
Types.to_object(stack.pop).delete arg.to_s
|
158
|
+
stack.push Types::Boolean.true
|
159
|
+
end
|
160
|
+
|
161
|
+
def deleteindex(arg)
|
162
|
+
obj = Types.to_object stack.pop
|
163
|
+
idx = Types.to_string stack.pop
|
164
|
+
obj.delete idx.string
|
165
|
+
stack.push Types::Boolean.true
|
136
166
|
end
|
137
167
|
|
138
168
|
def in(arg)
|
@@ -169,7 +199,7 @@ module Twostroke::Runtime
|
|
169
199
|
|
170
200
|
def setprop(arg)
|
171
201
|
val = stack.pop
|
172
|
-
obj = stack.pop
|
202
|
+
obj = Types.to_object(stack.pop)
|
173
203
|
obj.put arg.to_s, val
|
174
204
|
stack.push val
|
175
205
|
end
|
@@ -338,6 +368,11 @@ module Twostroke::Runtime
|
|
338
368
|
stack.push Types::Number.new(left ^ right)
|
339
369
|
end
|
340
370
|
|
371
|
+
def bnot(arg)
|
372
|
+
val = Types.to_int32 stack.pop
|
373
|
+
stack.push Types::Number.new ~val
|
374
|
+
end
|
375
|
+
|
341
376
|
def setindex(arg)
|
342
377
|
val = stack.pop
|
343
378
|
index = Types.to_string(stack.pop).string
|
@@ -436,6 +471,10 @@ module Twostroke::Runtime
|
|
436
471
|
finally_stack.pop
|
437
472
|
end
|
438
473
|
|
474
|
+
def endfinally(arg)
|
475
|
+
throw :exception, @exception if @exception
|
476
|
+
end
|
477
|
+
|
439
478
|
def this(arg)
|
440
479
|
stack.push @this
|
441
480
|
end
|
data/lib/twostroke/tokens.rb
CHANGED
@@ -7,9 +7,10 @@ module Twostroke
|
|
7
7
|
TOKENS = [
|
8
8
|
|
9
9
|
[ :MULTI_COMMENT, %r{/\*.*?\*/} ],
|
10
|
-
[ :SINGLE_COMMENT,
|
10
|
+
[ :SINGLE_COMMENT, /\/\/.*?($|\r|\u2029|\u2028)/ ],
|
11
11
|
|
12
|
-
[ :
|
12
|
+
[ :LINE_TERMINATOR, /[\n\r\u2028\u2029]/ ],
|
13
|
+
[ :WHITESPACE, /[[:space:]]+/ ],
|
13
14
|
[ :NUMBER, /((?<oct>0[0-7]+)|(?<hex>0x[A-Fa-f0-9]+)|(?<to_f>(\d+(\.?\d*([eE][+-]?\d+)?)?|\.\d+([eE][+-]?\d+)?)))/, ->m do
|
14
15
|
method, number = m.names.zip(m.captures).select { |k,v| v }.first
|
15
16
|
n = number.send method
|
@@ -25,21 +26,24 @@ module Twostroke
|
|
25
26
|
end,
|
26
27
|
[ :BAREWORD, /[a-zA-Z_\$][\$a-zA-Z_0-9]*/, ->m { m[0] } ],
|
27
28
|
|
28
|
-
[ :STRING, /(["'])((\\\n|\\.|[^\1])*?[^\1\\]?)\1/, ->m do
|
29
|
-
m[2]
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
case m[1]
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
[ :STRING, /(["'])((\\\n|\\.|[^\n\r\u2028\u2029\1])*?[^\1\\]?)\1/, ->m do
|
30
|
+
m[2].gsub(/\\(([0-6]{1,3})|u([a-f0-9]{4})|x([a-f0-9]{2})|\n|.)/i) do |m|
|
31
|
+
case m
|
32
|
+
when /\\([0-6]{1,3})/; m[1..-1].to_i(7).chr "utf-8"
|
33
|
+
when /\\u([a-f0-9]{4})/i; m[2..-1].to_i(16).chr "utf-8"
|
34
|
+
when /\\x([a-f0-9]{2})/i; m[2..-1].to_i(16).chr "utf-8"
|
35
|
+
else case m[1]
|
36
|
+
when "b"; "\b"
|
37
|
+
when "n"; "\n"
|
38
|
+
when "f"; "\f"
|
39
|
+
when "v"; "\v"
|
40
|
+
when "r"; "\r"
|
41
|
+
when "t"; "\t"
|
42
|
+
when "\n"; ""
|
43
|
+
else; m[1]
|
44
|
+
end
|
41
45
|
end
|
42
|
-
|
46
|
+
end
|
43
47
|
end ],
|
44
48
|
|
45
49
|
[ :REGEXP, %r{/(?<src>(\\.|[^\1])*?[^\1\\]?)/(?<opts>[gim]+)?}, ->m { [m[:src], m[:opts]] } ],
|
@@ -53,6 +57,18 @@ module Twostroke
|
|
53
57
|
|
54
58
|
[ :MEMBER_ACCESS, /\./ ],
|
55
59
|
|
60
|
+
[ :ADD_EQUALS, /\+=/ ],
|
61
|
+
[ :MINUS_EQUALS, /-=/ ],
|
62
|
+
[ :TIMES_EQUALS, /\*=/ ], # textmate barfs it's syntax highlighting on this one lol
|
63
|
+
[ :DIVIDE_EQUALS, /\/=/ ],
|
64
|
+
[ :MOD_EQUALS, /%=/ ],
|
65
|
+
[ :LEFT_SHIFT_EQUALS, /<<=/ ],
|
66
|
+
[ :RIGHT_TRIPLE_SHIFT_EQUALS, />>>=/ ],
|
67
|
+
[ :RIGHT_SHIFT_EQUALS, />>=/ ],
|
68
|
+
[ :BITWISE_AND_EQUALS, /&=/ ],
|
69
|
+
[ :BITWISE_XOR_EQUALS, /\^=/ ],
|
70
|
+
[ :BITWISE_OR_EQUALS, /\|=/ ],
|
71
|
+
|
56
72
|
[ :INCREMENT, /\+\+/ ],
|
57
73
|
[ :DECREMENT, /--/ ],
|
58
74
|
[ :PLUS, /\+/ ],
|
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.
|
4
|
+
version: 0.2.0
|
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:
|
12
|
+
date: 2012-01-10 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: An implementation of Javascript written in pure Ruby. Twostroke contains
|
15
15
|
a parser, a bytecode compiler, a VM, and a minimal implementation of the Javascript
|
@@ -29,7 +29,6 @@ files:
|
|
29
29
|
- lib/twostroke/ast/case.rb
|
30
30
|
- lib/twostroke/ast/continue.rb
|
31
31
|
- lib/twostroke/ast/declaration.rb
|
32
|
-
- lib/twostroke/ast/delete.rb
|
33
32
|
- lib/twostroke/ast/do_while.rb
|
34
33
|
- lib/twostroke/ast/false.rb
|
35
34
|
- lib/twostroke/ast/for_in.rb
|
@@ -37,6 +36,7 @@ files:
|
|
37
36
|
- lib/twostroke/ast/function.rb
|
38
37
|
- lib/twostroke/ast/if.rb
|
39
38
|
- lib/twostroke/ast/index.rb
|
39
|
+
- lib/twostroke/ast/label.rb
|
40
40
|
- lib/twostroke/ast/member_access.rb
|
41
41
|
- lib/twostroke/ast/multi_expression.rb
|
42
42
|
- lib/twostroke/ast/new.rb
|
@@ -53,7 +53,6 @@ files:
|
|
53
53
|
- lib/twostroke/ast/true.rb
|
54
54
|
- lib/twostroke/ast/try.rb
|
55
55
|
- lib/twostroke/ast/unary_operators.rb
|
56
|
-
- lib/twostroke/ast/unsorted_binop.rb
|
57
56
|
- lib/twostroke/ast/variable.rb
|
58
57
|
- lib/twostroke/ast/while.rb
|
59
58
|
- lib/twostroke/ast/with.rb
|
@@ -70,7 +69,8 @@ files:
|
|
70
69
|
- lib/twostroke/runtime/lib/console.rb
|
71
70
|
- lib/twostroke/runtime/lib/date.rb
|
72
71
|
- lib/twostroke/runtime/lib/error.rb
|
73
|
-
- lib/twostroke/runtime/lib/
|
72
|
+
- lib/twostroke/runtime/lib/etc.js
|
73
|
+
- lib/twostroke/runtime/lib/etc.rb
|
74
74
|
- lib/twostroke/runtime/lib/function.rb
|
75
75
|
- lib/twostroke/runtime/lib/math.rb
|
76
76
|
- lib/twostroke/runtime/lib/number.rb
|
data/lib/twostroke/ast/delete.rb
DELETED
@@ -1,97 +0,0 @@
|
|
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
|
-
:INSTANCEOF => 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
|
-
end
|
32
|
-
|
33
|
-
def self.operator_precedence
|
34
|
-
@precedences ||= {
|
35
|
-
:ASTERISK => 5,
|
36
|
-
:SLASH => 5,
|
37
|
-
:MOD => 5,
|
38
|
-
:PLUS => 6,
|
39
|
-
:MINUS => 6,
|
40
|
-
:LEFT_SHIFT => 7,
|
41
|
-
:RIGHT_SHIFT => 7,
|
42
|
-
:RIGHT_TRIPLE_SHIFT => 7,
|
43
|
-
:LT => 8,
|
44
|
-
:LTE => 8,
|
45
|
-
:GT => 8,
|
46
|
-
:GTE => 8,
|
47
|
-
:IN => 8,
|
48
|
-
:INSTANCEOF => 8,
|
49
|
-
:DOUBLE_EQUALS => 9,
|
50
|
-
:NOT_EQUALS => 9,
|
51
|
-
:TRIPLE_EQUALS => 9,
|
52
|
-
:NOT_DOUBLE_EQUALS => 9,
|
53
|
-
:AMPERSAND => 10,
|
54
|
-
:CARET => 11,
|
55
|
-
:PIPE => 12,
|
56
|
-
:AND => 13,
|
57
|
-
:OR => 14
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
def collapse(called_by_binop = false)
|
62
|
-
left_collapsed = left.is_a?(UnsortedBinop) ? left.collapse(true) : left.collapse
|
63
|
-
right_collapsed = right.is_a?(UnsortedBinop) ? right.collapse(true) : right.collapse
|
64
|
-
input = [*left_collapsed, op, *right_collapsed]
|
65
|
-
|
66
|
-
unless called_by_binop
|
67
|
-
stack = []
|
68
|
-
output = []
|
69
|
-
input.each do |token|
|
70
|
-
if token.is_a? Symbol
|
71
|
-
while stack.size > 0 && UnsortedBinop.operator_precedence[stack.last] <= UnsortedBinop.operator_precedence[token]
|
72
|
-
output.push stack.pop
|
73
|
-
end
|
74
|
-
stack.push token
|
75
|
-
else
|
76
|
-
output.push token
|
77
|
-
end
|
78
|
-
end
|
79
|
-
output.push stack.pop until stack.empty?
|
80
|
-
|
81
|
-
output.each do |token|
|
82
|
-
if token.is_a? Symbol
|
83
|
-
r = stack.pop
|
84
|
-
l = stack.pop
|
85
|
-
stack.push UnsortedBinop.operator_class[token].new(left: l, right: r)
|
86
|
-
else
|
87
|
-
stack.push token
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
stack.last
|
92
|
-
else
|
93
|
-
input
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
File without changes
|