More error analysis; refactoring of lit.puts

This commit is contained in:
perro tuerto 2023-03-24 12:08:26 -07:00
parent e353f5125f
commit 781a7920f6
4 changed files with 309 additions and 97 deletions

View File

@ -6,7 +6,7 @@ process of creating works of literature][1]".
## Requirements
- [Pandoc]
- [Pandoc] v3
## Install

202
dist/lin.lua vendored
View File

@ -6302,10 +6302,14 @@ function os.isunix()
return os.uname() ~= "windows"
end
-- Changes newlines so everything is in one line
-- @return string: String with formatted newlines as literal "\n"
function string:linearize()
return self:gsub("\n", "\\\\n")
-- Gets OS language
-- @return string, string, string: Language, locale and encoding
function os.lang()
local lang = os.getenv("LANG")
if lang ~= nil then
return lang:match("(%w%w)_?(%w?%w?)%.?(.*)")
end
return "en", "US", "UTF-8"
end
-- Checks if string is empty
@ -6314,22 +6318,47 @@ function string:isempty()
return self == ''
end
-- Changes newlines so everything is in one line
-- @return string: String with formatted newlines as literal "\n"
function string:linearize()
return self:gsub("\n", "\\n")
end
function string:indent(num, char)
num = num or 2
char = char or " "
char = string.rep(char, num)
return char .. self:gsub("\n", "\n" .. char)
end
-- Removes spaces at the beginning of the 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
function string:rstrip()
return self:gsub("%s+$", "")
end
-- Removes spaces at the beginning and at the end of the string
-- @return string: stripped string
function string:strip()
return self:lstrip():rstrip()
self = self:lstrip():rstrip()
return self
end
-- Alias of strip
function string:trim()
return self:strip()
end
-- The following are heavily influenced by Python pathlib
-- Check: https://docs.python.org/3/library/pathlib.html
-- Checks if string is a file or directory
-- Checks if string is a file or a directory
-- @return boolean: Exists or not
function string:exists()
return os.rename(self, self) ~= nil
@ -6363,6 +6392,8 @@ function string:read_text()
end
end
-- Gets file without suffix
-- @return string: File wihtout suffix
function string:stem()
return self:gsub("%.%a+$", "")
end
@ -6394,6 +6425,8 @@ if pandoc ~= nil then
-- Gets file extension namespace
-- Check: https://pandoc.org/MANUAL.html#general-options
-- If suffix is 'md' or empty, the default is markdown.
-- For the rest, the format is the suffix.
-- @param file string: File name
-- @return string: File extension according to Pandoc format namespaces
function pandoc.getext(file)
@ -6403,22 +6436,21 @@ if pandoc ~= nil then
-- Pandoc converter
-- Converts input file name into output format name.
-- If ofile is not nil, writes output file name instead of returning content.
-- If ofile is not nil, writes output file name.
-- If iformat is nil, defaults to ifile extension name.
-- @param ifile string: Input file name
-- @param oformat string: Output format name
-- @param ofile string or nil: Output file name
-- @param iformat string or nil: Input format name
-- @return string or nil: Output file content
-- @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)
if ofile == nil then
return doc
else
if ofile ~= nil then
local eol = (os:isunix() and "\n" or "\r\n")
io.open(ofile, "w"):write(doc, eol):close()
end
return doc
end
end
@ -6459,6 +6491,94 @@ lit.metastruct = {
},
}
-- Messages for the user
lit.msg = {
["INFO"] = {
["parsing"] = {
["en"] = "Parsing:\n#1",
["es"] = "Realizando análisis sintáctico:\n#1",
},
},
["WARNING"] = {
},
["ERROR"] = {
["invalid_key"] = {
["en"] = "Invalid key '#1' with value '#2'",
["es"] = "Clave '#1' inválida con valor '#2'",
},
["invalid_path"] = {
["en"] = "Invalid path '#1' in key '#2'",
["es"] = "Ruta '#1' inválida en clave '#2'",
},
["invalid_type"] = {
["en"] = "Invalid type '#1' in key '#2'",
["es"] = "Tipo '#1' inválido en clave '#2'",
},
["invalid_value"] = {
["en"] = "Invalid value '#1' in key '#2'",
["es"] = "Valor '#1' inválido en clave '#2'",
},
["no_key"] = {
["en"] = "Key '#1' not found",
["es"] = "Clave '#1' no encontrada",
},
["aborted"] = {
["en"] = "Aborted due previous errors",
["es"] = "Abortado debido a previos errores",
},
["yaml_empty"] = {
["en"] = "Empty YAML",
["es"] = "YAML vacío",
},
["yaml_invalid"] = {
["en"] = "Invalid YAML",
["es"] = "YAML inválido",
},
},
}
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 levelname = "INFO"
local level = 0
for mtype, msgs in pairs(lit.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, ...)
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]
@ -6469,40 +6589,44 @@ function lit.getmetaval(meta, key)
return mval, mtype
end
function lit.checkmetaextra(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, mval)
end
end
end
function lit.checkmetatype(type, meta, key)
local mval, mtype = lit.getmetaval(meta, key)
local err = ""
if type ~= mtype then
if type == "path" then
if not(pandoc.path.directory(mval):isdir()) then
err = "Invalid path '" .. mval .. "' in key '" .. key .. "'"
lit.puts("invalid_path", mval, key)
end
else
err = "Invalid type '" .. mtype .. "' in key '" .. key .. "'"
lit.puts("invalid_type", mtype, key)
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmetatable(table, meta, key)
local mval = lit.getmetaval(meta, key)
local err = ""
for _, pattern in pairs(table) do
if mval:match("^" .. pattern .. "$") then
err = ""
break
else
err = "Invalid value '" .. mval .. "' in key '" .. key .. "'"
if mval:match("^" .. pattern .. "$") == nil then
lit.puts("invalid_value", mval, key)
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmeta(meta, kind)
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil then
if kind == "mandatory" then
lit.puts("Key '" .. key .. "' not found", "ERROR")
lit.puts("no_key", key)
end
else
if type(val) == "table" then
@ -6517,7 +6641,8 @@ end
function lit.setmeta(meta)
lit.checkmeta(meta, "mandatory")
lit.checkmeta(meta, "optional")
-- TODO checks for 1) extra keys and 2) duplicates
lit.checkmetaextra(meta)
-- TODO checks for 2) duplicates
-- Set defaults if lit.status = true
return meta
end
@ -6562,51 +6687,32 @@ function lit.eval(code)
end
]]--
-- TODO
function lit.assert(e, status)
if status == false then
lit.puts("Aborted due previous errors", "ERROR")
lit.puts("aborted")
os.exit(1)
end
return e
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(msg, kind)
local kind = kind or "INFO"
local verbosity = lit.debuglevel(PANDOC_STATE.verbosity)
level = lit.debuglevel(kind)
if level >= verbosity then
print("[" .. kind .. "] [LIT] " .. msg)
end
end
function lit.parseyaml(rawyaml)
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("Empty YAML", "ERROR")
lit.puts("yaml_empty")
else
return lit.setmeta(yaml)
end
else
lit.puts("Invalid YAML", "ERROR")
lit.puts("yaml_invalid")
return nil
end
end
function lit.parseblock(parsed)
lit.puts("Parsing " .. table.concat(parsed, "\n"):linearize())
lit.puts("parsing", table.concat(parsed, ""):indent())
local yaml = lit.parseyaml(parsed[1])
-- TODO: parsecode
return "TODO"

