From reader to filter... again
This commit is contained in:
parent
a786c4f227
commit
37eb069a1b
26
README.md
26
README.md
|
@ -1,8 +1,8 @@
|
|||
# Literate Pandoc
|
||||
|
||||
Literate Pandoc is a [Pandoc reader] written in [Lua] for [literate] and
|
||||
[natural] programming (LNP), i.e.: "[Programming \[...\] as the process of
|
||||
creating works of literature][1]".
|
||||
Literate Pandoc is a [Pandoc filter] written in [Lua] for [literate] and
|
||||
[natural] programming (LiNP or just Linp), i.e.: "[Programming \[...\] as the
|
||||
process of creating works of literature][1]".
|
||||
|
||||
## Requirements
|
||||
|
||||
|
@ -11,22 +11,22 @@ creating works of literature][1]".
|
|||
## Install
|
||||
|
||||
1. Go to [Releases].
|
||||
2. Download the latest version of `literate.lua`.
|
||||
2. Download the latest version of `linp.lua`.
|
||||
3. Done!
|
||||
|
||||
## Usage
|
||||
|
||||
With `literate.lua` downloaded and Pandoc installed, do:
|
||||
With `linp.lua` downloaded and Pandoc installed, do:
|
||||
|
||||
pandoc -f PATH/TO/literate.lua -t FORMAT DOC
|
||||
pandoc -L PATH/TO/linp.lua -t FORMAT DOC
|
||||
|
||||
For example, if `DOC` is `source.md` and the output `FORMAT` is HTML, do:
|
||||
|
||||
pandoc -f PATH/TO/literate.lua -t html source.md
|
||||
pandoc -L PATH/TO/linp.lua -t html source.md
|
||||
|
||||
## Manual
|
||||
|
||||
Learn how to do LNP [here].
|
||||
Learn how to do LiNP [here].
|
||||
|
||||
## Test
|
||||
|
||||
|
@ -48,17 +48,17 @@ For example, if `FORMAT1` is Markdwon and `FORMAT2` is HTML, do:
|
|||
|
||||
For distribution tests, do:
|
||||
|
||||
sh scripts/test.sh --dist FORMAT1 FORMAT2
|
||||
sh scripts/test.sh FORMAT1 FORMAT2
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
This wouldn't be possible without these projects and their collaborators:
|
||||
|
||||
- [Pandoc][]: universal document converter and parser; handles the
|
||||
requirements for LNP.
|
||||
- [Lua][]: programming language; enables LNP.
|
||||
requirements for LiNP.
|
||||
- [Lua][]: programming language; enables LiNP.
|
||||
- [Fennel][]: [Lisp] dialect with full Lua compatibility; allows to go from
|
||||
LNP to Lisp or Lua.
|
||||
LiNP to Lisp or Lua.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -66,7 +66,7 @@ Literate Pandoc is under [GPLv3].
|
|||
|
||||
Happy hacking :)
|
||||
|
||||
[Pandoc reader]: https://pandoc.org/custom-readers.html
|
||||
[Pandoc filter]: https://pandoc.org/lua-filters.html
|
||||
[Lua]: https://www.lua.org/
|
||||
[literate]: https://en.wikipedia.org/wiki/Literate_programming
|
||||
[natural]: https://en.wikipedia.org/wiki/Natural-language_programming
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--[[
|
||||
Literate Pandoc & Fennel Bundle:
|
||||
A Pandoc reader for literate and natural programming
|
||||
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
|
||||
|
@ -10,7 +10,6 @@ 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
|
||||
]]--
|
||||
package.preload["fennel.repl"] = package.preload["fennel.repl"] or function(...)
|
||||
local utils = require("fennel.utils")
|
||||
|
@ -6258,33 +6257,48 @@ do
|
|||
end
|
||||
package.preload[module_name] = nil
|
||||
end
|
||||
local fennel = mod
|
||||
local fnl = mod
|
||||
----------------------------------- NATURAL -----------------------------------
|
||||
|
||||
-- Lua LPeg 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
|
||||
local nat = {}
|
||||
|
||||
----------------------------------- LITERATE ----------------------------------
|
||||
|
||||
-- TODO
|
||||
local lit = {}
|
||||
|
||||
-- Ordered collection of fn call, fn declarations or literal elements
|
||||
lit.collection = {}
|
||||
|
||||
-- Lexical elements
|
||||
local space = S(" \t\r\n")
|
||||
local lbracket = P"("
|
||||
local rbracket = P")"
|
||||
local word = (1 - (space + lbracket + rbracket))
|
||||
lit.space = lpeg.S" \t\r\n"
|
||||
lit.spot = (1 - lit.space)
|
||||
lit.t1 = lpeg.S"()" -- TMP
|
||||
lit.t2 = (1 - (lit.t1 + lit.space)) -- TMP
|
||||
|
||||
function lit.add_call(str)
|
||||
table.insert(lit.collection, {call = str})
|
||||
end
|
||||
|
||||
function lit.add_declaration(str)
|
||||
table.insert(lit.collection, {declaration = str})
|
||||
end
|
||||
|
||||
function lit.add_lit(str)
|
||||
table.insert(lit.collection, {literal = str})
|
||||
end
|
||||
|
||||
-- Grammar
|
||||
G = P{
|
||||
lit.G = lpeg.P {
|
||||
"Doc";
|
||||
Doc = space^0 * Ct(V"SExpr"^0) * space^0 / pandoc.Pandoc;
|
||||
SExpr = (V"List" + V"Atom");
|
||||
List = lbracket * V"SExpr"^0 * rbracket;
|
||||
Atom = space + V"Word";
|
||||
Word = word^1 / pandoc.Str;
|
||||
Doc = (lpeg.V"Call" + lpeg.V"Declaration" + lpeg.V"Literal")^0;
|
||||
Call = lit.t1^1 / lit.add_call;
|
||||
Declaration = lit.t2^1 / lit.add_declaration;
|
||||
Literal = lit.space^1 / lit.add_lit;
|
||||
}
|
||||
|
||||
-- Evals Lisp code
|
||||
-- @param code string: code to evaluate
|
||||
-- @return table: evaluation result as {bool, string, string, string}
|
||||
local function eval(code)
|
||||
function lit.eval(code)
|
||||
local is_passed, out = pcall (
|
||||
function () return fennel.eval(code) end,
|
||||
function (e) return e end
|
||||
|
@ -6298,15 +6312,48 @@ local function eval(code)
|
|||
return {is_passed = is_passed, preview = preview, out = out, lua = lua}
|
||||
end
|
||||
|
||||
-- Calls Pandoc Reader.
|
||||
function Reader(input, options)
|
||||
--[[
|
||||
local doc = lpeg.match(G, tostring(input))
|
||||
if options.standalone then
|
||||
return exec(doc)
|
||||
else
|
||||
return doc
|
||||
function lit.parse(raw)
|
||||
lpeg.match(lit.G, raw) -- TODO
|
||||
local doc = ""
|
||||
for i, t in ipairs(lit.collection) do
|
||||
for k, v in pairs(t) do
|
||||
-- print(i, k, v)
|
||||
doc = doc .. v
|
||||
end
|
||||
end
|
||||
]]--
|
||||
return pandoc.Pandoc(tostring(input))
|
||||
return doc
|
||||
end
|
||||
|
||||
------------------------------------ PANDOC -----------------------------------
|
||||
|
||||
local function is_chosen(block)
|
||||
chosen, content = true, block.content
|
||||
if content == nil or pandoc.utils.type(content) ~= "Inlines" then
|
||||
chosen = false
|
||||
end
|
||||
return chosen
|
||||
end
|
||||
|
||||
local function sanitize(inlines)
|
||||
for i, inline in ipairs(inlines) do
|
||||
if inline.tag == "Code" then
|
||||
inlines:remove(i)
|
||||
elseif inline.tag == "Quoted" then
|
||||
str = pandoc.utils.stringify(inline.content):gsub('"', '\\"')
|
||||
inlines[i] = '"' .. str .. '"'
|
||||
end
|
||||
end
|
||||
return pandoc.utils.stringify(inlines)
|
||||
end
|
||||
|
||||
return {
|
||||
{
|
||||
Block = function (block)
|
||||
if is_chosen(block) then
|
||||
raw = sanitize(block.content)
|
||||
doc = lit.parse(raw)
|
||||
print(doc)
|
||||
end
|
||||
end,
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
# Makes distribution release
|
||||
|
||||
# Variables
|
||||
NAME="literate.lua"
|
||||
NAME="linp.lua"
|
||||
DIST=dist/$NAME
|
||||
LICENSE="--[[
|
||||
Literate Pandoc & Fennel Bundle:
|
||||
A Pandoc reader for literate and natural programming
|
||||
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
|
||||
|
@ -15,11 +15,12 @@ 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
|
||||
(echo "$LICENSE") > $DIST
|
||||
head -n -1 opt/fennel.lua >> $DIST
|
||||
echo "local fennel = mod" >> $DIST
|
||||
tail -n +10 src/literate.lua >> $DIST
|
||||
echo "local fnl = mod" >> $DIST
|
||||
head -n -1 src/natural.lua >> $DIST
|
||||
head -n -1 src/literate.lua >> $DIST
|
||||
cat src/pandoc.lua >> $DIST
|
||||
|
|
|
@ -1,32 +1,25 @@
|
|||
# Makes tests
|
||||
|
||||
# Variables
|
||||
NAME=literate.lua
|
||||
READER=src/$NAME
|
||||
ARGS=()
|
||||
|
||||
# Removes unwanted args
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
"--dist") READER=dist/$NAME ;;
|
||||
*) ARGS+=($arg) ;;
|
||||
esac
|
||||
done
|
||||
FILTER=dist/linp.lua
|
||||
|
||||
# Checks args
|
||||
if [ -z "$ARGS" ]; then
|
||||
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
|
||||
|
||||
# Makes distribution bundle
|
||||
sh scripts/make_dist.sh
|
||||
|
||||
# Does tests
|
||||
clear && echo "🐾 Starting tests"
|
||||
for arg in "$ARGS"; do
|
||||
for arg in "$@"; do
|
||||
echo && echo "⚗️ Test in '$arg' format:"
|
||||
mds=$'\n\n'`(pandoc -t markdown tests/*.md)`
|
||||
rst=$'\n\n'`(pandoc -t markdown tests/*.rst)`
|
||||
org=$'\n\n'`(pandoc -t markdown tests/*.org)`
|
||||
echo "$mds" "$rst" "$org" | pandoc -f $READER -t $arg
|
||||
echo "$mds" "$rst" "$org" | pandoc -L $FILTER -t $arg
|
||||
done
|
||||
|
|
|
@ -1,54 +1,40 @@
|
|||
-------------------------------------------------------------------------------
|
||||
-- IMPORTANT: these lines are for development setup
|
||||
-- IMPORTANT: these lines are changed to "local fennel = mod"
|
||||
-- Enables Fennel <https://fennel-lang.org> for Lisp-Lua compatibility
|
||||
local src_root = pandoc.path.directory(PANDOC_SCRIPT_FILE)
|
||||
local fennel_lua = pandoc.path.join({src_root, "../opt/fennel.lua"})
|
||||
package.path = package.path .. ";" .. fennel_lua
|
||||
local fennel = require("fennel")
|
||||
-------------------------------------------------------------------------------
|
||||
----------------------------------- LITERATE ----------------------------------
|
||||
|
||||
------------------------------- GRAMMAR related -------------------------------
|
||||
-- TODO
|
||||
local lit = {}
|
||||
|
||||
-- Ordered collection of fn call, fn declarations or literal elements
|
||||
local collection = {}
|
||||
|
||||
-- Lua LPeg 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
|
||||
lit.collection = {}
|
||||
|
||||
-- Lexical elements
|
||||
local space = S" \t\r\n"
|
||||
local spot = (1 - space) -- TMP
|
||||
local t1 = S"()" -- TMP
|
||||
local t2 = (1 - (t1 + space)) -- TMP
|
||||
lit.space = lpeg.S" \t\r\n"
|
||||
lit.spot = (1 - lit.space)
|
||||
lit.t1 = lpeg.S"()" -- TMP
|
||||
lit.t2 = (1 - (lit.t1 + lit.space)) -- TMP
|
||||
|
||||
local function add_call(str)
|
||||
table.insert(collection, {call = str})
|
||||
function lit.add_call(str)
|
||||
table.insert(lit.collection, {call = str})
|
||||
end
|
||||
|
||||
local function add_declaration(str)
|
||||
table.insert(collection, {declaration = str})
|
||||
function lit.add_declaration(str)
|
||||
table.insert(lit.collection, {declaration = str})
|
||||
end
|
||||
|
||||
local function add_lit(str)
|
||||
table.insert(collection, {literal = str})
|
||||
function lit.add_lit(str)
|
||||
table.insert(lit.collection, {literal = str})
|
||||
end
|
||||
|
||||
-- Grammar
|
||||
G = P {
|
||||
lit.G = lpeg.P {
|
||||
"Doc";
|
||||
Doc = (V"Call" + V"Declaration" + V"Literal")^0;
|
||||
Call = t1^1 / add_call;
|
||||
Declaration = t2^1 / add_declaration;
|
||||
Literal = space^1 / add_lit;
|
||||
Doc = (lpeg.V"Call" + lpeg.V"Declaration" + lpeg.V"Literal")^0;
|
||||
Call = lit.t1^1 / lit.add_call;
|
||||
Declaration = lit.t2^1 / lit.add_declaration;
|
||||
Literal = lit.space^1 / lit.add_lit;
|
||||
}
|
||||
|
||||
--------------------------------- LNP related ---------------------------------
|
||||
|
||||
-- Evals Lisp code
|
||||
local function eval(code)
|
||||
function lit.eval(code)
|
||||
local is_passed, out = pcall (
|
||||
function () return fennel.eval(code) end,
|
||||
function (e) return e end
|
||||
|
@ -62,28 +48,16 @@ local function eval(code)
|
|||
return {is_passed = is_passed, preview = preview, out = out, lua = lua}
|
||||
end
|
||||
|
||||
function parse(_)
|
||||
function lit.parse(raw)
|
||||
lpeg.match(lit.G, raw) -- TODO
|
||||
local doc = ""
|
||||
for i, t in ipairs(collection) do
|
||||
for i, t in ipairs(lit.collection) do
|
||||
for k, v in pairs(t) do
|
||||
print(i, k, v)
|
||||
-- print(i, k, v)
|
||||
doc = doc .. v
|
||||
end
|
||||
end
|
||||
return doc
|
||||
end
|
||||
|
||||
function read(sources)
|
||||
raw = ""
|
||||
for _, src in ipairs(sources) do
|
||||
raw = raw .. src.text
|
||||
end
|
||||
return raw
|
||||
end
|
||||
|
||||
-- Calls Pandoc Reader.
|
||||
function Reader(sources, options)
|
||||
local raw = read(sources)
|
||||
local doc = parse(lpeg.match(G, raw))
|
||||
return pandoc.read(doc, FORMAT, options)
|
||||
end
|
||||
return lit
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
----------------------------------- NATURAL -----------------------------------
|
||||
|
||||
local nat = {}
|
||||
|
||||
return natural
|
|
@ -0,0 +1,33 @@
|
|||
------------------------------------ PANDOC -----------------------------------
|
||||
|
||||
local function is_chosen(block)
|
||||
chosen, content = true, block.content
|
||||
if content == nil or pandoc.utils.type(content) ~= "Inlines" then
|
||||
chosen = false
|
||||
end
|
||||
return chosen
|
||||
end
|
||||
|
||||
local function sanitize(inlines)
|
||||
for i, inline in ipairs(inlines) do
|
||||
if inline.tag == "Code" then
|
||||
inlines:remove(i)
|
||||
elseif inline.tag == "Quoted" then
|
||||
str = pandoc.utils.stringify(inline.content):gsub('"', '\\"')
|
||||
inlines[i] = '"' .. str .. '"'
|
||||
end
|
||||
end
|
||||
return pandoc.utils.stringify(inlines)
|
||||
end
|
||||
|
||||
return {
|
||||
{
|
||||
Block = function (block)
|
||||
if is_chosen(block) then
|
||||
raw = sanitize(block.content)
|
||||
doc = lit.parse(raw)
|
||||
print(doc)
|
||||
end
|
||||
end,
|
||||
}
|
||||
}
|
|
@ -26,6 +26,11 @@ Not declarations:
|
|||
* f19 = (+ 1 2); misses `()`
|
||||
* f20() = + 1 2; misses `()`
|
||||
* f21() (+ 1 2); misses `=`
|
||||
* `f22() = (+ 1 2)`; inline code is ignored
|
||||
|
||||
Block code is ignored:
|
||||
|
||||
f23() = (+ 1 2)
|
||||
|
||||
Overrides `f1()` with warn: f1() = (+ 2 3 4) and it should fail on `--fail-if-warnings`.
|
||||
|
||||
|
@ -75,6 +80,11 @@ Invalid calls:
|
|||
* f9(1, b: 2); mixed arg and kwarg
|
||||
* f10(...: 0); `...` can't be kwarg
|
||||
* f11(); not declared
|
||||
* `f22()`; inline code is ignored
|
||||
|
||||
Block code is ignored:
|
||||
|
||||
f23()
|
||||
|
||||
Invalid calls generate error.
|
||||
|
||||
|
|
Loading…
Reference in New Issue