loxxy 0.2.05 → 0.3.03

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce7eeb0247e67176d1dc4c67066eff4398ffaa8b75755b289eb3e53932d3006b
4
- data.tar.gz: 84f7c781c10b0f6f07cfb5bab98d2ab9d78340cf5b20426822226cbe5c266a94
3
+ metadata.gz: 734fc040d2487c17abd8d89d4f3422e4d55db226ef79b354e52402fc24cd61fc
4
+ data.tar.gz: e799c5d1044159e9bfdc1b337c370e95878c85752dcaaf60578e71c4763a7da1
5
5
  SHA512:
6
- metadata.gz: ad3cce2c2f3bcf8c0c3ae30b1dc050764c654e276a2d482934c2efd7ad6120b0db7e415003bd8d2f887ed118f723eee7c882f231ceadea773236d1d01e478db9
7
- data.tar.gz: d5e25c250ac629d76e01468fbb59a2fcb287b2a112fda9f2432e83df48a889581ecbe38723ab7371ba6ae3cdd94367051249907994caa78bfa852e7b328a0811
6
+ metadata.gz: d6d393729d977e6493979f4e89c664a1bba685b08c67a5e9e9b0d12354ac552deadae4e15a213560b752bdb9b44c7a05a5652fc8b4ed6500409fa04fe3cf29c1
7
+ data.tar.gz: f3b121e4a5ca07b4297aca8c43a3325b00dd30fb131f412a17a5e82d7887e8fc8749a2e1a422c75fe0c1045652970a121a78883b06c6bbc6baa6d896b0fea03f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,60 @@
1
+ ## [0.3.03] - 2021-05-23
2
+ - Fixes in the location of an undefined variable. Rewrite of the scanning of lox string.
3
+
4
+ ### Changed
5
+ - Method `BackEnd::Engine#after_variable_expr` the error message `Undefined variable` nows gives the location of the offending variable.
6
+ - Class `FrontEnd#Scanner` complete refactoring of String recognition.
7
+
8
+ ### Fixed
9
+ - Method `Ast::AstBuilder#reduce_variable_expr` now associates the correct location of the variable.
10
+
11
+ ## [0.3.02] - 2021-05-22
12
+ - New built-in expressions `getc`, `chr`, `exit` and `print_eeror` , fixes with deeply nested returns, set expressions
13
+
14
+ ### New
15
+ - Class `BackEnd::Engine` new built-in functions `getc`, `chr`, `exit`, and `print_error`
16
+
17
+ ### Changed
18
+ - Class `Ast::LoxSetExpr` value to assign is now a distinct atrrbute instead of a subnode
19
+ - Class `BackEnd::Engine` two distinct stacks: one of expression evaluation, another for argument passing
20
+
21
+ ### Fixed
22
+ - Class `BackEnd::LoxClass`: in some contexts the init method returned twice 'this'.
23
+ - Method `LoxFunction#call` mismatch between deeply nested returns and environments
24
+
25
+
26
+ ## [0.3.01] - 2021-05-08
27
+ - Fix in `Scanner` class, added more tests in test suite.
28
+
29
+ ### New
30
+ - Added the new subfolder `extra` under `test_suite`. It will contain tests for non-standard features or tests not covered in standard test suite.
31
+
32
+ ### Fixed
33
+ - Class `FrontEnd::Scanner`: Couldn't correctly recognize a plus preceding directly a number literal
34
+
35
+ ## [0.3.00] - 2021-05-07
36
+ - Milestone: `Loxxy` passes all reference test suite.
37
+
38
+ ### Fixed
39
+ - Method `BackEnd::Resolver#before_variable_expr`: Standard `Lox` allows re-declaration of a variable at top-level scope
40
+
41
+
42
+ ## [0.2.06] - 2021-05-04
43
+ - Nearly passing the 'official' test suite, fixing non-compliant behavior, specialized exceptions for errors
44
+
45
+ ### New
46
+ - Module `LoxFileTester` module that hosts methods that simplify the tests of `Lox` source file.
47
+
48
+ ### Changed
49
+ - Folder `test_suite` vastly reorganized. Sub-folder `baseline` contains spec files testing the `Lox` files from official implementation
50
+ - Class `BackEnd::Engine` replaced most `StandardError` by `Loxxy::RuntimeError` exception.
51
+ - Class `BackEnd::Resolver` replaced most `StandardError` by `Loxxy::RuntimeError` exception.
52
+ - Method `Datatype::Number#/` now handles correctly expression like `0/0` (integer divide)
53
+
54
+ ### Fixed
55
+ - `0/0` expression results in a ZeroDivisionError exception, in Lox this result to a NaN (Not a Number). Now, `Loxxy` is aligned to standard `Lox`
56
+ - `FrontEnd::Scanner` now always treats expression like `-123` as the unary or binary minus operator applied to a positive number.
57
+
1
58
  ## [0.2.05] - 2021-04-26