View File

@ -41,10 +41,14 @@ function os.isunix()
return os.uname() ~= "windows"
end
-- Changes newlines so everything is in one line
-- @return string: String with formatted newlines as literal "\n"
function string:linearize()
return self:gsub("\n", "\\\\n")
-- Gets OS language
-- @return string, string, string: Language, locale and encoding
function os.lang()
local lang = os.getenv("LANG")
if lang ~= nil then
return lang:match("(%w%w)_?(%w?%w?)%.?(.*)")
end
return "en", "US", "UTF-8"
end
-- Checks if string is empty
@ -53,22 +57,47 @@ function string:isempty()
return self == ''
end
-- Changes newlines so everything is in one line
-- @return string: String with formatted newlines as literal "\n"
function string:linearize()
return self:gsub("\n", "\\n")
end
function string:indent(num, char)
num = num or 2
char = char or " "
char = string.rep(char, num)
return char .. self:gsub("\n", "\n" .. char)
end
-- Removes spaces at the beginning of the 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
function string:rstrip()
return self:gsub("%s+$", "")
end
-- Removes spaces at the beginning and at the end of the string
-- @return string: stripped string
function string:strip()
return self:lstrip():rstrip()
self = self:lstrip():rstrip()
return self
end
-- Alias of strip
function string:trim()
return self:strip()
end
-- The following are heavily influenced by Python pathlib
-- Check: https://docs.python.org/3/library/pathlib.html
-- Checks if string is a file or directory
-- Checks if string is a file or a directory
-- @return boolean: Exists or not
function string:exists()
return os.rename(self, self) ~= nil
@ -102,6 +131,8 @@ function string:read_text()
end
end
-- Gets file without suffix
-- @return string: File wihtout suffix
function string:stem()
return self:gsub("%.%a+$", "")
end
@ -133,6 +164,8 @@ if pandoc ~= nil then
-- Gets file extension namespace
-- Check: https://pandoc.org/MANUAL.html#general-options
-- If suffix is 'md' or empty, the default is markdown.
-- For the rest, the format is the suffix.
-- @param file string: File name
-- @return string: File extension according to Pandoc format namespaces
function pandoc.getext(file)
@ -142,22 +175,21 @@ if pandoc ~= nil then
-- Pandoc converter
-- Converts input file name into output format name.
-- If ofile is not nil, writes output file name instead of returning content.
-- If ofile is not nil, writes output file name.
-- If iformat is nil, defaults to ifile extension name.
-- @param ifile string: Input file name
-- @param oformat string: Output format name
-- @param ofile string or nil: Output file name
-- @param iformat string or nil: Input format name
-- @return string or nil: Output file content
-- @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)
if ofile == nil then
return doc
else
if ofile ~= nil then
local eol = (os:isunix() and "\n" or "\r\n")
io.open(ofile, "w"):write(doc, eol):close()
end
return doc
end
end

