Just a bit more playing with the Lispy Calculator to while away a few minutes in the afternoon.

I believe that I’m only missing the if function from my eval, along with a lot of functions that show up in the environment. Let’s create a test.

        _:test("if", function()
            local prog = "(if (< 5 10) 10 20)"
            local result = LISP:eval(LISP:parse(prog))
            _:expect(result).is(10)
        end)

This is going to barf on the if and on the <.

9: if -- LISPCalc:53: No such symbol as Symbol if
    elseif x[1].token == 'if' then
        _ignored,test,conseq,alt = table.unpack(x)
        local exp = self:eval(test,env) and conseq or alt
        return self:eval(exp,env)

We evaluate the conditional (test) and then select which of the consequent and alternative to evaluate. if guarantees to evaluate only the chosen expression.

Now I imagine I’ll find < not to work.

9: if -- LISPCalc:53: No such symbol as Symbol <

So far so good. Put that into the environment.

    tab["<"] = function(a,b) return a<b end

I rather imagine that this will work. And it does. Life is good. Extend the test to a few more operators.

            prog = "(if (> 5 10) 10 20)"
            result = LISP:eval(LISP:parse(prog))
            _:expect(result).is(20)

Test will fail for want ot >. Providing that should make it green. And it does. Commit: if works. Added < and >.

Let’s do some more comparators and such. I feel as if I should test them all.

            result = LISP:exec("(if (<= 5 5) 1 0)")
            _:expect(result).is(1)
            result = LISP:exec("(if (<= 5 4) 1 0)")
            _:expect(result).is(0)
            result = LISP:exec("(if (>= 5 5) 1 0)")
            _:expect(result).is(1)
            result = LISP:exec("(if (>= 4 5) 1 0)")
            _:expect(result).is(0)

I decided that I need this:

9: if -- Tests:87: attempt to call a nil value (method 'exec')
function LISP:exec(prog)
    return self:eval(self:parse(prog))
end
9: if -- LISPCalc:53: No such symbol as Symbol <=
    tab[">="] = function(a,b) return a>=b end
    tab["<="] = function(a,b) return a<=b end

Tests green. Commit: >= and <=. LISP:exec does parse+eval.

We need = and eq?.

            result = LISP:exec("(if (= 5 5) 1 0)")
            _:expect(result).is(1)
            result = LISP:exec("(if (eq? 5 5) 1 0)")
            _:expect(result).is(1)
            result = LISP:exec("(if (= 5 6) 1 0)")
            _:expect(result).is(0)
            result = LISP:exec("(if (eq? 5 6) 1 0)")
            _:expect(result).is(0)
    tab["="] = function(a,b) return a==b end
    tab["eq?"] = function(a,b) return a==b end

Commit: = and eq?

A more careful reading tells me that eq? is identity and equals? is whatever = and op.eq are in Python. I’m going to ignore that issue for now.

I’m wondering about apply, among other things that I find in Norvig’s environment:

import math
import operator as op

def standard_env() -> Env:
    "An environment with some Scheme standard procedures."
    env = Env()
    env.update(vars(math)) # sin, cos, sqrt, pi, ...
    env.update({
        '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, 
        '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, 
        'abs':     abs,
        'append':  op.add,  
        'apply':   lambda proc, args: proc(*args),
        'begin':   lambda *x: x[-1],
        'car':     lambda x: x[0],
        'cdr':     lambda x: x[1:], 
        'cons':    lambda x,y: [x] + y,
        'eq?':     op.is_, 
        'expt':    pow,
        'equal?':  op.eq, 
        'length':  len, 
        'list':    lambda *x: List(x), 
        'list?':   lambda x: isinstance(x, List), 
        'map':     map,
        'max':     max,
        'min':     min,
        'not':     op.not_,
        'null?':   lambda x: x == [], 
        'number?': lambda x: isinstance(x, Number),  
		'print':   print,
        'procedure?': callable,
        'round':   round,
        'symbol?': lambda x: isinstance(x, Symbol),
    })
    return env

global_env = standard_env()

I am wondering about List, cons, car, and cdr, not because I don’t know what they are but because I’m not sure what the Python means. Remind me to learn some Python one of these days.

Same with map. Is it mapcar? I think Scheme says map when Lisp says mapcar. I think all that wants to wait until tomorrow, when I’ll mess with list. That should open up what apply and map are good for.

I really wish Norvig had provided some tests. It would help someone whose Lisp experience was before most of you were born. I’m a bit rusty.

Anyway, this was fun. See you next time!