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
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
|