From 9c5643d5f78e4228d47e293b421d7f388d47945f Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 19 Mar 2024 22:13:44 -0500 Subject: A new start! --- Makefile | 19 ++++++++++--------- README.md | 6 ++++++ base.lua | 37 +++++++++++++++++++++++++++++++++++++ eval.lua | 39 ++++++--------------------------------- read.lua | 2 +- repl.lua | 21 +++++++++++++++++++++ test.lua | 43 +++++++++++++++++++++++++++---------------- type.lua | 1 + 8 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 base.lua create mode 100644 repl.lua diff --git a/Makefile b/Makefile index 54c4f2c..f08ae06 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,12 @@ -LUA ?= rlwrap luajit +LUA ?= rlwrap luajit \ + -e 'pp = require "pp"' \ + -e 'eval = require "eval"' \ + -e 'read = require "read"' \ + -e 'type = require "type"' \ + -e 'utf8 = require "utf8"' \ + -e 'util = require "util"' \ + -e 'test = require "test"' + .PHONY: repl repl: @@ -6,14 +14,7 @@ repl: .PHONY: test test: - $(LUA) -i \ - -e 'pp = require "pp"' \ - -e 'eval = require "eval"' \ - -e 'read = require "read"' \ - -e 'type = require "type"' \ - -e 'utf8 = require "utf8"' \ - -e 'util = require "util"' \ - -e 'test = require "test"' + $(LUA) -e 'test.runtests()' .PHONY: check check: diff --git a/README.md b/README.md index 3728ba6..1dcd392 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ see COPYING for details. [email me]: mailto:acdw@acdw.net +## references + +- [Lua 5.1 reference](https://www.lua.org/manual/5.1/manual.html) +- [Luajit](https://luajit.org/) +- [R7RS](https://standards.scheme.org/corrected-r7rs/r7rs.html) + ## thanks thanks to peter norvig's [series on lisp interpreters][norvig] diff --git a/base.lua b/base.lua new file mode 100644 index 0000000..9c5b5b7 --- /dev/null +++ b/base.lua @@ -0,0 +1,37 @@ +--- lam.base --- base environment + +local base = {} +local type = require "type" +local isNull = type.isNull + +base.env = { + begin = + function (r) + local r = r + while not isNull(r.cdr) do + r = r.cdr + end + return r.car + end, + ["+"] = + function (r) + local r, a = r, 0 + while r.cdr do + r, a = r.cdr, a + r.car + end + return a + end, + ["-"] = + function (r) + if isNull(r) then return -1 end + if isNull(r.cdr) then return (- r.car) end + local r, a = r.cdr, r.car + while r.cdr do + r, a = r.cdr, a - r.car + end + return a + end, +} + +--- +return base diff --git a/eval.lua b/eval.lua index 5e897e2..e926bc4 100644 --- a/eval.lua +++ b/eval.lua @@ -1,6 +1,7 @@ --- lam.eval local eval = {} +local base = require "base" local type = require "type" local isNull, isList, isa, List, Cons = type.isNull, type.isList, type.isa, type.List, type.Cons @@ -30,6 +31,7 @@ function eval.Proc (params, body, env) inner[p.car] = a.car p, a = p.cdr, a.cdr end + pp.pp(self.body) return eval.eval( self.body, eval.Env(inner, self.env)) @@ -38,37 +40,8 @@ function eval.Proc (params, body, env) return setmetatable(v, mt) end -local global = { - begin = - function (r) - local r = r - while not isNull(r.cdr) do - r = r.cdr - end - return r.car - end, - ["+"] = - function (r) - local r, a = r, 0 - while r.cdr do - r, a = r.cdr, a + r.car - end - return a - end, - ["-"] = - function (r) - if isNull(r) then return -1 end - if isNull(r.cdr) then return (- r.car) end - local r, a = r.cdr, r.car - while r.cdr do - r, a = r.cdr, a - r.car - end - return a - end, -} - function eval.eval (x, env) - env = env or global + env = env or base.env if isa(x, "Symbol") then return env[x] elseif not isList(x) then @@ -77,13 +50,13 @@ function eval.eval (x, env) local op, args = x.car, x.cdr if op == "quote" then return args.car - elseif op == "define" then + elseif op == "define" or op == "def" then env[args.car] = eval.eval(args.cdr.car, env) return nil - elseif op == "lambda" then + elseif op == "lambda" or op == "lam" then return eval.Proc( args.car, - Cons("begin", args.cdr), + Cons("begin", args.cdr), -- i don't like this env) elseif op == "if" then assert(not isNull(args.cdr), "Malformed 'if'") diff --git a/read.lua b/read.lua index c5dacc0..d21e4cb 100644 --- a/read.lua +++ b/read.lua @@ -7,7 +7,7 @@ local util = require "util" local pop = util.pop local unpack = table.unpack or unpack -function program_characters (program) +local function program_characters (program) local chars = {} for pos, code in utf8.codes(program) do table.insert(chars, code) diff --git a/repl.lua b/repl.lua new file mode 100644 index 0000000..34e4c94 --- /dev/null +++ b/repl.lua @@ -0,0 +1,21 @@ +--- lam.repl + +local repl = {} +local eval = require("eval").eval +local read = require("read").read + +function repl.repl (prompt) + if not prompt then prompt = "lam> " end + io.input():setvbuf("line") + repeat + io.write(prompt) + io.output():flush() + local input = io.read() + if input == nil then break end + local value = eval(read(input)) + if value then print(value) end + until false +end + +--- +return repl diff --git a/test.lua b/test.lua index ce8c034..1d90df2 100644 --- a/test.lua +++ b/test.lua @@ -1,28 +1,39 @@ --- lam.test --- testing helpers local test = {} local eval = require("eval").eval local read = require("read").read +local luatype = require("type").luatype -function test.lambda () - local ls = { - [ [[((lambda (x) (+ x x)) 3)]] ] = 6, - [ [[((lambda () 100))]] ] = 100, - [ [[((lambda (x) 1 2 3) 4)]] ] = 3, - [ [[((lambda () 1 2 3))]] ] = 3, - [ [[((lambda (x) x (+ x x) (+ x x x)) 9)]] ] = 27, - } - for l, target in pairs(ls) do - io.write(string.format("%s == %s\n\t", l, target)) - local value = eval(read(l)) - if value == target then - print "ok" - else - print(string.format("not ok : %s", value)) +function test.test (form, expected) + local diag = string.format("%s == %s", form, expected) + local value = eval(read(form)) + if value == expected then + print(string.format("ok: %s", diag)) + else + print(string.format("not ok: %s != %s", diag, value)) + end +end + +function test.runtests () + for name, fn in pairs(test) do + if luatype(fn) == "function" + and name ~= "test" + and name ~= "runtests" + then + print(">>>", name) + fn() end end end +function test.lambda () + test.test([[((lambda (x) (+ x x)) 3)]], 6) + test.test([[((lambda () 100))]], 100) + test.test([[((lambda (x) 1 2 3) 4)]], 3) + test.test([[((lambda () 1 2 3))]], 3) + test.test([[((lambda (x) x (+ x x) (+ x x x)) 9)]], 27) +end + --- return test diff --git a/type.lua b/type.lua index 945f4d1..0d3b776 100644 --- a/type.lua +++ b/type.lua @@ -31,6 +31,7 @@ end -- Symbols and Numbers are strings and numbers, respectively. At some point -- I'll want to implement a full numeric tower and symbol tables or namespaces -- or whatever, but today is not that day + t.Symbol = tostring t.Number = tonumber -- cgit 1.4.1-21-gabe81