diff --git a/README.md b/README.md index 46dc7d7..6488557 100644 --- a/README.md +++ b/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 diff --git a/dist/literate.lua b/dist/linp.lua similarity index 99% rename from dist/literate.lua rename to dist/linp.lua index 7aac7be..60f8ecd 100644 --- a/dist/literate.lua +++ b/dist/linp.lua @@ -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, + } +} diff --git a/scripts/make_dist.sh b/scripts/make_dist.sh index ba67c0c..b4acf82 100644 --- a/scripts/make_dist.sh +++ b/scripts/make_dist.sh @@ -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 diff --git a/scripts/test.sh b/scripts/test.sh index 0192dad..404c645 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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 diff --git a/src/literate.lua b/src/literate.lua index d14e28b..18ee3f7 100644 --- a/src/literate.lua +++ b/src/literate.lua @@ -1,54 +1,40 @@ -------------------------------------------------------------------------------- --- IMPORTANT: these lines are for development setup --- IMPORTANT: these lines are changed to "local fennel = mod" --- Enables Fennel 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 diff --git a/src/natural.lua b/src/natural.lua new file mode 100644 index 0000000..ce2d19d --- /dev/null +++ b/src/natural.lua @@ -0,0 +1,5 @@ +----------------------------------- NATURAL ----------------------------------- + +local nat = {} + +return natural diff --git a/src/pandoc.lua b/src/pandoc.lua new file mode 100644 index 0000000..e0f6807 --- /dev/null +++ b/src/pandoc.lua @@ -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, + } +} diff --git a/tests/test.md b/tests/test.md index 0f27984..ee4b217 100644 --- a/tests/test.md +++ b/tests/test.md @@ -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.