loxxy 0.2.02 → 0.2.03

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