loxxy 0.0.27 → 0.0.28

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: 47ae4c0af1698de7187c530ec5feec9390a664ff681aae6c47a36e351bda229d
4
- data.tar.gz: ff309645b8e71608e6a3801d4a4be558b8e86d59d658af4ec2eb796989e88455
3
+ metadata.gz: 6b9202e7a755cffecb3226da5764a3cb6f75fd3179803eb25779ae42822bbf15
4
+ data.tar.gz: 49767873134c5c124aab3e6b20a49c60f968df180dfd6e945f3da2808eee59c7
5
5
  SHA512:
6
- metadata.gz: 73bea958f39a8cb97ed2e59c049ab58006845519a642d20e72cbdd554be60cb3b553f84dcdd64a36410796f83e87bb3239eee9933148b821b074916f4198a810
7
- data.tar.gz: a3235fd86a5c99c0ecf581be106e53d59a0643b3ac6299fbd0eccd0f5eacc5a15b63df3a22fb4edef8b30314fa0360432b017d9a2417668e365e2b9dd3f8b2b8
6
+ metadata.gz: 29c481015e414ea71eb55c0b4dcc4d793b011a8f16fa7f9218ec704b2baea820e47ba758ba9a2e30e5bdfe08bfd2ad8fa892e6d67e247938b8bcda7b005ff2a9
7
+ data.tar.gz: cbf671e54aa98e799b6d565cac52fb19fb3f5d7b3ae84841f27067ae427e5a7c25a7d40a07d39645150f73176709fbcc6e3bbf36e9cada14d8b329f335c621d5
data/README.md CHANGED
@@ -14,9 +14,13 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
14
14
 
15
15
  ### Current status
16
16
  The project is still in inception and the interpreter is being implemented...
17
- Currently it can execute a subset of __Lox__ language.
17
+ Currently it can execute all allowed __Lox__ expressions and statement except:
18
+ - Functions and closures,
19
+ - Classes and objects.
20
+
21
+ These will be implemented soon.
22
+
18
23
 
19
- But the __loxxy__ gem hosts also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
20
24
 
21
25
  ## What's the fuss about Lox?
22
26
  ... Nothing...
@@ -3,6 +3,7 @@
3
3
  require_relative 'lox_variable_expr'
4
4
  require_relative 'lox_literal_expr'
5
5
  require_relative 'lox_noop_expr'
6
+ require_relative 'lox_call_expr'
6
7
  require_relative 'lox_grouping_expr'
7
8
  require_relative 'lox_unary_expr'
8
9
  require_relative 'lox_binary_expr'
@@ -233,99 +233,35 @@ module Loxxy
233
233
  Ast::LoxAssignExpr.new(tokens[1].position, var_name, theChildren[3])
234
234
  end
235
235
 
236
- # rule('logic_or' => 'logic_and disjunct_plus')
237
- def reduce_logic_or_plus(production, range, tokens, theChildren)
238
- reduce_logical_expr(production, range, tokens, theChildren)
239
- end
240
-
241
- # rule('disjunct_plus' => 'disjunct_plus OR logic_and')
242
- def reduce_logic_or_plus_more(production, range, tokens, theChildren)
243
- reduce_binary_plus_more(production, range, tokens, theChildren)
244
- end
245
-
246
- # rule('disjunct_plus' => 'OR logic_and')
247
- def reduce_logic_or_plus_end(production, range, tokens, theChildren)
248
- reduce_binary_plus_end(production, range, tokens, theChildren)
249
- end
250
-
251
- # rule('logic_and' => 'equality conjunct_plus')
252
- def reduce_logic_and_plus(production, range, tokens, theChildren)
253
- reduce_logical_expr(production, range, tokens, theChildren)
254
- end
255
-
256
- # rule('conjunct_plus' => 'conjunct_plus AND equality')
257
- def reduce_logic_and_plus_more(production, range, tokens, theChildren)
258
- reduce_binary_plus_more(production, range, tokens, theChildren)
259
- end
260
-
261
- # rule('conjunct_plus' => 'AND equality')
262
- def reduce_logic_and_plus_end(production, range, tokens, theChildren)
263
- reduce_binary_plus_end(production, range, tokens, theChildren)
264
- end
265
-
266
- # rule('equality' => 'comparison equalityTest_plus')
267
- def reduce_equality_plus(production, range, tokens, theChildren)
268
- reduce_binary_operator(production, range, tokens, theChildren)
269
- end
270
-
271
- # rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
272
- def reduce_equality_t_plus_more(production, range, tokens, theChildren)
273
- reduce_binary_plus_more(production, range, tokens, theChildren)
274
- end
275
-
276
- # rule('equalityTest_star' => 'equalityTest comparison')
277
- def reduce_equality_t_plus_end(production, range, tokens, theChildren)
278
- reduce_binary_plus_end(production, range, tokens, theChildren)
279
- end
280
-
281
- # rule('comparison' => 'term comparisonTest_plus')
282
- def reduce_comparison_plus(production, range, tokens, theChildren)
283
- reduce_binary_operator(production, range, tokens, theChildren)
284
- end
285
-
286
236
  # rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
