Almost all block validations

This commit is contained in:
perro tuerto 2023-03-20 18:28:32 -07:00
parent 2ec9e0bd4b
commit ddb9c68e82
10 changed files with 368 additions and 109 deletions

189
dist/lin.lua vendored
View File

@ -6262,7 +6262,7 @@ local fnl = mod
local nat = {} local nat = {}
function nat.parse(str) function nat.get(str)
return str return str
end end
@ -6270,15 +6270,114 @@ end
-- Types functions extensions -- Types functions extensions
-- Changes newlines so everything is in one line
function string:linearize() function string:linearize()
return self:gsub("\n", "\\\\n") return self:gsub("\n", "\\\\n")
end end
-- Checks if is empty
function string:isempty()
return self == ''
end
-- Checks if is directory
function string:isdir()
if self ~= "." and os.rename(self, self) == nil then
return false
end
return true
end
-- Variable for all literate stuff -- Variable for all literate stuff
local lit = {} local lit = {}
-- Error collector -- Status indicator
lit.e = {} lit.status = true
-- Valid metadata structure
lit.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",
},
}
function lit.getmetaval(meta, key)
local mtype = pandoc.utils.type(meta[key])
local mval = meta[key]
if mtype == "Inlines" then
mval = pandoc.utils.stringify(mval)
mtype = pandoc.utils.type(mval)
end
return mval, mtype
end
function lit.checkmetatype(type, meta, key)
local mval, mtype = lit.getmetaval(meta, key)
local err = ""
if type ~= mtype then
if type == "path" then
if not(pandoc.path.directory(mval):isdir()) then
err = "Invalid path '" .. mval .. "' in key '" .. key .. "'"
end
else
err = "Invalid type '" .. mtype .. "' in key '" .. key .. "'"
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmetatable(table, meta, key)
local mval = lit.getmetaval(meta, key)
local err = ""
for _, pattern in pairs(table) do
if mval:match("^" .. pattern .. "$") then
err = ""
break
else
err = "Invalid value '" .. mval .. "' in key '" .. key .. "'"
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmeta(meta, kind)
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil then
if kind == "mandatory" then
lit.puts("Key '" .. key .. "' not found", "ERROR")
end
else
if type(val) == "table" then
lit.checkmetatable(val, meta, key)
else
lit.checkmetatype(val, meta, key)
end
end
end
end
function lit.setmeta(meta)
lit.checkmeta(meta, "mandatory")
lit.checkmeta(meta, "optional")
-- TODO checks for 1) extra keys and 2) duplicates
-- Set defaults if lit.status = true
return meta
end
-- Grammars -- Grammars
lit.g = {} lit.g = {}
@ -6289,9 +6388,9 @@ lit.space = lpeg.S" \t"
lit.anyspace = lpeg.S" \t\r\n" lit.anyspace = lpeg.S" \t\r\n"
lit.spot = (1 - lit.anyspace) lit.spot = (1 - lit.anyspace)
lit.any = (lit.spot + lit.space) lit.any = (lit.spot + lit.space)
lit.yaml_header = lit.space^0 * lpeg.P"---" * lit.space^0 * lit.newline lit.yamlheader = lit.space^0 * lpeg.P"---" * lit.space^0 * lit.newline
lit.yaml_footer = lit.space^0 * lpeg.P"..." * lit.space^0 * lit.newline^-1 lit.yamlfooter = lit.space^0 * lpeg.P"..." * lit.space^0 * lit.newline^-1
lit.yaml_body = -lit.yaml_footer * lit.any^0 * lit.newline lit.yamlbody = -lit.yamlfooter * lit.any^0 * lit.newline
lit.id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0 lit.id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
lit.ref = lpeg.P"#" * lit.id lit.ref = lpeg.P"#" * lit.id
@ -6299,7 +6398,7 @@ lit.ref = lpeg.P"#" * lit.id
lit.g.block = lpeg.P { lit.g.block = lpeg.P {
"Block"; "Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name"); Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(lit.yaml_header * lit.yaml_body^0 * lit.yaml_footer); YAML = lpeg.C(lit.yamlheader * lit.yamlbody^0 * lit.yamlfooter);
Code = lpeg.C((lit.any + lit.newline)^0); Code = lpeg.C((lit.any + lit.newline)^0);
} }
@ -6320,8 +6419,17 @@ function lit.eval(code)
end end
]]-- ]]--
function lit.debug_level(str) function lit.assert(e, status)
if status == false then
lit.puts("Aborted due previous errors", "ERROR")
os.exit(1)
end
return e
end
function lit.debuglevel(str)
if str == "ERROR" then if str == "ERROR" then
lit.status = false
return 2 return 2
elseif str == "WARNING" then elseif str == "WARNING" then
return 1 return 1
@ -6330,67 +6438,80 @@ function lit.debug_level(str)
end end
end end
function lit.puts(msg, level) function lit.puts(msg, kind)
local verbosity = lit.debug_level(PANDOC_STATE.verbosity) local kind = kind or "INFO"
level = lit.debug_level(level) local verbosity = lit.debuglevel(PANDOC_STATE.verbosity)
level = lit.debuglevel(kind)
if level >= verbosity then if level >= verbosity then
print("[" .. PANDOC_STATE.verbosity .. "] " .. msg) print("[" .. kind .. "] [LIT] " .. msg)
end end
end end
function lit.add_error(err) function lit.parseyaml(rawyaml)
-- TODO: avoid popen?
end local yaml = io.popen("pandoc -t json <<<\"" .. rawyaml .. "\" 2>&1")
function lit.check_yaml(raw_yaml)
local yaml = io.popen("pandoc -t json <<<\"" .. raw_yaml .. "\" 2>&1")
if yaml:read("*l"):sub(1, 1) == "{" then if yaml:read("*l"):sub(1, 1) == "{" then
yaml = pandoc.read(raw_yaml) yaml = pandoc.read(rawyaml).meta
return yaml if pandoc.utils.stringify(yaml):isempty() then
lit.puts("Empty YAML", "ERROR")
else
return lit.setmeta(yaml)
end
else else
lit.add_error(err) lit.puts("Invalid YAML", "ERROR")
return nil return nil
end end
end end
function lit.check_block(parsed) function lit.parseblock(parsed)
lit.puts("Parsing " .. table.concat(parsed, "\n"):linearize()) lit.puts("Parsing " .. table.concat(parsed, "\n"):linearize())
local yaml = lit.check_yaml(parsed[1]) local yaml = lit.parseyaml(parsed[1])
-- TODO: parsecode
return "TODO" return "TODO"
end end
function lit.getblock(codeblock)
function lit.parse_blocks(codeblock)
local parsed = lpeg.match(lit.g.block, codeblock.text) local parsed = lpeg.match(lit.g.block, codeblock.text)
local valid = (parsed ~= nil and lit.check_block(parsed) or false) parsed = (parsed ~= nil and lit.parseblock(parsed) or parsed)
return codeblock return codeblock, lit.status
end end
function lit.parse_inserts(code) function lit.getinsert(code)
-- print(code) return code, lit.status
return code
end end
------------------------------------ PANDOC ----------------------------------- ------------------------------------ PANDOC -----------------------------------
local litstatus = true
return { return {
{ {
-- Parses and evals literate blocks -- Parses and evals literate blocks
CodeBlock = function(codeblock) CodeBlock = function(codeblock)
return lit.parse_blocks(codeblock) codeblock, litstatus = lit.getblock(codeblock)
return codeblock
end,
-- Asserts literate
Pandoc = function(pandoc)
return lit.assert(pandoc, litstatus)
end, end,
}, },
{ {
-- Parses literate inserts -- Parses literate inserts
Code = function(code) Code = function(code)
return lit.parse_inserts(code) code, litstatus = lit.getinsert(code)
return code
end, end,
-- Asserts literate
Pandoc = function(pandoc)
return lit.assert(pandoc, litstatus)
end,
}, {
-- Parses and evals natural programming -- Parses and evals natural programming
-- TODO -- TODO
Inlines = function(inlines) Inlines = function(inlines)
md = pandoc.utils.stringify(inlines) md = pandoc.utils.stringify(inlines)
md = nat.parse(md) md = nat.get(md)
return inlines return inlines
end, end,
} }

View File

@ -3,7 +3,7 @@
# Variables # Variables
FILTER=dist/lin.lua FILTER=dist/lin.lua
FILES="tests/*.md" FILES="tests/*.md"
VERBOSE=false VERBOSE=""
AST=false AST=false
CMD="sh $0" CMD="sh $0"
@ -36,7 +36,7 @@ get_result () {
# Checks options # Checks options
while getopts ':vah' opt; do while getopts ':vah' opt; do
case "$opt" in case "$opt" in
v) VERBOSE=true ; shift ;; v) VERBOSE="--verbose" ; shift ;;
a) AST=true ; shift ;; a) AST=true ; shift ;;
h) echo_help ;; h) echo_help ;;
?) echo "ERROR: only -v, -a or -h is allowed" ; exit 1 ;; ?) echo "ERROR: only -v, -a or -h is allowed" ; exit 1 ;;
@ -61,11 +61,11 @@ clear && echo "🐾 Starting tests"
for file in $FILES; do for file in $FILES; do
echo "⚗️ $file:" echo "⚗️ $file:"
expectation=${file:6:4} expectation=${file:6:4}
ast=$(pandoc -L $FILTER -t json -o tmp.json $file) ast=$(pandoc $VERBOSE -L $FILTER -t json -o tmp.json $file)
result=$(get_result $? $file) result=$(get_result $? $file)
echo " Expect: $expectation" echo " Expect: $expectation"
echo " Result: $result" echo " Result: $result"
[ "$VERBOSE" = true ] && echo -e "$ast" [ "$VERBOSE" = "--verbose" ] && echo -e "$ast"
[ "$AST" = true ] && pandoc -L $FILTER -t native $file [ "$AST" = true ] && pandoc -L $FILTER -t native $file
rm tmp.json rm tmp.json
done done

