rupkl 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/rupkl/node/amend_expression.rb +48 -0
  4. data/lib/rupkl/node/any.rb +110 -0
  5. data/lib/rupkl/node/base.rb +75 -0
  6. data/lib/rupkl/node/boolean.rb +30 -5
  7. data/lib/rupkl/node/collection.rb +176 -0
  8. data/lib/rupkl/node/context.rb +27 -0
  9. data/lib/rupkl/node/data_size.rb +254 -0
  10. data/lib/rupkl/node/declared_type.rb +32 -0
  11. data/lib/rupkl/node/duration.rb +266 -0
  12. data/lib/rupkl/node/dynamic.rb +33 -60
  13. data/lib/rupkl/node/identifier.rb +19 -5
  14. data/lib/rupkl/node/if_expression.rb +45 -0
  15. data/lib/rupkl/node/intseq.rb +84 -0
  16. data/lib/rupkl/node/listing.rb +68 -0
  17. data/lib/rupkl/node/map.rb +120 -0
  18. data/lib/rupkl/node/mapping.rb +54 -0
  19. data/lib/rupkl/node/member_finder.rb +49 -0
  20. data/lib/rupkl/node/member_reference.rb +46 -21
  21. data/lib/rupkl/node/method_call.rb +63 -0
  22. data/lib/rupkl/node/method_definition.rb +199 -0
  23. data/lib/rupkl/node/node_common.rb +76 -0
  24. data/lib/rupkl/node/null.rb +24 -0
  25. data/lib/rupkl/node/number.rb +228 -10
  26. data/lib/rupkl/node/object.rb +626 -74
  27. data/lib/rupkl/node/operation.rb +175 -115
  28. data/lib/rupkl/node/pair.rb +58 -0
  29. data/lib/rupkl/node/pkl_module.rb +16 -28
  30. data/lib/rupkl/node/reference_resolver.rb +79 -0
  31. data/lib/rupkl/node/regex.rb +196 -0
  32. data/lib/rupkl/node/string.rb +415 -23
  33. data/lib/rupkl/node/struct_common.rb +150 -53
  34. data/lib/rupkl/node/this.rb +17 -0
  35. data/lib/rupkl/node/type_common.rb +34 -0
  36. data/lib/rupkl/node/value_common.rb +18 -13
  37. data/lib/rupkl/parser/expression.rb +197 -43
  38. data/lib/rupkl/parser/identifier.rb +2 -2
  39. data/lib/rupkl/parser/literal.rb +18 -12
  40. data/lib/rupkl/parser/method.rb +41 -0
  41. data/lib/rupkl/parser/misc.rb +24 -0
  42. data/lib/rupkl/parser/object.rb +141 -26
  43. data/lib/rupkl/parser/pkl_class.rb +37 -10
  44. data/lib/rupkl/parser/pkl_module.rb +5 -3
  45. data/lib/rupkl/parser/type.rb +28 -0
  46. data/lib/rupkl/parser.rb +8 -0
  47. data/lib/rupkl/pkl_object.rb +11 -7
  48. data/lib/rupkl/version.rb +1 -1
  49. data/lib/rupkl.rb +35 -6
  50. metadata +45 -7
  51. data/lib/rupkl/node/pkl_class.rb +0 -30
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuPkl
4
+ module Node
5
+ class Regex < Any
6
+ include Operatable
7
+
8
+ uninstantiable_class
9
+
10
+ def initialize(parent, pattern, position)
11
+ super
12
+ @pattern = Regexp.new(pattern.value)
13
+ end
14
+
15
+ attr_reader :pattern
16
+
17
+ def evaluate(_context = nil)
18
+ self
19
+ end
20
+
21
+ def to_ruby(_context = nil)
22
+ pattern
23
+ end
24
+
25
+ def to_pkl_string(context = nil)
26
+ to_string(context)
27
+ end
28
+
29
+ def to_string(context = nil)
30
+ source_string = source.to_pkl_string(context)
31
+ "Regex(#{source_string})"
32
+ end
33
+
34
+ def ==(other)
35
+ other.is_a?(self.class) && pattern == other.pattern
36
+ end
37
+
38
+ define_builtin_property(:pattern) do
39
+ source
40
+ end
41
+
42
+ define_builtin_property(:groupCount) do
43
+ Int.new(nil, group_count, position)
44
+ end
45
+
46
+ define_builtin_method(:findMatchesIn, input: String) do |args, parent, position|
47
+ find_matches(args[:input].value, parent, position)
48
+ end
49
+
50
+ define_builtin_method(:matchEntire, input: String) do |args, parent, position|
51
+ match_entire(args[:input].value, parent, position)
52
+ end
53
+
54
+ private
55
+
56
+ def source
57
+ @children[0]
58
+ end
59
+
60
+ def group_count
61
+ Regexp::Parser
62
+ .parse(pattern)
63
+ .each_expression.count { |exp, _| exp.capturing? }
64
+ end
65
+
66
+ def find_matches(string, parent, position)
67
+ matches = []
68
+ offset = 0
69
+ while (match_data = match_pattern(string, offset))
70
+ matches << RegexMatch.create(match_data, nil, position)
71
+ offset = calc_next_offset(match_data, offset)
72
+ end
73
+ List.new(parent, matches, position)
74
+ end
75
+
76
+ def match_entire(string, parent, position)
77
+ match_pattern(string, 0).then do |m|
78
+ if m && m.end(0) == string.size
79
+ RegexMatch.create(m, parent, position)
80
+ else
81
+ Null.new(parent, position)
82
+ end
83
+ end
84
+ end
85
+
86
+ def match_pattern(string, offset)
87
+ return if offset > string.size
88
+
89
+ pattern.match(string, offset)
90
+ end
91
+
92
+ def calc_next_offset(match_data, offset)
93
+ if match_data[0].empty?
94
+ offset + 1
95
+ else
96
+ match_data.end(0)
97
+ end
98
+ end
99
+ end
100
+
101
+ class RegexMatch < Any
102
+ include Operatable
103
+
104
+ uninstantiable_class
105
+
106
+ class << self
107
+ def create(match_data, parent, position)
108
+ groups = Array.new(match_data.size) do |i|
109
+ if match_data[i]
110
+ create_regex_match(
111
+ match_data[i], match_data.begin(i),
112
+ match_data.end(i), nil, nil, position
113
+ )
114
+ else
115
+ Null.new(nil, position)
116
+ end
117
+ end
118
+ create_regex_match(
119
+ match_data[0], match_data.begin(0),
120
+ match_data.end(0), groups, parent, position
121
+ )
122
+ end
123
+
124
+ private
125
+
126
+ def create_regex_match(value, start_offset, end_offset, groups, parent, position)
127
+ v = String.new(nil, value, nil, position)
128
+ s = Int.new(nil, start_offset, position)
129
+ e = Int.new(nil, end_offset, position)
130
+ g = List.new(nil, groups, position)
131
+ new(parent, v, s, e, g, position)
132
+ end
133
+ end
134
+
135
+ def value
136
+ @children[0]
137
+ end
138
+
139
+ def start
140
+ @children[1]
141
+ end
142
+
143
+ alias_method :start_offset, :start
144
+
145
+ def end
146
+ @children[2]
147
+ end
148
+
149
+ alias_method :end_offset, :end
150
+
151
+ def groups
152
+ @children[3]
153
+ end
154
+
155
+ def evaluate(_context = nil)
156
+ self
157
+ end
158
+
159
+ def to_ruby(context = nil)
160
+ {
161
+ value: value, start: start_offset, end: end_offset, groups: groups
162
+ }.transform_values { |v| v.to_ruby(context) }
163
+ end
164
+
165
+ def to_pkl_string(context = nil)
166
+ to_string(context)
167
+ end
168
+
169
+ def to_string(context)
170
+ value.to_string(context)
171
+ end
172
+
173
+ def ==(other)
174
+ other.is_a?(self.class) &&
175
+ value == other.value && start_offset == other.start &&
176
+ end_offset == other.end && groups == other.groups
177
+ end
178
+
179
+ define_builtin_property(:value) do
180
+ value
181
+ end
182
+
183
+ define_builtin_property(:start) do
184
+ start_offset
185
+ end
186
+
187
+ define_builtin_property(:end) do
188
+ end_offset
189
+ end
190
+
191
+ define_builtin_property(:groups) do
192
+ groups
193
+ end
194
+ end
195
+ end
196
+ end
@@ -2,53 +2,364 @@
2
2
 
