lib/turmali/interpreter.rb in turmali-0.0.1 vs lib/turmali/interpreter.rb in turmali-0.0.2

- old
+ new

@@ -3,44 +3,30 @@ require "turmali/runtime/method" require "turmali/runtime/context" require "turmali/runtime/class" require "turmali/runtime/bootstrap" -# First, we create an simple wrapper class to encapsulate the interpretation process. -# All this does is parse the code and call `eval` on the node at the top of the AST. class Interpreter def initialize @parser = Parser.new end def eval(code) @parser.parse(code).eval(RootContext) end end -# The `Nodes` class will always be at the top of the AST. Its only purpose it to -# contain other nodes. It correspond to a block of code or a series of expressions. -# -# The `eval` method of every node is the "interpreter" part of our language. -# All nodes know how to evalualte themselves and return the result of their evaluation. -# The `context` variable is the `Context` in which the node is evaluated (local -# variables, current self and current class). class Nodes def eval(context) return_value = nil nodes.each do |node| return_value = node.eval(context) end - return_value || Constants["nil"] # Last result is return value (or nil if none). + return_value || Constants["nil"] end end -# We're using `Constants` that we created before when bootstrapping the runtime to access -# the objects and classes from inside the runtime. -# -# Next, we implement `eval` on other node types. Think of that `eval` method as how the -# node bring itself to life inside the runtime. class NumberNode def eval(context) Constants["Number"].new_with_value(value) end end @@ -79,13 +65,10 @@ def eval(context) context.locals[name] end end -# When setting the value of a constant or a local variable, the `value` attribute -# is a node, created by the parser. We need to evaluate the node first, to convert -# it to an object, before storing it into a variable or constant. class SetConstantNode def eval(context) Constants[name] = value.eval(context) end end @@ -94,63 +77,50 @@ def eval(context) context.locals[name] = value.eval(context) end end -# The `CallNode` for calling a method is a little more complex. It needs to set the receiver -# first and then evaluate the arguments before calling the method. class CallNode def eval(context) if receiver value = receiver.eval(context) else - value = context.current_self # Default to `self` if no receiver. + value = context.current_self end evaluated_arguments = arguments.map { |arg| arg.eval(context) } value.call(method, evaluated_arguments) end end -# Defining a method, using the `def` keyword, is done by adding a method to the current class. class DefNode def eval(context) method = TurmaliMethod.new(params, body) context.current_class.runtime_methods[name] = method end end -# Defining a class is done in three steps: -# -# 1. Reopen or define the class. -# 2. Create a special context of evaluation (set `current_self` and `current_class` to the new class). -# 3. Evaluate the body of the class inside that context. -# -# Check back how `DefNode` was implemented, adding methods to `context.current_class`. Here is -# where we set the value of `current_class`. class ClassNode def eval(context) - turmali_class = Constants[name] # Check if class is already defined + turmali_class = Constants[name] - unless turmali_class # Class doesn't exist yet + unless turmali_class turmali_class = TurmaliClass.new - Constants[name] = turmali_class # Define the class in the runtime + Constants[name] = turmali_class end class_context = Context.new(turmali_class, turmali_class) body.eval(class_context) turmali_class end end -# Finally, to implement `if` in our language, -# we turn the condition node into a Ruby value to use Ruby's `if`. class IfNode def eval(context) if condition.eval(context).ruby_value body.eval(context) - else # If no body is evaluated, we return nil. + else Constants["nil"] end end -end +end \ No newline at end of file