From bebcbdd000b55fa86df4c3c30b6ec7c85ff2dfab Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Fri, 5 Apr 2024 13:38:22 -0500 Subject: Fix reading from files ... i'm PRETTY sure --- read.lua | 56 +++++++++++++++++++++----------------- repl.lua | 95 ++++++++++++++++++++++++++++++++++++---------------------------- 2 files changed, 84 insertions(+), 67 deletions(-) diff --git a/read.lua b/read.lua index 4fb9b70..35f5b57 100644 --- a/read.lua +++ b/read.lua @@ -7,10 +7,31 @@ local pop = require("util").pop -- TODO: -- - string reading --- - # syntax --- - comments -- - probably more +m.eof = setmetatable({}, { + __type = "EOF", + __tostring = function () return "#" end, +}) + +local function inport_next_token (port) + local tok, toktype + while true do + if #port.line == 0 then + if port.file then + local ln = port.file:read() + if ln == nil then return m.eof end + port.line = m.tochars(ln) + else + return nil + end + end + tok, toktype, port.line = m.scan(port.line)() + port.line = port.line or {} + if tok ~= nil then return tok, toktype end + end +end + function m.inport (source, kind) -- KIND can be one of "file", "string"; defaults to "file" -- SOURCE is the name of the file or the string to read, or nil; if nil, @@ -18,9 +39,9 @@ function m.inport (source, kind) local f, l local k = kind or "file" if source then - if kind == "file" then + if k == "file" then f = io.open(source, "r") - elseif kind == "string" then + elseif k == "string" then l = m.tochars(source) end else @@ -29,25 +50,10 @@ function m.inport (source, kind) end local t = { file = f, + filename = source, + kind = kind, line = l or {}, - next_token = - function (self) - local tok, toktype - while true do - if #self.line == 0 and self.file then - self.line = m.tochars( - self.file:read("*l")) - end - if not self.line or #self.line == 0 then - return nil - end - tok, toktype, self.line = - m.scan(self.line)() - if tok ~= nil then - return tok, toktype - end - end - end, + next_token = inport_next_token, } if t.file then t.close = function (self) self.file:close() end; end local mt = { @@ -232,12 +238,12 @@ m.readmacros = { table.insert(Q, m.read(port)) return t.list(Q) end, - comment = idf(nil), + comment = idf(nil) } function m.read (port) local function read_ahead (tok, toktype) - if tok == nil then error("Unexpected EOF") end + if tok == m.eof then error("Unexpected EOF") end if toktype == "open" then local L = {} while true do @@ -262,7 +268,7 @@ function m.read (port) end -- body of read local tok1, toktype1 = port:next_token() - if tok1 == nil then return nil + if tok1 == m.eof then return m.eof else return read_ahead(tok1, toktype1) end end diff --git a/repl.lua b/repl.lua index be739c7..86bc0af 100644 --- a/repl.lua +++ b/repl.lua @@ -1,9 +1,10 @@ --- lam.repl local m = {} -local read = require("read") -local eval = require("eval") -local pp = require("dump").pp +local _r = require("read") +local read, inport, read_string, eof = + _r.read, _r.inport, _r.read_string, _r.eof +local eval = require("eval").eval local function schemeprint (x) -- if x == nil then return end @@ -12,7 +13,7 @@ local function schemeprint (x) elseif x == false then print("#f") elseif x == nil then - print("#n") + print("#") else print(x) end @@ -26,57 +27,67 @@ local lam = [[ ------------- ]] -function m.repl (prompt, infile, out) - -- PROMPT should be a string, INFILE is a filename, and OUT is either a - -- filename, nil (in which case it will be stdout), or false (which - -- suppresses output) - local inport = read.inport(infile) - if out ~= false then - io.output(out) +local function handle_error (e) + local start = e:find(": ") + return e:sub(start + 2) +end + +function m.read_eval (filename, interactive) + -- interactive = { out = file or handle, prompt = string, } + local inport = inport(filename) + local prompt = interactive and interactive.prompt or "> " + if interactive then + io.output(interactive.out or io.stdout) io.write(lam) + io.output():setvbuf("line") + else + io.output(io.stdout) -- hmmm + io.output():setvbuf("no") end - io.output():setvbuf("line") - if prompt then - stderr = io.open("/dev/stderr", "w") -- Linux-only ! - end - while true do -- loop - if prompt then - stderr:write(prompt) - stderr:flush() + repeat + if interactive then + io.stderr:write(prompt) + io.stderr:flush() end -- read local ok, x = xpcall( - function () return read.read(inport) end, - function (e) - local start = e:find(": ") - return e:sub(start+2) - end + function () + local nxt = read(inport) + return nxt + end, + handle_error ) if not ok then - print("(read) not ok: " .. x) - x = nil + io.stderr:write("(read) not ok: ", x, "\n") + -- in interactive mode, errors should not be fatal. in + -- batch mode, they should be. + if not interactive then return nil end end -- eval - if x then - local ok, val = - xpcall( - function () return eval.eval(x) end, - function (e) - local start = e:find(": ") - return e:sub(start+2) - end - ) + if ok then + local ok, v = xpcall( + function () return eval(x) end, + handle_error + ) if not ok then - print("(eval) not ok: " .. val) - elseif out ~= false then - -- print - schemeprint(val) + io.stderr:write("(eval) not ok: ", v, "\n") + if not interactive then return nil end end + -- print + if ok and interactive then schemeprint(v) end + elseif interactive then + ok = "recover" end - end + until x == eof -- loop inport:close() - stderr:close() - io.output():close() +end + +function m.repl (prompt) + return m.read_eval(nil, { prompt = prompt, }) +end + +function m.load (filename) + return m.read_eval(filename) end -------- -- cgit 1.4.1-21-gabe81