3
3
  module RuPkl
4
4
  module Node
5
- class String
5
+ class String < Any
6
6
  include ValueCommon
7
+ include Operatable
7
8
 
8
- def initialize(value, portions, position)
9
- super(value, position)
9
+ uninstantiable_class
10
+
11
+ def initialize(parent, value, portions, position)
12
+ super(parent, value, position)
10
13
  @portions = portions
14
+ portions&.each do |portion|
15
+ portion.is_a?(NodeCommon) && add_child(portion)
16
+ end
11
17
  end
12
18
 
13
19
  attr_reader :portions
14
20
 
15
- def evaluate(scopes)
16
- s = value || evaluate_portions(scopes) || ''
17
- self.class.new(s, nil, position)
21
+ def evaluate(context = nil)
22
+ @value ||= evaluate_portions(context)
23
+ self
18
24
  end
19
25
 
20
- def to_pkl_string(scopes)
21
- super(scopes)
22
- .then { |s| escape(s) }
23
- .then { |s| "\"#{s}\"" }
24
- end
25
-
26
- def undefined_operator?(operator)
27
- [:[], :==, :'!=', :+].none?(operator)
26
+ def to_pkl_string(context = nil)
27
+ s = to_ruby(context)
28
+ if invalid_string?(s)
29
+ s
30
+ else
31
+ "\"#{escape(s)}\""
32
+ end
28
33
  end