287
237
  # TODO: is it meaningful to implement this rule?
288
238
 
289
- # rule('comparisonTest_plus' => 'comparisonTest term')
290
- def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
291
- reduce_binary_plus_end(production, range, tokens, theChildren)
292
- end
293
-
294
- # rule('term' => 'factor additive_plus')
295
- def reduce_term_additive(production, range, tokens, theChildren)
296
- reduce_binary_operator(production, range, tokens, theChildren)
297
- end
298
-
299
- # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
300
- def reduce_additive_plus_more(production, range, tokens, theChildren)
301
- reduce_binary_plus_more(production, range, tokens, theChildren)
302
- end
303
-
304
- # rule('additive_plus' => 'additionOp factor')
305
- def reduce_additive_plus_end(production, range, tokens, theChildren)
306
- reduce_binary_plus_end(production, range, tokens, theChildren)
239
+ # rule('unary' => 'unaryOp unary')
240
+ def reduce_unary_expr(_production, _range, tokens, theChildren)
241
+ operator = Name2unary[theChildren[0].symbol.name].to_sym
242
+ operand = theChildren[1]
243
+ LoxUnaryExpr.new(tokens[0].position, operator, operand)
307
244
  end
308
245
 
309
- # rule('factor' => 'multiplicative_plus')
310
- def reduce_factor_multiplicative(production, range, tokens, theChildren)
311
- reduce_binary_operator(production, range, tokens, theChildren)
246
+ # rule('call' => 'primary refinement_plus').as 'call_expr'
247
+ def reduce_call_expr(_production, _range, _tokens, theChildren)
248
+ theChildren[1].callee = theChildren[0]
249
+ theChildren[1]
312
250
  end
313
251
 
314
- # rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
315
- def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
316
- reduce_binary_plus_more(production, range, tokens, theChildren)
252
+ # rule('refinement_plus' => 'refinement').
253
+ def reduce_refinement_plus_end(_production, _range, _tokens, theChildren)
254
+ theChildren[0]
317
255
  end
318
256
 
319
- # rule('multiplicative_plus' => 'multOp unary')
320
- def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
321
- reduce_binary_plus_end(production, range, tokens, theChildren)
322
- end
257
+ # rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
258
+ def reduce_call_arglist(_production, _range, tokens, theChildren)
259
+ args = theChildren[1] || []
260
+ if args.size > 255
261
+ raise StandardError, "Can't have more than 255 arguments."
262
+ end
323
263
 
324
- # rule('unary' => 'unaryOp unary')
325
- def reduce_unary_expr(_production, _range, tokens, theChildren)
326
- operator = Name2unary[theChildren[0].symbol.name].to_sym
327
- operand = theChildren[1]
328
- LoxUnaryExpr.new(tokens[0].position, operator, operand)
264
+ LoxCallExpr.new(tokens[0].position, args)
329
265
  end
330
266
 
331
267
  # rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
@@ -347,6 +283,16 @@ module Loxxy
347
283
  var_name = theChildren[0].token.lexeme
348
284
  LoxVariableExpr.new(tokens[0].position, var_name)
349
285
  end
286
+
287
+ # rule('arguments' => 'arguments COMMA expression')
288
+ def reduce_arguments_plus_more(_production, _range, _tokens, theChildren)
289
+ theChildren[0] << theChildren[2]
290
+ end
291
+
292
+ # rule('arguments' => 'expression')
293
+ def reduce_arguments_plus_end(_production, _range, _tokens, theChildren)
294
+ theChildren
295
+ end
350
296
  end # class
351
297
  end # module
352
298
  end # module
@@ -145,6 +145,14 @@ module Loxxy
145
145
  broadcast(:after_unary_expr, anUnaryExpr)
146
146
  end
147
147
 
