Distribution now are two: bundle and minimal

This commit is contained in:
perro tuerto 2023-05-25 12:56:03 -07:00
parent 86a4be358f
commit eac9b36409
7 changed files with 596 additions and 29 deletions

View File

@ -8,21 +8,27 @@ process of creating works of literature][1]".
- [Pandoc] v3
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
1. Go to [Releases].
2. Download the latest version of `lin.lua`.
2. Download the latest version of `lin.bundle.lua` or `lin.min.lua`.
3. Done!
## Usage
With `lin.lua` downloaded and Pandoc installed, do:
With `lin.bundle.lua` or `lin.min.lua` downloaded and Pandoc installed, do:
pandoc -L PATH/TO/lin.lua -t FORMAT DOC
pandoc -L PATH/TO/lin.bundle.lua -t FORMAT DOC
For example, if `DOC` is `source.md` and the output `FORMAT` is HTML, do:
pandoc -L PATH/TO/lin.lua -t html source.md
pandoc -L PATH/TO/lin.bundle.lua -t html source.md
## Manual
@ -50,7 +56,7 @@ For make distribution filter, do:
pandoc lua scripts/make_dist.lua
For make jsons (intented for testing asserts), do:
For make JSON (intented for testing asserts), do:
pandoc lua scripts/make_jsons.lua
@ -67,8 +73,8 @@ This wouldn't be possible without these projects and their collaborators:
- [Pandoc][]: universal document converter and parser; handles the
requirements for LIN.
- [Lua][]: programming language; enables LIN.
- [Fennel][]: [Lisp] dialect with full Lua compatibility; allows native evals
for Lisp.
- [Fennel][2]: [Lisp] dialect with full Lua compatibility; allows native
evals for Lisp.
## License
@ -82,9 +88,11 @@ Happy hacking :)
[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
[Pandoc]: https://pandoc.org/
[fennel]: https://luarocks.org/modules/technomancy/fennel
[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/
[Fennel]: https://fennel-lang.org
[2]: https://fennel-lang.org
[Lisp]: https://en.wikipedia.org/wiki/Lisp_(programming_language)
[GPLv3]: https://git.cuates.net/perro/computable-pandoc/src/branch/no-masters/LICENSE.txt

View File

@ -11,6 +11,7 @@ Fennel:
Source: https://sr.ht/~technomancy/fennel or https://github.com/bakpakin/Fennel/issues
Website: https://fennel-lang.org
]]--
package.preload["fennel.repl"] = package.preload["fennel.repl"] or function(...)
local utils = require("fennel.utils")
local parser = require("fennel.parser")
@ -6556,6 +6557,7 @@ if pandoc ~= nil then
end
end
----------------------------------- NATURAL -----------------------------------
local nat = {}
@ -6563,6 +6565,7 @@ local nat = {}
function nat.get(str)
return str
end
---------------------------------- LITERATE ----------------------------------
-- Variable for all literate stuff

363
dist/lin.min.lua vendored Normal file
View File

