loxxy 0.1.13 → 0.1.14

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: 172ec5df8e553332b497968f7b910383cb38314aabbdb84fdb42fba6986841ea
4
- data.tar.gz: 3d72fd4f13f507f88e9e735e1ebdd0d63fd603b771f9cfb552b9aa38b5b3ca6a
3
+ metadata.gz: 51728e22602ac35f1c31a9540daf75d4dcf0e84d1f323dd3ec287440b6fe21ef
4
+ data.tar.gz: beda9e48d5024a567bf2e1dd84f107991443e8bf2592b2a01f39e2ca39adc751
5
5
  SHA512:
6
- metadata.gz: 61073737ef6c069e6e1177fa79cdac543253e06ea63d2ca1b00517c08a06f46948fe7d51257308935d92579152793b4fe60bc0f5b901a1c931e5eafab9f35ecd
7
- data.tar.gz: 57c87936451c3015511740d7d44f01d8f032a06e597bdfd1d133119413c227a61df11847f86a4be3a2b3440cc1f614a43b1144cceef05a58ade56ca1fa3c598b
6
+ metadata.gz: 72688ddf02138d6bab0cdbf8bcb3c0087a17e461dbd269fa56f4ddfe4f674172fd957ae071f0e61b6c7296ff3653a2734cc4fb9573340914337621b1d198ed0d
7
+ data.tar.gz: 45374a6d536f32c81f8bfdea4eeb670bb8c9d0ea0bdbc1d1c151379601d75f11e0353f20feb705a68cb647352f49c4c1231e7ba087081b9720297f79d1427db7
data/CHANGELOG.md CHANGED
@@ -1,7 +1,21 @@
1
+ ## [0.1.14] - 2021-04-05
2
+ - `Loxxy` now implements the 'this' keyword
3
+
4
+ ### New
5
+ - Class `Ast::LoxThisExpr` a syntax node that represents an occurrence of the `this` keyword
6
+ - Method `Ast::ASTBuilder#reduce_this_expr` parse action for this keyword
7
+ - Method `Ast::Visitor#visit_this_expr` visit of an `Ast::LoxThisExpr` node
8
+ - Method `BackEnd::Engine#after_this_expr` runtime action for this keyword (i.e. refers to current instance)
9
+ - Method `BackEnd::LoxFunction#bind` implementation of bound method
10
+
11
+ ### Changed
12
+ - Class `BackEnd::Resolver` implementing semantic actions for `this` keyword
13
+ - File `grammar.rb` added name to a syntax rule
14
+
1
15
  ## [0.1.13] - 2021-04-05
2
16
  - `Loxxy` now implements method calls
3
17
 
4
- ## New
18
+ ### New
5
19
  - Class `Ast::LoxGetExpr` a syntax node that represents a read access to an object property
6
20
  - Class `Ast::LoxSetExpr` a syntax node that represents a write access to an object property
7
21
  - Method `Ast::ASTBuilder#reduce_set_expr` parse action for write access to an object property
@@ -11,11 +25,11 @@
11
25
  - Method `BackEnd::Engine#after_set_expr` runtime action for property setting
12
26
  - Method `BackEnd::Engine#after_get_expr` runtime action for property getting
13
27
  - Method `BackEnd::LoxInstance#set` implementation of write accessor
14
- - Method `BackEnd::LoxInstance#getr` implementation of read accessor
28
+ - Method `BackEnd::LoxInstance#get` implementation of read accessor
15
29
  - Method `BackEnd::Resolver#after_set_expr` resolve action for property setting
16
30
  - Method `BackEnd::Resolver#after_get_expr` resolve action for property getting
17
31
 
18
- ## Changed
32
+ ### Changed
19
33
  - Method `Ast::ASTBuilder#reduce_assign_expr` expanded to support write access to an object property
20
34
  - Class `LoxClassStmt`: methods are now aggregate under the `body` attribute
21
35
  - Class `LoxFunStmt`: has a new attribute `is_method` and inherits from `Ast::LoxNode`
data/README.md CHANGED
@@ -177,8 +177,8 @@ Loxxy supports single line C-style comments.
177
177
  ### Keywords
178
178
  Loxxy implements the following __Lox__ reserved keywords:
179
179
  ```lang-none
180
- and, else, false, for, fun, if,
181
- nil, or, print, true, var, while
180
+ and, class, else, false, for, fun, if,
181
+ nil, or, print, return, this, true, var, while
182
182
  ```
183
183
 
184
184
  ### Datatypes
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'lox_fun_stmt'
4
+ require_relative 'lox_this_expr'
4
5
  require_relative 'lox_variable_expr'
5
6
  require_relative 'lox_literal_expr'
6
7
  require_relative 'lox_noop_expr'
@@ -354,6 +354,11 @@ module Loxxy
354
354
  LoxVariableExpr.new(tokens[0].position, var_name)
355
355
  end
356
356
 
357
+ # rule('primary' => 'THIS')
358
+ def reduce_this_expr(_production, _range, tokens, _children)
359
+ LoxThisExpr.new(tokens[0].position)
360
+ end
361
+
357
362
  # rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block').as 'function'
358
363
  def reduce_function(_production, _range, _tokens, theChildren)
359
364
  first_child = theChildren.first
@@ -205,6 +205,13 @@ module Loxxy
205
205
  broadcast(:after_variable_expr, aVariableExpr, self)
206
206
  end
207
207
 
208
+ # Visit event. The visitor is about to visit the this keyword.
209
+ # @param aThisExpr [Ast::LoxThisExpr] this expression
210
+ def visit_this_expr(aThisExpr)
211
+ broadcast(:before_this_expr, aThisExpr)
212
+ broadcast(:after_this_expr, aThisExpr, self)
213
+ end
214
+
208
215
  # Visit event. The visitor is about to visit the given terminal datatype value.
209
216
  # @param aValue [Ast::BuiltinDattype] the built-in datatype value
210
217
  def visit_builtin(aValue)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_node'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ # A node in a parse tree that represents the occurrence of 'this' keyword.
8
+ class LoxThisExpr < LoxNode
9
+ # Duck-typing: behaves like a LoxVarExpr
10
+ # @return [String] return the this keyword
11
+ def name
12
+ 'this'
13
+ end
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
20
+ end # class
21
+ end # module
22
+ end # module
@@ -281,6 +281,11 @@ module Loxxy
281
281
  stack.push(literalExpr.literal)
282
282
  end
283
283
 
284
+ def after_this_expr(aThisExpr, aVisitor)
285
+ var = variable_lookup(aThisExpr)
286
+ var.value.accept(aVisitor) # Evaluate this value then push on stack
287
+ end
288
+
284
289
  # @param aValue [Ast::BuiltinDattype] the built-in datatype value
285
290
  def before_visit_builtin(aValue)
286
291
  stack.push(aValue)
@@ -37,7 +37,6 @@ module Loxxy
37
37
  # @return [BackEnd::Variable] the variable
38
38
  def insert(anEntry)
39
39
  e = validated_entry(anEntry)
40
- # e.suffix = default_suffix if e.kind_of?(BackEnd::Variable)
41
40
  defns[e.name] = e
42
41
 
43
42
  e
@@ -52,6 +52,16 @@ module Loxxy
52
52
  engine.symbol_table.leave_environment
53
53
  end
54
54
 
55
+ def bind(anInstance)
56
+ new_env = Environment.new(closure)
57
+ this = Variable.new('this', anInstance)
58
+ new_env.insert(this)
59
+ bound_method = dup
60
+ bound_method.instance_variable_set(:@closure, new_env)
61
+
62
+ bound_method
63
+ end
64
+
55
65
  # Logical negation.
56
66
  # As a function is a truthy thing, its negation is thus false.
57
67
  # @return [Datatype::False]
@@ -9,7 +9,7 @@ module Loxxy
9
9
  # @return BackEnd::LoxClass] the class that this object is an instance of
10
10
  attr_reader :klass
11
11
 
12
- attr_reader :stack
12
+ attr_reader :engine
13
13
 
14
14
  # @return [Hash{String => BuiltinDatatype | LoxFunction | LoxInstance }]
15
15
  attr_reader :fields
@@ -18,12 +18,12 @@ module Loxxy
18
18
  # @param aClass [BackEnd::LoxClass] the class this this object belong
19
19
  def initialize(aClass, anEngine)
20
20
  @klass = aClass
21
- @stack = anEngine.stack
21
+ @engine = anEngine
22
22
  @fields = {}
23
23
  end
24
24
 
25
25
  def accept(_visitor)
26
- stack.push self
26
+ engine.stack.push self
27
27
  end
28
28
 
29
29
  # Text representation of a Lox instance
@@ -41,7 +41,7 @@ module Loxxy
41
41
  raise StandardError, "Undefined property '#{aName}'."
42
42
  end
43
43
 
44
- method
44
+ method.bind(self)
45
45
  end
46
46
 
47
47
  # Set the value of property with given name
@@ -26,10 +26,15 @@ module Loxxy
26
26
  # @return [Symbol] must be one of: :none, :function
27
27
  attr_reader :current_function
28
28
 
29
+ # An indicator that tells we're in the middle of a class declaration
30
+ # @return [Symbol] must be one of: :none, :class
31
+ attr_reader :current_class
32
+
29
33
  def initialize
30
34
  @scopes = []
31
35
  @locals = {}
32
36
  @current_function = :none
37
+ @current_class = :none
33
38
  end
34
39
 
35
40
  # Given an abstract syntax parse tree visitor, launch the visit
@@ -59,10 +64,16 @@ module Loxxy
59
64
  end
60
65
 
61
66
  def after_class_stmt(aClassStmt, aVisitor)
67
+ previous_class = current_class
68
+ @current_class = :class
62
69
  define(aClassStmt.name)
70
+ begin_scope
71
+ define('this')
63
72
  aClassStmt.body.each do |fun_stmt|
64
73
  resolve_function(fun_stmt, :method, aVisitor)
65
74
  end
75
+ end_scope
76
+ @current_class = previous_class
66
77
  end
67
78
 
68
79
  def before_for_stmt(aForStmt)
@@ -140,6 +151,18 @@ module Loxxy
140
151
  aGetExpr.object.accept(aVisitor)
141
152
  end
142
153
 
154
+ def before_this_expr(_thisExpr)
155
+ if current_class == :none
156
+ msg = "Error at 'this': Can't use 'this' outside of a class."
157
+ raise StandardError, msg
158
+ end
159
+ end
160
+
161
+ def after_this_expr(aThisExpr, aVisitor)
162
+ # 'this' behaves closely to a local variable
163
+ resolve_local(aThisExpr, aVisitor)
164
+ end
165
+
143
166
  # function declaration creates a new scope for its body & binds its parameters for that scope
144
167
  def before_fun_stmt(aFunStmt, aVisitor)
145
168
  declare(aFunStmt.name)
@@ -136,7 +136,7 @@ module Loxxy
136
136
  rule('primary' => 'TRUE').as 'literal_expr'
137
137
  rule('primary' => 'FALSE').as 'literal_expr'
138
138
  rule('primary' => 'NIL').as 'literal_expr'
139
- rule('primary' => 'THIS')
139
+ rule('primary' => 'THIS').as 'this_expr'
140
140
  rule('primary' => 'NUMBER').as 'literal_expr'
141
141
  rule('primary' => 'STRING').as 'literal_expr'
142
142
  rule('primary' => 'IDENTIFIER').as 'variable_expr'
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.1.13'
4
+ VERSION = '0.1.14'
5
5
  end
@@ -466,7 +466,7 @@ LOX_END
466
466
  snippet = <<-LOX_END
467
467
  class Duck {
468
468
  noise() {
469
- quack();
469
+ this.quack();
470
470
  }
471
471
 
472
472
  quack() {
@@ -509,6 +509,21 @@ LOX_END
509
509
  expect { subject.evaluate(program) }.not_to raise_error
510
510
  expect(sample_cfg[:ostream].string).to eq('quack')
511
511
  end
512
+
513
+ it "should support the 'this' keyword" do
514
+ program = <<-LOX_END
515
+ class Egotist {
516
+ speak() {
517
+ print this;
518
+ }
519
+ }
520
+
521
+ var method = Egotist().speak;
522
+ method(); // Output: Egotist instance
523
+ LOX_END
524
+ expect { subject.evaluate(program) }.not_to raise_error
525
+ expect(sample_cfg[:ostream].string).to eq('Egotist instance')
526
+ end
512
527
  end # context
513
528
  end # describe
514
529
  # rubocop: enable Metrics/BlockLength
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
@@ -108,6 +108,7 @@ files:
108
108
  - lib/loxxy/ast/lox_return_stmt.rb
109
109
  - lib/loxxy/ast/lox_seq_decl.rb
110
110
  - lib/loxxy/ast/lox_set_expr.rb
111
+ - lib/loxxy/ast/lox_this_expr.rb
111
112
  - lib/loxxy/ast/lox_unary_expr.rb
112
113
  - lib/loxxy/ast/lox_var_stmt.rb
113
114
  - lib/loxxy/ast/lox_variable_expr.rb