View File

@ -2,15 +2,114 @@
-- Types functions extensions -- Types functions extensions
-- Changes newlines so everything is in one line
function string:linearize() function string:linearize()
return self:gsub("\n", "\\\\n") return self:gsub("\n", "\\\\n")
end end
-- Checks if is empty
function string:isempty()
return self == ''
end
-- Checks if is directory
function string:isdir()
if self ~= "." and os.rename(self, self) == nil then
return false
end
return true
end
-- Variable for all literate stuff -- Variable for all literate stuff
local lit = {} local lit = {}
-- Error collector -- Status indicator
lit.e = {} lit.status = true
-- Valid metadata structure
lit.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",
},
}
function lit.getmetaval(meta, key)
local mtype = pandoc.utils.type(meta[key])
local mval = meta[key]
if mtype == "Inlines" then
mval = pandoc.utils.stringify(mval)
mtype = pandoc.utils.type(mval)
end
return mval, mtype
end
function lit.checkmetatype(type, meta, key)
local mval, mtype = lit.getmetaval(meta, key)
local err = ""
if type ~= mtype then
if type == "path" then
if not(pandoc.path.directory(mval):isdir()) then
err = "Invalid path '" .. mval .. "' in key '" .. key .. "'"
end
else
err = "Invalid type '" .. mtype .. "' in key '" .. key .. "'"
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmetatable(table, meta, key)
local mval = lit.getmetaval(meta, key)
local err = ""
for _, pattern in pairs(table) do
if mval:match("^" .. pattern .. "$") then
err = ""
break
else
err = "Invalid value '" .. mval .. "' in key '" .. key .. "'"
end
end
if not(err:isempty()) then lit.puts(err, "ERROR") end
end
function lit.checkmeta(meta, kind)
for key, val in pairs(lit.metastruct[kind]) do
if meta[key] == nil then
if kind == "mandatory" then
lit.puts("Key '" .. key .. "' not found", "ERROR")
end
else
if type(val) == "table" then
lit.checkmetatable(val, meta, key)
else
lit.checkmetatype(val, meta, key)
end
end
end
end
function lit.setmeta(meta)
lit.checkmeta(meta, "mandatory")
lit.checkmeta(meta, "optional")
-- TODO checks for 1) extra keys and 2) duplicates
-- Set defaults if lit.status = true
return meta
end
-- Grammars -- Grammars
lit.g = {} lit.g = {}
@ -21,9 +120,9 @@ lit.space = lpeg.S" \t"
lit.anyspace = lpeg.S" \t\r\n" lit.anyspace = lpeg.S" \t\r\n"
lit.spot = (1 - lit.anyspace) lit.spot = (1 - lit.anyspace)
lit.any = (lit.spot + lit.space) lit.any = (lit.spot + lit.space)
lit.yaml_header = lit.space^0 * lpeg.P"---" * lit.space^0 * lit.newline lit.yamlheader = lit.space^0 * lpeg.P"---" * lit.space^0 * lit.newline
lit.yaml_footer = lit.space^0 * lpeg.P"..." * lit.space^0 * lit.newline^-1 lit.yamlfooter = lit.space^0 * lpeg.P"..." * lit.space^0 * lit.newline^-1
lit.yaml_body = -lit.yaml_footer * lit.any^0 * lit.newline lit.yamlbody = -lit.yamlfooter * lit.any^0 * lit.newline
lit.id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0 lit.id = lpeg.R("az", "AZ") * lpeg.R("az", "AZ", "09")^0
lit.ref = lpeg.P"#" * lit.id lit.ref = lpeg.P"#" * lit.id
@ -31,7 +130,7 @@ lit.ref = lpeg.P"#" * lit.id
lit.g.block = lpeg.P { lit.g.block = lpeg.P {
"Block"; "Block";
Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name"); Block = lpeg.Ct(lpeg.V"YAML" * lpeg.V"Code", "name");
YAML = lpeg.C(lit.yaml_header * lit.yaml_body^0 * lit.yaml_footer); YAML = lpeg.C(lit.yamlheader * lit.yamlbody^0 * lit.yamlfooter);
Code = lpeg.C((lit.any + lit.newline)^0); Code = lpeg.C((lit.any + lit.newline)^0);
} }
@ -52,8 +151,17 @@ function lit.eval(code)
end end
]]-- ]]--
function lit.debug_level(str) function lit.assert(e, status)
if status == false then
lit.puts("Aborted due previous errors", "ERROR")
os.exit(1)
end
return e
end
function lit.debuglevel(str)
if str == "ERROR" then if str == "ERROR" then
lit.status = false
return 2 return 2
elseif str == "WARNING" then elseif str == "WARNING" then
return 1 return 1
@ -62,46 +170,46 @@ function lit.debug_level(str)
end end
end end
function lit.puts(msg, level) function lit.puts(msg, kind)
local verbosity = lit.debug_level(PANDOC_STATE.verbosity) local kind = kind or "INFO"
level = lit.debug_level(level) local verbosity = lit.debuglevel(PANDOC_STATE.verbosity)
level = lit.debuglevel(kind)
if level >= verbosity then if level >= verbosity then
print("[" .. PANDOC_STATE.verbosity .. "] " .. msg) print("[" .. kind .. "] [LIT] " .. msg)
end end
end end
function lit.add_error(err) function lit.parseyaml(rawyaml)
-- TODO: avoid popen?
end local yaml = io.popen("pandoc -t json <<<\"" .. rawyaml .. "\" 2>&1")
function lit.check_yaml(raw_yaml)
local yaml = io.popen("pandoc -t json <<<\"" .. raw_yaml .. "\" 2>&1")
if yaml:read("*l"):sub(1, 1) == "{" then if yaml:read("*l"):sub(1, 1) == "{" then
yaml = pandoc.read(raw_yaml) yaml = pandoc.read(rawyaml).meta
return yaml if pandoc.utils.stringify(yaml):isempty() then
lit.puts("Empty YAML", "ERROR")
else
return lit.setmeta(yaml)
end
else else
lit.add_error(err) lit.puts("Invalid YAML", "ERROR")
return nil return nil
end end
end end
function lit.check_block(parsed) function lit.parseblock(parsed)
lit.puts("Parsing " .. table.concat(parsed, "\n"):linearize()) lit.puts("Parsing " .. table.concat(parsed, "\n"):linearize())
local yaml = lit.check_yaml(parsed[1]) local yaml = lit.parseyaml(parsed[1])
-- TODO: parsecode
return "TODO" return "TODO"
end end
function lit.getblock(codeblock)
function lit.parse_blocks(codeblock)
local parsed = lpeg.match(lit.g.block, codeblock.text) local parsed = lpeg.match(lit.g.block, codeblock.text)
local valid = (parsed ~= nil and lit.check_block(parsed) or false) parsed = (parsed ~= nil and lit.parseblock(parsed) or parsed)
return codeblock return codeblock, lit.status
end end
function lit.parse_inserts(code) function lit.getinsert(code)
-- print(code) return code, lit.status
return code
end end
return lit return lit

