September 20, 2018

100 Days Of Clojure Code Day 6 - Make a Lisp - Part 2

I want to take a different approach today, and set up the simplest interpreter I can.

Here's one that Eric Normand shared on Apropos. We begin by defining some predicates for things that evaluate to themselves, like numbers and strings:

(defn my-eval [env expr]
  (cond
    (number? expr)
    expr
    (string? expr)
    expr))

(my-eval {} 10)
(my-eval {} "yo")

Symbols get the value out of the env:

(defn my-eval [env expr]
  (cond
    (number? expr)
    expr
    (string? expr)
    expr
    (symbol? expr)
    (get env expr)))

(my-eval {'x 10} 'x)

Now lists, booleans and conditional logic:

(defn my-eval [env expr]
  (cond
    (number? expr)
    expr
    (string? expr)
    expr
    (boolean? expr)
    expr
    (symbol? expr)
    (get env expr)
    (list? expr)
    (let [[op & args] expr]
      (case op
        'if
        (let [[test then else] args]
          (if (my-eval env test)
            (my-eval env then)
            (my-eval env else)))))))

(my-eval {} '(if true 10 20))
(my-eval {} '(if false 10 20))

Now function calls:

(defn my-eval [env expr]
  (cond
    (number? expr)
    expr
    (string? expr)
    expr
    (boolean? expr)
    expr
    (symbol? expr)
    (get env expr)
    (list? expr)
    (let [[op & args] expr]
      (case op
        'if
        (let [[test then else] args]
          (if (my-eval env test)
            (my-eval env then)
            (my-eval env else)))
        (let [[f & args](doall (map #(my-eval env %) expr))]
          (apply f args))))))

(my-eval {'+ +} '(+ 1 2))
(my-eval {'+ +} '(+ 1 2 5))
(my-eval {'+ +} '(+ 1 2 5
                    (if true 10 0)))
Tags: 100 Days Of Code coding exercises KLIPSE Clojure