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.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.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
'abs':     abs,
'apply':   lambda proc, args: proc(*args),
'begin':   lambda *x: x[-1],
'car':     lambda x: x,
'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!