class Campa::Evaler
All the actual logic on how to evaluate the differente known forms in implemented in here.
Attributes
Public Class Methods
# File lib/campa/evaler.rb, line 6 def initialize @printer = Printer.new end
Public Instance Methods
Returns the result of a given form evaluation.
The parameter expression is evaluated based on it's type. Primitives (like booleans, nil, stringsā¦) are returned as is. {Symbol}s and {List}s are handled like this:
-
{Symbol}'s are searched in the given {Context} (env parameter).
-
{List}'s are considered function invocations.
@param expression Can be any known form. @param env [#[], []=] Hash or {Context} containing the bindings
for the current <i>Campa</i> execution.
# File lib/campa/evaler.rb, line 22 def call(expression, env = {}) context = self.context(env) case expression when Numeric, TrueClass, FalseClass, NilClass, String, ::Symbol, List::EMPTY expression when Symbol resolve(expression, context) when List invoke(expression, context) end end
Receives a {Reader} object and evaluate all forms returned by each #next call. Uses {#call} to do the actual evaluation.
@param reader [Reader] representing the source code to be evaluated @param env [Context] to evaluate the code @return [Object] the result of evaluating the last form
available in the given {Reader}
# File lib/campa/evaler.rb, line 44 def eval(reader, env = {}) context = self.context(env) result = nil while (token = reader.next) result = call(token, context) end result end
Private Instance Methods
# File lib/campa/evaler.rb, line 111 def args_for_fun(fun, args, context) return args if fun.respond_to?(:macro?) && fun.macro? args.map { |exp| call(exp, context) } end
# File lib/campa/evaler.rb, line 58 def context(env) return env if env.is_a?(Context) Context.new(env) end
# File lib/campa/evaler.rb, line 82 def cr?(invocation) invocation.head.is_a?(Symbol) && invocation.head.label.match?(CR_REGEX) end
# File lib/campa/evaler.rb, line 98 def extract_fun(invocation, context) # probable lambda invocation return call(invocation.head, context) if invocation.head.is_a?(List) resolve(invocation.head, context) .then { |rs| rs.is_a?(List) ? call(rs, context) : rs } .tap { |fn| raise not_a_function(invocation) if !fn.respond_to?(:call) } end
# File lib/campa/evaler.rb, line 70 def invoke(invocation, context) return invoke_cadr(invocation, context) if cr?(invocation) fn = extract_fun(invocation, context) args = args_for_fun(fn, invocation.tail.to_a, context) if with_env?(fn) fn.call(*args, env: context) else fn.call(*args) end end
# File lib/campa/evaler.rb, line 87 def invoke_cadr(invocation, context) call( List.new( Symbol.new("_cadr"), invocation.head, call(invocation.tail.head, context) ), context ) end
# File lib/campa/evaler.rb, line 107 def not_a_function(invocation) Error::NotAFunction.new printer.call(invocation.head) end
# File lib/campa/evaler.rb, line 124 def params_from_fun(fun) return fun.parameters if fun.is_a?(Proc) fun.method(:call).parameters end
# File lib/campa/evaler.rb, line 64 def resolve(symbol, context) raise Error::Resolution, printer.call(symbol) if !context.include?(symbol) context[symbol] end
# File lib/campa/evaler.rb, line 117 def with_env?(fun) !params_from_fun(fun) .filter { |param| param[0] == :keyreq } .find { |param| param[1] == :env } .nil? end