From d7996cadc2e117cc163291abe953dec15cee6826 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Tue, 7 Sep 2010 18:32:22 +0200 Subject: revised table implementation. helper module. --- rst_context.lua | 49 ++++++++-- rst_helpers.lua | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rst_parser.lua | 135 ++++++++++++-------------- 3 files changed, 397 insertions(+), 80 deletions(-) create mode 100644 rst_helpers.lua diff --git a/rst_context.lua b/rst_context.lua index 7603509..fb69dfd 100644 --- a/rst_context.lua +++ b/rst_context.lua @@ -467,24 +467,59 @@ function rst_context.table (str) ]] end -function rst_context.grid_table (str) - return [[ +function rst_context.grid_table (tab) + local head = [[ \\setupTABLE[c][first] [background=color, backgroundcolor=grey, style=\tt] -\\setupTABLE[c][each] [frame=off] -\\setupTABLE[r][each] [frame=off] +\\setupTABLE[c][each] [frame=on] +\\setupTABLE[r][each] [frame=on] \\bTABLE[split=repeat,option=stretch] \\bTABLEhead \\bTR - \\bTH Option \\eTH - \\bTH Description \\eTH + \\bTH first \\eTH + \\bTH second \\eTH + \\bTH third \\eTH + \\bTH fourth \\eTH \\eTR \\eTABLEhead \\bTABLEbody -]] .. str .. [[ +]] + local tail = [[ \\eTABLEbody \\eTABLE ]] + local test = "" + for i,r in ipairs(tab.rows) do + --if not r.ignore then + if r.newrow and not r.ignore then + --if r.newrow then + --test = test .. string.format("%2s>%s\n", i, #j) + --print(i, unpack(j)) + local row = [[\\bTR]] + for n,c in ipairs(r) do + if not c.parent then + local celltext = c.stripped + if c.span.x or c.span.y then + local span_exp = "[" + if c.span.x then + span_exp = span_exp .. "nc=" .. c.span.x .. "," + end + if c.span.y then + span_exp = span_exp .. "nr=" .. c.span.y + end + celltext = span_exp .. "] " .. celltext + + end + + row = row .. "\n " .. [[\\bTC ]] .. celltext .. [[\\eTC]] + --print(c.parent) + end + end + test = test .. row .. "\n" .. [[\\eTR]] .. "\n" + end + end + --return head .. test .. tail + return head .. test .. tail end function rst_context.table_row (tab) diff --git a/rst_helpers.lua b/rst_helpers.lua new file mode 100644 index 0000000..e55112d --- /dev/null +++ b/rst_helpers.lua @@ -0,0 +1,293 @@ +#!/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 diff --git a/rst_parser.lua b/rst_parser.lua index 6630767..343b45c 100644 --- a/rst_parser.lua +++ b/rst_parser.lua @@ -11,11 +11,12 @@ -------------------------------------------------------------------------------- -- -require "lpeg" +--require "lpeg" rst = require "rst_context" +helpers = require "rst_helpers" -local rst_debug = true +local rst_debug = false local warn = function(str, ...) if not rst_debug then return false end @@ -54,7 +55,7 @@ tracklists.bullets.max = 0 tracklists.lastbullet = "" tracklists.roman_cache = {} -- storing roman numerals that were already converted tracklists.currentindent = "" -- used in definition lists and elsewhere -tracklists.currentlayout = {} -- table layout +tracklists.currentwidth = 0-- table layout local enclosed_mapping = { ["'"] = "'", @@ -221,16 +222,18 @@ local parser = P{ -- Grid table -------------------------------------------------------------------------------- - grid_table = Cs(V"gt_first_row" + grid_table = Ct(V"gt_first_row" * V"gt_other_rows") * V"blank_line"^1 - / rst.grid_table + --/ rst.grid_table + / function(tab) + return rst.grid_table(helpers.table.create(tab)) + end , gt_first_row = V"gt_setindent" - * V"gt_setlayout" + * C(V"gt_sethorizontal") * V"eol" - / "" , --gt_setindent = Cg(V"space"^0, "tableindent"), @@ -243,18 +246,12 @@ local parser = P{ gt_layoutmarkers = V"table_intersection" + V"table_hline" + V"table_header_hline", - gt_setlayout = Cmt(V"gt_layoutmarkers"^3, function (s, i, layout) - --warn("tab-l", layout) - local l = {} - for n, dimension in ipairs(string.explode(layout, "++")) do - l[n] = #dimension - end - l["length"] = #layout - warn("tab-l", unpack(l)) - tracklists.currentlayout = l - return true - end) - , + gt_sethorizontal = Cmt(V"gt_layoutmarkers"^3, function (s, i, width) + warn("tab-h", "width", "true", #width, "set", i) + tracklists.currentwidth = #width + return true + end) + , gt_other_rows = V"gt_head"^-1 * V"gt_body" @@ -262,63 +259,55 @@ local parser = P{ --gt_matchindent = Cmt(V"space"^0 * Cb"tableindent", function (s, i, this, matchme) gt_matchindent = Cmt(V"space"^0, function (s, i, this) - local matchme = tracklists.currentindent - warn("tab-m", this == matchme, #this, #matchme, i) - return this == matchme - end) - , + local matchme = tracklists.currentindent + warn("tab-m", "indent", #this == #matchme, #this, #matchme, i) + return #this == #matchme + end) + , - --gt_cell = Cmt(V"gt_content_cell" + V"gt_line_cell", function (s, i, cell) - --print("NEXT CELL:",cell) - --return true - --end), - gt_cell = C(V"gt_content_cell" + V"gt_line_cell"), + gt_cell = (V"gt_content_cell" + V"gt_line_cell") + * (V"table_intersection" + V"table_vline") + , - gt_content_cell = (V"table_vline" + V"table_intersection") - * C((1 - V"table_vline" - V"table_intersection" - V"eol")^1), + gt_content_cell = ((1 - V"table_vline" - V"table_intersection" - V"eol")^1), - gt_line_cell = V"table_intersection" - --* C((V"table_hline" - V"table_intersection")^1) - * C(V"table_hline"^1) - , + gt_line_cell = V"table_hline"^1, - gt_contentrow = Ct(V"gt_matchindent" - * V"gt_cell"^1 - * (V"table_vline" + V"table_intersection") - * V"eol" - ) - / rst.table_row + gt_contentrow = V"gt_matchindent" + * C((V"table_intersection" + V"table_vline") + * V"gt_cell"^1) + * V"whitespace"^-1 * V"eol" , - gt_body = V"gt_row"^1, + gt_body = ((V"gt_contentrow" - V"gt_bodysep")^1 * V"gt_bodysep")^1, - gt_row = (V"gt_contentrow" - V"gt_bodysep")^1 - * Cmt(V"gt_bodysep", function(s,i,str)print(str)return true end), + --gt_row = (V"gt_contentrow" - V"gt_bodysep")^1 + --* C(V"gt_bodysep") + --, gt_bodysep = V"gt_matchindent" - * Cmt(V"table_intersection" - * (V"table_hline"^1 * V"table_intersection")^1, function(s, i, separator) - warn("tab-s", #separator, #separator == tracklists.currentlayout.length, "",i) - local l = {} - for n, dimension in ipairs(string.explode(separator, "++")) do - l[n] = #dimension - end - l["length"] = #separator - --return l == tracklists.currentlayout - return l.length == tracklists.currentlayout.length - end) - * V"eol" + * C(Cmt(V"table_intersection" + * (V"table_hline"^1 * V"table_intersection")^1, function(s, i, separator) + local matchme = tracklists.currentwidth + warn("tab-m", "body", #separator == matchme, #separator, matchme, i) + return #separator == matchme + end)) + * V"whitespace"^-1 * V"eol" , gt_head = V"gt_contentrow"^1 - * Cmt(V"gt_headsep", function(s,i,str)print(str)return true end) + * V"gt_headsep" , gt_headsep = V"gt_matchindent" - * V"table_intersection" - * (V"table_header_hline"^1 * V"table_intersection")^1 - * V"eol" + * C(Cmt(V"table_intersection" + * (V"table_header_hline"^1 * V"table_intersection")^1, function(s, i, separator) + local matchme = tracklists.currentwidth + warn("tab-s", "head", #separator == matchme, #separator, matchme, i) + return #separator == matchme + end)) + * V"whitespace"^-1 * V"eol" , @@ -341,7 +330,7 @@ local parser = P{ return true end) / "" * -V"attrib_dash" - * V"text_elements"^1 + * V"text_element"^1 * V"eol" , @@ -351,7 +340,7 @@ local parser = P{ return tracklists.currentindent == indent end) / "" * -V"attrib_dash" - * V"text_elements"^1 + * V"text_element"^1 * V"eol" , @@ -366,7 +355,7 @@ local parser = P{ t.currentindent = ret and indent or t.currentindent return ret end) / "" - * V"text_elements"^1 + * V"text_element"^1 * V"eol" , @@ -375,7 +364,7 @@ local parser = P{ indent, tracklists.currentindent, i) return utf.len(tracklists.currentindent) == #indent end) / "" - * V"text_elements"^1 + * V"text_element"^1 * V"eol" , @@ -418,7 +407,7 @@ local parser = P{ * V"line_block_line" , - line_block_line = Cs(V"text_elements"^1 + line_block_line = Cs(V"text_element"^1 * V"line_block_cont"^0 * V"eol") / rst.line_block_line @@ -429,7 +418,7 @@ local parser = P{ warn("lbk-c", #spaces, #tracklists.currentindent, spaces, tracklists.currentindent, i) return #spaces >= #tracklists.currentindent end) / "" - * V"text_elements"^1 + * V"text_element"^1 , -------------------------------------------------------------------------------- @@ -532,12 +521,12 @@ local parser = P{ option_desc_single = V"space"^2 --* V"rest_of_line" - * V"text_elements"^1 + * V"text_element"^1 * V"eol", option_desc_more = V"space"^2 --* V"rest_of_line" - * V"text_elements"^1 + * V"text_element"^1 * V"eol" * V"indented_lines" * (V"blank_line" * V"indented_lines")^0, @@ -586,7 +575,7 @@ local parser = P{ field_name = (V"escaped_colon" + (1 - V"colon"))^1, - field_body = C(V"text_elements"^1 * V"eol" + field_body = C(V"text_element"^1 * V"eol" * V"indented_lines"^-1), -------------------------------------------------------------------------------- @@ -850,11 +839,11 @@ local parser = P{ --paragraph = -(V"double_dot" + V"double_underscore") -- + V"bullet_indent") paragraph = -V"punctuation" - * Cs((V"text_elements" + (V"eol" - V"endpar"))^1) + * Cs((V"text_element" + (V"eol" - V"endpar"))^1) * V"endpar" / rst.paragraph, - text_elements = V"included_literal_block" + text_element = V"included_literal_block" + V"enclosed_inline" + V"inline_elements" + V"word" @@ -981,14 +970,14 @@ local parser = P{ tracklists.currentindent = indent return true end) - * V"text_elements"^1 + * V"text_element"^1 * V"eol", indented_other = Cmt(V"space"^1, function (s, i, indent) warn("idt-m", indent, tracklists.currentindent, indent == tracklists.currentindent, i) return indent == tracklists.currentindent end) - * V"text_elements"^1 + * V"text_element"^1 * V"eol", -------------------------------------------------------------------------------- -- cgit v1.2.3