loxxy 0.0.27 → 0.0.28

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: 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