Merge 0.10->trunk
[prosody.git] / 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"]="\127", ["\\e"]="\27", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\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         if peek() == "." then
99                 num[#num+1] = read();
100                 while isNumeric(peek()) do
101                         num[#num+1] = read();
102                 end
103         end
104         return tonumber(t_concat(num));
105 end
106 local readItem = nil;
107 local function readTuple()
108         local t = {};
109         local s = {}; -- string representation
110         read(); -- read {, or [, or <
111         while true do
112                 local item = readItem();
113                 if not item then break; end
114                 if type(item) ~= "number" or item > 255 then
115                         s = nil;
116                 elseif s then
117                         s[#s+1] = string_char(item);
118                 end
119                 t_insert(t, item);
120         end
121         read(); -- read }, or ], or >
122         if s and #s > 0  then
123                 return t_concat(s)
124         else
125                 return t
126         end;
127 end
128 local function readBinary()
129         read("<"); -- read <
130         -- Discard PIDs
131         if isNumeric(peek()) then
132                 while peek() ~= ">" do read(); end
133                 read(">");
134                 return {};
135         end
136         local t = readTuple();
137         read(">") -- read >
138         local ch = peek();
139         if type(t) == "string" then
140                 -- binary is a list of integers
141                 return t;
142         elseif type(t) == "table" then
143                 if t[1] then
144                         -- binary contains string
145                         return t[1];
146                 else
147                         -- binary is empty
148                         return "";
149                 end;
150         else
151                 error();
152         end
153 end
154 readItem = function()
155         local ch = peek();
156         if ch == nil then return nil end
157         if ch == "{" or ch == "[" then
158                 return readTuple();
159         elseif isLowerAlpha(ch) then
160                 return readAtom1();
161         elseif ch == "'" then
162                 return readAtom2();
163         elseif isNumeric(ch) then
164                 return readNumber();
165         elseif ch == "\"" then
166                 return readString();
167         elseif ch == "<" then
168                 return readBinary();
169         elseif isSpace(ch) or ch == "," or ch == "|" then
170                 read();
171                 return readItem();
172         else
173                 --print("Unknown char: "..ch);
174                 return nil;
175         end
176 end
177 local function readChunk()
178         local x = readItem();
179         if x then read("."); end
180         return x;
181 end
182 local function readFile(filename)
183         file = io.open(filename);
184         if not file then error("File not found: "..filename); os.exit(0); end
185         return function()
186                 local x = readChunk();
187                 if not x and peek() then error("Invalid char: "..peek()); end
188                 return x;
189         end;
190 end
191
192 module "erlparse"
193
194 function parseFile(file)
195         return readFile(file);
196 end
197
198 return _M;