148
+ # Visit event. The visitor is about to visit a call expression.
149
+ # @param aCallExpr [AST::LoxCallExpr] call expression to visit
150
+ def visit_call_expr(aCallExpr)
151
+ broadcast(:before_call_expr, aCallExpr)
152
+ traverse_subnodes(aCallExpr)
153
+ broadcast(:after_call_expr, aCallExpr, self)
154
+ end
155
+
148
156
  # Visit event. The visitor is about to visit a grouping expression.
149
157
  # @param aGroupingExpr [AST::LoxGroupingExpr] grouping expression to visit
150
158
  def visit_grouping_expr(aGroupingExpr)
@@ -177,10 +185,12 @@ module Loxxy
177
185
 
178
186
  # Visit event. The visitor is about to visit the given non terminal node.
179
187
  # @param aNonTerminalNode [Rley::PTree::NonTerminalNode] the node to visit.
180
- def visit_nonterminal(_non_terminal_node)
188
+ def visit_nonterminal(non_terminal_node)
181
189
  # Loxxy interpreter encountered a CST node (Concrete Syntax Tree)
182
190
  # that it cannot handle.
183
- raise NotImplementedError, 'Loxxy cannot execute this code yet.'
191
+ symb = non_terminal_node.symbol.name
192
+ msg = "Loxxy cannot execute this code yet for non-terminal symbol '#{symb}'."
193
+ raise NotImplementedError, msg
184
194
  end
185
195
 
186
196
  private
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxCallExpr < LoxCompoundExpr
8
+ attr_accessor :callee
9
+ attr_reader :arguments
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param argList [Array<Loxxy::Ast::LoxNode>]
13
+ def initialize(aPosition, argList)
14
+ super(aPosition, [])
15
+ @arguments = argList
16
+ end
17
+
18
+ # Part of the 'visitee' role in Visitor design pattern.
19
+ # @param visitor [Ast::ASTVisitor] the visitor
20
+ def accept(visitor)
21
+ visitor.visit_call_expr(self)
22
+ end
23
+ end # class
24
+ end # module
25
+ end # module
@@ -25,6 +25,8 @@ module Loxxy
25
25
  @ostream = config.include?(:ostream) ? config[:ostream] : $stdout
26
26
  @symbol_table = SymbolTable.new
27
27
  @stack = []
28
+
29
+ init_globals
28
30
  end
29
31
 
30
32
  # Given an abstract syntax parse tree visitor, launch the visit
@@ -167,6 +169,17 @@ module Loxxy
167
169
  end
168
170
  end
169
171
 
172
+ def after_call_expr(aCallExpr, aVisitor)
173
+ # Evaluate callee part
174
+ aCallExpr.callee.accept(aVisitor)
175
+ callee = stack.pop
176
+ # TODO: reverse order?
177
+ aCallExpr.arguments.each do |arg|
178
+ arg.evaluate(aVisitor)
179
+ end
180
+ stack.push callee.call # Pass arguments
181
+ end
182
+
170
183
  def after_grouping_expr(_groupingExpr)
171
184
  # Do nothing: work was already done by visiting /evaluating the subexpression
172
185
  end
@@ -188,6 +201,32 @@ module Loxxy
188
201
  def before_visit_builtin(aValue)
189
202
  stack.push(aValue)
190
203
  end
204
+
205
+ private
206
+
207
+ NativeFunction = Struct.new(:callable, :interp) do
208
+ def accept(_visitor)
209
+ interp.stack.push callable
210
+ end
211
+ end
212
+
213
+ def init_globals
214
+ add_native_fun('clock', native_clock)
215
+ end
216
+
217
+ def add_native_fun(aName, aProc)
218
+ native_fun = Variable.new(aName, NativeFunction.new(aProc, self))
219
+ symbol_table.insert(native_fun)
220
+ end
221
+
222
+ # Ruby-native function that returns (as float) the number of seconds since
223
+ # a given time reference.
224
+ def native_clock
225
+ proc do
226
+ now = Time.now.to_f
227
+ Datatype::Number.new(now)
228
+ end
229
+ end
191
230
  end # class
192
231
  end # module
193
232
  end # module
@@ -88,37 +88,37 @@ module Loxxy
88
88
  rule('owner_opt' => 'call DOT')
89
89
  rule('owner_opt' => [])
90
90
  rule('logic_or' => 'logic_and')
