erblite 0.5.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.
@@ -0,0 +1,1332 @@
1
+ # Copyright 2026 OpenC3, Inc.
2
+ # All Rights Reserved.
3
+ #
4
+ # This program is distributed in the hope that it will be useful,
5
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
6
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7
+ # See LICENSE.md for more details.
8
+
9
+ # frozen_string_literal: true
10
+
11
+ module ERBLite
12
+ # The allowlist of methods, functions and constants templates can use.
13
+ #
14
+ # Every table maps a method name (a String) to an Fn holding a lambda and
15
+ # the allowed argument count. A name that is not in a table does not exist
16
+ # for templates (NoMethodError). Handlers receive (interp, receiver, args,
17
+ # block) and call fixed, native Ruby methods on the receiver; no handler
18
+ # ever calls a method whose name comes from the template.
19
+ module Builtins
20
+ INT_MIN = -(2**63)
21
+ INT_MAX = (2**63) - 1
22
+
23
+ Fn = Struct.new(:fn, :min, :max)
24
+
25
+ def self.defm(table, names, min, max = min, &fn)
26
+ Array(names).each { |n| table[n] = Fn.new(fn, min, max) }
27
+ end
28
+
29
+ def self.check_arity(m, n)
30
+ return if n >= m.min && (m.max.negative? || n <= m.max)
31
+
32
+ exp = if m.max == m.min then m.min.to_s
33
+ elsif m.max.negative? then "#{m.min}+"
34
+ else "#{m.min}..#{m.max}"
35
+ end
36
+ raise Error.new("ArgumentError", "wrong number of arguments (given #{n}, expected #{exp})")
37
+ end
38
+
39
+ # -- errors and type helpers -------------------------------------------------
40
+
41
+ def self.err(type, msg)
42
+ Error.new(type, msg)
43
+ end
44
+
45
+ # Converts a native Ruby exception raised inside a handler into a template
46
+ # error with the matching type name.
47
+ def self.map_error(e)
48
+ type = case e
49
+ when ::FloatDomainError then "FloatDomainError"
50
+ when ::ZeroDivisionError then "ZeroDivisionError"
51
+ when ::Math::DomainError then "Math::DomainError"
52
+ when ::KeyError then "KeyError"
53
+ when ::StopIteration then "IndexError"
54
+ when ::IndexError then "IndexError"
55
+ when ::RangeError then "RangeError"
56
+ when ::FrozenError then "FrozenError"
57
+ when ::NoMethodError then "NoMethodError"
58
+ when ::NameError then "NameError"
59
+ when ::TypeError then "TypeError"
60
+ when ::EncodingError then "ArgumentError"
61
+ when ::ArgumentError then "ArgumentError"
62
+ when ::IOError, ::SystemCallError then "IOError"
63
+ else "RuntimeError"
64
+ end
65
+ msg = e.message.to_s.gsub("ERBLite::ClassValue", "Class").gsub("ERBLite::Enum", "Enumerator")
66
+ Error.new(type, msg)
67
+ end
68
+
69
+ # [ERBLite] Largest String/Array an operation may build from a numeric
70
+ # size argument (String#*, Array#*, Array.new, ljust, a[i] = v, ...).
71
+ MAX_SIZE = 10_000_000
72
+
73
+ def self.cap(n)
74
+ raise err("LimitError", "result would be too large (more than #{MAX_SIZE} elements)") if n > MAX_SIZE
75
+
76
+ n
77
+ end
78
+
79
+ def self.ck(v)
80
+ if v.is_a?(Integer) && (v < INT_MIN || v > INT_MAX)
81
+ raise err("RangeError", "integer overflow: result does not fit in 64 bits")
82
+ end
83
+ v
84
+ end
85
+
86
+ def self.class_name(v)
87
+ case v
88
+ when nil then "NilClass"
89
+ when true then "TrueClass"
90
+ when false then "FalseClass"
91
+ when Integer then "Integer"
92
+ when Float then "Float"
93
+ when String then "String"
94
+ when Symbol then "Symbol"
95
+ when Array then "Array"
96
+ when Hash then "Hash"
97
+ when Range then "Range"
98
+ when Enum then "Enumerator"
99
+ when ClassValue then v.module? ? "Module" : "Class"
100
+ else "Object"
101
+ end
102
+ end
103
+
104
+ # Name used in "no implicit conversion of X into Y" messages.
105
+ def self.tname(v)
106
+ case v
107
+ when nil then "nil"
108
+ when true then "true"
109
+ when false then "false"
110
+ else class_name(v)
111
+ end
112
+ end
113
+
114
+ def self.describe(v)
115
+ case v
116
+ when nil then "nil"
117
+ when true then "true"
118
+ when false then "false"
119
+ when ClassValue then "#{v.module? ? "module" : "class"} #{v.name}"
120
+ else "an instance of #{class_name(v)}"
121
+ end
122
+ end
123
+
124
+ def self.need_int(v)
125
+ raise err("TypeError", "no implicit conversion of #{tname(v)} into Integer") unless v.is_a?(Integer)
126
+
127
+ v
128
+ end
129
+
130
+ # Integer or Float (truncated), as Ruby accepts for indexes and sizes.
131
+ def self.need_intish(v)
132
+ return v if v.is_a?(Integer)
133
+ return ck(v.to_i) if v.is_a?(Float)
134
+
135
+ raise err("TypeError", "no implicit conversion of #{tname(v)} into Integer")
136
+ end
137
+
138
+ def self.need_str(v)
139
+ raise err("TypeError", "no implicit conversion of #{tname(v)} into String") unless v.is_a?(String)
140
+
141
+ v
142
+ end
143
+
144
+ def self.need_ary(v)
145
+ raise err("TypeError", "no implicit conversion of #{tname(v)} into Array") unless v.is_a?(Array)
146
+
147
+ v
148
+ end
149
+
150
+ def self.need_hash(v)
151
+ raise err("TypeError", "no implicit conversion of #{tname(v)} into Hash") unless v.is_a?(Hash)
152
+
153
+ v
154
+ end
155
+
156
+ def self.need_num(v, into)
157
+ return v if v.is_a?(Integer) || v.is_a?(Float)
158
+
159
+ raise err("TypeError", "#{tname(v)} can't be coerced into #{into}")
160
+ end
161
+
162
+ def self.need_blk(b, name)
163
+ raise err("ArgumentError", "no block given (#{name})") unless b
164
+
165
+ b
166
+ end
167
+
168
+ def self.cmp_desc(v)
169
+ case v
170
+ when nil, true, false, Integer, Float, Symbol then v.inspect
171
+ else class_name(v)
172
+ end
173
+ end
174
+
175
+ def self.cmp_fail(a, b)
176
+ raise err("ArgumentError", "comparison of #{class_name(a)} with #{cmp_desc(b)} failed")
177
+ end
178
+
179
+ def self.to_s(v)
180
+ case v
181
+ when String then v
182
+ when nil then +""
183
+ when Symbol then v.to_s
184
+ when Array, Hash then inspect(v)
185
+ else v.to_s
186
+ end
187
+ end
188
+
189
+ # Symbol hash keys that print bare (`name: v`) in Ruby 3.4's Hash#inspect.
190
+ BARE_KEY = /\A(?:[A-Za-z_]|[^\x00-\x7f])(?:[A-Za-z0-9_]|[^\x00-\x7f])*[?!]?\z/
191
+
192
+ # Ruby 3.4 inspect for every template value, independent of the host
193
+ # Ruby version (Hash#inspect changed format in 3.4).
194
+ def self.inspect(v, seen = {}.compare_by_identity)
195
+ case v
196
+ when Array
197
+ return "[...]" if seen[v]
198
+
199
+ seen[v] = true
200
+ out = "[#{v.map { |e| inspect(e, seen) }.join(", ")}]"
201
+ seen.delete(v)
202
+ out
203
+ when Hash
204
+ return "{...}" if seen[v]
205
+ return "{}" if v.empty?
206
+
207
+ seen[v] = true
208
+ out = "{#{v.map { |k, val| "#{inspect_key(k, seen)} #{inspect(val, seen)}" }.join(", ")}}"
209
+ seen.delete(v)
210
+ out
211
+ when Range
212
+ "#{inspect(v.begin, seen) unless v.begin.nil?}#{v.exclude_end? ? "..." : ".."}#{inspect(v.end, seen) unless v.end.nil?}"
213
+ when Enum
214
+ v.inspect
215
+ else
216
+ v.inspect
217
+ end
218
+ end
219
+
220
+ def self.inspect_key(k, seen)
221
+ return "#{inspect(k, seen)} =>" unless k.is_a?(Symbol)
222
+
223
+ name = k.to_s
224
+ name.match?(BARE_KEY) ? "#{name}:" : "#{name.inspect}:"
225
+ end
226
+
227
+ def self.is_a(v, cls)
228
+ raise err("TypeError", "class or module required") unless cls.is_a?(ClassValue)
229
+
230
+ ANCESTORS.fetch(class_name(v), ["Object"]).include?(cls.name)
231
+ end
232
+
233
+ def self.case_eq(pat, v)
234
+ case pat
235
+ when ClassValue then is_a(v, pat)
236
+ when Range
237
+ begin
238
+ pat === v # rubocop:disable Style/CaseEquality
239
+ rescue ::StandardError
240
+ false
241
+ end
242
+ else pat == v
243
+ end
244
+ end
245
+
246
+ def self.make_range(lo, hi, excl)
247
+ Range.new(lo, hi, excl)
248
+ rescue ::ArgumentError
249
+ raise err("ArgumentError", "bad value for range")
250
+ end
251
+
252
+ # -- integer arithmetic with 64-bit overflow checks ----------------------------
253
+
254
+ def self.ipow(b, e)
255
+ raise err("RangeError", "negative exponent: Integer ** negative Integer is not supported") if e.negative?
256
+ return 1 if e.zero?
257
+ return b if b.zero? || b == 1
258
+ return e.even? ? 1 : -1 if b == -1
259
+ raise err("RangeError", "integer overflow: result does not fit in 64 bits") if e > 64
260
+
261
+ ck(b**e)
262
+ end
263
+
264
+ def self.shl(r, n)
265
+ return shr(r, -n) if n.negative?
266
+ return 0 if r.zero?
267
+ raise err("RangeError", "integer overflow: result does not fit in 64 bits") if n >= 64
268
+
269
+ ck(r << n)
270
+ end
271
+
272
+ def self.shr(r, n)
273
+ return shl(r, -n) if n.negative?
274
+ return r.negative? ? -1 : 0 if n >= 64
275
+
276
+ r >> n
277
+ end
278
+
279
+ def self.fpow(r, o)
280
+ res = r**o
281
+ res.is_a?(Float) || res.is_a?(Integer) ? res : Float::NAN
282
+ end
283
+
284
+ # -- classes and constants -------------------------------------------------------
285
+
286
+ ANCESTORS = {
287
+ "NilClass" => %w[NilClass Object], "TrueClass" => %w[TrueClass Object],
288
+ "FalseClass" => %w[FalseClass Object],
289
+ "Integer" => %w[Integer Numeric Comparable Object], "Float" => %w[Float Numeric Comparable Object],
290
+ "String" => %w[String Comparable Object], "Symbol" => %w[Symbol Comparable Object],
291
+ "Array" => %w[Array Enumerable Object], "Hash" => %w[Hash Enumerable Object],
292
+ "Range" => %w[Range Enumerable Object], "Enumerator" => %w[Enumerator Enumerable Object],
293
+ "Class" => %w[Class Module Object], "Module" => %w[Module Object]
294
+ }.freeze
295
+
296
+ CLASS_NAMES = %w[Integer Float String Symbol Array Hash Range NilClass TrueClass FalseClass
297
+ Numeric Object Enumerator File Dir].freeze
298
+ MODULE_NAMES = %w[Comparable Enumerable Math JSON].freeze
299
+ CONSTANTS = (CLASS_NAMES.to_h { |n| [n, ClassValue.new(n, false)] }
300
+ .merge(MODULE_NAMES.to_h { |n| [n, ClassValue.new(n, true)] })
301
+ .merge("Class" => ClassValue.new("Class", false), "Module" => ClassValue.new("Module", false)))
302
+ .freeze
303
+ PUBLIC_CONSTANTS = (CLASS_NAMES + MODULE_NAMES).to_h { |n| [n, true] }.freeze
304
+
305
+ SCOPED_CONSTANTS = {
306
+ "Math" => { "PI" => Math::PI, "E" => Math::E },
307
+ "Float" => { "INFINITY" => Float::INFINITY, "NAN" => Float::NAN, "EPSILON" => Float::EPSILON,
308
+ "MAX" => Float::MAX, "MIN" => Float::MIN }
309
+ }.freeze
310
+
311
+ def self.lookup_constant(name)
312
+ PUBLIC_CONSTANTS[name] ? CONSTANTS[name] : nil
313
+ end
314
+
315
+ def self.scoped_constant(scope, name, filename, line)
316
+ unless scope.is_a?(ClassValue)
317
+ raise Error.new("TypeError", "#{scope.inspect} is not a class/module", filename, line)
318
+ end
319
+
320
+ table = SCOPED_CONSTANTS[scope.name]
321
+ return table[name] if table&.key?(name)
322
+
323
+ raise Error.new("NameError", "uninitialized constant #{scope.name}::#{name}", filename, line)
324
+ end
325
+
326
+ def self.class_of(v)
327
+ CONSTANTS[class_name(v)]
328
+ end
329
+
330
+ # -- method lookup -----------------------------------------------------------------
331
+
332
+ COMMON_T = {}
333
+ NIL_T = {}
334
+ BOOL_T = {}
335
+ INT_T = {}
336
+ FLOAT_T = {}
337
+ STR_T = {}
338
+ SYM_T = {}
339
+ ARR_T = {}
340
+ HASH_T = {}
341
+ RANGE_T = {}
342
+ ENUMR_T = {}
343
+ ENUM_T = {}
344
+ CLASS_T = {}
345
+ CLASS_METHODS = Hash.new({}.freeze)
346
+ FUNCS = {}
347
+
348
+ def self.lookup(r, name)
349
+ case r
350
+ when String then STR_T[name] || COMMON_T[name]
351
+ when Integer then INT_T[name] || COMMON_T[name]
352
+ when Array then ARR_T[name] || ENUM_T[name] || COMMON_T[name]
353
+ when Hash then HASH_T[name] || ENUM_T[name] || COMMON_T[name]
354
+ when nil then NIL_T[name] || COMMON_T[name]
355
+ when Float then FLOAT_T[name] || COMMON_T[name]
356
+ when Symbol then SYM_T[name] || COMMON_T[name]
357
+ when true, false then BOOL_T[name] || COMMON_T[name]
358
+ when Range then RANGE_T[name] || ENUM_T[name] || COMMON_T[name]
359
+ when Enum then ENUMR_T[name] || ENUM_T[name] || COMMON_T[name]
360
+ when ClassValue then CLASS_METHODS[r.name][name] || CLASS_T[name] || COMMON_T[name]
361
+ end
362
+ end
363
+
364
+ # Iterator methods that return an Enumerator when called without a block.
365
+ ENUM_NO_BLOCK = %w[each each_pair each_with_index each_with_object each_slice each_cons map collect
366
+ flat_map collect_concat select filter reject filter_map find_all
367
+ each_key each_value each_index reverse_each sort_by
368
+ min_by max_by group_by partition delete_if find detect find_index index rindex
369
+ transform_values transform_keys take_while drop_while with_index with_object
370
+ step].to_h { |n| [n, true] }.freeze
371
+ INT_ENUM_NO_BLOCK = %w[times upto downto step].to_h { |n| [n, true] }.freeze
372
+ STR_ENUM_NO_BLOCK = %w[each_char each_line].to_h { |n| [n, true] }.freeze
373
+ ARG_FORMS = %w[find_index index rindex transform_keys].to_h { |n| [n, true] }.freeze
374
+
375
+ def self.enumerator_call?(recv, name, args)
376
+ case recv
377
+ when Array, Hash, Range, Enum
378
+ return false unless ENUM_NO_BLOCK[name]
379
+ return false if name == "each" && recv.is_a?(Enum)
380
+ return false if ARG_FORMS[name] && !args.empty?
381
+
382
+ if name == "each_slice" || name == "each_cons"
383
+ n = need_intish(args[0])
384
+ raise err("ArgumentError", "invalid #{name == "each_slice" ? "slice " : ""}size") unless n.positive?
385
+ end
386
+ true
387
+ when Integer then INT_ENUM_NO_BLOCK[name] || name == "then"
388
+ when String then STR_ENUM_NO_BLOCK[name] || name == "then"
389
+ else name == "then"
390
+ end
391
+ end
392
+
393
+ # ===================================================================================
394
+ # All values
395
+ # ===================================================================================
396
+
397
+ defm(COMMON_T, "==", 1) { |_, r, a| r == a[0] }
398
+ defm(COMMON_T, "!=", 1) { |_, r, a| r != a[0] }
399
+ defm(COMMON_T, "!", 0) { |it, r| !it.truthy(r) }
400
+ defm(COMMON_T, "nil?", 0) { |_, r| r.nil? }
401
+ defm(COMMON_T, "to_s", 0) { |_, r| Builtins.to_s(r) }
402
+ defm(COMMON_T, "inspect", 0) { |_, r| Builtins.inspect(r) }
403
+ defm(COMMON_T, "class", 0) { |_, r| Builtins.class_of(r) }
404
+ defm(COMMON_T, %w[is_a? kind_of?], 1) { |_, r, a| Builtins.is_a(r, a[0]) }
405
+ defm(COMMON_T, "instance_of?", 1) do |_, r, a|
406
+ raise Builtins.err("TypeError", "class or module required") unless a[0].is_a?(ClassValue)
407
+
408
+ Builtins.class_name(r) == a[0].name
409
+ end
410
+ defm(COMMON_T, "respond_to?", 1, 2) do |it, r, a|
411
+ n = a[0]
412
+ unless n.is_a?(Symbol) || n.is_a?(String)
413
+ raise Builtins.err("TypeError", "#{n.inspect} is not a symbol nor a string")
414
+ end
415
+
416
+ it.responds?(r, n.to_s)
417
+ end
418
+ defm(COMMON_T, "dup", 0) { |_, r| r.is_a?(String) || r.is_a?(Array) || r.is_a?(Hash) ? r.dup : r }
419
+ defm(COMMON_T, "freeze", 0) do |_, r|
420
+ r.freeze if r.is_a?(String) || r.is_a?(Array) || r.is_a?(Hash)
421
+ r
422
+ end
423
+ defm(COMMON_T, "frozen?", 0) { |_, r| r.is_a?(ClassValue) || r.is_a?(Enum) ? false : r.frozen? }
424
+ defm(COMMON_T, "to_json", 0, -1) { |_, r| JSONSupport.generate(r) }
425
+ defm(COMMON_T, "then", 0) { |_, r, _, b| b.call(r) }
426
+ defm(COMMON_T, "tap", 0) do |_, r, _, b|
427
+ Builtins.need_blk(b, "tap").call(r)
428
+ r
429
+ end
430
+ defm(COMMON_T, "eql?", 1) { |_, r, a| r.eql?(a[0]) }
431
+ defm(COMMON_T, "equal?", 1) { |_, r, a| r.equal?(a[0]) }
432
+ defm(COMMON_T, "===", 1) { |_, r, a| Builtins.case_eq(r, a[0]) }
433
+ defm(COMMON_T, "<=>", 1) { |_, r, a| r == a[0] ? 0 : nil }
434
+
435
+ # ===================================================================================
436
+ # nil, true, false
437
+ # ===================================================================================
438
+
439
+ defm(NIL_T, "to_a", 0) { [] }
440
+ defm(NIL_T, "to_i", 0) { 0 }
441
+ defm(NIL_T, "to_f", 0) { 0.0 }
442
+ defm(NIL_T, "to_h", 0) { {} }
443
+ defm(NIL_T, "&", 1) { false }
444
+ defm(NIL_T, %w[| ^], 1) { |it, _, a| it.truthy(a[0]) }
445
+
446
+ defm(BOOL_T, "&", 1) { |it, r, a| r ? it.truthy(a[0]) : false }
447
+ defm(BOOL_T, "|", 1) { |it, r, a| r ? true : it.truthy(a[0]) }
448
+ defm(BOOL_T, "^", 1) { |it, r, a| r ? !it.truthy(a[0]) : it.truthy(a[0]) }
449
+
450
+ # ===================================================================================
451
+ # Integer
452
+ # ===================================================================================
453
+
454
+ defm(INT_T, "+", 1) do |_, r, a|
455
+ o = a[0]
456
+ o.is_a?(Integer) ? Builtins.ck(r + o) : r + Builtins.need_num(o, "Integer")
457
+ end
458
+ defm(INT_T, "-", 1) do |_, r, a|
459
+ o = a[0]
460
+ o.is_a?(Integer) ? Builtins.ck(r - o) : r - Builtins.need_num(o, "Integer")
461
+ end
462
+ defm(INT_T, "*", 1) do |_, r, a|
463
+ o = a[0]
464
+ o.is_a?(Integer) ? Builtins.ck(r * o) : r * Builtins.need_num(o, "Integer")
465
+ end
466
+ defm(INT_T, "/", 1) do |_, r, a|
467
+ o = a[0]
468
+ if o.is_a?(Integer)
469
+ raise Builtins.err("ZeroDivisionError", "divided by 0") if o.zero?
470
+
471
+ Builtins.ck(r / o)
472
+ else
473
+ r / Builtins.need_num(o, "Integer")
474
+ end
475
+ end
476
+ defm(INT_T, %w[% modulo], 1) do |_, r, a|
477
+ o = a[0]
478
+ if o.is_a?(Integer)
479
+ raise Builtins.err("ZeroDivisionError", "divided by 0") if o.zero?
480
+
481
+ r % o
482
+ else
483
+ r % Builtins.need_num(o, "Integer")
484
+ end
485
+ end
486
+ defm(INT_T, "**", 1) do |_, r, a|
487
+ o = a[0]
488
+ o.is_a?(Integer) ? Builtins.ipow(r, o) : Builtins.fpow(r, Builtins.need_num(o, "Integer"))
489
+ end
490
+ defm(INT_T, "-@", 0) { |_, r| Builtins.ck(-r) }
491
+ defm(INT_T, "+@", 0) { |_, r| r }
492
+ defm(INT_T, "~", 0) { |_, r| ~r }
493
+ defm(INT_T, "&", 1) { |_, r, a| r & Builtins.need_int(a[0]) }
494
+ defm(INT_T, "|", 1) { |_, r, a| r | Builtins.need_int(a[0]) }
495
+ defm(INT_T, "^", 1) { |_, r, a| r ^ Builtins.need_int(a[0]) }
496
+ defm(INT_T, "<<", 1) { |_, r, a| Builtins.shl(r, Builtins.need_intish(a[0])) }
497
+ defm(INT_T, ">>", 1) { |_, r, a| Builtins.shr(r, Builtins.need_intish(a[0])) }
498
+ defm(INT_T, "<=>", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r <=> a[0] : nil }
499
+ defm(INT_T, "<", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r < a[0] : Builtins.cmp_fail(r, a[0]) }
500
+ defm(INT_T, "<=", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r <= a[0] : Builtins.cmp_fail(r, a[0]) }
501
+ defm(INT_T, ">", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r > a[0] : Builtins.cmp_fail(r, a[0]) }
502
+ defm(INT_T, ">=", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r >= a[0] : Builtins.cmp_fail(r, a[0]) }
503
+ defm(INT_T, "abs", 0) { |_, r| Builtins.ck(r.abs) }
504
+ defm(INT_T, %w[succ next], 0) { |_, r| Builtins.ck(r + 1) }
505
+ defm(INT_T, "pred", 0) { |_, r| Builtins.ck(r - 1) }
506
+ defm(INT_T, %w[to_i to_int ord], 0) { |_, r| r }
507
+ defm(INT_T, "to_f", 0) { |_, r| r.to_f }
508
+ defm(INT_T, %w[to_s inspect], 0, 1) do |_, r, a|
509
+ base = a.empty? ? 10 : Builtins.need_intish(a[0])
510
+ raise Builtins.err("ArgumentError", "invalid radix #{base}") unless base.between?(2, 36)
511
+
512
+ r.to_s(base)
513
+ end
514
+ defm(INT_T, "chr", 0) do |_, r|
515
+ raise Builtins.err("RangeError", "#{r} out of char range") unless r.between?(0, 255)
516
+
517
+ r.chr(Encoding::UTF_8)
518
+ end
519
+ defm(INT_T, "times", 0) { |it, r, _, b| r.times(&it.bp(b)) }
520
+ defm(INT_T, "upto", 1) { |it, r, a, b| r.upto(a[0], &it.bp(b)) }
521
+ defm(INT_T, "downto", 1) { |it, r, a, b| r.downto(a[0], &it.bp(b)) }
522
+ defm(INT_T, "step", 1, 2) { |it, r, a, b| r.step(a[0], a.length > 1 ? a[1] : 1, &it.bp(b)) }
523
+ defm(INT_T, "even?", 0) { |_, r| r.even? }
524
+ defm(INT_T, "odd?", 0) { |_, r| r.odd? }
525
+ defm(INT_T, "zero?", 0) { |_, r| r.zero? }
526
+ defm(INT_T, "positive?", 0) { |_, r| r.positive? }
527
+ defm(INT_T, "negative?", 0) { |_, r| r.negative? }
528
+ defm(INT_T, "between?", 2) { |_, r, a| r.between?(a[0], a[1]) }
529
+ defm(INT_T, "clamp", 1, 2) { |_, r, a| a.length == 1 ? r.clamp(a[0]) : r.clamp(a[0], a[1]) }
530
+ defm(INT_T, "divmod", 1) do |_, r, a|
531
+ o = a[0]
532
+ raise Builtins.err("ZeroDivisionError", "divided by 0") if o.is_a?(Integer) && o.zero?
533
+
534
+ r.divmod(Builtins.need_num(o, "Integer")).map { |x| Builtins.ck(x) }
535
+ end
536
+ defm(INT_T, "fdiv", 1) { |_, r, a| r.fdiv(Builtins.need_num(a[0], "Integer")) }
537
+ defm(INT_T, "gcd", 1) { |_, r, a| Builtins.ck(r.gcd(Builtins.need_int(a[0]))) }
538
+ defm(INT_T, "lcm", 1) { |_, r, a| Builtins.ck(r.lcm(Builtins.need_int(a[0]))) }
539
+ defm(INT_T, "digits", 0, 1) { |_, r, a| a.empty? ? r.digits : r.digits(Builtins.need_intish(a[0])) }
540
+ defm(INT_T, "bit_length", 0) { |_, r| r.bit_length }
541
+ defm(INT_T, "floor", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r : r.floor(Builtins.need_intish(a[0]))) }
542
+ defm(INT_T, "ceil", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r : r.ceil(Builtins.need_intish(a[0]))) }
543
+ defm(INT_T, "truncate", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r : r.truncate(Builtins.need_intish(a[0]))) }
544
+ defm(INT_T, "round", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r : r.round(Builtins.need_intish(a[0]))) }
545
+
546
+ # ===================================================================================
547
+ # Float
548
+ # ===================================================================================
549
+
550
+ defm(FLOAT_T, "+", 1) { |_, r, a| r + Builtins.need_num(a[0], "Float") }
551
+ defm(FLOAT_T, "-", 1) { |_, r, a| r - Builtins.need_num(a[0], "Float") }
552
+ defm(FLOAT_T, "*", 1) { |_, r, a| r * Builtins.need_num(a[0], "Float") }
553
+ defm(FLOAT_T, "/", 1) { |_, r, a| r / Builtins.need_num(a[0], "Float") }
554
+ defm(FLOAT_T, %w[% modulo], 1) { |_, r, a| r % Builtins.need_num(a[0], "Float") }
555
+ defm(FLOAT_T, "**", 1) { |_, r, a| Builtins.fpow(r, Builtins.need_num(a[0], "Float")) }
556
+ defm(FLOAT_T, "-@", 0) { |_, r| -r }
557
+ defm(FLOAT_T, "+@", 0) { |_, r| r }
558
+ defm(FLOAT_T, "<=>", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r <=> a[0] : nil }
559
+ defm(FLOAT_T, "<", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r < a[0] : Builtins.cmp_fail(r, a[0]) }
560
+ defm(FLOAT_T, "<=", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r <= a[0] : Builtins.cmp_fail(r, a[0]) }
561
+ defm(FLOAT_T, ">", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r > a[0] : Builtins.cmp_fail(r, a[0]) }
562
+ defm(FLOAT_T, ">=", 1) { |_, r, a| a[0].is_a?(Integer) || a[0].is_a?(Float) ? r >= a[0] : Builtins.cmp_fail(r, a[0]) }
563
+ defm(FLOAT_T, "abs", 0) { |_, r| r.abs }
564
+ defm(FLOAT_T, "to_f", 0) { |_, r| r }
565
+ defm(FLOAT_T, %w[to_s inspect], 0) { |_, r| r.to_s }
566
+ defm(FLOAT_T, "nan?", 0) { |_, r| r.nan? }
567
+ defm(FLOAT_T, "finite?", 0) { |_, r| r.finite? }
568
+ defm(FLOAT_T, "infinite?", 0) { |_, r| r.infinite? }
569
+ defm(FLOAT_T, "zero?", 0) { |_, r| r.zero? }
570
+ defm(FLOAT_T, "positive?", 0) { |_, r| r.positive? }
571
+ defm(FLOAT_T, "negative?", 0) { |_, r| r.negative? }
572
+ defm(FLOAT_T, "between?", 2) { |_, r, a| r.between?(a[0], a[1]) }
573
+ defm(FLOAT_T, "clamp", 1, 2) { |_, r, a| a.length == 1 ? r.clamp(a[0]) : r.clamp(a[0], a[1]) }
574
+ defm(FLOAT_T, "divmod", 1) { |_, r, a| r.divmod(Builtins.need_num(a[0], "Float")).map { |x| Builtins.ck(x) } }
575
+ defm(FLOAT_T, "fdiv", 1) { |_, r, a| r.fdiv(Builtins.need_num(a[0], "Float")) }
576
+ defm(FLOAT_T, %w[to_i to_int], 0) { |_, r| Builtins.ck(r.to_i) }
577
+ defm(FLOAT_T, "truncate", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r.truncate : r.truncate(Builtins.need_intish(a[0]))) }
578
+ defm(FLOAT_T, "floor", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r.floor : r.floor(Builtins.need_intish(a[0]))) }
579
+ defm(FLOAT_T, "ceil", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r.ceil : r.ceil(Builtins.need_intish(a[0]))) }
580
+ defm(FLOAT_T, "round", 0, 1) { |_, r, a| Builtins.ck(a.empty? ? r.round : r.round(Builtins.need_intish(a[0]))) }
581
+
582
+ # ===================================================================================
583
+ # String (no method changes a string in place)
584
+ # ===================================================================================
585
+
586
+ def self.str_index_args(a)
587
+ if a.length == 2
588
+ [need_intish(a[0]), need_intish(a[1])]
589
+ else
590
+ x = a[0]
591
+ return [x] if x.is_a?(Range)
592
+
593
+ [need_intish(x)]
594
+ end
595
+ end
596
+
597
+ defm(STR_T, %w[[] slice], 1, 2) do |_, r, a|
598
+ idx = Builtins.str_index_args(a)
599
+ idx.length == 2 ? r[idx[0], idx[1]] : r[idx[0]]
600
+ end
601
+ defm(STR_T, "+", 1) { |_, r, a| r + Builtins.need_str(a[0]) }
602
+ defm(STR_T, "+@", 0) { |_, r| r.frozen? ? r.dup : r }
603
+ defm(STR_T, "*", 1) do |_, r, a|
604
+ n = Builtins.need_intish(a[0])
605
+ Builtins.cap(r.length * n)
606
+ r * n
607
+ end
608
+ defm(STR_T, "%", 1) { |it, r, a| Format.format(it, r, a[0].is_a?(Array) ? a[0] : [a[0]]) }
609
+ # Strings are values [ERBLite]: returns a new string (the interpreter
610
+ # assigns it back). An Integer appends that code point.
611
+ defm(STR_T, "<<", 1) do |_, r, a|
612
+ o = a[0]
613
+ case o
614
+ when String then r + o
615
+ when Integer
616
+ raise Builtins.err("RangeError", "#{o} out of char range") unless o.between?(0, 0x10FFFF)
617
+
618
+ r + o.chr(Encoding::UTF_8)
619
+ else raise Builtins.err("TypeError", "no implicit conversion of #{Builtins.tname(o)} into String")
620
+ end
621
+ end
622
+ defm(STR_T, "<=>", 1) { |_, r, a| a[0].is_a?(String) ? r <=> a[0] : nil }
623
+ defm(STR_T, "<", 1) { |_, r, a| a[0].is_a?(String) ? r < a[0] : Builtins.cmp_fail(r, a[0]) }
624
+ defm(STR_T, "<=", 1) { |_, r, a| a[0].is_a?(String) ? r <= a[0] : Builtins.cmp_fail(r, a[0]) }
625
+ defm(STR_T, ">", 1) { |_, r, a| a[0].is_a?(String) ? r > a[0] : Builtins.cmp_fail(r, a[0]) }
626
+ defm(STR_T, ">=", 1) { |_, r, a| a[0].is_a?(String) ? r >= a[0] : Builtins.cmp_fail(r, a[0]) }
627
+ defm(STR_T, %w[length size], 0) { |_, r| r.length }
628
+ defm(STR_T, "bytesize", 0) { |_, r| r.bytesize }
629
+ defm(STR_T, "empty?", 0) { |_, r| r.empty? }
630
+ defm(STR_T, "upcase", 0) { |_, r| r.upcase }
631
+ defm(STR_T, "downcase", 0) { |_, r| r.downcase }
632
+ defm(STR_T, "capitalize", 0) { |_, r| r.capitalize }
633
+ defm(STR_T, "swapcase", 0) { |_, r| r.swapcase }
634
+ defm(STR_T, "strip", 0) { |_, r| r.strip }
635
+ defm(STR_T, "lstrip", 0) { |_, r| r.lstrip }
636
+ defm(STR_T, "rstrip", 0) { |_, r| r.rstrip }
637
+ defm(STR_T, "chomp", 0, 1) { |_, r, a| a.empty? || a[0].nil? ? r.chomp : r.chomp(Builtins.need_str(a[0])) }
638
+ defm(STR_T, "chop", 0) { |_, r| r.chop }
639
+ defm(STR_T, "chars", 0) { |it, r, _, b| b ? Builtins.yield_each(it, r.chars, b, r) : r.chars }
640
+ defm(STR_T, "lines", 0, 1) do |it, r, a, b|
641
+ res = a.empty? ? r.lines : r.lines(Builtins.need_str(a[0]))
642
+ b ? Builtins.yield_each(it, res, b, r) : res
643
+ end
644
+ defm(STR_T, "bytes", 0) { |it, r, _, b| b ? Builtins.yield_each(it, r.bytes, b, r) : r.bytes }
645
+ defm(STR_T, "reverse", 0) { |_, r| r.reverse }
646
+ defm(STR_T, "include?", 1) { |_, r, a| r.include?(Builtins.need_str(a[0])) }
647
+ defm(STR_T, "start_with?", 0, -1) { |_, r, a| a.any? { |s| r.start_with?(Builtins.need_str(s)) } }
648
+ defm(STR_T, "end_with?", 0, -1) { |_, r, a| a.any? { |s| r.end_with?(Builtins.need_str(s)) } }
649
+ defm(STR_T, "index", 1, 2) do |_, r, a|
650
+ a.length == 1 ? r.index(Builtins.need_str(a[0])) : r.index(Builtins.need_str(a[0]), Builtins.need_intish(a[1]))
651
+ end
652
+ defm(STR_T, "rindex", 1, 2) do |_, r, a|
653
+ a.length == 1 ? r.rindex(Builtins.need_str(a[0])) : r.rindex(Builtins.need_str(a[0]), Builtins.need_intish(a[1]))
654
+ end
655
+ # [ERBLite] counts non-overlapping occurrences of the substring.
656
+ defm(STR_T, "count", 1) do |_, r, a|
657
+ s = Builtins.need_str(a[0])
658
+ if s.empty?
659
+ 0
660
+ else
661
+ n = 0
662
+ i = 0
663
+ while (j = r.index(s, i))
664
+ n += 1
665
+ i = j + s.length
666
+ end
667
+ n
668
+ end
669
+ end
670
+ defm(STR_T, "sub", 1, 2) { |it, r, a, b| Builtins.str_sub(it, r, a, b, false) }
671
+ defm(STR_T, "gsub", 1, 2) { |it, r, a, b| Builtins.str_sub(it, r, a, b, true) }
672
+ defm(STR_T, "tr", 2) { |_, r, a| r.tr(Builtins.need_str(a[0]), Builtins.need_str(a[1])) }
673
+ defm(STR_T, "delete", 1, -1) { |_, r, a| r.delete(*a.map { |s| Builtins.need_str(s) }) }
674
+ defm(STR_T, "squeeze", 0, -1) { |_, r, a| r.squeeze(*a.map { |s| Builtins.need_str(s) }) }
675
+ %w[center ljust rjust].each do |nm|
676
+ defm(STR_T, nm, 1, 2) do |_, r, a|
677
+ w = Builtins.cap(Builtins.need_intish(a[0]))
678
+ pad = a.length > 1 ? Builtins.need_str(a[1]) : " "
679
+ case nm
680
+ when "center" then r.center(w, pad)
681
+ when "ljust" then r.ljust(w, pad)
682
+ else r.rjust(w, pad)
683
+ end
684
+ end
685
+ end
686
+ defm(STR_T, "to_i", 0, 1) { |_, r, a| Builtins.ck(r.to_i(a.empty? ? 10 : Builtins.need_int(a[0]))) }
687
+ defm(STR_T, "to_f", 0) { |_, r| r.to_f }
688
+ defm(STR_T, %w[to_s to_str], 0) { |_, r| r }
689
+ defm(STR_T, %w[to_sym intern], 0) { |_, r| r.to_sym }
690
+ defm(STR_T, "ord", 0) { |_, r| r.ord }
691
+ defm(STR_T, "hex", 0) { |_, r| Builtins.ck(r.hex) }
692
+ defm(STR_T, "oct", 0) { |_, r| Builtins.ck(r.oct) }
693
+ defm(STR_T, "each_char", 0) { |it, r, _, b| r.each_char(&it.bp(b)) }
694
+ defm(STR_T, "each_line", 0, 1) do |it, r, a, b|
695
+ a.empty? ? r.each_line(&it.bp(b)) : r.each_line(Builtins.need_str(a[0]), &it.bp(b))
696
+ end
697
+ defm(STR_T, "split", 0, 2) do |it, r, a, b|
698
+ sep = a.empty? || a[0].nil? ? nil : Builtins.need_str(a[0])
699
+ limit = a.length > 1 ? Builtins.need_intish(a[1]) : 0
700
+ res = r.split(sep, limit)
701
+ b ? Builtins.yield_each(it, res, b, r) : res
702
+ end
703
+
704
+ def self.yield_each(_it, list, blk, result)
705
+ list.each { |x| blk.call(x) }
706
+ result
707
+ end
708
+ defm(STR_T, "partition", 1) { |_, r, a| r.partition(Builtins.need_str(a[0])) }
709
+ defm(STR_T, "rpartition", 1) { |_, r, a| r.rpartition(Builtins.need_str(a[0])) }
710
+ defm(STR_T, "casecmp", 1) { |_, r, a| a[0].is_a?(String) ? r.casecmp(a[0]) : nil }
711
+ defm(STR_T, "casecmp?", 1) { |_, r, a| a[0].is_a?(String) ? r.casecmp?(a[0]) : nil }
712
+ defm(STR_T, "between?", 2) { |_, r, a| r.between?(a[0], a[1]) }
713
+ defm(STR_T, "ascii_only?", 0) { |_, r| r.ascii_only? }
714
+
715
+ # sub/gsub with a literal String pattern [ERBLite: no Regexp].
716
+ def self.str_sub(it, r, a, b, global)
717
+ pat = a[0]
718
+ unless pat.is_a?(String)
719
+ raise err("TypeError", "wrong argument type #{class_name(pat)} (expected String; regular expressions are not supported)")
720
+ end
721
+
722
+ rep = a[1]
723
+ if a.length == 1 && b.nil?
724
+ raise err("ArgumentError", "wrong number of arguments (given 1, expected 2)")
725
+ end
726
+ if a.length == 2 && !rep.is_a?(String) && !rep.is_a?(Hash)
727
+ raise err("TypeError", "no implicit conversion of #{tname(rep)} into String")
728
+ end
729
+
730
+ out = +""
731
+ pos = 0
732
+ len = r.length
733
+ while (idx = r.index(pat, pos))
734
+ out << r[pos...idx]
735
+ repl = if a.length == 2
736
+ rep.is_a?(Hash) ? to_s(rep[pat]) : expand_replacement(rep, pat, r[0...idx], r[(idx + pat.length)..])
737
+ else
738
+ to_s(b.call(pat.dup))
739
+ end
740
+ out << repl
741
+ if pat.empty?
742
+ out << r[idx] if idx < len
743
+ pos = idx + 1
744
+ else
745
+ pos = idx + pat.length
746
+ end
747
+ break if !global || pos > len
748
+ end
749
+ out << r[pos..] if pos <= len
750
+ out
751
+ end
752
+
753
+ def self.expand_replacement(rep, m, pre, post)
754
+ return rep unless rep.include?("\\")
755
+
756
+ out = +""
757
+ i = 0
758
+ n = rep.length
759
+ while i < n
760
+ c = rep[i]
761
+ if c == "\\" && i + 1 < n
762
+ d = rep[i + 1]
763
+ case d
764
+ when "0", "&" then out << m
765
+ when "`" then out << pre
766
+ when "'" then out << post
767
+ when "\\" then out << "\\"
768
+ when "1", "2", "3", "4", "5", "6", "7", "8", "9" then nil
769
+ else out << c << d
770
+ end
771
+ i += 2
772
+ else
773
+ out << c
774
+ i += 1
775
+ end
776
+ end
777
+ out
778
+ end
779
+
780
+ # ===================================================================================
781
+ # Symbol
782
+ # ===================================================================================
783
+
784
+ defm(SYM_T, %w[to_s id2name], 0) { |_, r| r.to_s }
785
+ defm(SYM_T, "name", 0) { |_, r| r.name }
786
+ defm(SYM_T, "to_sym", 0) { |_, r| r }
787
+ defm(SYM_T, %w[length size], 0) { |_, r| r.length }
788
+ defm(SYM_T, "upcase", 0) { |_, r| r.upcase }
789
+ defm(SYM_T, "downcase", 0) { |_, r| r.downcase }
790
+ defm(SYM_T, "capitalize", 0) { |_, r| r.capitalize }
791
+ defm(SYM_T, "<=>", 1) { |_, r, a| a[0].is_a?(Symbol) ? r <=> a[0] : nil }
792
+ defm(SYM_T, "<", 1) { |_, r, a| a[0].is_a?(Symbol) ? r < a[0] : Builtins.cmp_fail(r, a[0]) }
793
+ defm(SYM_T, "<=", 1) { |_, r, a| a[0].is_a?(Symbol) ? r <= a[0] : Builtins.cmp_fail(r, a[0]) }
794
+ defm(SYM_T, ">", 1) { |_, r, a| a[0].is_a?(Symbol) ? r > a[0] : Builtins.cmp_fail(r, a[0]) }
795
+ defm(SYM_T, ">=", 1) { |_, r, a| a[0].is_a?(Symbol) ? r >= a[0] : Builtins.cmp_fail(r, a[0]) }
796
+ defm(SYM_T, "between?", 2) { |_, r, a| r.between?(a[0], a[1]) }
797
+ defm(SYM_T, "start_with?", 0, -1) { |_, r, a| a.any? { |s| r.start_with?(Builtins.need_str(s)) } }
798
+ defm(SYM_T, "end_with?", 0, -1) { |_, r, a| a.any? { |s| r.end_with?(Builtins.need_str(s)) } }
799
+ defm(SYM_T, "empty?", 0) { |_, r| r.empty? }
800
+
801
+ # ===================================================================================
802
+ # Array
803
+ # ===================================================================================
804
+
805
+ def self.ary_index_args(a)
806
+ if a.length == 2
807
+ [need_intish(a[0]), need_intish(a[1])]
808
+ else
809
+ x = a[0]
810
+ return [x] if x.is_a?(Range)
811
+
812
+ [need_intish(x)]
813
+ end
814
+ end
815
+
816
+ defm(ARR_T, %w[[] slice], 1, 2) do |_, r, a|
817
+ idx = Builtins.ary_index_args(a)
818
+ idx.length == 2 ? r[idx[0], idx[1]] : r[idx[0]]
819
+ end
820
+ defm(ARR_T, "[]=", 2, 3) do |_, r, a|
821
+ if a.length == 3
822
+ r[Builtins.cap(Builtins.need_intish(a[0])), Builtins.need_intish(a[1])] = a[2]
823
+ elsif a[0].is_a?(Range)
824
+ Builtins.cap(a[0].begin) if a[0].begin.is_a?(Integer)
825
+ r[a[0]] = a[1]
826
+ else
827
+ r[Builtins.cap(Builtins.need_intish(a[0]))] = a[1]
828
+ end
829
+ a.last
830
+ end
831
+ defm(ARR_T, "+", 1) { |_, r, a| r + Builtins.need_ary(a[0]) }
832
+ defm(ARR_T, "-", 1) { |_, r, a| r - Builtins.need_ary(a[0]) }
833
+ defm(ARR_T, "&", 1) { |_, r, a| r & Builtins.need_ary(a[0]) }
834
+ defm(ARR_T, "|", 1) { |_, r, a| r | Builtins.need_ary(a[0]) }
835
+ defm(ARR_T, "*", 1) do |_, r, a|
836
+ o = a[0]
837
+ if o.is_a?(String)
838
+ r.join(o)
839
+ else
840
+ n = Builtins.need_intish(o)
841
+ Builtins.cap(r.length * n)
842
+ r * n
843
+ end
844
+ end
845
+ defm(ARR_T, "<<", 1) { |_, r, a| r << a[0] }
846
+ defm(ARR_T, "<=>", 1) { |_, r, a| a[0].is_a?(Array) ? r <=> a[0] : nil }
847
+ defm(ARR_T, %w[length size], 0) { |_, r| r.length }
848
+ defm(ARR_T, "empty?", 0) { |_, r| r.empty? }
849
+ defm(ARR_T, "first", 0, 1) { |_, r, a| a.empty? ? r.first : r.first(Builtins.need_intish(a[0])) }
850
+ defm(ARR_T, "last", 0, 1) { |_, r, a| a.empty? ? r.last : r.last(Builtins.need_intish(a[0])) }
851
+ defm(ARR_T, %w[push append], 0, -1) { |_, r, a| r.push(*a) }
852
+ defm(ARR_T, %w[unshift prepend], 0, -1) { |_, r, a| r.unshift(*a) }
853
+ defm(ARR_T, "insert", 1, -1) { |_, r, a| r.insert(Builtins.cap(Builtins.need_intish(a[0])), *a[1..]) }
854
+ defm(ARR_T, "pop", 0, 1) { |_, r, a| a.empty? ? r.pop : r.pop(Builtins.need_intish(a[0])) }
855
+ defm(ARR_T, "shift", 0, 1) { |_, r, a| a.empty? ? r.shift : r.shift(Builtins.need_intish(a[0])) }
856
+ defm(ARR_T, "concat", 0, -1) { |_, r, a| r.concat(*a.map { |x| Builtins.need_ary(x) }) }
857
+ defm(ARR_T, "delete", 1) { |it, r, a, b| r.delete(a[0], &it.bp(b)) }
858
+ defm(ARR_T, "delete_at", 1) { |_, r, a| r.delete_at(Builtins.need_intish(a[0])) }
859
+ defm(ARR_T, "delete_if", 0) { |it, r, _, b| r.delete_if(&it.bp(b)) }
860
+ defm(ARR_T, "clear", 0) { |_, r| r.clear }
861
+ defm(ARR_T, "fill", 0, 3) do |it, r, a, b|
862
+ if b
863
+ r.fill(*a.map { |x| x.is_a?(Range) || x.nil? ? x : Builtins.need_intish(x) }, &it.bp(b))
864
+ else
865
+ raise Builtins.err("ArgumentError", "wrong number of arguments (given 0, expected 1..3)") if a.empty?
866
+
867
+ rest = a[1..].map { |x| x.is_a?(Range) || x.nil? ? x : Builtins.need_intish(x) }
868
+ r.fill(a[0], *rest)
869
+ end
870
+ end
871
+ defm(ARR_T, %w[index find_index], 0, 1) { |it, r, a, b| a.empty? ? r.index(&it.bp(b)) : r.index(a[0]) }
872
+ defm(ARR_T, "rindex", 0, 1) { |it, r, a, b| a.empty? ? r.rindex(&it.bp(b)) : r.rindex(a[0]) }
873
+ defm(ARR_T, "join", 0, 1) do |_, r, a|
874
+ sep = a.empty? || a[0].nil? ? "" : Builtins.need_str(a[0])
875
+ r.join(sep)
876
+ end
877
+ defm(ARR_T, "reverse", 0) { |_, r| r.reverse }
878
+ defm(ARR_T, "rotate", 0, 1) { |_, r, a| r.rotate(a.empty? ? 1 : Builtins.need_intish(a[0])) }
879
+ defm(ARR_T, "flatten", 0, 1) { |_, r, a| a.empty? || a[0].nil? ? r.flatten : r.flatten(Builtins.need_intish(a[0])) }
880
+ defm(ARR_T, "compact", 0) { |_, r| r.compact }
881
+ defm(ARR_T, "transpose", 0) { |_, r| r.transpose }
882
+ defm(ARR_T, "values_at", 0, -1) do |_, r, a|
883
+ r.values_at(*a.map { |x| x.is_a?(Range) ? x : Builtins.need_intish(x) })
884
+ end
885
+ defm(ARR_T, "dig", 1, -1) { |_, r, a| r.dig(*a) }
886
+ defm(ARR_T, "assoc", 1) { |_, r, a| r.assoc(a[0]) }
887
+ # [ERBLite] product/combination/permutation return an Array without a block.
888
+ defm(ARR_T, "product", 0, -1) do |it, r, a, b|
889
+ others = a.map { |x| Builtins.need_ary(x) }
890
+ if b
891
+ r.product(*others) { |c| b.call(c) }
892
+ r
893
+ else
894
+ res = []
895
+ r.product(*others) do |c|
896
+ it.step!
897
+ res << c
898
+ end
899
+ res
900
+ end
901
+ end
902
+ defm(ARR_T, "combination", 1) do |it, r, a, b|
903
+ n = Builtins.need_intish(a[0])
904
+ if b
905
+ r.combination(n) { |c| b.call(c) }
906
+ r
907
+ else
908
+ res = []
909
+ r.combination(n) do |c|
910
+ it.step!
911
+ res << c
912
+ end
913
+ res
914
+ end
915
+ end
916
+ defm(ARR_T, "permutation", 0, 1) do |it, r, a, b|
917
+ n = a.empty? || a[0].nil? ? r.length : Builtins.need_intish(a[0])
918
+ if b
919
+ r.permutation(n) { |c| b.call(c) }
920
+ r
921
+ else
922
+ res = []
923
+ r.permutation(n) do |c|
924
+ it.step!
925
+ res << c
926
+ end
927
+ res
928
+ end
929
+ end
930
+ defm(ARR_T, "to_a", 0) { |_, r| r }
931
+ defm(ARR_T, "each", 0) { |it, r, _, b| r.each(&it.bp(b)) }
932
+ defm(ARR_T, "each_index", 0) { |it, r, _, b| r.each_index(&it.bp(b)) }
933
+ defm(ARR_T, "reverse_each", 0) { |it, r, _, b| r.reverse_each(&it.bp(b)) }
934
+
935
+ # ===================================================================================
936
+ # Hash
937
+ # ===================================================================================
938
+
939
+ defm(HASH_T, "[]", 1) { |_, r, a| r[a[0]] }
940
+ defm(HASH_T, %w[[]= store], 2) { |_, r, a| r[a[0]] = a[1] }
941
+ defm(HASH_T, "fetch", 1, 2) do |it, r, a, b|
942
+ if a.length == 2 then r.fetch(a[0], a[1])
943
+ elsif b then r.fetch(a[0], &it.bp(b))
944
+ else r.fetch(a[0])
945
+ end
946
+ end
947
+ defm(HASH_T, "dig", 1, -1) { |_, r, a| r.dig(*a) }
948
+ defm(HASH_T, %w[key? has_key? include? member?], 1) { |_, r, a| r.key?(a[0]) }
949
+ defm(HASH_T, %w[value? has_value?], 1) { |_, r, a| r.value?(a[0]) }
950
+ defm(HASH_T, "key", 1) { |_, r, a| r.key(a[0]) }
951
+ defm(HASH_T, "keys", 0) { |_, r| r.keys }
952
+ defm(HASH_T, "values", 0) { |_, r| r.values }
953
+ defm(HASH_T, "values_at", 0, -1) { |_, r, a| r.values_at(*a) }
954
+ defm(HASH_T, "fetch_values", 0, -1) { |it, r, a, b| r.fetch_values(*a, &it.bp(b)) }
955
+ defm(HASH_T, %w[length size], 0) { |_, r| r.length }
956
+ defm(HASH_T, "empty?", 0) { |_, r| r.empty? }
957
+ defm(HASH_T, "delete", 1) { |it, r, a, b| r.delete(a[0], &it.bp(b)) }
958
+ defm(HASH_T, "delete_if", 0) { |it, r, _, b| r.delete_if(&it.bp(b)) }
959
+ defm(HASH_T, "clear", 0) { |_, r| r.clear }
960
+ defm(HASH_T, "merge", 0, -1) { |it, r, a, b| r.merge(*a.map { |h| Builtins.need_hash(h) }, &it.bp(b)) }
961
+ defm(HASH_T, %w[merge! update], 0, -1) { |it, r, a, b| r.merge!(*a.map { |h| Builtins.need_hash(h) }, &it.bp(b)) }
962
+ defm(HASH_T, "to_a", 0) { |_, r| r.to_a }
963
+ defm(HASH_T, "to_h", 0) { |it, r, _, b| b ? r.to_h(&it.bp(b)) : r }
964
+ defm(HASH_T, "invert", 0) { |_, r| r.invert }
965
+ defm(HASH_T, "transform_values", 0) { |it, r, _, b| r.transform_values(&it.bp(b)) }
966
+ defm(HASH_T, "transform_keys", 0, 1) do |it, r, a, b|
967
+ a.empty? ? r.transform_keys(&it.bp(b)) : r.transform_keys(Builtins.need_hash(a[0]), &it.bp(b))
968
+ end
969
+ defm(HASH_T, %w[select filter], 0) { |it, r, _, b| r.select(&it.bp(b)) }
970
+ defm(HASH_T, "reject", 0) { |it, r, _, b| r.reject(&it.bp(b)) }
971
+ defm(HASH_T, %w[each each_pair], 0) { |it, r, _, b| r.each(&it.bp(b)) }
972
+ defm(HASH_T, "each_key", 0) { |it, r, _, b| r.each_key(&it.bp(b)) }
973
+ defm(HASH_T, "each_value", 0) { |it, r, _, b| r.each_value(&it.bp(b)) }
974
+ defm(HASH_T, "slice", 0, -1) { |_, r, a| r.slice(*a) }
975
+ defm(HASH_T, "except", 0, -1) { |_, r, a| r.except(*a) }
976
+ defm(HASH_T, "compact", 0) { |_, r| r.compact }
977
+ defm(HASH_T, "default", 0) { |_, r| r.default }
978
+ defm(HASH_T, "default=", 1) { |_, r, a| r.default = a[0] }
979
+
980
+ # ===================================================================================
981
+ # Range
982
+ # ===================================================================================
983
+
984
+ defm(RANGE_T, "first", 0, 1) { |_, r, a| a.empty? ? r.first : r.first(Builtins.need_intish(a[0])) }
985
+ defm(RANGE_T, "begin", 0) { |_, r| r.begin }
986
+ defm(RANGE_T, "last", 0, 1) { |_, r, a| a.empty? ? r.last : r.last(Builtins.need_intish(a[0])) }
987
+ defm(RANGE_T, "end", 0) { |_, r| r.end }
988
+ defm(RANGE_T, "min", 0, 1) { |it, r, a, b| a.empty? ? r.min(&it.bp(b)) : r.min(Builtins.need_intish(a[0]), &it.bp(b)) }
989
+ defm(RANGE_T, "max", 0, 1) { |it, r, a, b| a.empty? ? r.max(&it.bp(b)) : r.max(Builtins.need_intish(a[0]), &it.bp(b)) }
990
+ defm(RANGE_T, "size", 0) { |_, r| r.size }
991
+ defm(RANGE_T, "sum", 0, 1) { |it, r, a, b| Builtins.ck(r.sum(*a, &it.bp(b))) }
992
+ defm(RANGE_T, %w[to_a entries], 0) do |_, r|
993
+ Builtins.cap(r.size) if r.begin.is_a?(Integer) && r.end.is_a?(Integer)
994
+ r.to_a
995
+ end
996
+ defm(RANGE_T, "each", 0) { |it, r, _, b| r.each(&it.bp(b)) }
997
+ defm(RANGE_T, "reverse_each", 0) { |it, r, _, b| r.reverse_each(&it.bp(b)) }
998
+ defm(RANGE_T, "step", 1) do |it, r, a, b|
999
+ r.step(Builtins.need_num(a[0], "Integer"), &it.bp(b))
1000
+ r
1001
+ end
1002
+ defm(RANGE_T, %w[include? member?], 1) { |_, r, a| r.include?(a[0]) }
1003
+ defm(RANGE_T, %w[cover? ===], 1) { |_, r, a| r.cover?(a[0]) }
1004
+ defm(RANGE_T, "exclude_end?", 0) { |_, r| r.exclude_end? }
1005
+
1006
+ # ===================================================================================
1007
+ # Enumerator
1008
+ # ===================================================================================
1009
+
1010
+ defm(ENUMR_T, "each", 0) { |it, r, _, b| b ? it.enum_each(r, b) : r }
1011
+ defm(ENUMR_T, "each_with_index", 0) { |it, r, _, b| Builtins.with_index(it, r, 0, b) }
1012
+ defm(ENUMR_T, "with_index", 0, 1) do |it, r, a, b|
1013
+ Builtins.with_index(it, r, a.empty? || a[0].nil? ? 0 : Builtins.need_intish(a[0]), b)
1014
+ end
1015
+
1016
+ def self.with_index(it, r, start, b)
1017
+ i = start
1018
+ it.enum_each(r, NativeBlock.new { |*v|
1019
+ x = v.length == 1 ? v[0] : v
1020
+ idx = i
1021
+ i += 1
1022
+ b.call(x, idx)
1023
+ })
1024
+ end
1025
+
1026
+ defm(ENUMR_T, %w[with_object each_with_object], 1) do |it, r, a, b|
1027
+ memo = a[0]
1028
+ it.enum_each(r, NativeBlock.new { |*v| b.call(v.length == 1 ? v[0] : v, memo) })
1029
+ memo
1030
+ end
1031
+ defm(ENUMR_T, "size", 0) { |it, r| it.enum_to_a(r).length }
1032
+ defm(ENUMR_T, %w[to_a entries], 0) { |it, r| it.enum_to_a(r) }
1033
+
1034
+ # ===================================================================================
1035
+ # Enumerable (Array, Hash, Range, Enumerator)
1036
+ # ===================================================================================
1037
+
1038
+ def self.pattern_or_block(it, a, b)
1039
+ return it.bp(b) if a.empty?
1040
+
1041
+ pat = a[0]
1042
+ proc { |*v| case_eq(pat, v.length == 1 ? v[0] : v) }
1043
+ end
1044
+
1045
+ defm(ENUM_T, "each_with_index", 0) { |it, r, _, b| it.coll(r).each_with_index(&it.bp(b)) }
1046
+ defm(ENUM_T, "each_with_object", 1) { |it, r, a, b| it.coll(r).each_with_object(a[0], &it.bp(b)) }
1047
+ defm(ENUM_T, "each_slice", 1) { |it, r, a, b| it.coll(r).each_slice(Builtins.need_intish(a[0]), &it.bp(b)) }
1048
+ defm(ENUM_T, "each_cons", 1) { |it, r, a, b| it.coll(r).each_cons(Builtins.need_intish(a[0]), &it.bp(b)) }
1049
+ defm(ENUM_T, %w[map collect], 0) { |it, r, _, b| it.coll(r).map(&it.bp(b)) }
1050
+ defm(ENUM_T, %w[flat_map collect_concat], 0) { |it, r, _, b| it.coll(r).flat_map(&it.bp(b)) }
1051
+ defm(ENUM_T, "filter_map", 0) { |it, r, _, b| it.coll(r).filter_map(&it.bp(b)) }
1052
+ defm(ENUM_T, %w[select filter], 0) { |it, r, _, b| it.coll(r).select(&it.bp(b)) }
1053
+ defm(ENUM_T, "find_all", 0) { |it, r, _, b| it.coll(r).find_all(&it.bp(b)) }
1054
+ defm(ENUM_T, "reject", 0) { |it, r, _, b| it.coll(r).reject(&it.bp(b)) }
1055
+ defm(ENUM_T, %w[find detect], 0) { |it, r, _, b| it.coll(r).find(&it.bp(b)) }
1056
+ defm(ENUM_T, "find_index", 0, 1) do |it, r, a, b|
1057
+ a.empty? ? it.coll(r).find_index(&it.bp(b)) : it.coll(r).find_index(a[0])
1058
+ end
1059
+ defm(ENUM_T, "take_while", 0) { |it, r, _, b| it.coll(r).take_while(&it.bp(b)) }
1060
+ defm(ENUM_T, "drop_while", 0) { |it, r, _, b| it.coll(r).drop_while(&it.bp(b)) }
1061
+ defm(ENUM_T, "any?", 0, 1) { |it, r, a, b| it.coll(r).any?(&Builtins.pattern_or_block(it, a, b)) }
1062
+ defm(ENUM_T, "all?", 0, 1) { |it, r, a, b| it.coll(r).all?(&Builtins.pattern_or_block(it, a, b)) }
1063
+ defm(ENUM_T, "none?", 0, 1) { |it, r, a, b| it.coll(r).none?(&Builtins.pattern_or_block(it, a, b)) }
1064
+ defm(ENUM_T, "one?", 0, 1) { |it, r, a, b| it.coll(r).one?(&Builtins.pattern_or_block(it, a, b)) }
1065
+ defm(ENUM_T, "count", 0, 1) { |it, r, a, b| a.empty? ? it.coll(r).count(&it.bp(b)) : it.coll(r).count(a[0]) }
1066
+ defm(ENUM_T, %w[include? member?], 1) { |it, r, a| it.coll(r).include?(a[0]) }
1067
+ defm(ENUM_T, "min", 0, 1) do |it, r, a, b|
1068
+ c = it.coll(r)
1069
+ a.empty? ? c.min(&it.bp(b)) : c.min(Builtins.need_intish(a[0]), &it.bp(b))
1070
+ end
1071
+ defm(ENUM_T, "max", 0, 1) do |it, r, a, b|
1072
+ c = it.coll(r)
1073
+ a.empty? ? c.max(&it.bp(b)) : c.max(Builtins.need_intish(a[0]), &it.bp(b))
1074
+ end
1075
+ defm(ENUM_T, "min_by", 0, 1) do |it, r, a, b|
1076
+ a.empty? ? it.coll(r).min_by(&it.bp(b)) : it.coll(r).min_by(Builtins.need_intish(a[0]), &it.bp(b))
1077
+ end
1078
+ defm(ENUM_T, "max_by", 0, 1) do |it, r, a, b|
1079
+ a.empty? ? it.coll(r).max_by(&it.bp(b)) : it.coll(r).max_by(Builtins.need_intish(a[0]), &it.bp(b))
1080
+ end
1081
+ defm(ENUM_T, "minmax", 0) { |it, r, _, b| it.coll(r).minmax(&it.bp(b)) }
1082
+ defm(ENUM_T, "sum", 0, 1) { |it, r, a, b| Builtins.ck(it.coll(r).sum(*a, &it.bp(b))) }
1083
+ defm(ENUM_T, %w[inject reduce], 0, 2) { |it, r, a, b| Builtins.inject(it, r, a, b) }
1084
+ defm(ENUM_T, "group_by", 0) { |it, r, _, b| it.coll(r).group_by(&it.bp(b)) }
1085
+ defm(ENUM_T, "partition", 0) { |it, r, _, b| it.coll(r).partition(&it.bp(b)) }
1086
+ defm(ENUM_T, "chunk_while", 0) do |it, r, _, b|
1087
+ it.coll(r).chunk_while(&it.bp(Builtins.need_blk(b, "chunk_while"))).to_a
1088
+ end
1089
+ defm(ENUM_T, "slice_when", 0) do |it, r, _, b|
1090
+ it.coll(r).slice_when(&it.bp(Builtins.need_blk(b, "slice_when"))).to_a
1091
+ end
1092
+ defm(ENUM_T, "tally", 0) { |it, r| it.coll(r).tally }
1093
+ defm(ENUM_T, "zip", 0, -1) do |it, r, a, b|
1094
+ others = a.map do |x|
1095
+ case x
1096
+ when Array then x
1097
+ when Hash, Range then x.to_a
1098
+ when Enum then it.enum_to_a(x)
1099
+ else raise Builtins.err("TypeError", "wrong argument type #{Builtins.class_name(x)} (must respond to :each)")
1100
+ end
1101
+ end
1102
+ it.coll(r).zip(*others, &it.bp(b))
1103
+ end
1104
+ defm(ENUM_T, "first", 0, 1) { |it, r, a| a.empty? ? it.coll(r).first : it.coll(r).first(Builtins.need_intish(a[0])) }
1105
+ defm(ENUM_T, "take", 1) { |it, r, a| it.coll(r).take(Builtins.need_intish(a[0])) }
1106
+ defm(ENUM_T, "drop", 1) { |it, r, a| it.coll(r).drop(Builtins.need_intish(a[0])) }
1107
+ defm(ENUM_T, "sort", 0) { |it, r, _, b| it.coll(r).sort(&it.bp(b)) }
1108
+ defm(ENUM_T, "sort_by", 0) { |it, r, _, b| it.coll(r).sort_by(&it.bp(b)) }
1109
+ defm(ENUM_T, "uniq", 0) { |it, r, _, b| it.coll(r).uniq(&it.bp(b)) }
1110
+ defm(ENUM_T, %w[to_a entries], 0) { |it, r| it.coll(r).to_a }
1111
+ defm(ENUM_T, "to_h", 0) { |it, r, _, b| it.coll(r).to_h(&it.bp(b)) }
1112
+ defm(ENUM_T, "reverse_each", 0) do |it, r, _, b|
1113
+ it.coll(r).reverse_each(&it.bp(b))
1114
+ r
1115
+ end
1116
+
1117
+ UNSET = Object.new.freeze
1118
+
1119
+ # inject/reduce. A Symbol operator is dispatched through the allowlist.
1120
+ def self.inject(it, r, a, b)
1121
+ acc = UNSET
1122
+ op = nil
1123
+ op_given = false
1124
+ case a.length
1125
+ when 0
1126
+ need_blk(b, "inject")
1127
+ when 1
1128
+ if b
1129
+ acc = a[0]
1130
+ else
1131
+ op = a[0]
1132
+ op_given = true
1133
+ end
1134
+ else
1135
+ acc = a[0]
1136
+ op = a[1]
1137
+ op_given = true
1138
+ end
1139
+ if op_given
1140
+ unless op.is_a?(Symbol) || op.is_a?(String)
1141
+ raise err("TypeError", "#{op.inspect} is not a symbol nor a string")
1142
+ end
1143
+
1144
+ op = op.to_s
1145
+ end
1146
+ it.coll(r).each do |*v|
1147
+ x = v.length == 1 ? v[0] : v
1148
+ acc = if UNSET.equal?(acc) then x
1149
+ elsif op then it.call_method(acc, op, [x], nil)
1150
+ else b.call(acc, x)
1151
+ end
1152
+ end
1153
+ UNSET.equal?(acc) ? nil : acc
1154
+ end
1155
+
1156
+ # ===================================================================================
1157
+ # Class values (Integer, Array, Math, File, ...)
1158
+ # ===================================================================================
1159
+
1160
+ defm(CLASS_T, %w[name to_s inspect], 0) { |_, r| r.name.dup }
1161
+ defm(CLASS_T, "==", 1) { |_, r, a| r.equal?(a[0]) }
1162
+ defm(CLASS_T, "===", 1) { |_, r, a| Builtins.is_a(a[0], r) }
1163
+
1164
+ array_class = {}
1165
+ defm(array_class, "new", 0, 2) do |_, _, a, b|
1166
+ if a.length == 1 && a[0].is_a?(Array)
1167
+ a[0].dup
1168
+ else
1169
+ n = Builtins.cap(a.empty? ? 0 : Builtins.need_intish(a[0]))
1170
+ if b
1171
+ raise Builtins.err("ArgumentError", "negative array size") if n.negative?
1172
+
1173
+ Array.new(n) { |i| b.call(i) }
1174
+ else
1175
+ Array.new(n, a[1])
1176
+ end
1177
+ end
1178
+ end
1179
+ CLASS_METHODS["Array"] = array_class
1180
+
1181
+ hash_class = {}
1182
+ defm(hash_class, "new", 0, 1) do |_, _, a, b|
1183
+ raise Builtins.err("ArgumentError", "Hash.new with a block is not supported") if b
1184
+
1185
+ Hash.new(a[0])
1186
+ end
1187
+ CLASS_METHODS["Hash"] = hash_class
1188
+
1189
+ math = {}
1190
+ %w[sqrt cbrt sin cos tan asin acos atan exp log2 log10].each do |nm|
1191
+ defm(math, nm, 1) do |_, _, a|
1192
+ x = Builtins.math_arg(a[0])
1193
+ case nm
1194
+ when "sqrt" then Math.sqrt(x)
1195
+ when "cbrt" then Builtins.cbrt(x)
1196
+ when "sin" then Math.sin(x)
1197
+ when "cos" then Math.cos(x)
1198
+ when "tan" then Math.tan(x)
1199
+ when "asin" then Math.asin(x)
1200
+ when "acos" then Math.acos(x)
1201
+ when "atan" then Math.atan(x)
1202
+ when "exp" then Math.exp(x)
1203
+ when "log2" then Math.log2(x)
1204
+ else Math.log10(x)
1205
+ end
1206
+ end
1207
+ end
1208
+ defm(math, "log", 1, 2) do |_, _, a|
1209
+ a.length == 1 ? Math.log(Builtins.math_arg(a[0])) : Math.log(Builtins.math_arg(a[0]), Builtins.math_arg(a[1]))
1210
+ end
1211
+ defm(math, "atan2", 2) { |_, _, a| Math.atan2(Builtins.math_arg(a[0]), Builtins.math_arg(a[1])) }
1212
+ defm(math, "hypot", 2) { |_, _, a| Math.hypot(Builtins.math_arg(a[0]), Builtins.math_arg(a[1])) }
1213
+ CLASS_METHODS["Math"] = math
1214
+
1215
+ # Correctly rounded cube root [ERBLite]. Platform cbrt() results (and Ruby's
1216
+ # own glibc Newton correction) are off by one ulp for some inputs, differently
1217
+ # per platform. Start from Math.cbrt and step to the double whose rounding
1218
+ # interval contains the exact root, comparing cubes of the interval midpoints
1219
+ # exactly. A midpoint's cube is never exactly a double, so there are no ties.
1220
+ def self.cbrt(x)
1221
+ return x if x.zero? || !x.finite?
1222
+ return -cbrt(-x) if x.negative?
1223
+
1224
+ y = Math.cbrt(x)
1225
+ rx = x.to_r
1226
+ loop do
1227
+ up = y.next_float
1228
+ if rx > ((y.to_r + up.to_r) / 2)**3
1229
+ y = up
1230
+ next
1231
+ end
1232
+ down = y.prev_float
1233
+ if rx < ((down.to_r + y.to_r) / 2)**3
1234
+ y = down
1235
+ next
1236
+ end
1237
+ return y
1238
+ end
1239
+ end
1240
+
1241
+ def self.math_arg(x)
1242
+ return x if x.is_a?(Integer) || x.is_a?(Float)
1243
+
1244
+ raise err("TypeError", "can't convert #{tname(x)} into Float")
1245
+ end
1246
+
1247
+ json = {}
1248
+ defm(json, "parse", 1, 2) { |_, _, a| JSONSupport.parse(a[0], a[1]) }
1249
+ defm(json, "generate", 1) { |_, _, a| JSONSupport.generate(a[0]) }
1250
+ defm(json, "pretty_generate", 1) { |_, _, a| JSONSupport.pretty_generate(a[0]) }
1251
+ CLASS_METHODS["JSON"] = json
1252
+
1253
+ file = {}
1254
+ defm(file, "read", 1) { |it, _, a| Sandbox.read(it.state, a[0]) }
1255
+ defm(file, "exist?", 1) { |it, _, a| Sandbox.check(it.state, a[0], :exist) }
1256
+ defm(file, "file?", 1) { |it, _, a| Sandbox.check(it.state, a[0], :file) }
1257
+ defm(file, "directory?", 1) { |it, _, a| Sandbox.check(it.state, a[0], :directory) }
1258
+ defm(file, "basename", 1, 2) do |_, _, a|
1259
+ a.length == 1 ? File.basename(Builtins.need_str(a[0])) : File.basename(Builtins.need_str(a[0]), Builtins.need_str(a[1]))
1260
+ end
1261
+ defm(file, "dirname", 1) { |_, _, a| File.dirname(Builtins.need_str(a[0])) }
1262
+ defm(file, "extname", 1) { |_, _, a| File.extname(Builtins.need_str(a[0])) }
1263
+ defm(file, "join", 0, -1) { |_, _, a| File.join(*Builtins.join_parts(a)) }
1264
+ CLASS_METHODS["File"] = file
1265
+
1266
+ def self.join_parts(a)
1267
+ a.map do |x|
1268
+ x.is_a?(Array) ? join_parts(x) : need_str(x)
1269
+ end
1270
+ end
1271
+
1272
+ dir = {}
1273
+ defm(dir, "glob", 1) { |it, _, a| Sandbox.glob(it.state, a[0]) }
1274
+ CLASS_METHODS["Dir"] = dir
1275
+
1276
+ # ===================================================================================
1277
+ # Functions
1278
+ # ===================================================================================
1279
+
1280
+ defm(FUNCS, %w[format sprintf], 1, -1) { |it, _, a| Format.format(it, a[0], a[1..]) }
1281
+ defm(FUNCS, "Integer", 1, 2) do |_, _, a|
1282
+ x = a[0]
1283
+ if a.length == 2 && !a[1].nil?
1284
+ base = Builtins.need_intish(a[1])
1285
+ raise Builtins.err("ArgumentError", "base specified for non string value") unless x.is_a?(String)
1286
+
1287
+ Builtins.ck(Kernel.Integer(x, base))
1288
+ else
1289
+ case x
1290
+ when Integer then x
1291
+ when Float, String then Builtins.ck(Kernel.Integer(x))
1292
+ else raise Builtins.err("TypeError", "can't convert #{Builtins.tname(x)} into Integer")
1293
+ end
1294
+ end
1295
+ end
1296
+ defm(FUNCS, "Float", 1) do |_, _, a|
1297
+ x = a[0]
1298
+ case x
1299
+ when Float then x
1300
+ when Integer then x.to_f
1301
+ when String then Kernel.Float(x)
1302
+ else raise Builtins.err("TypeError", "can't convert #{Builtins.tname(x)} into Float")
1303
+ end
1304
+ end
1305
+ defm(FUNCS, "String", 1) { |_, _, a| Builtins.to_s(a[0]) }
1306
+ defm(FUNCS, "Array", 1) do |it, _, a|
1307
+ x = a[0]
1308
+ case x
1309
+ when nil then []
1310
+ when Array then x
1311
+ when Hash, Range then x.to_a
1312
+ when Enum then it.enum_to_a(x)
1313
+ else [x]
1314
+ end
1315
+ end
1316
+ defm(FUNCS, "raise", 0, 1) do |_, _, a|
1317
+ raise Builtins.err("RuntimeError", "unhandled exception") if a.empty?
1318
+ raise Builtins.err("TypeError", "exception class/object expected") unless a[0].is_a?(String)
1319
+
1320
+ raise Builtins.err("RuntimeError", a[0])
1321
+ end
1322
+ defm(FUNCS, "loop", 0) do |_, _, _, b|
1323
+ Builtins.need_blk(b, "loop")
1324
+ b.call while true # rubocop:disable Style/InfiniteLoop
1325
+ end
1326
+ defm(FUNCS, "render", 1, 2) { |it, _, a| it.render_partial(a[0], a.length > 1 ? a[1] : {}) }
1327
+
1328
+ [COMMON_T, NIL_T, BOOL_T, INT_T, FLOAT_T, STR_T, SYM_T, ARR_T, HASH_T, RANGE_T, ENUMR_T, ENUM_T,
1329
+ CLASS_T, FUNCS, array_class, hash_class, math, json, file, dir].each(&:freeze)
1330
+ CLASS_METHODS.freeze
1331
+ end
1332
+ end