loxxy 0.3.02 → 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: a0e398714d64763c288f06c315611c86bf3dae2e3403055ae3ca240a10bd292f
4
- data.tar.gz: 8db95e837824181e1794277dc3e67e4005432006877124bbd62ffd07def57591
3
+ metadata.gz: 734fc040d2487c17abd8d89d4f3422e4d55db226ef79b354e52402fc24cd61fc
4
+ data.tar.gz: e799c5d1044159e9bfdc1b337c370e95878c85752dcaaf60578e71c4763a7da1
5
5
  SHA512:
6
- metadata.gz: caf2aab9a8c03997fbde67467b7cb66a786c2fb8ef46fc24869c42ac7da76831f5e771a282c938ec83585666de801602ffc9818fc489454ec93dec9bd76bf5a7
7
- data.tar.gz: 9d98db7d9bddec915929dc1cbaed057248ccb5e0cc63efc3d0d7e99536ea9b330925d91d6b1ffd6805194dc3805b0025228f66d11f11b5455cb8f52d7753e6eb
6
+ metadata.gz: d6d393729d977e6493979f4e89c664a1bba685b08c67a5e9e9b0d12354ac552deadae4e15a213560b752bdb9b44c7a05a5652fc8b4ed6500409fa04fe3cf29c1
7
+ data.tar.gz: f3b121e4a5ca07b4297aca8c43a3325b00dd30fb131f412a17a5e82d7887e8fc8749a2e1a422c75fe0c1045652970a121a78883b06c6bbc6baa6d896b0fea03f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
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
+
1
11
  ## [0.3.02] - 2021-05-22
2
12
  - New built-in expressions `getc`, `chr`, `exit` and `print_eeror` , fixes with deeply nested returns, set expressions
3
13
 
@@ -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')
@@ -326,7 +326,10 @@ module Loxxy
326
326
  def after_variable_expr(aVarExpr, aVisitor)
327
327
  var_name = aVarExpr.name
328
328
  var = variable_lookup(aVarExpr)
329
- 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
330
333
 
331
334
  var.value.accept(aVisitor) # Evaluate variable value then push on stack
332
335
  end
@@ -47,11 +47,10 @@ module Loxxy
47
47
 
48
48
  def validated_value(aValue)
49
49
  unless aValue.is_a?(String)
50
- raise StandardError, "Invalid number value #{aValue}"
50
+ raise StandardError, "Invalid string value #{aValue}"
51
51
  end
52
52
 
53
- # Remove double quotes delimiter
54
- aValue.gsub(/(^")|("$)/, '')
53
+ aValue
55
54
  end
56
55
  end # class
57
56
  end # module
@@ -61,6 +61,20 @@ module Loxxy
61
61
  print return super this true var while
62
62
  ].map { |x| [x, x] }.to_h
63
63
 
64
+ # Single character that have a special meaning when escaped
65
+ # @return [{Char => String}]
66
+ @@escape_chars = {
67
+ ?a => "\a",
68
+ ?b => "\b",
69
+ ?e => "\e",
70
+ ?f => "\f",
71
+ ?n => "\n",
72
+ ?r => "\r",
73
+ ?s => "\s",
74
+ ?t => "\t",
75
+ ?v => "\v"
76
+ }.freeze
77
+
64
78
  # Constructor. Initialize a tokenizer for Lox input.
65
79
  # @param source [String] Lox text to tokenize.
66
80
  def initialize(source = nil)
@@ -104,18 +118,14 @@ module Loxxy
104
118
  elsif (lexeme = scanner.scan(/[!=><]=?/))
105
119
  # One or two special character tokens
106
120
  token = build_token(@@lexeme2name[lexeme], lexeme)
