Compare commits
No commits in common. "no-masters" and "0.1.0" have entirely different histories.
no-masters
...
0.1.0
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "opt/luaminify"]
|
||||||
|
path = opt/luaminify
|
||||||
|
url = https://github.com/stravant/LuaMinify.git
|
86
README.md
86
README.md
|
@ -1,98 +1,72 @@
|
||||||
# 👾 Computable Pandoc
|
# Literate Pandoc
|
||||||
|
|
||||||
Computable Pandoc is a [Pandoc filter] written in [Lua] for [literate] and
|
[Pandoc] is a world famous "swiss-army" document converted. This is because
|
||||||
[natural] programming (LIN programming), i.e.: "[Programming \[...\] as the
|
Pandoc is also a document parser. Thanks to this capability, this repo is a
|
||||||
process of creating works of literature][1]".
|
[Pandoc filter] written in [Lua] for [literate] and [natural] programming
|
||||||
|
(LNP), i.e.: ["Programming \[...\] as the process of creating works of
|
||||||
|
literature"][1].
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- [Pandoc] v3
|
- [Pandoc]
|
||||||
|
|
||||||
if you decide to use `lin.min.lua` instead of `lin.bundle.lua`, you need to
|
|
||||||
install and preload the following rocks:
|
|
||||||
|
|
||||||
- [fennel]
|
|
||||||
- [lua-dog]
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
1. Go to [Releases].
|
Just download this repo:
|
||||||
2. Download the latest version of `lin.bundle.lua` or `lin.min.lua`.
|
|
||||||
3. Done!
|
git clone https://git.cuates.net/perro/literate-pandoc.git
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
With `lin.bundle.lua` or `lin.min.lua` downloaded and Pandoc installed, do:
|
With Pandoc installed and this repo downloaded, do:
|
||||||
|
|
||||||
pandoc -L PATH/TO/lin.bundle.lua -t FORMAT DOC
|
pandoc --lua-filter PATH/TO/literate-pandoc/src/literate.lua -t FORMAT DOC
|
||||||
|
|
||||||
For example, if `DOC` is `source.md` and the output `FORMAT` is HTML, do:
|
For example, if `DOC` is `source.md` and the output `FORMAT` is HTML, do:
|
||||||
|
|
||||||
pandoc -L PATH/TO/lin.bundle.lua -t html source.md
|
pandoc --lua-filter PATH/TO/literate-pandoc/src/literate.lua -t html source.md
|
||||||
|
|
||||||
## Manual
|
## Manual
|
||||||
|
|
||||||
Learn how to do LIN programming [here].
|
Learn how to do LNP [here].
|
||||||
|
|
||||||
## Develop
|
## Test
|
||||||
|
|
||||||
Clone this repo:
|
Inside this repo, do:
|
||||||
|
|
||||||
git clone https://git.cuates.net/perro/computable-pandoc.git
|
sh scripts/test.sh FORMAT1 FORMAT2
|
||||||
|
|
||||||
Enter the repo:
|
For example, if `FORMAT1` is Markdwon and `FORMAT2` is HTML, do:
|
||||||
|
|
||||||
cd computable-pandoc
|
sh tests/test.sh markdown html
|
||||||
|
|
||||||
Inside, do the tests:
|
For distribution tests, do:
|
||||||
|
|
||||||
pandoc lua scripts/test.lua
|
sh scripts/test.sh --dist FORMAT1 FORMAT2
|
||||||
|
|
||||||
For other kind of tests, do:
|
|
||||||
|
|
||||||
pandoc lua scripts/test.lua -h
|
|
||||||
|
|
||||||
For make distribution filter, do:
|
|
||||||
|
|
||||||
pandoc lua scripts/make_dist.lua
|
|
||||||
|
|
||||||
For make JSON (intented for testing asserts), do:
|
|
||||||
|
|
||||||
pandoc lua scripts/make_jsons.lua
|
|
||||||
|
|
||||||
For update Lua modules (needs [`luarocks`]), do:
|
|
||||||
|
|
||||||
sh scripts/get_rocks.sh
|
|
||||||
|
|
||||||
Contribute!
|
|
||||||
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
This wouldn't be possible without these projects and their collaborators:
|
This wouldn't be possible without these projects and their collaborators:
|
||||||
|
|
||||||
- [Pandoc][]: universal document converter and parser; handles the
|
- [Pandoc][]: universal document converter and parser; handles the
|
||||||
requirements for LIN.
|
requirements for LNP.
|
||||||
- [Lua][]: programming language; enables LIN.
|
- [Lua][]: programming language; enables LNP.
|
||||||
- [Fennel][2]: [Lisp] dialect with full Lua compatibility; allows native
|
- [Fennel][]: [Lisp] dialect with full Lua compatibility; allows to go from
|
||||||
evals for Lisp.
|
LNP to Lisp or Lua.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Computable Pandoc is under [GPLv3].
|
Literate Pandoc is under [GPLv3].
|
||||||
|
|
||||||
Happy hacking :)
|
Happy hacking :)
|
||||||
|
|
||||||
|
[Pandoc]: https://pandoc.org/
|
||||||
[Pandoc filter]: https://pandoc.org/lua-filters.html
|
[Pandoc filter]: https://pandoc.org/lua-filters.html
|
||||||
[Lua]: https://www.lua.org/
|
[Lua]: https://www.lua.org/
|
||||||
[literate]: https://en.wikipedia.org/wiki/Literate_programming
|
[literate]: https://en.wikipedia.org/wiki/Literate_programming
|
||||||
[natural]: https://en.wikipedia.org/wiki/Natural-language_programming
|
[natural]: https://en.wikipedia.org/wiki/Natural-language_programming
|
||||||
[1]: https://web.archive.org/web/20170605163729/http://www.desy.de/user/projects/LitProg/Philosophy.html
|
[1]: https://web.archive.org/web/20170605163729/http://www.desy.de/user/projects/LitProg/Philosophy.html
|
||||||
[Pandoc]: https://pandoc.org/
|
[here]: https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/man/README.md
|
||||||
[fennel]: https://luarocks.org/modules/technomancy/fennel
|
[Fennel]: https://fennel-lang.org
|
||||||
[lua-dog]: https://luarocks.org/modules/perro/lua-dog
|
|
||||||
[Releases]: https://git.cuates.net/perro/computable-pandoc/releases
|
|
||||||
[here]: https://git.cuates.net/perro/computable-pandoc/src/branch/no-masters/man/README.md
|
|
||||||
[`luarocks`]: https://luarocks.org/
|
|
||||||
[2]: https://fennel-lang.org
|
|
||||||
[Lisp]: https://en.wikipedia.org/wiki/Lisp_(programming_language)
|
[Lisp]: https://en.wikipedia.org/wiki/Lisp_(programming_language)
|
||||||
[GPLv3]: https://git.cuates.net/perro/computable-pandoc/src/branch/no-masters/LICENSE.txt
|
[GPLv3]: https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/LICENSE.txt
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,401 +0,0 @@
|
||||||
--[[
|
|
||||||
Computable Pandoc:
|
|
||||||
(C) 2023 perro hi@perrotuerto.blog
|
|
||||||
License: GPLv3 https://git.cuates.net/perro/computable-pandoc/src/branch/no-masters/LICENSE.txt
|
|
||||||
Source: https://git.cuates.net/perro/computable-pandoc
|
|
||||||
]]--
|
|
||||||
|
|
||||||
require "fennel"
|
|
||||||
require "nat"
|
|
||||||
|
|
||||||
local lua_dog = require("dog")
|
|
||||||
|
|
||||||
dog.import()
|
|
||||||
|
|
||||||
---------------------------------- LITERATE ----------------------------------
|
|
||||||
|
|
||||||
-- Stores all literate stuff
|
|
||||||
local lit = {}
|
|
||||||
|
|
||||||
-- Indicates status
|
|
||||||
lit.status = true
|
|
||||||
|
|
||||||
-- Stores literate functions
|
|
||||||
lit.fns = {}
|
|
||||||
|
|
||||||
-- Returns specific grammar
|
|
||||||
function lit.g(name)
|
|
||||||
|
|
||||||
-- Stores all grammars
|
|
||||||
local grammars = {}
|
|
||||||
|
|
||||||
-- Shortcuts
|
|
||||||
local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt =
|
|
||||||
lpeg.P, lpeg.S, lpeg.R, lpeg.Cf, lpeg.Cc, lpeg.Ct, lpeg.V,
|
|
||||||
lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.B, lpeg.C, lpeg.Cmt
|
|
||||||
|
|
||||||
-- Lexical elements
|
|
||||||
local newline = P"\r"^-1 * P"\n"
|
|
||||||
local space = S" \t"
|
|
||||||
local anyspace = S" \t\r\n"
|
|
||||||
local spot = (1 - anyspace)
|
|
||||||
local any = (spot + space)
|
|
||||||
local yamlheader = space^0 * P"---" * space^0 * newline
|
|
||||||
local yamlfooter = space^0 * P"..." * space^0 * newline^-1
|
|
||||||
local yamlbody = -yamlfooter * any^0 * newline
|
|
||||||
local label = R("az", "AZ") * R("az", "AZ", "09", "__")^0
|
|
||||||
local ref = -B"\\" * P"#" * C(label)
|
|
||||||
local notref = (1 - ref)^0
|
|
||||||
|
|
||||||
-- Function declaration grammar
|
|
||||||
grammars["declaration"] = P {
|
|
||||||
"Declaration";
|
|
||||||
Declaration = Ct(V"YAML" * V"Code", "name");
|
|
||||||
YAML = C(yamlheader * yamlbody^0 * yamlfooter);
|
|
||||||
Code = C((any + newline)^0);
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Argument references grammar
|
|
||||||
grammars["references"] = notref * P {
|
|
||||||
"References";
|
|
||||||
References = Ct((ref * notref)^0);
|
|
||||||
} * -1
|
|
||||||
|
|
||||||
return grammars[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Prints located messages
|
|
||||||
function lit.puts(yaml_key, ...)
|
|
||||||
|
|
||||||
-- Returns debug level as number
|
|
||||||
local function debuglevel(str)
|
|
||||||
if str == "ERROR" then
|
|
||||||
lit.status = false
|
|
||||||
return 2
|
|
||||||
elseif str == "WARNING" then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets located message
|
|
||||||
local function getmsg(key, ...)
|
|
||||||
local msg = pandoc.metatotable(pandoc.read([[---
|
|
||||||
INFO:
|
|
||||||
checking:
|
|
||||||
en: "Checking:\n#1"
|
|
||||||
es: "Comprobando:\n#1"
|
|
||||||
skip_check:
|
|
||||||
en: "Skipping '#1': this check is not supported"
|
|
||||||
es: "Skipping '#1': esta comprobación no está soportada"
|
|
||||||
WARNING:
|
|
||||||
ignore_lang:
|
|
||||||
en: "Keys 'cmd' and 'lang' present: '#1' overrides '#2'"
|
|
||||||
es: "Llaves 'cmd' y 'lang' presentes: '#1' sobrescribe '#2'"
|
|
||||||
ERROR:
|
|
||||||
invalid_key:
|
|
||||||
en: Invalid key '#1'
|
|
||||||
es: Clave '#1' inválida
|
|
||||||
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'
|
|
||||||
invalid_cmd:
|
|
||||||
en: Invalid cmd '#1'
|
|
||||||
es: Comando '#1' inválido
|
|
||||||
no_key:
|
|
||||||
en: Key '#1' not found
|
|
||||||
es: Clave '#1' no encontrada
|
|
||||||
aborted:
|
|
||||||
en: Aborted due previous errors
|
|
||||||
es: Abortado debido a previos errores
|
|
||||||
meta_empty:
|
|
||||||
en: Empty metadata
|
|
||||||
es: Metadatos vacíos
|
|
||||||
meta_invalid:
|
|
||||||
en: Invalid metadata
|
|
||||||
es: Metadatos inválidos
|
|
||||||
...
|
|
||||||
]]).meta)
|
|
||||||
local levelname, level, lang = "INFO", 0, os.lang()
|
|
||||||
for mtype, msgs in pairs(msg) do
|
|
||||||
if msgs[key] then
|
|
||||||
key = (msgs[key][lang] ~= nil and msgs[key][lang] or msgs[key]["en"])
|
|
||||||
for i, str in ipairs({...}) do
|
|
||||||
key = key:gsub("#" .. i, str)
|
|
||||||
end
|
|
||||||
levelname, level = mtype, debuglevel(mtype)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return key, levelname, level
|
|
||||||
end
|
|
||||||
|
|
||||||
local verbosity = debuglevel(PANDOC_STATE.verbosity)
|
|
||||||
local msg, levelname, level = getmsg(yaml_key, ...)
|
|
||||||
if level >= verbosity then
|
|
||||||
print("[" .. levelname .. "] [LIT] " .. msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses and evaluates literate functions in document
|
|
||||||
function lit.exam(doc)
|
|
||||||
|
|
||||||
-- Extracts function declarations
|
|
||||||
local function extract(codeblock)
|
|
||||||
|
|
||||||
-- Checks function declarations
|
|
||||||
local function check(match)
|
|
||||||
|
|
||||||
-- Stores valid declaration
|
|
||||||
local declaration = {}
|
|
||||||
|
|
||||||
-- Valid metadata structure
|
|
||||||
local metastruct = {
|
|
||||||
["mandatory"] = {
|
|
||||||
-- Value as table == whole patterns to match
|
|
||||||
["id"] = {"%a[_%w]*"},
|
|
||||||
},
|
|
||||||
["optional"] = {
|
|
||||||
["lang"] = {
|
|
||||||
"lua", "fennel", "python", "js", "ruby", "lisp", "graphviz",
|
|
||||||
},
|
|
||||||
["cmd"] = {".+"},
|
|
||||||
-- Value as string == type to match
|
|
||||||
["args"] = "table",
|
|
||||||
["shift"] = "boolean",
|
|
||||||
["wipe"] = "boolean",
|
|
||||||
["typed"] = "boolean",
|
|
||||||
["link"] = "path",
|
|
||||||
["img"] = "path",
|
|
||||||
["alt"] = "string",
|
|
||||||
["dump"] = "path",
|
|
||||||
["quote"] = "boolean",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Language commands
|
|
||||||
-- Eachs command is a table of table;
|
|
||||||
-- First table indicates one or more internal or external evaluations
|
|
||||||
-- Second table indicates one or more commands to try
|
|
||||||
local langcmds = {
|
|
||||||
["lua"] = {["int_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
["fennel"] = {["int_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
["python"] = {["ext_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
["js"] = {["ext_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
["ruby"] = {["ext_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
["lisp"] = {["ext_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
["graphviz"] = {["ext_eval"] = { "CMD1", "CMD2", } },
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Prints error
|
|
||||||
-- Since error messages debuglevel are "ERROR", lit.status turns false
|
|
||||||
-- Flush the declaration to avoid storage in lit.fns
|
|
||||||
local function putserr(...)
|
|
||||||
lit.puts(...)
|
|
||||||
declaration = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Adds 'eval' key in declaration
|
|
||||||
-- This key indicates what to eval
|
|
||||||
local function addeval()
|
|
||||||
declaration["eval"] = {}
|
|
||||||
-- If lang and cmd are present, ignores lang and use cmd as eval
|
|
||||||
if declaration["lang"] and declaration["cmd"] then
|
|
||||||
lit.puts("ignore_lang", declaration["cmd"], declaration["lang"])
|
|
||||||
declaration["eval"] = {["ext_eval"] = { declaration["cmd"] } }
|
|
||||||
-- If neither lang or cmd are presents, defaults lang and eval to lua
|
|
||||||
elseif not(declaration["lang"]) and not(declaration["cmd"])then
|
|
||||||
declaration["lang"] = "lua"
|
|
||||||
declaration["eval"] = langcmds["lua"]
|
|
||||||
-- If only lang present, eval is lang
|
|
||||||
elseif declaration["lang"] and not(declaration["cmd"]) then
|
|
||||||
declaration["eval"] = langcmds[declaration["lang"]]
|
|
||||||
-- If only cmd present, eval is cmd
|
|
||||||
elseif not(declaration["lang"]) and declaration["cmd"] then
|
|
||||||
declaration["eval"] = {["ext_eval"] = { declaration["cmd"] } }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for valid command for Unix systems
|
|
||||||
local function checkcmd()
|
|
||||||
if declaration["cmd"] then
|
|
||||||
local cmd = declaration["cmd"]:split()[1]
|
|
||||||
if os.isunix() then
|
|
||||||
local status = io.try("type", cmd)
|
|
||||||
if not(status) then
|
|
||||||
putserr("invalid_cmd", cmd)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
lit.puts("skip_check", "checkcmd")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for extra, unwanted and wrong metadata
|
|
||||||
local function checkextra()
|
|
||||||
for key, _ in pairs(declaration) do
|
|
||||||
local missing1 = metastruct["mandatory"][key] == nil
|
|
||||||
local missing2 = metastruct["optional"][key] == nil
|
|
||||||
if missing1 and missing2 then
|
|
||||||
putserr("invalid_key", key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for valid metadata
|
|
||||||
local function checkmeta(kind)
|
|
||||||
|
|
||||||
-- Checks for valid metadata type
|
|
||||||
local function checktype(type1, key)
|
|
||||||
local val = declaration[key]
|
|
||||||
local type2 = type(val)
|
|
||||||
if type1 ~= type2 then
|
|
||||||
if type1 == "path" then
|
|
||||||
if not(pandoc.path.directory(val):isdir()) then
|
|
||||||
putserr("invalid_path", val, key)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
putserr("invalid_type", type2, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for valid metadata pattern
|
|
||||||
local function checkpattern(table, key)
|
|
||||||
local val = tostring(declaration[key])
|
|
||||||
local err = key
|
|
||||||
for _, pattern in pairs(table) do
|
|
||||||
if val:match("^" .. pattern .. "$") then
|
|
||||||
err = ""
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not(err:isempty()) then
|
|
||||||
putserr("invalid_value", val, err)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, val in pairs(metastruct[kind]) do
|
|
||||||
if declaration[key] == nil and kind == "mandatory" then
|
|
||||||
putserr("no_key", key)
|
|
||||||
elseif declaration[key] and type(val) == "table" then
|
|
||||||
checkpattern(val, key)
|
|
||||||
elseif declaration[key] then
|
|
||||||
checktype(val, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses metadata
|
|
||||||
local function parsemeta(yaml)
|
|
||||||
local isok, res = pcall(pandoc.read, yaml)
|
|
||||||
if isok and not(pandoc.utils.stringify(res.meta):isempty()) then
|
|
||||||
declaration = pandoc.metatotable(res.meta)
|
|
||||||
checkmeta("mandatory")
|
|
||||||
checkmeta("optional")
|
|
||||||
checkextra()
|
|
||||||
checkcmd()
|
|
||||||
if next(declaration) then addeval() end
|
|
||||||
elseif isok and pandoc.utils.stringify(res.meta):isempty() then
|
|
||||||
putserr("meta_empty")
|
|
||||||
else
|
|
||||||
putserr("meta_invalid")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
-- Parses code
|
|
||||||
local function parsecode(rawcode)
|
|
||||||
local code = ""
|
|
||||||
local references = lpeg.match(lit.g("references"), rawcode)
|
|
||||||
if references then
|
|
||||||
for _, ref in ipairs(references) do
|
|
||||||
print("reference:", ref)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
declaration["code"] = code
|
|
||||||
end
|
|
||||||
|
|
||||||
if match then
|
|
||||||
lit.puts("checking", table.concat(match, ""):indent())
|
|
||||||
parsemeta(match[1])
|
|
||||||
if next(declaration) then parsecode(match[2]) end
|
|
||||||
end
|
|
||||||
return declaration
|
|
||||||
end
|
|
||||||
|
|
||||||
local parsed = check(lpeg.match(lit.g("declaration"), codeblock.text))
|
|
||||||
if next(parsed) then
|
|
||||||
-- TODO:
|
|
||||||
-- Eval parsed lit.fns and modify code block accordingly
|
|
||||||
lit.fns[parsed["id"]] = parsed
|
|
||||||
end
|
|
||||||
return codeblock
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calls literate functions
|
|
||||||
local function call(el)
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
-- Calls literate functions in code inlines
|
|
||||||
local function codecall(code)
|
|
||||||
return pandoc.Str("EVAL: " .. pandoc.utils.stringify(code))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function metacall(meta)
|
|
||||||
local function inlinescall(inlines)
|
|
||||||
return inlines:walk {
|
|
||||||
Code = function(code) return codecall(code) end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, val in pairs(meta) do
|
|
||||||
if pandoc.utils.type(val) == "Inlines" then
|
|
||||||
meta[key] = inlinescall(val)
|
|
||||||
elseif pandoc.utils.type(val) == "List" then
|
|
||||||
for i, item in ipairs(val) do
|
|
||||||
if pandoc.utils.type(item) == "Inlines" then
|
|
||||||
meta[key][i] = inlinescall(item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return meta
|
|
||||||
end
|
|
||||||
|
|
||||||
if pandoc.utils.type(el) == "Meta" then
|
|
||||||
return metacall(el)
|
|
||||||
else
|
|
||||||
return codecall(el)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Asserts literate status
|
|
||||||
local function assert()
|
|
||||||
if not(lit.status) then
|
|
||||||
lit.puts("aborted")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
doc = doc:walk { CodeBlock = function(block) return extract(block) end }
|
|
||||||
doc = doc:walk { Meta = function(meta) return call(meta) end }
|
|
||||||
doc = doc:walk { Code = function(code) return call(code) end }
|
|
||||||
return doc:walk { Pandoc = function(_) assert() end }
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------ PANDOC -----------------------------------
|
|
||||||
|
|
||||||
return {
|
|
||||||
-- Parses and evals literate programming
|
|
||||||
{ Pandoc = function(e) return lit.exam(e) end },
|
|
||||||
-- TODO Parses and evals natural programming
|
|
||||||
-- { Inlines = function(e) return nat.get(pandoc.utils.stringify(e)) end },
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,66 @@
|
||||||
|
# Test 1
|
||||||
|
|
||||||
|
With Literate Pandoc you can write code as you write everything else! For
|
||||||
|
example, this is a function declaration: f() = (+ 1 2 3).
|
||||||
|
|
||||||
|
Every function declaration has:
|
||||||
|
|
||||||
|
1. A *function name* consisting in:
|
||||||
|
1. One or more alphanumeric characters following by a opening parenthesis
|
||||||
|
(i.e. `%w+(`).
|
||||||
|
2. Optional arguments (args).
|
||||||
|
3. Closing parenthesis (`)`).
|
||||||
|
2. A *function assignment* indicated with the equal sign (`=`).
|
||||||
|
3. A *function body* composed by Lisp code between parentheses.
|
||||||
|
|
||||||
|
Any function can be called at any time by is function name, for example now
|
||||||
|
we call f()! This also apply for function that haven't been defined, like
|
||||||
|
foo(1).
|
||||||
|
|
||||||
|
The functions can contain args, for example:
|
||||||
|
|
||||||
|
* foo(n) = (* #n #n) is a function declaration with the arg `n`; any arg can
|
||||||
|
be used in the function body if it is prefixed with a hashtag (`#n`).
|
||||||
|
* They can contain more args like bar(a, b) = (- a b).
|
||||||
|
* For a variable number of args the `...` symbol is used: baz(...) =
|
||||||
|
(.. #...). You can call baz() or baz('hello', ' ', 'world!').
|
||||||
|
|
||||||
|
All args can be treated as keyword args (kwargs), so foo(3) can be foo(n: 3),
|
||||||
|
or bar(2, 1) can be bar(b: 1, a: 2).
|
||||||
|
|
||||||
|
With kwargs you can declare args in any order and makes args more readable. The
|
||||||
|
trade-off is that they are verbose. Also, `...` arg can't be use as kwarg!
|
||||||
|
|
||||||
|
Calling f(), foo(2), bar(4, 3), baz(':', ')') will evaluate the functions and
|
||||||
|
will print the output during Pandoc conversion, but, what if we want to...
|
||||||
|
|
||||||
|
1. ...write the evaluation result instead of the function call?
|
||||||
|
For example, see `6` instead of `f()`, or `:)` instead of `baz(':', ')')`.
|
||||||
|
2. ...remove the function call? So you don't see `f()` anymore.
|
||||||
|
3. ...avoid evaluation?
|
||||||
|
4. ...write the evaluation result to a file?
|
||||||
|
5. ...write the evaluated code to a file?
|
||||||
|
6. ...write Lisp code as Lua code to a file?
|
||||||
|
|
||||||
|
Well, we can do that with *reserved kwargs*. All reserved kwargs are optional
|
||||||
|
(you don't declare them) and start with underscore (`_`):
|
||||||
|
|
||||||
|
* `_action`: indicates action to perfom with the function call; can be:
|
||||||
|
* `'flip'`: puts its evaluation result instead;
|
||||||
|
* `'wipe'`: deletes it from source document but still calls it;
|
||||||
|
* `'skip'`: doesn't perform call;
|
||||||
|
* `'kill'`: doesn't perform call and deletes it from source.
|
||||||
|
* `_eval`: saves evaluation to file; takes file path string as value.
|
||||||
|
* `_code`: saves code to file; takes file path string as value.
|
||||||
|
* `_lua`: converts Lisp code to Lua and saves it to file; takes file path
|
||||||
|
string as value.
|
||||||
|
|
||||||
|
With reserved kwargs, we can finally replace f() by its evaluation result like
|
||||||
|
this: f(_action: 'flip'); or skip its evaluation: f(_action: 'skip').
|
||||||
|
|
||||||
|
We can also save different files:
|
||||||
|
|
||||||
|
* The avaluation results of foo(4, _eval: 'results.txt') and bar(5, 4, _eval:
|
||||||
|
'results.txt').
|
||||||
|
* The code of foo(4, _code: 'code.lisp') and bar(5, 4, _code: 'code.lisp').
|
||||||
|
* The Lua code of foo(4, _lua: 'code.lua') and bar (5, 4, _lua: 'code.lua').
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
LUAROCKS_SYSCONFDIR='/etc/luarocks' exec '/usr/bin/lua5.4' -e 'package.path="/home/perro/Repositorios/computable-pandoc/opt/share/lua/5.4/?.lua;/home/perro/Repositorios/computable-pandoc/opt/share/lua/5.4/?/init.lua;"..package.path;package.cpath="/home/perro/Repositorios/computable-pandoc/opt/lib/lua/5.4/?.so;"..package.cpath;local k,l,_=pcall(require,"luarocks.loader") _=k and l.add_context("fennel","1.3.0-1")' '/home/perro/Repositorios/computable-pandoc/opt/lib/luarocks/rocks-5.4/fennel/1.3.0-1/bin/fennel' "$@"
|
|
File diff suppressed because one or more lines are too long
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright © 2016-2022 Calvin Rose and contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,129 +0,0 @@
|
||||||
# Fennel
|
|
||||||
|
|
||||||
[Fennel][1] is a lisp that compiles to Lua. It aims to be easy to use,
|
|
||||||
expressive, and has almost zero overhead compared to writing Lua directly.
|
|
||||||
|
|
||||||
* *Full Lua compatibility* - You can use any function or library from Lua.
|
|
||||||
* *Zero overhead* - Compiled code should be just as efficient as hand-written Lua.
|
|
||||||
* *Compile-time macros* - Ship compiled code with no runtime dependency on Fennel.
|
|
||||||
* *Embeddable* - Fennel is a one-file library as well as an executable. Embed it in other programs to support runtime extensibility and interactive development.
|
|
||||||
|
|
||||||
At [https://fennel-lang.org][1] there's a live in-browser repl you can
|
|
||||||
use without installing anything. At [https://fennel-lang.org/see][3]
|
|
||||||
you can see what Lua output a given piece of Fennel compiles to, or
|
|
||||||
what the equivalent Fennel for a given piece of Lua would be.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
* The [setup](setup.md) guide is a great place to start
|
|
||||||
* The [tutorial](tutorial.md) teaches the basics of the language
|
|
||||||
* The [rationale](rationale.md) explains the reasoning of why Fennel was created
|
|
||||||
* The [reference](reference.md) describes all Fennel special forms
|
|
||||||
* The [macro guide](macros.md) explains how to write macros
|
|
||||||
* The [API listing](api.md) shows how to integrate Fennel into your codebase
|
|
||||||
* The [style guide](style.md) gives tips on how to write clear and concise code
|
|
||||||
* The [Lua primer](lua-primer.md) gives a very brief intro to Lua with
|
|
||||||
pointers to further details
|
|
||||||
|
|
||||||
For more examples, see [the cookbook][2] on [the wiki][7].
|
|
||||||
|
|
||||||
The [changelog](changelog.md) has a list of user-visible changes for
|
|
||||||
each release.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
#### Hello World
|
|
||||||
```Fennel
|
|
||||||
(print "hello, world!")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fibonacci sequence
|
|
||||||
```Fennel
|
|
||||||
(fn fib [n]
|
|
||||||
(if (< n 2)
|
|
||||||
n
|
|
||||||
(+ (fib (- n 1)) (fib (- n 2)))))
|
|
||||||
|
|
||||||
(print (fib 10))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building Fennel from source
|
|
||||||
|
|
||||||
Building Fennel from source allows you to use versions of Fennel that
|
|
||||||
haven't been released, and it makes contributing to Fennel easier.
|
|
||||||
|
|
||||||
### To build Fennel from source
|
|
||||||
|
|
||||||
1. `cd` to a directory in which you want to download Fennel, such as
|
|
||||||
`~/src`
|
|
||||||
2. Run `git clone https://git.sr.ht/~technomancy/fennel`
|
|
||||||
3. Run `cd fennel`
|
|
||||||
4. Run `make fennel` to create a standalone script called `fennel`
|
|
||||||
5. Copy or link the `fennel` script to a directory on your `$PATH`, such as `/usr/local/bin`
|
|
||||||
|
|
||||||
**Note**: If you copied the `fennel` script to one of the
|
|
||||||
directories on your `$PATH`, then you can run `fennel filename.fnl` to
|
|
||||||
run a Fennel file anywhere on your system.
|
|
||||||
|
|
||||||
## Differences from Lua
|
|
||||||
|
|
||||||
* Syntax is much more regular and predictable (no statements; no operator precedence)
|
|
||||||
* It's impossible to set *or read* a global by accident
|
|
||||||
* Pervasive destructuring anywhere locals are introduced
|
|
||||||
* Clearer syntactic distinction between sequential tables and key/value tables
|
|
||||||
* Separate looping constructs for numeric loops vs iterators instead of overloading `for`
|
|
||||||
* Opt-in mutability for local variables
|
|
||||||
* Opt-in nil checks for function arguments
|
|
||||||
* Pattern matching
|
|
||||||
* Ability to extend the syntax with your own macros
|
|
||||||
|
|
||||||
## Differences from other lisp languages
|
|
||||||
|
|
||||||
* Its VM can be embedded in other programs with only 180 kB
|
|
||||||
* Access to [excellent FFI][4]
|
|
||||||
* LuaJIT consistently ranks at the top of performance shootouts
|
|
||||||
* Inherits aggressively simple semantics from Lua; easy to learn
|
|
||||||
* Lua VM is already embedded in databases, window managers, games, etc
|
|
||||||
* Low memory usage
|
|
||||||
* Readable compiler output resembles input
|
|
||||||
|
|
||||||
## Why not Fennel?
|
|
||||||
|
|
||||||
Fennel inherits the limitations of the Lua runtime, which does not offer
|
|
||||||
pre-emptive multitasking or OS-level threads. Libraries for Lua work
|
|
||||||
great with Fennel, but the selection of libraries is not as extensive
|
|
||||||
as it is with more popular languages. While LuaJIT has excellent
|
|
||||||
overall performance, purely-functional algorithms will not be as
|
|
||||||
efficient as they would be on a VM with generational garbage collection.
|
|
||||||
|
|
||||||
Even for cases where the Lua runtime is a good fit, Fennel might not
|
|
||||||
be a good fit when end-users are expected to write their own code to
|
|
||||||
extend the program, because the available documentation for learning
|
|
||||||
Lua is much more readily-available than it is for Fennel.
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
* Join the `#fennel` chat [thru IRC on Libera.Chat][9] or [on Matrix][10]
|
|
||||||
* The [mailing list][5] has slower-paced discussion and announcements
|
|
||||||
* Report issues on the mailing list, [Sourcehut todo][11] or [Github issues][12]
|
|
||||||
* You can browse and edit [the Wiki][7]
|
|
||||||
* View builds in Fennel's [continuous integration][8]
|
|
||||||
* Community interactions are subject to the [code of conduct](CODE-OF-CONDUCT.md).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Copyright © 2016-2023 Calvin Rose and contributors
|
|
||||||
|
|
||||||
Released under the [MIT license](LICENSE).
|
|
||||||
|
|
||||||
[1]: https://fennel-lang.org
|
|
||||||
[2]: https://wiki.fennel-lang.org/Cookbook
|
|
||||||
[3]: https://fennel-lang.org/see
|
|
||||||
[4]: http://luajit.org/ext_ffi_tutorial.html
|
|
||||||
[5]: https://lists.sr.ht/%7Etechnomancy/fennel
|
|
||||||
[7]: https://wiki.fennel-lang.org/
|
|
||||||
[8]: https://builds.sr.ht/~technomancy/fennel
|
|
||||||
[9]: https://libera.chat
|
|
||||||
[10]: https://matrix.to/#/!rnpLWzzTijEUDhhtjW:matrix.org?via=matrix.org
|
|
||||||
[11]: https://todo.sr.ht/~technomancy/fennel
|
|
||||||
[12]: https://github.com/bakpakin/Fennel/issues
|
|
|
@ -1,8 +0,0 @@
|
||||||
package = "fennel"
|
|
||||||
local fennel_version = "1.3.0"
|
|
||||||
version = (fennel_version .. "-1")
|
|
||||||
source = {url = ("https://fennel-lang.org/downloads/fennel-" .. fennel_version .. ".tar.gz")}
|
|
||||||
description = {summary = "A lisp that compiles to Lua", detailed = ("Get your parens on--write macros and " .. "homoiconic code on the Lua runtime!"), license = "MIT", homepage = "https://fennel-lang.org/"}
|
|
||||||
dependencies = {"lua >= 5.1"}
|
|
||||||
build = {type = "builtin", install = {bin = {fennel = "fennel"}}, modules = {fennel = "fennel.lua"}}
|
|
||||||
return nil
|
|
|
@ -1,13 +0,0 @@
|
||||||
rock_manifest = {
|
|
||||||
bin = {
|
|
||||||
fennel = "18b74a4083bcb33ebf161b339c82bf5f"
|
|
||||||
},
|
|
||||||
doc = {
|
|
||||||
LICENSE = "3e5a2c7152ee8b5c8d34815a16195a52",
|
|
||||||
["README.md"] = "a7f18dbb5131d2b23aad4fb1b55a31a6"
|
|
||||||
},
|
|
||||||
["fennel-1.3.0-1.rockspec"] = "c7300e84d68e04c9c2f900c685458024",
|
|
||||||
lua = {
|
|
||||||
["fennel.lua"] = "7e6bcb21700f66be5c027f3994d645de"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
# Lua Dog
|
|
||||||
|
|
||||||
Adds functions to Lua Standard Libraries and Pandoc Library.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
luarocks install lua-dog
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
You can use Lua Dog in 2 ways.
|
|
||||||
|
|
||||||
The laziest way is:
|
|
||||||
|
|
||||||
local dog = require("dog")
|
|
||||||
dog.import()
|
|
||||||
-- Lua Dog 'dog.os.uname()' function was imported as 'os.uname()'
|
|
||||||
os.uname()
|
|
||||||
|
|
||||||
This will import all Lua Dog functions to the Lua Standard Libraries and Pandoc Library.
|
|
||||||
|
|
||||||
If you prefer not to import, the way is:
|
|
||||||
|
|
||||||
require "dog"
|
|
||||||
-- Lua Dog 'dog.os.uname()' function was NOT imported as 'os.uname()'
|
|
||||||
dog.os.uname()
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
|
|
||||||
Check `src`.
|
|
|
@ -1,23 +0,0 @@
|
||||||
package = "lua-dog"
|
|
||||||
version = "1.1.0-1"
|
|
||||||
source = {
|
|
||||||
url = "https://gitlab.com/perritotuerto/codigo/lua-dog/-/archive/v" ..
|
|
||||||
version .. "/lua-dog-v" .. version .. ".tar.gz",
|
|
||||||
}
|
|
||||||
description = {
|
|
||||||
summary = "Lua extensions for lazy dogs",
|
|
||||||
detailed = [[
|
|
||||||
Extends Lua Standard Libraries and Pandoc Library.
|
|
||||||
]],
|
|
||||||
homepage = "https://gitlab.com/perritotuerto/codigo/lua-dog/",
|
|
||||||
license = "GPLv3",
|
|
||||||
}
|
|
||||||
dependencies = {
|
|
||||||
"lua >= 5.4, < 5.5"
|
|
||||||
}
|
|
||||||
build = {
|
|
||||||
type = "builtin",
|
|
||||||
modules = {
|
|
||||||
dog = "src/dog.lua"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
rock_manifest = {
|
|
||||||
doc = {
|
|
||||||
["README.md"] = "37185e33a737c1f449075b9ca3da5323"
|
|
||||||
},
|
|
||||||
lua = {
|
|
||||||
["dog.lua"] = "6c0d509614d5de3254d649ba0a75c6ee"
|
|
||||||
},
|
|
||||||
["lua-dog-1.1.0-1.rockspec"] = "a979848880cdf078b2c98ce87a37f15a"
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
commands = {
|
|
||||||
fennel = {
|
|
||||||
"fennel/1.3.0-1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependencies = {
|
|
||||||
fennel = {
|
|
||||||
["1.3.0-1"] = {
|
|
||||||
{
|
|
||||||
constraints = {
|
|
||||||
{
|
|
||||||
op = ">=",
|
|
||||||
version = {
|
|
||||||
5, 1, string = "5.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
name = "lua"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
["lua-dog"] = {
|
|
||||||
["1.1.0-1"] = {
|
|
||||||
{
|
|
||||||
constraints = {
|
|
||||||
{
|
|
||||||
op = ">=",
|
|
||||||
version = {
|
|
||||||
5, 4, string = "5.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
op = "<",
|
|
||||||
version = {
|
|
||||||
5, 5, string = "5.5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
name = "lua"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modules = {
|
|
||||||
dog = {
|
|
||||||
"lua-dog/1.1.0-1"
|
|
||||||
},
|
|
||||||
fennel = {
|
|
||||||
"fennel/1.3.0-1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repository = {
|
|
||||||
fennel = {
|
|
||||||
["1.3.0-1"] = {
|
|
||||||
{
|
|
||||||
arch = "installed",
|
|
||||||
commands = {
|
|
||||||
fennel = "fennel"
|
|
||||||
},
|
|
||||||
dependencies = {},
|
|
||||||
modules = {
|
|
||||||
fennel = "fennel.lua"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
["lua-dog"] = {
|
|
||||||
["1.1.0-1"] = {
|
|
||||||
{
|
|
||||||
arch = "installed",
|
|
||||||
commands = {},
|
|
||||||
dependencies = {},
|
|
||||||
modules = {
|
|
||||||
dog = "dog.lua"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit cce8f5b51bdffd1e0439956ccc308d0e5d164f90
|
|
@ -1,331 +0,0 @@
|
||||||
-- Variable for storing all the functions
|
|
||||||
local dog = {}
|
|
||||||
|
|
||||||
-- Functions are divided according to the standard library to extend
|
|
||||||
dog.io = {}
|
|
||||||
dog.os = {}
|
|
||||||
dog.string = {}
|
|
||||||
dog.utf8 = {}
|
|
||||||
|
|
||||||
-- Imports dog functions to _G
|
|
||||||
-- Allows using the other functions without a prefix, i.e.:
|
|
||||||
-- dog.os.uname() => os.uname()
|
|
||||||
function dog.import()
|
|
||||||
local msg = "[WARNING] [DOG] "
|
|
||||||
msg = msg .. "Ignoring import: '@1' already exists; use '@2' instead"
|
|
||||||
for libkey, dogfns in pairs(dog) do
|
|
||||||
local lib = _G[libkey]
|
|
||||||
if type(dogfns) == "table" then
|
|
||||||
for fnkey, fn in pairs(dogfns) do
|
|
||||||
if not(lib[fnkey]) then
|
|
||||||
lib[fnkey] = fn
|
|
||||||
else
|
|
||||||
local name = libkey .. "." .. fnkey .. "()"
|
|
||||||
msg = msg:gsub("@1", name):gsub("@2", "dog." .. name)
|
|
||||||
print(msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Tries popen
|
|
||||||
-- @param ... string: Chunks for popen
|
|
||||||
-- @return boolean, string: Status and output of popen
|
|
||||||
function dog.io.try(...)
|
|
||||||
local cmd = table.concat({...}, " ") .. " 2>&1"
|
|
||||||
local handle = io.popen(cmd)
|
|
||||||
local output = handle:read("*a")
|
|
||||||
local status = (handle:close() ~= nil and true or false)
|
|
||||||
return status, output
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets OS short name
|
|
||||||
-- It is just intented to know if it is Linux, macOS or Windows.
|
|
||||||
-- @return string: "linux" or "bsd" or "macos" or "windows"
|
|
||||||
function dog.os.uname()
|
|
||||||
local status, output = dog.io.try("uname")
|
|
||||||
if status then
|
|
||||||
output = output:gsub(" .*", ""):lower()
|
|
||||||
if output ~= "linux" and output:match("bsd") ~= nil then
|
|
||||||
return "bsd"
|
|
||||||
elseif output ~= "linux" then
|
|
||||||
return "macos"
|
|
||||||
end
|
|
||||||
return "linux"
|
|
||||||
end
|
|
||||||
return "windows"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks if OS is windows
|
|
||||||
-- @return boolean: Windows or not
|
|
||||||
function dog.os.iswin()
|
|
||||||
return dog.os.uname() == "windows"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks if OS is Unix
|
|
||||||
-- @return boolean: Unix or not
|
|
||||||
function dog.os.isunix()
|
|
||||||
return dog.os.uname() ~= "windows"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets OS language
|
|
||||||
-- @return string, string, string: Language, locale and encoding
|
|
||||||
function dog.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
|
|
||||||
-- @return boolean: Empty or not
|
|
||||||
function dog.string.isempty(str)
|
|
||||||
return str == ''
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Changes newlines so everything is in one line
|
|
||||||
-- @return string: String with formatted newlines as literal "\n"
|
|
||||||
function dog.string.linearize(str)
|
|
||||||
return str:gsub("\n", "\\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Normalizes string
|
|
||||||
-- @return string: Normalized string
|
|
||||||
function dog.string.normalize(str)
|
|
||||||
str = str:lower()
|
|
||||||
for newchar, chars in pairs(dog.utf8.table()) do
|
|
||||||
for _, oldchar in ipairs(chars) do
|
|
||||||
str = str:gsub(oldchar, newchar)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Adds indent
|
|
||||||
-- @param num number: Indent size, 2 by default
|
|
||||||
-- @param char string: Indent character, space by default
|
|
||||||
-- @return strin: Indented string
|
|
||||||
function dog.string.indent(str, num, char)
|
|
||||||
num = num or 2
|
|
||||||
char = char or " "
|
|
||||||
char = string.rep(char, num)
|
|
||||||
return char .. str: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 dog.string.split(str, sep)
|
|
||||||
sep = sep or "%s+"
|
|
||||||
local parts = {}
|
|
||||||
for part in str:gmatch("([^" .. sep .. "]+)") do
|
|
||||||
table.insert(parts, part)
|
|
||||||
end
|
|
||||||
return parts
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Removes spaces at the beginning of the string
|
|
||||||
-- @return string: Left stripped string
|
|
||||||
function dog.string.lstrip(str)
|
|
||||||
return str:gsub("^%s+", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Removes spaces at the end of the string
|
|
||||||
-- @return string: Right stripped string
|
|
||||||
function dog.string.rstrip(str)
|
|
||||||
return str:gsub("%s+$", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Removes spaces at the beginning and at the end of the string
|
|
||||||
-- @return string: Stripped string
|
|
||||||
function dog.string.strip(str)
|
|
||||||
str = dog.string.lstrip(str)
|
|
||||||
return dog.string.rstrip(str)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Alias of strip
|
|
||||||
function dog.string.trim(str)
|
|
||||||
return dog.string.strip(str)
|
|
||||||
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 a directory
|
|
||||||
-- @return boolean: Exists or not
|
|
||||||
function dog.string.exists(str)
|
|
||||||
return os.rename(str, str) ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks if string is a file
|
|
||||||
-- @return boolean: File or not
|
|
||||||
function dog.string.isfile(str)
|
|
||||||
if dog.string.exists(str) then
|
|
||||||
return io.open(str, "a+") ~= nil
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks if string is a directory
|
|
||||||
-- @return boolean: Directory or not
|
|
||||||
function dog.string.isdir(str)
|
|
||||||
if dog.string.exists(str) then
|
|
||||||
return io.open(str, "a+") == nil
|
|
||||||
elseif str == "." or str == ".." then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Reads file content as string
|
|
||||||
-- @return string or nil: File as string or nil
|
|
||||||
function dog.string.readtext(str)
|
|
||||||
if dog.string.exists(str) then
|
|
||||||
return io.open(str):read("*a")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Read file content as lines
|
|
||||||
-- @return table: Table of file lines or nil
|
|
||||||
function dog.string.readlines(str)
|
|
||||||
local lines = {}
|
|
||||||
if dog.string.exists(str) then
|
|
||||||
for line in io.open(str):lines() do
|
|
||||||
table.insert(lines, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return lines
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets file without suffix
|
|
||||||
-- @return string: File wihtout suffix
|
|
||||||
function dog.string.stem(str)
|
|
||||||
return str:gsub("%.%a+$", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets file extensions
|
|
||||||
-- @return table: List of file extensions
|
|
||||||
function dog.string.suffixes(str)
|
|
||||||
local suffixes = {}
|
|
||||||
for suffix in str:gmatch("%.%a+") do
|
|
||||||
table.insert(suffixes, suffix)
|
|
||||||
end
|
|
||||||
return suffixes
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets file final extension
|
|
||||||
-- @return string: Final file extension
|
|
||||||
function dog.string.suffix(str)
|
|
||||||
local suffixes = str:suffixes()
|
|
||||||
if suffixes[#suffixes] then
|
|
||||||
return suffixes[#suffixes]
|
|
||||||
end
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets an equivalency table between ASCII and Unicode
|
|
||||||
-- Note: filled on demand.
|
|
||||||
-- @return table: ASCII-Unicode table equivalency
|
|
||||||
function dog.utf8.table()
|
|
||||||
return {
|
|
||||||
["a"] = {"á", "à", "ä"},
|
|
||||||
["e"] = {"é", "è", "ë"},
|
|
||||||
["i"] = {"í", "ì", "ï"},
|
|
||||||
["o"] = {"ó", "ò", "ö"},
|
|
||||||
["u"] = {"ú", "ù", "ü"},
|
|
||||||
["n"] = {"ñ"},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Extends Pandoc Library
|
|
||||||
-- Check: https://pandoc.org/lua-filters.html#module-pandoc
|
|
||||||
|
|
||||||
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)
|
|
||||||
local ext = dog.string.suffix(file):gsub("^.", "")
|
|
||||||
return ((ext == "md" or dog.string.isempty(ext)) and "markdown" or ext)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pandoc converter
|
|
||||||
-- Converts input file name into output format name.
|
|
||||||
-- 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: Output file content
|
|
||||||
function pandoc.convert(ifile, oformat, ofile, iformat)
|
|
||||||
iformat = (iformat == nil and pandoc.getext(ifile) or iformat)
|
|
||||||
local itext = pandoc.read(dog.string.readtext(ifile), iformat)
|
|
||||||
local doc = pandoc.write(itext, oformat)
|
|
||||||
if ofile ~= nil then
|
|
||||||
local eol = (dog.os:isunix() and "\n" or "\r\n")
|
|
||||||
io.open(ofile, "w"):write(doc, eol):close()
|
|
||||||
end
|
|
||||||
return doc
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Stringifies Pandoc content
|
|
||||||
-- Avoids undesired behavios of pandoc.utils.stringify, such as quotes and
|
|
||||||
-- backslashes conversions.
|
|
||||||
-- @param content pandoc.MetaValue: Pandoc content value
|
|
||||||
-- @return string: Pandoc stringified value
|
|
||||||
function pandoc.utils.rawstringify(content)
|
|
||||||
return pandoc.utils.stringify(content:walk {
|
|
||||||
Plain = function(plain)
|
|
||||||
table.insert(plain.content, pandoc.Space())
|
|
||||||
return plain
|
|
||||||
end,
|
|
||||||
Quoted = function(quoted)
|
|
||||||
local q = (quoted.quotetype == SingleQuoted and '"' or "'")
|
|
||||||
return pandoc.Str(q .. pandoc.utils.stringify(quoted.content) .. q)
|
|
||||||
end,
|
|
||||||
RawInline = function(rawinline)
|
|
||||||
return pandoc.Str(rawinline.text:gsub("\\n", "\n"):gsub("\\t", "\t"))
|
|
||||||
end,
|
|
||||||
SoftBreak = function(_)
|
|
||||||
return pandoc.Str("\n")
|
|
||||||
end,
|
|
||||||
Inline = function(inline) return pandoc.utils.stringify(inline) end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Converts pandoc.Meta to table
|
|
||||||
-- Note that stringified numbers, such "1", are converted to numbers because
|
|
||||||
-- Pandoc doesn't make distinctions between strings and numbers.
|
|
||||||
-- Check: https://pandoc.org/lua-filters.html#type-metavalue
|
|
||||||
-- @param meta pandoc.Meta: Pandoc MetaValues collection
|
|
||||||
-- @return table: Pandoc metadata as table
|
|
||||||
function pandoc.metatotable(meta)
|
|
||||||
local newmeta = {}
|
|
||||||
for k, v in pairs(meta) do
|
|
||||||
if pandoc.utils.type(v) == "table" then
|
|
||||||
newmeta[k] = pandoc.metatotable(v)
|
|
||||||
else
|
|
||||||
if type(v) == "boolean" then
|
|
||||||
newmeta[k] = v
|
|
||||||
elseif tonumber(v) then
|
|
||||||
newmeta[k] = tonumber(v)
|
|
||||||
else
|
|
||||||
newmeta[k] = pandoc.utils.rawstringify(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return newmeta
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return dog
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Variables
|
||||||
|
NAME="fennel-1.3.0"
|
||||||
|
ROOT=$(git rev-parse --show-toplevel)
|
||||||
|
URL="https://fennel-lang.org/downloads/$NAME.tar.gz"
|
||||||
|
|
||||||
|
# Copies Fennel from release tarball
|
||||||
|
cd $ROOT/opt
|
||||||
|
curl -O $URL
|
||||||
|
tar -xvzf $NAME.tar.gz
|
||||||
|
mv $NAME/fennel.lua .
|
||||||
|
rm -rf $NAME*
|
|
@ -1,16 +0,0 @@
|
||||||
# Downloads Lua Modules
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
ROOT=$(dirname $0)/..
|
|
||||||
|
|
||||||
# Goes to root directory
|
|
||||||
cd $ROOT
|
|
||||||
|
|
||||||
# Gets modules from luarocks
|
|
||||||
if type luarocks > /dev/null 2>&1; then
|
|
||||||
luarocks install fennel --tree opt
|
|
||||||
luarocks install lua-dog --tree opt
|
|
||||||
else
|
|
||||||
echo "ERROR: luarocks is required; see <https://luarocks.org/>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,63 +0,0 @@
|
||||||
-- Makes bundle and min distribution
|
|
||||||
|
|
||||||
-- Adds local luarocks modules
|
|
||||||
local optpath = "./opt/share/lua/5.4/"
|
|
||||||
package.path = optpath .. "?.lua;" .. package.path
|
|
||||||
|
|
||||||
-- Adds Lua custom extensions
|
|
||||||
local lua_dog = require("dog")
|
|
||||||
lua_dog.import()
|
|
||||||
|
|
||||||
-- Makes distribution
|
|
||||||
local function make_dist(name, bundle)
|
|
||||||
|
|
||||||
-- Chomps file
|
|
||||||
local function chomp(str)
|
|
||||||
str = string.readtext(str):strip()
|
|
||||||
return "\n" .. str .. "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Defaults to true
|
|
||||||
bundle = (bundle == nil or bundle == true)
|
|
||||||
|
|
||||||
-- Variables
|
|
||||||
local dist = pandoc.path.join({"dist", name})
|
|
||||||
local fnl = chomp(optpath .. "fennel.lua"):gsub("\nreturn mod\n", "\nlocal fnl = mod\n")
|
|
||||||
local dog = chomp(optpath .. "dog.lua"):gsub("\nreturn dog\n", "")
|
|
||||||
local nat = chomp("src/natural.lua"):gsub("\nreturn nat\n", "")
|
|
||||||
local lit = chomp("src/literate.lua"):gsub("`locale%(%)`", ("src/locale.yaml"):readtext())
|
|
||||||
local pan = chomp("src/pandoc.lua")
|
|
||||||
local license = string.strip([[
|
|
||||||
Computable Pandoc:
|
|
||||||
(C) 2023 perro hi@perrotuerto.blog
|
|
||||||
License: GPLv3 https://git.cuates.net/perro/computable-pandoc/src/branch/no-masters/LICENSE.txt
|
|
||||||
Source: https://git.cuates.net/perro/computable-pandoc
|
|
||||||
]])
|
|
||||||
local extralicense = string.strip([[
|
|
||||||
Computable Pandoc & Fennel Bundle:
|
|
||||||
A Pandoc filter for literate and natural programming
|
|
||||||
]] .. license .. "\n" .. [[
|
|
||||||
Fennel:
|
|
||||||
(C) 2016-2023 Calvin Rose and contributors
|
|
||||||
License: MIT License https://git.sr.ht/~technomancy/fennel/tree/main/item/LICENSE
|
|
||||||
Source: https://sr.ht/~technomancy/fennel or https://github.com/bakpakin/Fennel/issues
|
|
||||||
Website: https://fennel-lang.org
|
|
||||||
]])
|
|
||||||
|
|
||||||
-- Bundles Fennel and Computable Pandoc
|
|
||||||
local file = io.open(dist, "w")
|
|
||||||
if bundle then
|
|
||||||
file:write("--[[\n", extralicense, "\n]]--\n")
|
|
||||||
file:write(fnl, dog, nat)
|
|
||||||
else
|
|
||||||
file:write("--[[\n", license, "\n]]--\n")
|
|
||||||
file:write('\nrequire "fennel"\nrequire "nat"\n')
|
|
||||||
file:write('\nlocal lua_dog = require("dog")\n')
|
|
||||||
end
|
|
||||||
file:write('\ndog.import()\n')
|
|
||||||
file:write(lit, pan)
|
|
||||||
file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
make_dist("lin.bundle.lua")
|
|
||||||
make_dist("lin.min.lua", false)
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Variables
|
||||||
|
NAME="literate.min.lua"
|
||||||
|
ROOT=$(git rev-parse --show-toplevel)
|
||||||
|
DIST=$ROOT/dist/$NAME
|
||||||
|
LICENSE="--[[
|
||||||
|
Literate Pandoc & Fennel Bundle:
|
||||||
|
A Pandoc filter for literate and natural programming
|
||||||
|
Fennel:
|
||||||
|
(C) 2016-2023 Calvin Rose and contributors
|
||||||
|
License: MIT License https://git.sr.ht/~technomancy/fennel/tree/main/item/LICENSE
|
||||||
|
Source: https://sr.ht/~technomancy/fennel or https://github.com/bakpakin/Fennel/issues
|
||||||
|
Website: https://fennel-lang.org
|
||||||
|
Literate Pandoc:
|
||||||
|
(C) 2023 perro hi@perrotuerto.blog
|
||||||
|
License: GPLv3 https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/LICENSE.txt
|
||||||
|
Source: https://git.cuates.net/perro/literate-pandoc
|
||||||
|
The following code is minified, check Literate Pandoc source for a readable version
|
||||||
|
]]--"
|
||||||
|
|
||||||
|
# Merges Fennel and Literate Pandoc
|
||||||
|
head -n -1 $ROOT/opt/fennel.lua > $DIST
|
||||||
|
echo "local fennel = mod" >> $DIST
|
||||||
|
tail -n +11 $ROOT/src/literate.lua >> $DIST
|
||||||
|
echo "Bundling complete"
|
||||||
|
|
||||||
|
# Minifies code
|
||||||
|
cd opt/luaminify
|
||||||
|
lua CommandLineMinify.lua $DIST $DIST.tmp
|
||||||
|
|
||||||
|
# Adds license
|
||||||
|
(echo "$LICENSE" && cat $DIST.tmp) > $DIST
|
||||||
|
rm $DIST.tmp
|
|
@ -1,24 +0,0 @@
|
||||||
-- Makes JSON test files
|
|
||||||
|
|
||||||
-- Adds local luarocks modules
|
|
||||||
package.path = "./opt/share/lua/5.4/?.lua;" .. package.path
|
|
||||||
|
|
||||||
-- Adds Lua custom extensions
|
|
||||||
local lua_dog = require("dog")
|
|
||||||
lua_dog.import()
|
|
||||||
|
|
||||||
-- Gets command according to OS
|
|
||||||
local function getcmd()
|
|
||||||
if os:isunix() then
|
|
||||||
return "ls"
|
|
||||||
else
|
|
||||||
return "dir"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Makes JSON from markup files
|
|
||||||
for file in io.popen(getcmd() .. " tests/asts"):lines() do
|
|
||||||
local json = pandoc.path.join({"tests", "asts", file})
|
|
||||||
local mark = pandoc.path.join({"tests", pandoc.path.filename(json:stem())})
|
|
||||||
pandoc.convert(mark, "json", json)
|
|
||||||
end
|
|
109
scripts/test.lua
109
scripts/test.lua
|
@ -1,109 +0,0 @@
|
||||||
-- Makes tests
|
|
||||||
|
|
||||||
-- Adds local luarocks modules
|
|
||||||
package.path = "./opt/share/lua/5.4/?.lua;" .. package.path
|
|
||||||
|
|
||||||
-- Adds Lua custom extensions
|
|
||||||
require "scripts.make_dist"
|
|
||||||
|
|
||||||
-- Variables
|
|
||||||
local filter = "dist/lin.bundle.lua"
|
|
||||||
local verbose = ""
|
|
||||||
local trace = ""
|
|
||||||
local files = {}
|
|
||||||
local cmds = {
|
|
||||||
["unix"] = {
|
|
||||||
["pandoc"] = "pandoc",
|
|
||||||
["clear"] = "clear",
|
|
||||||
["ls"] = "ls",
|
|
||||||
},
|
|
||||||
["win"] = {
|
|
||||||
["pandoc"] = "pandoc.exe",
|
|
||||||
["clear"] = "cls",
|
|
||||||
["ls"] = "dir",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Prints help
|
|
||||||
local function help()
|
|
||||||
local cmd = " pandoc lua script/test.lua"
|
|
||||||
print(table.concat({
|
|
||||||
"Usage:",
|
|
||||||
cmd .. " [OPTIONS] [FILES]",
|
|
||||||
"Options",
|
|
||||||
" -V --verbose Prints verbose debbuging output",
|
|
||||||
" -t --trace Prints diagnostic output tracing parser progress",
|
|
||||||
" -h --help Prints this help",
|
|
||||||
"Examples:",
|
|
||||||
cmd .. " Tests for 'tests/*.*",
|
|
||||||
cmd .. " -V Verbose tests for 'tests/*.*'",
|
|
||||||
cmd .. " -t Tests and tracing for 'tests/*.*'",
|
|
||||||
cmd .. " tests/fail* Tests for 'tests/fail*'",
|
|
||||||
cmd .. " -Vt tests/fail* Verbose tests and tracing for 'tests/fail*'",
|
|
||||||
cmd .. " -h Display this help",
|
|
||||||
}, "\n"))
|
|
||||||
os.exit()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets command according to OS
|
|
||||||
local function getcmd(str)
|
|
||||||
local system = (os.isunix() and "unix" or "win")
|
|
||||||
return cmds[system][str]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Obtains result as "pass" | "fail" | "diff" (AST doesn't match)
|
|
||||||
local function getresult(test)
|
|
||||||
local tmp, name = "tmp.json", pandoc.path.filename(test)
|
|
||||||
local json = pandoc.path.join({"tests", "asts", name .. ".json"})
|
|
||||||
local ok, out = io.try(getcmd("pandoc"), "-L", filter, verbose, trace, "-t json", "-o", tmp, test)
|
|
||||||
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()
|
|
||||||
elseif ok and json1 ~= json2 then
|
|
||||||
return "diff", out:strip()
|
|
||||||
else
|
|
||||||
return "fail", out:strip()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses args
|
|
||||||
for _, a in ipairs(arg) do
|
|
||||||
if a:match("^-.*h.*") then
|
|
||||||
help()
|
|
||||||
elseif a == "-V" or a == "--verbose" then
|
|
||||||
verbose = "--verbose"
|
|
||||||
elseif a == "-t" or a == "--trace" then
|
|
||||||
trace = "--trace"
|
|
||||||
elseif a == "-Vt" or a == "-tV" then
|
|
||||||
verbose = "--verbose"
|
|
||||||
trace = "--trace"
|
|
||||||
elseif a:match("^-") == nil then
|
|
||||||
table.insert(files, pandoc.path.normalize(a))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Default files
|
|
||||||
if #files == 0 then
|
|
||||||
for file in io.popen(getcmd("ls") .. " tests"):lines() do
|
|
||||||
if file ~= "asts" then
|
|
||||||
table.insert(files, pandoc.path.join({"tests", file}))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clears terminal
|
|
||||||
os.execute(getcmd("clear"))
|
|
||||||
print "🐾 Starting tests"
|
|
||||||
|
|
||||||
-- Does tests
|
|
||||||
for _, file in ipairs(files) do
|
|
||||||
local expectation = pandoc.path.filename(file):gsub("%W.+$", "")
|
|
||||||
local result, output = getresult(file)
|
|
||||||
print("⚗️ " .. file .. ":")
|
|
||||||
print(" Expect: " .. expectation)
|
|
||||||
print(" Result: " .. result)
|
|
||||||
if #verbose > 0 or #trace > 0 then
|
|
||||||
print(output)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Variables
|
||||||
|
ROOT=$(git rev-parse --show-toplevel)
|
||||||
|
FILTER=$ROOT/src/literate.lua
|
||||||
|
ARGS=()
|
||||||
|
|
||||||
|
# Removes unwanted args
|
||||||
|
for arg in "$@"; do
|
||||||
|
case $arg in
|
||||||
|
"--dist") FILTER=$ROOT/dist/literate.min.lua ;;
|
||||||
|
*) ARGS+=($arg) ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Moves to tests directory and clears the terminal
|
||||||
|
cd $ROOT/tests
|
||||||
|
clear
|
||||||
|
|
||||||
|
# Checks args
|
||||||
|
if [ -z "$*" ]; then
|
||||||
|
echo "ERROR: At least one argument is needed. For example:"
|
||||||
|
echo " sh $0 native"
|
||||||
|
echo " sh $0 native markdown"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Does tests
|
||||||
|
echo "🐾 Starting tests"
|
||||||
|
for arg in "$ARGS"; do
|
||||||
|
echo && echo "⚗️ Test in '$arg' format:"
|
||||||
|
mds=$'\n\n'`(pandoc -t markdown *.md)`
|
||||||
|
rst=$'\n\n'`(pandoc -t markdown *.rst)`
|
||||||
|
org=$'\n\n'`(pandoc -t markdown *.org)`
|
||||||
|
echo "$mds" "$rst" "$org" | pandoc --lua-filter $FILTER -t $arg
|
||||||
|
done
|
395
src/literate.lua
395
src/literate.lua
|
@ -1,341 +1,68 @@
|
||||||
---------------------------------- LITERATE ----------------------------------
|
-- IMPORTANT: for distribution the first 10 lines are changed to:
|
||||||
|
-- local fennel = mod
|
||||||
-- Stores all literate stuff
|
-- Initial setup for development:
|
||||||
local lit = {}
|
-- Enables Fennel for Lisp-Lua embeded compatibility
|
||||||
|
-- Cfr. https://fennel-lang.org
|
||||||
-- Indicates status
|
local src_root = pandoc.path.directory(PANDOC_SCRIPT_FILE)
|
||||||
lit.status = true
|
local fennel_lua = pandoc.path.join({src_root, "../opt/fennel.lua"})
|
||||||
|
package.path = package.path .. ";" .. fennel_lua
|
||||||
-- Stores literate functions
|
local fennel = require("fennel")
|
||||||
lit.fns = {}
|
-- IMPORTANT: code for distribution starts after this line.
|
||||||
|
--[[
|
||||||
-- Returns specific grammar
|
-- Lua LPeg shortcuts
|
||||||
function lit.g(name)
|
|
||||||
-- Stores all grammars
|
|
||||||
local grammars = {}
|
|
||||||
|
|
||||||
-- Shortcuts
|
|
||||||
local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt =
|
local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt =
|
||||||
lpeg.P,
|
lpeg.P, lpeg.S, lpeg.R, lpeg.Cf, lpeg.Cc, lpeg.Ct, lpeg.V,
|
||||||
lpeg.S,
|
lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.B, lpeg.C, lpeg.Cmt
|
||||||
lpeg.R,
|
|
||||||
lpeg.Cf,
|
|
||||||
lpeg.Cc,
|
|
||||||
lpeg.Ct,
|
|
||||||
lpeg.V,
|
|
||||||
lpeg.Cs,
|
|
||||||
lpeg.Cg,
|
|
||||||
lpeg.Cb,
|
|
||||||
lpeg.B,
|
|
||||||
lpeg.C,
|
|
||||||
lpeg.Cmt
|
|
||||||
|
|
||||||
-- Lexical elements
|
-- Lexical elements
|
||||||
local newline = P("\r") ^ -1 * P("\n")
|
local space = S(" \t\r\n")
|
||||||
local space = S(" \t")
|
local lbracket = P"("
|
||||||
local anyspace = S(" \t\r\n")
|
local rbracket = P")"
|
||||||
local spot = (1 - anyspace)
|
local word = (1 - (space + lbracket + rbracket))
|
||||||
local any = (spot + space)
|
|
||||||
local yamlheader = space ^ 0 * P("---") * space ^ 0 * newline
|
|
||||||
local yamlfooter = space ^ 0 * P("...") * space ^ 0 * newline ^ -1
|
|
||||||
local yamlbody = -yamlfooter * any ^ 0 * newline
|
|
||||||
local label = R("az", "AZ") * R("az", "AZ", "09", "__") ^ 0
|
|
||||||
local ref = -B("\\") * P("#") * C(label)
|
|
||||||
local notref = (1 - ref) ^ 0
|
|
||||||
|
|
||||||
-- Function declaration grammar
|
-- Grammar for Pandoc parser that converts:
|
||||||
grammars["declaration"] = P({
|
-- Lisp expression => pandoc.Plain
|
||||||
"Declaration",
|
-- Lisp list => pandoc.Span
|
||||||
Declaration = Ct(V("YAML") * V("Code"), "name"),
|
-- Lisp atom => pandoc.Str
|
||||||
YAML = C(yamlheader * yamlbody ^ 0 * yamlfooter),
|
G = P{
|
||||||
Code = C((any + newline) ^ 0),
|
"Doc";
|
||||||
})
|
Doc = space^0 * Ct(V"SExpr"^0) * space^0 / pandoc.Pandoc;
|
||||||
|
SExpr = (V"List" + V"Atom");
|
||||||
-- Argument references grammar
|
List = lbracket * V"SExpr"^0 * rbracket / embed;
|
||||||
grammars["references"] = notref
|
Atom = space + V"Word";
|
||||||
* P({
|
Word = word^1 / pandoc.Str;
|
||||||
"References",
|
|
||||||
References = Ct((ref * notref) ^ 0),
|
|
||||||
})
|
|
||||||
* -1
|
|
||||||
|
|
||||||
return grammars[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Prints located messages
|
|
||||||
function lit.puts(yaml_key, ...)
|
|
||||||
-- Returns debug level as number
|
|
||||||
local function debuglevel(str)
|
|
||||||
if str == "ERROR" then
|
|
||||||
lit.status = false
|
|
||||||
return 2
|
|
||||||
elseif str == "WARNING" then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Gets located message
|
|
||||||
local function getmsg(key, ...)
|
|
||||||
local msg = pandoc.metatotable(pandoc.read([[`locale()`]]).meta)
|
|
||||||
local levelname, level, lang = "INFO", 0, os.lang()
|
|
||||||
for mtype, msgs in pairs(msg) do
|
|
||||||
if msgs[key] then
|
|
||||||
key = (msgs[key][lang] ~= nil and msgs[key][lang] or msgs[key]["en"])
|
|
||||||
for i, str in ipairs({ ... }) do
|
|
||||||
key = key:gsub("#" .. i, str)
|
|
||||||
end
|
|
||||||
levelname, level = mtype, debuglevel(mtype)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return key, levelname, level
|
|
||||||
end
|
|
||||||
|
|
||||||
local verbosity = debuglevel(PANDOC_STATE.verbosity)
|
|
||||||
local msg, levelname, level = getmsg(yaml_key, ...)
|
|
||||||
if level >= verbosity then print("[" .. levelname .. "] [LIT] " .. msg) end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses and evaluates literate functions in document
|
|
||||||
function lit.exam(doc)
|
|
||||||
-- Extracts function declarations
|
|
||||||
local function extract(codeblock)
|
|
||||||
-- Checks function declarations
|
|
||||||
local function check(match)
|
|
||||||
-- Stores valid declaration
|
|
||||||
local declaration = {}
|
|
||||||
|
|
||||||
-- Valid metadata structure
|
|
||||||
local metastruct = {
|
|
||||||
["mandatory"] = {
|
|
||||||
-- Value as table == whole patterns to match
|
|
||||||
["id"] = { "%a[_%w]*" },
|
|
||||||
},
|
|
||||||
["optional"] = {
|
|
||||||
["lang"] = {
|
|
||||||
"lua",
|
|
||||||
"fennel",
|
|
||||||
"python",
|
|
||||||
"js",
|
|
||||||
"ruby",
|
|
||||||
"lisp",
|
|
||||||
"graphviz",
|
|
||||||
},
|
|
||||||
["cmd"] = { ".+" },
|
|
||||||
-- Value as string == type to match
|
|
||||||
["args"] = "table",
|
|
||||||
["shift"] = "boolean",
|
|
||||||
["wipe"] = "boolean",
|
|
||||||
["typed"] = "boolean",
|
|
||||||
["link"] = "path",
|
|
||||||
["img"] = "path",
|
|
||||||
["alt"] = "string",
|
|
||||||
["dump"] = "path",
|
|
||||||
["quote"] = "boolean",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
]]--
|
||||||
|
|
||||||
-- Language commands
|
-- Evals Lisp code
|
||||||
-- Eachs command is a table of table;
|
-- @param code string: code to evaluate
|
||||||
-- First table indicates one or more internal or external evaluations
|
-- @return table: evaluation result as {bool, string, string, string}
|
||||||
-- Second table indicates one or more commands to try
|
local function eval(code)
|
||||||
local langcmds = {
|
is_passed, out = pcall (
|
||||||
["lua"] = { ["int_eval"] = { "CMD1", "CMD2" } },
|
function () return fennel.eval(code) end,
|
||||||
["fennel"] = { ["int_eval"] = { "CMD1", "CMD2" } },
|
function (e) return e end
|
||||||
["python"] = { ["ext_eval"] = { "CMD1", "CMD2" } },
|
)
|
||||||
["js"] = { ["ext_eval"] = { "CMD1", "CMD2" } },
|
lua = ""
|
||||||
["ruby"] = { ["ext_eval"] = { "CMD1", "CMD2" } },
|
out = tostring(out)
|
||||||
["lisp"] = { ["ext_eval"] = { "CMD1", "CMD2" } },
|
preview = out:gsub("\n.*", "")
|
||||||
["graphviz"] = { ["ext_eval"] = { "CMD1", "CMD2" } },
|
if is_passed then
|
||||||
|
lua = fennel.compileString(code)
|
||||||
|
end
|
||||||
|
return {is_passed = is_passed, preview = preview, out = out, lua = lua}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
CodeBlock = function (block)
|
||||||
|
if block.classes:includes("eval") then
|
||||||
|
local raw = block.text
|
||||||
|
print("⚙️ ", raw)
|
||||||
|
local res = eval(raw)
|
||||||
|
print("", res["is_passed"], "→", res["preview"])
|
||||||
|
if block.classes:includes("replace") then
|
||||||
|
return pandoc.CodeBlock(res["out"], {code=raw})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Prints error
|
|
||||||
-- Since error messages debuglevel are "ERROR", lit.status turns false
|
|
||||||
-- Flush the declaration to avoid storage in lit.fns
|
|
||||||
local function putserr(...)
|
|
||||||
lit.puts(...)
|
|
||||||
declaration = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Adds 'eval' key in declaration
|
|
||||||
-- This key indicates what to eval
|
|
||||||
local function addeval()
|
|
||||||
declaration["eval"] = {}
|
|
||||||
-- If lang and cmd are present, ignores lang and use cmd as eval
|
|
||||||
if declaration["lang"] and declaration["cmd"] then
|
|
||||||
lit.puts("ignore_lang", declaration["cmd"], declaration["lang"])
|
|
||||||
declaration["eval"] = { ["ext_eval"] = { declaration["cmd"] } }
|
|
||||||
-- If neither lang or cmd are presents, defaults lang and eval to lua
|
|
||||||
elseif not declaration["lang"] and not declaration["cmd"] then
|
|
||||||
declaration["lang"] = "lua"
|
|
||||||
declaration["eval"] = langcmds["lua"]
|
|
||||||
-- If only lang present, eval is lang
|
|
||||||
elseif declaration["lang"] and not declaration["cmd"] then
|
|
||||||
declaration["eval"] = langcmds[declaration["lang"]]
|
|
||||||
-- If only cmd present, eval is cmd
|
|
||||||
elseif not declaration["lang"] and declaration["cmd"] then
|
|
||||||
declaration["eval"] = { ["ext_eval"] = { declaration["cmd"] } }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for valid command for Unix systems
|
|
||||||
local function checkcmd()
|
|
||||||
if declaration["cmd"] then
|
|
||||||
local cmd = declaration["cmd"]:split()[1]
|
|
||||||
if os.isunix() then
|
|
||||||
local status = io.try("type", cmd)
|
|
||||||
if not status then putserr("invalid_cmd", cmd) end
|
|
||||||
else
|
|
||||||
lit.puts("skip_check", "checkcmd")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for extra, unwanted and wrong metadata
|
|
||||||
local function checkextra()
|
|
||||||
for key, _ in pairs(declaration) do
|
|
||||||
local missing1 = metastruct["mandatory"][key] == nil
|
|
||||||
local missing2 = metastruct["optional"][key] == nil
|
|
||||||
if missing1 and missing2 then putserr("invalid_key", key) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for valid metadata
|
|
||||||
local function checkmeta(kind)
|
|
||||||
-- Checks for valid metadata type
|
|
||||||
local function checktype(type1, key)
|
|
||||||
local val = declaration[key]
|
|
||||||
local type2 = type(val)
|
|
||||||
if type1 ~= type2 then
|
|
||||||
if type1 == "path" then
|
|
||||||
if not (pandoc.path.directory(val):isdir()) then
|
|
||||||
putserr("invalid_path", val, key)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
putserr("invalid_type", type2, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks for valid metadata pattern
|
|
||||||
local function checkpattern(table, key)
|
|
||||||
local val = tostring(declaration[key])
|
|
||||||
local err = key
|
|
||||||
for _, pattern in pairs(table) do
|
|
||||||
if val:match("^" .. pattern .. "$") then
|
|
||||||
err = ""
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not (err:isempty()) then putserr("invalid_value", val, err) end
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, val in pairs(metastruct[kind]) do
|
|
||||||
if declaration[key] == nil and kind == "mandatory" then
|
|
||||||
putserr("no_key", key)
|
|
||||||
elseif declaration[key] and type(val) == "table" then
|
|
||||||
checkpattern(val, key)
|
|
||||||
elseif declaration[key] then
|
|
||||||
checktype(val, key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parses metadata
|
|
||||||
local function parsemeta(yaml)
|
|
||||||
local isok, res = pcall(pandoc.read, yaml)
|
|
||||||
if isok and not (pandoc.utils.stringify(res.meta):isempty()) then
|
|
||||||
declaration = pandoc.metatotable(res.meta)
|
|
||||||
checkmeta("mandatory")
|
|
||||||
checkmeta("optional")
|
|
||||||
checkextra()
|
|
||||||
checkcmd()
|
|
||||||
if next(declaration) then addeval() end
|
|
||||||
elseif isok and pandoc.utils.stringify(res.meta):isempty() then
|
|
||||||
putserr("meta_empty")
|
|
||||||
else
|
|
||||||
putserr("meta_invalid")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO
|
|
||||||
-- Parses code
|
|
||||||
local function parsecode(rawcode)
|
|
||||||
local code = ""
|
|
||||||
local references = lpeg.match(lit.g("references"), rawcode)
|
|
||||||
if references then
|
|
||||||
for _, ref in ipairs(references) do
|
|
||||||
print("reference:", ref)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
declaration["code"] = code
|
|
||||||
end
|
|
||||||
|
|
||||||
if match then
|
|
||||||
lit.puts("checking", table.concat(match, ""):indent())
|
|
||||||
parsemeta(match[1])
|
|
||||||
if next(declaration) then parsecode(match[2]) end
|
|
||||||
end
|
|
||||||
return declaration
|
|
||||||
end
|
|
||||||
|
|
||||||
local parsed = check(lpeg.match(lit.g("declaration"), codeblock.text))
|
|
||||||
if next(parsed) then
|
|
||||||
-- TODO:
|
|
||||||
-- Eval parsed lit.fns and modify code block accordingly
|
|
||||||
lit.fns[parsed["id"]] = parsed
|
|
||||||
end
|
|
||||||
return codeblock
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Calls literate functions
|
|
||||||
local function call(el)
|
|
||||||
-- TODO
|
|
||||||
-- Calls literate functions in code inlines
|
|
||||||
local function codecall(code)
|
|
||||||
return pandoc.Str("EVAL: " .. pandoc.utils.stringify(code))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function metacall(meta)
|
|
||||||
local function inlinescall(inlines)
|
|
||||||
return inlines:walk({
|
|
||||||
Code = function(code) return codecall(code) end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, val in pairs(meta) do
|
|
||||||
if pandoc.utils.type(val) == "Inlines" then
|
|
||||||
meta[key] = inlinescall(val)
|
|
||||||
elseif pandoc.utils.type(val) == "List" then
|
|
||||||
for i, item in ipairs(val) do
|
|
||||||
if pandoc.utils.type(item) == "Inlines" then
|
|
||||||
meta[key][i] = inlinescall(item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return meta
|
|
||||||
end
|
|
||||||
|
|
||||||
if pandoc.utils.type(el) == "Meta" then
|
|
||||||
return metacall(el)
|
|
||||||
else
|
|
||||||
return codecall(el)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Asserts literate status
|
|
||||||
local function assert()
|
|
||||||
if not lit.status then
|
|
||||||
lit.puts("aborted")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
doc = doc:walk({ CodeBlock = function(block) return extract(block) end })
|
|
||||||
doc = doc:walk({ Meta = function(meta) return call(meta) end })
|
|
||||||
doc = doc:walk({ Code = function(code) return call(code) end })
|
|
||||||
return doc:walk({ Pandoc = function(_) assert() end })
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
---
|
|
||||||
INFO:
|
|
||||||
checking:
|
|
||||||
en: "Checking:\n#1"
|
|
||||||
es: "Comprobando:\n#1"
|
|
||||||
skip_check:
|
|
||||||
en: "Skipping '#1': this check is not supported"
|
|
||||||
es: "Skipping '#1': esta comprobación no está soportada"
|
|
||||||
WARNING:
|
|
||||||
ignore_lang:
|
|
||||||
en: "Keys 'cmd' and 'lang' present: '#1' overrides '#2'"
|
|
||||||
es: "Llaves 'cmd' y 'lang' presentes: '#1' sobrescribe '#2'"
|
|
||||||
ERROR:
|
|
||||||
invalid_key:
|
|
||||||
en: Invalid key '#1'
|
|
||||||
es: Clave '#1' inválida
|
|
||||||
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'
|
|
||||||
invalid_cmd:
|
|
||||||
en: Invalid cmd '#1'
|
|
||||||
es: Comando '#1' inválido
|
|
||||||
no_key:
|
|
||||||
en: Key '#1' not found
|
|
||||||
es: Clave '#1' no encontrada
|
|
||||||
aborted:
|
|
||||||
en: Aborted due previous errors
|
|
||||||
es: Abortado debido a previos errores
|
|
||||||
meta_empty:
|
|
||||||
en: Empty metadata
|
|
||||||
es: Metadatos vacíos
|
|
||||||
meta_invalid:
|
|
||||||
en: Invalid metadata
|
|
||||||
es: Metadatos inválidos
|
|
||||||
...
|
|
|
@ -1,7 +0,0 @@
|
||||||
----------------------------------- NATURAL -----------------------------------
|
|
||||||
|
|
||||||
local nat = {}
|
|
||||||
|
|
||||||
function nat.get(str) return str end
|
|
||||||
|
|
||||||
return nat
|
|
|
@ -1,8 +0,0 @@
|
||||||
------------------------------------ PANDOC -----------------------------------
|
|
||||||
|
|
||||||
return {
|
|
||||||
-- Parses and evals literate programming
|
|
||||||
{ Pandoc = function(e) return lit.exam(e) end },
|
|
||||||
-- TODO Parses and evals natural programming
|
|
||||||
-- { Inlines = function(e) return nat.get(pandoc.utils.stringify(e)) end },
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
||||||
{"pandoc-api-version":[1,23],"meta":{},"blocks":[{"t":"Header","c":[1,["declaration-override",[],[]],[{"t":"Str","c":"Declaration"},{"t":"Space"},{"t":"Str","c":"Override"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn1\n...\n1 + 1"]},{"t":"Para","c":[{"t":"Str","c":"Override:"}]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn1\n...\n1 + 2"]},{"t":"Header","c":[1,["declaration-with-language-and-command",[],[]],[{"t":"Str","c":"Declaration"},{"t":"Space"},{"t":"Str","c":"with"},{"t":"Space"},{"t":"Str","c":"Language"},{"t":"Space"},{"t":"Str","c":"and"},{"t":"Space"},{"t":"Str","c":"Command"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn4\nlang: python\ncmd: python -E -X utf8\n...\n1 + 1"]},{"t":"Header","c":[1,["declaration-with-unused-argument",[],[]],[{"t":"Str","c":"Declaration"},{"t":"Space"},{"t":"Str","c":"with"},{"t":"Space"},{"t":"Str","c":"Unused"},{"t":"Space"},{"t":"Str","c":"Argument"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn3\nargs:\n a: 1\n...\n1 + 1"]}]}
|
|
|
@ -1,142 +0,0 @@
|
||||||
# Invalid Declarations
|
|
||||||
|
|
||||||
All errors should be collected, printed and exit with 1.
|
|
||||||
|
|
||||||
Malformed YAML:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
fail
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Empty YAML:
|
|
||||||
|
|
||||||
---
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Empty YAML and code:
|
|
||||||
|
|
||||||
---
|
|
||||||
...
|
|
||||||
|
|
||||||
Misses id:
|
|
||||||
|
|
||||||
---
|
|
||||||
identifier: fn1
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Wrong id (doesn't starts with `%a`):
|
|
||||||
|
|
||||||
---
|
|
||||||
id: 1
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Wrong id (doesn't follows with `[_%w]`):
|
|
||||||
|
|
||||||
---
|
|
||||||
id: f-1
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Wrong id (more than 1 word):
|
|
||||||
|
|
||||||
---
|
|
||||||
id: f n1
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Invalid value:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
shift: "true"
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Invalid path:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
dump: invalid/path.txt
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Extra key:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
language: fennel
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Uknown lang:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
lang: fail
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Uknown cmd:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
cmd: piton -E -X utf8
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Empty code:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
...
|
|
||||||
|
|
||||||
Misses arg:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
...
|
|
||||||
#a + #b
|
|
||||||
|
|
||||||
Infinite loop:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
args:
|
|
||||||
x: 1
|
|
||||||
...
|
|
||||||
`fn1(2)` * #x
|
|
||||||
|
|
||||||
Invalid code:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
...
|
|
||||||
false + false
|
|
||||||
|
|
||||||
# Declarations With Invalid Calls:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
typed: true
|
|
||||||
args:
|
|
||||||
a: 1
|
|
||||||
b: 2
|
|
||||||
...
|
|
||||||
#a * #b
|
|
||||||
|
|
||||||
# Invalid Calls
|
|
||||||
|
|
||||||
- `fn1(` never end
|
|
||||||
- `fn1(invalid arg)`
|
|
||||||
- `fn1()` misses args
|
|
||||||
- `fn1(1)` less args
|
|
||||||
- `fn1(1, 2, 3)` extra args
|
|
||||||
- `fn1(x: 3, y: 4)` wrong kwargs
|
|
||||||
- `fn1(1, a: 2)` mixed arg and kwarg
|
|
||||||
- `fn1(a: 1.0, b: 2.0)` misses type
|
|
||||||
- `fn2()` not declared
|
|
|
@ -1,202 +0,0 @@
|
||||||
---
|
|
||||||
bool: true
|
|
||||||
int: 1
|
|
||||||
float: 1.1
|
|
||||||
string: foo all
|
|
||||||
list:
|
|
||||||
- true
|
|
||||||
- 1
|
|
||||||
- 1.1
|
|
||||||
- foo all
|
|
||||||
- "`fn1()`"
|
|
||||||
- Another test `fn2()`
|
|
||||||
fn1_test: "`fn1()`"
|
|
||||||
fn2_test: Another test `fn2()`
|
|
||||||
...
|
|
||||||
|
|
||||||
# Calls Before Declarations
|
|
||||||
|
|
||||||
- `fn1()``fn2()`
|
|
||||||
- `fn3(true)`
|
|
||||||
- `fn4(2.0, 3.1)`
|
|
||||||
- `fn3(n: false)`
|
|
||||||
- `fn4(b: 4, a: 5)`
|
|
||||||
|
|
||||||
# Declarations
|
|
||||||
|
|
||||||
All literate declarations goes in code blocks that should be printed on
|
|
||||||
`--verbose`.
|
|
||||||
|
|
||||||
Minimal (Lua by default):
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
...
|
|
||||||
1 + 2 + 3
|
|
||||||
|
|
||||||
With scape:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn2
|
|
||||||
...
|
|
||||||
"\#x"
|
|
||||||
|
|
||||||
With arg:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn3
|
|
||||||
args:
|
|
||||||
n: str
|
|
||||||
...
|
|
||||||
#n == #n
|
|
||||||
|
|
||||||
With lang and args:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn4
|
|
||||||
lang: fennel
|
|
||||||
args:
|
|
||||||
a: 1
|
|
||||||
b: 2
|
|
||||||
...
|
|
||||||
(* #a #b)
|
|
||||||
|
|
||||||
With cmd (ignores lang):
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn5
|
|
||||||
cmd: python -E -X utf8
|
|
||||||
args:
|
|
||||||
n: 2
|
|
||||||
...
|
|
||||||
#n + #n
|
|
||||||
|
|
||||||
With shift:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn6
|
|
||||||
shift: true
|
|
||||||
...
|
|
||||||
"The literate block is shifted by its eval result."
|
|
||||||
|
|
||||||
With wipe:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn7
|
|
||||||
wipe: true
|
|
||||||
...
|
|
||||||
"This evals but it is wipe from doc."
|
|
||||||
|
|
||||||
With typed:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn4
|
|
||||||
typed: true
|
|
||||||
args:
|
|
||||||
a: 1
|
|
||||||
b: 2
|
|
||||||
...
|
|
||||||
#a * #b
|
|
||||||
|
|
||||||
With link and alt:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn8
|
|
||||||
lang: graphviz
|
|
||||||
link: ./graph.png
|
|
||||||
alt: A graph.
|
|
||||||
...
|
|
||||||
digraph G {
|
|
||||||
a -> b;
|
|
||||||
b -> c
|
|
||||||
c -> a;
|
|
||||||
}
|
|
||||||
|
|
||||||
With img and alt:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn9
|
|
||||||
lang: graphviz
|
|
||||||
img: ./graph.png
|
|
||||||
alt: A graph.
|
|
||||||
...
|
|
||||||
digraph G {
|
|
||||||
a -> b;
|
|
||||||
b -> c
|
|
||||||
c -> a;
|
|
||||||
}
|
|
||||||
|
|
||||||
With dump and quote:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn10
|
|
||||||
dump: ./dump.txt
|
|
||||||
quote: true
|
|
||||||
...
|
|
||||||
This code is saved into './dump.txt' because of 'dump'.
|
|
||||||
This code is not evaluated because 'quote' is true.
|
|
||||||
|
|
||||||
With inner function:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn11
|
|
||||||
args:
|
|
||||||
x: 2
|
|
||||||
...
|
|
||||||
`fn1()` * x
|
|
||||||
|
|
||||||
With inner function with args:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn12
|
|
||||||
args:
|
|
||||||
y: 1
|
|
||||||
z: 2
|
|
||||||
...
|
|
||||||
#y + `fn4(3, 4)` + #z
|
|
||||||
|
|
||||||
With inner inner function:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn13
|
|
||||||
args:
|
|
||||||
a: 1
|
|
||||||
...
|
|
||||||
#a + `fn4(#a, `fn1()`)`
|
|
||||||
|
|
||||||
# Code Blocks That Are Not Declarations
|
|
||||||
|
|
||||||
Always ignored:
|
|
||||||
|
|
||||||
---
|
|
||||||
echo "Ignore me!"
|
|
||||||
|
|
||||||
# Calls and Data Types
|
|
||||||
|
|
||||||
- `fn3(true)`
|
|
||||||
- `fn3(false)`
|
|
||||||
- `fn3([])`
|
|
||||||
- `fn3([0, 1])`
|
|
||||||
- `fn3({})`
|
|
||||||
- `fn3({"k1": 1, "k2": 2})`
|
|
||||||
- `fn4(3)`
|
|
||||||
- `fn5(1.0)`
|
|
||||||
- `fn5("str")`
|
|
||||||
|
|
||||||
# Messy Calls
|
|
||||||
|
|
||||||
- `fn1( )`
|
|
||||||
- `fn3("\"str\"")`
|
|
||||||
- `fn3( 4)`
|
|
||||||
- `fn3(5 )`
|
|
||||||
- `fn3( 6 )`
|
|
||||||
- `fn3( n: 7)`
|
|
||||||
- `fn3(n: 8 )`
|
|
||||||
- `fn3( n: 9 )`
|
|
||||||
- `fn3(n:10)`
|
|
||||||
- `fn4( a: 6, b: 7)`
|
|
||||||
- `fn4( a: 8 , b: 9)`
|
|
||||||
- `fn4( a: 10 , b: 11)`
|
|
||||||
- `fn4( a: 12 , b: 13)`
|
|
||||||
- `fn4( a: 14 , b: 15 )`
|
|
||||||
- `fn4(a:16,b:17)`
|
|
|
@ -1,31 +0,0 @@
|
||||||
# Declaration Override
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
Override:
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn1
|
|
||||||
...
|
|
||||||
1 + 2
|
|
||||||
|
|
||||||
# Declaration with Language and Command
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn4
|
|
||||||
lang: python
|
|
||||||
cmd: python -E -X utf8
|
|
||||||
...
|
|
||||||
1 + 1
|
|
||||||
|
|
||||||
# Declaration with Unused Argument
|
|
||||||
|
|
||||||
---
|
|
||||||
id: fn3
|
|
||||||
args:
|
|
||||||
a: 1
|
|
||||||
...
|
|
||||||
1 + 1
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
# Test with Markdown
|
||||||
|
|
||||||
|
## Function Declarations
|
||||||
|
|
||||||
|
Valid declarations:
|
||||||
|
|
||||||
|
* A declaration: f1() = (+ 1 2 3). All declarations should be print on `--verbose`.
|
||||||
|
* f2() = (+ 4 5 6) is another declaration.
|
||||||
|
* f3() = (+ 7 8 9)
|
||||||
|
* Two declarations: f4() = (- 9 8) and f5() = (- 7 6).
|
||||||
|
* Two consecutive declarations: f6() = (- 5 4) f7() = (- 3 2).
|
||||||
|
* A declaration with one arg: f8(n) = (* #n #n).
|
||||||
|
* A declaration with two args: f9(a, b) = (* #a #b).
|
||||||
|
* A declaration with variable number of args: f10(...) = (.. #...).
|
||||||
|
|
||||||
|
Not declarations:
|
||||||
|
|
||||||
|
* \f11() = (+ 1 2); doesn't starts with `%a`
|
||||||
|
* f-12() = (+ 1 2); doesn't continue with `%w`
|
||||||
|
* f 13() = (+ 1 2); starts with `%d`
|
||||||
|
* f14 () = (+ 1 2); space before `(`
|
||||||
|
* f15) = (+ 1 2); misses `(`
|
||||||
|
* f16() = + 1 2); misses `(`
|
||||||
|
* f17( = (+ 1 2); misses `)`
|
||||||
|
* f18() = (+ 1 2); misses `)`
|
||||||
|
* f19 = (+ 1 2); misses `()`
|
||||||
|
* f20() = + 1 2; misses `()`
|
||||||
|
* f21() (+ 1 2); misses `=`
|
||||||
|
* `f22() = (+ 1 2)`; inside inline code
|
||||||
|
|
||||||
|
```
|
||||||
|
f23() = (+ 1 2) inside code block
|
||||||
|
```
|
||||||
|
|
||||||
|
Overrides `f1()` with warn: f1() = (+ 2 3 4) and it should fail on `--fail-if-warnings`.
|
||||||
|
|
||||||
|
## Functions Calls
|
||||||
|
|
||||||
|
Valid calls:
|
||||||
|
|
||||||
|
* A common call: f1(). All calls should be print on `--verbose`.
|
||||||
|
* f2() another common call.
|
||||||
|
* f3()
|
||||||
|
* Two calls: f4() and f5().
|
||||||
|
* Two consecutive calls: f6() f7().
|
||||||
|
* A call with one arg: f8(2).
|
||||||
|
* A call with two args: f9(2, 3).
|
||||||
|
* A call with variable number of args: f10("The popular ", "\"Hello,", " ", "", "World!\"").
|
||||||
|
* A call with args as kwargs: f8(n: 3) and f9(a: 4, b: 5).
|
||||||
|
|
||||||
|
Valid calls and data types:
|
||||||
|
|
||||||
|
* f10(); no data
|
||||||
|
* f10(1, 1_000, 1.0); numbers
|
||||||
|
* f10("string"); string
|
||||||
|
* f10([]); empty array / list / sequential table
|
||||||
|
* f10([0 1]); array / list / sequential table
|
||||||
|
* f10({}); empty dict / table
|
||||||
|
* f10({"k" 0}); dict / table
|
||||||
|
|
||||||
|
Not calls:
|
||||||
|
|
||||||
|
* \f11(); doesn't starts with `%a`
|
||||||
|
* f-12(); doesn't continue with `%w`
|
||||||
|
* f 13(); starts with `%d`
|
||||||
|
* f14 (); space before `(`
|
||||||
|
* f15); misses `(`
|
||||||
|
* f16(a); invalid data type
|
||||||
|
* f17(; misses `)`
|
||||||
|
* f18(a:); misses kwarg value
|
||||||
|
* f19(1 2); misses comma separator
|
||||||
|
* f20( ); extra space
|
||||||
|
* f21({); incomplete data type
|
||||||
|
* `f22()`; inside inline code
|
||||||
|
|
||||||
|
```
|
||||||
|
f23() inside code block
|
||||||
|
```
|
||||||
|
|
||||||
|
Invalid calls:
|
||||||
|
|
||||||
|
* f8(); misses arg
|
||||||
|
* f8(a: 3); wrong kwarg
|
||||||
|
* f9(1, 2, 3); wrong args number
|
||||||
|
* f9(1, b: 2); mixed arg and kwarg
|
||||||
|
* f10(...: 0); `...` can't be kwarg
|
||||||
|
* f11(); not declared
|
||||||
|
|
||||||
|
Invalid calls generate error.
|
||||||
|
|
||||||
|
# Function Calls with Reserved Keyword Arguments
|
||||||
|
|
||||||
|
Valid calls:
|
||||||
|
|
||||||
|
* f1($action: "return"); returns result after call.
|
||||||
|
* f2($action: "clear"); clears it from source document after call.
|
||||||
|
* f3($action: "wipe"); wipes it and its declaration after all calls.
|
||||||
|
* f4($action: "dump"); dumps its declaration after call.
|
||||||
|
* f4($action: "quote"); dumps its declaration without call.
|
||||||
|
* f5($eval: "f5.txt", $code: "f5.fnl", $lua: "f5.lua"); writes evaluation results, Lisp code and Lua code.
|
||||||
|
* f6($eval: "f6-7.txt", $code: "f6-7.fnl", $lua: "f6-7.lua"); writes in same files than below.
|
||||||
|
* f7($eval: "f6-7.txt", $code: "f6-7.fnl", $lua: "f6-7.lua"); writes in same files than above.
|
||||||
|
* f8(4, $action: "return")
|
||||||
|
* f8($action: "return", 5)
|
||||||
|
* f9(a: 1, b: 2, $action: "return")
|
||||||
|
* f9($action: "return", a: 1, b: 2)
|
||||||
|
* f9(a: 1, $action: "return", b: 2)
|
||||||
|
|
||||||
|
Invalid calls:
|
||||||
|
|
||||||
|
* f1($act: "return"); invalid rkwarg
|
||||||
|
* f2($action: "clearr"); invalid rkwarg value
|
||||||
|
* f3($eval: "/path/does/not/exists"); invalid path
|
||||||
|
* f4($code: "/path/does/not/exists"); invalid path
|
||||||
|
* f5($lua: "/path/does/not/exists"); invalid path
|
||||||
|
|
||||||
|
Invalid calls generate error.
|
||||||
|
|
||||||
|
# Function Recursion
|
||||||
|
|
||||||
|
Valid recursion:
|
||||||
|
|
||||||
|
* A declaration that uses a call inside: f24(x) = (* f1() x), result: f24($action: "return").
|
||||||
|
* A declaration that uses a call inside with "quote" action: f25(y, z) = (+ f2($action: "quote") y z), result: f25(9, 8, $action: "return").
|
||||||
|
* A call with other function as arg: f8(f1()).
|
||||||
|
* A call with other function as kwarg: f9(b: 3, a: f2()).
|
||||||
|
|
||||||
|
Invalid recursion:
|
||||||
|
|
||||||
|
* f26(i) = (* f11() i); \f11() not declared.
|
||||||
|
* f27(j) = (* f27(1) f27(2)); infinite loop.
|
||||||
|
* f8(n: f11()); \f11() not declared.
|
||||||
|
* f8(f8(3)); infinite loop.
|
|
@ -0,0 +1 @@
|
||||||
|
* Test with Org Mode
|
|
@ -0,0 +1,2 @@
|
||||||
|
Test with reStructuredText
|
||||||
|
==========================
|
Loading…
Reference in New Issue