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