--- Prosody IM v0.1
--- Copyright (C) 2008 Matthew Wild
--- Copyright (C) 2008 Waqas Hussain
+-- Prosody IM
+-- Copyright (C) 2008-2009 Matthew Wild
+-- Copyright (C) 2008-2009 Waqas Hussain
--
--- This program is free software; you can redistribute it and/or
--- modify it under the terms of the GNU General Public License
--- as published by the Free Software Foundation; either version 2
--- of the License, or (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--- GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License
--- along with this program; if not, write to the Free Software
--- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
--
-\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 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
+ 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 = string.byte("AaZz09@_ ", 1, 9);
+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);
+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..escapes[slash];
+ slash = nil;
+ elseif ch == "\"" then
+ break;
+ elseif ch == "\\" then
+ slash = ch;
+ else
+ str = str..ch;
+ end
+ end
+ return str;
+end
+local function readAtom1()
+ local var = read();
+ while isAtom(peek()) do
+ var = var..read();
+ end
+ return var;
+end
+local function readAtom2()
+ local str = read("'");
+ local slash = nil;
+ while true do
+ local ch = read();
+ str = str..ch;
+ if ch == "'" and not slash then break; end
+ end
+ return str;
+end
+local function readNumber()
+ local num = read();
+ while isNumeric(peek()) do
+ num = num..read();
+ end
+ return tonumber(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) ~= type(0) or item > 255 then
+ s = nil;
+ elseif s then
+ s = s..string.char(item);
+ end
+ table.insert(t, item);
+ end
+ read(); -- read }, or ], or >
+ if s and s ~= "" then
+ return s
+ else
+ return t
+ end;
+end
+local function readBinary()
+ read("<"); -- read <
+ local t = readTuple();
+ read(">") -- read >
+ local ch = peek();
+ if type(t) == type("") then
+ -- binary is a list of integers
+ return t;
+ elseif type(t) == type({}) 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;