29
34
 
30
- def invalid_key_operand?(key)
31
- !key.is_a?(Integer)
35
+ def copy(parent = nil, position = @position)
36
+ copied_portions =
37
+ portions&.map do |portion|
38
+ portion.is_a?(NodeCommon) && portion.copy || portion
39
+ end
40
+ self.class.new(parent, nil, copied_portions, position)
32
41
  end
33
42
 
34
43
  def find_by_key(key)
35
44
  index = key.value
36
45
  return nil unless (0...value.length).include?(index)
37
46
 
38
- self.class.new(value[index], nil, portions)
47
+ self.class.new(parent, value[index], nil, position)
48
+ end
49
+
50
+ def b_op_add(r_operand, position)
51
+ result = value + r_operand.value
52
+ String.new(nil, result, nil, position)
53
+ end
54
+
55
+ define_builtin_property(:length) do
56
+ Int.new(self, value.length, position)
57
+ end
58
+
59
+ define_builtin_property(:lastIndex) do
60
+ Int.new(self, value.length - 1, position)
61
+ end
62
+
63
+ define_builtin_property(:isEmpty) do
64
+ Boolean.new(self, value.empty?, position)
65
+ end
66
+
67
+ define_builtin_property(:isBlank) do
68
+ result = /\A\p{White_Space}*\z/.match?(value)
69
+ Boolean.new(self, result, position)
70
+ end
71
+
72
+ define_builtin_property(:md5) do
73
+ hash = Digest::MD5.hexdigest(value)
74
+ String.new(self, hash, nil, position)
75
+ end
76
+
77
+ define_builtin_property(:sha1) do
78
+ hash = Digest::SHA1.hexdigest(value)
79
+ String.new(self, hash, nil, position)
80
+ end
81
+
82
+ define_builtin_property(:sha256) do
83
+ hash = Digest::SHA256.hexdigest(value)
84
+ String.new(self, hash, nil, position)
85
+ end
86
+
87
+ define_builtin_property(:sha256Int) do
88
+ hash = Digest::SHA256.digest(value).unpack1('q')
89
+ Int.new(self, hash, position)
90
+ end
91
+
92
+ define_builtin_property(:base64) do
93
+ result = Base64.urlsafe_encode64(value)
94
+ String.new(self, result, nil, position)
95
+ end
96
+
97
+ define_builtin_property(:base64Decoded) do
98
+ result = Base64.urlsafe_decode64(value)
99
+ String.new(self, result, nil, position)
100
+ rescue ArgumentError
101
+ message = "illegal base64: \"#{value}\""
102
+ raise EvaluationError.new(message, position)
103
+ end
104
+
105
+ define_builtin_method(:getOrNull, index: Int) do |args, parent, position|
106
+ index = args[:index].value
107
+ if (0...value.size).include?(index)
108
+ String.new(parent, value[index], nil, position)
109
+ else
110
+ Null.new(parent, position)
111
+ end
112
+ end
113
+
114
+ define_builtin_method(
115
+ :substring, start: Int, exclusive_end: Int
116
+ ) do |args, parent, position|
117
+ s = args[:start].value
118
+ e = args[:exclusive_end].value
119
+ check_range(s, 0, position)
120
+ check_range(e, s, position)
121
+
122
+ String.new(parent, value[s...e], nil, position)
123
+ end
124
+
125
+ define_builtin_method(
126
+ :substringOrNull, start: Int, exclusive_end: Int
127
+ ) do |args, parent, position|
128
+ s = args[:start].value
129
+ e = args[:exclusive_end].value
130
+ if inside_range?(s, 0) && inside_range?(e, s)
131
+ String.new(parent, value[s...e], nil, position)
132
+ else
133
+ Null.new(parent, position)
134
+ end
135
+ end
136
+
137
+ define_builtin_method(:repeat, count: Int) do |args, parent, position|
138
+ check_positive_number(args[:count], position)
139
+
140
+ result = value * args[:count].value
141
+ String.new(parent, result, nil, position)
142
+ end
143
+
144
+ define_builtin_method(:contains, pattern: String) do |args, parent, position|
145
+ result = value.include?(args[:pattern].value)
146
+ Boolean.new(parent, result, position)
147
+ end
148
+
149
+ define_builtin_method(:startsWith, pattern: String) do |args, parent, position|
150
+ result = value.start_with?(args[:pattern].value)
151
+ Boolean.new(parent, result, position)
152
+ end
153
+
154
+ define_builtin_method(:endsWith, pattern: String) do |args, parent, position|
155
+ result = value.end_with?(args[:pattern].value)
156
+ Boolean.new(parent, result, position)
157
+ end
158
+
159
+ define_builtin_method(:indexOf, pattern: String) do |args, parent, position|
160
+ index_of(:index, args[:pattern], parent, position) do
161
+ message = "\"#{args[:pattern].value}\" does not occur in \"#{value}\""
162
+ raise EvaluationError.new(message, position)
163
+ end
164
+ end
165
+
166
+ define_builtin_method(:indexOfOrNull, pattern: String) do |args, parent, position|
167
+ index_of(:index, args[:pattern], parent, position) do
168
+ Null.new(parent, position)
169
+ end
170
+ end
171
+
172
+ define_builtin_method(:lastIndexOf, pattern: String) do |args, parent, position|
173
+ index_of(:rindex, args[:pattern], parent, position) do
174
+ message = "\"#{args[:pattern].value}\" does not occur in \"#{value}\""
175
+ raise EvaluationError.new(message, position)
176
+ end
177
+ end
178
+
179
+ define_builtin_method(
180
+ :lastIndexOfOrNull, pattern: String
181
+ ) do |args, parent, position|
182
+ index_of(:rindex, args[:pattern], parent, position) do
183
+ Null.new(parent, position)
184
+ end
185
+ end
186
+
187
+ define_builtin_method(:take, n: Int) do |args, parent, position|
188
+ check_positive_number(args[:n], position)
189
+
190
+ result = value[0, args[:n].value] || value
191
+ String.new(parent, result, nil, position)
192
+ end
193
+
194
+ define_builtin_method(:takeLast, n: Int) do |args, parent, position|
195
+ check_positive_number(args[:n], position)
196
+
197
+ pos = value.size - args[:n].value
198
+ result = pos.negative? && value || value[pos..]
199
+ String.new(parent, result, nil, position)
200
+ end
201
+
202
+ define_builtin_method(:drop, n: Int) do |args, parent, position|
203
+ check_positive_number(args[:n], position)
204
+
205
+ result = value[args[:n].value..] || ''
206
+ String.new(parent, result, nil, position)
207
+ end
208
+
209
+ define_builtin_method(:dropLast, n: Int) do |args, parent, position|
210
+ check_positive_number(args[:n], position)
211
+
212
+ length = value.size - args[:n].value
213
+ result = length.negative? && '' || value[0, length]
214
+ String.new(parent, result, nil, position)
215
+ end
216
+
217
+ define_builtin_method(
218
+ :replaceFirst, pattern: String, replacement: String
219
+ ) do |args, parent, position|
220
+ result = value.sub(args[:pattern].value, args[:replacement].value)
221
+ String.new(parent, result, nil, position)
222
+ end
223
+
224
+ define_builtin_method(
225
+ :replaceLast, pattern: String, replacement: String
226
+ ) do |args, parent, position|
227
+ pattern = args[:pattern].value
228
+ replacement = args[:replacement].value
229
+ result =
230
+ if (index = value.rindex(pattern))
231
+ value.dup.tap { |s| s[index, replacement.size] = replacement }
232
+ else
233
+ value
234
+ end
235
+ String.new(parent, result, nil, position)
236
+ end
237
+
238
+ define_builtin_method(
239
+ :replaceAll, pattern: String, replacement: String
240
+ ) do |args, parent, position|
241
+ result = value.gsub(args[:pattern].value, args[:replacement].value)
242
+ String.new(parent, result, nil, position)
243
+ end
244
+
245
+ define_builtin_method(
246
+ :replaceRange, start: Int, exclusive_end: Int, replacement: String
247
+ ) do |args, parent, position|
248
+ s = args[:start].value
249
+ e = args[:exclusive_end].value
250
+ r = args[:replacement].value
251
+
252
+ check_range(s, 0, position)
253
+ check_range(e, s, position)
254
+
255
+ result = value.dup.tap { _1[s...e] = r }
256
+ String.new(parent, result, nil, position)
257
+ end
258
+
259
+ define_builtin_method(:toUpperCase) do |_, parent, position|
260
+ String.new(parent, value.upcase, nil, position)
261
+ end
262
+
263
+ define_builtin_method(:toLowerCase) do |_, parent, position|
264
+ String.new(parent, value.downcase, nil, position)
265
+ end
266
+
267
+ define_builtin_method(:reverse) do |_, parent, position|
268
+ String.new(parent, value.reverse, nil, position)
269
+ end
270
+
271
+ define_builtin_method(:trim) do |_, parent, position|
272
+ pattern = /(?:\A\p{White_Space}+)|(?:\p{White_Space}+\z)/
273
+ String.new(parent, value.gsub(pattern, ''), nil, position)
274
+ end
275
+
276
+ define_builtin_method(:trimStart) do |_, parent, position|
277
+ pattern = /\A\p{White_Space}+/
278
+ String.new(parent, value.sub(pattern, ''), nil, position)
279
+ end
280
+
281
+ define_builtin_method(:trimEnd) do |_, parent, position|
282
+ pattern = /\p{White_Space}+\z/
283
+ String.new(parent, value.sub(pattern, ''), nil, position)
284
+ end
285
+
286
+ define_builtin_method(
287
+ :padStart, width: Int, char: String
288
+ ) do |args, parent, position|
289
+ pad(args[:width], args[:char], :pre, parent, position)
290
+ end
291
+
292
+ define_builtin_method(
293
+ :padEnd, width: Int, char: String
294
+ ) do |args, parent, position|
295
+ pad(args[:width], args[:char], :post, parent, position)
296
+ end
297
+
298
+ define_builtin_method(:capitalize) do |_, parent, position|
299
+ result =
300
+ value.empty? && value || value.dup.tap { |s| s[0] = s[0].upcase }
301
+ String.new(parent, result, nil, position)
302
+ end
303
+
304
+ define_builtin_method(:decapitalize) do |_, parent, position|
305
+ result =
306
+ value.empty? && value || value.dup.tap { |s| s[0] = s[0].downcase }
307
+ String.new(parent, result, nil, position)
308
+ end
309
+
310
+ define_builtin_method(:toInt) do |_, parent, position|
311
+ to_int(parent, position) do
312
+ message = "cannot parse string as Int \"#{value}\""
313
+ raise EvaluationError.new(message, position)
314
+ end
315
+ end
316
+
317
+ define_builtin_method(:toIntOrNull) do |_, parent, position|
318
+ to_int(parent, position) do
319
+ Null.new(parent, position)
320
+ end
321
+ end
322
+
323
+ define_builtin_method(:toFloat) do |_, parent, position|
324
+ to_float(parent, position) do
325
+ message = "cannot parse string as Float \"#{value}\""
326
+ raise EvaluationError.new(message, position)
327
+ end
328
+ end
329
+
330
+ define_builtin_method(:toFloatOrNull) do |_, parent, position|
331
+ to_float(parent, position) do
332
+ Null.new(parent, position)
333
+ end
334
+ end
335
+
336
+ define_builtin_method(:toBoolean) do |_, parent, position|
337
+ to_boolean(parent, position) do
338
+ message = "cannot parse string as Boolean \"#{value}\""
339
+ raise EvaluationError.new(message, position)
340
+ end
341
+ end
342
+
343
+ define_builtin_method(:toBooleanOrNull) do |_, parent, position|
344
+ to_boolean(parent, position) do
345
+ Null.new(parent, position)
346
+ end
39
347
  end
