Refactoring: nested functions

This commit is contained in:
perro tuerto 2023-04-03 11:39:11 -07:00
parent 8bdc6035a8
commit 15be6fca8e
5 changed files with 510 additions and 392 deletions

443
dist/lin.lua vendored
View File

@ -6274,7 +6274,6 @@ end
-- Gets OS short name
-- It is just intented to know if it is Linux, macOS or Windows.
-- Sorry
-- @return string: "linux" or "bsd" or "macos" or "windows"
function os.uname()
local status, output = io.try("uname")
@ -6312,6 +6311,20 @@ function os.lang()
return "en", "US", "UTF-8"
end
-- Gets an equivalency table between ASCII and Unicode
-- Note: filled on demand.
-- @return table: ASCII-Unicode table equivalency
function utf8.table()
return {
["a"] = {"á", "à", "ä"},
["e"] = {"é", "è", "ë"},
["i"] = {"í", "ì", "ï"},
["o"] = {"ó", "ò", "ö"},
["u"] = {"ú", "ù", "ü"},
["n"] = {"ñ"},
}
end
-- Checks if string is empty
-- @return boolean: Empty or not
function string:isempty()
@ -6324,6 +6337,22 @@ function string:linearize()
return self:gsub("\n", "\\n")
end
-- Normalizes string
-- @return string: Normalized string
function string:normalize()
self = self:lower()
for newchar, chars in pairs(utf8.table()) do
for _, oldchar in ipairs(chars) do
self = self:gsub(oldchar, newchar)
end
end
return self
end
-- Adds indent
-- @param num number: Indent size, 2 by default
-- @param char string: Indent character, space by default
-- @return strin: Indented string
function string:indent(num, char)
num = num or 2
char = char or " "
@ -6331,20 +6360,34 @@ function string:indent(num, char)
return char .. self:gsub("\n", "\n" .. char)
end
-- Splits the string
-- Note: only support splitting by one character.
-- Could be solved by slicing with find.
-- @param sep string: String separator, space by default
-- @return table: String matches
function string:split(sep)
sep = sep or "%s+"
local parts = {}
for part in self:gmatch("([^" .. sep .. "]+)") do
table.insert(parts, part)
end
return parts
end
-- Removes spaces at the beginning of the string
-- @return string: left stripped string
-- @return string: Left stripped string
function string:lstrip()
return self:gsub("^%s+", "")
end
-- Removes spaces at the end of the string
-- @return string: right stripped string
-- @return string: Right stripped string
function string:rstrip()
return self:gsub("%s+$", "")
end
-- Removes spaces at the beginning and at the end of the string
-- @return string: stripped string
-- @return string: Stripped string
function string:strip()
self = self:lstrip():rstrip()
return self
@ -6384,14 +6427,26 @@ function string:isdir()
return false
end
-- Reads file content
-- @return string or nil: File content or nil
function string:read_text()
-- Reads file content as string
-- @return string or nil: File as string or nil
function string:readtext()
if self:exists() then
return io.open(self):read("*a")
end
end
-- Read file content as lines
-- @return table: Table of file lines or nil
function string:readlines()
local lines = {}
if self:exists() then
for line in io.open(self):lines() do
table.insert(lines, line)
end
end
return lines
end
-- Gets file without suffix
-- @return string: File wihtout suffix
function string:stem()
@ -6399,7 +6454,7 @@ function string:stem()
end
-- Gets file extensions
-- @return table: list of file extensions
-- @return table: List of file extensions
function string:suffixes()
local suffixes = {}
for suffix in self:gmatch("%.%a+") do
@ -6409,7 +6464,7 @@ function string:suffixes()
end
-- Gets file final extension
-- @return string: final file extension
-- @return string: Final file extension
function string:suffix()
local suffixes = self:suffixes()
if suffixes[#suffixes] then
@ -6445,7 +6500,7 @@ if pandoc ~= nil then
-- @return string: Output file content
function pandoc.convert(ifile, oformat, ofile, iformat)
iformat = (iformat == nil and pandoc.getext(ifile) or iformat)
local doc = pandoc.write(pandoc.read(ifile:read_text(), iformat), oformat)
local doc = pandoc.write(pandoc.read(ifile:readtext(), iformat), oformat)
if ofile ~= nil then
local eol = (os:isunix() and "\n" or "\r\n")
io.open(ofile, "w"):write(doc, eol):close()
@ -6492,27 +6547,32 @@ lit.metastruct = {
}
-- Grammars
lit.g = {}
function lit.g(name)
-- Lexical elements
lit.newline = lpeg.P"\r"^-1 * lpeg.P"\n"
lit.space = lpeg.S" \t"
lit.anyspace = lpeg.S" \t\r\n"
lit.spot = (1 - lit.anyspace)
lit.any = (lit.spot + lit.space)
lit.yamlheader = lit.space^0 * lpeg.P"---" * lit.space^0 * lit.newline
lit.yamlfooter = lit.space^0 * lpeg.P"..." * lit.space^0 * lit.newline^-1
lit.yamlbody = -lit.yamlfooter * lit.any^0 * lit.newline
lit.id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
lit.ref = lpeg.P"#" * lit.id
-- Lexical elements
local newline = lpeg.P"\r"^-1 * lpeg.P"\n"
local space = lpeg.S" \t"
local anyspace = lpeg.S" \t\r\n"
local spot = (1 - anyspace)
local any = (spot + space)
local yamlheader = space^0 * lpeg.P"---" * space^0 * newline
local yamlfooter = space^0 * lpeg.P"..." * space^0 * newline^-1
local yamlbody = -yamlfooter * any^0 * newline
local id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
local ref = lpeg.P"#" * id
-- Blocks grammar
lit.g.block = lpeg.P {
"Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(lit.yamlheader * lit.yamlbody^0 * lit.yamlfooter);
Code = lpeg.C((lit.any + lit.newline)^0);
}
local grammars = {
-- Blocks grammar
["block"] = lpeg.P {
"Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(yamlheader * yamlbody^0 * yamlfooter);
Code = lpeg.C((any + newline)^0);
},
}
return grammars[name]
end
-- Evals Lisp code
--[[
@ -6531,74 +6591,80 @@ function lit.eval(code)
end
]]--
function lit.stringify_quoted(quoted)
local quote = (quoted.quotetype == SingleQuoted and '"' or "'")
quoted = pandoc.utils.stringify(quoted.content)
return pandoc.Str(quote .. quoted .. quote)
end
function lit.stringify_rawinline(rawinline)
local str = rawinline.text
str = str:gsub("\\n", "\n"):gsub("\\t", "\t")
return pandoc.Str(str)
end
function lit.stringify(content)
if pandoc.utils.type(content) == "Inlines" then
return pandoc.utils.stringify(content:walk {
Quoted = function(e) return lit.stringify_quoted(e) end,
RawInline = function(e) return lit.stringify_rawinline(e) end,
Inline = function(inline) return pandoc.utils.stringify(inline) end,
})
else
return pandoc.utils.stringify(content)
end
end
function lit.string2other(str)
if str == "true" or str == "false" then
return str == "true"
-- WARN "1" always num; cfr. https://pandoc.org/lua-filters.html#type-metavalue
elseif tonumber(str) then
return tonumber(str)
else
return str
end
end
function lit.meta2table(meta)
local function stringify(content)
local function stringify_quoted(quoted)
local quote = (quoted.quotetype == SingleQuoted and '"' or "'")
quoted = pandoc.utils.stringify(quoted.content)
return pandoc.Str(quote .. quoted .. quote)
end
local function stringify_rawinline(rawinline)
local str = rawinline.text
str = str:gsub("\\n", "\n"):gsub("\\t", "\t")
return pandoc.Str(str)
end
if pandoc.utils.type(content) == "Inlines" then
return pandoc.utils.stringify(content:walk {
Quoted = function(e) return stringify_quoted(e) end,
RawInline = function(e) return stringify_rawinline(e) end,
Inline = function(inline) return pandoc.utils.stringify(inline) end,
})
else
return pandoc.utils.stringify(content)
end
end
local function string2other(str)
if str == "true" or str == "false" then
return str == "true"
-- Note that "1" always num;
-- cfr. https://pandoc.org/lua-filters.html#type-metavalue
elseif tonumber(str) then
return tonumber(str)
else
return str
end
end
for k, v in pairs(meta) do
if pandoc.utils.type(v) == "table" then
meta[k] = lit.meta2table(v)
else
meta[k] = lit.string2other(lit.stringify(v))
meta[k] = string2other(stringify(v))
end
end
return meta
end
function lit.debuglevel(str)
if str == "ERROR" then
lit.status = false
return 2
elseif str == "WARNING" then
return 1
else
return 0
end
end
function lit.puts(key, ...)
function lit.setmsg(msg, ...)
local lang = os.lang()
msg = (msg[lang] ~= nil and msg[lang] or msg)
for i, str in ipairs({...}) do
msg = msg:gsub("#" .. i, str)
local function debuglevel(str)
if str == "ERROR" then
lit.status = false
return 2
elseif str == "WARNING" then
return 1
else
return 0
end
end
return msg
end
function lit.getmsg(key, ...)
local msg = lit.meta2table(pandoc.read([[---
local function getmsg(key, ...)
local function setmsg(msg, ...)
local lang = os.lang()
msg = (msg[lang] ~= nil and msg[lang] or msg)
for i, str in ipairs({...}) do
msg = msg:gsub("#" .. i, str)
end
return msg
end
local msg = lit.meta2table(pandoc.read([[---
INFO:
parsing:
en: Parsing:\n#1
@ -6634,97 +6700,24 @@ ERROR:
es: YAML inválido
...
]]).meta)
local levelname, level = "INFO", 0
for mtype, msgs in pairs(msg) do
if msgs[key] ~= nil then
key = lit.setmsg(msgs[key], ...)
levelname, level = mtype, lit.debuglevel(mtype)
break
local levelname, level = "INFO", 0
for mtype, msgs in pairs(msg) do
if msgs[key] ~= nil then
key = setmsg(msgs[key], ...)
levelname, level = mtype, debuglevel(mtype)
break
end
end
return key, levelname, level
end
return key, levelname, level
end
function lit.puts(key, ...)
local verbosity = lit.debuglevel(PANDOC_STATE.verbosity)
local msg, levelname, level = lit.getmsg(key, ...)
local verbosity = debuglevel(PANDOC_STATE.verbosity)
local msg, levelname, level = getmsg(key, ...)
if level >= verbosity then
print("[" .. levelname .. "] [LIT] " .. msg)
end
end
function lit.getmetaval(meta, key)
local mtype = pandoc.utils.type(meta[key])
local mval = meta[key]
if mtype == "Inlines" then
mval = pandoc.utils.stringify(mval)
mtype = pandoc.utils.type(mval)
end
return mval, mtype
end
function lit.checkextra(meta)
for key, val in pairs(meta) do
local missing1 = lit.metastruct["mandatory"][key] == nil
local missing2 = lit.metastruct["optional"][key] == nil
local mval = lit.getmetaval(meta, key)
if missing1 and missing2 then
lit.puts("invalid_key", key)
end
end
end
function lit.checkmeta_type(type, meta, key)
local mval, mtype = lit.getmetaval(meta, key)
if type ~= mtype then
if type == "path" then
if not(pandoc.path.directory(mval):isdir()) then
lit.puts("invalid_path", mval, key)
end
else
lit.puts("invalid_type", mtype, key)
end
end
end
function lit.checkmeta_match(table, mval, key)
for _, pattern in pairs(table) do
if mval:match("^" .. pattern .. "$") then
return ""
end
end
return key
end
function lit.checkmeta_table(table, meta, key)
local mval = lit.getmetaval(meta, key)
local err = lit.checkmeta_match(table, mval, key)
if not(err:isempty()) then
lit.puts("invalid_value", mval, err)
end
end
function lit.checkmeta(meta, kind)
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil and kind == "mandatory" then
lit.puts("no_key", key)
elseif meta[key] and type(val) == "table" then
lit.checkmeta_table(val, meta, key)
elseif meta[key] then
lit.checkmeta_type(val, meta, key)
end
end
end
function lit.setmeta(meta)
lit.checkmeta(meta, "mandatory")
lit.checkmeta(meta, "optional")
lit.checkextra(meta)
-- TODO checks for 2) duplicates
-- Set defaults if lit.status = true
return meta
end
-- TODO
function lit.assert(e, status)
if status == false then
@ -6734,32 +6727,98 @@ function lit.assert(e, status)
return e
end
function lit.parseyaml(rawyaml)
-- TODO io.try
local yaml = io.popen("pandoc -t json <<<\"" .. rawyaml .. "\" 2>&1")
if yaml:read("*l"):sub(1, 1) == "{" then
yaml = pandoc.read(rawyaml).meta
if pandoc.utils.stringify(yaml):isempty() then
lit.puts("yaml_empty")
else
return lit.setmeta(yaml)
end
else
lit.puts("yaml_invalid")
return nil
end
end
function lit.parseblock(parsed)
lit.puts("parsing", table.concat(parsed, ""):indent())
local yaml = lit.parseyaml(parsed[1])
-- TODO: parsecode
return "TODO"
end
function lit.getblock(codeblock)
local parsed = lpeg.match(lit.g.block, codeblock.text)
parsed = (parsed ~= nil and lit.parseblock(parsed) or parsed)
local function checkextra(meta)
for key, val in pairs(meta) do
local missing1 = lit.metastruct["mandatory"][key] == nil
local missing2 = lit.metastruct["optional"][key] == nil
if missing1 and missing2 then
lit.puts("invalid_key", key)
end
end
end
local function checkmeta(meta, kind)
local function checktype(type1, meta, key)
local val = meta[key]
local type2 = type(val)
if type1 ~= type2 then
if type1 == "path" then
if not(pandoc.path.directory(val):isdir()) then
lit.puts("invalid_path", val, key)
end
else
lit.puts("invalid_type", type2, key)
end
end
end
local function checktable(table, meta, key)
local function checkmatch(table, val, key)
for _, pattern in pairs(table) do
if val:match("^" .. pattern .. "$") then
return ""
end
end
return key
end
local val = meta[key]
local err = checkmatch(table, val, key)
if not(err:isempty()) then
lit.puts("invalid_value", val, err)
end
end
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil and kind == "mandatory" then
lit.puts("no_key", key)
elseif meta[key] and type(val) == "table" then
checktable(val, meta, key)
elseif meta[key] then
checktype(val, meta, key)
end
end
end
local function setmeta(meta)
local tmeta = lit.meta2table(meta)
checkmeta(tmeta, "mandatory")
checkmeta(tmeta, "optional")
checkextra(tmeta)
-- TODO checks for 2) duplicates
-- Set defaults if lit.status = true
return meta
end
local function parseyaml(rawyaml)
-- TODO io.try
local yaml = io.popen("pandoc -t json <<<\"" .. rawyaml .. "\" 2>&1")
if yaml:read("*l"):sub(1, 1) == "{" then
yaml = pandoc.read(rawyaml).meta
if pandoc.utils.stringify(yaml):isempty() then
lit.puts("yaml_empty")
else
return setmeta(yaml)
end
else
lit.puts("yaml_invalid")
return nil
end
end
local function inspect(parsed)
lit.puts("parsing", table.concat(parsed, ""):indent())
local yaml = parseyaml(parsed[1])
-- TODO: parsecode
return "TODO"
end
local parsed = lpeg.match(lit.g("block"), codeblock.text)
parsed = (parsed ~= nil and inspect(parsed) or parsed)
return codeblock, lit.status
end

View File

@ -13,7 +13,6 @@ end
-- Gets OS short name
-- It is just intented to know if it is Linux, macOS or Windows.
-- Sorry
-- @return string: "linux" or "bsd" or "macos" or "windows"
function os.uname()
local status, output = io.try("uname")
@ -51,6 +50,20 @@ function os.lang()
return "en", "US", "UTF-8"
end
-- Gets an equivalency table between ASCII and Unicode
-- Note: filled on demand.
-- @return table: ASCII-Unicode table equivalency
function utf8.table()
return {
["a"] = {"á", "à", "ä"},
["e"] = {"é", "è", "ë"},
["i"] = {"í", "ì", "ï"},
["o"] = {"ó", "ò", "ö"},
["u"] = {"ú", "ù", "ü"},
["n"] = {"ñ"},
}
end
-- Checks if string is empty
-- @return boolean: Empty or not
function string:isempty()
@ -63,6 +76,22 @@ function string:linearize()
return self:gsub("\n", "\\n")
end
-- Normalizes string
-- @return string: Normalized string
function string:normalize()
self = self:lower()
for newchar, chars in pairs(utf8.table()) do
for _, oldchar in ipairs(chars) do
self = self:gsub(oldchar, newchar)
end
end
return self
end
-- Adds indent
-- @param num number: Indent size, 2 by default
-- @param char string: Indent character, space by default
-- @return strin: Indented string
function string:indent(num, char)
num = num or 2
char = char or " "
@ -70,20 +99,34 @@ function string:indent(num, char)
return char .. self:gsub("\n", "\n" .. char)
end
-- Splits the string
-- Note: only support splitting by one character.
-- Could be solved by slicing with find.
-- @param sep string: String separator, space by default
-- @return table: String matches
function string:split(sep)
sep = sep or "%s+"
local parts = {}
for part in self:gmatch("([^" .. sep .. "]+)") do
table.insert(parts, part)
end
return parts
end
-- Removes spaces at the beginning of the string
-- @return string: left stripped string
-- @return string: Left stripped string
function string:lstrip()
return self:gsub("^%s+", "")
end
-- Removes spaces at the end of the string
-- @return string: right stripped string
-- @return string: Right stripped string
function string:rstrip()
return self:gsub("%s+$", "")
end
-- Removes spaces at the beginning and at the end of the string
-- @return string: stripped string
-- @return string: Stripped string
function string:strip()
self = self:lstrip():rstrip()
return self
@ -123,14 +166,26 @@ function string:isdir()
return false
end
-- Reads file content
-- @return string or nil: File content or nil
function string:read_text()
-- Reads file content as string
-- @return string or nil: File as string or nil
function string:readtext()
if self:exists() then
return io.open(self):read("*a")
end
end
-- Read file content as lines
-- @return table: Table of file lines or nil
function string:readlines()
local lines = {}
if self:exists() then
for line in io.open(self):lines() do
table.insert(lines, line)
end
end
return lines
end
-- Gets file without suffix
-- @return string: File wihtout suffix
function string:stem()
@ -138,7 +193,7 @@ function string:stem()
end
-- Gets file extensions
-- @return table: list of file extensions
-- @return table: List of file extensions
function string:suffixes()
local suffixes = {}
for suffix in self:gmatch("%.%a+") do
@ -148,7 +203,7 @@ function string:suffixes()
end
-- Gets file final extension
-- @return string: final file extension
-- @return string: Final file extension
function string:suffix()
local suffixes = self:suffixes()
if suffixes[#suffixes] then
@ -184,7 +239,7 @@ if pandoc ~= nil then
-- @return string: Output file content
function pandoc.convert(ifile, oformat, ofile, iformat)
iformat = (iformat == nil and pandoc.getext(ifile) or iformat)
local doc = pandoc.write(pandoc.read(ifile:read_text(), iformat), oformat)
local doc = pandoc.write(pandoc.read(ifile:readtext(), iformat), oformat)
if ofile ~= nil then
local eol = (os:isunix() and "\n" or "\r\n")
io.open(ofile, "w"):write(doc, eol):close()

View File

@ -9,7 +9,7 @@ local function make_dist()
-- Chomps file
local function chomp(str, without)
local without = without or false
str = string.read_text(str):strip()
str = string.readtext(str):strip()
if without then
return str:gsub("\n[^\n]+$", "")
end
@ -24,7 +24,7 @@ local function make_dist()
local pan = chomp("src/pandoc.lua")
local nat = chomp("src/natural.lua", true)
local lit = chomp("src/literate.lua", true)
local msg = ("src/locale.yaml"):read_text()
local msg = ("src/locale.yaml"):readtext()
local license = string.strip([[
Computable Pandoc & Fennel Bundle:
A Pandoc filter for literate and natural programming

View File

@ -54,7 +54,7 @@ local function getresult(file, f, v, t)
local tmp, name = "tmp.json", pandoc.path.filename(file)
local json = pandoc.path.join({"tests", "asts", name .. ".json"})
local ok, out = io.try(getcmd("pandoc"), f, v, t, "-t json", "-o", tmp, file)
local json1, json2 = tmp:read_text(), json:read_text()
local json1, json2 = tmp:readtext(), json:readtext()
os.remove("tmp.json")
if (ok and json2 == nil) or (ok and json1 == json2) then
return "pass", out:strip()

View File

@ -29,27 +29,32 @@ lit.metastruct = {
}
-- Grammars
lit.g = {}
function lit.g(name)
-- Lexical elements
lit.newline = lpeg.P"\r"^-1 * lpeg.P"\n"
lit.space = lpeg.S" \t"
lit.anyspace = lpeg.S" \t\r\n"
lit.spot = (1 - lit.anyspace)
lit.any = (lit.spot + lit.space)
lit.yamlheader = lit.space^0 * lpeg.P"---" * lit.space^0 * lit.newline
lit.yamlfooter = lit.space^0 * lpeg.P"..." * lit.space^0 * lit.newline^-1
lit.yamlbody = -lit.yamlfooter * lit.any^0 * lit.newline
lit.id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
lit.ref = lpeg.P"#" * lit.id
-- Lexical elements
local newline = lpeg.P"\r"^-1 * lpeg.P"\n"
local space = lpeg.S" \t"
local anyspace = lpeg.S" \t\r\n"
local spot = (1 - anyspace)
local any = (spot + space)
local yamlheader = space^0 * lpeg.P"---" * space^0 * newline
local yamlfooter = space^0 * lpeg.P"..." * space^0 * newline^-1
local yamlbody = -yamlfooter * any^0 * newline
local id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
local ref = lpeg.P"#" * id
-- Blocks grammar
lit.g.block = lpeg.P {
"Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(lit.yamlheader * lit.yamlbody^0 * lit.yamlfooter);
Code = lpeg.C((lit.any + lit.newline)^0);
}
local grammars = {
-- Blocks grammar
["block"] = lpeg.P {
"Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(yamlheader * yamlbody^0 * yamlfooter);
Code = lpeg.C((any + newline)^0);
},
}
return grammars[name]
end
-- Evals Lisp code
--[[
@ -68,165 +73,98 @@ function lit.eval(code)
end
]]--
function lit.stringify_quoted(quoted)
local quote = (quoted.quotetype == SingleQuoted and '"' or "'")
quoted = pandoc.utils.stringify(quoted.content)
return pandoc.Str(quote .. quoted .. quote)
end
function lit.stringify_rawinline(rawinline)
local str = rawinline.text
str = str:gsub("\\n", "\n"):gsub("\\t", "\t")
return pandoc.Str(str)
end
function lit.stringify(content)
if pandoc.utils.type(content) == "Inlines" then
return pandoc.utils.stringify(content:walk {
Quoted = function(e) return lit.stringify_quoted(e) end,
RawInline = function(e) return lit.stringify_rawinline(e) end,
Inline = function(inline) return pandoc.utils.stringify(inline) end,
})
else
return pandoc.utils.stringify(content)
end
end
function lit.string2other(str)
if str == "true" or str == "false" then
return str == "true"
-- WARN "1" always num; cfr. https://pandoc.org/lua-filters.html#type-metavalue
elseif tonumber(str) then
return tonumber(str)
else
return str
end
end
function lit.meta2table(meta)
local function stringify(content)
local function stringify_quoted(quoted)
local quote = (quoted.quotetype == SingleQuoted and '"' or "'")
quoted = pandoc.utils.stringify(quoted.content)
return pandoc.Str(quote .. quoted .. quote)
end
local function stringify_rawinline(rawinline)
local str = rawinline.text
str = str:gsub("\\n", "\n"):gsub("\\t", "\t")
return pandoc.Str(str)
end
if pandoc.utils.type(content) == "Inlines" then
return pandoc.utils.stringify(content:walk {
Quoted = function(e) return stringify_quoted(e) end,
RawInline = function(e) return stringify_rawinline(e) end,
Inline = function(inline) return pandoc.utils.stringify(inline) end,
})
else
return pandoc.utils.stringify(content)
end
end
local function string2other(str)
if str == "true" or str == "false" then
return str == "true"
-- Note that "1" always num;
-- cfr. https://pandoc.org/lua-filters.html#type-metavalue
elseif tonumber(str) then
return tonumber(str)
else
return str
end
end
for k, v in pairs(meta) do
if pandoc.utils.type(v) == "table" then
meta[k] = lit.meta2table(v)
else
meta[k] = lit.string2other(lit.stringify(v))
meta[k] = string2other(stringify(v))
end
end
return meta
end
function lit.debuglevel(str)
if str == "ERROR" then
lit.status = false
return 2
elseif str == "WARNING" then
return 1
else
return 0
end
end
function lit.setmsg(msg, ...)
local lang = os.lang()
msg = (msg[lang] ~= nil and msg[lang] or msg)
for i, str in ipairs({...}) do
msg = msg:gsub("#" .. i, str)
end
return msg
end
function lit.getmsg(key, ...)
local msg = lit.meta2table(pandoc.read([[#locale()]]).meta)
local levelname, level = "INFO", 0
for mtype, msgs in pairs(msg) do
if msgs[key] ~= nil then
key = lit.setmsg(msgs[key], ...)
levelname, level = mtype, lit.debuglevel(mtype)
break
end
end
return key, levelname, level
end
function lit.puts(key, ...)
local verbosity = lit.debuglevel(PANDOC_STATE.verbosity)
local msg, levelname, level = lit.getmsg(key, ...)
local function debuglevel(str)
if str == "ERROR" then
lit.status = false
return 2
elseif str == "WARNING" then
return 1
else
return 0
end
end
local function getmsg(key, ...)
local function setmsg(msg, ...)
local lang = os.lang()
msg = (msg[lang] ~= nil and msg[lang] or msg)
for i, str in ipairs({...}) do
msg = msg:gsub("#" .. i, str)
end
return msg
end
local msg = lit.meta2table(pandoc.read([[#locale()]]).meta)
local levelname, level = "INFO", 0
for mtype, msgs in pairs(msg) do
if msgs[key] ~= nil then
key = setmsg(msgs[key], ...)
levelname, level = mtype, debuglevel(mtype)
break
end
end
return key, levelname, level
end
local verbosity = debuglevel(PANDOC_STATE.verbosity)
local msg, levelname, level = getmsg(key, ...)
if level >= verbosity then
print("[" .. levelname .. "] [LIT] " .. msg)
end
end
function lit.getmetaval(meta, key)
local mtype = pandoc.utils.type(meta[key])
local mval = meta[key]
if mtype == "Inlines" then
mval = pandoc.utils.stringify(mval)
mtype = pandoc.utils.type(mval)
end
return mval, mtype
end
function lit.checkextra(meta)
for key, val in pairs(meta) do
local missing1 = lit.metastruct["mandatory"][key] == nil
local missing2 = lit.metastruct["optional"][key] == nil
local mval = lit.getmetaval(meta, key)
if missing1 and missing2 then
lit.puts("invalid_key", key)
end
end
end
function lit.checkmeta_type(type, meta, key)
local mval, mtype = lit.getmetaval(meta, key)
if type ~= mtype then
if type == "path" then
if not(pandoc.path.directory(mval):isdir()) then
lit.puts("invalid_path", mval, key)
end
else
lit.puts("invalid_type", mtype, key)
end
end
end
function lit.checkmeta_match(table, mval, key)
for _, pattern in pairs(table) do
if mval:match("^" .. pattern .. "$") then
return ""
end
end
return key
end
function lit.checkmeta_table(table, meta, key)
local mval = lit.getmetaval(meta, key)
local err = lit.checkmeta_match(table, mval, key)
if not(err:isempty()) then
lit.puts("invalid_value", mval, err)
end
end
function lit.checkmeta(meta, kind)
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil and kind == "mandatory" then
lit.puts("no_key", key)
elseif meta[key] and type(val) == "table" then
lit.checkmeta_table(val, meta, key)
elseif meta[key] then
lit.checkmeta_type(val, meta, key)
end
end
end
function lit.setmeta(meta)
lit.checkmeta(meta, "mandatory")
lit.checkmeta(meta, "optional")
lit.checkextra(meta)
-- TODO checks for 2) duplicates
-- Set defaults if lit.status = true
return meta
end
-- TODO
function lit.assert(e, status)
if status == false then
@ -236,32 +174,98 @@ function lit.assert(e, status)
return e
end
function lit.parseyaml(rawyaml)
-- TODO io.try
local yaml = io.popen("pandoc -t json <<<\"" .. rawyaml .. "\" 2>&1")
if yaml:read("*l"):sub(1, 1) == "{" then
yaml = pandoc.read(rawyaml).meta
if pandoc.utils.stringify(yaml):isempty() then
lit.puts("yaml_empty")
else
return lit.setmeta(yaml)
end
else
lit.puts("yaml_invalid")
return nil
end
end
function lit.parseblock(parsed)
lit.puts("parsing", table.concat(parsed, ""):indent())
local yaml = lit.parseyaml(parsed[1])
-- TODO: parsecode
return "TODO"
end
function lit.getblock(codeblock)
local parsed = lpeg.match(lit.g.block, codeblock.text)
parsed = (parsed ~= nil and lit.parseblock(parsed) or parsed)
local function checkextra(meta)
for key, val in pairs(meta) do
local missing1 = lit.metastruct["mandatory"][key] == nil
local missing2 = lit.metastruct["optional"][key] == nil
if missing1 and missing2 then
lit.puts("invalid_key", key)
end
end
end
local function checkmeta(meta, kind)
local function checktype(type1, meta, key)
local val = meta[key]
local type2 = type(val)
if type1 ~= type2 then
if type1 == "path" then
if not(pandoc.path.directory(val):isdir()) then
lit.puts("invalid_path", val, key)
end
else
lit.puts("invalid_type", type2, key)
end
end
end
local function checktable(table, meta, key)
local function checkmatch(table, val, key)
for _, pattern in pairs(table) do
if val:match("^" .. pattern .. "$") then
return ""
end
end
return key
end
local val = meta[key]
local err = checkmatch(table, val, key)
if not(err:isempty()) then
lit.puts("invalid_value", val, err)
end
end
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil and kind == "mandatory" then
lit.puts("no_key", key)
elseif meta[key] and type(val) == "table" then
checktable(val, meta, key)
elseif meta[key] then
checktype(val, meta, key)
end
end
end
local function setmeta(meta)
local tmeta = lit.meta2table(meta)
checkmeta(tmeta, "mandatory")
checkmeta(tmeta, "optional")
checkextra(tmeta)
-- TODO checks for 2) duplicates
-- Set defaults if lit.status = true
return meta
end
local function parseyaml(rawyaml)
-- TODO io.try
local yaml = io.popen("pandoc -t json <<<\"" .. rawyaml .. "\" 2>&1")
if yaml:read("*l"):sub(1, 1) == "{" then
yaml = pandoc.read(rawyaml).meta
if pandoc.utils.stringify(yaml):isempty() then
lit.puts("yaml_empty")
else
return setmeta(yaml)
end
else
lit.puts("yaml_invalid")
return nil
end
end
local function inspect(parsed)
lit.puts("parsing", table.concat(parsed, ""):indent())
local yaml = parseyaml(parsed[1])
-- TODO: parsecode
return "TODO"
end
local parsed = lpeg.match(lit.g("block"), codeblock.text)
parsed = (parsed ~= nil and inspect(parsed) or parsed)
return codeblock, lit.status
end