diff options
-rw-r--r-- | read.lua | 56 | ||||
-rw-r--r-- | 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 | |||
7 | 7 | ||
8 | -- TODO: | 8 | -- TODO: |
9 | -- - string reading | 9 | -- - string reading |
10 | -- - # syntax | ||
11 | -- - comments | ||
12 | -- - probably more | 10 | -- - probably more |
13 | 11 | ||
12 | m.eof = setmetatable({}, { | ||
13 | __type = "EOF", | ||
14 | __tostring = function () return "#<eof>" end, | ||
15 | }) | ||
16 | |||
17 | local 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 | ||
33 | end | ||
34 | |||
14 | function m.inport (source, kind) | 35 | function 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 | ||
238 | function m.read (port) | 244 | function 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 |
268 | end | 274 | end |
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 | ||
3 | local m = {} | 3 | local m = {} |
4 | local read = require("read") | 4 | local _r = require("read") |
5 | local eval = require("eval") | 5 | local read, inport, read_string, eof = |
6 | local pp = require("dump").pp | 6 | _r.read, _r.inport, _r.read_string, _r.eof |
7 | local eval = require("eval").eval | ||
7 | 8 | ||
8 | local function schemeprint (x) | 9 | local 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 | ||
29 | function m.repl (prompt, infile, out) | 30 | local 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) | 33 | end |
33 | local inport = read.inport(infile) | 34 | |
34 | if out ~= false then | 35 | function 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 | |||
73 | schemeprint(val) | ||
74 | end | 75 | end |
76 | |||
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() | 83 | end |
79 | io.output():close() | 84 | |
85 | function m.repl (prompt) | ||
86 | return m.read_eval(nil, { prompt = prompt, }) | ||
87 | end | ||
88 | |||
89 | function m.load (filename) | ||
90 | return m.read_eval(filename) | ||
80 | end | 91 | end |
81 | 92 | ||
82 | -------- | 93 | -------- |