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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d628b0737a29bab0869827af50a363846200bfab7baddc63279e0e3fb904fa75
4
- data.tar.gz: d617939297fe24cf267fb66c198cbc172e05a7df4472eac546bd9b5e27eef0cd
3
+ metadata.gz: ce7eeb0247e67176d1dc4c67066eff4398ffaa8b75755b289eb3e53932d3006b
4
+ data.tar.gz: 84f7c781c10b0f6f07cfb5bab98d2ab9d78340cf5b20426822226cbe5c266a94
5
5
  SHA512:
6
- metadata.gz: dde10867e0a0243a5c0e2349a37df2ec76178aa2a7da625abd42363175301bf773d8d56aa249dde380361b13af5181c33a8ed65a4cf1bd2bbdaf2c946b534f99
7
- data.tar.gz: 204243d4d781f54820a66d483d791676d144b3a4af7febbb2a1a27041c651d2ea4a0015e2bf3408754c9ea31dc0f66b4c6b0d8ad6175edc5912391331f95e9e1
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, _tokens, theChildren)
229
- for_stmt = theChildren[2]
230
- for_stmt.body_stmt = theChildren[4]
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
- Ast::LoxForStmt.new(tokens[0].position, init, test, update)
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 StandardError, 'Superclass must be a class.'
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.accept(aVisitor)
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 StandardError, "Unknown variable #{var_name}" unless variable
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.accept(aVisitor)
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 (in uppercase)
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
- AND CLASS ELSE FALSE FUN FOR IF NIL OR
61
- PRINT RETURN SUPER THIS TRUE VAR WHILE
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.upcase]
117
- tok_type = keyw || 'IDENTIFIER'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.2.04'
4
+ VERSION = '0.2.05'
5
5
  end
@@ -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.04
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-25 00:00:00.000000000 Z
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