2
59
  - `Loxxy` now transforms for loops into while loops (desugaring), fix in Scanner class
3
60
 
data/README.md CHANGED
@@ -19,11 +19,17 @@ Although __Lox__ is fairly simple, it is far from being a toy language:
19
19
  ### Loxxy gem features
20
20
  - Complete tree-walking interpreter including lexer, parser and resolver
21
21
  - 100% pure Ruby with clean design (not a port from some other language)
22
+ - Passes the `jox` (THE reference `Lox` implementation) test suite
22
23
  - Minimal runtime dependency (Rley gem). Won't drag a bunch of gems...
23
24
  - Ruby API for integrating a Lox interpreter with your code.
24
25
  - A command-line interpreter `loxxy`
25
26
  - Open for your language extensions...
26
27
 
28
+ ### Why `Loxxy` ?
29
+ - If programming languages are one of your subject interest...
30
+ - ... and you wanted learn how to implement one in Ruby...
31
+ - ... then `Loxxy` can help to understand and experiment in this rewarding craft.
32
+
27
33
  ## How to start in 1, 2, 3...?
28
34
  ... in less than 3 minutes...
29
35
 
@@ -318,7 +318,7 @@ module Loxxy
318
318
  name_assignee = theChildren[1].token.lexeme.dup
319
319
  if theChildren[0].kind_of?(Ast::LoxSetExpr)
320
320
  theChildren[0].property = name_assignee
321
- theChildren[0].subnodes << theChildren[3]
321
+ theChildren[0].value = theChildren[3]
322
322
  theChildren[0]
323
323
  else
324
324
  Ast::LoxAssignExpr.new(tokens[1].position, name_assignee, theChildren[3])
@@ -395,9 +395,10 @@ module Loxxy
395
395
  end
396
396
 
397
397
  # rule('primary' => 'IDENTIFIER')
398
- def reduce_variable_expr(_production, _range, tokens, theChildren)
398
+ def reduce_variable_expr(_production, _range, _tokens, theChildren)
399
399
  var_name = theChildren[0].token.lexeme
400
- LoxVariableExpr.new(tokens[0].position, var_name)
400
+ pos = theChildren[0].token.position
401
+ LoxVariableExpr.new(pos, var_name)
401
402
  end
402
403
 
403
404
  # rule('primary' => 'THIS')
@@ -126,7 +126,6 @@ module Loxxy
126
126
  # @param aSetExpr [AST::LOXGetExpr] the get expression node to visit
127
127
  def visit_set_expr(aSetExpr)
128
128
  broadcast(:before_set_expr, aSetExpr, self)
129
- traverse_subnodes(aSetExpr)
130
129
  broadcast(:after_set_expr, aSetExpr, self)
131
130
  end
132
131
 
@@ -4,17 +4,20 @@ require_relative 'lox_compound_expr'
4
4
 
5
5
  module Loxxy
6
6
  module Ast
7
- class LoxSetExpr < LoxCompoundExpr
7
+ class LoxSetExpr < LoxNode
8
8
  # @return [Ast::LoxNode] the object to which the property belongs to
9
9
  attr_reader :object
10
10
 
11
11
  # @return [String] Name of an object property
12
12
  attr_accessor :property
13
13
 
14
+ # @return [LoxNode, Datatype] value to assign
15
+ attr_accessor :value
16
+
14
17
  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
15
18
  # @param anObject [Ast::LoxNode] The object which the given property is being set
16
19
  def initialize(aPosition, anObject)
17
- super(aPosition, [])
20
+ super(aPosition)
18
21
  @object = anObject
