Class: Loxxy::BackEnd::Engine
- Inherits:
-
Object
- Object
- Loxxy::BackEnd::Engine
- Defined in:
- lib/loxxy/back_end/engine.rb
Overview
An instance of this class executes the statements as when they occur during the abstract syntax tree walking.
Defined Under Namespace
Classes: NativeFunction
Instance Attribute Summary collapse
- #binary_operators ⇒ Hash { Symbol => BinaryOperator} readonly
-
#config ⇒ Hash
readonly
A set of configuration options.
- #resolver ⇒ BackEnd::Resolver readonly
-
#stack ⇒ Array<Datatype::BuiltinDatatype>
readonly
Data stack for arguments and return results.
- #symbol_table ⇒ BackEnd::SymbolTable readonly
- #unary_operators ⇒ Hash { Symbol => UnaryOperator} readonly
Instance Method Summary collapse
- #after_assign_expr(anAssignExpr, _visitor) ⇒ Object
- #after_binary_expr(aBinaryExpr) ⇒ Object
- #after_block_stmt(_aBlockStmt) ⇒ Object
- #after_call_expr(aCallExpr, aVisitor) ⇒ Object
- #after_class_stmt(aClassStmt, aVisitor) ⇒ Object
- #after_fun_stmt(aFunStmt, _visitor) ⇒ Object
- #after_get_expr(aGetExpr, aVisitor) ⇒ Object
- #after_grouping_expr(_groupingExpr) ⇒ Object
- #after_if_stmt(anIfStmt, aVisitor) ⇒ Object
- #after_logical_expr(aLogicalExpr, visitor) ⇒ Object
- #after_print_stmt(_printStmt) ⇒ Object
- #after_return_stmt(_returnStmt, _aVisitor) ⇒ Object
-
#after_seq_decl(aSeqDecls) ⇒ Object
Visit event handling.
- #after_set_expr(aSetExpr, aVisitor) ⇒ Object
- #after_super_expr(aSuperExpr, aVisitor) ⇒ Object
- #after_this_expr(aThisExpr, aVisitor) ⇒ Object
- #after_unary_expr(anUnaryExpr) ⇒ Object
- #after_var_stmt(aVarStmt) ⇒ Object
- #after_variable_expr(aVarExpr, aVisitor) ⇒ Object
- #after_while_stmt(aWhileStmt, aVisitor) ⇒ Object
- #before_block_stmt(_aBlockStmt) ⇒ Object
- #before_class_stmt(_class_stmt) ⇒ Object
- #before_for_stmt(aForStmt) ⇒ Object
- #before_fun_stmt(_fun_stmt, _visitor) ⇒ Object
- #before_if_stmt(_if_stmt) ⇒ Object
- #before_literal_expr(literalExpr) ⇒ Object
- #before_print_stmt(_print_stmt) ⇒ Object
- #before_return_stmt(_return_stmt) ⇒ Object
- #before_set_expr(_set_expr, _visitor) ⇒ Object
- #before_var_stmt(_var_stmt) ⇒ Object
- #before_visit_builtin(aValue) ⇒ Object
- #before_while_stmt(_while_stmt) ⇒ Object
-
#current_env ⇒ Loxxy::BackEnd::Environment
Returns the current environment.
-
#execute(aVisitor) ⇒ Loxxy::Datatype::BuiltinDatatype
Given an abstract syntax parse tree visitor, launch the visit and execute the visit events in the output stream.
- #expr_stack ⇒ Object
-
#initialize(theOptions) ⇒ Engine
constructor
A new instance of Engine.
- #logical_2nd_arg(operand2) ⇒ Object
- #reset_expr_stack ⇒ Object
Constructor Details
#initialize(theOptions) ⇒ Engine
Returns a new instance of Engine.
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/loxxy/back_end/engine.rb', line 36 def initialize(theOptions) @config = theOptions @istream = config.include?(:istream) ? config[:istream] : $stdin @ostream = config.include?(:ostream) ? config[:ostream] : $stdout @symbol_table = SymbolTable.new @stack = [] reset_expr_stack init_unary_operators init_binary_operators init_globals end |
Instance Attribute Details
#binary_operators ⇒ Hash { Symbol => BinaryOperator} (readonly)
30 31 32 |
# File 'lib/loxxy/back_end/engine.rb', line 30 def binary_operators @binary_operators end |
#config ⇒ Hash (readonly)
Returns A set of configuration options.
18 19 20 |
# File 'lib/loxxy/back_end/engine.rb', line 18 def config @config end |
#resolver ⇒ BackEnd::Resolver (readonly)
33 34 35 |
# File 'lib/loxxy/back_end/engine.rb', line 33 def resolver @resolver end |
#stack ⇒ Array<Datatype::BuiltinDatatype> (readonly)
Returns Data stack for arguments and return results.
24 25 26 |
# File 'lib/loxxy/back_end/engine.rb', line 24 def stack @stack end |
#symbol_table ⇒ BackEnd::SymbolTable (readonly)
21 22 23 |
# File 'lib/loxxy/back_end/engine.rb', line 21 def symbol_table @symbol_table end |
#unary_operators ⇒ Hash { Symbol => UnaryOperator} (readonly)
27 28 29 |
# File 'lib/loxxy/back_end/engine.rb', line 27 def unary_operators @unary_operators end |
Instance Method Details
#after_assign_expr(anAssignExpr, _visitor) ⇒ Object
198 199 200 201 202 203 204 205 |
# File 'lib/loxxy/back_end/engine.rb', line 198 def after_assign_expr(anAssignExpr, _visitor) var_name = anAssignExpr.name variable = variable_lookup(anAssignExpr) raise Loxxy::RuntimeError, "Undefined variable '#{var_name}'." unless variable value = expr_stack.last # ToS remains since an assignment produces a value variable.assign(value) end |
#after_binary_expr(aBinaryExpr) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/loxxy/back_end/engine.rb', line 257 def after_binary_expr(aBinaryExpr) operand2 = expr_stack.pop operand1 = expr_stack.pop op = aBinaryExpr.operator operator = binary_operators[op] operator.validate_operands(operand1, operand2) if operand1.respond_to?(op) result = operand1.send(op, operand2) expr_stack.push convert2lox_datatype(result) else msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}." raise StandardError, msg1 end end |
#after_block_stmt(_aBlockStmt) ⇒ Object
194 195 196 |
# File 'lib/loxxy/back_end/engine.rb', line 194 def after_block_stmt(_aBlockStmt) symbol_table.leave_environment end |
#after_call_expr(aCallExpr, aVisitor) ⇒ Object
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/loxxy/back_end/engine.rb', line 286 def after_call_expr(aCallExpr, aVisitor) # Evaluate callee part aCallExpr.callee.accept(aVisitor) callee = expr_stack.pop before_size = expr_stack.size aCallExpr.arguments.reverse_each { |arg| arg.accept(aVisitor) } after_size = expr_stack.size if after_size > before_size stack.concat(expr_stack.pop(after_size - before_size)) end case callee when NativeFunction expr_stack.push callee.call # Pass arguments when LoxFunction, LoxClass arg_count = aCallExpr.arguments.size if arg_count != callee.arity msg = "Expected #{callee.arity} arguments but got #{arg_count}." raise Loxxy::RuntimeError, msg end callee.call(self, aVisitor) else raise Loxxy::RuntimeError, 'Can only call functions and classes.' end end |
#after_class_stmt(aClassStmt, aVisitor) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/loxxy/back_end/engine.rb', line 90 def after_class_stmt(aClassStmt, aVisitor) if aClassStmt.superclass aClassStmt.superclass.accept(aVisitor) parent = expr_stack.pop unless parent.kind_of?(LoxClass) raise Loxxy::RuntimeError, 'Superclass must be a class.' end else parent = nil end if parent # Create an environment specific for 'super' super_env = Environment.new(symbol_table.current_env) symbol_table.enter_environment(super_env) end # Convert LoxFunStmt into LoxFunction meths = aClassStmt.body.map do |func_node| func_node.is_method = true func_node.accept(aVisitor) mth = expr_stack.pop mth.is_initializer = true if mth.name == 'init' mth end klass = LoxClass.new(aClassStmt.name, parent, meths, self) if parent super_var = Variable.new('super', klass) symbol_table.insert(super_var) symbol_table.leave_environment end new_var = Variable.new(aClassStmt.name, klass) symbol_table.insert(new_var) end |
#after_fun_stmt(aFunStmt, _visitor) ⇒ Object
370 371 372 373 374 375 376 377 378 |
# File 'lib/loxxy/back_end/engine.rb', line 370 def after_fun_stmt(aFunStmt, _visitor) function = LoxFunction.new(aFunStmt.name, aFunStmt.params, aFunStmt.body, self) if aFunStmt.is_method expr_stack.push function else new_var = Variable.new(aFunStmt.name, function) symbol_table.insert(new_var) end end |
#after_get_expr(aGetExpr, aVisitor) ⇒ Object
312 313 314 315 316 317 318 319 320 |
# File 'lib/loxxy/back_end/engine.rb', line 312 def after_get_expr(aGetExpr, aVisitor) aGetExpr.object.accept(aVisitor) instance = expr_stack.pop unless instance.kind_of?(LoxInstance) raise Loxxy::RuntimeError, 'Only instances have properties.' end expr_stack.push instance.get(aGetExpr.property) end |
#after_grouping_expr(_groupingExpr) ⇒ Object
322 323 324 |
# File 'lib/loxxy/back_end/engine.rb', line 322 def after_grouping_expr(_groupingExpr) # Do nothing: work was already done by visiting /evaluating the subexpression end |
#after_if_stmt(anIfStmt, aVisitor) ⇒ Object
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/loxxy/back_end/engine.rb', line 145 def after_if_stmt(anIfStmt, aVisitor) # Retrieve the result of the condition evaluation # condition = stack.pop condition = expr_stack.pop if condition.truthy? anIfStmt.then_stmt.accept(aVisitor) elsif anIfStmt.else_stmt anIfStmt.else_stmt.accept(aVisitor) end end |
#after_logical_expr(aLogicalExpr, visitor) ⇒ Object
226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/loxxy/back_end/engine.rb', line 226 def after_logical_expr(aLogicalExpr, visitor) op = aLogicalExpr.operator operand1 = expr_stack.pop # only first operand was evaluated result = nil if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?) result = operand1 else raw_operand2 = aLogicalExpr.subnodes[1] raw_operand2.accept(visitor) # Visit means operand2 is evaluated operand2 = expr_stack.pop result = logical_2nd_arg(operand2) end expr_stack.push result end |
#after_print_stmt(_printStmt) ⇒ Object
160 161 162 163 |
# File 'lib/loxxy/back_end/engine.rb', line 160 def after_print_stmt(_printStmt) tos = expr_stack.pop @ostream.print tos ? tos.to_str : 'nil' end |
#after_return_stmt(_returnStmt, _aVisitor) ⇒ Object
169 170 171 172 |
# File 'lib/loxxy/back_end/engine.rb', line 169 def after_return_stmt(_returnStmt, _aVisitor) stack.push(expr_stack.pop) throw(:return) end |
#after_seq_decl(aSeqDecls) ⇒ Object
Visit event handling
82 83 84 |
# File 'lib/loxxy/back_end/engine.rb', line 82 def after_seq_decl(aSeqDecls) # Do nothing, subnodes were already evaluated end |
#after_set_expr(aSetExpr, aVisitor) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/loxxy/back_end/engine.rb', line 211 def after_set_expr(aSetExpr, aVisitor) # Evaluate receiver object part (i.e. 'this') aSetExpr.object.accept(aVisitor) assignee = expr_stack.pop unless assignee.kind_of?(LoxInstance) raise Loxxy::RuntimeError, 'Only instances have fields.' end aSetExpr.value.accept(aVisitor) value = expr_stack.pop assignee.set(aSetExpr.property, value) expr_stack.push value end |
#after_super_expr(aSuperExpr, aVisitor) ⇒ Object
347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/loxxy/back_end/engine.rb', line 347 def after_super_expr(aSuperExpr, aVisitor) offset = resolver.locals[aSuperExpr] env = symbol_table.current_env (offset - 1).times { env = env.enclosing } instance = env.defns['this'].value.accept(aVisitor)[0] superklass = variable_lookup(aSuperExpr).value.superclass method = superklass.find_method(aSuperExpr.property) unless method raise Loxxy::RuntimeError, "Undefined property '#{aSuperExpr.property}'." end expr_stack.push method.bind(instance) end |
#after_this_expr(aThisExpr, aVisitor) ⇒ Object
342 343 344 345 |
# File 'lib/loxxy/back_end/engine.rb', line 342 def after_this_expr(aThisExpr, aVisitor) var = variable_lookup(aThisExpr) var.value.accept(aVisitor) # Evaluate this value then push on stack end |
#after_unary_expr(anUnaryExpr) ⇒ Object
272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/loxxy/back_end/engine.rb', line 272 def after_unary_expr(anUnaryExpr) operand = expr_stack.pop op = anUnaryExpr.operator operator = unary_operators[op] operator.validate_operand(operand) if operand.respond_to?(op) result = operand.send(op) expr_stack.push convert2lox_datatype(result) else msg1 = "`#{op}': Unimplemented operator for a #{operand.class}." raise StandardError, msg1 end end |
#after_var_stmt(aVarStmt) ⇒ Object
129 130 131 132 133 134 135 |
# File 'lib/loxxy/back_end/engine.rb', line 129 def after_var_stmt(aVarStmt) new_var = Variable.new(aVarStmt.name, Datatype::Nil.instance) symbol_table.insert(new_var) value = expr_stack.pop new_var.assign(value) end |
#after_variable_expr(aVarExpr, aVisitor) ⇒ Object
326 327 328 329 330 331 332 333 334 335 |
# File 'lib/loxxy/back_end/engine.rb', line 326 def after_variable_expr(aVarExpr, aVisitor) var_name = aVarExpr.name var = variable_lookup(aVarExpr) unless var pos = "line #{aVarExpr.position.line}:#{aVarExpr.position.column}" raise Loxxy::RuntimeError, "[#{pos}] Undefined variable '#{var_name}'." end var.value.accept(aVisitor) # Evaluate variable value then push on stack end |
#after_while_stmt(aWhileStmt, aVisitor) ⇒ Object
178 179 180 181 182 183 184 185 186 |
# File 'lib/loxxy/back_end/engine.rb', line 178 def after_while_stmt(aWhileStmt, aVisitor) loop do condition = expr_stack.pop break unless condition.truthy? aWhileStmt.body.accept(aVisitor) aWhileStmt.condition&.accept(aVisitor) end end |
#before_block_stmt(_aBlockStmt) ⇒ Object
188 189 190 191 192 |
# File 'lib/loxxy/back_end/engine.rb', line 188 def before_block_stmt(_aBlockStmt) reset_expr_stack new_env = Environment.new symbol_table.enter_environment(new_env) end |
#before_class_stmt(_class_stmt) ⇒ Object
86 87 88 |
# File 'lib/loxxy/back_end/engine.rb', line 86 def before_class_stmt(_class_stmt) reset_expr_stack end |
#before_for_stmt(aForStmt) ⇒ Object
137 138 139 |
# File 'lib/loxxy/back_end/engine.rb', line 137 def before_for_stmt(aForStmt) before_block_stmt(aForStmt) end |
#before_fun_stmt(_fun_stmt, _visitor) ⇒ Object
366 367 368 |
# File 'lib/loxxy/back_end/engine.rb', line 366 def before_fun_stmt(_fun_stmt, _visitor) reset_expr_stack end |
#before_if_stmt(_if_stmt) ⇒ Object
141 142 143 |
# File 'lib/loxxy/back_end/engine.rb', line 141 def before_if_stmt(_if_stmt) reset_expr_stack end |
#before_literal_expr(literalExpr) ⇒ Object
338 339 340 |
# File 'lib/loxxy/back_end/engine.rb', line 338 def before_literal_expr(literalExpr) expr_stack.push(literalExpr.literal) end |
#before_print_stmt(_print_stmt) ⇒ Object
156 157 158 |
# File 'lib/loxxy/back_end/engine.rb', line 156 def before_print_stmt(_print_stmt) reset_expr_stack end |
#before_return_stmt(_return_stmt) ⇒ Object
165 166 167 |
# File 'lib/loxxy/back_end/engine.rb', line 165 def before_return_stmt(_return_stmt) reset_expr_stack end |
#before_set_expr(_set_expr, _visitor) ⇒ Object
207 208 209 |
# File 'lib/loxxy/back_end/engine.rb', line 207 def before_set_expr(_set_expr, _visitor) reset_expr_stack end |
#before_var_stmt(_var_stmt) ⇒ Object
125 126 127 |
# File 'lib/loxxy/back_end/engine.rb', line 125 def before_var_stmt(_var_stmt) reset_expr_stack end |
#before_visit_builtin(aValue) ⇒ Object
362 363 364 |
# File 'lib/loxxy/back_end/engine.rb', line 362 def before_visit_builtin(aValue) expr_stack.push(aValue) end |
#before_while_stmt(_while_stmt) ⇒ Object
174 175 176 |
# File 'lib/loxxy/back_end/engine.rb', line 174 def before_while_stmt(_while_stmt) reset_expr_stack end |
#current_env ⇒ Loxxy::BackEnd::Environment
Returns the current environment
51 52 53 |
# File 'lib/loxxy/back_end/engine.rb', line 51 def current_env symbol_table.current_env end |
#execute(aVisitor) ⇒ Loxxy::Datatype::BuiltinDatatype
Given an abstract syntax parse tree visitor, launch the visit and execute the visit events in the output stream.
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/loxxy/back_end/engine.rb', line 67 def execute(aVisitor) # Do variable resolution pass first @resolver = BackEnd::Resolver.new resolver.analyze(aVisitor) aVisitor.subscribe(self) aVisitor.start aVisitor.unsubscribe(self) expr_stack.empty? ? Datatype::Nil.instance : expr_stack.pop end |
#expr_stack ⇒ Object
55 56 57 |
# File 'lib/loxxy/back_end/engine.rb', line 55 def expr_stack current_env.expr_stack end |
#logical_2nd_arg(operand2) ⇒ Object
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/loxxy/back_end/engine.rb', line 241 def logical_2nd_arg(operand2) case operand2 when false False.instance # Convert to Lox equivalent when nil Nil.instance # Convert to Lox equivalent when true True.instance # Convert to Lox equivalent when Proc # Second operand wasn't yet evaluated... operand2.call else operand2 end end |
#reset_expr_stack ⇒ Object
59 60 61 |
# File 'lib/loxxy/back_end/engine.rb', line 59 def reset_expr_stack current_env.expr_stack.clear end |