tools/erlparse.lua

1
-- Prosody IM
2
-- Copyright (C) 2008-2010 Matthew Wild
3
-- Copyright (C) 2008-2010 Waqas Hussain
4
-- 
5
-- This project is MIT/X11 licensed. Please see the
6
-- COPYING file in the source package for more information.
7
--
8
 
9
local string_byte, string_char = string.byte, string.char;
10
local t_concat, t_insert = table.concat, table.insert;
11
local type, tonumber, tostring = type, tonumber, tostring;
12
 
13
local file = nil;
14
local last = nil;
15
local line = 1;
16
local function read(expected)
17
	local ch;
18
	if last then
19
		ch = last; last = nil;
20
	else
21
		ch = file:read(1);
22
		if ch == "\n" then line = line + 1; end
23
	end
24
	if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil").." on line "..line); end
25
	return ch;
26
end
27
local function pushback(ch)
28
	if last then error(); end
29
	last = ch;
30
end
31
local function peek()
32
	if not last then last = read(); end
33
	return last;
34
end
35
 
36
local _A, _a, _Z, _z, _0, _9, __, _at, _space, _minus = string_byte("AaZz09@_ -", 1, 10);
37
local function isLowerAlpha(ch)
38
	ch = string_byte(ch) or 0;
39
	return (ch >= _a and ch <= _z);
40
end
41
local function isNumeric(ch)
42
	ch = string_byte(ch) or 0;
43
	return (ch >= _0 and ch <= _9) or ch == _minus;
44
end
45
local function isAtom(ch)
46
	ch = string_byte(ch) or 0;
47
	return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z) or (ch >= _0 and ch <= _9) or ch == __ or ch == _at;
48
end
49
local function isSpace(ch)
50
	ch = string_byte(ch) or "x";
51
	return ch <= _space;
52
end
53
 
54
local escapes = {["\\b"]="\b", ["\\d"]="\d", ["\\e"]="\e", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\s"]="\s", ["\\t"]="\t", ["\\v"]="\v", ["\\\""]="\"", ["\\'"]="'", ["\\\\"]="\\"};
55
local function readString()
56
	read("\""); -- skip quote
57
	local slash = nil;
58
	local str = {};
59
	while true do
60
		local ch = read();
61
		if slash then
62
			slash = slash..ch;
63
			if not escapes[slash] then error("Unknown escape sequence: "..slash); end
64
			str[#str+1] = escapes[slash];
65
			slash = nil;
66
		elseif ch == "\"" then
67
			break;
68
		elseif ch == "\\" then
69
			slash = ch;
70
		else
71
			str[#str+1] = ch;
72
		end
73
	end
74
	return t_concat(str);
75
end
76
local function readAtom1()
77
	local var = { read() };
78
	while isAtom(peek()) do
79
		var[#var+1] = read();
80
	end
81
	return t_concat(var);
82
end
83
local function readAtom2()
84
	local str = { read("'") };
85
	local slash = nil;
86
	while true do
87
		local ch = read();
88
		str[#str+1] = ch;
89
		if ch == "'" and not slash then break; end
90
	end
91
	return t_concat(str);
92
end
93
local function readNumber()
94
	local num = { read() };
95
	while isNumeric(peek()) do
96
		num[#num+1] = read();
97
	end
98
	return tonumber(t_concat(num));
99
end
100
local readItem = nil;
101
local function readTuple()
102
	local t = {};
103
	local s = {}; -- string representation
104
	read(); -- read {, or [, or <
105
	while true do
106
		local item = readItem();
107
		if not item then break; end
108
		if type(item) ~= "number" or item > 255 then
109
			s = nil;
110
		elseif s then
111
			s[#s+1] = string_char(item);
112
		end
113
		t_insert(t, item);
114
	end
115
	read(); -- read }, or ], or >
116
	if s and #s > 0  then
117
		return t_concat(s)
118
	else
119
		return t
120
	end;
121
end
122
local function readBinary()
123
	read("<"); -- read <
124
	-- Discard PIDs
125
	if isNumeric(peek()) then
126
		while peek() ~= ">" do read(); end
127
		read(">");
128
		return {};
129
	end
130
	local t = readTuple();
131
	read(">") -- read >
132
	local ch = peek();
133
	if type(t) == "string" then
134
		-- binary is a list of integers
135
		return t;
136
	elseif type(t) == "table" then
137
		if t[1] then
138
			-- binary contains string
139
			return t[1];
140
		else
141
			-- binary is empty
142
			return "";
143
		end;
144
	else
145
		error();
146
	end
147
end
148
readItem = function()
149
	local ch = peek();
150
	if ch == nil then return nil end
151
	if ch == "{" or ch == "[" then
152
		return readTuple();
153
	elseif isLowerAlpha(ch) then
154
		return readAtom1();
155
	elseif ch == "'" then
156
		return readAtom2();
157
	elseif isNumeric(ch) then
158
		return readNumber();
159
	elseif ch == "\"" then
160
		return readString();
161
	elseif ch == "<" then
162
		return readBinary();
163
	elseif isSpace(ch) or ch == "," or ch == "|" then
164
		read();
165
		return readItem();
166
	else
167
		--print("Unknown char: "..ch);
168
		return nil;
169
	end
170
end
171
local function readChunk()
172
	local x = readItem();
173
	if x then read("."); end
174
	return x;
175
end
176
local function readFile(filename)
177
	file = io.open(filename);
178
	if not file then error("File not found: "..filename); os.exit(0); end
179
	return function()
180
		local x = readChunk();
181
		if not x and peek() then error("Invalid char: "..peek()); end
182
		return x;
183
	end;
184
end
185
 
186
module "erlparse"
187
 
188
function parseFile(file)
189
	return readFile(file);
190
end
191
 
192
return _M;