19
22
  end
20
23
 
@@ -20,7 +20,7 @@ module Loxxy
20
20
  # @return [BackEnd::SymbolTable]
21
21
  attr_reader :symbol_table
22
22
 
23
- # @return [Array<Datatype::BuiltinDatatype>] Data stack for the expression results
23
+ # @return [Array<Datatype::BuiltinDatatype>] Data stack for arguments and return results
24
24
  attr_reader :stack
25
25
 
26
26
  # @return [Hash { Symbol => UnaryOperator}]
@@ -35,17 +35,31 @@ module Loxxy
35
35
  # @param theOptions [Hash]
36
36
  def initialize(theOptions)
37
37
  @config = theOptions
38
+ @istream = config.include?(:istream) ? config[:istream] : $stdin
38
39
  @ostream = config.include?(:ostream) ? config[:ostream] : $stdout
39
40
  @symbol_table = SymbolTable.new
40
41
  @stack = []
41
- @unary_operators = {}
42
- @binary_operators = {}
43
42
 
43
+ reset_expr_stack
44
44
  init_unary_operators
45
45
  init_binary_operators
46
46
  init_globals
47
47
  end
48
48
 
49
+ # Returns the current environment
50
+ # @return [Loxxy::BackEnd::Environment]
51
+ def current_env
52
+ symbol_table.current_env
53
+ end
54
+
55
+ def expr_stack
56
+ current_env.expr_stack
57
+ end
58
+
59
+ def reset_expr_stack
60
+ current_env.expr_stack.clear
61
+ end
62
+
49
63
  # Given an abstract syntax parse tree visitor, launch the visit
50
64
  # and execute the visit events in the output stream.
51
65
  # @param aVisitor [AST::ASTVisitor]
@@ -58,7 +72,7 @@ module Loxxy
58
72
  aVisitor.subscribe(self)
59
73
  aVisitor.start
60
74
  aVisitor.unsubscribe(self)
61
- stack.empty? ? Datatype::Nil.instance : stack.pop
75
+ expr_stack.empty? ? Datatype::Nil.instance : expr_stack.pop
62
76
  end
63
77
 
64
78
  ##########################################################################
@@ -69,10 +83,14 @@ module Loxxy
69
83
  # Do nothing, subnodes were already evaluated
70
84
  end
71
85
 
86
+ def before_class_stmt(_class_stmt)
87
+ reset_expr_stack
88
+ end
89
+
72
90
  def after_class_stmt(aClassStmt, aVisitor)
73
91
  if aClassStmt.superclass
74
92
  aClassStmt.superclass.accept(aVisitor)
75
- parent = stack.pop
93
+ parent = expr_stack.pop
76
94
  unless parent.kind_of?(LoxClass)
77
95
  raise Loxxy::RuntimeError, 'Superclass must be a class.'
78
96
  end
@@ -89,7 +107,7 @@ module Loxxy
89
107
  meths = aClassStmt.body.map do |func_node|
90
108
  func_node.is_method = true
91
109
  func_node.accept(aVisitor)
92
- mth = stack.pop
110
+ mth = expr_stack.pop
93
111
  mth.is_initializer = true if mth.name == 'init'
94
112
  mth
95
113
  end
@@ -104,11 +122,15 @@ module Loxxy
104
122
  symbol_table.insert(new_var)
105
123
  end
106
124
 
125
+ def before_var_stmt(_var_stmt)
126
+ reset_expr_stack
127
+ end
128
+
107
129
  def after_var_stmt(aVarStmt)
108
130
  new_var = Variable.new(aVarStmt.name, Datatype::Nil.instance)
109
131
  symbol_table.insert(new_var)
110
132
 
111
- value = stack.pop
133
+ value = expr_stack.pop
112
134
  new_var.assign(value)
113
135
  end
114
136
 
@@ -116,9 +138,14 @@ module Loxxy
116
138
  before_block_stmt(aForStmt)
117
139
  end
118
140
 
141
+ def before_if_stmt(_if_stmt)
142
+ reset_expr_stack
143
+ end
144
+
119
145
  def after_if_stmt(anIfStmt, aVisitor)
120
146
  # Retrieve the result of the condition evaluation
