local base = require("inputters.base")
local _variant = "epnf"
local parser
local function load_parser ()
parser = require("inputters.sil-" .. _variant)
end
local inputter = pl.class(base)
inputter._name = "sil"
inputter.order = 50
inputter.appropriate = function (round, filename, doc)
if not parser then
load_parser()
end
if round == 1 then
return filename:match(".sil$")
elseif round == 2 then
local sniff = doc:sub(1, 100)
local promising = sniff:match("\\begin") or sniff:match("\\document") or sniff:match("\\sile")
return promising and inputter.appropriate(3, filename, doc) or false
elseif round == 3 then
local status, _ = pcall(parser, doc)
return status
end
end
function inputter:_init (options)
options = options or {}
if options.variant then
_variant = options.variant
load_parser()
else
if not parser then
load_parser()
end
end
self._parser = parser
base._init(self)
end
local linecache = {}
local lno, col, lastpos
local function resetCache ()
lno = 1
col = 1
lastpos = 0
linecache = { { lno = 1, pos = 1 } }
end
local function getline (str, pos)
local start = 1
lno = 1
if pos > lastpos then
lno = linecache[#linecache].lno
start = linecache[#linecache].pos + 1
col = 1
else
for j = 1, #linecache - 1 do
if linecache[j + 1].pos >= pos then
lno = linecache[j].lno
col = pos - linecache[j].pos
return lno, col
end
end
end
for i = start, pos do
if string.sub(str, i, i) == "\n" then
lno = lno + 1
col = 1
linecache[#linecache + 1] = { pos = i, lno = lno }
lastpos = i
end
col = col + 1
end
return lno, col
end
local function ast_from_parse_tree (tree, doc, depth)
if type(tree) == "string" then
return tree
end
if tree.pos then
tree.lno, tree.col = getline(doc, tree.pos)
tree.pos = nil
end
local sep if SU.debugging("inputter") then
depth = depth + 1
sep = (" "):rep(depth)
end
SU.debug("inputter", sep and (sep .. "Processing ID:"), tree.id)
local res
if tree.id == "comment" then
SU.debug("inputter", sep and (sep .. "Discarding comment"))
res = {}
elseif
false
or tree.id == "document"
or tree.id == "braced_content"
or tree.id == "passthrough_content"
or tree.id == "braced_passthrough_content"
or tree.id == "env_passthrough_content"
or tree.id == "text"
or tree.id == "passthrough_text"
or tree.id == "braced_passthrough_text"
or tree.id == "env_passthrough_text"
then
SU.debug("inputter", sep and (sep .. "Massaging a node"))
res = ast_from_parse_tree(tree[1], doc, depth)
elseif false or tree.id == "environment" or tree.id == "command" then
SU.debug("inputter", sep and (sep .. "Processing command"), tree.command, #tree, "subtrees")
local newtree = { command = tree.command,
options = tree.options,
id = tree.id,
lno = tree.lno,
col = tree.col,
}
for _, node in ipairs(tree) do
if type(node) == "table" then
SU.debug("inputter", sep and (sep .. " -"), node.id or "table")
local ast_node = ast_from_parse_tree(node, doc, depth)
if type(ast_node) == "table" and not ast_node.id then
SU.debug("inputter", sep and (sep .. " -"), "Collapsing subtree")
if #ast_node > 0 then
for _, child in ipairs(ast_node) do
if type(child) ~= "table" or child.id or #child > 0 then
table.insert(newtree, child)
end
end
end
else
table.insert(newtree, ast_node)
end
end
end
res = newtree
elseif tree.id == "content" then
SU.debug("inputter", sep and (sep .. "Massage content node"), #tree, "subtrees")
local newtree = {} for i, node in ipairs(tree) do
SU.debug("inputter", sep and (sep .. " -"), node.id)
newtree[i] = ast_from_parse_tree(node, doc, depth)
end
res = #newtree == 1 and not newtree.id and newtree[1] or newtree
elseif tree.id then
SU.error("Unknown node type: " .. tree.id)
else
SU.debug("inputter", sep and (sep .. "Table node"), #tree, "subtrees")
res = #tree == 1 and tree[1] or tree
end
SU.debug("inputter", sep and (sep .. "Returning a"), type(res) == "table" and res.id or "string")
return res
end
function inputter:parse (doc)
local status, result = pcall(self._parser, doc)
if not status then
return SU.error(([[
Unable to parse input document to an AST tree
Parser error:
%s
thrown from document beginning.]]):format(pl.stringx.indent(result, 6)))
end
resetCache()
local top = ast_from_parse_tree(result[1], doc, 0)
local tree
if top.command == "document" or top.command == "sile" then
tree = top
elseif type(top) == "table" then
for _, leaf in ipairs(top) do
if leaf.command and (leaf.command == "document" or leaf.command == "sile") then
tree = leaf
break
end
end
end
if not tree then
tree = { top, command = "document" }
end
return { tree }
end
return inputter