Merge from waqas
[prosody.git] / tools / erlparse.lua
1 -- Prosody IM v0.2
2 -- Copyright (C) 2008 Matthew Wild
3 -- Copyright (C) 2008 Waqas Hussain
4 -- 
5 -- This program is free software; you can redistribute it and/or
6 -- modify it under the terms of the GNU General Public License
7 -- as published by the Free Software Foundation; either version 2
8 -- of the License, or (at your option) any later version.
9 -- 
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 -- GNU General Public License for more details.
14 -- 
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 --
19
20
21 \r
22 local file = nil;\r
23 local last = nil;\r
24 local function read(expected)\r
25         local ch;\r
26         if last then\r
27                 ch = last; last = nil;\r
28         else ch = file:read(1); end\r
29         if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil")); end\r
30         return ch;\r
31 end\r
32 local function pushback(ch)\r
33         if last then error(); end\r
34         last = ch;\r
35 end\r
36 local function peek()\r
37         if not last then last = read(); end\r
38         return last;\r
39 end\r
40 \r
41 local _A, _a, _Z, _z, _0, _9, __, _space = string.byte("AaZz09_ ", 1, 8);\r
42 local function isAlpha(ch)\r
43         ch = string.byte(ch) or 0;\r
44         return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z);\r
45 end\r
46 local function isNumeric(ch)\r
47         ch = string.byte(ch) or 0;\r
48         return (ch >= _0 and ch <= _9);\r
49 end\r
50 local function isVar(ch)\r
51         ch = string.byte(ch) or 0;\r
52         return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z) or (ch >= _0 and ch <= _9) or ch == __;\r
53 end\r
54 local function isSpace(ch)\r
55         ch = string.byte(ch) or "x";\r
56         return ch <= _space;\r
57 end\r
58 \r
59 local function readString()\r
60         read("\""); -- skip quote\r
61         local slash = nil;\r
62         local str = "";\r
63         while true do\r
64                 local ch = read();\r
65                 if ch == "\"" and not slash then break; end\r
66                 str = str..ch;\r
67         end\r
68         str = str:gsub("\\.", {["\\b"]="\b", ["\\d"]="\d", ["\\e"]="\e", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\s"]="\s", ["\\t"]="\t", ["\\v"]="\v", ["\\\""]="\"", ["\\'"]="'", ["\\\\"]="\\"});\r
69         return str;\r
70 end\r
71 local function readSpecialString()\r
72         read("<"); read("<"); -- read <<\r
73         local str = "";\r
74         if peek() == "\"" then\r
75                 str = readString();\r
76         elseif peek() ~= ">" then\r
77                 error();\r
78         end\r
79         read(">"); read(">"); -- read >>\r
80         return str;\r
81 end\r
82 local function readVar()\r
83         local var = read();\r
84         while isVar(peek()) do\r
85                 var = var..read();\r
86         end\r
87         return var;\r
88 end\r
89 local function readNumber()\r
90         local num = read();\r
91         while isNumeric(peek()) do\r
92                 num = num..read();\r
93         end\r
94         return tonumber(num);\r
95 end\r
96 local readItem = nil;\r
97 local function readTuple()\r
98         local t = {};\r
99         read(); -- read { or [\r
100         while true do\r
101                 local item = readItem();\r
102                 if not item then break; end\r
103                 table.insert(t, item);\r
104         end\r
105         read(); -- read } or ]\r
106         return t;\r
107 end\r
108 readItem = function()\r
109         local ch = peek();\r
110         if ch == nil then return nil end\r
111         if ch == "{" or ch == "[" then\r
112                 return readTuple();\r
113         elseif isAlpha(ch) then\r
114                 return readVar();\r
115         elseif isNumeric(ch) then\r
116                 return readNumber();\r
117         elseif ch == "\"" then\r
118                 return readString();\r
119         elseif ch == "<" then\r
120                 return readSpecialString();\r
121         elseif isSpace(ch) or ch == "," or ch == "|" then\r
122                 read();\r
123                 return readItem();\r
124         else\r
125                 --print("Unknown char: "..ch);\r
126                 return nil;\r
127         end\r
128 end\r
129 local function readChunk()\r
130         local x = readItem();\r
131         if x then read("."); end\r
132         return x;\r
133 end\r
134 local function readFile(filename)\r
135         file = io.open(filename);\r
136         if not file then error("File not found: "..filename); os.exit(0); end\r
137         return function()\r
138                 local x = readChunk();\r
139                 if not x and peek() then error("Invalid char: "..peek()); end\r
140                 return x;\r
141         end;\r
142 end\r
143 \r
144 module "erlparse"\r
145 \r
146 function parseFile(file)\r
147         return readFile(file);\r
148 end\r
149 \r
150 return _M;\r