mirror of
https://github.com/bjc/prosody.git
synced 2025-04-01 20:27:39 +03:00
198 lines
4.4 KiB
Lua
198 lines
4.4 KiB
Lua
-- 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;
|
|
|
|
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"]="\127", ["\\e"]="\27", ["\\f"]="\f", ["\\n"]="\n", ["\\r"]="\r", ["\\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
|
|
if peek() == "." then
|
|
num[#num+1] = read();
|
|
while isNumeric(peek()) do
|
|
num[#num+1] = read();
|
|
end
|
|
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
|
|
|
|
local _M = {};
|
|
|
|
function _M.parseFile(file)
|
|
return readFile(file);
|
|
end
|
|
|
|
return _M;
|