util.encodings: Fix small typo introduced in 7f789266b741
[prosody.git] / tools / erlparse.lua
index 0bd4b3b84a61988c03ca383b61509ac24363c3f1..dc3a2f94e6d44c3d03fa20d09dc57c94bec31d69 100644 (file)
@@ -1,21 +1,27 @@
 -- Prosody IM
--- Copyright (C) 2008-2009 Matthew Wild
--- Copyright (C) 2008-2009 Waqas Hussain
+-- 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;
 
 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); end
-       if expected and ch ~= expected then error("expected: "..expected.."; got: "..(ch or "nil")); end
+       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)
@@ -27,86 +33,133 @@ local function peek()
        return last;
 end
 
-local _A, _a, _Z, _z, _0, _9, __, _space = string.byte("AaZz09_ ", 1, 8);
-local function isAlpha(ch)
-       ch = string.byte(ch) or 0;
-       return (ch >= _A and ch <= _Z) or (ch >= _a and ch <= _z);
+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);
+       ch = string_byte(ch) or 0;
+       return (ch >= _0 and ch <= _9) or ch == _minus;
 end
-local function isVar(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 == __;
+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";
+       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 = "";
+       local str = {};
        while true do
                local ch = read();
-               if ch == "\"" and not slash then break; end
-               str = str..ch;
+               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
-       str = str:gsub("\\.", {["\\b"]="\b", ["\\d"]="\d", ["\\e"]="\e", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\s"]="\s", ["\\t"]="\t", ["\\v"]="\v", ["\\\""]="\"", ["\\'"]="'", ["\\\\"]="\\"});
-       return str;
-end
-local function readSpecialString()
-       read("<"); read("<"); -- read <<
-       local str = "";
-       if peek() == "\"" then
-               str = readString();
-       elseif peek() ~= ">" then
-               error();
+       return t_concat(str);
+end
+local function readAtom1()
+       local var = { read() };
+       while isAtom(peek()) do
+               var[#var+1] = read();
        end
-       read(">"); read(">"); -- read >>
-       return str;
+       return t_concat(var);
 end
-local function readVar()
-       local var = read();
-       while isVar(peek()) do
-               var = var..read();
+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 var;
+       return t_concat(str);
 end
 local function readNumber()
-       local num = read();
+       local num = { read() };
        while isNumeric(peek()) do
-               num = num..read();
+               num[#num+1] = read();
        end
-       return tonumber(num);
+       return tonumber(t_concat(num));
 end
 local readItem = nil;
 local function readTuple()
        local t = {};
-       read(); -- read { or [
+       local s = {}; -- string representation
+       read(); -- read {, or [, or <
        while true do
                local item = readItem();
                if not item then break; end
-               table.insert(t, item);
+               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
-       read(); -- read } or ]
-       return t;
 end
 readItem = function()
        local ch = peek();
        if ch == nil then return nil end
        if ch == "{" or ch == "[" then
                return readTuple();
-       elseif isAlpha(ch) then
-               return readVar();
+       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 readSpecialString();
+               return readBinary();
        elseif isSpace(ch) or ch == "," or ch == "|" then
                read();
                return readItem();