loxxy 0.2.01 → 0.2.02

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: 3137f0d16bb2c3dbbc4e2036abbf45888ba06b863fef69a8682898ffdb2a146b
4
- data.tar.gz: 344ff5f99e7d64dd45074aaacde5622ecb676d8d3112773555b0fbcffbd5aa89
3
+ metadata.gz: afc822710c5023cdffd8eeb9e9316736f2b2d9ddb2d05e4c4e835dee9b9b4d8b
4
+ data.tar.gz: 5debd7264833555cc34c64586de96d4e811eef4ebebe25f6c42a0832dee8e433
5
5
  SHA512:
6
- metadata.gz: f3cd447081d53619afeb040d9eade7fab0c0d5db79b8407fba412df727fb72849e5671679d7fd13fded703b9009399febfac6506ef91ac0bdd3220ef26472408
7
- data.tar.gz: 4a83b84a9853787369d51f48d7c2eacfe8a7e58b4ab8cb5d5322b02e0a5de68c4481effa61587db70f066902d21c7b2a310509de74d13f2cf1476a490e2728a7
6
+ metadata.gz: d988772978387faa28692101e6b2dab580a93b6f5738ad9094faf22d8aed78aca68123982ff0a25d9b9029d4d27ef4f82788009b10942a14ce5dd9bd9f1e37a7
7
+ data.tar.gz: 711b3188b0dd14a9821eee5227ee0d574c60144b7be88a3f722d10b4506556fa864685659c7493acfd6d63c2cf82f8c0c6c36cc0431a57a2d476efe3b4bf7efd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## [0.2.02] - 2021-04-21
2
+ - Improvements in the scanner class (escape sequence for quotes and newlines), error messages closer to jlox.
3
+
4
+ ### Changed
5
+ - File `loxxy` executable doesn't show a stack trace for scanner errors
6
+ - Class `ScannerError` is now a subclass of `Loxxy::Error`
7
+ - Class `Scanner` now returns a specific error message for unterminated strings
8
+ - Class `Scanner` error messages are closer to the ones from jlox
9
+ - Class `Scanner` supports now escape sequences \" for double quotes, \n for newlines
10
+ - File `README.md` Reshuffled some text
11
+
1
12
  ## [0.2.01] - 2021-04-18
2
13
  - Minor improvements in CLI, starting re-documenting `README.md`.
3
14
 
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build status](https://ci.appveyor.com/api/projects/status/8e5p7dgjanm0qjkp?svg=true)](https://ci.appveyor.com/project/famished-tiger/loxxy)
4
4
  [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/famished-tiger/loxxy/blob/main/LICENSE.txt)
5
5
 
6
- ### What is loxxy?
6
+ ## What is loxxy?
7
7
  A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html ),
8
8
  a simple language defined in Bob Nystrom's excellent online book [Crafting Interpreters](https://craftinginterpreters.com/ ).
9
9
 
@@ -16,6 +16,14 @@ Although __Lox__ is fairly simple, it is far from being a toy language:
16
16
  - Functions and closures
17
17
  - Object-orientation (classes, methods, inheritance).
18
18
 
19
+ ### Loxxy gem features
20
+ - Complete tree-walking interpreter including lexer, parser and resolver
21
+ - 100% pure Ruby with clean design (not a port from some other language)
22
+ - Minimal runtime dependency (Rley gem). Won't drag a bunch of gems...
23
+ - Ruby API for integrating a Lox interpreter with your code.
24
+ - A command-line interpreter `loxxy`
25
+ - Open for your language extensions
26
+
19
27
  ## How to start in 1, 2, 3...?
20
28
  ... in less than 3 minutes...
21
29
 
@@ -42,10 +50,11 @@ Lo and behold! The output device displays the famous greeting:
42
50
  Hello, world.
43
51
 
44
52
 
45
- Congrats! You ran your first `Lox` program with __Loxxy__.
53
+ Congrats! You ran your first `Lox` program thanks __Loxxy__ gem.
54
+ For a less superficial encounter with the language jump to the next section.
46
55
 
47
- ### Something beefier?...
48
- Let's admit it, the hello world example was unimpressive.
56
+ ## So you want something beefier?...
57
+ Let's admit it, the hello world example was unimpressive.
49
58
  To a get a taste of `Lox` object-oriented capabilities, let's try another `Hello world` variant:
50
59
 
