loxxy 0.2.05 → 0.2.06

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: cbb2f24e245891baabf20458e5b663784ba7f82ff902ab7892007d9b53d99347
4
+ data.tar.gz: eab4ede6f48470449f731e3074b2ac2fe72b53449c9471231ea64d0007a873ae
5
5
  SHA512:
6
- metadata.gz: ad3cce2c2f3bcf8c0c3ae30b1dc050764c654e276a2d482934c2efd7ad6120b0db7e415003bd8d2f887ed118f723eee7c882f231ceadea773236d1d01e478db9
7
- data.tar.gz: d5e25c250ac629d76e01468fbb59a2fcb287b2a112fda9f2432e83df48a889581ecbe38723ab7371ba6ae3cdd94367051249907994caa78bfa852e7b328a0811
6
+ metadata.gz: 8037d3c239e39d47554c4e7f7698e5908cdaa5e460c8972fe8dc5b58ee5fd59647c413db95250ff739c08f9ca891c8f576eb8d78aa63cf40039e8d1b3328e9a2
7
+ data.tar.gz: 795face50a76140ba2f6be2e6e6c44e671992baaea2ecade980acf1baed173dc59cbe7cd2ce419b509e8593068ffc3e637e0678dab2db72a96250d4e53ef0338
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [0.2.06] - 2021-05-04
2
+ - Nearly passing the 'official' test suite, fixing non-compliant behavior, specialized exceptions for errors
3
+
4
+ ### New
5
+ - Module `LoxFileTester` module that hosts methods that simplify the tests of `Lox` source file.
6
+
7
+ ### Changed
8
+ - Folder `test_suite` vastly reorganized. Sub-folder `baseline` contains spec files testing the `Lox` files from official implementation
9
+ - Class `BackEnd::Engine` replaced most `StandardError` by `Loxxy::RuntimeError` exception.
10
+ - Class `BackEnd::Resolver` replaced most `StandardError` by `Loxxy::RuntimeError` exception.
11
+ - Method `Datatype::Number#/` now handles correctly expression like `0/0` (integer divide)
12
+
13
+ ### Fixed
14
+ - `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`
15
+ - `FrontEnd::Scanner` now always treats expression like `-123` as the unary or binary minus operator applied to a positive number.
16
+
1
17
  ## [0.2.05] - 2021-04-26
2
18
  - `Loxxy` now transforms for loops into while loops (desugaring), fix in Scanner class
3
19
 
@@ -218,7 +218,8 @@ module Loxxy
218
218
  operator = binary_operators[op]
219
219
  operator.validate_operands(operand1, operand2)
220
220
  if operand1.respond_to?(op)
221
- stack.push operand1.send(op, operand2)
221
+ result = operand1.send(op, operand2)
222
+ stack.push convert2lox_datatype(result)
222
223
  else
223
224
  msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
224
225
  raise StandardError, msg1
@@ -231,7 +232,8 @@ module Loxxy
231
232
  operator = unary_operators[op]
232
233
  operator.validate_operand(operand)
233
234
  if operand.respond_to?(op)
234
- stack.push operand.send(op)
235
+ result = operand.send(op)
236
+ stack.push convert2lox_datatype(result)
235
237
  else
236
238
  msg1 = "`#{op}': Unimplemented operator for a #{operand.class}."
237
239
  raise StandardError, msg1
@@ -299,7 +301,7 @@ module Loxxy
299
301
  superklass = variable_lookup(aSuperExpr).value.superclass
300
302
  method = superklass.find_method(aSuperExpr.property)
301
303
  unless method
302
- raise StandardError, "Undefined property '#{aSuperExpr.property}'."
304
+ raise Loxxy::RuntimeError, "Undefined property '#{aSuperExpr.property}'."
303
305
  end
304
306
 
305
307
  stack.push method.bind(instance)
@@ -354,7 +356,7 @@ module Loxxy
354
356
  unary_operators[:-@] = negate_op
355
357
 
356
358
  negation_op = UnaryOperator.new('!', [Datatype::BuiltinDatatype,
357
- BackEnd::LoxFunction])
359
+ BackEnd::LoxInstance, BackEnd::LoxFunction, BackEnd::LoxClass])
358
360
  unary_operators[:!] = negation_op
359
361
  end
360
362
 
@@ -408,6 +410,15 @@ module Loxxy
408
410
  Datatype::Number.new(now)
409
411
  end
410
412
  end
413
+
414
+ def convert2lox_datatype(item)
415
+ case item
416
+ when TrueClass then Datatype::True.instance
417
+ when FalseClass then Datatype::False.instance
418
+ else
419
+ item
420
+ end
421
+ end
411
422
  end # class
412
423
  end # module
413
424
  end # module
@@ -69,7 +69,7 @@ module Loxxy
69
69
  define(aClassStmt.name)
70
70
  if aClassStmt.superclass
