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_get_expr(aGetExpr, aVisitor) ⇒ Object
- #after_if_stmt(anIfStmt, aVisitor) ⇒ Object
- #after_logical_expr(aLogicalExpr, 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_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
131 132 133 |
# File 'lib/loxxy/back_end/resolver.rb', line 131 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
158 159 160 161 162 |
# File 'lib/loxxy/back_end/resolver.rb', line 158 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 Loxxy::RuntimeError, "'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_get_expr(aGetExpr, aVisitor) ⇒ Object
164 165 166 167 |
# File 'lib/loxxy/back_end/resolver.rb', line 164 def after_get_expr(aGetExpr, aVisitor) # Evaluate object part aGetExpr.object.accept(aVisitor) end |
#after_if_stmt(anIfStmt, aVisitor) ⇒ Object
91 92 93 94 |
# File 'lib/loxxy/back_end/resolver.rb', line 91 def after_if_stmt(anIfStmt, aVisitor) anIfStmt.then_stmt.accept(aVisitor) anIfStmt.else_stmt&.accept(aVisitor) end |
#after_logical_expr(aLogicalExpr, aVisitor) ⇒ Object
141 142 143 144 |
# File 'lib/loxxy/back_end/resolver.rb', line 141 def after_logical_expr(aLogicalExpr, aVisitor) # Force the visit of second operand (resolver should ignore shortcuts) aLogicalExpr.operands.last.accept(aVisitor) end |
#after_set_expr(aSetExpr, aVisitor) ⇒ Object
135 136 137 138 139 |
# File 'lib/loxxy/back_end/resolver.rb', line 135 def after_set_expr(aSetExpr, aVisitor) aSetExpr.value.accept(aVisitor) # Evaluate object part aSetExpr.object.accept(aVisitor) end |
#after_super_expr(aSuperExpr, aVisitor) ⇒ Object
rubocop: disable Style/CaseLikeIf rubocop: disable Style/StringConcatenation
183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/loxxy/back_end/resolver.rb', line 183 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 Loxxy::RuntimeError, err_msg elsif current_class == :class err_msg = msg_prefix + 'in a class without superclass.' raise Loxxy::RuntimeError, err_msg end # 'super' behaves closely to a local variable resolve_local(aSuperExpr, aVisitor) end |
#after_this_expr(aThisExpr, aVisitor) ⇒ Object
176 177 178 179 |
# File 'lib/loxxy/back_end/resolver.rb', line 176 def after_this_expr(aThisExpr, aVisitor) # 'this' behaves closely to a local variable resolve_local(aThisExpr, aVisitor) end |
#after_var_stmt(aVarStmt) ⇒ Object
126 127 128 |
# File 'lib/loxxy/back_end/resolver.rb', line 126 def after_var_stmt(aVarStmt) define(aVarStmt.name) end |
#after_variable_expr(aVarExpr, aVisitor) ⇒ Object
154 155 156 |
# File 'lib/loxxy/back_end/resolver.rb', line 154 def after_variable_expr(aVarExpr, aVisitor) resolve_local(aVarExpr, aVisitor) end |
#after_while_stmt(aWhileStmt, aVisitor) ⇒ Object
113 114 115 116 |
# File 'lib/loxxy/back_end/resolver.rb', line 113 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_fun_stmt(aFunStmt, aVisitor) ⇒ Object
function declaration creates a new scope for its body & binds its parameters for that scope
201 202 203 204 205 |
# File 'lib/loxxy/back_end/resolver.rb', line 201 def before_fun_stmt(aFunStmt, aVisitor) declare(aFunStmt.name) define(aFunStmt.name) resolve_function(aFunStmt, :function, aVisitor) end |
#before_return_stmt(returnStmt) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/loxxy/back_end/resolver.rb', line 96 def before_return_stmt(returnStmt) if scopes.size < 2 msg = "Error at 'return': Can't return from top-level code." raise Loxxy::RuntimeError, msg end if current_function == :none msg = "Error at 'return': Can't return from outside a function." raise Loxxy::RuntimeError, msg end if current_function == :initializer msg = "Error at 'return': Can't return a value from an initializer." raise Loxxy::RuntimeError, msg unless returnStmt.subnodes[0].kind_of?(Datatype::Nil) end end |
#before_this_expr(_thisExpr) ⇒ Object
169 170 171 172 173 174 |
# File 'lib/loxxy/back_end/resolver.rb', line 169 def before_this_expr(_thisExpr) if current_class == :none msg = "Error at 'this': Can't use 'this' outside of a class." raise Loxxy::RuntimeError, msg end end |
#before_var_stmt(aVarStmt) ⇒ Object
A variable declaration adds a new variable to current scope
119 120 121 122 123 124 |
# File 'lib/loxxy/back_end/resolver.rb', line 119 def before_var_stmt(aVarStmt) # Oddly enough, Lox allows the re-definition of a variable at top-level scope return if scopes.size == 1 && scopes.last[aVarStmt.name] declare(aVarStmt.name) end |
#before_variable_expr(aVarExpr) ⇒ Object
Variable expressions require their variables resolved
147 148 149 150 151 152 |
# File 'lib/loxxy/back_end/resolver.rb', line 147 def before_variable_expr(aVarExpr) var_name = aVarExpr.name if !scopes.empty? && (scopes.last[var_name] == false) raise Loxxy::RuntimeError, "Can't read variable #{var_name} in its own initializer" end end |