91
- rule('logic_or' => 'logic_and disjunct_plus').as 'logic_or_plus'
92
- rule('disjunct_plus' => 'disjunct_plus OR logic_and').as 'logic_or_plus_more'
93
- rule('disjunct_plus' => 'OR logic_and').as 'logic_or_plus_end'
91
+ rule('logic_or' => 'logic_and disjunct_plus').as 'logical_expr'
92
+ rule('disjunct_plus' => 'disjunct_plus OR logic_and').as 'binary_plus_more'
93
+ rule('disjunct_plus' => 'OR logic_and').as 'binary_plus_end'
94
94
  rule('logic_and' => 'equality')
95
- rule('logic_and' => 'equality conjunct_plus').as 'logic_and_plus'
96
- rule('conjunct_plus' => 'conjunct_plus AND equality').as 'logic_and_plus_more'
97
- rule('conjunct_plus' => 'AND equality').as 'logic_and_plus_end'
95
+ rule('logic_and' => 'equality conjunct_plus').as 'logical_expr'
96
+ rule('conjunct_plus' => 'conjunct_plus AND equality').as 'binary_plus_more'
97
+ rule('conjunct_plus' => 'AND equality').as 'binary_plus_end'
98
98
  rule('equality' => 'comparison')
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'
99
+ rule('equality' => 'comparison equalityTest_plus').as 'binary_operator'
100
+ rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison').as 'binary_plus_more'
101
+ rule('equalityTest_plus' => 'equalityTest comparison').as 'binary_plus_end'
102
102
  rule('equalityTest' => 'BANG_EQUAL')
103
103
  rule('equalityTest' => 'EQUAL_EQUAL')
104
104
  rule('comparison' => 'term')
105
- rule('comparison' => 'term comparisonTest_plus').as 'comparison_plus'
105
+ rule('comparison' => 'term comparisonTest_plus').as 'binary_operator'
106
106
  rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
107
- rule('comparisonTest_plus' => 'comparisonTest term').as 'comparison_t_plus_end'
107
+ rule('comparisonTest_plus' => 'comparisonTest term').as 'binary_plus_end'
108
108
  rule('comparisonTest' => 'GREATER')
109
109
  rule('comparisonTest' => 'GREATER_EQUAL')
110
110
  rule('comparisonTest' => 'LESS')
111
111
  rule('comparisonTest' => 'LESS_EQUAL')
112
112
  rule('term' => 'factor')
113
- rule('term' => 'factor additive_plus').as 'term_additive'
114
- rule('additive_plus' => 'additive_plus additionOp factor').as 'additive_plus_more'
115
- rule('additive_plus' => 'additionOp factor').as 'additive_plus_end'
113
+ rule('term' => 'factor additive_plus').as 'binary_operator'
114
+ rule('additive_plus' => 'additive_plus additionOp factor').as 'binary_plus_more'
115
+ rule('additive_plus' => 'additionOp factor').as 'binary_plus_end'
116
116
  rule('additionOp' => 'MINUS')
117
117
  rule('additionOp' => 'PLUS')
118
118
  rule('factor' => 'unary')
119
- rule('factor' => 'unary multiplicative_plus').as 'factor_multiplicative'
120
- rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as 'multiplicative_plus_more'
121
- rule('multiplicative_plus' => 'multOp unary').as 'multiplicative_plus_end'
119
+ rule('factor' => 'unary multiplicative_plus').as 'binary_operator'
120
+ rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as 'binary_plus_more'
121
+ rule('multiplicative_plus' => 'multOp unary').as 'binary_plus_end'
122
122
  rule('multOp' => 'SLASH')
123
123
  rule('multOp' => 'STAR')
124
124
  rule('unary' => 'unaryOp unary').as 'unary_expr'
@@ -126,10 +126,10 @@ module Loxxy
126
126
  rule('unaryOp' => 'BANG')
127
127
  rule('unaryOp' => 'MINUS')
128
128
  rule('call' => 'primary')
129
- rule('call' => 'primary refinement_plus')
130
- rule('refinement_plus' => 'refinement_plus refinement')
131
- rule('refinement_plus' => 'refinement')
132
- rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
129
+ rule('call' => 'primary refinement_plus').as 'call_expr'
130
+ rule('refinement_plus' => 'refinement_plus refinement') # .as 'refinement_plus_more'
131
+ rule('refinement_plus' => 'refinement').as 'refinement_plus_end'
132
+ rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN').as 'call_arglist'
133
133
  rule('refinement' => 'DOT IDENTIFIER')
