util.encodings: Fix small typo introduced in 7f789266b741
[prosody.git] / tools / erlparse.lua
index f7452765cb55bdaf0e3fdb82782fe892a48f1781..dc3a2f94e6d44c3d03fa20d09dc57c94bec31d69 100644 (file)
--- Prosody IM v0.4
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
 -- 
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
 --
 
+local string_byte, string_char = string.byte, string.char;
+local t_concat, t_insert = table.concat, table.insert;
+local type, tonumber, tostring = type, tonumber, tostring;
 
-\r
-local file = nil;\r
-local last = nil;\r
-local function read(expected)\r
-       local ch;\r
-       if last then\r
-               ch = last; last = nil;\r
-       else ch = file:read(1); end\r
-       if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil")); end\r
-       return ch;\r
-end\r
-local function pushback(ch)\r
-       if last then error(); end\r
-       last = ch;\r
-end\r
-local function peek()\r
-       if not last then last = read(); end\r
-       return last;\r
-end\r
-\r
-local _A, _a, _Z, _z, _0, _9, __, _space = string.byte("AaZz09_ ", 1, 8);\r
-local function isAlpha(ch)\r
-       ch = string.byte(ch) or 0;\r
-       return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z);\r
-end\r
-local function isNumeric(ch)\r
-       ch = string.byte(ch) or 0;\r
-       return (ch >= _0 and ch <= _9);\r
-end\r
-local function isVar(ch)\r
-       ch = string.byte(ch) or 0;\r
-       return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z) or (ch >= _0 and ch <= _9) or ch == __;\r
-end\r
-local function isSpace(ch)\r
-       ch = string.byte(ch) or "x";\r
-       return ch <= _space;\r
-end\r
-\r
-local function readString()\r
-       read("\""); -- skip quote\r
-       local slash = nil;\r
-       local str = "";\r
-       while true do\r
-               local ch = read();\r
-               if ch == "\"" and not slash then break; end\r
-               str = str..ch;\r
-       end\r
-       str = str:gsub("\\.", {["\\b"]="\b", ["\\d"]="\d", ["\\e"]="\e", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\s"]="\s", ["\\t"]="\t", ["\\v"]="\v", ["\\\""]="\"", ["\\'"]="'", ["\\\\"]="\\"});\r
-       return str;\r
-end\r
-local function readSpecialString()\r
-       read("<"); read("<"); -- read <<\r
-       local str = "";\r
-       if peek() == "\"" then\r
-               str = readString();\r
-       elseif peek() ~= ">" then\r
-               error();\r
-       end\r
-       read(">"); read(">"); -- read >>\r
-       return str;\r
-end\r
-local function readVar()\r
-       local var = read();\r
-       while isVar(peek()) do\r
-               var = var..read();\r
-       end\r
-       return var;\r
-end\r
-local function readNumber()\r
-       local num = read();\r
-       while isNumeric(peek()) do\r
-               num = num..read();\r
-       end\r
-       return tonumber(num);\r
-end\r
-local readItem = nil;\r
-local function readTuple()\r
-       local t = {};\r
-       read(); -- read { or [\r
-       while true do\r
-               local item = readItem();\r
-               if not item then break; end\r
-               table.insert(t, item);\r
-       end\r
-       read(); -- read } or ]\r
-       return t;\r
-end\r
-readItem = function()\r
-       local ch = peek();\r
-       if ch == nil then return nil end\r
-       if ch == "{" or ch == "[" then\r
-               return readTuple();\r
-       elseif isAlpha(ch) then\r
-               return readVar();\r
-       elseif isNumeric(ch) then\r
-               return readNumber();\r
-       elseif ch == "\"" then\r
-               return readString();\r
-       elseif ch == "<" then\r
-               return readSpecialString();\r
-       elseif isSpace(ch) or ch == "," or ch == "|" then\r
-               read();\r
-               return readItem();\r
-       else\r
-               --print("Unknown char: "..ch);\r
-               return nil;\r
-       end\r
-end\r
-local function readChunk()\r
-       local x = readItem();\r
-       if x then read("."); end\r
-       return x;\r
-end\r
-local function readFile(filename)\r
-       file = io.open(filename);\r
-       if not file then error("File not found: "..filename); os.exit(0); end\r
-       return function()\r
-               local x = readChunk();\r
-               if not x and peek() then error("Invalid char: "..peek()); end\r
-               return x;\r
-       end;\r
-end\r
-\r
-module "erlparse"\r
-\r
-function parseFile(file)\r
-       return readFile(file);\r
-end\r
-\r
-return _M;\r
+local file = nil;
+local last = nil;
+local line = 1;
+local function read(expected)
+       local ch;
+       if last then
+               ch = last; last = nil;
+       else
+               ch = file:read(1);
+               if ch == "\n" then line = line + 1; end
+       end
+       if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil").." on line "..line); end
+       return ch;
+end
+local function pushback(ch)
+       if last then error(); end
+       last = ch;
+end
+local function peek()
+       if not last then last = read(); end
+       return last;
+end
+
+local _A, _a, _Z, _z, _0, _9, __, _at, _space, _minus = string_byte("AaZz09@_ -", 1, 10);
+local function isLowerAlpha(ch)
+       ch = string_byte(ch) or 0;
+       return (ch >= _a and ch <= _z);
+end
+local function isNumeric(ch)
+       ch = string_byte(ch) or 0;
+       return (ch >= _0 and ch <= _9) or ch == _minus;
+end
+local function isAtom(ch)
+       ch = string_byte(ch) or 0;
+       return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z) or (ch >= _0 and ch <= _9) or ch == __ or ch == _at;
+end
+local function isSpace(ch)
+       ch = string_byte(ch) or "x";
+       return ch <= _space;
+end
+
+local escapes = {["\\b"]="\b", ["\\d"]="\d", ["\\e"]="\e", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\s"]="\s", ["\\t"]="\t", ["\\v"]="\v", ["\\\""]="\"", ["\\'"]="'", ["\\\\"]="\\"};
+local function readString()
+       read("\""); -- skip quote
+       local slash = nil;
+       local str = {};
+       while true do
+               local ch = read();
+               if slash then
+                       slash = slash..ch;
+                       if not escapes[slash] then error("Unknown escape sequence: "..slash); end
+                       str[#str+1] = escapes[slash];
+                       slash = nil;
+               elseif ch == "\"" then
+                       break;
+               elseif ch == "\\" then
+                       slash = ch;
+               else
+                       str[#str+1] = ch;
+               end
+       end
+       return t_concat(str);
+end
+local function readAtom1()
+       local var = { read() };
+       while isAtom(peek()) do
+               var[#var+1] = read();
+       end
+       return t_concat(var);
+end
+local function readAtom2()
+       local str = { read("'") };
+       local slash = nil;
+       while true do
+               local ch = read();
+               str[#str+1] = ch;
+               if ch == "'" and not slash then break; end
+       end
+       return t_concat(str);
+end
+local function readNumber()
+       local num = { read() };
+       while isNumeric(peek()) do
+               num[#num+1] = read();
+       end
+       return tonumber(t_concat(num));
+end
+local readItem = nil;
+local function readTuple()
+       local t = {};
+       local s = {}; -- string representation
+       read(); -- read {, or [, or <
+       while true do
+               local item = readItem();
+               if not item then break; end
+               if type(item) ~= "number" or item > 255 then
+                       s = nil;
+               elseif s then
+                       s[#s+1] = string_char(item);
+               end
+               t_insert(t, item);
+       end
+       read(); -- read }, or ], or >
+       if s and #s > 0  then
+               return t_concat(s)
+       else
+               return t
+       end;
+end
+local function readBinary()
+       read("<"); -- read <
+       -- Discard PIDs
+       if isNumeric(peek()) then
+               while peek() ~= ">" do read(); end
+               read(">");
+               return {};
+       end
+       local t = readTuple();
+       read(">") -- read >
+       local ch = peek();
+       if type(t) == "string" then
+               -- binary is a list of integers
+               return t;
+       elseif type(t) == "table" then
+               if t[1] then
+                       -- binary contains string
+                       return t[1];
+               else
+                       -- binary is empty
+                       return "";
+               end;
+       else
+               error();
+       end
+end
+readItem = function()
+       local ch = peek();
+       if ch == nil then return nil end
+       if ch == "{" or ch == "[" then
+               return readTuple();
+       elseif isLowerAlpha(ch) then
+               return readAtom1();
+       elseif ch == "'" then
+               return readAtom2();
+       elseif isNumeric(ch) then
+               return readNumber();
+       elseif ch == "\"" then
+               return readString();
+       elseif ch == "<" then
+               return readBinary();
+       elseif isSpace(ch) or ch == "," or ch == "|" then
+               read();
+               return readItem();
+       else
+               --print("Unknown char: "..ch);
+               return nil;
+       end
+end
+local function readChunk()
+       local x = readItem();
+       if x then read("."); end
+       return x;
+end
+local function readFile(filename)
+       file = io.open(filename);
+       if not file then error("File not found: "..filename); os.exit(0); end
+       return function()
+               local x = readChunk();
+               if not x and peek() then error("Invalid char: "..peek()); end
+               return x;
+       end;
+end
+
+module "erlparse"
+
+function parseFile(file)
+       return readFile(file);
+end
+
+return _M;