Traverse the pandoc AST; just like walk, but with element
contexts, more filter targets, and mutability semantics.
-
Mutability semantics: Jog embraces mutability. It is not necessary to
returna new element, modifications to the element are kept automatically.A filter to convert all strings to uppercase
{ Str = function (str) str.text = str.text:upper() return str end }could be written without the
returnwhen usingjog{ Str = function (str) str.text = str.text:upper() end } -
Context: Filter functions sometimes depend on the context in which the element is seen. Jog allows to enable element contexts by setting the
contextfield in the filter totrue. The context, i.e., a list of all parent elements, is passed to the filter function as a second argument.E.g., the below filter prints the type of all context elements to stdout:
local jog = require 'jog' --- Return the tag (e.g., `Str`) or type (e.g. `Meta`) of an element. local function tag_or_type (x) return x.t or pandoc.utils.type(x) end -- Lua filter to print the context of all Str elements. local print_context = { context = true, -- enable element contexts Str = function (inln, context) print(table.concat(context:map(tag_or_type), ', ')) end } function Pandoc (doc) return jog(doc, print_context) end
Running the above code on the Markdown document
Let's *jog*!would yieldPandoc, Blocks, Para, Inlines, Str Pandoc, Blocks, Para, Inlines, Emph, Inlines, Str -
More elements to filter on: Beside the normal
walkfilter functions,jogalso supports filtering onListelements and on table objects such asCell,Row,TableHead,TableFoot.-- count table cells local ncells = 0 local cellcounter = { Cell = function (cell) ncells = ncells + 1 end } jog(my_table, cellcounter) -- `ncells` now contains the number of cells
-
Performance: jog is written with a strong focus on performance. For example, let's consider an ad-hoc benchmark using a filter that counts the number of strings in a document with 36k words.
local nstr = 0 local filter = {Str = function (str) nstr = nstr + 1 end} function Pandoc (doc) runtime(doc:walk(filter)) runtime(jog(doc, filter)) end
Runtimes:
"walker" runtime :walk0.29 s jog0.17 s The relative difference can become even larger if multiple filters are applied on the same object in sequence.
However, it should also be noted that
jogis not always faster than:walk. There are filters for which:walkoutperformsjog, and vice versa. Jog performs particularly well when a lot of elements need to be touched, while:walkshines when the filtered elements are comparatively rare.
Download jog.lua and place it where pandoc's Lua
interpreter can find it, or install it with luarocks.
luarocks install --local jogAfter that, jog can be required as a normal other Lua library:
local jog = require 'jog'The library defines a function jog to traverse a given object:
jog.jog(ast_element, filter)For convenience, the library object itself can also be used as a
function, meaning the above can be shortened to jog(ast_element, filter).
Run jog.add_method([method_name]) to add a jog method to all
"joggable" pandoc AST Lua objects:
local jog = require 'jog'
jog.add_method()
pandoc.Pandoc{'Test'}:jog(my_filter)How to trot along the AST can be configured by setting fields in the filter.
-
context: setting this to a truthy value will pass the element context as a second argument to all filter functions.Example:
local filter = { context = true, Inline = function (inln, context) -- ... end, }
-
traverse: sets the traversal order. The normal traversal is depth-first. Set this totopdownto let nodes closer to the root be filtered first. Filter functions can returnfalseas a second return value to signal to jog that it should not act on subelements below the current node.Example:
local filter = { traverse = 'topdown', Inline = function (inln, context) -- ... return inln, false -- don't filter the subelements end, }