Skip to content

Instantly share code, notes, and snippets.

@intelfx
Last active January 14, 2016 05:33
Show Gist options
  • Save intelfx/203bb9b9eeb03396f621 to your computer and use it in GitHub Desktop.
Save intelfx/203bb9b9eeb03396f621 to your computer and use it in GitHub Desktop.
LORCODE writer for Pandoc
-- This is a custom writer for pandoc for http://linux.org.ru
-- markup language. It has been dirty-hacked from the sample custom
-- writer which produced HTML output.
--
-- Invoke with: pandoc -t sample.lua
--
-- Note: you need not have lua installed on your system to use this
-- custom writer. However, if you do have lua installed, you can
-- use it to test changes to the script. 'lua sample.lua' will
-- produce informative error messages if your code contains
-- syntax errors.
-- Run cmd on a temporary file containing inp and return result.
local function pipe(cmd, inp)
local tmp = os.tmpname()
local tmph = io.open(tmp, "w")
tmph:write(inp)
tmph:close()
local outh = io.popen(cmd .. " " .. tmp,"r")
local result = outh:read("*all")
outh:close()
os.remove(tmp)
return result
end
-- Table to store footnotes, so they can be included at the end.
local notes = {}
-- Blocksep is used to separate block elements.
function Blocksep()
return "\n"
end
-- This function is called once for the whole document. Parameters:
-- body is a string, metadata is a table, variables is a table.
-- This gives you a fragment. You could use the metadata table to
-- fill variables in a custom lua template. Or, pass `--template=...`
-- to pandoc, and pandoc will add do the template processing as
-- usual.
function Doc(body, metadata, variables)
local buffer = {}
local function add(s)
table.insert(buffer, s)
end
add(body)
if #notes > 0 then
add('[list=1]')
for _,note in pairs(notes) do
add(note)
end
add('[/list]')
end
return table.concat(buffer,'\n')
end
-- The functions that follow render corresponding pandoc elements.
-- s is always a string, attr is always a table of attributes, and
-- items is always an array of strings (the items in a list).
-- Comments indicate the types of other variables.
function Str(s)
return s
end
function Space()
return " "
end
function LineBreak()
return "[br]\n"
end
function Emph(s)
return "[i]" .. s .. "[/i]"
end
function Strong(s)
return "[b]" .. s .. "[/b]"
end
function Strikeout(s)
return '[s]' .. s .. '[/s]'
end
function __Underline(s)
return '[u]' .. s .. '[/u]'
end
function Link(s, src, tit)
return "[url=" .. src .. "]" .. s .. "[/url]"
end
function Image(s, src, tit)
return Link(s,src,tit)
end
function Code(s, attr)
return "[inline]" .. s .. "[/inline]"
end
function Note(s)
local num = #notes + 1
-- add a list item with the note to the note table.
table.insert(notes, '[*]' .. s)
-- return the footnote reference, linked to the note.
return '[[' .. num .. ']]'
end
function Plain(s)
return s
end
function Para(s)
return s .. Blocksep()
end
-- lev is an integer, the header level.
function Header(lev, s, attr)
if lev == 1 then
return Strong(__Underline(s)) .. Blocksep()
elseif lev == 2 then
return Strong(s) .. Blocksep()
elseif lev >= 3 then
return Emph(s) .. Blocksep()
end
end
function BlockQuote(s)
return '> ' .. s:gsub('%s*$', ''):gsub('\n', '\n> ') .. Blocksep()
end
function CodeBlock(s, attr)
-- If code block has class 'dot', pipe the contents through dot
-- and base64, and include the base64-encoded png as a data: URL.
if attr.class and string.match(' ' .. attr.class .. ' ',' dot ') then
local png = pipe("base64", pipe("dot -Tpng", s))
return Image('(dot graph)', 'data:image/png;base64,' .. png, '')
-- otherwise treat as code (one could pipe through a highlighter)
else
if attr.class and (attr.class ~= "") then
return '[code=' .. attr.class .. ']\n' .. s .. '\n[/code]' .. Blocksep()
else
return '[code]\n' .. s .. '\n[/code]' .. Blocksep()
end
end
end
function BulletList(items)
local buffer = {}
for _, item in pairs(items) do
table.insert(buffer, "[*] " .. item)
end
return "[list]\n" .. table.concat(buffer, "\n") .. "\n[/list]" .. Blocksep()
end
function OrderedList(items)
local buffer = {}
for _, item in pairs(items) do
table.insert(buffer, "[*] " .. item)
end
return "[list=1]\n" .. table.concat(buffer, "\n") .. "\n[/list]" .. Blocksep()
end
-- inspect = require ('inspect')
-- function _(arg)
-- print(inspect(arg))
-- end
-- Caption is a string, aligns is an array of strings,
-- widths is an array of floats, headers is an array of
-- strings, rows is an array of arrays of strings.
function Table(caption, aligns, widths, headers, rows)
local utf8 = require ('utf8')
-- This is a rewrite of http://github.com/ozh/ascii-tables
local cTL, cTM, cTR
local cML, cMM, cMR
local cBL, cBM, cBR
local cH, cV
local sL, sM, sR
--
--
--
cTL = "+";
cTM = "+";
cTR = "+";
cML = "+";
cMM = "+";
cMR = "+";
cBL = "+";
cBM = "+";
cBR = "+";
cH = "=";
cV = "|";
sL = "+";
sM = "-";
sR = "+";
--
--
--
-- compute widths of columns
local column_lengths = {}
for _, row in pairs(rows) do
for col_nr, cell in pairs(row) do
local length = utf8.width(cell)
if not column_lengths[col_nr] or column_lengths[col_nr] < length then
column_lengths[col_nr] = length
end
end
end
-- compute whether we have headers
local empty_header = true
for i, h in pairs(headers) do
if h ~= "" then
empty_header = false
break
end
end
-- output buffer
local buffer = {}
local function add(s)
table.insert(buffer, s)
end
local function add_separator_row(left, fill, middle, right)
local line = left
for i=1, #column_lengths do
line = line .. string.rep(fill, column_lengths[i] + 2) .. ((i < #column_lengths) and middle or right)
end
add (line)
end
local function add_text (left, cells, middle, right)
local line = left
for i=1, #column_lengths do
local padding = column_lengths[i] - utf8.width(cells[i])
local text
if aligns[i] == 'AlignRight' then
text = string.rep(' ', padding) .. cells[i]
elseif aligns[i] == 'AlignCenter' then
local padding_left = math.floor (padding / 2)
local padding_right = padding - padding_left
text = string.rep(' ', padding_left) .. cells[i] .. string.rep(' ', padding_right)
else -- if aligns[i] == 'AlignLeft'
text = cells[i] .. string.rep(' ', padding)
end
line = line .. " " .. text .. " " .. ((i < #column_lengths) and middle or right)
end
add (line)
end
-- header and header separator (if exists)
if not empty_header then
add_separator_row(cTL, cH, cTM, cTR)
add_text(cV, headers, cV, cV)
add_separator_row(cML, cH, cMM, cMR)
else
add_separator_row(cTL, sM, cTM, cTR)
end
-- data rows
for i, row in pairs(rows) do
add_text(cV, row, cV, cV)
if i < #rows then
add_separator_row(sL, sM, sR, sR)
else
add_separator_row(cBL, cH, cBM, cBR)
end
end
return ((caption ~= "") and Header(3, caption) or "") .. "[pre]\n" .. table.concat(buffer, '\n') .. "\n[/pre]" .. Blocksep()
end
function DoubleQuoted(s)
return "«" .. s .. "»"
end
-- The following code will produce runtime warnings when you haven't defined
-- all of the functions you need for the custom writer, so it's useful
-- to include when you're working on a writer.
local meta = {}
meta.__index =
function(_, key)
io.stderr:write(string.format("WARNING: Undefined function '%s'\n",key))
return function() return "" end
end
setmetatable(_G, meta)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment