loxxy 0.1.13 → 0.1.14

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