1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
--- lam.port --- port objects
-- because the implementation for ports is fairly involved, they're in their own
-- file outside of `type'.
local m = {}
local util = require("util")
local error = util.error
local tochars = util.tochars
-- The EOF object is what the reader emits when it hits an end-of-file or use up
-- a port.
m.eof = setmetatable({}, {
__type = "eof",
__tostring = function () return "#<eof>" end,
})
---[[ INPUT PORTS ]]---
-- return the next token from PORT, given READTABLE
local function input_port_next_token (port, readtable)
repeat
if #port.buffer == 0 then
if port.file then
local ln = port.file:read()
if ln == nil then
return m.eof
end
port.buffer = tochars(ln)
else
return m.eof
end
end
local token, token_type
local c = port.buffer[1]
if readtable.chars[c] then
token, token_type, port.buffer =
readtable.chars[c](port.buffer)
else
for re, fn in pairs(readtable.regex) do
if c:match(re) then
token, token_type, port.buffer =
fn(port.buffer)
break
end
end
if token == nil then
token, token_type, port.buffer =
readtable.default(port.buffer)
end
end
port.buffer = port.buffer or {}
if token then
return token, token_type
end
until nil
end
function m.input_port (source, source_type)
-- SOURCE is the name of the file/string to read or nil; nil means
-- standard input. SOURCE_TYPE is one of "file", "string"; "file" is
-- the default.
local f, b
source_type = source_type or "file"
if source then
if source_type == "file" then
f = io.open(source, "r")
elseif source_type == "string" then
b = tochars(source)
else
error("input-port: bad type", source_type)
end
else
f = io.input() -- ignore SOURCE_TYPE
end
local t = {
file = f,
name = f and source or "[string]",
type = source_type,
buffer = b or {},
flush = function (self) self.buffer = {} end,
next = input_port_next_token, -- port:next(readtable)
close =
function (self)
if self.file then self.file:close() end
end,
}
local mt = {
__type = "input-port",
__tostring =
function (self)
return string.format("#<port %s>", self.name)
end,
}
return setmetatable(t, mt)
end
---[[ OUTPUT PORTS ]]---
-- TODO
--------
return m
|