loxxy 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/loxxy.svg)](https://badge.fury.io/rb/loxxy)
|
3
|
+
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](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
|