View File

@ -28,6 +28,94 @@ lit.metastruct = {
},
}
-- Messages for the user
lit.msg = {
["INFO"] = {
["parsing"] = {
["en"] = "Parsing:\n#1",
["es"] = "Realizando análisis sintáctico:\n#1",
},
},
["WARNING"] = {
},
["ERROR"] = {
["invalid_key"] = {
["en"] = "Invalid key '#1' with value '#2'",
["es"] = "Clave '#1' inválida con valor '#2'",
},
["invalid_path"] = {
["en"] = "Invalid path '#1' in key '#2'",
["es"] = "Ruta '#1' inválida en clave '#2'",
},
["invalid_type"] = {
["en"] = "Invalid type '#1' in key '#2'",
["es"] = "Tipo '#1' inválido en clave '#2'",
},
["invalid_value"] = {
["en"] = "Invalid value '#1' in key '#2'",
["es"] = "Valor '#1' inválido en clave '#2'",
},
["no_key"] = {
["en"] = "Key '#1' not found",
["es"] = "Clave '#1' no encontrada",
},
["aborted"] = {
["en"] = "Aborted due previous errors",
["es"] = "Abortado debido a previos errores",
},
["yaml_empty"] = {
["en"] = "Empty YAML",
["es"] = "YAML vacío",
},
["yaml_invalid"] = {
["en"] = "Invalid YAML",
["es"] = "YAML inválido",
},
},
}
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 levelname = "INFO"
local level = 0
for mtype, msgs in pairs(lit.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, ...)
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]
@ -38,40 +126,44 @@ function lit.getmetaval(meta, key)
return mval, mtype
end
function lit.checkmetaextra(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, mval)
end
end
end
function lit.checkmetatype(type, meta, key)
local mval, mtype = lit.getmetaval(meta, key)
local err = ""
if type ~= mtype then
if type == "path" then
if not(pandoc.path.directory(mval):isdir()) then
err = "Invalid path '" .. mval .. "' in key '" .. key .. "'"
lit.puts("invalid_path", mval, key)
end
else
err = "Invalid type '" .. mtype .. "' in key '" .. key .. "'"
lit.puts("invalid_type", mtype, key)
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmetatable(table, meta, key)
local mval = lit.getmetaval(meta, key)
local err = ""
for _, pattern in pairs(table) do
if mval:match("^" .. pattern .. "$") then
err = ""
break
else
err = "Invalid value '" .. mval .. "' in key '" .. key .. "'"
if mval:match("^" .. pattern .. "$") == nil then
lit.puts("invalid_value", mval, key)
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmeta(meta, kind)
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil then
if kind == "mandatory" then
lit.puts("Key '" .. key .. "' not found", "ERROR")
lit.puts("no_key", key)
end
else
if type(val) == "table" then
@ -86,7 +178,8 @@ end
function lit.setmeta(meta)
lit.checkmeta(meta, "mandatory")
lit.checkmeta(meta, "optional")
-- TODO checks for 1) extra keys and 2) duplicates
lit.checkmetaextra(meta)
-- TODO checks for 2) duplicates
-- Set defaults if lit.status = true
return meta
end
@ -131,51 +224,32 @@ function lit.eval(code)
end
]]--
-- TODO
function lit.assert(e, status)
if status == false then
lit.puts("Aborted due previous errors", "ERROR")
lit.puts("aborted")
os.exit(1)
end
return e
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(msg, kind)
local kind = kind or "INFO"
local verbosity = lit.debuglevel(PANDOC_STATE.verbosity)
level = lit.debuglevel(kind)
if level >= verbosity then
print("[" .. kind .. "] [LIT] " .. msg)
end
end
function lit.parseyaml(rawyaml)
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("Empty YAML", "ERROR")
lit.puts("yaml_empty")
else
return lit.setmeta(yaml)
end
else
lit.puts("Invalid YAML", "ERROR")
lit.puts("yaml_invalid")
return nil
end
end
function lit.parseblock(parsed)
lit.puts("Parsing " .. table.concat(parsed, "\n"):linearize())
lit.puts("parsing", table.concat(parsed, ""):indent())
local yaml = lit.parseyaml(parsed[1])
-- TODO: parsecode
return "TODO"