loxxy 0.1.08 → 0.1.13
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 +79 -4
- data/README.md +2 -3
- data/lib/loxxy/ast/all_lox_nodes.rb +3 -0
- data/lib/loxxy/ast/ast_builder.rb +44 -2
- data/lib/loxxy/ast/ast_visitor.rb +21 -1
- data/lib/loxxy/ast/lox_class_stmt.rb +30 -0
- data/lib/loxxy/ast/lox_fun_stmt.rb +6 -4
- data/lib/loxxy/ast/lox_get_expr.rb +30 -0
- data/lib/loxxy/ast/lox_return_stmt.rb +2 -1
- data/lib/loxxy/ast/lox_set_expr.rb +28 -0
- data/lib/loxxy/back_end/engine.rb +45 -3
- data/lib/loxxy/back_end/lox_class.rb +59 -0
- data/lib/loxxy/back_end/lox_function.rb +1 -3
- data/lib/loxxy/back_end/lox_instance.rb +58 -0
- data/lib/loxxy/back_end/resolver.rb +48 -2
- data/lib/loxxy/front_end/grammar.rb +9 -7
- data/lib/loxxy/version.rb +1 -1
- data/spec/front_end/parser_spec.rb +77 -0
- data/spec/interpreter_spec.rb +50 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 172ec5df8e553332b497968f7b910383cb38314aabbdb84fdb42fba6986841ea
|
4
|
+
data.tar.gz: 3d72fd4f13f507f88e9e735e1ebdd0d63fd603b771f9cfb552b9aa38b5b3ca6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61073737ef6c069e6e1177fa79cdac543253e06ea63d2ca1b00517c08a06f46948fe7d51257308935d92579152793b4fe60bc0f5b901a1c931e5eafab9f35ecd
|
7
|
+
data.tar.gz: 57c87936451c3015511740d7d44f01d8f032a06e597bdfd1d133119413c227a61df11847f86a4be3a2b3440cc1f614a43b1144cceef05a58ade56ca1fa3c598b
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,85 @@
|
|
1
|
+
## [0.1.13] - 2021-04-05
|
2
|
+
- `Loxxy` now implements method calls
|
3
|
+
|
4
|
+
## New
|
5
|
+
- Class `Ast::LoxGetExpr` a syntax node that represents a read access to an object property
|
6
|
+
- Class `Ast::LoxSetExpr` a syntax node that represents a write access to an object property
|
7
|
+
- Method `Ast::ASTBuilder#reduce_set_expr` parse action for write access to an object property
|
8
|
+
- Method `Ast::ASTBuilder#reduce_get_expr` parse action for read access to an object property
|
9
|
+
- Method `Ast::Visitor#visit_set_expr` visit of an `Ast::LoxSetExpr` node
|
10
|
+
- Method `Ast::Visitor#visit_get_expr` visit of an `Ast::LoxGetExpr` node
|
11
|
+
- Method `BackEnd::Engine#after_set_expr` runtime action for property setting
|
12
|
+
- Method `BackEnd::Engine#after_get_expr` runtime action for property getting
|
13
|
+
- Method `BackEnd::LoxInstance#set` implementation of write accessor
|
14
|
+
- Method `BackEnd::LoxInstance#getr` implementation of read accessor
|
15
|
+
- Method `BackEnd::Resolver#after_set_expr` resolve action for property setting
|
16
|
+
- Method `BackEnd::Resolver#after_get_expr` resolve action for property getting
|
17
|
+
|
18
|
+
## Changed
|
19
|
+
- Method `Ast::ASTBuilder#reduce_assign_expr` expanded to support write access to an object property
|
20
|
+
- Class `LoxClassStmt`: methods are now aggregate under the `body` attribute
|
21
|
+
- Class `LoxFunStmt`: has a new attribute `is_method` and inherits from `Ast::LoxNode`
|
22
|
+
- Method `BackEnd::Engine#after_class_stmt` methods are aggregated into the classes
|
23
|
+
- Method `BackEnd::Engine#after_fun_stmt` extension for method
|
24
|
+
- File `grammar.rb` added names to two syntax rules
|
25
|
+
|
26
|
+
|
27
|
+
## [0.1.12] - 2021-04-03
|
28
|
+
- Intermediate version: `Loxxy` does instance creation (default constructor)
|
29
|
+
|
30
|
+
### New
|
31
|
+
- Method `BackEnd::LoxClass#call` made class callable (for invoking the constructor)
|
32
|
+
- Class `BackEnd::LoxInstance` runtime representation of a Lox instance (object).
|
33
|
+
|
34
|
+
### Changed
|
35
|
+
- Method `BackEnd::Engine#after_call_expr` Added Lox class as callable thing.
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
- Method `Ast::ASTBuilder#reduce_class_body` couldn't handle properly empty classes
|
39
|
+
|
40
|
+
## [0.1.11] - 2021-04-03
|
41
|
+
- Intermediate version: `Loxxy` does class declarations
|
42
|
+
|
43
|
+
### New
|
44
|
+
- Class `Ast::LoxClassStmt` a syntax node that represents a class declaration
|
45
|
+
- Method `Ast::ASTBuilder#reduce_class_decl` creates a `LoxClassStmt` instance
|
46
|
+
- Method `Ast::ASTBuilder#reduce_class_name`
|
47
|
+
- Method `Ast::ASTBuilder#reduce_reduce_class_body` collect the methods of the class
|
48
|
+
- Method `Ast::ASTBuilder#reduce_method_plus_more` for dealing with methods
|
49
|
+
- Method `Ast::ASTBuilder#reduce_method_plus_end`
|
50
|
+
- Method `Ast::ASTVisitor#visit_class_stmt` for visiting an `Ast::LoxClassStmt` node
|
51
|
+
- Method `Ast::LoxBlockStmt#empty?` returns true if the code block is empty
|
52
|
+
- Method `BackEnd::Engine#after_class_stmt`
|
53
|
+
- Method `BackEnd::Resolver#after_class_stmt`
|
54
|
+
- Method `BackEnd::Resolver#before_class_stmt`
|
55
|
+
- Class `BackEnd::LoxClass` runtime representation of a Lox class.
|
56
|
+
|
57
|
+
### Changed
|
58
|
+
- File `grammar.rb` refactoring of class declaration syntax rules
|
59
|
+
|
60
|
+
## [0.1.10] - 2021-03-31
|
61
|
+
- Flag return statements occurring outside functions as an error
|
62
|
+
|
63
|
+
### Changed
|
64
|
+
- Class `BackEnd::Resolver` Added attribute `current_function` to know whether the visited parse node is located inside a function
|
65
|
+
|
66
|
+
|
67
|
+
## [0.1.09] - 2021-03-28
|
68
|
+
- Fix and test suite for return statements
|
69
|
+
|
70
|
+
### CHANGED
|
71
|
+
- `Loxxy` reports an error when a return statement occurs in top-level scope
|
72
|
+
|
73
|
+
### Fixed
|
74
|
+
- A return without explicit value genrated an exception in some cases.
|
75
|
+
|
1
76
|
## [0.1.08] - 2021-03-27
|
2
77
|
- `Loxxy` implements variable resolving and binding as described in Chapter 11 of "Crafting Interpreters" book.
|
3
78
|
|
4
79
|
### New
|
5
80
|
- Class `BackEnd::Resolver` implements the variable resolution (whenever a variable is in use, locate the declaration of that variable)
|
6
81
|
|
7
|
-
###
|
82
|
+
### CHANGED
|
8
83
|
- Class `Ast::Visitor` changes in some method signatures
|
9
84
|
- Class `BackEnd::Engine` new attribute `resolver` that points to a `BackEnd::Resolver` instance
|
10
85
|
- Class `BackEnd::Engine` several methods dealing with variables have been adapted to take the resolver into account.
|
@@ -94,9 +169,9 @@
|
|
94
169
|
- Method `Ast::ASTVisitor#visit_fun_stmt` for visiting an `Ast::LoxFunStmt` node
|
95
170
|
- Method `Ast::LoxBlockStmt#empty?` returns true if the code block is empty
|
96
171
|
- Method `BackEnd::Engine#after_fun_stmt`
|
97
|
-
- Method `
|
98
|
-
- Method `
|
99
|
-
- Method `
|
172
|
+
- Method `BackEnd::NativeFunction#call`
|
173
|
+
- Method `BackEnd::NativeFunction#to_str`
|
174
|
+
- Method `BackEnd::LoxFunction` runtime representation of a Lox function.
|
100
175
|
|
101
176
|
### Changed
|
102
177
|
- Method `BackEnd::Engine#after_call_expr`
|
data/README.md
CHANGED
@@ -13,10 +13,9 @@ a simple language defined in Bob Nystrom's online book [Crafting Interpreters](h
|
|
13
13
|
a Lox interpreter written in Lox.
|
14
14
|
|
15
15
|
### Current status
|
16
|
-
The interpreter currently
|
16
|
+
The interpreter currently can execute all allowed __Lox__ expressions and statements except
|
17
17
|
object-oriented feaures (classes and objects).
|
18
|
-
|
19
|
-
Our intent is implement to these missing features in Q2 2021.
|
18
|
+
The goal is to implement these missing features in Q2 2021.
|
20
19
|
|
21
20
|
|
22
21
|
## What's the fuss about Lox?
|
@@ -4,11 +4,13 @@ require_relative 'lox_fun_stmt'
|
|
4
4
|
require_relative 'lox_variable_expr'
|
5
5
|
require_relative 'lox_literal_expr'
|
6
6
|
require_relative 'lox_noop_expr'
|
7
|
+
require_relative 'lox_get_expr'
|
7
8
|
require_relative 'lox_call_expr'
|
8
9
|
require_relative 'lox_grouping_expr'
|
9
10
|
require_relative 'lox_unary_expr'
|
10
11
|
require_relative 'lox_binary_expr'
|
11
12
|
require_relative 'lox_logical_expr'
|
13
|
+
require_relative 'lox_set_expr'
|
12
14
|
require_relative 'lox_assign_expr'
|
13
15
|
require_relative 'lox_block_stmt'
|
14
16
|
require_relative 'lox_while_stmt'
|
@@ -17,4 +19,5 @@ require_relative 'lox_print_stmt'
|
|
17
19
|
require_relative 'lox_if_stmt'
|
18
20
|
require_relative 'lox_for_stmt'
|
19
21
|
require_relative 'lox_var_stmt'
|
22
|
+
require_relative 'lox_class_stmt'
|
20
23
|
require_relative 'lox_seq_decl'
|
@@ -163,6 +163,31 @@ module Loxxy
|
|
163
163
|
[theChildren[0]]
|
164
164
|
end
|
165
165
|
|
166
|
+
# rule('classDecl' => 'CLASS classNaming class_body')
|
167
|
+
def reduce_class_decl(_production, _range, _tokens, theChildren)
|
168
|
+
Ast::LoxClassStmt.new(tokens[1].position, theChildren[1], theChildren[2])
|
169
|
+
end
|
170
|
+
|
171
|
+
# rule('classNaming' => 'IDENTIFIER')
|
172
|
+
def reduce_class_name(_production, _range, _tokens, theChildren)
|
173
|
+
theChildren[0].token.lexeme
|
174
|
+
end
|
175
|
+
|
176
|
+
# rule('class_body' => 'LEFT_BRACE methods_opt RIGHT_BRACE')
|
177
|
+
def reduce_class_body(_production, _range, _tokens, theChildren)
|
178
|
+
theChildren[1].nil? ? [] : theChildren[1]
|
179
|
+
end
|
180
|
+
|
181
|
+
# rule('method_plus' => 'method_plus function')
|
182
|
+
def reduce_method_plus_more(_production, _range, _tokens, theChildren)
|
183
|
+
theChildren[0] << theChildren[1]
|
184
|
+
end
|
185
|
+
|
186
|
+
# rule('method_plus' => 'function')
|
187
|
+
def reduce_method_plus_end(_production, _range, _tokens, theChildren)
|
188
|
+
theChildren
|
189
|
+
end
|
190
|
+
|
166
191
|
# rule('funDecl' => 'FUN function')
|
167
192
|
def reduce_fun_decl(_production, _range, _tokens, theChildren)
|
168
193
|
theChildren[1]
|
@@ -244,10 +269,22 @@ module Loxxy
|
|
244
269
|
|
245
270
|
# rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment')
|
246
271
|
def reduce_assign_expr(_production, _range, tokens, theChildren)
|
247
|
-
|
248
|
-
|
272
|
+
name_assignee = theChildren[1].token.lexeme.dup
|
273
|
+
if theChildren[0].kind_of?(Ast::LoxSetExpr)
|
274
|
+
theChildren[0].property = name_assignee
|
275
|
+
theChildren[0].subnodes << theChildren[3]
|
276
|
+
theChildren[0]
|
277
|
+
else
|
278
|
+
Ast::LoxAssignExpr.new(tokens[1].position, name_assignee, theChildren[3])
|
279
|
+
end
|
249
280
|
end
|
250
281
|
|
282
|
+
# rule('owner_opt' => 'call DOT')
|
283
|
+
def reduce_set_expr(_production, _range, tokens, theChildren)
|
284
|
+
Ast::LoxSetExpr.new(tokens[1].position, theChildren[0])
|
285
|
+
end
|
286
|
+
|
287
|
+
|
251
288
|
# rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
|
252
289
|
# TODO: is it meaningful to implement this rule?
|
253
290
|
|
@@ -292,6 +329,11 @@ module Loxxy
|
|
292
329
|
LoxCallExpr.new(tokens[0].position, args)
|
293
330
|
end
|
294
331
|
|
332
|
+
# rule('refinement' => 'DOT IDENTIFIER').as 'get_expr'
|
333
|
+
def reduce_get_expr(_production, _range, tokens, theChildren)
|
334
|
+
LoxGetExpr.new(tokens[0].position, theChildren[1].token.lexeme)
|
335
|
+
end
|
336
|
+
|
295
337
|
# rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
|
296
338
|
def reduce_grouping_expr(_production, _range, tokens, theChildren)
|
297
339
|
subexpr = theChildren[1]
|
@@ -67,6 +67,14 @@ module Loxxy
|
|
67
67
|
broadcast(:after_var_stmt, aVarStmt)
|
68
68
|
end
|
69
69
|
|
70
|
+
# Visit event. The visitor is about to visit a class declaration.
|
71
|
+
# @param aClassStmt [AST::LOXClassStmt] the for statement node to visit
|
72
|
+
def visit_class_stmt(aClassStmt)
|
73
|
+
broadcast(:before_class_stmt, aClassStmt)
|
74
|
+
traverse_subnodes(aClassStmt) # The methods are visited here...
|
75
|
+
broadcast(:after_class_stmt, aClassStmt, self)
|
76
|
+
end
|
77
|
+
|
70
78
|
# Visit event. The visitor is about to visit a for statement.
|
71
79
|
# @param aForStmt [AST::LOXForStmt] the for statement node to visit
|
72
80
|
def visit_for_stmt(aForStmt)
|
@@ -123,6 +131,13 @@ module Loxxy
|
|
123
131
|
broadcast(:after_assign_expr, anAssignExpr, self)
|
124
132
|
end
|
125
133
|
|
134
|
+
# @param aSetExpr [AST::LOXGetExpr] the get expression node to visit
|
135
|
+
def visit_set_expr(aSetExpr)
|
136
|
+
broadcast(:before_set_expr, aSetExpr)
|
137
|
+
traverse_subnodes(aSetExpr)
|
138
|
+
broadcast(:after_set_expr, aSetExpr, self)
|
139
|
+
end
|
140
|
+
|
126
141
|
# Visit event. The visitor is about to visit a logical expression.
|
127
142
|
# Since logical expressions may take shorcuts by not evaluating all their
|
128
143
|
# sub-expressiosns, they are responsible for visiting or not their children.
|
@@ -161,6 +176,12 @@ module Loxxy
|
|
161
176
|
broadcast(:after_call_expr, aCallExpr, self)
|
162
177
|
end
|
163
178
|
|
179
|
+
# @param aGetExpr [AST::LOXGetExpr] the get expression node to visit
|
180
|
+
def visit_get_expr(aGetExpr)
|
181
|
+
broadcast(:before_get_expr, aGetExpr)
|
182
|
+
broadcast(:after_get_expr, aGetExpr, self)
|
183
|
+
end
|
184
|
+
|
164
185
|
# Visit event. The visitor is about to visit a grouping expression.
|
165
186
|
# @param aGroupingExpr [AST::LoxGroupingExpr] grouping expression to visit
|
166
187
|
def visit_grouping_expr(aGroupingExpr)
|
@@ -195,7 +216,6 @@ module Loxxy
|
|
195
216
|
# @param aFunStmt [AST::LoxFunStmt] function declaration to visit
|
196
217
|
def visit_fun_stmt(aFunStmt)
|
197
218
|
broadcast(:before_fun_stmt, aFunStmt, self)
|
198
|
-
traverse_subnodes(aFunStmt)
|
199
219
|
broadcast(:after_fun_stmt, aFunStmt, self)
|
200
220
|
end
|
201
221
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lox_compound_expr'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module Ast
|
7
|
+
class LoxClassStmt < LoxCompoundExpr
|
8
|
+
# @return [String] the class name
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# @return [Array<Ast::LoxFunStmt>] the methods
|
12
|
+
attr_reader :body
|
13
|
+
|
14
|
+
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
|
+
# @param condExpr [Loxxy::Ast::LoxNode] iteration condition
|
16
|
+
# @param theBody [Array<Loxxy::Ast::LoxNode>]
|
17
|
+
def initialize(aPosition, aName, theMethods)
|
18
|
+
super(aPosition, [])
|
19
|
+
@name = aName.dup
|
20
|
+
@body = theMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
24
|
+
# @param visitor [Ast::ASTVisitor] the visitor
|
25
|
+
def accept(visitor)
|
26
|
+
visitor.visit_class_stmt(self)
|
27
|
+
end
|
28
|
+
end # class
|
29
|
+
end # module
|
30
|
+
end # module
|
@@ -1,24 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'lox_node'
|
4
4
|
|
5
5
|
module Loxxy
|
6
6
|
module Ast
|
7
7
|
# rubocop: disable Style/AccessorGrouping
|
8
|
-
class LoxFunStmt <
|
8
|
+
class LoxFunStmt < LoxNode
|
9
9
|
attr_reader :name
|
10
10
|
attr_reader :params
|
11
11
|
attr_reader :body
|
12
|
+
attr_accessor :is_method
|
12
13
|
|
13
14
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
14
15
|
# @param aName [String]
|
15
16
|
# @param arguments [Array<String>]
|
16
17
|
# @param body [Ast::LoxBlockStmt]
|
17
18
|
def initialize(aPosition, aName, paramList, aBody)
|
18
|
-
super(aPosition
|
19
|
-
@name = aName
|
19
|
+
super(aPosition)
|
20
|
+
@name = aName.dup
|
20
21
|
@params = paramList
|
21
22
|
@body = aBody
|
23
|
+
@is_method = false
|
22
24
|
end
|
23
25
|
|
24
26
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lox_node'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module Ast
|
7
|
+
class LoxGetExpr < LoxNode
|
8
|
+
# @return [Ast::LoxNode] the object to which the property belongs to
|
9
|
+
attr_accessor :object
|
10
|
+
|
11
|
+
# @return [String] Name of an object property
|
12
|
+
attr_reader :property
|
13
|
+
|
14
|
+
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
|
+
# @param aPropertyName [String] Name of an object property
|
16
|
+
def initialize(aPosition, aPropertyName)
|
17
|
+
super(aPosition)
|
18
|
+
@property = aPropertyName
|
19
|
+
end
|
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
|
+
|
27
|
+
alias callee= object=
|
28
|
+
end # class
|
29
|
+
end # module
|
30
|
+
end # module
|
@@ -8,7 +8,8 @@ module Loxxy
|
|
8
8
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
9
9
|
# @param anExpression [Ast::LoxNode] expression to return
|
10
10
|
def initialize(aPosition, anExpression)
|
11
|
-
|
11
|
+
expr = anExpression || Datatype::Nil.instance
|
12
|
+
super(aPosition, [expr])
|
12
13
|
end
|
13
14
|
|
14
15
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lox_compound_expr'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module Ast
|
7
|
+
class LoxSetExpr < LoxCompoundExpr
|
8
|
+
# @return [Ast::LoxNode] the object to which the property belongs to
|
9
|
+
attr_reader :object
|
10
|
+
|
11
|
+
# @return [String] Name of an object property
|
12
|
+
attr_accessor :property
|
13
|
+
|
14
|
+
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
|
+
# @param anObject [Ast::LoxNode] The object which the given property is being set
|
16
|
+
def initialize(aPosition, anObject)
|
17
|
+
super(aPosition, [])
|
18
|
+
@object = anObject
|
19
|
+
end
|
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
|
26
|
+
end # class
|
27
|
+
end # module
|
28
|
+
end # module
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# Load all the classes implementing AST nodes
|
4
4
|
require_relative '../ast/all_lox_nodes'
|
5
5
|
require_relative 'binary_operator'
|
6
|
+
require_relative 'lox_class'
|
6
7
|
require_relative 'lox_function'
|
7
8
|
require_relative 'resolver'
|
8
9
|
require_relative 'symbol_table'
|
@@ -68,6 +69,19 @@ module Loxxy
|
|
68
69
|
# Do nothing, subnodes were already evaluated
|
69
70
|
end
|
70
71
|
|
72
|
+
def after_class_stmt(aClassStmt, aVisitor)
|
73
|
+
# Convert LoxFunStmt into LoxFunction
|
74
|
+
meths = aClassStmt.body.map do |func_node|
|
75
|
+
func_node.is_method = true
|
76
|
+
func_node.accept(aVisitor)
|
77
|
+
stack.pop
|
78
|
+
end
|
79
|
+
|
80
|
+
klass = LoxClass.new(aClassStmt.name, meths, self)
|
81
|
+
new_var = Variable.new(aClassStmt.name, klass)
|
82
|
+
symbol_table.insert(new_var)
|
83
|
+
end
|
84
|
+
|
71
85
|
def before_var_stmt(aVarStmt)
|
72
86
|
new_var = Variable.new(aVarStmt.name, Datatype::Nil.instance)
|
73
87
|
symbol_table.insert(new_var)
|
@@ -146,6 +160,18 @@ module Loxxy
|
|
146
160
|
variable.assign(value)
|
147
161
|
end
|
148
162
|
|
163
|
+
def after_set_expr(aSetExpr, aVisitor)
|
164
|
+
value = stack.pop
|
165
|
+
# Evaluate object part
|
166
|
+
aSetExpr.object.accept(aVisitor)
|
167
|
+
assignee = stack.pop
|
168
|
+
unless assignee.kind_of?(LoxInstance)
|
169
|
+
raise StandardError, 'Only instances have fields.'
|
170
|
+
end
|
171
|
+
|
172
|
+
assignee.set(aSetExpr.property, value)
|
173
|
+
end
|
174
|
+
|
149
175
|
def after_logical_expr(aLogicalExpr, visitor)
|
150
176
|
op = aLogicalExpr.operator
|
151
177
|
operand1 = stack.pop # only first operand was evaluated
|
@@ -221,11 +247,23 @@ module Loxxy
|
|
221
247
|
raise Loxxy::RuntimeError, msg
|
222
248
|
end
|
223
249
|
callee.call(self, aVisitor)
|
250
|
+
when LoxClass
|
251
|
+
callee.call(self, aVisitor)
|
224
252
|
else
|
225
253
|
raise Loxxy::RuntimeError, 'Can only call functions and classes.'
|
226
254
|
end
|
227
255
|
end
|
228
256
|
|
257
|
+
def after_get_expr(aGetExpr, aVisitor)
|
258
|
+
aGetExpr.object.accept(aVisitor)
|
259
|
+
instance = stack.pop
|
260
|
+
unless instance.kind_of?(LoxInstance)
|
261
|
+
raise StandardError, 'Only instances have properties.'
|
262
|
+
end
|
263
|
+
|
264
|
+
stack.push instance.get(aGetExpr.property)
|
265
|
+
end
|
266
|
+
|
229
267
|
def after_grouping_expr(_groupingExpr)
|
230
268
|
# Do nothing: work was already done by visiting /evaluating the subexpression
|
231
269
|
end
|
@@ -233,7 +271,7 @@ module Loxxy
|
|
233
271
|
def after_variable_expr(aVarExpr, aVisitor)
|
234
272
|
var_name = aVarExpr.name
|
235
273
|
var = variable_lookup(aVarExpr)
|
236
|
-
raise StandardError, "
|
274
|
+
raise StandardError, "Undefined variable '#{var_name}'." unless var
|
237
275
|
|
238
276
|
var.value.accept(aVisitor) # Evaluate variable value then push on stack
|
239
277
|
end
|
@@ -250,8 +288,12 @@ module Loxxy
|
|
250
288
|
|
251
289
|
def after_fun_stmt(aFunStmt, _visitor)
|
252
290
|
function = LoxFunction.new(aFunStmt.name, aFunStmt.params, aFunStmt.body, self)
|
253
|
-
|
254
|
-
|
291
|
+
if aFunStmt.is_method
|
292
|
+
stack.push function
|
293
|
+
else
|
294
|
+
new_var = Variable.new(aFunStmt.name, function)
|
295
|
+
symbol_table.insert(new_var)
|
296
|
+
end
|
255
297
|
end
|
256
298
|
|
257
299
|
private
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../datatype/all_datatypes'
|
4
|
+
require_relative 'lox_instance'
|
5
|
+
|
6
|
+
module Loxxy
|
7
|
+
module BackEnd
|
8
|
+
# Runtime representation of a Lox class.
|
9
|
+
class LoxClass
|
10
|
+
# @return [String] The name of the class
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# @return [Hash{String => LoxFunction}] the list of methods
|
14
|
+
attr_reader :meths
|
15
|
+
attr_reader :stack
|
16
|
+
|
17
|
+
# Create a class with given name
|
18
|
+
# @param aName [String] The name of the class
|
19
|
+
def initialize(aName, theMethods, anEngine)
|
20
|
+
@name = aName.dup
|
21
|
+
@meths = {}
|
22
|
+
theMethods.each do |func|
|
23
|
+
meths[func.name] = func
|
24
|
+
end
|
25
|
+
@stack = anEngine.stack
|
26
|
+
end
|
27
|
+
|
28
|
+
def accept(_visitor)
|
29
|
+
stack.push self
|
30
|
+
end
|
31
|
+
|
32
|
+
def arity
|
33
|
+
0
|
34
|
+
end
|
35
|
+
|
36
|
+
def call(engine, _visitor)
|
37
|
+
instance = LoxInstance.new(self, engine)
|
38
|
+
engine.stack.push(instance)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param aName [String] the method name to search for
|
42
|
+
def find_method(aName)
|
43
|
+
meths[aName]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Logical negation.
|
47
|
+
# As a function is a truthy thing, its negation is thus false.
|
48
|
+
# @return [Datatype::False]
|
49
|
+
def !
|
50
|
+
Datatype::False.instance
|
51
|
+
end
|
52
|
+
|
53
|
+
# Text representation of a Lox class
|
54
|
+
def to_str
|
55
|
+
name
|
56
|
+
end
|
57
|
+
end # class
|
58
|
+
end # module
|
59
|
+
end # module
|
@@ -6,7 +6,6 @@ module Loxxy
|
|
6
6
|
module BackEnd
|
7
7
|
# rubocop: disable Style/AccessorGrouping
|
8
8
|
# Representation of a Lox function.
|
9
|
-
# It is a named slot that can be associated with a value at the time.
|
10
9
|
class LoxFunction
|
11
10
|
# @return [String] The name of the function (if any)
|
12
11
|
attr_reader :name
|
@@ -18,7 +17,7 @@ module Loxxy
|
|
18
17
|
attr_reader :closure
|
19
18
|
|
20
19
|
# Create a function with given name
|
21
|
-
# @param aName [String] The name of the
|
20
|
+
# @param aName [String] The name of the function
|
22
21
|
def initialize(aName, parameterList, aBody, anEngine)
|
23
22
|
@name = aName.dup
|
24
23
|
@parameters = parameterList
|
@@ -37,7 +36,6 @@ module Loxxy
|
|
37
36
|
end
|
38
37
|
|
39
38
|
def call(engine, aVisitor)
|
40
|
-
# new_env = Environment.new(engine.symbol_table.current_env)
|
41
39
|
new_env = Environment.new(closure)
|
42
40
|
engine.symbol_table.enter_environment(new_env)
|
43
41
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../datatype/all_datatypes'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module BackEnd
|
7
|
+
# Runtime representation of a Lox object (instance).
|
8
|
+
class LoxInstance
|
9
|
+
# @return BackEnd::LoxClass] the class that this object is an instance of
|
10
|
+
attr_reader :klass
|
11
|
+
|
12
|
+
attr_reader :stack
|
13
|
+
|
14
|
+
# @return [Hash{String => BuiltinDatatype | LoxFunction | LoxInstance }]
|
15
|
+
attr_reader :fields
|
16
|
+
|
17
|
+
# Create an instance from given class
|
18
|
+
# @param aClass [BackEnd::LoxClass] the class this this object belong
|
19
|
+
def initialize(aClass, anEngine)
|
20
|
+
@klass = aClass
|
21
|
+
@stack = anEngine.stack
|
22
|
+
@fields = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def accept(_visitor)
|
26
|
+
stack.push self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Text representation of a Lox instance
|
30
|
+
def to_str
|
31
|
+
"#{klass.to_str} instance"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Look up the value of property with given name
|
35
|
+
# aName [String] name of object property
|
36
|
+
def get(aName)
|
37
|
+
return fields[aName] if fields.include? aName
|
38
|
+
|
39
|
+
method = klass.find_method(aName)
|
40
|
+
unless method
|
41
|
+
raise StandardError, "Undefined property '#{aName}'."
|
42
|
+
end
|
43
|
+
|
44
|
+
method
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set the value of property with given name
|
48
|
+
# aName [String] name of object property
|
49
|
+
def set(aName, aValue)
|
50
|
+
unless fields.include? aName
|
51
|
+
raise StandardError, "Undefined property '#{aName}'."
|
52
|
+
end
|
53
|
+
|
54
|
+
fields[aName] = aValue
|
55
|
+
end
|
56
|
+
end # class
|
57
|
+
end # module
|
58
|
+
end # module
|
@@ -22,9 +22,14 @@ module Loxxy
|
|
22
22
|
# @return [Hash {LoxNode => Integer}]
|
23
23
|
attr_reader :locals
|
24
24
|
|
25
|
+
# An indicator that tells we're in the middle of a function declaration
|
26
|
+
# @return [Symbol] must be one of: :none, :function
|
27
|
+
attr_reader :current_function
|
28
|
+
|
25
29
|
def initialize
|
26
30
|
@scopes = []
|
27
31
|
@locals = {}
|
32
|
+
@current_function = :none
|
28
33
|
end
|
29
34
|
|
30
35
|
# Given an abstract syntax parse tree visitor, launch the visit
|
@@ -48,6 +53,18 @@ module Loxxy
|
|
48
53
|
end_scope
|
49
54
|
end
|
50
55
|
|
56
|
+
# A class declaration adds a new variable to current scope
|
57
|
+
def before_class_stmt(aClassStmt)
|
58
|
+
declare(aClassStmt.name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def after_class_stmt(aClassStmt, aVisitor)
|
62
|
+
define(aClassStmt.name)
|
63
|
+
aClassStmt.body.each do |fun_stmt|
|
64
|
+
resolve_function(fun_stmt, :method, aVisitor)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
51
68
|
def before_for_stmt(aForStmt)
|
52
69
|
before_block_stmt(aForStmt)
|
53
70
|
end
|
@@ -64,6 +81,18 @@ module Loxxy
|
|
64
81
|
anIfStmt.else_stmt&.accept(aVisitor)
|
65
82
|
end
|
66
83
|
|
84
|
+
def before_return_stmt(_returnStmt)
|
85
|
+
if scopes.size < 2
|
86
|
+
msg = "Error at 'return': Can't return from top-level code."
|
87
|
+
raise StandardError, msg
|
88
|
+
end
|
89
|
+
|
90
|
+
if current_function == :none
|
91
|
+
msg = "Error at 'return': Can't return from outside a function."
|
92
|
+
raise StandardError, msg
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
67
96
|
def after_while_stmt(aWhileStmt, aVisitor)
|
68
97
|
aWhileStmt.body.accept(aVisitor)
|
69
98
|
aWhileStmt.condition.accept(aVisitor)
|
@@ -83,6 +112,11 @@ module Loxxy
|
|
83
112
|
resolve_local(anAssignExpr, aVisitor)
|
84
113
|
end
|
85
114
|
|
115
|
+
def after_set_expr(aSetExpr, aVisitor)
|
116
|
+
# Evaluate object part
|
117
|
+
aSetExpr.object.accept(aVisitor)
|
118
|
+
end
|
119
|
+
|
86
120
|
# Variable expressions require their variables resolved
|
87
121
|
def before_variable_expr(aVarExpr)
|
88
122
|
var_name = aVarExpr.name
|
@@ -101,11 +135,16 @@ module Loxxy
|
|
101
135
|
aCallExpr.arguments.reverse_each { |arg| arg.accept(aVisitor) }
|
102
136
|
end
|
103
137
|
|
138
|
+
def after_get_expr(aGetExpr, aVisitor)
|
139
|
+
# Evaluate object part
|
140
|
+
aGetExpr.object.accept(aVisitor)
|
141
|
+
end
|
142
|
+
|
104
143
|
# function declaration creates a new scope for its body & binds its parameters for that scope
|
105
144
|
def before_fun_stmt(aFunStmt, aVisitor)
|
106
145
|
declare(aFunStmt.name)
|
107
146
|
define(aFunStmt.name)
|
108
|
-
resolve_function(aFunStmt, aVisitor)
|
147
|
+
resolve_function(aFunStmt, :function, aVisitor)
|
109
148
|
end
|
110
149
|
|
111
150
|
private
|
@@ -122,6 +161,10 @@ module Loxxy
|
|
122
161
|
return if scopes.empty?
|
123
162
|
|
124
163
|
curr_scope = scopes.last
|
164
|
+
if curr_scope.include?(aVarName)
|
165
|
+
msg = "Error at '#{aVarName}': Already variable with this name in this scope."
|
166
|
+
raise StandardError, msg
|
167
|
+
end
|
125
168
|
|
126
169
|
# The initializer is not yet processed.
|
127
170
|
# Mark the variable as 'not yet ready' = exists but may not be referenced yet
|
@@ -151,7 +194,9 @@ module Loxxy
|
|
151
194
|
end
|
152
195
|
end
|
153
196
|
|
154
|
-
def resolve_function(aFunStmt, aVisitor)
|
197
|
+
def resolve_function(aFunStmt, funVisitState, aVisitor)
|
198
|
+
enclosing_function = current_function
|
199
|
+
@current_function = funVisitState
|
155
200
|
begin_scope
|
156
201
|
|
157
202
|
aFunStmt.params&.each do |param_name|
|
@@ -165,6 +210,7 @@ module Loxxy
|
|
165
210
|
end
|
166
211
|
|
167
212
|
end_scope
|
213
|
+
@current_function = enclosing_function
|
168
214
|
end
|
169
215
|
end # class
|
170
216
|
end # mmodule
|
@@ -37,12 +37,14 @@ module Loxxy
|
|
37
37
|
rule('declaration' => 'varDecl')
|
38
38
|
rule('declaration' => 'statement')
|
39
39
|
|
40
|
-
rule('classDecl' => 'CLASS classNaming class_body')
|
40
|
+
rule('classDecl' => 'CLASS classNaming class_body').as 'class_decl'
|
41
41
|
rule('classNaming' => 'IDENTIFIER LESS IDENTIFIER')
|
42
|
-
rule('classNaming' => 'IDENTIFIER')
|
43
|
-
rule('class_body' => 'LEFT_BRACE
|
44
|
-
rule('
|
45
|
-
rule('
|
42
|
+
rule('classNaming' => 'IDENTIFIER').as 'class_name'
|
43
|
+
rule('class_body' => 'LEFT_BRACE methods_opt RIGHT_BRACE').as 'class_body'
|
44
|
+
rule('methods_opt' => 'method_plus')
|
45
|
+
rule('methods_opt' => [])
|
46
|
+
rule('method_plus' => 'method_plus function').as 'method_plus_more'
|
47
|
+
rule('method_plus' => 'function').as 'method_plus_end'
|
46
48
|
|
47
49
|
rule('funDecl' => 'FUN function').as 'fun_decl'
|
48
50
|
|
@@ -85,7 +87,7 @@ module Loxxy
|
|
85
87
|
rule('expression' => 'assignment')
|
86
88
|
rule('assignment' => 'owner_opt IDENTIFIER EQUAL assignment').as 'assign_expr'
|
87
89
|
rule('assignment' => 'logic_or')
|
88
|
-
rule('owner_opt' => 'call DOT')
|
90
|
+
rule('owner_opt' => 'call DOT').as 'set_expr'
|
89
91
|
rule('owner_opt' => [])
|
90
92
|
rule('logic_or' => 'logic_and')
|
91
93
|
rule('logic_or' => 'logic_and disjunct_plus').as 'logical_expr'
|
@@ -130,7 +132,7 @@ module Loxxy
|
|
130
132
|
rule('refinement_plus' => 'refinement_plus refinement').as 'refinement_plus_more'
|
131
133
|
rule('refinement_plus' => 'refinement').as 'refinement_plus_end'
|
132
134
|
rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN').as 'call_arglist'
|
133
|
-
rule('refinement' => 'DOT IDENTIFIER')
|
135
|
+
rule('refinement' => 'DOT IDENTIFIER').as 'get_expr'
|
134
136
|
rule('primary' => 'TRUE').as 'literal_expr'
|
135
137
|
rule('primary' => 'FALSE').as 'literal_expr'
|
136
138
|
rule('primary' => 'NIL').as 'literal_expr'
|
data/lib/loxxy/version.rb
CHANGED
@@ -305,6 +305,83 @@ LOX_END
|
|
305
305
|
expect(expr.operands[1].operands[1].literal.value).to eq(5)
|
306
306
|
end
|
307
307
|
end # context
|
308
|
+
|
309
|
+
context 'Object orientation:' do
|
310
|
+
it 'should parse object property get access' do
|
311
|
+
input = 'print someObject.someProperty;'
|
312
|
+
ptree = subject.parse(input)
|
313
|
+
expr = ptree.root.subnodes[0]
|
314
|
+
expect(expr).to be_kind_of(Ast::LoxPrintStmt)
|
315
|
+
get_expr = expr.subnodes[0]
|
316
|
+
expect(get_expr).to be_kind_of(Ast::LoxGetExpr)
|
317
|
+
expect(get_expr.object.name).to eq('someObject')
|
318
|
+
expect(get_expr.property).to eq('someProperty')
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should parse nested call expressions' do
|
322
|
+
input = 'print egg.scramble(3).with(cheddar);'
|
323
|
+
# From section 12.3.1, one expects something like:
|
324
|
+
# LoxCallExpr
|
325
|
+
# +- arguments = ['cheddar']
|
326
|
+
# +- callee = LoxGetExpr
|
327
|
+
# +- property = 'with'
|
328
|
+
# +- object = LoxCallExpr
|
329
|
+
# +- arguments = [3]
|
330
|
+
# +- callee = LoxGetExpr
|
331
|
+
# +- property = 'scramble'
|
332
|
+
# +- object = variable 'egg'
|
333
|
+
ptree = subject.parse(input)
|
334
|
+
print_stmt = ptree.root.subnodes[0]
|
335
|
+
expect(print_stmt).to be_kind_of(Ast::LoxPrintStmt)
|
336
|
+
outer_call = print_stmt.subnodes[0]
|
337
|
+
expect(outer_call).to be_kind_of(Ast::LoxCallExpr)
|
338
|
+
expect(outer_call.arguments[0].name).to eq('cheddar')
|
339
|
+
expect(outer_call.callee).to be_kind_of(Ast::LoxGetExpr)
|
340
|
+
expect(outer_call.callee.property).to eq('with')
|
341
|
+
inner_call = outer_call.callee.object
|
342
|
+
expect(inner_call).to be_kind_of(Ast::LoxCallExpr)
|
343
|
+
expect(inner_call.arguments[0].literal).to eq(3)
|
344
|
+
expect(inner_call.callee).to be_kind_of(Ast::LoxGetExpr)
|
345
|
+
expect(inner_call.callee.property).to eq('scramble')
|
346
|
+
expect(inner_call.callee.object.name).to eq('egg')
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'should parse object property set access' do
|
350
|
+
input = 'someObject.someProperty = value;'
|
351
|
+
ptree = subject.parse(input)
|
352
|
+
expr = ptree.root.subnodes[0]
|
353
|
+
expect(expr).to be_kind_of(Ast::LoxSetExpr)
|
354
|
+
expect(expr.object.name).to eq('someObject')
|
355
|
+
expect(expr.property).to eq('someProperty')
|
356
|
+
expect(expr.subnodes[0]).to be_kind_of(Ast::LoxVariableExpr)
|
357
|
+
expect(expr.subnodes[0].name).to eq('value')
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should parse complex set access' do
|
361
|
+
input = 'breakfast.omelette.filling.meat = ham;'
|
362
|
+
# From section 12.3.2, one expects something like:
|
363
|
+
# LoxSetExpr
|
364
|
+
# +- property = 'meat'
|
365
|
+
# +- subnodes[0] = LoxVariableExpr 'ham'
|
366
|
+
# +- object = LoxGetExpr
|
367
|
+
# +- property = 'filling'
|
368
|
+
# +- object = LoxGetExpr
|
369
|
+
# +- property = 'omelette'
|
370
|
+
# +- object = LoxVariableExpr 'breakfast'
|
371
|
+
ptree = subject.parse(input)
|
372
|
+
expr = ptree.root.subnodes[0]
|
373
|
+
expect(expr).to be_kind_of(Ast::LoxSetExpr)
|
374
|
+
expect(expr.property).to eq('meat')
|
375
|
+
expect(expr.subnodes[0]).to be_kind_of(Ast::LoxVariableExpr)
|
376
|
+
expect(expr.subnodes[0].name).to eq('ham')
|
377
|
+
expect(expr.object).to be_kind_of(Ast::LoxGetExpr)
|
378
|
+
expect(expr.object.property).to eq('filling')
|
379
|
+
expect(expr.object.object).to be_kind_of(Ast::LoxGetExpr)
|
380
|
+
expect(expr.object.object.property).to eq('omelette')
|
381
|
+
expect(expr.object.object.object).to be_kind_of(Ast::LoxVariableExpr)
|
382
|
+
expect(expr.object.object.object.name).to eq('breakfast')
|
383
|
+
end
|
384
|
+
end # context
|
308
385
|
end # describe
|
309
386
|
end # module
|
310
387
|
end # module
|
data/spec/interpreter_spec.rb
CHANGED
@@ -460,6 +460,56 @@ LOX_END
|
|
460
460
|
expect(sample_cfg[:ostream].string).to eq('Hello, world!')
|
461
461
|
end
|
462
462
|
end # context
|
463
|
+
|
464
|
+
context 'Object orientation:' do
|
465
|
+
let(:duck_class) do
|
466
|
+
snippet = <<-LOX_END
|
467
|
+
class Duck {
|
468
|
+
noise() {
|
469
|
+
quack();
|
470
|
+
}
|
471
|
+
|
472
|
+
quack() {
|
473
|
+
print "quack";
|
474
|
+
}
|
475
|
+
}
|
476
|
+
LOX_END
|
477
|
+
|
478
|
+
snippet
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'should support class declaration' do
|
482
|
+
program = <<-LOX_END
|
483
|
+
#{duck_class}
|
484
|
+
|
485
|
+
print Duck; // Class names can appear in statements
|
486
|
+
LOX_END
|
487
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
488
|
+
expect(sample_cfg[:ostream].string).to eq('Duck')
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'should support default instance creation' do
|
492
|
+
program = <<-LOX_END
|
493
|
+
#{duck_class}
|
494
|
+
|
495
|
+
var daffy = Duck(); // Default constructor
|
496
|
+
print daffy;
|
497
|
+
LOX_END
|
498
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
499
|
+
expect(sample_cfg[:ostream].string).to eq('Duck instance')
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'should support calls to method' do
|
503
|
+
program = <<-LOX_END
|
504
|
+
#{duck_class}
|
505
|
+
|
506
|
+
var daffy = Duck(); // Default constructor
|
507
|
+
daffy.quack();
|
508
|
+
LOX_END
|
509
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
510
|
+
expect(sample_cfg[:ostream].string).to eq('quack')
|
511
|
+
end
|
512
|
+
end # context
|
463
513
|
end # describe
|
464
514
|
# rubocop: enable Metrics/BlockLength
|
465
515
|
end # module
|
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.1.
|
4
|
+
version: 0.1.13
|
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-
|
11
|
+
date: 2021-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -93,9 +93,11 @@ files:
|
|
93
93
|
- lib/loxxy/ast/lox_binary_expr.rb
|
94
94
|
- lib/loxxy/ast/lox_block_stmt.rb
|
95
95
|
- lib/loxxy/ast/lox_call_expr.rb
|
96
|
+
- lib/loxxy/ast/lox_class_stmt.rb
|
96
97
|
- lib/loxxy/ast/lox_compound_expr.rb
|
97
98
|
- lib/loxxy/ast/lox_for_stmt.rb
|
98
99
|
- lib/loxxy/ast/lox_fun_stmt.rb
|
100
|
+
- lib/loxxy/ast/lox_get_expr.rb
|
99
101
|
- lib/loxxy/ast/lox_grouping_expr.rb
|
100
102
|
- lib/loxxy/ast/lox_if_stmt.rb
|
101
103
|
- lib/loxxy/ast/lox_literal_expr.rb
|
@@ -105,6 +107,7 @@ files:
|
|
105
107
|
- lib/loxxy/ast/lox_print_stmt.rb
|
106
108
|
- lib/loxxy/ast/lox_return_stmt.rb
|
107
109
|
- lib/loxxy/ast/lox_seq_decl.rb
|
110
|
+
- lib/loxxy/ast/lox_set_expr.rb
|
108
111
|
- lib/loxxy/ast/lox_unary_expr.rb
|
109
112
|
- lib/loxxy/ast/lox_var_stmt.rb
|
110
113
|
- lib/loxxy/ast/lox_variable_expr.rb
|
@@ -113,7 +116,9 @@ files:
|
|
113
116
|
- lib/loxxy/back_end/engine.rb
|
114
117
|
- lib/loxxy/back_end/entry.rb
|
115
118
|
- lib/loxxy/back_end/environment.rb
|
119
|
+
- lib/loxxy/back_end/lox_class.rb
|
116
120
|
- lib/loxxy/back_end/lox_function.rb
|
121
|
+
- lib/loxxy/back_end/lox_instance.rb
|
117
122
|
- lib/loxxy/back_end/resolver.rb
|
118
123
|
- lib/loxxy/back_end/symbol_table.rb
|
119
124
|
- lib/loxxy/back_end/unary_operator.rb
|