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.
- checksums.yaml +7 -0
- data/LICENSE.md +21 -0
- data/README.md +19 -0
- data/bin/erblite +53 -0
- data/lib/erblite/builtins.rb +1332 -0
- data/lib/erblite/error.rb +52 -0
- data/lib/erblite/format.rb +212 -0
- data/lib/erblite/interpreter.rb +486 -0
- data/lib/erblite/json.rb +331 -0
- data/lib/erblite/lexer.rb +649 -0
- data/lib/erblite/nodes.rb +46 -0
- data/lib/erblite/parser.rb +953 -0
- data/lib/erblite/sandbox.rb +192 -0
- data/lib/erblite/values.rb +139 -0
- data/lib/erblite/version.rb +13 -0
- data/lib/erblite.rb +134 -0
- metadata +57 -0
|
@@ -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
|