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