Code blocks are now eval by Fennel
This commit is contained in:
parent
0b5213c078
commit
a36006cee2
|
@ -0,0 +1,11 @@
|
|||
# Variables
|
||||
NAME="fennel-1.3.0"
|
||||
URL="https://fennel-lang.org/downloads/$NAME"
|
||||
ASC="https://fennel-lang.org/downloads/$NAME.asc"
|
||||
DIR=$(git rev-parse --show-toplevel)
|
||||
|
||||
# Copies Fennel from release tarball
|
||||
cd $DIR/src
|
||||
curl -o fennel $URL
|
||||
chmod +x fennel
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# Variables
|
||||
DIR=`dirname -- "$0"`
|
||||
DIR=$(git rev-parse --show-toplevel)
|
||||
|
||||
# Moves to tests directory and clears the terminal
|
||||
cd $DIR
|
||||
cd $DIR/tests
|
||||
clear
|
||||
|
||||
# Checks args
|
||||
|
@ -17,8 +17,8 @@ fi
|
|||
echo "🐾 Starting tests"
|
||||
for arg in "$@"; do
|
||||
echo && echo "⚗️ Test in '$arg' format:"
|
||||
md=`cat src/*.md`
|
||||
rst=$'\n'`(pandoc -t markdown src/*.rst)`
|
||||
org=$'\n'`pandoc -t markdown src/*.org`
|
||||
echo "$md" "$rst" "$org" | pandoc --lua-filter ../src/literate.lua -t $arg
|
||||
md=`cat *.md`
|
||||
rst=$'\n'`(pandoc -t markdown *.rst)`
|
||||
org=$'\n'`pandoc -t markdown *.org`
|
||||
echo "$md" "$rst" "$org" | pandoc --lua-filter $DIR/src/literate.lua -t $arg
|
||||
done
|
File diff suppressed because one or more lines are too long
140
src/literate.lua
140
src/literate.lua
|
@ -4,6 +4,13 @@ literate.lua
|
|||
Code under GPLv3 <https://www.gnu.org/licenses/gpl-3.0.en.html>
|
||||
]]
|
||||
|
||||
-- Gets source root directory and Fennel path
|
||||
-- This will allow to use Lisp inside Lua
|
||||
-- Cfr. https://fennel-lang.org
|
||||
local src_root = pandoc.path.directory(PANDOC_SCRIPT_FILE)
|
||||
local fennel = pandoc.path.join({src_root, "fennel"})
|
||||
|
||||
--[[
|
||||
-- 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,
|
||||
|
@ -15,117 +22,6 @@ local lbracket = P"("
|
|||
local rbracket = P")"
|
||||
local word = (1 - (space + lbracket + rbracket))
|
||||
|
||||
-- Table of Lisp functions.
|
||||
-- The key is the operator and the value is the operator function.
|
||||
-- All functions 'e' are the operands, a list of Lisp atoms as pandoc.Inlines.
|
||||
-- Note: it doesn't support all the available Lisp functions, just the required
|
||||
-- for this exercise.
|
||||
local operators = {
|
||||
-- Lisp addition function.
|
||||
-- The operands are looped, converted to a number and added to the result.
|
||||
-- The result is a number that is converted to pandoc.Str because Pandoc
|
||||
-- structure only accepts Pandoc elements.
|
||||
["+"] = function (e)
|
||||
local result = 0
|
||||
e:walk {
|
||||
Str = function (str)
|
||||
local num = tonumber(pandoc.utils.stringify(str))
|
||||
result = result + num
|
||||
end
|
||||
}
|
||||
return pandoc.Str(result)
|
||||
end,
|
||||
-- Lisp list function.
|
||||
-- Operands are already in a list, so it returns the same.
|
||||
["list"] = function (e) return e end,
|
||||
-- Lisp first function.
|
||||
-- Since operands are already in a list, it returns the first element.
|
||||
["first"] = function (e) return e[1] end,
|
||||
}
|
||||
|
||||
-- Transforms lists to spans.
|
||||
-- Each Lisp list turns to Pandoc Span.
|
||||
-- @param ... pandoc.Inline: Lisp atoms are treated as Pandoc Inline.
|
||||
local function embed (...)
|
||||
local els = table.pack(...)
|
||||
return pandoc.Span(els)
|
||||
end
|
||||
|
||||
-- Evals Lisp list.
|
||||
-- The first list element is the operator for evaluation.
|
||||
-- @param list pandoc.Inlines: Lisp list as pandoc.Inlines.
|
||||
-- @return pandoc.Span or pandoc.Str: Evaluation result.
|
||||
local function eval_list(list)
|
||||
local operator = pandoc.utils.stringify(list[1])
|
||||
list:remove(1)
|
||||
-- When first atom is a number and there aren't other atoms.
|
||||
if operator:match("[%d]") and #list == 0 then
|
||||
return operator
|
||||
-- When first atom is not a number.
|
||||
-- Note: this assumes that the function is implemented in 'operators'.
|
||||
elseif operator:match("[%D]") then
|
||||
return operators[operator](list)
|
||||
-- Rest of cases.
|
||||
-- For example, when first atom is a number but it has other atoms.
|
||||
else
|
||||
print("ERROR: " .. operator .. " is not a function")
|
||||
sys.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
-- Evals Lisp atom.
|
||||
-- When atom is in a list, it doesn't get affected; otherwise, all the
|
||||
-- non digit elements are eliminated.
|
||||
-- @param atom pandoc.Str: Lisp atom as pandoc.Str.
|
||||
-- @param in_list boolean: Indicates if atom is in a list or not.
|
||||
-- @return atom pandoc.Str: Lisp atom.
|
||||
local function eval_atom(atom, in_list)
|
||||
if not in_list then
|
||||
atom = pandoc.utils.stringify(atom)
|
||||
atom = atom:gsub("[%D]", "")
|
||||
-- Note: Pandoc content empty structures are ignored on conversion, this
|
||||
-- enables expressions like '+ 1 2' be '1 2'.
|
||||
atom = pandoc.Str(atom)
|
||||
end
|
||||
return atom
|
||||
end
|
||||
|
||||
-- Formats code.
|
||||
-- Adds and space between each pandoc.Str (Lisp atom).
|
||||
-- @param res pandoc.Plain: Lisp eval result.
|
||||
-- @return res string: Formatted result.
|
||||
local function format(res)
|
||||
local res = res:walk {
|
||||
Str = function (str) return pandoc.Inlines{str, pandoc.Space()} end
|
||||
}
|
||||
return pandoc.utils.stringify(res)
|
||||
end
|
||||
|
||||
-- Evals code.
|
||||
-- The code consists in a table of pandoc.Plain elements,
|
||||
-- each pandoc.Plain is a table of pandoc.Span or pandoc.Str,
|
||||
-- so the equivalency with Lisp is:
|
||||
-- pandoc.Plain => Lisp expression
|
||||
-- pandoc.Span => Lisp list
|
||||
-- pandoc.Str => Lisp atom
|
||||
-- According to the children Pandoc element of pandoc.Plain, this
|
||||
-- function deals with Lisp lists or Lisp atoms. The last walk is just for
|
||||
-- formatting, since it separates atoms with an space.
|
||||
-- @param code pandoc.Plain: Lisp expression as pandoc.Plain.
|
||||
-- @return res string: Result of evaluation.
|
||||
local function eval(code)
|
||||
local res = code:walk {
|
||||
Plain = function (plain)
|
||||
local in_span = plain.content[1].tag == "Span"
|
||||
return plain:walk {
|
||||
Span = function (span) return eval_list(span.content) end,
|
||||
Str = function (str) return eval_atom(str, in_span) end
|
||||
}
|
||||
end
|
||||
}
|
||||
return format(res)
|
||||
end
|
||||
|
||||
-- Grammar for Pandoc parser that converts:
|
||||
-- Lisp expression => pandoc.Plain
|
||||
-- Lisp list => pandoc.Span
|
||||
|
@ -138,18 +34,32 @@ G = P{
|
|||
Atom = space + V"Word";
|
||||
Word = word^1 / pandoc.Str;
|
||||
}
|
||||
]]--
|
||||
|
||||
-- Evals Lisp code
|
||||
-- @param code string: code to evaluate
|
||||
-- @return table: code evaluated as a table of lines
|
||||
local function eval(code)
|
||||
local res = {}
|
||||
local cmd = fennel .. " -e '" .. code .. "' 2>&1"
|
||||
local handle = io.popen(cmd)
|
||||
for line in handle:lines() do
|
||||
table.insert(res, line)
|
||||
end
|
||||
handle:close()
|
||||
return res
|
||||
end
|
||||
|
||||
return {
|
||||
{
|
||||
CodeBlock = function (block)
|
||||
if block.classes:includes("eval") then
|
||||
local raw = block.text
|
||||
local code = lpeg.match(G, raw)
|
||||
local res = eval(code)
|
||||
print("⚙️ ", raw)
|
||||
print("", "=>", res)
|
||||
local res = eval(raw)
|
||||
print("", "=>", res[1])
|
||||
if block.classes:includes("replace") then
|
||||
return pandoc.CodeBlock(res, {code=raw})
|
||||
return pandoc.CodeBlock(table.concat(res, "\n"), {code=raw})
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# Test 2
|
||||
|
||||
This is written in MD format.
|
||||
|
||||
Evals and replaces:
|
||||
|
||||
``` {.eval .replace}
|
||||
(list 1 (+ 2 3) 9)
|
||||
```
|
|
@ -11,5 +11,5 @@ Does nothing:
|
|||
Evals:
|
||||
|
||||
``` eval
|
||||
(first (list 1 (+ 2 3) 9))
|
||||
(+ 1 2 3)
|
||||
```
|
|
@ -0,0 +1,15 @@
|
|||
# Test 2
|
||||
|
||||
This is written in MD format.
|
||||
|
||||
Evals and replaces:
|
||||
|
||||
``` {.eval .replace}
|
||||
(.. "hello" " world")
|
||||
```
|
||||
|
||||
Fails and replaces with error:
|
||||
|
||||
``` {.eval .replace}
|
||||
(FAIL! "hello" " world")
|
||||
```
|
|
@ -8,11 +8,11 @@ Evals:
|
|||
.. code::
|
||||
:class: eval
|
||||
|
||||
(+ 2 3)
|
||||
(+ 4 5 6)
|
||||
|
||||
Evals and replaces:
|
||||
|
||||
.. code::
|
||||
:class: eval replace
|
||||
|
||||
(first (list 9))
|
||||
(+ 4 5 6)
|
|
@ -4,11 +4,11 @@ This is written in ORG format.
|
|||
Evals:
|
||||
|
||||
#+begin_src eval
|
||||
(+ 2 3 9)
|
||||
(+ 7 8 9)
|
||||
#+end_src
|
||||
|
||||
Fails:
|
||||
|
||||
#+begin_src eval replace
|
||||
(list (+ 2 3 9))
|
||||
(+ 7 8 9)
|
||||
#+end_src
|
Loading…
Reference in New Issue