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 +4 -4
- data/CHANGELOG.md +17 -3
- data/README.md +2 -2
- data/lib/loxxy/ast/all_lox_nodes.rb +1 -0
- data/lib/loxxy/ast/ast_builder.rb +5 -0
- data/lib/loxxy/ast/ast_visitor.rb +7 -0
- data/lib/loxxy/ast/lox_this_expr.rb +22 -0
- data/lib/loxxy/back_end/engine.rb +5 -0
- data/lib/loxxy/back_end/environment.rb +0 -1
- data/lib/loxxy/back_end/lox_function.rb +10 -0
- data/lib/loxxy/back_end/lox_instance.rb +4 -4
- data/lib/loxxy/back_end/resolver.rb +23 -0
- data/lib/loxxy/front_end/grammar.rb +1 -1
- data/lib/loxxy/version.rb +1 -1
- data/spec/interpreter_spec.rb +16 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51728e22602ac35f1c31a9540daf75d4dcf0e84d1f323dd3ec287440b6fe21ef
|
4
|
+
data.tar.gz: beda9e48d5024a567bf2e1dd84f107991443e8bf2592b2a01f39e2ca39adc751
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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#
|
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
|
-
|
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
|
@@ -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)
|
@@ -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 :
|
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
|
-
@
|
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
data/spec/interpreter_spec.rb
CHANGED
@@ -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.
|
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
|