diff options
Diffstat (limited to 'support/digestif/digestif/ManuscriptLaTeX.lua')
-rw-r--r-- | support/digestif/digestif/ManuscriptLaTeX.lua | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/support/digestif/digestif/ManuscriptLaTeX.lua b/support/digestif/digestif/ManuscriptLaTeX.lua new file mode 100644 index 0000000000..1366b0e146 --- /dev/null +++ b/support/digestif/digestif/ManuscriptLaTeX.lua @@ -0,0 +1,360 @@ +local lpeg = require "lpeg" +local util = require "digestif.util" +local Manuscript = require "digestif.Manuscript" +local Parser = require "digestif.Parser" + +local path_join, path_split = util.path_join, util.path_split +local path_normalize = util.path_normalize +local nested_get, nested_put = util.nested_get, util.nested_put +local map, update, merge = util.map, util.update, util.merge +local format_filename_template = util.format_filename_template + +local ManuscriptLatex = util.class(Manuscript) + +ManuscriptLatex.parser = Parser() +ManuscriptLatex.format = "latex" +ManuscriptLatex.packages = {} +ManuscriptLatex.commands = {} +ManuscriptLatex.environments = {} +ManuscriptLatex.init_callbacks = {} +ManuscriptLatex.scan_references_callbacks = {} +ManuscriptLatex:add_package("latex") + +--* Snippets + +-- Make a snippet for an environment. +-- +-- Arguments: +-- cs: The command name +-- args: An argument list +-- +-- Returns: +-- A snippet string +-- +function ManuscriptLatex:snippet_env(cs, args) + local argsnippet = args and self:snippet_args(args) or "" + return "begin{" .. cs .. "}" .. argsnippet .. "\n\t$0\n\\end{" .. cs .. "}" +end + +-- Pretty-print an environment signature. +function ManuscriptLatex:signature_env(cs, args) + return self:signature_args(args, "\\begin{" .. cs .. "}") +end + +--* Helper functions + +local function first_mand(args) + for i, v in ipairs(args) do + if not v.optional then return i end + end +end + +local function first_opt(args) + for i, v in ipairs(args) do + if v.optional then return i end + end +end + +--* Global scanning + +--** Basic document elements + +function ManuscriptLatex.init_callbacks.input(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local idx = self.child_index + local template = self.commands[cs].filename or "?" + for r in self:argument_items(first_mand, pos, cs) do + local f = format_filename_template(template, self:substring_stripped(r)) + local ok = self:add_package(f) + if not ok then + idx[#idx + 1] = { + name = path_normalize(path_join(path_split(self.filename), f)), + pos = r.pos, + cont = r.cont, + manuscript = self + } + end + end + return cont +end + +function ManuscriptLatex.init_callbacks.label (m, pos, cs) + local idx = m.label_index + local args = m.commands[cs].arguments + local r = m:parse_command(pos, cs) + local i = first_mand(args) + if r[i] then + local l = m:substring_stripped(r[i]) + idx[#idx + 1] = { + name = l, + pos = r[i].pos, + cont = r[i].cont, + outer_pos = r.pos, + outer_cont = r.cont, + manuscript = m + } + end + return r.cont +end + +function ManuscriptLatex.init_callbacks.section(self, pos, cs) + local idx = self.section_index + for r in self:argument_items(first_mand, pos, cs) do + idx[#idx + 1] = { + name = self:substring_stripped(r), + level = self.commands[cs].section_level, + pos = r.pos, + cont = r.cont, + outer_pos = pos, + manuscript = self + } + end + return pos + #cs + 1 +end + +--** Bibliographic items + +function ManuscriptLatex.init_callbacks.bibitem (m, pos, cs) + local idx = m.bib_index + local args = m.commands[cs].arguments + local r = m:parse_command(pos, cs) + local i = first_mand(args) + if r[i] then + idx[#idx + 1] = { + name = m:substring_stripped(r[i]), + pos = r[i].pos, + cont = r[i].cont, + outer_pos = r.pos, + outer_cont = r.cont, + manuscript = m + } + end + return r.cont +end + +function ManuscriptLatex.init_callbacks.amsrefs_bib(self, pos, cs) + local idx = self.bib_index + local r = self:parse_command(pos, cs) + if r.incomplete then return r.cont end + local keys = self:read_keys(r[3]) + local authors, title, date = {}, "", "(n.d.)" + for i = 1, #keys do + local k, v = keys[i].key, keys[i].value + if k == "author" then + authors[#authors+1] = self.parser.clean(v:match("[^,]+", 2)) + elseif k == "title" then + title = self.parser.clean(v:sub(2, -2)) + elseif k == "date" then + date = self.parser.clean(v:match("(%d+)")) + end + end + idx[#idx + 1] = { + name = self:substring_stripped(r[1]), + text = string.format( + "%s %s; %s", + table.concat(authors, ", "), + date, + title), + pos = r[1].pos, + cont = r[1].cont, + outer_pos = r.pos, + outer_cont = r.cont, + manuscript = self + } + return r.cont +end + +--** Command definitions, TeX style + +ManuscriptLatex.init_callbacks.def = + require "digestif.ManuscriptPlainTeX".init_callbacks.def + +--** Command and environment definitions, LaTeX style + +local to_args = {} +for i = 1, 9 do + local t = merge(to_args[i-1] or {}) + t[i] = {meta = "#" .. i} + to_args[i] = t +end + +local function newcommand_args(number, default) + local args = to_args[number] + if default and args then + args = merge(args) -- make a copy + args[1] = { + meta = "#1", + optional = true, + delimiters = {"[", "]"}, + details = "Default: “" .. default .. "”." + } + end + return args +end + +function ManuscriptLatex.init_callbacks.newcommand(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local csname, nargs, optdefault + for r in self:argument_items(first_mand, pos, cs) do + csname = self:substring_stripped(r):sub(2) + end + for r in self:argument_items("number", pos, cs) do + nargs = tonumber(self:substring_stripped(r)) + end + for r in self:argument_items("default", pos, cs) do + optdefault = self:substring_stripped(r) + end + if csname then + local idx = self:get_index "newcommand" + idx[#idx+1] = { + name = csname, + pos = pos, + cont = cont, + manuscript = self, + arguments = newcommand_args(nargs, optdefault) + } + if not self.commands[csname] then + self.commands[csname] = idx[#idx] + end + end + return cont +end + +function ManuscriptLatex.init_callbacks.newenvironment(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local csname, nargs, optdefault + for r in self:argument_items(first_mand, pos, cs) do + csname = self:substring_stripped(r) + end + for r in self:argument_items("number", pos, cs) do + nargs = tonumber(self:substring_stripped(r)) + end + for r in self:argument_items("default", pos, cs) do + optdefault = self:substring_stripped(r) + end + if csname then + local idx = self:get_index("newenvironment") + idx[#idx+1] = { + name = csname, + pos = pos, + cont = cont, + manuscript = self, + arguments = newcommand_args(nargs, optdefault) + } + if not self.environments[csname] then + self.environments[csname] = idx[#idx] + end + end + return cont +end + +--** Command and environment definitions, xparse style + +local P, C, Cc, Cg, Ct = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cg, lpeg.Ct +local Pgroup = util.between_balanced("{", "}") +local Pdefault = Cg(Pgroup / "Default: “%1”", "details") + +local xparse_args = util.matcher( + Ct(util.many(Ct(util.sequence( + P" "^0, + (P"+" * Cg(Cc(true), "long"))^-1, + P"!"^-1, + util.choice( + "m", + "r" * Cg(Ct(C(1) * C(1)), "delimiters"), + "R" * Cg(Ct(C(1) * C(1)), "delimiters") * Pdefault, + "v" * Cg(Cc"verbatim", "type"), + "o" * Cg(Cc{"[", "]"}, "delimiters") * Cg(Cc(true), "optional"), + "O" * Cg(Cc{"[", "]"}, "delimiters") * Cg(Cc(true), "optional") * Pdefault, + "d" * Cg(Ct(C(1) * C(1)), "delimiters") * Cg(Cc(true), "optional"), + "D" * Cg(Ct(C(1) * C(1)), "delimiters") * Cg(Cc(true), "optional") * Pdefault, + "s" * Cg(Cc"*", "literal") * Cg(Cc"literal", "type") * Cg(Cc(true), "optional"), + "t" * Cg(C(1), "literal") * Cg(Cc"literal", "type") * Cg(Cc(true), "optional"), + "e" * Pgroup, + "E" * Pgroup * Pdefault)))))) + +function ManuscriptLatex.init_callbacks.NewDocumentCommand(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local csname, arg_spec + for r in self:argument_items("command", pos, cs) do + csname = self:substring_stripped(r):sub(2) + end + for r in self:argument_items("arg spec", pos, cs) do + arg_spec = self:substring_stripped(r) + end + if csname then + local idx = self:get_index "newcommand" + idx[#idx+1] = { + name = csname, + pos = pos, + cont = cont, + manuscript = self, + arguments = xparse_args(arg_spec) + } + if not self.commands[csname] then + self.commands[csname] = idx[#idx] + end + end + return cont +end + +function ManuscriptLatex.init_callbacks.NewDocumentEnvironment(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local csname, arg_spec + for r in self:argument_items("environment", pos, cs) do + csname = self:substring_stripped(r) + end + for r in self:argument_items("arg spec", pos, cs) do + arg_spec = self:substring_stripped(r) + end + if csname then + local idx = self:get_index "newenvironment" + idx[#idx+1] = { + name = csname, + pos = pos, + cont = cont, + manuscript = self, + arguments = xparse_args(arg_spec) + } + if not self.environments[csname] then + self.environments[csname] = idx[#idx] + end + end + return cont +end + +--* Scan references callbacks + +function ManuscriptLatex.scan_references_callbacks.ref(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local idx = self.ref_index + for r in self:argument_items(first_mand, pos, cs) do + idx[#idx + 1] = { + name = self:substring_stripped(r), + pos = r.pos, + cont = r.cont, + outer_pos = pos, + outer_cont = cont, + manuscript = self + } + end + return cont +end + +function ManuscriptLatex.scan_references_callbacks.cite(self, pos, cs) + local cont = self:parse_command(pos, cs).cont + local idx = self.cite_index + for r in self:argument_items(first_mand, pos, cs) do + idx[#idx + 1] = { + name = self:substring_stripped(r), + pos = r.pos, + cont = r.cont, + outer_pos = pos, + outer_cont = cont, + manuscript = self + } + end + return cont +end + +return ManuscriptLatex |