about summary refs log tree commit diff stats
path: root/port.lua
blob: 812f05e2a46994a77b31f819603a25cc117c6416 (plain)
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