121
+ elsif scanner.scan(/"/) # Start of string detected...
122
+ token = build_string_token
107
123
  elsif (lexeme = scanner.scan(/\d+(?:\.\d+)?/))
108
124
  token = build_token('NUMBER', lexeme)
109
- elsif (lexeme = scanner.scan(/"(?:\\"|[^"])*"/))
110
- token = build_token('STRING', lexeme)
111
125
  elsif (lexeme = scanner.scan(/[a-zA-Z_][a-zA-Z_0-9]*/))
112
126
  keyw = @@keywords[lexeme]
113
127
  tok_type = keyw ? keyw.upcase : 'IDENTIFIER'
114
128
  token = build_token(tok_type, lexeme)
115
- elsif scanner.scan(/"(?:\\"|[^"])*\z/)
116
- # Error: unterminated string...
117
- col = scanner.pos - @line_start + 1
118
- raise ScanError, "Error: [line #{lineno}:#{col}]: Unterminated string."
119
129
  else # Unknown token
120
130
  col = scanner.pos - @line_start + 1
121
131
  _erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
@@ -153,8 +163,6 @@ module Loxxy
153
163
  value = Datatype::Nil.instance
154
164
  when 'NUMBER'
155
165
  value = Datatype::Number.new(aLexeme)
156
- when 'STRING'
157
- value = Datatype::LXString.new(unescape_string(aLexeme))
158
166
  when 'TRUE'
159
167
  value = Datatype::True.instance
160
168
  else
@@ -164,27 +172,47 @@ module Loxxy
164
172
  return [value, symb]
165
173
  end
166
174
 
167
- # Replace any sequence sequence by their "real" value.
168
- def unescape_string(aText)
169
- result = +''
170
- previous = nil
171
-
172
- aText.each_char do |ch|
173
- if previous
174
- if ch == ?n
175
- result << "\n"
176
- else
177
- result << ch
178
- end
179
- previous = nil
180
- elsif ch == '\\'
181
- previous = ?\
175
+ # precondition: current position at leading quote
176
+ def build_string_token
177
+ scan_pos = scanner.pos
178
+ line = @lineno
179
+ column_start = scan_pos - @line_start
180
+ literal = +''
181
+ loop do
182
+ substr = scanner.scan(/[^"\\\r\n]*/)
183
+ if scanner.eos?
184
+ pos_start = "line #{line}:#{column_start}"
185
+ raise ScanError, "Error: [#{pos_start}]: Unterminated string."
182
186
  else
183
- result << ch
187
+ literal << substr
188
+ special = scanner.scan(/["\\\r\n]/)
189
+ case special
190
+ when '"' # Terminating quote found
191
+ break
192
+ when "\r"
193
+ next_line
194
+ special << scanner.scan(/./) if scanner.match?(/\n/)
195
+ literal << special
196
+ when "\n"
197
+ next_line
198
+ literal << special
199
+ when '\\'
200
+ ch = scanner.scan(/./)
201
+ next unless ch
202
+
203
+ escaped = @@escape_chars[ch]
204
+ if escaped
205
+ literal << escaped
206
+ else
207
+ literal << ch
208
+ end
209
+ end
184
210
  end
185
211
  end
186
-
187
- result
212
+ pos = Rley::Lexical::Position.new(line, column_start)
213
+ lox_string = Datatype::LXString.new(literal)
214
+ lexeme = scanner.string[scan_pos - 1..scanner.pos - 1]
215
+ Literal.new(lox_string, lexeme, 'STRING', pos)
188
216
  end
189
217
 
190
218
  # Skip non-significant whitespaces and comments.
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.3.02'
4
+ VERSION = '0.3.03'
5
5
  end
@@ -211,22 +211,25 @@ LOX_END
211
211
  end
212
212
 
213
213
  it 'should recognize escaped quotes' do
214
- embedded_quotes = %q{she said: \"Hello\"}
215
- result = subject.send(:unescape_string, embedded_quotes)
216
- expect(result).to eq('she said: "Hello"')
214
+ embedded_quotes = %q{"she said: \"Hello\""}
215
+ subject.start_with(embedded_quotes)
216
+ result = subject.tokens[0]
217
+ expect(result.value).to eq('she said: "Hello"')
217
218
  end
218
219
 
219
220
  it 'should recognize escaped backslash' do
220
- embedded_backslash = 'backslash>\\\\'
221
- result = subject.send(:unescape_string, embedded_backslash)
222
- expect(result).to eq('backslash>\\')
221
+ embedded_backslash = '"backslash>\\\\"'
222
+ subject.start_with(embedded_backslash)
223
+ result = subject.tokens[0]
224
+ expect(result.value).to eq('backslash>\\')
223
225
  end
224
226
 
225
227
  # rubocop: disable Style/StringConcatenation
226
228
  it 'should recognize newline escape sequence' do
227
- embedded_newline = 'line1\\nline2'
228
- result = subject.send(:unescape_string, embedded_newline)
229
- expect(result).to eq('line1' + "\n" + 'line2')
229
+ embedded_newline = '"line1\\nline2"'
230
+ subject.start_with(embedded_newline)
231
+ result = subject.tokens[0]
232
+ expect(result.value).to eq('line1' + "\n" + 'line2')
230
233
  end
231
234
  # rubocop: enable Style/StringConcatenation
232
235
 
@@ -289,7 +292,7 @@ LOX_END
289
292
  it 'should complain if it finds an unterminated string' do
290
293
  subject.start_with('var a = "Unfinished;')
291
294
  err = Loxxy::ScanError
292
- err_msg = 'Error: [line 1:21]: Unterminated string.'
295
+ err_msg = 'Error: [line 1:9]: Unterminated string.'
293
296
  expect { subject.tokens }.to raise_error(err, err_msg)
294
297
  end
295
298
 
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.3.02
4
+ version: 0.3.03
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-05-22 00:00:00.000000000 Z
11
+ date: 2021-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley