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
data/lib/twostroke/parser.rb
CHANGED
@@ -56,8 +56,10 @@ module Twostroke
|
|
56
56
|
st = case peek_token.type
|
57
57
|
when :RETURN; send :return
|
58
58
|
when :BREAK; send :break
|
59
|
+
when :CONTINUE; continue
|
59
60
|
when :THROW; send :throw
|
60
61
|
when :VAR; var
|
62
|
+
when :WITH; consume_semicolon = false; with
|
61
63
|
when :IF; consume_semicolon = false; send :if
|
62
64
|
when :FOR; consume_semicolon = false; send :for
|
63
65
|
when :SWITCH; consume_semicolon = false; send :switch
|
@@ -101,7 +103,7 @@ module Twostroke
|
|
101
103
|
expr = binop expr
|
102
104
|
# this has a higher precedence than the ternary
|
103
105
|
# so we'll hackily check for a ternary after this
|
104
|
-
if try_peek_token && peek_token(false).type == :QUESTION
|
106
|
+
if !no_ternary && try_peek_token && peek_token(false).type == :QUESTION
|
105
107
|
ternary(expr)
|
106
108
|
else
|
107
109
|
expr
|
@@ -134,6 +136,8 @@ module Twostroke
|
|
134
136
|
when :REGEXP; regexp
|
135
137
|
when :THIS; this
|
136
138
|
when :NULL; null
|
139
|
+
when :TRUE; send :true
|
140
|
+
when :FALSE; send :false
|
137
141
|
when :NEW; send :new
|
138
142
|
when :DELETE; delete
|
139
143
|
when :BAREWORD; bareword
|
@@ -195,6 +199,16 @@ module Twostroke
|
|
195
199
|
AST::Null.new
|
196
200
|
end
|
197
201
|
|
202
|
+
def true
|
203
|
+
assert_type next_token, :TRUE
|
204
|
+
AST::True.new
|
205
|
+
end
|
206
|
+
|
207
|
+
def false
|
208
|
+
assert_type next_token, :FALSE
|
209
|
+
AST::False.new
|
210
|
+
end
|
211
|
+
|
198
212
|
def bareword
|
199
213
|
assert_type next_token, :BAREWORD
|
200
214
|
AST::Variable.new name: token.val
|
@@ -209,6 +223,15 @@ module Twostroke
|
|
209
223
|
ternary
|
210
224
|
end
|
211
225
|
|
226
|
+
def with
|
227
|
+
assert_type next_token, :WITH
|
228
|
+
assert_type next_token, :OPEN_PAREN
|
229
|
+
with = AST::With.new object: expression
|
230
|
+
assert_type next_token, :CLOSE_PAREN
|
231
|
+
with.statement = statement
|
232
|
+
with
|
233
|
+
end
|
234
|
+
|
212
235
|
def if
|
213
236
|
assert_type next_token, :IF
|
214
237
|
assert_type next_token, :OPEN_PAREN
|
@@ -268,16 +291,26 @@ module Twostroke
|
|
268
291
|
assert_type next_token, :CLOSE_PAREN
|
269
292
|
assert_type next_token, :OPEN_BRACE
|
270
293
|
current_case = nil
|
294
|
+
default = false
|
271
295
|
while ![:CLOSE_BRACE].include? peek_token.type
|
272
296
|
if peek_token.type == :CASE
|
273
297
|
assert_type next_token, :CASE
|
274
298
|
expr = expression
|
275
|
-
|
299
|
+
node = AST::Case.new expression: expr
|
300
|
+
assert_type next_token, :COLON
|
301
|
+
sw.cases << node
|
302
|
+
current_case = node.statements
|
303
|
+
elsif peek_token.type == :DEFAULT
|
304
|
+
assert_type next_token, :DEFAULT
|
305
|
+
error! "only one default case allowed" if default
|
306
|
+
default = true
|
307
|
+
node = AST::Case.new
|
276
308
|
assert_type next_token, :COLON
|
277
|
-
sw.cases <<
|
309
|
+
sw.cases << node
|
310
|
+
current_case = node.statements
|
278
311
|
else
|
279
312
|
error! "statements may only appear under a case" if current_case.nil?
|
280
|
-
current_case
|
313
|
+
current_case << statement
|
281
314
|
end
|
282
315
|
end
|
283
316
|
assert_type next_token, :CLOSE_BRACE
|
@@ -340,11 +373,11 @@ module Twostroke
|
|
340
373
|
assert_type next_token, :MEMBER_ACCESS
|
341
374
|
assert_type next_token, :BAREWORD
|
342
375
|
access = AST::MemberAccess.new object: obj, member: token.val
|
343
|
-
if peek_token.type == :MEMBER_ACCESS
|
376
|
+
if peek_token(false).type == :MEMBER_ACCESS
|
344
377
|
member_access access
|
345
|
-
elsif peek_token.type == :OPEN_PAREN
|
378
|
+
elsif peek_token(false).type == :OPEN_PAREN
|
346
379
|
call access
|
347
|
-
elsif peek_token.type == :EQUALS
|
380
|
+
elsif peek_token(false).type == :EQUALS
|
348
381
|
assignment access
|
349
382
|
else
|
350
383
|
access
|
@@ -354,7 +387,7 @@ module Twostroke
|
|
354
387
|
def new
|
355
388
|
assert_type next_token, :NEW
|
356
389
|
node = AST::New.new
|
357
|
-
node.callee = expression_after_unary(
|
390
|
+
node.callee = expression_after_unary(true, true)
|
358
391
|
if try_peek_token && peek_token.type == :OPEN_PAREN
|
359
392
|
call = call(node.callee)
|
360
393
|
node.arguments = call.arguments
|
@@ -394,6 +427,11 @@ module Twostroke
|
|
394
427
|
AST::Break.new
|
395
428
|
end
|
396
429
|
|
430
|
+
def continue
|
431
|
+
assert_type next_token, :CONTINUE
|
432
|
+
AST::Continue.new
|
433
|
+
end
|
434
|
+
|
397
435
|
def throw
|
398
436
|
assert_type next_token, :THROW
|
399
437
|
AST::Throw.new expression: expression
|
@@ -489,7 +527,7 @@ module Twostroke
|
|
489
527
|
|
490
528
|
def parens
|
491
529
|
assert_type next_token, :OPEN_PAREN
|
492
|
-
expr = expression
|
530
|
+
expr = AST::BracketedExpression.new value: expression
|
493
531
|
assert_type next_token, :CLOSE_PAREN
|
494
532
|
expr
|
495
533
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
Array.prototype.forEach = function(callback, thisObj) {
|
2
|
+
if(this == null) {
|
3
|
+
throw new TypeError("this is null or undefined");
|
4
|
+
}
|
5
|
+
var obj = Object(this);
|
6
|
+
thisObj = thisObj || this;
|
7
|
+
for(var i = 0; i < obj.length; i++) {
|
8
|
+
if(i in this) {
|
9
|
+
callback.call(thisObj, obj[i], i, obj);
|
10
|
+
}
|
11
|
+
}
|
12
|
+
};
|
13
|
+
|
14
|
+
Array.prototype.reverse = function() {
|
15
|
+
for(var i = 0; i < this.length / 2; i++) {
|
16
|
+
var j = this.length - 1 - i;
|
17
|
+
var tmp = this[i];
|
18
|
+
this[i] = this[j];
|
19
|
+
this[j] = tmp;
|
20
|
+
}
|
21
|
+
return this;
|
22
|
+
};
|
23
|
+
|
24
|
+
Array.prototype.concat = function(other) {
|
25
|
+
var x = [];
|
26
|
+
Array.prototype.slice.call(this).forEach(function(el) {
|
27
|
+
x.push(el);
|
28
|
+
});
|
29
|
+
Array.prototype.slice.call(other).forEach(function(el) {
|
30
|
+
x.push(el);
|
31
|
+
});
|
32
|
+
return x;
|
33
|
+
};
|
34
|
+
|
35
|
+
Array.prototype.join = function(delim) {
|
36
|
+
delim = delim.toString();
|
37
|
+
var str = "";
|
38
|
+
this.forEach(function(el, i) {
|
39
|
+
if(i > 0) {
|
40
|
+
str += delim;
|
41
|
+
}
|
42
|
+
str += el.toString();
|
43
|
+
});
|
44
|
+
return str;
|
45
|
+
};
|
46
|
+
|
47
|
+
Array.prototype.indexOf = function(el, from) {
|
48
|
+
from = Number(from || 0);
|
49
|
+
if(from < 0) {
|
50
|
+
from += this.length;
|
51
|
+
}
|
52
|
+
for(var i = from; i < this.length; i++) {
|
53
|
+
if(this[i] === el) {
|
54
|
+
return i;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
return -1;
|
58
|
+
};
|
59
|
+
|
60
|
+
Array.prototype.lastIndexOf = function(el, from) {
|
61
|
+
from = Number(from || this.length);
|
62
|
+
if(from < 0) {
|
63
|
+
from += this.length;
|
64
|
+
}
|
65
|
+
for(var i = from - 1; i >= 0; i--) {
|
66
|
+
if(this[i] === el) {
|
67
|
+
return i;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return -1;
|
71
|
+
};
|
72
|
+
|
73
|
+
Array.prototype.filter = function(callback, thisObj) {
|
74
|
+
var x = [];
|
75
|
+
this.forEach(function(el, i, obj) {
|
76
|
+
if(callback.call(this, el, i, obj)) {
|
77
|
+
x.push(el);
|
78
|
+
}
|
79
|
+
}, thisObj);
|
80
|
+
return x;
|
81
|
+
};
|
82
|
+
|
83
|
+
Array.prototype.every = function(callback, thisObj) {
|
84
|
+
for(var i = 0; i < this.length; i++) {
|
85
|
+
if(i in this) {
|
86
|
+
if(!callback.call(thisObj, this[i], i, this)) {
|
87
|
+
return false;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
return true;
|
92
|
+
};
|
93
|
+
|
94
|
+
Array.prototype.map = function(callback, thisObj) {
|
95
|
+
var x = [];
|
96
|
+
this.forEach(function(el, i, obj) {
|
97
|
+
x.push(callback.call(this, el, i, obj));
|
98
|
+
}, thisObj);
|
99
|
+
return x;
|
100
|
+
};
|
101
|
+
|
102
|
+
Array.prototype.some = function(callback, thisObj) {
|
103
|
+
for(var i = 0; i < this.length; i++) {
|
104
|
+
if(i in this) {
|
105
|
+
if(callback.call(thisObj, this[i], i, this)) {
|
106
|
+
return true;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
return false;
|
111
|
+
};
|
112
|
+
|
113
|
+
Array.prototype.reduce = function(callback, accumulator) {
|
114
|
+
var i = 0;
|
115
|
+
if(accumulator === undefined) {
|
116
|
+
if(!this.length) {
|
117
|
+
throw new TypeError("Reduce of empty array with no initial value");
|
118
|
+
}
|
119
|
+
while(i < this.length && !(i in this)) i++;
|
120
|
+
accumulator = this[i++];
|
121
|
+
}
|
122
|
+
for(; i < this.length; i++) {
|
123
|
+
if(i in this) {
|
124
|
+
accumulator = callback(accumulator, this[i], i, this);
|
125
|
+
}
|
126
|
+
}
|
127
|
+
return accumulator;
|
128
|
+
};
|
129
|
+
|
130
|
+
Array.prototype.reduceRight = function(callback, accumulator) {
|
131
|
+
var i = this.length - 1;
|
132
|
+
if(accumulator === undefined) {
|
133
|
+
if(!this.length) {
|
134
|
+
throw new TypeError("Reduce of empty array with no initial value");
|
135
|
+
}
|
136
|
+
accumulator = this[i--];
|
137
|
+
}
|
138
|
+
for(; i >= 0; i--) {
|
139
|
+
if(i in this) {
|
140
|
+
accumulator = callback(accumulator, this[i], i, this);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
return accumulator;
|
144
|
+
};
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
ary = Types::Array.constructor_function
|
4
|
+
scope.set_var "Array", ary
|
5
|
+
|
6
|
+
proto = Types::Object.new
|
7
|
+
# Array.prototype.toString
|
8
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) do
|
9
|
+
Lib.throw_type_error "Array.prototype.toString is not generic" unless this.is_a?(Types::Array)
|
10
|
+
Types::String.new this.items.map { |o|
|
11
|
+
if o.nil? || o.is_a?(Types::Undefined) || o.is_a?(Types::Null)
|
12
|
+
""
|
13
|
+
else
|
14
|
+
Types.to_string(o).string
|
15
|
+
end
|
16
|
+
}.join(",")
|
17
|
+
end, nil, "toString", [])
|
18
|
+
# Array.prototype.valueOf
|
19
|
+
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this }, nil, "valueOf", [])
|
20
|
+
# Array.prototype.pop
|
21
|
+
proto.proto_put "pop", Types::Function.new(->(scope, this, args) {
|
22
|
+
Lib.throw_type_error "Array.prototype.pop is not generic" unless this.is_a? Types::Array
|
23
|
+
this.items.pop || Types::Undefined.new
|
24
|
+
}, nil, "pop", [])
|
25
|
+
# Array.prototype.shift
|
26
|
+
proto.proto_put "shift", Types::Function.new(->(scope, this, args) {
|
27
|
+
Lib.throw_type_error "Array.prototype.shift is not generic" unless this.is_a? Types::Array
|
28
|
+
this.items.shift || Types::Undefined.new
|
29
|
+
}, nil, "shift", [])
|
30
|
+
# Array.prototype.push
|
31
|
+
proto.proto_put "push", Types::Function.new(->(scope, this, args) {
|
32
|
+
Lib.throw_type_error "Array.prototype.push is not generic" unless this.is_a? Types::Array
|
33
|
+
args.each { |a| this.items.push a }
|
34
|
+
Types::Number.new this.items.size
|
35
|
+
}, nil, "push", [])
|
36
|
+
# Array.prototype.unshift
|
37
|
+
proto.proto_put "unshift", Types::Function.new(->(scope, this, args) {
|
38
|
+
Lib.throw_type_error "Array.prototype.unshift is not generic" unless this.is_a? Types::Array
|
39
|
+
args.each { |a| this.items.unshift a }
|
40
|
+
Types::Number.new this.items.size
|
41
|
+
}, nil, "unshift", [])
|
42
|
+
# Array.prototype.slice
|
43
|
+
proto.proto_put "slice", Types::Function.new(->(scope, this, args) {
|
44
|
+
begin_index = Types.to_number(args[0] || Types::Undefined.new)
|
45
|
+
end_index = Types.to_number(args[1] || Types::Undefined.new)
|
46
|
+
begin_index = Types::Number.new(0) if begin_index.nan? || begin_index.infinite?
|
47
|
+
if end_index.nan? || end_index.infinite?
|
48
|
+
Types::Array.new this.generic_items[begin_index.number.to_i..-1]
|
49
|
+
else
|
50
|
+
Types::Array.new this.generic_items[begin_index.number.to_i...end_index.number.to_i]
|
51
|
+
end
|
52
|
+
}, nil, "slice", [])
|
53
|
+
# Array.prototype.sort
|
54
|
+
proto.proto_put "sort", Types::Function.new(->(scope, this, args) {
|
55
|
+
Lib.throw_type_error "Array.prototype.sort is not generic" unless this.is_a? Types::Array
|
56
|
+
sortfn = args[0] || ->(scope, this, args) { Types::Number.new(Types.to_string(args[0]).string <=> Types.to_string(args[1]).string) }
|
57
|
+
this.items.reject!(&:nil?)
|
58
|
+
this.items.sort! { |a,b| Types.to_number(sortfn.(scope, this, [a,b])).number }
|
59
|
+
this
|
60
|
+
}, nil, "sort", [])
|
61
|
+
# Array.prototype.length
|
62
|
+
proto.define_own_property "length", get: ->(this) { Types::Number.new this.items.size }, set: ->(this,val) do
|
63
|
+
Lib.throw_type_error "Array.prototype.length is not generic" unless this.is_a? Types::Array
|
64
|
+
len = Types.to_number(val)
|
65
|
+
Lib.throw_range_error "invalid array length" if len.nan? || len.number < 0 || (len.number % 1) != 0
|
66
|
+
this.items = this.items[0...len.number.to_i]
|
67
|
+
len
|
68
|
+
end, writable: true
|
69
|
+
ary.proto_put "prototype", proto
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
obj = Types::BooleanObject.constructor_function
|
4
|
+
scope.set_var "Boolean", obj
|
5
|
+
|
6
|
+
proto = Types::Object.new
|
7
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
8
|
+
if this.is_a?(Types::BooleanObject)
|
9
|
+
Types::String.new(this.boolean.to_s)
|
10
|
+
else
|
11
|
+
Lib.throw_type_error "Boolean.prototype.toString is not generic"
|
12
|
+
end
|
13
|
+
}, nil, "toString", [])
|
14
|
+
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) {
|
15
|
+
if this.is_a?(Types::BooleanObject)
|
16
|
+
Types::Boolean.new(this.boolean)
|
17
|
+
else
|
18
|
+
Types.to_primitive(this)
|
19
|
+
end
|
20
|
+
}, nil, "valueOf", [])
|
21
|
+
obj.proto_put "prototype", proto
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
log = Types::Function.new(->(scope, this, args) { puts args.map { |o| Types.to_string(o).string }.join(" ") }, nil, "log", ["string"])
|
4
|
+
|
5
|
+
console = Types::Object.new
|
6
|
+
["log", "info", "warn", "error"].each do |m|
|
7
|
+
console.proto_put m, log
|
8
|
+
end
|
9
|
+
console.proto_put "_gets", Types::Function.new(->(scope, this, args) { Types::String.new gets }, nil, "_gets", [])
|
10
|
+
console.proto_put "_print", Types::Function.new(->(scope, this, args) { print args.map { |o| Types.to_string(o).string }.join(" ") }, nil, "_print", ["string"])
|
11
|
+
scope.set_var "console", console
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
obj = Types::Function.new(->(scope, this, args) {
|
4
|
+
return Types::String.new Time.now.strftime("%a %b %d %Y %H:%M:%S GMT%z (%Z)") unless this.constructing?
|
5
|
+
args.map! { |a| Types.to_primitive a }
|
6
|
+
if args.size.zero?
|
7
|
+
this.data[:time] = Time.now
|
8
|
+
elsif args.size == 1
|
9
|
+
if args[0].is_a?(Types::Number)
|
10
|
+
this.data[:time] = Time.at *(args[0].number*1000).divmod(1000_000)
|
11
|
+
else
|
12
|
+
this.data[:time] = Time.parse Types.to_string(args[0]).string rescue this.data[:time] = :invalid
|
13
|
+
end
|
14
|
+
else
|
15
|
+
args = args.take(7)
|
16
|
+
.map { |a| Types.to_uint32(a) }
|
17
|
+
.zip([9999, 12, 31, 23, 59, 59, 999]).map { |a,b| [a || 0, b].min }
|
18
|
+
args[6] *= 1000 if args[6]
|
19
|
+
this.data[:time] = Time.local *args
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
}, nil, "Date", [])
|
23
|
+
scope.set_var "Date", obj
|
24
|
+
|
25
|
+
obj.proto_put "now", Types::Function.new(->(scope, this, args) { Types::Number.new (Time.now.to_f * 1000).floor }, nil, "now", [])
|
26
|
+
obj.proto_put "parse", Types::Function.new(->(scope, this, args) {
|
27
|
+
Types::Number.new (Time.parse(Types.to_string(args[0]).string).to_f * 1000).floor
|
28
|
+
}, nil, "parse", [])
|
29
|
+
obj.proto_put "UTC", Types::Function.new(->(scope, this, args) {
|
30
|
+
args = args.take(7)
|
31
|
+
.map { |a| Types.to_uint32(a) }
|
32
|
+
.zip([9999, 12, 31, 23, 59, 59, 999]) { |a,b| [a || 0, b].min }
|
33
|
+
args[6] *= 1000 if args[6]
|
34
|
+
Types::Number.new (Time.utc(*args).to_f * 1000).floor
|
35
|
+
}, nil, "UTC", [])
|
36
|
+
|
37
|
+
proto = Types::Object.new
|
38
|
+
obj.proto_put "prototype", proto
|
39
|
+
|
40
|
+
{ "Date" => :day, "Day" => :wday, "FullYear" => :year, "Hours" => :hour,
|
41
|
+
"Milliseconds" => ->t{ (t.usec / 1000).to_i }, "Minutes" => :min, "Month" => :month,
|
42
|
+
"Seconds" => :sec, "Time" => ->t{ (t.to_f * 1000).floor }, "TimezoneOffset" => ->t{ t.utc_offset / 60 } }.each do |prop,method|
|
43
|
+
# Date.prototype.getXXX
|
44
|
+
proto.proto_put "get#{prop}", Types::Function.new(->(scope, this, args) {
|
45
|
+
Lib.throw_type_error "this is not a Date object" unless this.data[:time]
|
46
|
+
return Types::Number.new(Float::NAN) if this.data[:time] == :invalid
|
47
|
+
Types::Number.new(
|
48
|
+
if method.is_a? Symbol then this.data[:time].send method
|
49
|
+
elsif method.is_a? Proc then method.call this.data[:time]
|
50
|
+
end
|
51
|
+
)
|
52
|
+
}, nil, "get#{prop}", [])
|
53
|
+
# Date.prototype.getUTCXXX
|
54
|
+
proto.proto_put "getUTC#{prop}", Types::Function.new(->(scope, this, args) {
|
55
|
+
Lib.throw_type_error "this is not a Date object" unless this.data[:time]
|
56
|
+
return Types::Number.new(Float::NAN) if this.data[:time] == :invalid
|
57
|
+
Types::Number.new(
|
58
|
+
if method.is_a? Symbol then this.data[:time].send method
|
59
|
+
elsif method.is_a? Proc then method.call this.data[:time]
|
60
|
+
end
|
61
|
+
)
|
62
|
+
}, nil, "getUTC#{prop}", [])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
error = Types::Function.new(->(scope, this, args) {
|
4
|
+
this.proto_put "name", Types::String.new("Error")
|
5
|
+
this.proto_put "message", (args[0] || Types::Undefined.new)
|
6
|
+
nil # so the constructor ends up returning the object being constructed
|
7
|
+
}, nil, "Error", [])
|
8
|
+
scope.set_var "Error", error
|
9
|
+
error.prototype = Types::Object.new
|
10
|
+
error.prototype.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
11
|
+
Types::String.new "#{Types.to_string(this.get "name").string}: #{Types.to_string(this.get "message").string}"
|
12
|
+
}, nil, "toString", [])
|
13
|
+
error.proto_put "prototype", error.prototype
|
14
|
+
|
15
|
+
["Eval", "Range", "Reference", "Syntax", "Type", "URI"].each do |e|
|
16
|
+
obj = Types::Function.new(->(scope, this, args) {
|
17
|
+
this.proto_put "name", Types::String.new("#{e}Error")
|
18
|
+
this.proto_put "message", (args[0] || Types::Undefined.new)
|
19
|
+
nil
|
20
|
+
}, nil, "#{e}Error", [])
|
21
|
+
scope.set_var "#{e}Error", obj
|
22
|
+
obj.prototype = error.prototype
|
23
|
+
# proto = Types::Object.new
|
24
|
+
# proto.prototype = error.prototype
|
25
|
+
obj.proto_put "prototype", error.prototype
|
26
|
+
Lib.define_singleton_method "throw_#{e.downcase}_error" do |message|
|
27
|
+
exc = Types::Object.new
|
28
|
+
exc.construct prototype: obj.prototype, _class: obj do
|
29
|
+
exc.proto_put "name", Types::String.new("#{e}Error")
|
30
|
+
exc.proto_put "message", Types::String.new(message)
|
31
|
+
end
|
32
|
+
throw :exception, exc
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
obj = Types::Function.constructor_function
|
4
|
+
scope.set_var "Function", obj
|
5
|
+
|
6
|
+
proto = Types::Object.new
|
7
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
8
|
+
if this.is_a?(Types::Function)
|
9
|
+
this.primitive_value
|
10
|
+
else
|
11
|
+
Lib.throw_type_error "Function.prototype.toString is not generic"
|
12
|
+
end
|
13
|
+
}, nil, "toString", [])
|
14
|
+
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this }, nil, "valueOf", [])
|
15
|
+
proto.define_own_property "arity", get: ->(this) { this.arguments.size }, writable: false
|
16
|
+
proto.define_own_property "length", get: ->(this) { this.arguments.size }, writable: false
|
17
|
+
proto.define_own_property "name", get: ->(this) { this.name }, writable: false
|
18
|
+
# Function.prototype.apply
|
19
|
+
proto.proto_put "apply", Types::Function.new(->(scope, this, args) do
|
20
|
+
Lib.throw_type_error "cannot call Function.prototype.apply on non-callable object" unless this.respond_to?(:call)
|
21
|
+
if args[0].nil? || args[0].is_a?(Types::Null) || args[0].is_a?(Types::Undefined)
|
22
|
+
call_this = scope.global_scope.root_object
|
23
|
+
else
|
24
|
+
call_this = Types.to_object(args[0])
|
25
|
+
end
|
26
|
+
call_args = args[1].is_a?(Types::Object) ? args[1].generic_items : []
|
27
|
+
this.call scope, call_this, call_args
|
28
|
+
end, nil, "apply", [])
|
29
|
+
# Function.prototype.call
|
30
|
+
proto.proto_put "call", Types::Function.new(->(scope, this, args) do
|
31
|
+
Lib.throw_type_error "cannot call Function.prototype.call on non-callable object" unless this.respond_to?(:call)
|
32
|
+
if args[0].nil? || args[0].is_a?(Types::Null) || args[0].is_a?(Types::Undefined)
|
33
|
+
call_this = scope.global_scope.root_object
|
34
|
+
else
|
35
|
+
call_this = Types.to_object(args[0])
|
36
|
+
end
|
37
|
+
this.call(scope, call_this, args[1..-1])
|
38
|
+
end, nil, "call", [])
|
39
|
+
# Function.prototype.toString
|
40
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) do
|
41
|
+
this.primitive_value
|
42
|
+
end, nil, "toString", [])
|
43
|
+
obj.proto_put "prototype", proto
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
obj = Types::Object.new
|
4
|
+
scope.set_var "Math", obj
|
5
|
+
|
6
|
+
scope.set_var "Infinity", Types::Number.new(Float::INFINITY)
|
7
|
+
scope.set_var "NaN", Types::Number.new(Float::NAN)
|
8
|
+
|
9
|
+
# one argument functions
|
10
|
+
%w(sqrt sin cos tan).each do |method|
|
11
|
+
obj.proto_put method, Types::Function.new(->(scope, this, args) {
|
12
|
+
Types::Number.new(Math.send method, Types.to_number(args[0] || Undefined.new).number)
|
13
|
+
}, nil, method, [])
|
14
|
+
end
|
15
|
+
|
16
|
+
obj.proto_put "random", Types::Function.new(->(scope, this, args) {
|
17
|
+
Types::Number.new rand
|
18
|
+
}, nil, "random", [])
|
19
|
+
|
20
|
+
obj.proto_put "floor", Types::Function.new(->(scope, this, args) {
|
21
|
+
Types::Number.new Types.to_number(args[0] || Undefined.new).number.floor
|
22
|
+
}, nil, "floor", [])
|
23
|
+
|
24
|
+
obj.proto_put "ceil", Types::Function.new(->(scope, this, args) {
|
25
|
+
Types::Number.new Types.to_number(args[0] || Undefined.new).number.ceil
|
26
|
+
}, nil, "ceil", [])
|
27
|
+
|
28
|
+
obj.proto_put "max", Types::Function.new(->(scope, this, args) {
|
29
|
+
Types::Number.new [-Float::INFINITY, *args.map { |a| Types.to_number(a).number }].max
|
30
|
+
}, nil, "max", [])
|
31
|
+
|
32
|
+
obj.proto_put "min", Types::Function.new(->(scope, this, args) {
|
33
|
+
Types::Number.new [Float::INFINITY, *args.map { |a| Types.to_number(a).number }].min
|
34
|
+
}, nil, "min", [])
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Twostroke::Runtime
|
2
|
+
Lib.register do |scope|
|
3
|
+
obj = Types::NumberObject.constructor_function
|
4
|
+
scope.set_var "Number", obj
|
5
|
+
|
6
|
+
proto = Types::Object.new
|
7
|
+
proto.proto_put "toString", Types::Function.new(->(scope, this, args) {
|
8
|
+
if this.is_a?(Types::NumberObject)
|
9
|
+
Types::String.new(this.number.to_s)
|
10
|
+
else
|
11
|
+
Lib.throw_type_error "Number.prototype.toString is not generic"
|
12
|
+
end
|
13
|
+
}, nil, "toString", [])
|
14
|
+
proto.proto_put "valueOf", Types::Function.new(->(scope, this, args) { this.is_a?(Types::NumberObject) ? Types::Number.new(this.number) : Types.to_primitive(this) }, nil, "valueOf", [])
|
15
|
+
# Number.prototype.toExponential
|
16
|
+
proto.proto_put "toExponential", Types::Function.new(->(scope, this, args) do
|
17
|
+
n = Types.to_number(this)
|
18
|
+
if n.nan? || n.infinite?
|
19
|
+
Types::String.new n.to_s
|
20
|
+
else
|
21
|
+
places = Math.log(n.number, 10).floor
|
22
|
+
significand = n.number / (10 ** places).to_f
|
23
|
+
if args.size > 0
|
24
|
+
sigfigs = Types.to_number(args[0] || Types::Undefined.new)
|
25
|
+
if sigfigs.nan? || sigfigs.infinite?
|
26
|
+
sigfigs = 0
|
27
|
+
else
|
28
|
+
sigfigs = sigfigs.number.to_i
|
29
|
+
end
|
30
|
+
Types::String.new sprintf("%.#{sigfigs}fe%s%d", significand, ("+" if places >= 0), places)
|
31
|
+
else
|
32
|
+
Types::String.new "#{significand}e#{"+" if places >= 0}#{places}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end, nil, "toExponential", [])
|
36
|
+
# Number.prototype.toFixed
|
37
|
+
proto.proto_put "toFixed", Types::Function.new(->(scope, this, args) do
|
38
|
+
digits = Types.to_number(args[0] || Undefined.new)
|
39
|
+
if digits.nan? || digits.infinite?
|
40
|
+
digits = 0
|
41
|
+
else
|
42
|
+
digits = digits.number
|
43
|
+
end
|
44
|
+
Types::String.new sprintf("%.#{[[0,digits].max,20].min}f", Types.to_number(this).number)
|
45
|
+
end, nil, "toFixed", [])
|
46
|
+
# Number.prototype.toLocaleString
|
47
|
+
proto.proto_put "toLocaleString", proto.get("toString")
|
48
|
+
# Number.prototype.toString
|
49
|
+
proto.proto_put "toPrecision", Types::Function.new(->(scope, this, args) do
|
50
|
+
digits = Types.to_number(args[0] || Undefined.new)
|
51
|
+
if digits.nan? || digits.infinite?
|
52
|
+
digits = 0
|
53
|
+
else
|
54
|
+
digits = digits.number
|
55
|
+
end
|
56
|
+
Types::Number.new Types.to_number(this).number.round([[digits,0].max, 100].min)
|
57
|
+
end, nil, "toString", [])
|
58
|
+
obj.proto_put "prototype", proto
|
59
|
+
obj.proto_put "MAX_VALUE", Types::Number.new(Float::MAX)
|
60
|
+
obj.proto_put "MIN_VALUE", Types::Number.new(Float::MIN)
|
61
|
+
obj.proto_put "NaN", Types::Number.new(Float::NAN)
|
62
|
+
obj.proto_put "NEGATIVE_INFINITY", Types::Number.new(-Float::INFINITY)
|
63
|
+
obj.proto_put "POSITIVE_INFINITY", Types::Number.new(Float::INFINITY)
|
64
|
+
end
|
65
|
+
end
|
File without changes
|