loxxy 0.2.05 → 0.3.03

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 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