121
- condition = stack.pop
147
+ # condition = stack.pop
148
+ condition = expr_stack.pop
122
149
  if condition.truthy?
123
150
  anIfStmt.then_stmt.accept(aVisitor)
124
151
  elsif anIfStmt.else_stmt
@@ -126,18 +153,31 @@ module Loxxy
126
153
  end
127
154
  end
128
155
 
156
+ def before_print_stmt(_print_stmt)
157
+ reset_expr_stack
158
+ end
159
+
129
160
  def after_print_stmt(_printStmt)
130
- tos = stack.pop
161
+ tos = expr_stack.pop
131
162
  @ostream.print tos ? tos.to_str : 'nil'
132
163
  end
133
164
 
165
+ def before_return_stmt(_return_stmt)
166
+ reset_expr_stack
167
+ end
168
+
134
169
  def after_return_stmt(_returnStmt, _aVisitor)
170
+ stack.push(expr_stack.pop)
135
171
  throw(:return)
136
172
  end
137
173
 
174
+ def before_while_stmt(_while_stmt)
175
+ reset_expr_stack
176
+ end
177
+
138
178
  def after_while_stmt(aWhileStmt, aVisitor)
139
179
  loop do
140
- condition = stack.pop
180
+ condition = expr_stack.pop
141
181
  break unless condition.truthy?
142
182
 
143
183
  aWhileStmt.body.accept(aVisitor)
@@ -146,6 +186,7 @@ module Loxxy
146
186
  end
147
187
 
148
188
  def before_block_stmt(_aBlockStmt)
189
+ reset_expr_stack
149
190
  new_env = Environment.new
150
191
  symbol_table.enter_environment(new_env)
151
192
  end
@@ -159,40 +200,42 @@ module Loxxy
159
200
  variable = variable_lookup(anAssignExpr)
160
201
  raise Loxxy::RuntimeError, "Undefined variable '#{var_name}'." unless variable
161
202
 
162
- value = stack.last # ToS remains since an assignment produces a value
203
+ value = expr_stack.last # ToS remains since an assignment produces a value
163
204
  variable.assign(value)
164
205
  end
165
206
 
166
- def before_set_expr(aSetExpr, aVisitor)
167
- # Evaluate receiver object part
168
- aSetExpr.object.accept(aVisitor)
207
+ def before_set_expr(_set_expr, _visitor)
208
+ reset_expr_stack
169
209
  end
170
210
 
171
- def after_set_expr(aSetExpr, _visitor)
172
- value = stack.pop
173
- assignee = stack.pop
211
+ def after_set_expr(aSetExpr, aVisitor)
212
+ # Evaluate receiver object part (i.e. 'this')
213
+ aSetExpr.object.accept(aVisitor)
214
+ assignee = expr_stack.pop
174
215
  unless assignee.kind_of?(LoxInstance)
175
216
  raise Loxxy::RuntimeError, 'Only instances have fields.'
176
217
  end
177
218
 
219
+ aSetExpr.value.accept(aVisitor)
220
+ value = expr_stack.pop
221
+
178
222
  assignee.set(aSetExpr.property, value)
179
- stack.push value
223
+ expr_stack.push value
180
224
  end
181
225
 
182
226
  def after_logical_expr(aLogicalExpr, visitor)
183
227
  op = aLogicalExpr.operator
184
- operand1 = stack.pop # only first operand was evaluated
228
+ operand1 = expr_stack.pop # only first operand was evaluated
185
229
  result = nil
186
230
  if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?)
187
231
  result = operand1
188
232
  else
189
233
  raw_operand2 = aLogicalExpr.subnodes[1]
190
234
  raw_operand2.accept(visitor) # Visit means operand2 is evaluated
191
- operand2 = stack.pop
235
+ operand2 = expr_stack.pop
192
236
  result = logical_2nd_arg(operand2)
193
237
  end
194
-
195
- stack.push result
238
+ expr_stack.push result
196
239
  end
197
240
 
198
241
  def logical_2nd_arg(operand2)
@@ -212,13 +255,14 @@ module Loxxy
212
255
  end
213
256
 
214
257
  def after_binary_expr(aBinaryExpr)
215
- operand2 = stack.pop
216
- operand1 = stack.pop
258
+ operand2 = expr_stack.pop
259
+ operand1 = expr_stack.pop
217
260
  op = aBinaryExpr.operator