View File

@ -2,7 +2,7 @@
local nat = {} local nat = {}
function nat.parse(str) function nat.get(str)
return str return str
end end

View File

@ -1,22 +1,35 @@
------------------------------------ PANDOC ----------------------------------- ------------------------------------ PANDOC -----------------------------------
local litstatus = true
return { return {
{ {
-- Parses and evals literate blocks -- Parses and evals literate blocks
CodeBlock = function(codeblock) CodeBlock = function(codeblock)
return lit.parse_blocks(codeblock) codeblock, litstatus = lit.getblock(codeblock)
return codeblock
end,
-- Asserts literate
Pandoc = function(pandoc)
return lit.assert(pandoc, litstatus)
end, end,
}, },
{ {
-- Parses literate inserts -- Parses literate inserts
Code = function(code) Code = function(code)
return lit.parse_inserts(code) code, litstatus = lit.getinsert(code)
return code
end, end,
-- Asserts literate
Pandoc = function(pandoc)
return lit.assert(pandoc, litstatus)
end,
}, {
-- Parses and evals natural programming -- Parses and evals natural programming
-- TODO -- TODO
Inlines = function(inlines) Inlines = function(inlines)
md = pandoc.utils.stringify(inlines) md = pandoc.utils.stringify(inlines)
md = nat.parse(md) md = nat.get(md)
return inlines return inlines
end, end,
} }

View File

@ -16,24 +16,11 @@ Empty YAML:
... ...
1 + 2 + 3 1 + 2 + 3
Empty code:
---
id: fn1
...
Empty YAML and code: Empty YAML and code:
--- ---
... ...
Misses arg:
---
id: fn1
...
#a + #b
Misses id: Misses id:
--- ---
@ -48,7 +35,7 @@ Wrong id (doesn't starts with `%a`):
... ...
1 + 2 + 3 1 + 2 + 3
Wrong id (doesn't follows with `%w`): Wrong id (doesn't follows with `[_%w]`):
--- ---
id: f-1 id: f-1
@ -62,17 +49,23 @@ Wrong id (more than 1 word):
... ...
1 + 2 + 3 1 + 2 + 3
Uknown cmd: Invalid value:
--- ---
id: fn1 id: fn1
cmd: pyton -E -X utf8 shift: "true"
args:
n: 2
... ...
#n + #n Invalid shift value
Unknown key: Invalid path:
---
id: fn1
dump: invalid/path.txt
...
Invalid dump value
Extra key:
--- ---
id: fn1 id: fn1
@ -83,13 +76,18 @@ Unknown key:
... ...
(* #a #b) (* #a #b)
Invalid value: Empty code:
--- ---
id: fn1 id: fn1
shift: "true"
... ...
"The literate block is shifted by its eval result."
Misses arg:
---
id: fn1
...
#a + #b
Invalid code: Invalid code:
@ -98,12 +96,22 @@ Invalid code:
... ...
false + false false + false
Uknown cmd:
---
id: fn1
cmd: piton -E -X utf8
args:
n: 2
...
#n + #n
Infinite loop: Infinite loop:
--- ---
id: fn1 id: fn1
args: args:
x: 1 x: 1
... ...
#fn1(2) * #x #fn1(2) * #x

View File

@ -54,7 +54,7 @@ With cmd (ignores lang):
... ...
#n + #n #n + #n
With shift (nil by default): With shift:
--- ---
id: fn6 id: fn6
@ -62,7 +62,7 @@ With shift (nil by default):
... ...
"The literate block is shifted by its eval result." "The literate block is shifted by its eval result."
With wipe (nil by default): With wipe:
--- ---
id: fn7 id: fn7
@ -70,7 +70,7 @@ With wipe (nil by default):
... ...
"This evals but it is wipe from doc." "This evals but it is wipe from doc."
With typed (nil by default): With typed:
--- ---
id: fn4 id: fn4
@ -81,7 +81,7 @@ With typed (nil by default):
... ...
#a * #b #a * #b
With link (nil by default) and alt (optional): With link and alt:
--- ---
id: fn8 id: fn8
@ -95,7 +95,7 @@ With link (nil by default) and alt (optional):
c -> a; c -> a;
} }
With img (nil by default) and alt (optional): With img and alt:
--- ---
id: fn9 id: fn9
@ -109,10 +109,20 @@ With img (nil by default) and alt (optional):
c -> a; c -> a;
} }
With inner function: With dump and quote:
--- ---
id: fn10 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: args:
x: 2 x: 2
... ...
@ -121,7 +131,7 @@ With inner function:
With inner function with args: With inner function with args:
--- ---
id: fn11 id: fn12
args: args:
y: 1 y: 1
z: 2 z: 2
@ -131,7 +141,7 @@ With inner function with args:
With inner inner function: With inner inner function:
--- ---
id: fn12 id: fn13
args: args:
a: 1 a: 1
... ...

