Class: Loxxy::BackEnd::Resolver
- Inherits:
-
Object
- Object
- Loxxy::BackEnd::Resolver
- Defined in:
- lib/loxxy/back_end/resolver.rb
Overview
A class aimed to perform variable resolution when it visits the parse tree. Resolving means retrieve the declaration of a variable/function everywhere it is referenced.
Instance Attribute Summary collapse
-
#current_class ⇒ Symbol
readonly
An indicator that tells we're in the middle of a class declaration.
-
#current_function ⇒ Symbol
readonly
An indicator that tells we're in the middle of a function declaration.
-
#locals ⇒ Hash {LoxNode => Integer}
readonly
A map from a LoxNode involving a variable and the number of enclosing scopes where it is declared.
-
#scopes ⇒ Array<Hash{String => Boolean}>
readonly
A stack of Hashes of the form String => Boolean.
Instance Method Summary collapse
-
#after_assign_expr(anAssignExpr, aVisitor) ⇒ Object
Assignment expressions require their variables resolved.
- #after_block_stmt(_aBlockStmt) ⇒ Object
- #after_call_expr(aCallExpr, aVisitor) ⇒ Object
- #after_class_stmt(aClassStmt, aVisitor) ⇒ Object
- #after_for_stmt(aForStmt, aVisitor) ⇒ Object
- #after_get_expr(aGetExpr, aVisitor) ⇒ Object
- #after_if_stmt(anIfStmt, aVisitor) ⇒ Object
- #after_set_expr(aSetExpr, aVisitor) ⇒ Object
-
#after_super_expr(aSuperExpr, aVisitor) ⇒ Object
rubocop: disable Style/CaseLikeIf rubocop: disable Style/StringConcatenation.
- #after_this_expr(aThisExpr, aVisitor) ⇒ Object
- #after_var_stmt(aVarStmt) ⇒ Object
- #after_variable_expr(aVarExpr, aVisitor) ⇒ Object
- #after_while_stmt(aWhileStmt, aVisitor) ⇒ Object
-
#analyze(aVisitor) ⇒ Loxxy::Datatype::BuiltinDatatype
Given an abstract syntax parse tree visitor, launch the visit and execute the visit events in the output stream.
-
#before_block_stmt(_aBlockStmt) ⇒ Object
block statement introduces a new scope.
-
#before_class_stmt(aClassStmt) ⇒ Object
A class declaration adds a new variable to current scope.
- #before_for_stmt(aForStmt) ⇒ Object
-
#before_fun_stmt(aFunStmt, aVisitor) ⇒ Object
function declaration creates a new scope for its body & binds its parameters for that scope.
- #before_return_stmt(returnStmt) ⇒ Object
- #before_this_expr(_thisExpr) ⇒ Object
-
#before_var_stmt(aVarStmt) ⇒ Object
A variable declaration adds a new variable to current scope.
-
#before_variable_expr(aVarExpr) ⇒ Object
Variable expressions require their variables resolved.
-
#initialize ⇒ Resolver
constructor
A new instance of Resolver.
Constructor Details
#initialize ⇒ Resolver
Returns a new instance of Resolver.
33 34 35 36 37 38 |
# File 'lib/loxxy/back_end/resolver.rb', line 33 def initialize @scopes = [] @locals = {} @current_function = :none @current_class = :none end |
Instance Attribute Details
#current_class ⇒ Symbol (readonly)
An indicator that tells we're in the middle of a class declaration
31 32 33 |
# File 'lib/loxxy/back_end/resolver.rb', line 31 def current_class @current_class end |
#current_function ⇒ Symbol (readonly)
An indicator that tells we're in the middle of a function declaration
27 28 29 |
# File 'lib/loxxy/back_end/resolver.rb', line 27 def current_function @current_function end |
#locals ⇒ Hash {LoxNode => Integer} (readonly)
A map from a LoxNode involving a variable and the number of enclosing scopes where it is declared.
23 24 25 |
# File 'lib/loxxy/back_end/resolver.rb', line 23 def locals @locals end |
#scopes ⇒ Array<Hash{String => Boolean}> (readonly)
A stack of Hashes of the form String => Boolean
18 19 20 |
# File 'lib/loxxy/back_end/resolver.rb', line 18 def scopes @scopes end |
Instance Method Details
#after_assign_expr(anAssignExpr, aVisitor) ⇒ Object
Assignment expressions require their variables resolved
139 140 141 |
# File 'lib/loxxy/back_end/resolver.rb', line 139 def after_assign_expr(anAssignExpr, aVisitor) resolve_local(anAssignExpr, aVisitor) end |
#after_block_stmt(_aBlockStmt) ⇒ Object
57 58 59 |
# File 'lib/loxxy/back_end/resolver.rb', line 57 def after_block_stmt(_aBlockStmt) end_scope end |
#after_call_expr(aCallExpr, aVisitor) ⇒ Object
160 161 162 163 164 |
# File 'lib/loxxy/back_end/resolver.rb', line 160 def after_call_expr(aCallExpr, aVisitor) # Evaluate callee part aCallExpr.callee.accept(aVisitor) aCallExpr.arguments.reverse_each { |arg| arg.accept(aVisitor) } end |
#after_class_stmt(aClassStmt, aVisitor) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/loxxy/back_end/resolver.rb', line 66 def after_class_stmt(aClassStmt, aVisitor) previous_class = current_class @current_class = :class define(aClassStmt.name) if aClassStmt.superclass if aClassStmt.name == aClassStmt.superclass.name raise StandardError, "'A class can't inherit from itself." end @current_class = :subclass aClassStmt.superclass.accept(aVisitor) begin_scope define('super') end begin_scope define('this') aClassStmt.body.each do |fun_stmt| mth_type = fun_stmt.name == 'init' ? :initializer : :method resolve_function(fun_stmt, mth_type, aVisitor) end end_scope end_scope if aClassStmt.superclass @current_class = previous_class end |
#after_for_stmt(aForStmt, aVisitor) ⇒ Object
95 96 97 98 99 100 |
# File 'lib/loxxy/back_end/resolver.rb', line 95 def after_for_stmt(aForStmt, aVisitor) aForStmt.test_expr.accept(aVisitor) aForStmt.body_stmt.accept(aVisitor) aForStmt.update_expr&.accept(aVisitor) after_block_stmt(aForStmt) end |
#after_get_expr(aGetExpr, aVisitor) ⇒ Object
166 167 168 169 |
# File 'lib/loxxy/back_end/resolver.rb', line 166 def after_get_expr(aGetExpr, aVisitor) # Evaluate object part aGetExpr.object.accept(aVisitor) end |
#after_if_stmt(anIfStmt, aVisitor) ⇒ Object
102 103 104 105 |
# File 'lib/loxxy/back_end/resolver.rb', line 102 def after_if_stmt(anIfStmt, aVisitor) anIfStmt.then_stmt.accept(aVisitor) anIfStmt.else_stmt&.accept(aVisitor) end |
#after_set_expr(aSetExpr, aVisitor) ⇒ Object
143 144 145 146 |
# File 'lib/loxxy/back_end/resolver.rb', line 143 def after_set_expr(aSetExpr, aVisitor) # Evaluate object part aSetExpr.object.accept(aVisitor) end |
#after_super_expr(aSuperExpr, aVisitor) ⇒ Object
rubocop: disable Style/CaseLikeIf rubocop: disable Style/StringConcatenation
185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/loxxy/back_end/resolver.rb', line 185 def after_super_expr(aSuperExpr, aVisitor) msg_prefix = "Error at 'super': Can't use 'super' " if current_class == :none err_msg = msg_prefix + 'outside of a class.' raise StandardError, err_msg elsif current_class == :class err_msg = msg_prefix + 'in a class without superclass.' raise StandardError, err_msg end # 'super' behaves closely to a local variable resolve_local(aSuperExpr, aVisitor) end |
#after_this_expr(aThisExpr, aVisitor) ⇒ Object
178 179 180 181 |
# File 'lib/loxxy/back_end/resolver.rb', line 178 def after_this_expr(aThisExpr, aVisitor) # 'this' behaves closely to a local variable resolve_local(aThisExpr, aVisitor) end |
#after_var_stmt(aVarStmt) ⇒ Object
134 135 136 |
# File 'lib/loxxy/back_end/resolver.rb', line 134 def after_var_stmt(aVarStmt) define(aVarStmt.name) end |
#after_variable_expr(aVarExpr, aVisitor) ⇒ Object
156 157 158 |
# File 'lib/loxxy/back_end/resolver.rb', line 156 def after_variable_expr(aVarExpr, aVisitor) resolve_local(aVarExpr, aVisitor) end |
#after_while_stmt(aWhileStmt, aVisitor) ⇒ Object
124 125 126 127 |
# File 'lib/loxxy/back_end/resolver.rb', line 124 def after_while_stmt(aWhileStmt, aVisitor) aWhileStmt.body.accept(aVisitor) aWhileStmt.condition.accept(aVisitor) end |
#analyze(aVisitor) ⇒ Loxxy::Datatype::BuiltinDatatype
Given an abstract syntax parse tree visitor, launch the visit and execute the visit events in the output stream.
44 45 46 47 48 49 50 |
# File 'lib/loxxy/back_end/resolver.rb', line 44 def analyze(aVisitor) begin_scope aVisitor.subscribe(self) aVisitor.start aVisitor.unsubscribe(self) end_scope end |
#before_block_stmt(_aBlockStmt) ⇒ Object
block statement introduces a new scope
53 54 55 |
# File 'lib/loxxy/back_end/resolver.rb', line 53 def before_block_stmt(_aBlockStmt) begin_scope end |
#before_class_stmt(aClassStmt) ⇒ Object
A class declaration adds a new variable to current scope
62 63 64 |
# File 'lib/loxxy/back_end/resolver.rb', line 62 def before_class_stmt(aClassStmt) declare(aClassStmt.name) end |
#before_for_stmt(aForStmt) ⇒ Object
91 92 93 |
# File 'lib/loxxy/back_end/resolver.rb', line 91 def before_for_stmt(aForStmt) before_block_stmt(aForStmt) end |
#before_fun_stmt(aFunStmt, aVisitor) ⇒ Object
function declaration creates a new scope for its body & binds its parameters for that scope
203 204 205 206 207 |
# File 'lib/loxxy/back_end/resolver.rb', line 203 def before_fun_stmt(aFunStmt, aVisitor) declare(aFunStmt.name) define(aFunStmt.name) resolve_function(aFunStmt, :function, aVisitor) end |
#before_return_stmt(returnStmt) ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/loxxy/back_end/resolver.rb', line 107 def before_return_stmt(returnStmt) if scopes.size < 2 msg = "Error at 'return': Can't return from top-level code." raise StandardError, msg end if current_function == :none msg = "Error at 'return': Can't return from outside a function." raise StandardError, msg end if current_function == :initializer msg = "Error at 'return': Can't return a value from an initializer." raise StandardError, msg unless returnStmt.subnodes[0].kind_of?(Datatype::Nil) end end |
#before_this_expr(_thisExpr) ⇒ Object
171 172 173 174 175 176 |
# File 'lib/loxxy/back_end/resolver.rb', line 171 def before_this_expr(_thisExpr) if current_class == :none msg = "Error at 'this': Can't use 'this' outside of a class." raise StandardError, msg end end |
#before_var_stmt(aVarStmt) ⇒ Object
A variable declaration adds a new variable to current scope
130 131 132 |
# File 'lib/loxxy/back_end/resolver.rb', line 130 def before_var_stmt(aVarStmt) declare(aVarStmt.name) end |
#before_variable_expr(aVarExpr) ⇒ Object
Variable expressions require their variables resolved
149 150 151 152 153 154 |
# File 'lib/loxxy/back_end/resolver.rb', line 149 def before_variable_expr(aVarExpr) var_name = aVarExpr.name if !scopes.empty? && (scopes.last[var_name] == false) raise StandardError, "Can't read variable #{var_name} in its own initializer" end end |