218
261
  operator = binary_operators[op]
219
262
  operator.validate_operands(operand1, operand2)
220
263
  if operand1.respond_to?(op)
221
- stack.push operand1.send(op, operand2)
264
+ result = operand1.send(op, operand2)
265
+ expr_stack.push convert2lox_datatype(result)
222
266
  else
223
267
  msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
224
268
  raise StandardError, msg1
@@ -226,12 +270,13 @@ module Loxxy
226
270
  end
227
271
 
228
272
  def after_unary_expr(anUnaryExpr)
229
- operand = stack.pop
273
+ operand = expr_stack.pop
230
274
  op = anUnaryExpr.operator
231
275
  operator = unary_operators[op]
232
276
  operator.validate_operand(operand)
233
277
  if operand.respond_to?(op)
234
- stack.push operand.send(op)
278
+ result = operand.send(op)
279
+ expr_stack.push convert2lox_datatype(result)
235
280
  else
236
281
  msg1 = "`#{op}': Unimplemented operator for a #{operand.class}."
237
282
  raise StandardError, msg1
@@ -241,12 +286,17 @@ module Loxxy
241
286
  def after_call_expr(aCallExpr, aVisitor)
242
287
  # Evaluate callee part
243
288
  aCallExpr.callee.accept(aVisitor)
244
- callee = stack.pop
289
+ callee = expr_stack.pop
290
+ before_size = expr_stack.size
245
291
  aCallExpr.arguments.reverse_each { |arg| arg.accept(aVisitor) }
292
+ after_size = expr_stack.size
293
+ if after_size > before_size
294
+ stack.concat(expr_stack.pop(after_size - before_size))
295
+ end
246
296
 
247
297
  case callee
248
298
  when NativeFunction
249
- stack.push callee.call # Pass arguments
299
+ expr_stack.push callee.call # Pass arguments
250
300
  when LoxFunction, LoxClass
251
301
  arg_count = aCallExpr.arguments.size
252
302
  if arg_count != callee.arity
@@ -261,12 +311,12 @@ module Loxxy
261
311
 
262
312
  def after_get_expr(aGetExpr, aVisitor)
263
313
  aGetExpr.object.accept(aVisitor)
264
- instance = stack.pop
314
+ instance = expr_stack.pop
265
315
  unless instance.kind_of?(LoxInstance)
266
316
  raise Loxxy::RuntimeError, 'Only instances have properties.'
267
317
  end
268
318
 
269
- stack.push instance.get(aGetExpr.property)
319
+ expr_stack.push instance.get(aGetExpr.property)
270
320
  end
271
321
 
272
322
  def after_grouping_expr(_groupingExpr)
@@ -276,14 +326,17 @@ module Loxxy
276
326
  def after_variable_expr(aVarExpr, aVisitor)
277
327
  var_name = aVarExpr.name
278
328
  var = variable_lookup(aVarExpr)
279
- raise Loxxy::RuntimeError, "Undefined variable '#{var_name}'." unless var
329
+ unless var
330
+ pos = "line #{aVarExpr.position.line}:#{aVarExpr.position.column}"
331
+ raise Loxxy::RuntimeError, "[#{pos}] Undefined variable '#{var_name}'."
332
+ end
280
333
 
281
334
  var.value.accept(aVisitor) # Evaluate variable value then push on stack
282
335
  end
283
336
 
284
337
  # @param literalExpr [Ast::LoxLiteralExpr]
285
338
  def before_literal_expr(literalExpr)
286
- stack.push(literalExpr.literal)
339
+ expr_stack.push(literalExpr.literal)
287
340
  end
288
341
 
289
342
  def after_this_expr(aThisExpr, aVisitor)
@@ -299,21 +352,25 @@ module Loxxy
299
352
  superklass = variable_lookup(aSuperExpr).value.superclass
300
353
  method = superklass.find_method(aSuperExpr.property)
301
354
  unless method
302
- raise StandardError, "Undefined property '#{aSuperExpr.property}'."
355
+ raise Loxxy::RuntimeError, "Undefined property '#{aSuperExpr.property}'."
303
356
  end
304
357
 
