loxxy 0.2.02 → 0.2.03

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: afc822710c5023cdffd8eeb9e9316736f2b2d9ddb2d05e4c4e835dee9b9b4d8b
4
- data.tar.gz: 5debd7264833555cc34c64586de96d4e811eef4ebebe25f6c42a0832dee8e433
3
+ metadata.gz: eb84858746525e4b5f6821e8168066f4af75b639e8320710cd36213ac72a65ed
4
+ data.tar.gz: 6ef7b9c8edcfdd1cda3aadfedeff6fef99c1194e92782c3dfe1b1abcc8052e96
5
5
  SHA512:
6
- metadata.gz: d988772978387faa28692101e6b2dab580a93b6f5738ad9094faf22d8aed78aca68123982ff0a25d9b9029d4d27ef4f82788009b10942a14ce5dd9bd9f1e37a7
7
- data.tar.gz: 711b3188b0dd14a9821eee5227ee0d574c60144b7be88a3f722d10b4506556fa864685659c7493acfd6d63c2cf82f8c0c6c36cc0431a57a2d476efe3b4bf7efd
6
+ metadata.gz: 901731bc9670cab315c53e864430636c14c70b9c15d2218d81ae5657b40e5261b79d7ae7ff7937a638931a0c89af8d75f99a65973dd4cbd9a3418588f6388e87
7
+ data.tar.gz: fdedef4073b56af0e5bd2f348b15149dd80ab96d8814b5f0f114529372594ae661203509306603b6be8773976f577aaf73466d6a778a4596f11fd0d383c88dd6
data/README.md CHANGED
@@ -22,17 +22,28 @@ Although __Lox__ is fairly simple, it is far from being a toy language:
22
22
  - Minimal runtime dependency (Rley gem). Won't drag a bunch of gems...
23
23
  - Ruby API for integrating a Lox interpreter with your code.
24
24
  - A command-line interpreter `loxxy`
25
- - Open for your language extensions
25
+ - Open for your language extensions...
26
26
 
27
27
  ## How to start in 1, 2, 3...?
28
28
  ... in less than 3 minutes...
29
29
 
30
30
  ### 1. Installing
31
- Install __Loxxy__ as a gem:
31
+ __Loxxy__'s installation is pretty standard:
32
32
 
33
33
 
34
34
  $ gem install loxxy
35
35
 
36
+ Alternatively, you can install `loxxy` with Bundler.
37
+ Add this line to your application's Gemfile:
38
+
39
+ gem 'loxxy'
40
+
41
+ And then execute:
42
+
43
+ $ bundle
44
+
45
+
46
+
36
47
  ### 2. Your first `Lox` program
37
48
  Create a text file and enter the following lines:
