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