305
- stack.push method.bind(instance)
358
+ expr_stack.push method.bind(instance)
306
359
  end
307
360
 
308
361
  # @param aValue [Ast::BuiltinDattype] the built-in datatype value
309
362
  def before_visit_builtin(aValue)
310
- stack.push(aValue)
363
+ expr_stack.push(aValue)
364
+ end
365
+
366
+ def before_fun_stmt(_fun_stmt, _visitor)
367
+ reset_expr_stack
311
368
  end
312
369
 
313
370
  def after_fun_stmt(aFunStmt, _visitor)
314
371
  function = LoxFunction.new(aFunStmt.name, aFunStmt.params, aFunStmt.body, self)
315
372
  if aFunStmt.is_method
316
- stack.push function
373
+ expr_stack.push function
317
374
  else
318
375
  new_var = Variable.new(aFunStmt.name, function)
319
376
  symbol_table.insert(new_var)
@@ -335,30 +392,20 @@ module Loxxy
335
392
  env.defns[aVarNode.name]
336
393
  end
337
394
 
338
- NativeFunction = Struct.new(:callable, :interp) do
339
- def accept(_visitor)
340
- interp.stack.push self
341
- end
342
-
343
- def call
344
- callable.call
345
- end
346
-
347
- def to_str
348
- '<native fn>'
349
- end
350
- end
351
-
352
395
  def init_unary_operators
396
+ @unary_operators = {}
397
+
353
398
  negate_op = UnaryOperator.new('-', [Datatype::Number])
354
399
  unary_operators[:-@] = negate_op
355
400
 
356
401
  negation_op = UnaryOperator.new('!', [Datatype::BuiltinDatatype,
357
- BackEnd::LoxFunction])
402
+ BackEnd::LoxInstance, BackEnd::LoxFunction, BackEnd::LoxClass])
358
403
  unary_operators[:!] = negation_op
359
404
  end
360
405
 
361
406
  def init_binary_operators
407
+ @binary_operators = {}
408
+
362
409
  plus_op = BinaryOperator.new('+', [[Datatype::Number, :idem],
363
410
  [Datatype::LXString, :idem]])
364
411
  binary_operators[:+] = plus_op
@@ -393,6 +440,24 @@ module Loxxy
393
440
 
394
441
  def init_globals
395
442
  add_native_fun('clock', native_clock)
443
+ add_native_fun('getc', native_getc)
444
+ add_native_fun('chr', native_chr)
445
+ add_native_fun('exit', native_exit)
446
+ add_native_fun('print_error', native_print_error)
447
+ end
448
+
449
+ NativeFunction = Struct.new(:callable, :interp) do
450
+ def accept(_visitor)
451
+ interp.expr_stack.push self
452
+ end
453
+
454
+ def call
455
+ callable.call
456
+ end
457
+
458
+ def to_str
459
+ '<native fn>'
460
+ end
396
461
  end
397
462
 
398
463
  def add_native_fun(aName, aProc)
@@ -408,6 +473,47 @@ module Loxxy
408
473
  Datatype::Number.new(now)
409
474
  end
410
475
  end
476
+
477
+ # Read a single character and return the character code as an integer.
478
+ def native_getc
479
+ proc do
480
+ ch = @istream.getc
481
+ Datatype::Number.new(ch.codepoints[0])
482
+ end
483
+ end
484
+
485
+ # chr(ch): Convert given character code number to a single-character string.
486
+ def native_chr
487
+ proc do
488
+ codepoint = stack.pop
489
+ Datatype::LXString.new(codepoint.value.chr)
490
+ end
491
+ end
492
+
493
+ # exit(status): Exit with given status code.
494
+ def native_exit
495
+ proc do
496
+ status = stack.pop
497
+ exit(status.value)
498
+ end
499
+ end
500
+
501
+ # print_error(message): Print message string on stderr.
502
+ def native_print_error
503
+ proc do
504
+ message = stack.pop
505
+ $stderr.print message.value
506
+ end
507
+ end
508
+
509
+ def convert2lox_datatype(item)
510
+ case item
511
+ when TrueClass then Datatype::True.instance
512
+ when FalseClass then Datatype::False.instance
513
+ else
514
+ item
515
+ end
516
+ end
411
517
  end # class
412
518
  end # module
413
519
  end # module