loxxy 0.2.04 → 0.2.05

Sign up to get free protection for your applications and to get access to all the features.
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