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