@ -0,0 +1,363 @@
--[[
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 "dog"
----------------------------------- NATURAL -----------------------------------
local nat = {}
function nat.get(str)
return str
end
---------------------------------- LITERATE ----------------------------------
-- Variable for all literate stuff
local lit = {}
-- Status indicator
lit.status = true
-- Grammars
function lit.g(name)
-- Lexical elements
local newline = lpeg.P"\r"^-1 * lpeg.P"\n"
local space = lpeg.S" \t"
local anyspace = lpeg.S" \t\r\n"
local spot = (1 - anyspace)
local any = (spot + space)
local yamlheader = space^0 * lpeg.P"---" * space^0 * newline
local yamlfooter = space^0 * lpeg.P"..." * space^0 * newline^-1
local yamlbody = -yamlfooter * any^0 * newline
-- Still not used
local id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
local ref = lpeg.P"#" * id
local grammars = {
-- Block grammar
["block"] = lpeg.P {
"Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(yamlheader * yamlbody^0 * yamlfooter);
Code = lpeg.C((any + newline)^0);
},
}
return grammars[name]
end
-- Prints located messages
function lit.puts(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] ~= nil 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(key, ...)
if level >= verbosity then
print("[" .. levelname .. "] [LIT] " .. msg)
end
end
-- Examinates (parses and evaluates) document
function lit.exam(doc)
-- Evaluates code block
-- Evals Lisp code
--[[
local function eval(code)
local is_passed, out = pcall (
function () return fennel.eval(code) end,
function (e) return e end
)
local lua = ""
local out = tostring(out)
local preview = out:gsub("\n.*", "")
if is_passed then
lua = fennel.compileString(code)
end
return {is_passed = is_passed, preview = preview, out = out, lua = lua}
end
]]--
-- Parses code block
local function parse(codeblock)
-- Checks code block
local function check(parsed)
-- Indicates if checks passes
local ispass = true
-- 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
local langcmds = {
["lua"] = {["int"] = "TODO"},
["fennel"] = {["int"] = "TODO"},
["python"] = {["ext"] = "TODO"},
["js"] = {["ext"] = "TODO"},
["ruby"] = {["ext"] = "TODO"},
["lisp"] = {["ext"] = "TODO"},
["graphviz"] = {["ext"] = "TODO"},
}
-- Prints error and does not pass the check
local function putserr(...)
lit.puts(...)
ispass = false
end
-- Adds 'eval' key in meta
-- This keys indicates what to eval
local function addeval(meta)
meta["eval"] = {}
if ispass then
if meta["lang"] and meta["cmd"] then
lit.puts("ignore_lang", meta["cmd"], meta["lang"])
meta["eval"] = {["ext"] = meta["cmd"]}
elseif not(meta["lang"]) and not(meta["cmd"])then
meta["lang"] = "lua"
meta["eval"] = langcmds["lua"]
elseif meta["lang"] and not(meta["cmd"]) then
meta["eval"] = langcmds[meta["lang"]]
elseif not(meta["lang"]) and meta["cmd"] then
meta["eval"] = {["ext"] = meta["cmd"]}
end
end
return meta
end
-- Checks for valid command
local function checkcmd(meta)
if meta["cmd"] then
cmd = meta["cmd"]:split()[1]
if os.isunix() then
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 and unwanted metadata
local function checkextra(meta)
for key, val in pairs(meta) 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(meta, kind)
-- Checks for valid metadata type
local function checktype(type1, meta, key)
local val = meta[key]
local type2 = type(val)
if type1 ~= type2 then
if type1 == "path" then
if not(pandoc.path.directory(val):isdir()) then
putserr("invalid_path", val, key)
end
else
putserr("invalid_type", type2, key)
end
end
end
-- Checks for valid metadata pattern
local function checkpattern(table, meta, key)
local val = tostring(meta[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 meta[key] == nil and kind == "mandatory" then
putserr("no_key", key)
elseif meta[key] and type(val) == "table" then
checkpattern(val, meta, key)
elseif meta[key] then
checktype(val, meta, key)
end
end
end
-- Parses metadata
local function parsemeta(yaml)
local isok, res = pcall(pandoc.read, yaml)
local meta = {}
if isok and not(pandoc.utils.stringify(res.meta):isempty()) then
meta = pandoc.metatotable(res.meta)
checkmeta(meta, "mandatory")
checkmeta(meta, "optional")
checkextra(meta)
checkcmd(meta)
elseif isok and pandoc.utils.stringify(res.meta):isempty() then
putserr("meta_empty")
else
putserr("meta_invalid")
end
return addeval(meta)
end
lit.puts("checking", table.concat(parsed, ""):indent())
local meta = parsemeta(parsed[1])
-- TODO:
-- meta["code"] = checkcode(parsed[2], meta)
-- NOTE: if not valid code, return meta["eval"] = {}
return meta
end
local parsed = lpeg.match(lit.g("block"), codeblock.text)
local checked = (parsed ~= nil and check(parsed) or parsed)
-- TODO:
-- evaluated = eval(checked)
-- TODO: write evaluated and do overrides with warns
-- return evaluated
return codeblock
end
-- TODO
-- Evals and inserts inline code
local function insert(code)
return code
end
-- Asserts literate status
local function assert()
if not(lit.status) then
lit.puts("aborted")
os.exit(1)
end
end
return doc:walk { CodeBlock = function(block) return parse(block) end }
:walk { Code = function(inline) return insert(inline) end }
:walk { Pandoc = function(_) assert() end }
end
------------------------------------ PANDOC -----------------------------------
return {
-- Parses and evals literate programming
{ Pandoc = function(doc) return lit.exam(doc) end },
{
-- Parses and evals natural programming
-- TODO
Inlines = function(inlines)
md = pandoc.utils.stringify(inlines)
md = nat.get(md)
return inlines
end,
}
}

View File

@ -1,4 +1,4 @@
-- Makes bundle distribution
-- Makes bundle and min distribution
-- Adds local luarocks modules
local optpath = "./opt/share/lua/5.4/"
@ -8,34 +8,36 @@ package.path = optpath .. "?.lua;" .. package.path
require "dog"
-- Makes distribution
local function make_dist()
local function make_dist(name, bundle)
-- Chomps file
local function chomp(str, without)
local function chomp(str)
local without = without or false
str = string.readtext(str):strip()
if without then
return str:gsub("\n[^\n]+$", "")
end
return "\n" .. str .. "\n"
end
-- Variables
local name = "lin.lua"
local dist = pandoc.path.join({"dist", name})
local bundle = (bundle == nil or bundle == true)
local dist = pandoc.path.join({"dist", name})
local ext = chomp(optpath .. "dog.lua")
local fnl = chomp(optpath .. "fennel.lua", true)
local fnl = chomp(optpath .. "fennel.lua")
local fnl = fnl:gsub("\nreturn mod\n", "\nlocal fnl = mod\n")
local pan = chomp("src/pandoc.lua")
local nat = chomp("src/natural.lua", true)
local lit = chomp("src/literate.lua", true)
local nat = chomp("src/natural.lua"):gsub("\nreturn nat\n", "")
local msg = ("src/locale.yaml"):readtext()
local lit = chomp("src/literate.lua"):gsub("\nreturn lit\n", "")
local lit = lit:gsub("#locale%(%)", msg)
local license = string.strip([[
Computable Pandoc & Fennel Bundle:
A Pandoc filter for literate and natural programming
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
@ -44,11 +46,20 @@ Fennel:
]])
-- Bundles Fennel and Computable Pandoc
-- TODO: lit and nat should be rocks and, therefore, should be:
-- added only in bundle
-- use 'require' in minimal
file = io.open(dist, "w")
file:write("--[[\n", license, "\n]]--\n")
file:write(fnl, "\nlocal fnl = mod\n", ext, nat)
file:write(lit:gsub("#locale%(%)", msg), pan)
if bundle then
file:write("--[[\n", extralicense, "\n]]--\n")
file:write(fnl, ext)
else
file:write("--[[\n", license, "\n]]--\n")
file:write('\nrequire "fennel"\nrequire "dog"\n')
end
file:write(nat, lit, pan)
file:close()
end
make_dist()
make_dist("lin.bundle.lua")
make_dist("lin.min.lua", false)

View File

@ -6,9 +6,6 @@ package.path = "./opt/share/lua/5.4/?.lua;" .. package.path
-- Adds Lua custom extensions
require "dog"
-- Variables
local filter = "dist/lin.lua"
-- Gets command according to OS
local function getcmd(str)
local system = (os.isunix() and "unix" or "win")

View File

@ -8,7 +8,7 @@ require "dog"
require "scripts.make_dist"
-- Variables
local filter = "dist/lin.lua"
local filter = "dist/lin.bundle.lua"
local verbose = ""
local trace = ""
local files = {}

185
tests/pass.lit.infos.md Normal file
View File

@ -0,0 +1,185 @@
# Inserts Before Blocks
- `fn1()``fn2()`
- `fn3(true)`
- `fn4(2.0, 3.1)`
- `fn3(n: false)`
- `fn4(b: 4, a: 5)`
# Literate Blocks
All literate blocks should be print 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
Always ignored:
---
echo "Ignore me!"
# Inserts 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 Inserts
- `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)`