about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--read.lua56
-rw-r--r--repl.lua95
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
7 7
8-- TODO: 8-- TODO:
9-- - string reading 9-- - string reading
10-- - # syntax
11-- - comments
12-- - probably more 10-- - probably more
13 11
12m.eof = setmetatable({}, {
13 __type = "EOF",
14 __tostring = function () return "#<eof>" end,
15})
16
17local function inport_next_token (port)
18 local tok, toktype
19 while true do
20 if #port.line == 0 then
21 if port.file then
22 local ln = port.file:read()
23 if ln == nil then return m.eof end
24 port.line = m.tochars(ln)
25 else
26 return nil
27 end
28 end
29 tok, toktype, port.line = m.scan(port.line)()
30 port.line = port.line or {}
31 if tok ~= nil then return tok, toktype end
32 end
33end
34
14function m.inport (source, kind) 35function m.inport (source, kind)
15 -- KIND can be one of "file", "string"; defaults to "file" 36 -- KIND can be one of "file", "string"; defaults to "file"
16 -- SOURCE is the name of the file or the string to read, or nil; if nil, 37 -- 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)
18 local f, l 39 local f, l
19 local k = kind or "file" 40 local k = kind or "file"
20 if source then 41 if source then
21 if kind == "file" then 42 if k == "file" then
22 f = io.open(source, "r") 43 f = io.open(source, "r")
23 elseif kind == "string" then 44 elseif k == "string" then
24 l = m.tochars(source) 45 l = m.tochars(source)
25 end 46 end
26 else 47 else
@@ -29,25 +50,10 @@ function m.inport (source, kind)
29 end 50 end
30 local t = { 51 local t = {
31 file = f, 52 file = f,
53 filename = source,
54 kind = kind,
32 line = l or {}, 55 line = l or {},
33 next_token = 56 next_token = inport_next_token,
34 function (self)
35 local tok, toktype
36 while true do
37 if #self.line == 0 and self.file then
38 self.line = m.tochars(
39 self.file:read("*l"))
40 end
41 if not self.line or #self.line == 0 then
42 return nil
43 end
44 tok, toktype, self.line =
45 m.scan(self.line)()
46 if tok ~= nil then
47 return tok, toktype
48 end
49 end
50 end,
51 } 57 }
52 if t.file then t.close = function (self) self.file:close() end; end 58 if t.file then t.close = function (self) self.file:close() end; end
53 local mt = { 59 local mt = {
@@ -232,12 +238,12 @@ m.readmacros = {
232 table.insert(Q, m.read(port)) 238 table.insert(Q, m.read(port))
233 return t.list(Q) 239 return t.list(Q)
234 end, 240 end,
235 comment = idf(nil), 241 comment = idf(nil)
236} 242}
237 243
238function m.read (port) 244function m.read (port)
239 local function read_ahead (tok, toktype) 245 local function read_ahead (tok, toktype)
240 if tok == nil then error("Unexpected EOF") end 246 if tok == m.eof then error("Unexpected EOF") end
241 if toktype == "open" then 247 if toktype == "open" then
242 local L = {} 248 local L = {}
243 while true do 249 while true do
@@ -262,7 +268,7 @@ function m.read (port)
262 end 268 end
263 -- body of read 269 -- body of read
264 local tok1, toktype1 = port:next_token() 270 local tok1, toktype1 = port:next_token()
265 if tok1 == nil then return nil 271 if tok1 == m.eof then return m.eof
266 else return read_ahead(tok1, toktype1) 272 else return read_ahead(tok1, toktype1)
267 end 273 end
268end 274end
diff --git a/repl.lua b/repl.lua index be739c7..86bc0af 100644 --- a/repl.lua +++ b/repl.lua
@@ -1,9 +1,10 @@
1--- lam.repl 1--- lam.repl
2 2
3local m = {} 3local m = {}
4local read = require("read") 4local _r = require("read")
5local eval = require("eval") 5local read, inport, read_string, eof =
6local pp = require("dump").pp 6 _r.read, _r.inport, _r.read_string, _r.eof
7local eval = require("eval").eval
7 8
8local function schemeprint (x) 9local function schemeprint (x)
9 -- if x == nil then return end 10 -- if x == nil then return end
@@ -12,7 +13,7 @@ local function schemeprint (x)
12 elseif x == false then 13 elseif x == false then
13 print("#f") 14 print("#f")
14 elseif x == nil then 15 elseif x == nil then
15 print("#n") 16 print("#<nil>")
16 else 17 else
17 print(x) 18 print(x)
18 end 19 end
@@ -26,57 +27,67 @@ local lam = [[
26 ------------- 27 -------------
27]] 28]]
28 29
29function m.repl (prompt, infile, out) 30local function handle_error (e)
30 -- PROMPT should be a string, INFILE is a filename, and OUT is either a 31 local start = e:find(": ")
31 -- filename, nil (in which case it will be stdout), or false (which 32 return e:sub(start + 2)
32 -- suppresses output) 33end
33 local inport = read.inport(infile) 34
34 if out ~= false then 35function m.read_eval (filename, interactive)
35 io.output(out) 36 -- interactive = { out = file or handle, prompt = string, }
37 local inport = inport(filename)
38 local prompt = interactive and interactive.prompt or "> "
39 if interactive then
40 io.output(interactive.out or io.stdout)
36 io.write(lam) 41 io.write(lam)
42 io.output():setvbuf("line")
43 else
44 io.output(io.stdout) -- hmmm
45 io.output():setvbuf("no")
37 end 46 end
38 io.output():setvbuf("line") 47 repeat
39 if prompt then 48 if interactive then
40 stderr = io.open("/dev/stderr", "w") -- Linux-only ! 49 io.stderr:write(prompt)
41 end 50 io.stderr:flush()
42 while true do -- loop
43 if prompt then
44 stderr:write(prompt)
45 stderr:flush()
46 end 51 end
47 -- read 52 -- read
48 local ok, x = xpcall( 53 local ok, x = xpcall(
49 function () return read.read(inport) end, 54 function ()
50 function (e) 55 local nxt = read(inport)
51 local start = e:find(": ") 56 return nxt
52 return e:sub(start+2) 57 end,
53 end 58 handle_error
54 ) 59 )
55 if not ok then 60 if not ok then
56 print("(read) not ok: " .. x) 61 io.stderr:write("(read) not ok: ", x, "\n")
57 x = nil 62 -- in interactive mode, errors should not be fatal. in
63 -- batch mode, they should be.
64 if not interactive then return nil end
58 end 65 end
59 -- eval 66 -- eval
60 if x then 67 if ok then
61 local ok, val = 68 local ok, v = xpcall(
62 xpcall( 69 function () return eval(x) end,
63 function () return eval.eval(x) end, 70 handle_error
64 function (e) 71 )
65 local start = e:find(": ")
66 return e:sub(start+2)
67 end
68 )
69 if not ok then 72 if not ok then
70 print("(eval) not ok: " .. val) 73 io.stderr:write("(eval) not ok: ", v, "\n")
71 elseif out ~= false then 74 if not interactive then return nil end
72 -- print
73 schemeprint(val)
74 end 75 end
76 -- print
77 if ok and interactive then schemeprint(v) end
78 elseif interactive then
79 ok = "recover"
75 end 80 end
76 end 81 until x == eof -- loop
77 inport:close() 82 inport:close()
78 stderr:close() 83end
79 io.output():close() 84
85function m.repl (prompt)
86 return m.read_eval(nil, { prompt = prompt, })
87end
88
89function m.load (filename)
90 return m.read_eval(filename)
80end 91end
81 92
82-------- 93--------