#!/usr/bin/env texlua -------------------------------------------------------------------------------- -- FILE: rst_helpers.lua -- USAGE: ./rst_helpers.lua -- DESCRIPTION: Complement to the reStructuredText parser -- AUTHOR: Philipp Gesang (Phg), -- VERSION: 1.0 -- CREATED: 07/09/10 01:03:08 CEST -------------------------------------------------------------------------------- -- local utf = unicode.utf8 local P, R, S, V, match = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match local C, Carg, Cb, Cc, Cg, Cmt, Cp, Cs, Ct = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct local helpers = {} helpers.table = {} helpers.cell = {} local patterns = {} do local p = patterns p.dash = P"-" p.equals = P"=" p.plus = P"+" p.bar = P"|" p.eol = P"\n" p.last = -P(1) p.celldelim = p.bar + p.plus p.cellcontent = (1 - p.celldelim) p.cell = p.celldelim * C((1 - p.celldelim)^1) * #p.celldelim p.cell_line = p.plus * p.dash^1 * #p.plus p.dashesonly = p.dash^1 * p.last p.cells = P{ [1] = "cells", cells = p.celldelim * (C(V"in_cell") * (V"matchwidth" * C(V"in_cell")) ^1), in_cell = p.cellcontent^1 + (p.dash - p.cellcontent)^1, matchwidth = Cmt(C(p.celldelim) * Carg(1), function(s,i,del, layout) --print("~~~~~~~~~~~~~~ NEXT MATCH ~~~~~~~~~~~~~~") local pos = 1 local lw = layout.widths for n=1, #lw do pos = pos + lw[n] + 1 --print(">",pos,i-1,i-1==pos) if (i - 1) == pos then return true end end return false end), } p.sep_line = p.plus * (p.dash^1 * p.plus)^1 * p.last p.sep_head = p.plus * (p.equals^1 * p.plus)^1 * p.last p.sep_part = ((1 - p.cell_line)^0 * p.cell_line) - p.sep_line p.new_row = p.sep_line + p.sep_head + p.sep_part p.whitespace = S" \t\v\r\n"^1 p.strip = p.whitespace^0 * C((1 - (p.whitespace * p.last))^1) * p.whitespace^0 * p.last end function helpers.cell.create(raw, n_row, n_col, continue) local p = patterns local cell = {} cell.stripped = raw and p.strip:match(raw) or "" cell.content = raw cell.width = raw and utf.len(raw) or 0 cell.bytes = raw and #raw or 0 cell.variant = "normal" -- [normal|separator|y_continue|x_continue] cell.pos = {} cell.pos.x = n_col cell.pos.y = n_row cell.span = {} cell.span.x = 1 cell.span.y = 1 cell.parent = nil return cell end function helpers.cell.get_x_span(content, layout, init) --print(#content,">"..content.."<") --print(layout.widths[init],">"..layout.slices[init].."<") local acc = 0 local lw = layout.widths for n=init, #lw do acc = acc + lw[n] + 1 --print(n, layout.slices[n], acc) if utf.len(content) + 1 == acc then return n - init end end return false end -- Extending a cell by 1 cell horizontally. function helpers.cell.add_x (cell) cell.span.x = cell.span.x + 1 end local function set_layout (line) local p = patterns local layout = {} local slice = Ct((p.plus * C(p.dash^1) * #p.plus)^1) layout.widths = {} layout.slices = {} for n, elm in ipairs(slice:match(line)) do layout.widths[n] = #elm layout.slices[n] = elm end return layout end function helpers.table.create(raw) local self = {} self.rows = {} local p = patterns self.resolve_parent = function(row, col) print(row, col) local cell = self.rows[row][col] local par_row, par_col if cell.parent then par_row, par_col = self.resolve_parent(cell.parent.y, cell.parent.x) else return par_row, par_col end end self.__init = function() local hc = helpers.cell self.layout = set_layout(raw[1]) local rowcount = 0 -- first get the correct horizontal spans local next_is_new_row = true for n, line in ipairs(raw) do -- caching the test results local sepline = p.sep_line:match(line) local sephead = p.sep_head:match(line) local seppart = p.sep_part:match(line) local newrow = sepline or sephead or seppart if newrow and n > 1 then if sephead then self.has_head = true end rowcount = rowcount + 1 end if not (sepline or sephead) then local row = {} row.newrow = (newrow or next_is_new_row) and true or false next_is_new_row = false local sl = self.layout local splitted = {p.cells:match(line, 1, sl)} local pos_layout, pos_line = 1, 1 local last = nil local ignore = {} ignore.n, ignore.parent = 0, nil while pos_layout <= #sl.slices do --local splitpos = splitted[pos_line] local splitpos = splitted[pos_layout] local width_layout = sl.widths[pos_layout] local span = 1 local this if ignore.n > 0 then ignore.n = ignore.n - 1 this = hc.create(false, n, pos_layout, true) row[pos_layout] = this this.parent = ignore.parent else local width_cell = utf.len(splitpos) if width_cell > width_layout then --print(splitpos, width_cell, sl.slices[pos_layout], width_layout) -- check the horizontal span span = span + hc.get_x_span(splitpos, sl, pos_layout) end pos_line = pos_line + span this = hc.create(splitpos, n, pos_layout, false) if p.dashesonly:match(splitpos) then this.variant = "separator" end this.span.x = span last = this row[pos_layout] = this ignore.n = span - 1 ignore.parent = ignore.n > 0 and { y = rowcount, x = pos_layout } or nil end pos_layout = pos_layout + 1 --print(">",this.pos.y,this.pos.x,this.span.y,this.span.x) --if this.span.y > 1 then --print(">"..this.content.."<") --end end self.rows[#self.rows+1] = row --print(#self.rows.."<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") --for i,j in ipairs(row) do print(i,j.content) end --print(row.newrow) else -- separator next_is_new_row = true end --print(string.format("[t-%2s,%2s]> %s", n, m, cell)) end --print("rc = "..rowcount, "nr = "..#self.rows) self.rowcount = rowcount if self.rowcount < #self.rows then -- some cells span vertically local has_partsep = function(row) for n, cell in ipairs(row) do if cell.variant == "separator" then return true end end return false end local dont_do_next = false for nr,row in ipairs(self.rows) do local ps = has_partsep(row) row.ignore = ps and true or false if ps then self.rows[nr+1].newrow = true end if (not row.newrow or ps) and not dont_do_next then --print(">>>>>>>>>> NEXT <<<<<<<<<<<<") --print(row.newrow and has_partsep(row)) -- continues last row either fully or partially for nc, cell in ipairs(row) do --print(nr, nc, cell.content) if cell.content == false then -- empty cell elseif cell.variant ~= "separator" then local par_row, par_col if cell.parent then par_row, par_col = self.resolve_parent(cell.parent.y, cell.parent.x) --print("old parent: ", par_row, par_col) else -- no previous span, setting parent to preceding row par_row, par_col = nr -1, nc --print("new parent: ", par_row, par_col) end print (nr, nc, cell.content, cell.parent, par_row, par_col) local parent = self.rows[par_row][par_col] --parent.span.y = parent.span.y + 1 parent.content = parent.content .. cell.content parent.stripped = parent.stripped .. " " .. cell.stripped if ps then local successor = self.rows[nr+1][nc] successor.parent = { x = par_col, y = par_row } parent.span.y = parent.span.y + 1 parent.content = parent.content .. successor.content parent.stripped = parent.stripped .. " " .. successor.stripped dont_do_next = true else dont_do_next = false end print(cell.content, parent.content) --add_y(cell) end end elseif dont_do_next then --row.ignore = true dont_do_next = false end end end -- vertical span handler end -- end __init self.__init() return self end return helpers