51
60
  ```javascript
@@ -172,12 +181,6 @@ Indeed, an open-source language that misses some features is an invitation for t
172
181
  There are already a number of programming languages derived from `Lox`...
173
182
 
174
183
  ## Why `Loxxy`? What's in it for me?...
175
- Features:
176
- - Complete tree-walking interpreter including lexer, parser and resolver
177
- - 100% pure Ruby with clean design (not a port from some other language)
178
- - Ruby API for integrating a Lox interpreter with your code.
179
- - Minimal runtime dependency (Rley gem). Won't drag a bunch of gems...
180
-
181
184
 
182
185
  ### Purpose of this project:
183
186
  - To deliver an open source example of a programming language fully implemented in Ruby
data/bin/loxxy CHANGED
@@ -17,11 +17,15 @@ class LoxxyRunner
17
17
  return if file_names.nil? || file_names.empty?
18
18
 
19
19
  lox = Loxxy::Interpreter.new
20
- file_names.each do |lox_file|
21
- fname = validate_filename(lox_file)
22
- next unless file_exist?(fname)
23
-
24
- File.open(fname, 'r') { |f| lox.evaluate(f.read) }
20
+ begin
21
+ file_names.each do |lox_file|
22
+ fname = validate_filename(lox_file)
23
+ next unless file_exist?(fname)
24
+
25
+ File.open(fname, 'r') { |f| lox.evaluate(f.read) }
26
+ end
27
+ rescue Loxxy::ScanError => e
28
+ $stderr.puts e.message
25
29
  end
26
30
  end
27
31
 
data/lib/loxxy/error.rb CHANGED
@@ -7,6 +7,9 @@ module Loxxy
7
7
  # Error occurring while Loxxy executes some invalid Lox code.
8
8
  class RuntimeError < Error; end
9
9
 
10
+ # Error occurring while Loxxy scans invalid input.
11
+ class ScanError < Error; end
12
+
10
13
  # Error occurring while Loxxy parses some invalid Lox code.
11
14
  class SyntaxError < Error; end
12
15
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'strscan'
4
4
  require 'rley'
5
+ require_relative '../error'
5
6
  require_relative '../datatype/all_datatypes'
6
7
  require_relative 'literal'
7
8
 
@@ -60,8 +61,6 @@ module Loxxy
60
61
  PRINT RETURN SUPER THIS TRUE VAR WHILE
61
62
  ].map { |x| [x, x] }.to_h
62
63
 
63
- class ScanError < StandardError; end
64
-
65
64
  # Constructor. Initialize a tokenizer for Lox input.
66
65
  # @param source [String] Lox text to tokenize.
67
66
  def initialize(source = nil)
@@ -117,11 +116,14 @@ module Loxxy
117
116
  keyw = @@keywords[lexeme.upcase]
118
117
  tok_type = keyw || 'IDENTIFIER'
119
118
  token = build_token(tok_type, lexeme)
119
+ elsif scanner.scan(/"(?:\\"|[^"])*\z/)
120
+ # Error: unterminated string...
121
+ col = scanner.pos - @line_start + 1
122
+ raise ScanError, "Error: [line #{lineno}:#{col}]: Unterminated string."
120
123
  else # Unknown token
121
- erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
122
- sequel = scanner.scan(/.{1,20}/)
123
- erroneous += sequel unless sequel.nil?
124
- raise ScanError, "Unknown token #{erroneous} on line #{lineno}"
124
+ col = scanner.pos - @line_start + 1
125
+ _erroneous = curr_ch.nil? ? '' : scanner.scan(/./)
126
+ raise ScanError, "Error: [line #{lineno}:#{col}]: Unexpected character."
125
127
  end
126
128
 
127
129
  return token
@@ -156,7 +158,7 @@ module Loxxy
156
158
  when 'NUMBER'
157
159
  value = Datatype::Number.new(aLexeme)
158
160
  when 'STRING'
159
- value = Datatype::LXString.new(aLexeme)
161
+ value = Datatype::LXString.new(unescape_string(aLexeme))
160
162
  when 'TRUE'
161
163
  value = Datatype::True.instance
162
164
  else
@@ -166,6 +168,29 @@ module Loxxy
166
168
  return [value, symb]
167
169
  end
168
170
 
171
+ # Replace any sequence sequence by their "real" value.
172
+ def unescape_string(aText)
173
+ result = +''
174
+ previous = nil
175
+
176
+ aText.each_char do |ch|
177
+ if previous
178
+ if ch == ?n
179
+ result << "\n"
180
+ else
181
+ result << ch
182
+ end
183
+ previous = nil
184
+ elsif ch == '\\'
185
+ previous = ?\
186
+ else
187
+ result << ch
188
+ end
189
+ end
190
+
191
+ result
192
+ end
193
+
169
194
  # Skip non-significant whitespaces and comments.
170
195
  # Advance the scanner until something significant is found.
171
196
  def skip_intertoken_spaces
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.01'
4
+ VERSION = '0.2.02'
5
5
  end
@@ -189,6 +189,26 @@ LOX_END
189
189
  end
190
190
  end
191
191
 
192
+ it 'should recognize escaped quotes' do
193
+ embedded_quotes = %q{she said: \"Hello\"}
194
+ result = subject.send(:unescape_string, embedded_quotes)
195
+ expect(result).to eq('she said: "Hello"')
196
+ end
197
+
198
+ it 'should recognize escaped backslash' do
199
+ embedded_backslash = 'backslash>\\\\'
200
+ result = subject.send(:unescape_string, embedded_backslash)
201
+ expect(result).to eq('backslash>\\')
202
+ end
203
+
204
+ # rubocop: disable Style/StringConcatenation
205
+ it 'should recognize newline escape sequence' do
206
+ embedded_newline = 'line1\\nline2'
207
+ result = subject.send(:unescape_string, embedded_newline)
208
+ expect(result).to eq('line1' + "\n" + 'line2')
209
+ end
210
+ # rubocop: enable Style/StringConcatenation
211
+
192
212
  it 'should recognize a nil token' do
193
213
  subject.start_with('nil')
194
214
  token_nil = subject.tokens[0]
@@ -237,6 +257,20 @@ LOX_END
237
257
  ]
238
258
  match_expectations(subject, expectations)
239
259
  end
260
+
261
+ it 'should complain if it finds an unterminated string' do
262
+ subject.start_with('var a = "Unfinished;')
263
+ err = Loxxy::ScanError
264
+ err_msg = 'Error: [line 1:21]: Unterminated string.'
265
+ expect { subject.tokens }.to raise_error(err, err_msg)
266
+ end
267
+
268
+ it 'should complain if it finds an unexpected character' do
269
+ subject.start_with('var a = ?1?;')
270
+ err = Loxxy::ScanError
271
+ err_msg = 'Error: [line 1:9]: Unexpected character.'
272
+ expect { subject.tokens }.to raise_error(err, err_msg)
273
+ end
240
274
  end # context
241
275
  end # describe
242
276
  end # module
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.01
4
+ version: 0.2.02
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-18 00:00:00.000000000 Z
11
+ date: 2021-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley