loxxy 0.2.04 → 0.2.05
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 +17 -0
- data/lib/loxxy/ast/all_lox_nodes.rb +0 -1
- data/lib/loxxy/ast/ast_builder.rb +27 -4
- data/lib/loxxy/ast/ast_visitor.rb +1 -9
- data/lib/loxxy/back_end/engine.rb +3 -25
- data/lib/loxxy/back_end/resolver.rb +1 -12
- data/lib/loxxy/front_end/scanner.rb +5 -5
- data/lib/loxxy/version.rb +1 -1
- data/spec/front_end/scanner_spec.rb +7 -0
- metadata +2 -3
- data/lib/loxxy/ast/lox_for_stmt.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce7eeb0247e67176d1dc4c67066eff4398ffaa8b75755b289eb3e53932d3006b
|
4
|
+
data.tar.gz: 84f7c781c10b0f6f07cfb5bab98d2ab9d78340cf5b20426822226cbe5c266a94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad3cce2c2f3bcf8c0c3ae30b1dc050764c654e276a2d482934c2efd7ad6120b0db7e415003bd8d2f887ed118f723eee7c882f231ceadea773236d1d01e478db9
|
7
|
+
data.tar.gz: d5e25c250ac629d76e01468fbb59a2fcb287b2a112fda9f2432e83df48a889581ecbe38723ab7371ba6ae3cdd94367051249907994caa78bfa852e7b328a0811
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## [0.2.05] - 2021-04-26
|
2
|
+
- `Loxxy` now transforms for loops into while loops (desugaring), fix in Scanner class
|
3
|
+
|
4
|
+
### Changed
|
5
|
+
- Method `Ast::ASTBuilder#reduce_for_stmt` converts 'for' loops into 'while' loops
|
6
|
+
- Method `Ast::ASTBuilder#reduce_for_control takes care of case for(expr1;;expr2) now test expression is set to true
|
7
|
+
|
8
|
+
### Fixed
|
9
|
+
- Method `FrontEnd::Scanner#next_token` keyword recognition was case insensitive
|
10
|
+
|
11
|
+
### Removed
|
12
|
+
- Method `Ast::Visitor#visitor_for_stmt`
|
13
|
+
- Method `BackEnd::Engine#after_for_stmt`
|
14
|
+
- Method `BackEnd::Resolver#before_for_stmt`
|
15
|
+
- Method `BackEnd::Resolver#after_for_stmt`
|
16
|
+
|
17
|
+
|
1
18
|
## [0.2.04] - 2021-04-25
|
2
19
|
- `Loxxy` passes the test suite for `for` statements
|
3
20
|
|
@@ -19,7 +19,6 @@ require_relative 'lox_while_stmt'
|
|
19
19
|
require_relative 'lox_return_stmt'
|
20
20
|
require_relative 'lox_print_stmt'
|
21
21
|
require_relative 'lox_if_stmt'
|
22
|
-
require_relative 'lox_for_stmt'
|
23
22
|
require_relative 'lox_var_stmt'
|
24
23
|
require_relative 'lox_class_stmt'
|
25
24
|
require_relative 'lox_seq_decl'
|
@@ -225,16 +225,39 @@ module Loxxy
|
|
225
225
|
end
|
226
226
|
|
227
227
|
# rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
|
228
|
-
def reduce_for_stmt(_production, _range,
|
229
|
-
|
230
|
-
|
228
|
+
def reduce_for_stmt(_production, _range, tokens, theChildren)
|
229
|
+
# Following 'Crafting Interpreters', we replace the for statement by a while loop
|
230
|
+
return theChildren[4] if theChildren[2].compact.empty? # for(;;) => execute body once
|
231
|
+
|
232
|
+
(init, test, update) = theChildren[2]
|
233
|
+
if update
|
234
|
+
new_body = LoxSeqDecl.new(tokens[0].position, [theChildren[4], update])
|
235
|
+
stmt = Ast::LoxBlockStmt.new(tokens[1].position, new_body)
|
236
|
+
else
|
237
|
+
stmt = theChildren[4]
|
238
|
+
end
|
239
|
+
while_stmt = Ast::LoxWhileStmt.new(tokens[0].position, test, stmt)
|
240
|
+
|
241
|
+
if init
|
242
|
+
block_body = LoxSeqDecl.new(tokens[0].position, [init, while_stmt])
|
243
|
+
for_stmt = Ast::LoxBlockStmt.new(tokens[1].position, block_body)
|
244
|
+
else
|
245
|
+
for_stmt = while_stmt
|
246
|
+
end
|
247
|
+
|
231
248
|
for_stmt
|
232
249
|
end
|
233
250
|
|
234
251
|
# rule('forControl' => 'forInitialization forTest forUpdate')
|
235
252
|
def reduce_for_control(_production, _range, tokens, theChildren)
|
236
253
|
(init, test, update) = theChildren
|
237
|
-
|
254
|
+
if test.nil? && update
|
255
|
+
# when test expr is nil but update expr is not, then force test to be true
|
256
|
+
test = LoxLiteralExpr.new(tokens[0].position, Datatype::True.instance)
|
257
|
+
[init, test, update]
|
258
|
+
else
|
259
|
+
theChildren
|
260
|
+
end
|
238
261
|
end
|
239
262
|
|
240
263
|
# rule('forInitialization' => 'SEMICOLON')
|
@@ -75,14 +75,6 @@ module Loxxy
|
|
75
75
|
broadcast(:after_class_stmt, aClassStmt, self)
|
76
76
|
end
|
77
77
|
|
78
|
-
# Visit event. The visitor is about to visit a for statement.
|
79
|
-
# @param aForStmt [AST::LOXForStmt] the for statement node to visit
|
80
|
-
def visit_for_stmt(aForStmt)
|
81
|
-
broadcast(:before_for_stmt, aForStmt)
|
82
|
-
traverse_subnodes(aForStmt) # The condition is visited/evaluated here...
|
83
|
-
broadcast(:after_for_stmt, aForStmt, self)
|
84
|
-
end
|
85
|
-
|
86
78
|
# Visit event. The visitor is about to visit a if statement.
|
87
79
|
# @param anIfStmt [AST::LOXIfStmt] the if statement node to visit
|
88
80
|
def visit_if_stmt(anIfStmt)
|
@@ -111,7 +103,7 @@ module Loxxy
|
|
111
103
|
# @param aWhileStmt [AST::LOXWhileStmt] the while statement node to visit
|
112
104
|
def visit_while_stmt(aWhileStmt)
|
113
105
|
broadcast(:before_while_stmt, aWhileStmt)
|
114
|
-
traverse_subnodes(aWhileStmt) # The condition is visited/evaluated here...
|
106
|
+
traverse_subnodes(aWhileStmt) if aWhileStmt.condition # The condition is visited/evaluated here...
|
115
107
|
broadcast(:after_while_stmt, aWhileStmt, self)
|
116
108
|
end
|
117
109
|
|
@@ -74,7 +74,7 @@ module Loxxy
|
|
74
74
|
aClassStmt.superclass.accept(aVisitor)
|
75
75
|
parent = stack.pop
|
76
76
|
unless parent.kind_of?(LoxClass)
|
77
|
-
raise
|
77
|
+
raise Loxxy::RuntimeError, 'Superclass must be a class.'
|
78
78
|
end
|
79
79
|
else
|
80
80
|
parent = nil
|
@@ -116,28 +116,6 @@ module Loxxy
|
|
116
116
|
before_block_stmt(aForStmt)
|
117
117
|
end
|
118
118
|
|
119
|
-
def after_for_stmt(aForStmt, aVisitor)
|
120
|
-
iterating = false
|
121
|
-
|
122
|
-
loop do
|
123
|
-
if aForStmt.test_expr
|
124
|
-
aForStmt.test_expr.accept(aVisitor)
|
125
|
-
condition = stack.pop
|
126
|
-
break unless condition.truthy?
|
127
|
-
elsif iterating
|
128
|
-
# when both test and update expressions are nil => execute body once
|
129
|
-
break unless aForStmt.update_expr
|
130
|
-
else
|
131
|
-
iterating = true
|
132
|
-
end
|
133
|
-
|
134
|
-
aForStmt.body_stmt.accept(aVisitor)
|
135
|
-
aForStmt.update_expr&.accept(aVisitor)
|
136
|
-
stack.pop
|
137
|
-
end
|
138
|
-
after_block_stmt(aForStmt)
|
139
|
-
end
|
140
|
-
|
141
119
|
def after_if_stmt(anIfStmt, aVisitor)
|
142
120
|
# Retrieve the result of the condition evaluation
|
143
121
|
condition = stack.pop
|
@@ -163,7 +141,7 @@ module Loxxy
|
|
163
141
|
break unless condition.truthy?
|
164
142
|
|
165
143
|
aWhileStmt.body.accept(aVisitor)
|
166
|
-
aWhileStmt.condition
|
144
|
+
aWhileStmt.condition&.accept(aVisitor)
|
167
145
|
end
|
168
146
|
end
|
169
147
|
|
@@ -179,7 +157,7 @@ module Loxxy
|
|
179
157
|
def after_assign_expr(anAssignExpr, _visitor)
|
180
158
|
var_name = anAssignExpr.name
|
181
159
|
variable = variable_lookup(anAssignExpr)
|
182
|
-
raise
|
160
|
+
raise Loxxy::RuntimeError, "Undefined variable '#{var_name}'." unless variable
|
183
161
|
|
184
162
|
value = stack.last # ToS remains since an assignment produces a value
|
185
163
|
variable.assign(value)
|
@@ -88,17 +88,6 @@ module Loxxy
|
|
88
88
|
@current_class = previous_class
|
89
89
|
end
|
90
90
|
|
91
|
-
def before_for_stmt(aForStmt)
|
92
|
-
before_block_stmt(aForStmt)
|
93
|
-
end
|
94
|
-
|
95
|
-
def after_for_stmt(aForStmt, aVisitor)
|
96
|
-
aForStmt.test_expr&.accept(aVisitor)
|
97
|
-
aForStmt.body_stmt.accept(aVisitor)
|
98
|
-
aForStmt.update_expr&.accept(aVisitor)
|
99
|
-
after_block_stmt(aForStmt)
|
100
|
-
end
|
101
|
-
|
102
91
|
def after_if_stmt(anIfStmt, aVisitor)
|
103
92
|
anIfStmt.then_stmt.accept(aVisitor)
|
104
93
|
anIfStmt.else_stmt&.accept(aVisitor)
|
@@ -123,7 +112,7 @@ module Loxxy
|
|
123
112
|
|
124
113
|
def after_while_stmt(aWhileStmt, aVisitor)
|
125
114
|
aWhileStmt.body.accept(aVisitor)
|
126
|
-
aWhileStmt.condition
|
115
|
+
aWhileStmt.condition&.accept(aVisitor)
|
127
116
|
end
|
128
117
|
|
129
118
|
# A variable declaration adds a new variable to current scope
|
@@ -54,11 +54,11 @@ module Loxxy
|
|
54
54
|
'<=' => 'LESS_EQUAL'
|
55
55
|
}.freeze
|
56
56
|
|
57
|
-
# Here are all the implemented Lox keywords
|
57
|
+
# Here are all the implemented Lox keywords
|
58
58
|
# These are enumerated in section 4.2.1 Token type
|
59
59
|
@@keywords = %w[
|
60
|
-
|
61
|
-
|
60
|
+
and class else false fun for if nil or
|
61
|
+
print return super this true var while
|
62
62
|
].map { |x| [x, x] }.to_h
|
63
63
|
|
64
64
|
# Constructor. Initialize a tokenizer for Lox input.
|
@@ -113,8 +113,8 @@ module Loxxy
|
|
113
113
|
elsif (lexeme = scanner.scan(/"(?:\\"|[^"])*"/))
|
114
114
|
token = build_token('STRING', lexeme)
|
115
115
|
elsif (lexeme = scanner.scan(/[a-zA-Z_][a-zA-Z_0-9]*/))
|
116
|
-
keyw = @@keywords[lexeme
|
117
|
-
tok_type = keyw
|
116
|
+
keyw = @@keywords[lexeme]
|
117
|
+
tok_type = keyw ? keyw.upcase : 'IDENTIFIER'
|
118
118
|
token = build_token(tok_type, lexeme)
|
119
119
|
elsif scanner.scan(/"(?:\\"|[^"])*\z/)
|
120
120
|
# Error: unterminated string...
|
data/lib/loxxy/version.rb
CHANGED
@@ -217,6 +217,13 @@ LOX_END
|
|
217
217
|
expect(token_nil.lexeme).to eq('nil')
|
218
218
|
expect(token_nil.value).to be_kind_of(Datatype::Nil)
|
219
219
|
end
|
220
|
+
|
221
|
+
it 'should differentiate nil from variable spelled same' do
|
222
|
+
subject.start_with('Nil')
|
223
|
+
similar = subject.tokens[0]
|
224
|
+
expect(similar.terminal).to eq('IDENTIFIER')
|
225
|
+
expect(similar.lexeme).to eq('Nil')
|
226
|
+
end
|
220
227
|
end # context
|
221
228
|
|
222
229
|
context 'Handling comments:' do
|
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.05
|
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-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -99,7 +99,6 @@ files:
|
|
99
99
|
- lib/loxxy/ast/lox_call_expr.rb
|
100
100
|
- lib/loxxy/ast/lox_class_stmt.rb
|
101
101
|
- lib/loxxy/ast/lox_compound_expr.rb
|
102
|
-
- lib/loxxy/ast/lox_for_stmt.rb
|
103
102
|
- lib/loxxy/ast/lox_fun_stmt.rb
|
104
103
|
- lib/loxxy/ast/lox_get_expr.rb
|
105
104
|
- lib/loxxy/ast/lox_grouping_expr.rb
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'lox_compound_expr'
|
4
|
-
|
5
|
-
module Loxxy
|
6
|
-
module Ast
|
7
|
-
class LoxForStmt < LoxCompoundExpr
|
8
|
-
# @return [LoxNode] test expression
|
9
|
-
attr_reader :test_expr
|
10
|
-
|
11
|
-
# @return [LoxNode] update expression
|
12
|
-
attr_reader :update_expr
|
13
|
-
|
14
|
-
# @return [LoxNode] body statement
|
15
|
-
attr_accessor :body_stmt
|
16
|
-
|
17
|
-
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
18
|
-
# @param initialization [Loxxy::Ast::LoxNode]
|
19
|
-
# @param testExpr [Loxxy::Ast::LoxNode]
|
20
|
-
# @param updateExpr [Loxxy::Ast::LoxNode]
|
21
|
-
def initialize(aPosition, initialization, testExpr, updateExpr)
|
22
|
-
child = initialization ? [initialization] : []
|
23
|
-
super(aPosition, child)
|
24
|
-
@test_expr = testExpr
|
25
|
-
@update_expr = updateExpr
|
26
|
-
end
|
27
|
-
|
28
|
-
# Accessor to the condition expression
|
29
|
-
# @return [LoxNode]
|
30
|
-
def condition
|
31
|
-
subnodes[0]
|
32
|
-
end
|
33
|
-
|
34
|
-
define_accept # Add `accept` method as found in Visitor design pattern
|
35
|
-
end # class
|
36
|
-
end # module
|
37
|
-
end # module
|