71
71
  if aClassStmt.name == aClassStmt.superclass.name
72
- raise StandardError, "'A class can't inherit from itself."
72
+ raise Loxxy::RuntimeError, "'A class can't inherit from itself."
73
73
  end
74
74
 
75
75
  @current_class = :subclass
@@ -96,17 +96,17 @@ module Loxxy
96
96
  def before_return_stmt(returnStmt)
97
97
  if scopes.size < 2
98
98
  msg = "Error at 'return': Can't return from top-level code."
99
- raise StandardError, msg
99
+ raise Loxxy::RuntimeError, msg
100
100
  end
101
101
 
102
102
  if current_function == :none
103
103
  msg = "Error at 'return': Can't return from outside a function."
104
- raise StandardError, msg
104
+ raise Loxxy::RuntimeError, msg
105
105
  end
106
106
 
107
107
  if current_function == :initializer
108
108
  msg = "Error at 'return': Can't return a value from an initializer."
109
- raise StandardError, msg unless returnStmt.subnodes[0].kind_of?(Datatype::Nil)
109
+ raise Loxxy::RuntimeError, msg unless returnStmt.subnodes[0].kind_of?(Datatype::Nil)
110
110
  end
111
111
  end
112
112
 
@@ -160,7 +160,7 @@ module Loxxy
160
160
  def before_this_expr(_thisExpr)
161
161
  if current_class == :none
162
162
  msg = "Error at 'this': Can't use 'this' outside of a class."
163
- raise StandardError, msg
163
+ raise Loxxy::RuntimeError, msg
164
164
  end
165
165
  end
166
166
 
@@ -175,11 +175,11 @@ module Loxxy
175
175
  msg_prefix = "Error at 'super': Can't use 'super' "
176
176
  if current_class == :none
177
177
  err_msg = msg_prefix + 'outside of a class.'
178
- raise StandardError, err_msg
178
+ raise Loxxy::RuntimeError, err_msg
179
179
 
180
180
  elsif current_class == :class
181
181
  err_msg = msg_prefix + 'in a class without superclass.'
182
- raise StandardError, err_msg
182
+ raise Loxxy::RuntimeError, err_msg
183
183
 
184
184
  end
185
185
  # 'super' behaves closely to a local variable
@@ -205,14 +205,19 @@ module Loxxy
205
205
  scopes.pop
206
206
  end
207
207
 
208
+ # rubocop: disable Style/SoleNestedConditional
208
209
  def declare(aVarName)
209
210
  return if scopes.empty?
210
211
 
211
212
  curr_scope = scopes.last
212
- if curr_scope.include?(aVarName)
213
- msg = "Error at '#{aVarName}': Already variable with this name in this scope."
214
- raise StandardError, msg
213
+ if scopes.size > 1 # Not at top-level?
214
+ # Oddly enough, Lox allows variable re-declaration at top-level
215
+ if curr_scope.include?(aVarName)
216
+ msg = "Error at '#{aVarName}': Already variable with this name in this scope."
217
+ raise Loxxy::RuntimeError, msg
218
+ end
215
219
  end
220
+ # rubocop: enable Style/SoleNestedConditional
216
221
 
217
222
  # The initializer is not yet processed.
218
223
  # Mark the variable as 'not yet ready' = exists but may not be referenced yet
@@ -7,6 +7,10 @@ module Loxxy
7
7
  module Datatype
8
8
  # Class for representing a Lox numeric value.
9
9
  class Number < BuiltinDatatype
10
+ def zero?
11
+ value.zero?
12
+ end
13
+
10
14
  # Perform the addition of two Lox numbers or
11
15
  # one Lox number and a Ruby Numeric
12
16
  # @param other [Loxxy::Datatype::Number, Numeric]
@@ -59,17 +63,28 @@ module Loxxy
59
63
  # one Lox number and a Ruby Numeric
60
64
  # @param other [Loxxy::Datatype::Number, Numeric]
61
65
  # @return [Loxxy::Datatype::Number]
66
+ # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
62
67
  def /(other)
63
68
  case other
64
- when Number
65
- self.class.new(value / other.value)
66
- when Numeric
67
- self.class.new(value / other)
69
+ when Number, Numeric
70
+ if other.zero?
71
+ if zero?
72
+ # NaN case detected
73
+ self.class.new(0.0 / 0.0)
74
+ else
75
+ raise ZeroDivisionError
76
+ end
77
+ elsif other.kind_of?(Number)
78
+ self.class.new(value / other.value)
79
+ else
80
+ self.class.new(value / other)
81
+ end
68
82
  else
69
83
  err_msg = "'/': Operands must be numbers."
70
84
  raise TypeError, err_msg
71
85
  end
72
86
  end
87
+ # rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
73
88
 
74
89
  # Unary minus (return value with changed sign)
75
90
  # @return [Loxxy::Datatype::Number]
