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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +13 -10
- data/bin/loxxy +9 -5
- data/lib/loxxy/error.rb +3 -0
- data/lib/loxxy/front_end/scanner.rb +32 -7
- data/lib/loxxy/version.rb +1 -1
- data/spec/front_end/scanner_spec.rb +34 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afc822710c5023cdffd8eeb9e9316736f2b2d9ddb2d05e4c4e835dee9b9b4d8b
|
4
|
+
data.tar.gz: 5debd7264833555cc34c64586de96d4e811eef4ebebe25f6c42a0832dee8e433
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
[](https://ci.appveyor.com/project/famished-tiger/loxxy)
|
4
4
|
[](https://github.com/famished-tiger/loxxy/blob/main/LICENSE.txt)
|
5
5
|
|
6
|
-
|
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
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
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
@@ -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.
|
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-
|
11
|
+
date: 2021-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|