-- 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)
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();