40
348
 
41
349
  private
42
350
 
43
- def evaluate_portions(scopes)
44
- portions
45
- &.map { evaluate_portion(scopes, _1) }
46
- &.join
351
+ def evaluate_portions(context)
352
+ evaluated_portions =
353
+ portions&.map do |portion|
354
+ evaluate_portion(portion, context)
355
+ .tap { |s| return s if invalid_string?(s) }
356
+ end
357
+ evaluated_portions&.join || ''
47
358
  end
48
359
 
49
- def evaluate_portion(scopes, portion)
360
+ def evaluate_portion(portion, context)
50
361
  if portion.respond_to?(:to_string)
51
- portion.to_string(scopes)
362
+ portion.to_string(context)
52
363
  else
53
364
  portion
54
365
  end
@@ -61,6 +372,87 @@ module RuPkl
61
372
  }
62
373
  string.gsub(/([\t\n\r"\\])/) { replace[_1] }
63
374
  end
375
+
376
+ def valid_key_operand?(key)
377
+ key.is_a?(Int)
378
+ end
379
+
380
+ def defined_operator?(operator)
381
+ [:[], :+].any?(operator)
382
+ end
383
+
384
+ def check_range(index, start_index, position)
385
+ return if inside_range?(index, start_index)
386
+
387
+ message = "index #{index} is out of range " \
388
+ "#{start_index}..#{value.size}: \"#{value}\""
389
+ raise EvaluationError.new(message, position)
390
+ end
391
+
392
+ def inside_range?(index, start_index)
393
+ (start_index..value.size).include?(index)
394
+ end
395
+
396
+ def index_of(method, pattern, parent, position)
397
+ result = value.__send__(method, pattern.value)
398
+ result && Int.new(parent, result, position) || yield
399
+ end
400
+
401
+ def pad(width, char, pre_post, parent, position)
402
+ unless char.value.length == 1
403
+ message = "expected a char, but got \"#{char.value}\""
404
+ raise EvaluationError.new(message, position)
405
+ end
406
+
407
+ result =
408
+ pad_prefix_postfix(width, char, pre_post)
409
+ .then { |pre, post| [pre, value, post].join }
410
+ String.new(parent, result, nil, position)
411
+ end
412
+
413
+ def pad_prefix_postfix(width, char, pre_post)
414
+ pad_width = width.value - value.length
415
+ if pad_width <= 0
416
+ [nil, nil]
417
+ elsif pre_post == :pre
418
+ [char.value * pad_width, nil]
419
+ else
420
+ [nil, char.value * pad_width]
421
+ end
422
+ end
423
+
424
+ def to_int(parent, position)
425
+ Int.new(parent, Integer(remove_underscore_from_number(value), 10), position)
426
+ rescue ArgumentError
427
+ yield
428
+ end
429
+
430
+ def to_float(parent, position)
431
+ result =
432
+ case value
433
+ when 'NaN' then ::Float::NAN
434
+ when 'Infinity' then ::Float::INFINITY
435
+ when '-Infinity' then -::Float::INFINITY
436
+ else Float(remove_underscore_from_number(value))
437
+ end
438
+ Float.new(parent, result, position)
439
+ rescue ArgumentError
440
+ yield
441
+ end
442
+
443
+ def remove_underscore_from_number(string)
444
+ string.gsub(/(?:(?<=\d)|(?<=.[eE][+-]))_+/, '')
445
+ end
446
+
447
+ def to_boolean(parent, position)
448
+ result =
449
+ case value
450
+ when /\Atrue\z/i then true
451
+ when /\Afalse\z/i then false
452
+ else return yield
453
+ end
454
+ Boolean.new(parent, result, position)
455
+ end
64
456
  end
65
457
  end
66
458
  end