Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
210 lines (183 sloc) 4.64 KB
class Awesome
end
# Collection of nodes each one representing an expression.
class Nodes < Awesome
attr_accessor :nodes
def initialize(nodes)
@nodes = nodes
end
def <<(node)
@nodes << node
self
end
# This method is the "interpreter" part of our language.
# All nodes know how to eval itself and returns the result
# of its evaluation.
# The "context" variable is the environment in which the node
# is evaluated (local variables, current class, etc.).
def eval(context)
# The last value evaluated in a method is the return value.
@nodes.map { |node| node.eval(context) }.last
end
end
# Literals are static values that have a Ruby representation,
# eg.: a string, a number, true, false, nil, etc.
class LiteralNode < Awesome
attr_accessor :value
def initialize(value)
@value = value
end
def eval(context)
case @value
when Numeric
Runtime["Number"].new_value(@value)
when String
Runtime["String"].new_value(@value)
when TrueClass
Runtime["true"]
when FalseClass
Runtime["false"]
when NilClass
Runtime["nil"]
else
raise "Unknown literal type: " + @value.class.name
end
end
end
class VarNode < Awesome
attr_accessor :name
def initialize(name)
@name = name
end
def eval(context)
context[@name]
end
end
# Node of a method call or local variable access,
# can take any of these forms:
#
# method # this form can also be a local variable
# method(argument1, argument2)
# receiver.method
# receiver.method(argument1, argument2)
#
class CallNode < Awesome
attr_accessor :receiver, :method, :arguments, :is_end
def initialize(receiver, method, arguments=[], is_end=false)
@receiver = receiver
@method = method
@arguments = arguments
@is_end = is_end
end
def eval(context)
# If there's no receiver and the method name is
# the name of a local variable, then it's a local
# variable access.
# This trick allows us to skip the () when calling
# a method.
if @receiver.nil? && context.locals[@method]
context.locals[@method]
# Method call
else
# In case there's no receiver we default to self
# So that calling "print" is like "self.print".
if @receiver
receiver = @receiver.eval(context)
else
receiver = context.current_self
end
arguments = @arguments.map { |arg| arg.eval(context) }
receiver.call(@method, arguments)
end
end
end
class ArrayNode < Awesome
attr_accessor :values
def initialize(arguments=[])
@values = arguments
end
def end(context)
context[@value]
end
end
# Retreiving the value of a constant.
class GetConstantNode < Awesome
attr_accessor :name
def initialize(name)
@name = name
end
def eval(context)
context[@name]
end
end
# Setting the value of a constant.
class SetConstantNode < Awesome
attr_accessor :name, :value
def initialize(name, value)
@name = name
@value = value
end
def eval(context)
context[@name] = @value.eval(context)
end
end
# Setting the value of a local variable.
class SetLocalNode < Awesome
attr_accessor :name, :value, :is_end
def initialize(name, value, is_end)
@name = name
@value = value
@is_end = is_end
end
def eval(context)
context.locals[@name] = @value.eval(context)
end
end
# Method definition.
class DefNode < Awesome
attr_accessor :name, :params, :body
def initialize(name, params, body)
@name = name
@params = params
@body = body
end
def eval(context)
context.current_class.awesome_methods[@name] = AwesomeMethod.new(@params, @body)
end
end
# Class definition.
class ClassNode < Awesome
attr_accessor :name, :body
def initialize(name, body)
@name = name
@body = body
end
def eval(context)
# Create the class and put it's value in a constant.
awesome_class = AwesomeClass.new
context[@name] = awesome_class
# Evaluate the body of the class in its context.
@body.eval(Context.new(awesome_class, awesome_class))
awesome_class
end
end
# if-else control structure.
# Look at this node if you want to implement other
# control structures like while, for, loop, etc.
class IfNode < Awesome
attr_accessor :condition, :body, :else_body
def initialize(condition, body, else_body=nil)
@condition = condition
@body = body
@else_body = else_body
end
def eval(context)
# We turn the condition node into a Ruby value
# to use Ruby's "if" control structure.
if @condition.eval(context).ruby_value
@body.eval(context)
elsif @else_body
@else_body.eval(context)
end
end
end