38
49
  ```javascript
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loxxy
4
+ module Ast
5
+ # Mix-in module that relies on meta-programming to add a method
6
+ # called `accept` to the host class (an AST node).
7
+ # That method fulfills the expected behavior of the `visitee` in
8
+ # the Visitor design pattern.
9
+ module ASTVisitee
10
+ # Convert the input string into a snake case string.
11
+ # Example: ClassExpr => class_expr
12
+ # @param aName [String] input name to convert
13
+ # @return [String] the name converted into snake case
14
+ def snake_case(aName)
15
+ converted = +''
16
+ down = false
17
+ aName.each_char do |ch|
18
+ lower = ch.downcase
19
+ if lower == ch
20
+ converted << ch
21
+ down = true
22
+ else
23
+ converted << '_' if down && converted[-1] != '_'
24
+ converted << lower
25
+ down = false
26
+ end
27
+ end
28
+
29
+ converted
30
+ end
31
+
32
+ # This method adds a method named `accept` that takes a visitor
33
+ # The visitor is expected to implement a method named:
34
+ # visit + class name (without the Lox prefix) in snake case
35
+ # Example: class name = LoxClassStmt => visit method = visit_class_stmt
36
+ def define_accept
37
+ return if instance_methods(false).include?(:accept)
38
+
39
+ base_name = name.split('::').last
40
+ name_suffix = snake_case(base_name).sub(/^lox/, '')
41
+ accept_body = <<-MTH_END
42
+ # Part of the 'visitee' role in Visitor design pattern.
43
+ # @param visitor [Ast::ASTVisitor] the visitor
44
+ def accept(aVisitor)
45
+ aVisitor.visit#{name_suffix}(self)
46
+ end
47
+ MTH_END
48
+
49
+ class_eval(accept_body)
50
+ end
51
+ end # module
52
+ end # module
53
+ end # module
@@ -133,7 +133,7 @@ module Loxxy
133
133
 
134
134
  # @param aSetExpr [AST::LOXGetExpr] the get expression node to visit
135
135
  def visit_set_expr(aSetExpr)
136
- broadcast(:before_set_expr, aSetExpr)
136
+ broadcast(:before_set_expr, aSetExpr, self)
137
137
  traverse_subnodes(aSetExpr)
138
138
  broadcast(:after_set_expr, aSetExpr, self)
139
139
  end
@@ -17,11 +17,7 @@ module Loxxy
17
17
  @name = aName
18
18
  end
19
19
 
20
- # Part of the 'visitee' role in Visitor design pattern.
21
- # @param visitor [Ast::ASTVisitor] the visitor
22
- def accept(visitor)
23
- visitor.visit_assign_expr(self)
24
- end
20
+ define_accept # Add `accept` method as found in Visitor design pattern
25
21
  end # class
26
22
  end # module
27
23
  end # module
@@ -16,12 +16,7 @@ module Loxxy
16
16
  @operator = anOperator
17
17
  end
18
18
 
19
- # Part of the 'visitee' role in Visitor design pattern.
20
- # @param visitor [Ast::ASTVisitor] the visitor
21
- def accept(visitor)
22
- visitor.visit_binary_expr(self)
23
- end
24
-
19
+ define_accept # Add `accept` method as found in Visitor design pattern
25
20
  alias operands subnodes
26
21
  end # class
27
22
  end # module
@@ -15,11 +15,7 @@ module Loxxy
15
15
  subnodes.size == 1 && subnodes[0].nil?
16
16
  end
17
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_block_stmt(self)
22
- end
18
+ define_accept # Add `accept` method as found in Visitor design pattern
23
19
  end # class
24
20
  end # module
25
21
  end # module
@@ -15,11 +15,7 @@ module Loxxy
15
15
  @arguments = argList
16
16
  end
17
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
18
+ define_accept # Add `accept` method as found in Visitor design pattern
23
19
  end # class
24
20
  end # module
25
21
  end # module
@@ -24,11 +24,7 @@ module Loxxy
24
24
  @body = theMethods
25
25
  end
26
26
 
27
- # Part of the 'visitee' role in Visitor design pattern.
28
- # @param visitor [Ast::ASTVisitor] the visitor
29
- def accept(visitor)
30
- visitor.visit_class_stmt(self)
31
- end
27
+ define_accept # Add `accept` method as found in Visitor design pattern
32
28
  end # class
33
29
  end # module
34
30
  end # module
@@ -25,17 +25,13 @@ module Loxxy
25
25
  @update_expr = updateExpr
26
26
  end
27
27
 
28
- # Part of the 'visitee' role in Visitor design pattern.
29
- # @param visitor [Ast::ASTVisitor] the visitor
30
- def accept(visitor)
31
- visitor.visit_for_stmt(self)
32
- end
33
-
34
28
  # Accessor to the condition expression
35
29
  # @return [LoxNode]
36
30
  def condition
37
31
  subnodes[0]
38
32
  end
33
+
34
+ define_accept # Add `accept` method as found in Visitor design pattern
39
35
  end # class
40
36
  end # module
41
37
  end # module
@@ -23,11 +23,7 @@ module Loxxy
23
23
  @is_method = false
24
24
  end
25
25
 
26
- # Part of the 'visitee' role in Visitor design pattern.
27
- # @param visitor [Ast::ASTVisitor] the visitor
28
- def accept(visitor)
29
- visitor.visit_fun_stmt(self)
30
- end
26
+ define_accept # Add `accept` method as found in Visitor design pattern
31
27
  end # class
32
28
  # rubocop: enable Style/AccessorGrouping
33
29
  end # module
@@ -18,12 +18,7 @@ module Loxxy
18
18
  @property = aPropertyName
19
19
  end
20
20
 
21
- # Part of the 'visitee' role in Visitor design pattern.
22
- # @param visitor [ASTVisitor] the visitor
23
- def accept(visitor)
24
- visitor.visit_get_expr(self)
25
- end
26
-
21
+ define_accept # Add `accept` method as found in Visitor design pattern
27
22
  alias callee= object=
28
23
  end # class
29
24
  end # module
@@ -11,11 +11,7 @@ module Loxxy
11
11
  super(aPosition, [subExpr])
12
12
  end
13
13
 
14
- # Part of the 'visitee' role in Visitor design pattern.
15
- # @param visitor [Ast::ASTVisitor] the visitor
16
- def accept(visitor)
17
- visitor.visit_grouping_expr(self)
18
- end
14
+ define_accept # Add `accept` method as found in Visitor design pattern
19
15
  end # class
20
16
  end # module
21
17
  end # module
@@ -21,17 +21,13 @@ module Loxxy
21
21
  @else_stmt = elseStmt
22
22
  end
23
23
 
24
- # Part of the 'visitee' role in Visitor design pattern.
25
- # @param visitor [Ast::ASTVisitor] the visitor
26
- def accept(visitor)
27
- visitor.visit_if_stmt(self)
28
- end
29
-
30
24
  # Accessor to the condition expression
31
25
  # @return [LoxNode]
32
26
  def condition
33
27
  subnodes[0]
34
28
  end
29
+
30
+ define_accept # Add `accept` method as found in Visitor design pattern
35
31
  end # class
36
32
  end # module
37
33
  end # module
@@ -15,11 +15,7 @@ module Loxxy
15
15
  @literal = aLiteral
16
16
  end
17
17
 
18
- # Part of the 'visitee' role in Visitor design pattern.
19
- # @param visitor [ASTVisitor] the visitor
20
- def accept(visitor)
21
- visitor.visit_literal_expr(self)
22
- end
18
+ define_accept # Add `accept` method as found in Visitor design pattern
23
19
  end # class
24
20
  end # module
25
21
  end # module
@@ -16,12 +16,7 @@ module Loxxy
16
16
  @operator = anOperator
17
17
  end
18
18
 
19
- # Part of the 'visitee' role in Visitor design pattern.
20
- # @param visitor [Ast::ASTVisitor] the visitor
21
- def accept(visitor)
22
- visitor.visit_logical_expr(self)
23
- end
24
-
19
+ define_accept # Add `accept` method as found in Visitor design pattern
25
20
  alias operands subnodes
26
21
  end # class
27
22
  end # module
@@ -1,8 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'ast_visitee'
4
+
3
5
  module Loxxy
4
6
  module Ast
7
+ # Abstract class.
8
+ # Instances of its subclasses represent nodes of an abstract syntax tree
9
+ # that is the product of the parse of an input text.
5
10
  class LoxNode
11
+ # Let nodes take `visitee` role as defined in the Visitor design pattern
12
+ extend ASTVisitee
13
+
6
14
  # return [Rley::Lexical::Position] Position of the entry in the input stream.
7
15
  attr_reader :position
8
16
 
@@ -16,7 +24,7 @@ module Loxxy
16
24
  # Default: do nothing ...
17
25
  end
18
26
 
19
- # Abstract method.
27
+ # Abstract method (must be overriden in subclasses).
20
28
  # Part of the 'visitee' role in Visitor design pattern.
21
29
  # @param _visitor [LoxxyTreeVisitor] the visitor
22
30
  def accept(_visitor)
@@ -11,11 +11,7 @@ module Loxxy
11
11
  super(aPosition, [anExpression])
12
12
  end
13
13
 
14
- # Part of the 'visitee' role in Visitor design pattern.
15
- # @param visitor [Ast::ASTVisitor] the visitor
16
- def accept(visitor)
17
- visitor.visit_print_stmt(self)
18
- end
14
+ define_accept # Add `accept` method as found in Visitor design pattern
19
15
  end # class
20
16
  end # module
21
17
  end # module
@@ -12,11 +12,7 @@ module Loxxy
12
12
  super(aPosition, [expr])
13
13
  end
14
14
 
15
- # Part of the 'visitee' role in Visitor design pattern.
16
- # @param visitor [Ast::ASTVisitor] the visitor
17
- def accept(visitor)
18
- visitor.visit_return_stmt(self)
19
- end
15
+ define_accept # Add `accept` method as found in Visitor design pattern
20
16
  end # class
21
17
  end # module
22
18
  end # module
@@ -5,11 +5,7 @@ require_relative 'lox_compound_expr'
5
5
  module Loxxy
6
6
  module Ast
7
7
  class LoxSeqDecl < LoxCompoundExpr
8
- # Part of the 'visitee' role in Visitor design pattern.
9
- # @param visitor [Ast::ASTVisitor] the visitor
10
- def accept(visitor)
11
- visitor.visit_seq_decl(self)
12
- end
8
+ define_accept # Add `accept` method as found in Visitor design pattern
13
9
  end # class
14
10
  end # module
15
11
  end # module
@@ -18,11 +18,7 @@ module Loxxy
18
18
  @object = anObject
19
19
  end
20
20
 
21
- # Part of the 'visitee' role in Visitor design pattern.
22
- # @param visitor [ASTVisitor] the visitor
23
- def accept(visitor)
24
- visitor.visit_set_expr(self)
25
- end
21
+ define_accept # Add `accept` method as found in Visitor design pattern
26
22
  end # class
27
23
  end # module
28
24
  end # module
@@ -18,17 +18,13 @@ module Loxxy
18
18
  @property = aMethodName
19
19
  end
20
20
 
21
- # Part of the 'visitee' role in Visitor design pattern.
22
- # @param visitor [ASTVisitor] the visitor
23
- def accept(visitor)
24
- visitor.visit_super_expr(self)
25
- end
26
-
27
21
  # Quack like a LoxVariableExpr
28
22
  # @return [String] the `super` keyword
29
23
  def name
30
24
  'super'
31
25
  end
26
+
27
+ define_accept # Add `accept` method as found in Visitor design pattern
32
28
  alias callee= object=
33
29
  end # class
34
30
  end # module
@@ -12,11 +12,7 @@ module Loxxy
12
12
  'this'
13
13
  end
14
14
 
15
- # Part of the 'visitee' role in Visitor design pattern.
16
- # @param _visitor [LoxxyTreeVisitor] the visitor
17
- def accept(aVisitor)
18
- aVisitor.visit_this_expr(self)
19
- end
15
+ define_accept # Add `accept` method as found in Visitor design pattern
20
16
  end # class
21
17
  end # module
22
18
  end # module
@@ -15,12 +15,7 @@ module Loxxy
15
15
  @operator = anOperator
16
16
  end
17
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_unary_expr(self)
22
- end
23
-
18
+ define_accept # Add `accept` method as found in Visitor design pattern
24
19
  alias operands subnodes
25
20
  end # class
26
21
  end # module
@@ -18,11 +18,7 @@ module Loxxy
18
18
  @name = aName
19
19
  end
20
20
 
21
- # Part of the 'visitee' role in Visitor design pattern.
22
- # @param visitor [Ast::ASTVisitor] the visitor
23
- def accept(visitor)
24
- visitor.visit_var_stmt(self)
25
- end
21
+ define_accept # Add `accept` method as found in Visitor design pattern
26
22
  end # class
27
23
  end # module
28
24
  end # module
@@ -16,11 +16,7 @@ module Loxxy
16
16
  @name = aName
17
17
  end
18
18
 
19
- # Part of the 'visitee' role in Visitor design pattern.
20
- # @param visitor [Ast::ASTVisitor] the visitor
21
- def accept(visitor)
22
- visitor.visit_variable_expr(self)
23
- end
19
+ define_accept # Add `accept` method as found in Visitor design pattern
24
20
  end # class
25
21
  end # module
26
22
  end # module
@@ -16,17 +16,13 @@ module Loxxy
16
16
  @body = theBody
17
17
  end
18
18
 
19
- # Part of the 'visitee' role in Visitor design pattern.
20
- # @param visitor [Ast::ASTVisitor] the visitor
21
- def accept(visitor)
22
- visitor.visit_while_stmt(self)
23
- end
24
-
25
19
  # Accessor to the condition expression
26
20
  # @return [LoxNode]
27
21
  def condition
28
22
  subnodes[0]
29
23
  end
24
+
25
+ define_accept # Add `accept` method as found in Visitor design pattern
30
26
  end # class
31
27
  end # module
32
28
  end # module
@@ -176,16 +176,20 @@ module Loxxy
176
176
  variable.assign(value)
177
177
  end
178
178
 
179
- def after_set_expr(aSetExpr, aVisitor)
180
- value = stack.pop
181
- # Evaluate object part
179
+ def before_set_expr(aSetExpr, aVisitor)
180
+ # Evaluate receiver object part
182
181
  aSetExpr.object.accept(aVisitor)
182
+ end
183
+
184
+ def after_set_expr(aSetExpr, _visitor)
185
+ value = stack.pop
183
186
  assignee = stack.pop
184
187
  unless assignee.kind_of?(LoxInstance)
185
- raise StandardError, 'Only instances have fields.'
188
+ raise Loxxy::RuntimeError, 'Only instances have fields.'
186
189
  end
187
190
 
188
191
  assignee.set(aSetExpr.property, value)
192
+ stack.push value
189
193
  end
190
194
 
191
195
  def after_logical_expr(aLogicalExpr, visitor)
@@ -272,7 +276,7 @@ module Loxxy
272
276
  aGetExpr.object.accept(aVisitor)
273
277
  instance = stack.pop
274
278
  unless instance.kind_of?(LoxInstance)
275
- raise StandardError, 'Only instances have properties.'
279
+ raise Loxxy::RuntimeError, 'Only instances have properties.'
276
280
  end
277
281
 
278
282
  stack.push instance.get(aGetExpr.property)
@@ -285,7 +289,7 @@ module Loxxy
285
289
  def after_variable_expr(aVarExpr, aVisitor)
286
290
  var_name = aVarExpr.name
287
291
  var = variable_lookup(aVarExpr)
288
- raise StandardError, "Undefined variable '#{var_name}'." unless var
292
+ raise Loxxy::RuntimeError, "Undefined variable '#{var_name}'." unless var
289
293
 
290
294
  var.value.accept(aVisitor) # Evaluate variable value then push on stack
291
295
  end
@@ -38,7 +38,7 @@ module Loxxy
38
38
 
39
39
  method = klass.find_method(aName)
40
40
  unless method
41
- raise StandardError, "Undefined property '#{aName}'."
41
+ raise Loxxy::RuntimeError, "Undefined property '#{aName}'."
42
42
  end
43
43
 
44
44
  method.bind(self)
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.02'
4
+ VERSION = '0.2.03'
5
5
  end
@@ -480,6 +480,18 @@ LOX_END
480
480
  snippet
481
481
  end
482
482
 
483
+ it 'should support field assignment expression' do
484
+ program = <<-LOX_END
485
+ class Foo {}
486
+
487
+ var foo = Foo();
488
+
489
+ print foo.bar = "bar value"; // expect: bar value
490
+ LOX_END
491
+ expect { subject.evaluate(program) }.not_to raise_error
492
+ expect(sample_cfg[:ostream].string).to eq('bar value')
493
+ end
494
+
483
495
  it 'should support class declaration' do
484
496
  program = <<-LOX_END
485
497
  #{duck_class}
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.02
4
+ version: 0.2.03
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-21 00:00:00.000000000 Z
11
+ date: 2021-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -91,6 +91,7 @@ files:
91
91
  - lib/loxxy.rb
92
92
  - lib/loxxy/ast/all_lox_nodes.rb
93
93
  - lib/loxxy/ast/ast_builder.rb
94
+ - lib/loxxy/ast/ast_visitee.rb
94
95
  - lib/loxxy/ast/ast_visitor.rb
95
96
  - lib/loxxy/ast/lox_assign_expr.rb
96
97
  - lib/loxxy/ast/lox_binary_expr.rb