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