Class: Iode::Interpreter

Inherits:
Object
  • Object
show all
Defined in:
lib/iode/interpreter.rb

Overview

Iode interpreter, providing the central #eval function.

Instance Method Summary collapse

Constructor Details

#initialize(scope = Scope.new) ⇒ Interpreter

Create a new Interpreter with a given Scope.

Parameters:

  • scope (Scope) (defaults to: Scope.new)

    the initial environment



24
25
26
# File 'lib/iode/interpreter.rb', line 24

def initialize(scope = Scope.new)
  @env = scope
end

Instance Method Details

#apply(fn, args) ⇒ Object

Apply a function to its arguments.

Parameters:

  • fn (Callable)

    a Proc or a Lambda

  • args (Array)

    a list of arguments to apply with

Returns:

  • (Object)

    the function return value



96
97
98
99
100
101
102
# File 'lib/iode/interpreter.rb', line 96

def apply(fn, args)
  if fn.respond_to?(:call)
    fn.call(*args)
  else
    raise "Cannot apply non-function `#{fn}`"
  end
end

#car(list) ⇒ Object

Get the head (car) of a list.

Parameters:

  • list (Array)

    the list to return the car from

Returns:

  • (Object)

    the first element in list



35
36
37
38
# File 'lib/iode/interpreter.rb', line 35

def car(list)
  v, *_ = list
  v
end

#cdr(list) ⇒ Array

Get the tail (cdr) of a list.

Parameters:

  • list (Array)

    the list to return the cdr from

Returns:

  • (Array)

    all but the head of the list



47
48
49
50
# File 'lib/iode/interpreter.rb', line 47

def cdr(list)
  _, *v = list
  v
end

#eval(sexp) ⇒ Object

Given an iode data structure, execute it.

Parameters:

  • sexp (Object)

    any valid S-expression in iode

Returns:

  • (Object)

    whatever the expression evaluates to



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/iode/interpreter.rb', line 111

def eval(sexp)
  case sexp
  when Array
    case car(sexp)
    when nil
      nil
    when :quote
      car(cdr(sexp))
    when :if
      if eval(car(cdr(sexp)))
        eval(car(cdr(cdr(sexp))))
      else
        eval(car(cdr(cdr(cdr(sexp)))))
      end
    when :progn
      progn(*cdr(sexp))
    when :set!
      @env[car(cdr(sexp))] = eval(car(cdr(cdr(sexp))))
    when :def
      @env.define(car(cdr(sexp)), eval(car(cdr(cdr(sexp)))))
    when :lambda
      lambda(car(cdr(sexp)), *cdr(cdr(sexp)))
    else
      apply(eval(car(sexp)), cdr(sexp).map(&method(:eval)))
    end
  when Symbol
    @env[sexp]
  else
    sexp
  end
end

#lambda(argnames, *sexps) ⇒ Proc

Create a new lambda for Iode.

These lambdas act as closures in their environment.

Parameters:

  • argnames (Array)

    a list of argument names as function inputs

  • *sexps (Object...)

    variadic list of S-Expressions for the body

Returns:

  • (Proc)

    a callable lambda



78
79
80
81
82
83
84
# File 'lib/iode/interpreter.rb', line 78

def lambda(argnames, *sexps)
  Proc.new do |*args|
    Interpreter.new(
      @env.push_scope(Hash[argnames.zip(args)])
    ).progn(*sexps)
  end
end

#progn(*sexps) ⇒ Object

Create an explicit progn block.

A progn encapsulates a list of S-Expressions to be evaluated in sequence. The last evaluated S-Expression becomes the value of the progn.

Parameters:

  • *sexps (Object...)

    a list of S-Expressions to wrap in a progn

Returns:

  • (Object)

    the value of the last S-Expression



62
63
64
# File 'lib/iode/interpreter.rb', line 62

def progn(*sexps)
  sexps.inject(nil){|_,s| eval(s)}
end