@@ -84,7 +84,7 @@ module Loxxy
84
84
  token = _next_token
85
85
  tok_sequence << token unless token.nil?
86
86
  end
87
- tok_sequence << build_token('EOF', '')
87
+ tok_sequence << build_token('EOF', nil)
88
88
 
89
89
  return tok_sequence
90
90
  end
@@ -99,16 +99,16 @@ module Loxxy
99
99
 
100
100
  token = nil
101
101
 
102
- if '(){},.;/*'.include? curr_ch
102
+ if '(){},.;-/*'.include? curr_ch
103
103
  # Single delimiter or separator character
104
104
  token = build_token(@@lexeme2name[curr_ch], scanner.getch)
105
- elsif (lexeme = scanner.scan(/[+\-](?!\d)/))
105
+ elsif (lexeme = scanner.scan(/\+(?!\d)/))
106
106
  # Minus or plus character not preceding a digit
107
107
  token = build_token(@@lexeme2name[lexeme], lexeme)
108
108
  elsif (lexeme = scanner.scan(/[!=><]=?/))
109
109
  # One or two special character tokens
110
110
  token = build_token(@@lexeme2name[lexeme], lexeme)
111
- elsif (lexeme = scanner.scan(/-?\d+(?:\.\d+)?/))
111
+ elsif (lexeme = scanner.scan(/\d+(?:\.\d+)?/))
112
112
  token = build_token('NUMBER', lexeme)
113
113
  elsif (lexeme = scanner.scan(/"(?:\\"|[^"])*"/))
114
114
  token = build_token('STRING', lexeme)
@@ -133,7 +133,8 @@ module Loxxy
133
133
  def build_token(aSymbolName, aLexeme)
134
134
  begin
135
135
  (value, symb) = convert_to(aLexeme, aSymbolName)
136
- col = scanner.pos - aLexeme.size - @line_start + 1
136
+ lex_length = aLexeme ? aLexeme.size : 0
137
+ col = scanner.pos - lex_length - @line_start + 1
137
138
  pos = Rley::Lexical::Position.new(@lineno, col)
138
139
  if value
139
140
  token = Literal.new(value, aLexeme.dup, symb, pos)
data/lib/loxxy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.2.05'
4
+ VERSION = '0.2.06'
5
5
  end
@@ -124,18 +124,15 @@ LOX_END
124
124
 
125
125
  it 'should recognize number values' do
126
126
  input = <<-LOX_END
127
- 123 987654
128
- 0 -0
129
- 123.456 -0.001
130
- LOX_END
127
+ 123 987654
128
+ 0 123.456
129
+ LOX_END
131
130
 
132
131
  expectations = [
133
132
  ['123', 123],
134
133
  ['987654', 987654],
135
134
  ['0', 0],
136
- ['-0', 0],
137
- ['123.456', 123.456],
138
- ['-0.001', -0.001]
135
+ ['123.456', 123.456]
139
136
  ]
140
137
 
141
138
  subject.start_with(input)
@@ -149,6 +146,30 @@ LOX_END
149
146
  end
150
147
  end
151
148
 
149
+ it 'should recognize negative number values' do
150
+ input = <<-LOX_END
151
+ -0
152
+ -0.001
153
+ LOX_END
154
+
155
+ expectations = [
156
+ ['-', '0'],
157
+ ['-', '0.001']
158
+ ].flatten
159
+
160
+ subject.start_with(input)
161
+ tokens = subject.tokens
162
+ tokens.pop
163
+ i = 0
164
+ tokens.each_slice(2) do |(sign, lit)|
165
+ expect(sign.terminal).to eq('MINUS')
166
+ expect(sign.lexeme).to eq(expectations[i])
167
+ expect(lit.terminal).to eq('NUMBER')
168
+ expect(lit.lexeme).to eq(expectations[i + 1])
169
+ i += 2
170
+ end
171
+ end
172
+
152
173
  it 'should recognize leading and trailing dots as distinct tokens' do
153
174
  input = '.456 123.'
154
175
 
@@ -148,6 +148,19 @@ module Loxxy
148
148
  end
149
149
  end
150
150
 
151
+ it 'should ignore spaces surrounding minus in subtraction of two numbers' do
152
+ [
153
+ ['1 - 1;', 0],
154
+ ['1 -1;', 0],
155
+ ['1- 1;', 0],
156
+ ['1-1;', 0]
157
+ ].each do |(source, predicted)|
158
+ lox = Loxxy::Interpreter.new
159
+ result = lox.evaluate(source)
160
+ expect(result.value == predicted).to be_truthy
161
+ end
162
+ end
163
+
151
164
  it 'should evaluate the negation of an object' do
152
165
  [
153
166
  ['!true;', false],
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.05
4
+ version: 0.2.06
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-26 00:00:00.000000000 Z
11
+ date: 2021-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley