loxxy 0.3.03 → 0.4.00
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 +13 -4
- data/README.md +63 -5
- data/lib/loxxy/ast/ast_visitor.rb +0 -1
- data/lib/loxxy/ast/lox_class_stmt.rb +8 -5
- data/lib/loxxy/ast/lox_fun_stmt.rb +12 -5
- data/lib/loxxy/back_end/engine.rb +3 -1
- data/lib/loxxy/back_end/lox_instance.rb +14 -2
- data/lib/loxxy/back_end/resolver.rb +5 -0
- data/lib/loxxy/version.rb +1 -1
- 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: 47c5d65fa1f3c2d0025013577f46edf741572df743d8986e238f49a08ea32702
|
4
|
+
data.tar.gz: 72a3beb6828362e6dc7c73072555ed6f87ed5bf57b4052ac55e06503b9582e29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e83ee511c1ead4592153bb1e8ed8167439894bfefa2bb54060100eb9d221d77c453b4bad3d3e4a36209f3761812a5b9b0aa98b02a53556314c022d1f0b605bca
|
7
|
+
data.tar.gz: 8d939d7ff60687dd64c4dc6e4beccffa512e3483f5b38042a83fffbc0a5cc83d218999f855b8765a47e24b8c0df7808ecd649e70cec11832a0591b5c141a659c
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,21 @@
|
|
1
|
-
## [0.
|
2
|
-
-
|
1
|
+
## [0.4.00] - 2021-05-24
|
2
|
+
- Version bump. `Loxxy` is capable to run the LoxLox interpreter, an interpreter written in `Lox`.
|
3
|
+
|
4
|
+
### New
|
5
|
+
- Method `BackEnd::LoxInstance#falsey?` added
|
6
|
+
- Method `BackEnd::LoxInstance#truthy?` added
|
3
7
|
|
4
8
|
### Changed
|
5
9
|
- Method `BackEnd::Engine#after_variable_expr` the error message `Undefined variable` nows gives the location of the offending variable.
|
6
|
-
- Class `
|
10
|
+
- Class `Ast::LoxClassStmt`is now a subclass of `LoxNode`
|
11
|
+
|
12
|
+
- File `README.md` added an explanation on how to run `LoxLox`interpreter.
|
7
13
|
|
8
14
|
### Fixed
|
9
|
-
- Method `Ast::
|
15
|
+
- Method `Ast::LoxClassStmt#initialize` fixed inconsistencies in its Yard/RDoc documentation.
|
16
|
+
- Method `Ast::LoxFunStmt#initialize` fixed inconsistencies in its Yard/RDoc documentation.
|
17
|
+
- Method `BackEnd::Engine#native_getc` now returns -1 when its reaches EOF.
|
18
|
+
- Method `BackEnd::Resolver#after_logical_expr` was missing and this caused the lack of resultation in the second operand.
|
10
19
|
|
11
20
|
## [0.3.02] - 2021-05-22
|
12
21
|
- New built-in expressions `getc`, `chr`, `exit` and `print_eeror` , fixes with deeply nested returns, set expressions
|
data/README.md
CHANGED
@@ -19,7 +19,8 @@ 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
|
+
- Passes the `jox` (THE reference `Lox` implementation) test suite
|
23
|
+
- Can run a Lox imterpreter implemented in ... `Lox` [LoxLox](https://github.com/benhoyt/loxlox),
|
23
24
|
- Minimal runtime dependency (Rley gem). Won't drag a bunch of gems...
|
24
25
|
- Ruby API for integrating a Lox interpreter with your code.
|
25
26
|
- A command-line interpreter `loxxy`
|
@@ -53,6 +54,7 @@ And then execute:
|
|
53
54
|
### 2. Your first `Lox` program
|
54
55
|
Create a text file and enter the following lines:
|
55
56
|
```javascript
|
57
|
+
// hello.lox
|
56
58
|
// Your firs Lox program
|
57
59
|
print "Hello, world.";
|
58
60
|
```
|
@@ -75,6 +77,7 @@ Let's admit it, the hello world example was unimpressive.
|
|
75
77
|
To a get a taste of `Lox` object-oriented capabilities, let's try another `Hello world` variant:
|
76
78
|
|
77
79
|
```javascript
|
80
|
+
// oo_hello.lox
|
78
81
|
// Object-oriented hello world
|
79
82
|
class Greeter {
|
80
83
|
// in Lox, initializers/constructors are named `init`
|
@@ -98,6 +101,7 @@ Our next assignment: compute the first 20 elements of the Fibbonacci sequence.
|
|
98
101
|
Here's an answer using the `while` loop construct:
|
99
102
|
|
100
103
|
```javascript
|
104
|
+
// fibbonacci.lox
|
101
105
|
// Compute the first 20 elements from the Fibbonacci sequence
|
102
106
|
|
103
107
|
var a = 0; // Use the var keyword to declare a new variable
|
@@ -127,6 +131,7 @@ Fans of `for` loops will be pleased to find their favorite looping construct.
|
|
127
131
|
Here again, the Fibbonacci sequence refactored with a `for` loop:
|
128
132
|
|
129
133
|
```javascript
|
134
|
+
// fibbonacci_v2.lox
|
130
135
|
// Fibbonacci sequence - version 2
|
131
136
|
var a = 0;
|
132
137
|
var b = 1;
|
@@ -169,13 +174,69 @@ for (var i = 0; i < count; i = i + 1) {
|
|
169
174
|
}
|
170
175
|
```
|
171
176
|
|
177
|
+
### Loxxy goes meta...
|
178
|
+
The `Loxxy` is able to run the `LoxLox` interpreter.
|
179
|
+
[LoxLox](https://github.com/benhoyt/loxlox) is a Lox interpreter written in Lox by Ben Hoyt.
|
180
|
+
This interpreter with over 1900 lines long is (one of) the longest Lox pragram.
|
181
|
+
As such, it is a good testbed for any Lox interpreter.
|
182
|
+
|
183
|
+
Executing a lox program with the LoxLox interpreter that is itself running on top of Loxxy.
|
184
|
+
#### Step 1 Download `lox.lox´ file
|
185
|
+
Download the [LoxLox](https://github.com/benhoyt/loxlox) source file in Github.
|
186
|
+
|
187
|
+
#### Step 2 (alternative a): running from the command line
|
188
|
+
|
189
|
+
```
|
190
|
+
$ loxxy lox.lox
|
191
|
+
```
|
192
|
+
|
193
|
+
Once loxxy CLI starts its interpreter that, in turn, executes the LoxLox interpreter.
|
194
|
+
This may take a couple of seconds.
|
195
|
+
Don't be surprised, if the program seems unresponsive: it is waiting for you input.
|
196
|
+
Enter a line like this:
|
197
|
+
```
|
198
|
+
print "Hello, world!";
|
199
|
+
```
|
200
|
+
Then terminate with an end of file (crtl-D on Linuxes, crtl-z on Windows) followed by an enter key.
|
201
|
+
You should see the famous greeting.
|
202
|
+
|
203
|
+
#### Step 2 (alternative b): launching the interpreter from Ruby snippet
|
204
|
+
The following snippet executes the LoxLox interpreter and feeds to it the
|
205
|
+
input text. That input text is made available through a StringIO that replaces
|
206
|
+
the `$stdio` device.
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
require 'stringio'
|
210
|
+
require 'loxxy'
|
211
|
+
|
212
|
+
# Place your Lox pragram within the heredoc
|
213
|
+
program = <<-LOX_END
|
214
|
+
print "Hello, world!";
|
215
|
+
LOX_END
|
216
|
+
|
217
|
+
lox_filename = 'lox.lox'
|
218
|
+
File.open(lox_filename, 'r') do |f|
|
219
|
+
source = f.read
|
220
|
+
cfg = { istream: StringIO.new(program, 'r')}
|
221
|
+
lox = Loxxy::Interpreter.new(cfg)
|
222
|
+
lox.evaluate(source)
|
223
|
+
end
|
224
|
+
```
|
225
|
+
|
226
|
+
Save this snippet as a Ruby file, launch Ruby with this file in command line.
|
227
|
+
After a couple of seconds, you'll see the Ruby interpreter that executes the
|
228
|
+
Loxxy interpreter that itself executes the LoxLox interpreter written in Lox.
|
229
|
+
That last interpreter is the one that run the hello world line.
|
230
|
+
|
231
|
+
That's definitively meta...
|
232
|
+
|
172
233
|
This completes our quick tour of `Lox`, to learn more about the language,
|
173
234
|
check the online book [Crafting Interpreters](https://craftinginterpreters.com/ )
|
174
235
|
|
175
236
|
## What's the fuss about Lox?
|
176
237
|
... Nothing...
|
177
238
|
Bob Nystrom designed a language __simple__ enough so that he could present
|
178
|
-
two implementations (
|
239
|
+
two interpreter implementations (a tree-walking one, then a bytecode one) in one single book.
|
179
240
|
|
180
241
|
In other words, __Lox__ contains interesting features found in most general-purpose
|
181
242
|
languages. In addition to that, there are [numerous implementations](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations) in different languages
|
@@ -202,14 +263,11 @@ There are already a number of programming languages derived from `Lox`...
|
|
202
263
|
### Purpose of this project:
|
203
264
|
- To deliver an open source example of a programming language fully implemented in Ruby
|
204
265
|
(from the scanner and parser to an interpreter).
|
205
|
-
- The implementation should be mature enough to run [LoxLox](https://github.com/benhoyt/loxlox),
|
206
|
-
a Lox interpreter written in Lox.
|
207
266
|
|
208
267
|
### Roadmap
|
209
268
|
- Extend the test suite
|
210
269
|
- Improve the error handling
|
211
270
|
- Improve the documentation
|
212
|
-
- Ability run the LoxLox interpreter
|
213
271
|
|
214
272
|
## Hello world example
|
215
273
|
The next examples show how to use the interpreter directly from Ruby code.
|
@@ -71,7 +71,6 @@ module Loxxy
|
|
71
71
|
# @param aClassStmt [AST::LOXClassStmt] the for statement node to visit
|
72
72
|
def visit_class_stmt(aClassStmt)
|
73
73
|
broadcast(:before_class_stmt, aClassStmt)
|
74
|
-
traverse_subnodes(aClassStmt) # The methods are visited here...
|
75
74
|
broadcast(:after_class_stmt, aClassStmt, self)
|
76
75
|
end
|
77
76
|
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'lox_node'
|
4
4
|
|
5
5
|
module Loxxy
|
6
6
|
module Ast
|
7
|
-
|
7
|
+
# A parse tree node that represents a Lox class declaration.
|
8
|
+
class LoxClassStmt < LoxNode
|
8
9
|
# @return [String] the class name
|
9
10
|
attr_reader :name
|
10
11
|
|
@@ -14,11 +15,13 @@ module Loxxy
|
|
14
15
|
# @return [Array<Ast::LoxFunStmt>] the methods
|
15
16
|
attr_reader :body
|
16
17
|
|
18
|
+
# Constructor for a parse node that represents a Lox function declaration
|
17
19
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
18
|
-
# @param
|
19
|
-
# @param
|
20
|
+
# @param aName [String] the class name
|
21
|
+
# @param aSuperclassName [String] the super class name
|
22
|
+
# @param theMethods [Array<Loxxy::Ast::LoxFunStmt>] the methods
|
20
23
|
def initialize(aPosition, aName, aSuperclassName, theMethods)
|
21
|
-
super(aPosition
|
24
|
+
super(aPosition)
|
22
25
|
@name = aName.dup
|
23
26
|
@superclass = aSuperclassName
|
24
27
|
@body = theMethods
|
@@ -4,17 +4,25 @@ require_relative 'lox_node'
|
|
4
4
|
|
5
5
|
module Loxxy
|
6
6
|
module Ast
|
7
|
-
#
|
7
|
+
# A parse tree node that represents a Lox function declaration.
|
8
8
|
class LoxFunStmt < LoxNode
|
9
|
+
# @return [String] the function name
|
9
10
|
attr_reader :name
|
11
|
+
|
12
|
+
# @return [Array<String>] the parameter names
|
10
13
|
attr_reader :params
|
14
|
+
|
15
|
+
# @return [Ast::LoxBlockStmt] the parse tree representing the function's body
|
11
16
|
attr_reader :body
|
17
|
+
|
18
|
+
# @return [Boolean] true if the function is a method
|
12
19
|
attr_accessor :is_method
|
13
20
|
|
21
|
+
# Constructor for a parse node that represents a Lox function declaration
|
14
22
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
|
-
# @param aName [String]
|
16
|
-
# @param
|
17
|
-
# @param
|
23
|
+
# @param aName [String] the function name
|
24
|
+
# @param paramList [Array<String>] the parameter names
|
25
|
+
# @param aBody [Ast::LoxBlockStmt] the parse tree representing the function's body
|
18
26
|
def initialize(aPosition, aName, paramList, aBody)
|
19
27
|
super(aPosition)
|
20
28
|
@name = aName.dup
|
@@ -25,6 +33,5 @@ module Loxxy
|
|
25
33
|
|
26
34
|
define_accept # Add `accept` method as found in Visitor design pattern
|
27
35
|
end # class
|
28
|
-
# rubocop: enable Style/AccessorGrouping
|
29
36
|
end # module
|
30
37
|
end # module
|
@@ -475,10 +475,12 @@ module Loxxy
|
|
475
475
|
end
|
476
476
|
|
477
477
|
# Read a single character and return the character code as an integer.
|
478
|
+
# LoxLox requires the end of input to be a negative number
|
478
479
|
def native_getc
|
479
480
|
proc do
|
480
481
|
ch = @istream.getc
|
481
|
-
|
482
|
+
val = ch ? ch.codepoints[0] : -1
|
483
|
+
Datatype::Number.new(val)
|
482
484
|
end
|
483
485
|
end
|
484
486
|
|
@@ -22,8 +22,16 @@ module Loxxy
|
|
22
22
|
@fields = {}
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
# In Lox, only false and Nil have false value...
|
26
|
+
# @return [FalseClass]
|
27
|
+
def falsey?
|
28
|
+
false # Default implementation
|
29
|
+
end
|
30
|
+
|
31
|
+
# Any instance is truthy
|
32
|
+
# @return [TrueClass]
|
33
|
+
def truthy?
|
34
|
+
true # Default implementation
|
27
35
|
end
|
28
36
|
|
29
37
|
# Text representation of a Lox instance
|
@@ -31,6 +39,10 @@ module Loxxy
|
|
31
39
|
"#{klass.to_str} instance"
|
32
40
|
end
|
33
41
|
|
42
|
+
def accept(_visitor)
|
43
|
+
engine.expr_stack.push self
|
44
|
+
end
|
45
|
+
|
34
46
|
# Look up the value of property with given name
|
35
47
|
# aName [String] name of object property
|
36
48
|
def get(aName)
|
@@ -138,6 +138,11 @@ module Loxxy
|
|
138
138
|
aSetExpr.object.accept(aVisitor)
|
139
139
|
end
|
140
140
|
|
141
|
+
def after_logical_expr(aLogicalExpr, aVisitor)
|
142
|
+
# Force the visit of second operand (resolver should ignore shortcuts)
|
143
|
+
aLogicalExpr.operands.last.accept(aVisitor)
|
144
|
+
end
|
145
|
+
|
141
146
|
# Variable expressions require their variables resolved
|
142
147
|
def before_variable_expr(aVarExpr)
|
143
148
|
var_name = aVarExpr.name
|
data/lib/loxxy/version.rb
CHANGED
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.
|
4
|
+
version: 0.4.00
|
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-
|
11
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|