twostroke 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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