134
134
  rule('primary' => 'TRUE').as 'literal_expr'
135
135
  rule('primary' => 'FALSE').as 'literal_expr'
@@ -149,8 +149,8 @@ module Loxxy
149
149
  rule('parameters' => 'IDENTIFIER')
150
150
  rule('arguments_opt' => 'arguments')
151
151
  rule('arguments_opt' => [])
152
- rule('arguments' => 'arguments COMMA expression')
153
- rule('arguments' => 'expression')
152
+ rule('arguments' => 'arguments COMMA expression').as 'arguments_plus_more'
153
+ rule('arguments' => 'expression').as 'arguments_plus_end'
154
154
  end
155
155
 
156
156
  unless defined?(Grammar)
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.0.27'
4
+ VERSION = '0.0.28'
5
5
  end
@@ -26,6 +26,7 @@ module Loxxy
26
26
  expect(subject.to_str).to eq(sample_text)
27
27
  end
28
28
 
29
+ # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
29
30
  it 'compares with another Lox string' do
30
31
  result = subject == LXString.new(sample_text.dup)
31
32
  expect(result).to be_true
@@ -40,6 +41,7 @@ module Loxxy
40
41
  result = LXString.new('') == LXString.new('')
41
42
  expect(result).to be_true
42
43
  end
44
+ # rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
43
45
 
44
46
  it 'compares with a Ruby string' do
45
47
  result = subject == sample_text.dup
@@ -21,6 +21,7 @@ module Loxxy
21
21
  end
22
22
  end
23
23
 
24
+ # rubocop: disable Lint/FloatComparison
24
25
  context 'Provided services:' do
25
26
  it 'should compare with other Lox numbers' do
26
27
  result = subject == Number.new(sample_value)
@@ -51,7 +52,8 @@ module Loxxy
51
52
  subtraction = subject - Number.new(10)
52
53
  expect(subtraction == -22.34).to be_true
53
54
  end
54
- end
55
+ end # context
56
+ # rubocop: enable Lint/FloatComparison
55
57
  end # describe
56
58
  end # module
57
59
  end # module
@@ -208,6 +208,7 @@ LOX_END
208
208
  expect(eof_token.terminal).to eq('EOF')
209
209
  end
210
210
 
211
+ # rubocop: disable Lint/PercentStringArray
211
212
  it 'should skip end of line comments' do
212
213
  input = <<-LOX_END
213
214
  // first comment
@@ -223,6 +224,7 @@ LOX_END
223
224
  ]
224
225
  match_expectations(subject, expectations)
225
226
  end
227
+ # rubocop: enable Lint/PercentStringArray
226
228
 
227
229
  it 'should cope with single slash (divide) expression' do
228
230
  subject.start_with('8 / 2')
@@ -384,18 +384,16 @@ LOX_END
384
384
  expect(sample_cfg[:ostream].string).to eq('012')
385
385
  end
386
386
 
387
- it 'should implement for loops without initialization' do
387
+ it 'should implement nullary function calls' do
388
388
  program = <<-LOX_END
389
- var i = 0;
390
- // No variable in initialization.
391
- for (; i < 2; i = i + 1) print i;
392
- // output: 0
393
- // output: 1
389
+ print clock(); // Lox expect the 'clock' predefined native function
394
390
  LOX_END
395
391
  expect { subject.evaluate(program) }.not_to raise_error
396
- expect(sample_cfg[:ostream].string).to eq('01')
392
+ tick = sample_cfg[:ostream].string
393
+ expect(Time.now.to_f - tick.to_f).to be < 0.1
397
394
  end
398
395
 
396
+
399
397
  it 'should print the hello world message' do
400
398
  expect { subject.evaluate(hello_world) }.not_to raise_error
401
399
  expect(sample_cfg[:ostream].string).to eq('Hello, world!')
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.27
4
+ version: 0.0.28
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-24 00:00:00.000000000 Z
11
+ date: 2021-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -90,6 +90,7 @@ files:
90
90
  - lib/loxxy/ast/lox_assign_expr.rb
91
91
  - lib/loxxy/ast/lox_binary_expr.rb
92
92
  - lib/loxxy/ast/lox_block_stmt.rb
93
+ - lib/loxxy/ast/lox_call_expr.rb
93
94
  - lib/loxxy/ast/lox_compound_expr.rb
94
95
  - lib/loxxy/ast/lox_for_stmt.rb
95
96
  - lib/loxxy/ast/lox_grouping_expr.rb