loxxy 0.0.9 → 0.0.10
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 +47 -8
- data/lib/loxxy/ast/ast_builder.rb +66 -36
- data/lib/loxxy/front_end/grammar.rb +3 -3
- data/lib/loxxy/version.rb +1 -1
- data/spec/front_end/parser_spec.rb +35 -2
- 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: e4f33203580d2b83ee7139e449787b439c0fb56c5a8c3dcc8035d114ffd957d3
|
4
|
+
data.tar.gz: 46c9fb2f55849c6aa9b9e4d58588746ddb3ac3353f7ed09922d9394a9fc512c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30f554af6338b50297ce6befaa62c86c8406df681e25098cc95dc28da64bb6feabbb9e085846eb8032f51d8d3655332105eb1d7165903bbd0d6be24cb13c4d0b
|
7
|
+
data.tar.gz: d9d9a69a010d16807f6c4bf24b15e8135367bb62b6d2e052943e0d6030751f282c777f652dd3fced08093aee8abae228e63d365cae156fa6e87ac7f52fc7e2e6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## [0.0.10] - 2021-01-08
|
2
|
+
- AST node generation for equality expression.
|
3
|
+
|
4
|
+
## Changed
|
5
|
+
- Class `AST::ASTBuilder` refactoring and added `reduce_` methods for equality operations.
|
6
|
+
- File `grammar.rb`added name to equality rules
|
7
|
+
- File `README.md` added gem version and license badges, expanded roadmap section.
|
8
|
+
|
9
|
+
## Fixed
|
10
|
+
- File `grammar.rb`: a rule had still the discarded non-terminal `equalityTest_star` in its lhs.
|
11
|
+
|
1
12
|
## [0.0.9] - 2021-01-07
|
2
13
|
- AST node generation for comparison expression.
|
3
14
|
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# loxxy
|
2
|
+
[](https://badge.fury.io/rb/loxxy)
|
3
|
+
[](https://github.com/famished-tiger/loxxy/blob/main/LICENSE.txt)
|
4
|
+
|
2
5
|
|
3
6
|
A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html ),
|
4
7
|
a simple language used in Bob Nystrom's online book [Crafting Interpreters](https://craftinginterpreters.com/ ).
|
@@ -6,7 +9,7 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
|
|
6
9
|
## Purpose of this project:
|
7
10
|
- To deliver an open source example of a programming language fully implemented in Ruby
|
8
11
|
(from the scanner, parser, code generation).
|
9
|
-
- The implementation should be mature enough to run (
|
12
|
+
- The implementation should be mature enough to run [LoxLox](https://github.com/benhoyt/loxlox),
|
10
13
|
a Lox interpreter written in Lox.
|
11
14
|
|
12
15
|
## Current status
|
@@ -18,12 +21,48 @@ The __loxxy__ gem hosts two distinct parsers classes (`RawParser` and `Parser`).
|
|
18
21
|
Currently it generates AST for arithmetic expressions with literal numbers only.
|
19
22
|
|
20
23
|
## Roadmap
|
21
|
-
|
22
|
-
-
|
23
|
-
-
|
24
|
-
-
|
25
|
-
-
|
26
|
-
|
24
|
+
### Done
|
25
|
+
- Scanner (tokenizer)
|
26
|
+
- Lox grammar (in format required by Rley gem)
|
27
|
+
- Raw parser. It parses Lox programs and generates a raw parse tree.
|
28
|
+
- Tailored parser for generating AST (Abstract Syntax Tree)
|
29
|
+
|
30
|
+
### Started
|
31
|
+
- Custom AST builder class
|
32
|
+
- Classes for representing Lox expressions in AST
|
33
|
+
|
34
|
+
### TODO
|
35
|
+
AST Node generation
|
36
|
+
Goal: parser should generate AST for any input Lox program
|
37
|
+
- [X] Equality operator
|
38
|
+
- [] Logical operator (and, or)
|
39
|
+
- [] Unary expressions (negate, not)
|
40
|
+
- [] Grouping expressions
|
41
|
+
- [] Print statement
|
42
|
+
- [] Simple assignment expressions
|
43
|
+
- [] Variable declaration
|
44
|
+
- [] Dot notation
|
45
|
+
- [] Block statement
|
46
|
+
- [] If statement
|
47
|
+
- [] For statement
|
48
|
+
- [] While statement
|
49
|
+
- [] Function declaration
|
50
|
+
- [] Call expression
|
51
|
+
- [] Return statement
|
52
|
+
- [] Class declaration
|
53
|
+
- [] AST generation covers complete grammar
|
54
|
+
|
55
|
+
Tree-walking:
|
56
|
+
- [] Tree visitor recognizes all AST node types
|
57
|
+
|
58
|
+
Interpreter:
|
59
|
+
- Keywords: symbol table, scope, activation record, class & object model, Lox test suite
|
60
|
+
- [] Milestone: Interpreter handles expressions but function calls
|
61
|
+
- [] Milestone: Interpreter handles `for`, `if`, `print`, `while` and block statements
|
62
|
+
- [] Milestone: Interpreter handles variable declarations (global, block)
|
63
|
+
- [] Milestone: Interpreter handles function declarations and calls
|
64
|
+
- [] Milestone: Interpreter supports class and object definition
|
65
|
+
- [] Milestone: Lox interpreter complete
|
27
66
|
|
28
67
|
## Example using RawParser class
|
29
68
|
```ruby
|
@@ -10,13 +10,31 @@ module Loxxy
|
|
10
10
|
# (Abstract Syntax Tree) from a sequence of input tokens and
|
11
11
|
# visit events produced by walking over a GFGParsing object.
|
12
12
|
class ASTBuilder < Rley::ParseRep::ASTBaseBuilder
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
# Mapping Token name => operator | separator | delimiter characters
|
14
|
+
# @return [Hash{String => String}]
|
15
|
+
Name2special = {
|
16
|
+
'AND' => 'and',
|
17
|
+
'BANG' => '!',
|
18
|
+
'BANG_EQUAL' => '!=',
|
19
|
+
'COMMA' => ',',
|
20
|
+
'DOT' => '.',
|
21
|
+
'EQUAL' => '=',
|
22
|
+
'EQUAL_EQUAL' => '==',
|
23
|
+
'GREATER' => '>',
|
24
|
+
'GREATER_EQUAL' => '>=',
|
25
|
+
'LEFT_BRACE' => '{',
|
26
|
+
'LEFT_PAREN' => '(',
|
27
|
+
'LESS' => '<',
|
28
|
+
'LESS_EQUAL' => '<=',
|
29
|
+
'MINUS' => '-',
|
30
|
+
'OR' => 'or',
|
31
|
+
'PLUS' => '+',
|
32
|
+
'RIGHT_BRACE' => '}',
|
33
|
+
'RIGHT_PAREN' => ')',
|
34
|
+
'SEMICOLON' => ';',
|
35
|
+
'SLASH' => '/',
|
36
|
+
'STAR' => '*'
|
37
|
+
}.freeze
|
20
38
|
|
21
39
|
attr_reader :strict
|
22
40
|
|
@@ -76,7 +94,7 @@ module Loxxy
|
|
76
94
|
def reduce_binary_operator(_production, _range, tokens, theChildren)
|
77
95
|
operand1 = theChildren[0]
|
78
96
|
|
79
|
-
# Second child is
|
97
|
+
# Second child is array with couples [operator, operand2]
|
80
98
|
theChildren[1].each do |(operator, operand2)|
|
81
99
|
operand1 = LoxBinaryExpr.new(tokens[0].position, operator, operand1, operand2)
|
82
100
|
end
|
@@ -84,6 +102,36 @@ module Loxxy
|
|
84
102
|
operand1
|
85
103
|
end
|
86
104
|
|
105
|
+
# rule('something_plus' => 'something_plus operator symbol')
|
106
|
+
def reduce_binary_plus_more(_production, _range, _tokens, theChildren)
|
107
|
+
result = theChildren[0]
|
108
|
+
operator = Name2special[theChildren[1].symbol.name].to_sym
|
109
|
+
operand2 = theChildren[2]
|
110
|
+
result << [operator, operand2]
|
111
|
+
end
|
112
|
+
|
113
|
+
# rule('something_plus' => 'something_plus symbol')
|
114
|
+
def reduce_binary_plus_end(_production, _range, _tokens, theChildren)
|
115
|
+
operator = Name2special[theChildren[0].symbol.name].to_sym
|
116
|
+
operand2 = theChildren[1]
|
117
|
+
[[operator, operand2]]
|
118
|
+
end
|
119
|
+
|
120
|
+
# rule('equality' => 'comparison equalityTest_plus')
|
121
|
+
def reduce_equality_plus(production, range, tokens, theChildren)
|
122
|
+
reduce_binary_operator(production, range, tokens, theChildren)
|
123
|
+
end
|
124
|
+
|
125
|
+
# rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
|
126
|
+
def reduce_equality_t_plus_more(production, range, tokens, theChildren)
|
127
|
+
reduce_binary_plus_more(production, range, tokens, theChildren)
|
128
|
+
end
|
129
|
+
|
130
|
+
# rule('equalityTest_star' => 'equalityTest comparison')
|
131
|
+
def reduce_equality_t_plus_end(production, range, tokens, theChildren)
|
132
|
+
reduce_binary_plus_end(production, range, tokens, theChildren)
|
133
|
+
end
|
134
|
+
|
87
135
|
# rule('comparison' => 'term comparisonTest_plus')
|
88
136
|
def reduce_comparison_plus(production, range, tokens, theChildren)
|
89
137
|
reduce_binary_operator(production, range, tokens, theChildren)
|
@@ -93,16 +141,8 @@ module Loxxy
|
|
93
141
|
# TODO: is it meaningful to implement this rule?
|
94
142
|
|
95
143
|
# rule('comparisonTest_plus' => 'comparisonTest term')
|
96
|
-
def reduce_comparison_t_plus_end(
|
97
|
-
|
98
|
-
'GREATER' => '>',
|
99
|
-
'GREATER_EQUAL' => '>=',
|
100
|
-
'LESS' => '<',
|
101
|
-
'LESS_EQUAL' => '<='
|
102
|
-
}
|
103
|
-
operator = name2operators[theChildren[0].symbol.name].to_sym
|
104
|
-
operand2 = theChildren[1]
|
105
|
-
[[operator, operand2]]
|
144
|
+
def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
|
145
|
+
reduce_binary_plus_end(production, range, tokens, theChildren)
|
106
146
|
end
|
107
147
|
|
108
148
|
# rule('term' => 'factor additive_plus')
|
@@ -111,18 +151,13 @@ module Loxxy
|
|
111
151
|
end
|
112
152
|
|
113
153
|
# rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
|
114
|
-
def reduce_additive_plus_more(
|
115
|
-
|
116
|
-
operator = theChildren[1].symbol.name == 'MINUS' ? :- : :+
|
117
|
-
operand2 = theChildren[2]
|
118
|
-
result << [operator, operand2]
|
154
|
+
def reduce_additive_plus_more(production, range, tokens, theChildren)
|
155
|
+
reduce_binary_plus_more(production, range, tokens, theChildren)
|
119
156
|
end
|
120
157
|
|
121
158
|
# rule('additive_plus' => 'additionOp factor')
|
122
|
-
def reduce_additive_plus_end(
|
123
|
-
|
124
|
-
operand2 = theChildren[1]
|
125
|
-
[[operator, operand2]]
|
159
|
+
def reduce_additive_plus_end(production, range, tokens, theChildren)
|
160
|
+
reduce_binary_plus_end(production, range, tokens, theChildren)
|
126
161
|
end
|
127
162
|
|
128
163
|
# rule('factor' => 'multiplicative_plus')
|
@@ -131,18 +166,13 @@ module Loxxy
|
|
131
166
|
end
|
132
167
|
|
133
168
|
# rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
|
134
|
-
def reduce_multiplicative_plus_more(
|
135
|
-
|
136
|
-
operator = theChildren[1].symbol.name == 'SLASH' ? :/ : :*
|
137
|
-
operand2 = theChildren[2]
|
138
|
-
result << [operator, operand2]
|
169
|
+
def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
|
170
|
+
reduce_binary_plus_more(production, range, tokens, theChildren)
|
139
171
|
end
|
140
172
|
|
141
173
|
# rule('multiplicative_plus' => 'multOp unary')
|
142
|
-
def reduce_multiplicative_plus_end(
|
143
|
-
|
144
|
-
operand2 = theChildren[1]
|
145
|
-
[[operator, operand2]]
|
174
|
+
def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
|
175
|
+
reduce_binary_plus_end(production, range, tokens, theChildren)
|
146
176
|
end
|
147
177
|
|
148
178
|
# rule('primary' => 'FALSE' | TRUE').as 'literal_expr'
|
@@ -96,9 +96,9 @@ module Loxxy
|
|
96
96
|
rule('conjunct_plus' => 'conjunct_plus AND equality')
|
97
97
|
rule('conjunct_' => 'AND equality')
|
98
98
|
rule('equality' => 'comparison')
|
99
|
-
rule('equality' => 'comparison equalityTest_plus')
|
100
|
-
rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
|
101
|
-
rule('
|
99
|
+
rule('equality' => 'comparison equalityTest_plus').as 'equality_plus'
|
100
|
+
rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison').as 'equality_t_plus_more'
|
101
|
+
rule('equalityTest_plus' => 'equalityTest comparison').as 'equality_t_plus_end'
|
102
102
|
rule('equalityTest' => 'BANG_EQUAL')
|
103
103
|
rule('equalityTest' => 'EQUAL_EQUAL')
|
104
104
|
rule('comparison' => 'term')
|
data/lib/loxxy/version.rb
CHANGED
@@ -255,8 +255,25 @@ LOX_END
|
|
255
255
|
end # context
|
256
256
|
|
257
257
|
context 'Parsing comparison expressions' do
|
258
|
-
|
259
|
-
|
258
|
+
it 'should parse the comparison of two number literals' do
|
259
|
+
%w[> >= < <=].each do |predicate|
|
260
|
+
input = "3 #{predicate} 2;"
|
261
|
+
ptree = subject.parse(input)
|
262
|
+
parent = ptree.root.subnodes[0]
|
263
|
+
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
264
|
+
expect(parent.symbol.name).to eq('exprStmt')
|
265
|
+
expr = parent.subnodes[0]
|
266
|
+
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
267
|
+
expect(expr.operator).to eq(predicate.to_sym)
|
268
|
+
expect(expr.operands[0].literal.value).to eq(3)
|
269
|
+
expect(expr.operands[1].literal.value).to eq(2)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end # context
|
273
|
+
|
274
|
+
context 'Parsing equality expressions' do
|
275
|
+
it 'should parse the equality of two number literals' do
|
276
|
+
%w[!= ==].each do |predicate|
|
260
277
|
input = "3 #{predicate} 2;"
|
261
278
|
ptree = subject.parse(input)
|
262
279
|
parent = ptree.root.subnodes[0]
|
@@ -269,6 +286,22 @@ LOX_END
|
|
269
286
|
expect(expr.operands[1].literal.value).to eq(2)
|
270
287
|
end
|
271
288
|
end
|
289
|
+
|
290
|
+
it 'should parse combination of equality expressions' do
|
291
|
+
input = '5 != 2 == false; // A bit contrived example'
|
292
|
+
ptree = subject.parse(input)
|
293
|
+
parent = ptree.root.subnodes[0]
|
294
|
+
expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
|
295
|
+
expect(parent.symbol.name).to eq('exprStmt')
|
296
|
+
expr = parent.subnodes[0]
|
297
|
+
expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
|
298
|
+
expect(expr.operator).to eq(:==)
|
299
|
+
expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
|
300
|
+
expect(expr.operands[0].operator).to eq(:!=)
|
301
|
+
expect(expr.operands[0].operands[0].literal.value).to eq(5)
|
302
|
+
expect(expr.operands[0].operands[1].literal.value).to eq(2)
|
303
|
+
expect(expr.operands[1].literal.value).to be_falsey
|
304
|
+
end
|
272
305
|
end # context
|
273
306
|
end # describe
|
274
307
|
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.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-01-
|
11
|
+
date: 2021-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|