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.
Files changed (80) hide show
  1. data/lib/twostroke/ast/array.rb +8 -0
  2. data/lib/twostroke/ast/assignment.rb +7 -0
  3. data/lib/twostroke/ast/binary_operators.rb +7 -0
  4. data/lib/twostroke/ast/body.rb +6 -0
  5. data/lib/twostroke/ast/break.rb +4 -0
  6. data/lib/twostroke/ast/call.rb +7 -0
  7. data/lib/twostroke/ast/case.rb +9 -1
  8. data/lib/twostroke/ast/continue.rb +11 -0
  9. data/lib/twostroke/ast/declaration.rb +4 -0
  10. data/lib/twostroke/ast/delete.rb +6 -0
  11. data/lib/twostroke/ast/do_while.rb +7 -0
  12. data/lib/twostroke/ast/false.rb +11 -0
  13. data/lib/twostroke/ast/for_in.rb +8 -0
  14. data/lib/twostroke/ast/for_loop.rb +10 -1
  15. data/lib/twostroke/ast/function.rb +6 -0
  16. data/lib/twostroke/ast/if.rb +8 -0
  17. data/lib/twostroke/ast/index.rb +7 -0
  18. data/lib/twostroke/ast/member_access.rb +6 -0
  19. data/lib/twostroke/ast/multi_expression.rb +7 -0
  20. data/lib/twostroke/ast/new.rb +7 -0
  21. data/lib/twostroke/ast/null.rb +4 -0
  22. data/lib/twostroke/ast/number.rb +4 -0
  23. data/lib/twostroke/ast/object_literal.rb +6 -0
  24. data/lib/twostroke/ast/regexp.rb +4 -0
  25. data/lib/twostroke/ast/return.rb +6 -0
  26. data/lib/twostroke/ast/string.rb +4 -0
  27. data/lib/twostroke/ast/switch.rb +7 -0
  28. data/lib/twostroke/ast/ternary.rb +8 -0
  29. data/lib/twostroke/ast/this.rb +4 -0
  30. data/lib/twostroke/ast/throw.rb +6 -0
  31. data/lib/twostroke/ast/true.rb +11 -0
  32. data/lib/twostroke/ast/try.rb +8 -0
  33. data/lib/twostroke/ast/unary_operators.rb +7 -1
  34. data/lib/twostroke/ast/variable.rb +4 -0
  35. data/lib/twostroke/ast/while.rb +8 -1
  36. data/lib/twostroke/ast/with.rb +16 -0
  37. data/lib/twostroke/compiler/javascript.rb +396 -0
  38. data/lib/twostroke/compiler/tsasm.rb +595 -0
  39. data/lib/twostroke/compiler.rb +8 -0
  40. data/lib/twostroke/parser.rb +47 -9
  41. data/lib/twostroke/runtime/lib/array.js +144 -0
  42. data/lib/twostroke/runtime/lib/array.rb +71 -0
  43. data/lib/twostroke/runtime/lib/boolean.rb +23 -0
  44. data/lib/twostroke/runtime/lib/console.rb +13 -0
  45. data/lib/twostroke/runtime/lib/date.rb +65 -0
  46. data/lib/twostroke/runtime/lib/error.rb +36 -0
  47. data/lib/twostroke/runtime/lib/function.js +0 -0
  48. data/lib/twostroke/runtime/lib/function.rb +45 -0
  49. data/lib/twostroke/runtime/lib/math.rb +36 -0
  50. data/lib/twostroke/runtime/lib/number.rb +65 -0
  51. data/lib/twostroke/runtime/lib/object.js +0 -0
  52. data/lib/twostroke/runtime/lib/object.rb +55 -0
  53. data/lib/twostroke/runtime/lib/regexp.rb +28 -0
  54. data/lib/twostroke/runtime/lib/string.rb +86 -0
  55. data/lib/twostroke/runtime/lib/undefined.rb +7 -0
  56. data/lib/twostroke/runtime/lib.rb +42 -0
  57. data/lib/twostroke/runtime/scope.rb +120 -0
  58. data/lib/twostroke/runtime/types/array.rb +79 -0
  59. data/lib/twostroke/runtime/types/boolean.rb +23 -0
  60. data/lib/twostroke/runtime/types/boolean_object.rb +25 -0
  61. data/lib/twostroke/runtime/types/function.rb +83 -0
  62. data/lib/twostroke/runtime/types/null.rb +11 -3
  63. data/lib/twostroke/runtime/types/number.rb +31 -0
  64. data/lib/twostroke/runtime/types/number_object.rb +25 -0
  65. data/lib/twostroke/runtime/types/object.rb +169 -20
  66. data/lib/twostroke/runtime/types/regexp.rb +31 -0
  67. data/lib/twostroke/runtime/types/string.rb +16 -0
  68. data/lib/twostroke/runtime/types/string_object.rb +52 -0
  69. data/lib/twostroke/runtime/types/undefined.rb +11 -3
  70. data/lib/twostroke/runtime/types/value.rb +14 -0
  71. data/lib/twostroke/runtime/types.rb +133 -4
  72. data/lib/twostroke/runtime/vm.rb +25 -0
  73. data/lib/twostroke/runtime/vm_frame.rb +459 -0
  74. data/lib/twostroke/runtime.rb +6 -5
  75. data/lib/twostroke/tokens.rb +20 -8
  76. data/lib/twostroke.rb +3 -1
  77. metadata +41 -7
  78. data/lib/twostroke/runtime/context.rb +0 -33
  79. data/lib/twostroke/runtime/environment.rb +0 -13
  80. data/lib/twostroke/runtime/types/basic_type.rb +0 -5
@@ -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
- current_case = AST::Case.new expression: expr
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 << current_case
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.statements << statement
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(false, true)
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