File diff suppressed because one or more lines are too long

View File

@ -17,7 +17,7 @@ Override:
--- ---
id: fn3 id: fn3
args: args:
- a: 1 a: 1
... ...
7 + 8 + 9 7 + 8 + 9
@ -28,7 +28,7 @@ Override:
lang: python lang: python
cmd: python -E -X utf8 cmd: python -E -X utf8
args: args:
- n: 2 n: 2
... ...
#n * #n #n * #n
@ -36,6 +36,5 @@ Override:
--- ---
id: fn5 id: fn5
lang: echo
... ...
"Lang becomes cmd." "Lang becomes cmd."

View File

@ -1 +1 @@
{"pandoc-api-version":[1,23],"meta":{},"blocks":[{"t":"Header","c":[1,["block-override",[],[]],[{"t":"Str","c":"Block"},{"t":"Space"},{"t":"Str","c":"Override"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn1\n...\n1 + 2 + 3\n\n---\nid: fn1\n...\n4 + 5 + 6"]},{"t":"Header","c":[1,["block-with-unused-argument",[],[]],[{"t":"Str","c":"Block"},{"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...\n7 + 8 + 9"]},{"t":"Header","c":[1,["block-with-language-and-command",[],[]],[{"t":"Str","c":"Block"},{"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\nargs:\n- n: 2\n...\n#n * #n"]},{"t":"Header","c":[1,["block-with-unknown-language",[],[]],[{"t":"Str","c":"Block"},{"t":"Space"},{"t":"Str","c":"with"},{"t":"Space"},{"t":"Str","c":"Unknown"},{"t":"Space"},{"t":"Str","c":"Language"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn5\nlang: echo\n...\n\"Lang becomes cmd.\""]}]} {"pandoc-api-version":[1,23],"meta":{},"blocks":[{"t":"Header","c":[1,["block-override",[],[]],[{"t":"Str","c":"Block"},{"t":"Space"},{"t":"Str","c":"Override"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn1\n...\n1 + 2 + 3"]},{"t":"Para","c":[{"t":"Str","c":"Override:"}]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn1\n...\n4 + 5 + 6"]},{"t":"Header","c":[1,["block-with-unused-argument",[],[]],[{"t":"Str","c":"Block"},{"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...\n7 + 8 + 9"]},{"t":"Header","c":[1,["block-with-language-and-command",[],[]],[{"t":"Str","c":"Block"},{"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\nargs:\n n: 2\n...\n#n * #n"]},{"t":"Header","c":[1,["block-with-unknown-language",[],[]],[{"t":"Str","c":"Block"},{"t":"Space"},{"t":"Str","c":"with"},{"t":"Space"},{"t":"Str","c":"Unknown"},{"t":"Space"},{"t":"Str","c":"Language"}]]},{"t":"CodeBlock","c":[["",[],[]],"---\nid: fn5\n...\n\"Lang becomes cmd.\""]}]}