diff options
-rw-r--r-- | rst_context.lua | 115 | ||||
-rw-r--r-- | rst_helpers.lua | 121 | ||||
-rw-r--r-- | rst_parser.lua | 136 |
3 files changed, 318 insertions, 54 deletions
diff --git a/rst_context.lua b/rst_context.lua index 64c2cd6..851bd7e 100644 --- a/rst_context.lua +++ b/rst_context.lua @@ -13,9 +13,9 @@ require "lpeg" -require help = "rst_helpers" +help = require "rst_helpers" -local dbg_write = help.dbg_write +local dbg_write = help.dbg_writef local C, Cb, Cc, Cg, Cmt, Cp, Cs, Ct, P, R, S, V, match = lpeg.C, lpeg.Cb, lpeg.Cc, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match @@ -615,36 +615,6 @@ function rst_context.table (str) end function rst_context.grid_table (tab) - local head - if tab.has_head then - head = [[ -\\setupTABLE[c][first] [background=color, backgroundcolor=grey, style=\tt] -\\setupTABLE[c][each] [frame=on] -\\setupTABLE[r][each] [frame=on] -\\bTABLE[split=repeat,option=stretch] -\\bTABLEhead -\\bTR - \\bTH first \\eTH - \\bTH second \\eTH - \\bTH third \\eTH - \\bTH fourth \\eTH -\\eTR -\\eTABLEhead -\\bTABLEbody -]] - else - head = [[ -\\setupTABLE[c][each] [frame=on] -\\setupTABLE[r][each] [frame=on] -\\bTABLE[split=repeat,option=stretch] -\\bTABLEbody -]] - end - local tail = [[ - -\\eTABLEbody -\\eTABLE -]] local body = "" for i,r in ipairs(tab.rows) do local isempty = true @@ -679,18 +649,87 @@ function rst_context.grid_table (tab) body = body .. row .. "\n" .. [[\\eTR]] .. "\n" end end + local head + if tab.has_head then + head = [[ +\\setupTABLE[c][first] [background=color, backgroundcolor=grey, style=\tt] +\\setupTABLE[c][each] [frame=on] +\\setupTABLE[r][each] [frame=on] +\\bTABLE[split=repeat,option=stretch] +\\bTABLEhead +\\bTR + \\bTH first \\eTH + \\bTH second \\eTH + \\bTH third \\eTH + \\bTH fourth \\eTH +\\eTR +\\eTABLEhead +\\bTABLEbody +]] + else + head = [[ +\\setupTABLE[c][each] [frame=on] +\\setupTABLE[r][each] [frame=on] +\\bTABLE[split=repeat,option=stretch] +\\bTABLEbody +]] + end + local tail = [[ + +\\eTABLEbody +\\eTABLE +]] return head .. body .. tail end -function rst_context.table_row (tab) - local tmp = [[\\bTR]] - for n, cell in ipairs(tab) do - tmp = tmp .. [[\\bTC]] .. cell .. [[\\eTC]] .. "\n" +function rst_context.simple_table(tab) + local head + if tab.has_head then + head = [[ +\\setupTABLE[c][first] [background=color, backgroundcolor=grey, style=\tt] +\\setupTABLE[c][each] [frame=on] +\\setupTABLE[r][each] [frame=on] +\\bTABLE[split=repeat,option=stretch] +\\bTABLEhead +\\bTR + \\bTH first \\eTH + \\bTH second \\eTH + \\bTH third \\eTH +\\eTR +\\eTABLEhead +\\bTABLEbody +]] + else + head = [[ +\\setupTABLE[c][each] [frame=on] +\\setupTABLE[r][each] [frame=on] +\\bTABLE[split=repeat,option=stretch] +\\bTABLEbody +]] end + local tail = [[ - tmp = tmp .. [[\\eTR +\\eTABLEbody +\\eTABLE ]] - return tmp + local body = "" + for nr,row in ipairs(tab) do + if not row.separator and not row.ignore then + dbg_write(">tr>" .. #row) + body = body .. [[\\bTR]] + for nc,cell in ipairs(row) do + dbg_write("%7s | ", cell.content) + if cell.span then + body = body .. string.format([=[\\bTC[nc=%s]%s\\eTC]=], cell.span, cell.content) + else + body = body .. [[\\bTC ]] .. cell.content .. [[\\eTC]] + end + end + dbg_write("\n") + body = body .. "\\\\eTR\n" + end + end + return head .. body .. tail end return rst_context diff --git a/rst_helpers.lua b/rst_helpers.lua index acd61ed..fb0dd84 100644 --- a/rst_helpers.lua +++ b/rst_helpers.lua @@ -20,13 +20,14 @@ local helpers = {} helpers.table = {} helpers.cell = {} -local function dbg_writef(...) +function helpers.dbg_writef(...) if helpers_debug == true then io.write(string.format(...)) end end -helpers.dbg_write = dbg_write +--helpers.dbg_write = dbg_write +local dbg_write = helpers.dbg_writef local patterns = {} @@ -38,6 +39,9 @@ do p.bar = P"|" p.eol = P"\n" p.last = -P(1) + p.space = P" " + + p.dash_or_equals = p.dash + p.equals p.celldelim = p.bar + p.plus p.cellcontent = (1 - p.celldelim) @@ -45,6 +49,16 @@ do p.cell_line = p.plus * p.dash^1 * #p.plus p.dashesonly = p.dash^1 * p.last + p.col_start = Cp() * p.dash_or_equals^1 + p.col_stop = p.dash_or_equals^1 * Cp() + p.column_starts = p.col_start * ( p.space^1 * p.col_start)^1 + p.column_stops = p.col_stop * ( p.space^1 * p.col_stop)^1 + + p.st_headsep = p.equals^1 * (p.space^1 * p.equals^1)^1 + p.st_colspan = p.dash^1 * (p.space^1 * p.dash^1)^0 * p.space^0 * p.last + p.st_span_starts = Ct(Cp() * p.dash^1 * (p.space^1 * Cp() * p.dash^1)^0) + p.st_span_stops = Ct(p.dash^1 * Cp() * (p.space^1 * p.dash^1 * Cp())^0) + p.cells = P{ [1] = "cells", cells = p.celldelim @@ -457,9 +471,110 @@ function helpers.table.create(raw) end end - self.__draw_debug() + --self.__draw_debug() return self +end + + + +-- Check the column boundaries of a simple table. +function helpers.get_st_boundaries (str) + local p = patterns + local starts, stops, slices = {}, {}, {} + for n, elm in ipairs({ p.column_starts:match(str) }) do + slices[n] = { start = elm } + starts[elm] = true + end + for n, elm in ipairs({ p.column_stops :match(str) }) do + slices[n]["stop"] = elm + stops[elm] = true + end + return { starts = starts, stops = stops, slices = slices } +end + +function helpers.table.simple(raw) + local rows = {} + local multispans = {} + local bounds = helpers.get_st_boundaries(raw[1]) + local p = patterns + + for nr=1, #raw do + local row = raw[nr] + if p.st_colspan:match(row) then + local spans = {} + spans.starts = p.st_span_starts:match(row) + spans.stops = p.st_span_stops :match(row) + local all_match = true + for n=1, #spans.starts do -- check if they match the table layout + local start = spans.starts[n] + local stop = spans.stops[n] + if not bounds.starts[start] or + not bounds.stops[stop] then + all_match = false + break + end + end + if all_match then + spans.cols = {} + for n, start in ipairs(spans.starts) do -- find the start and end columns + local stop = spans.stops[n] + local starts_at, stops_at + for o=1, #bounds.slices do + if bounds.slices[o].start == start then + starts_at = o + end + if bounds.slices[o].stop == stop then + stops_at = o + end + end + spans.cols[n] = 1 + stops_at - starts_at + end + multispans[nr-1] = spans + end + end + end + + for nr, row in ipairs(raw) do + local newrow = {} + if p.st_headsep:match(row) then + newrow.separator = true + if nr > 1 and #raw > nr then -- Ends the header. + rows.has_head = true + newrow.end_head = true + end + elseif multispans[nr] then + local spans = multispans[nr] + for nc, span in ipairs(spans.cols) do + local this = {} + this.content = string.strip(row:sub(spans.starts[nc], spans.stops[nc])) + this.span = span + newrow[#newrow+1] = this + end + elseif multispans[nr-1] then -- some cells span columns + newrow.ignore = true + else + local nc = 1 + repeat + local this = {} + this.content = string.strip(row:sub(bounds.slices[nc].start, bounds.slices[nc].stop - 1)) + if bounds.starts[nc+1] then + -- Only spaces allowed between columns. + local between = s:sub(bounds.stops[nc], bounds.slices[nc+1].start) + if not match(p.space^1 * -P(1), between) then + -- This is probably too hard a step. + helpers.dbg_write("[sta-h] Please revise your table boundaries.") + os.exit(1) + end + end + newrow[nc] = this + nc = nc + 1 + until nc > #bounds.slices + end + rows[nr] = newrow + end + return rows end return helpers + diff --git a/rst_parser.lua b/rst_parser.lua index cc4304d..54c3719 100644 --- a/rst_parser.lua +++ b/rst_parser.lua @@ -55,7 +55,8 @@ 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.currentwidth = 0-- table layout +tracklists.currentwidth = 0 -- table layout +tracklists.currentlayout = {} -- table layout local enclosed_mapping = { ["'"] = "'", @@ -212,14 +213,122 @@ local parser = P{ -- Table block -------------------------------------------------------------------------------- - table_block = V"grid_table" - --+ V"simple_table" + table_block = V"simple_table" + + V"grid_table" / rst.table , +-------------------------------------------------------------------------------- +-- Simple tables +-------------------------------------------------------------------------------- + + simple_table = Ct(V"st_first_row" + * V"st_other_rows") + --* V"blank_line"^1 + * V"end_block"^1 + / function (tab) + return rst.simple_table(helpers.table.simple(tab)) + end + , + + st_first_row = V"st_setindent" + * C(V"st_setlayout") + * V"space"^0 + * V"eol" + , + + st_setindent = Cmt(V"space"^0, function(s, i, indent) + warn("sta-i", "true", #indent, "set", i) + tracklists.currentindent = indent + return true + end) + , + + st_matchindent = Cmt(V"space"^0, function(s, i, indent) + warn("sta-m", tracklists.currentindent == indent, #indent, #tracklists.currentindent, i) + return tracklists.currentindent == indent + end) + , + + st_setlayout = Cmt((V"equals"^1) * (V"spaces" * V"equals"^1)^1, function(s, i, layout) + local tc = tracklists.currentlayout + warn("sta-l", #layout, "set", "", i) + tc.raw = layout + tc.bounds = help.get_st_boundaries(layout) + return true + end) + , + + st_other_rows = (V"st_content"^1 * V"st_separator")^1, + + st_content = C(V"st_matchlayout"), + + st_matchlayout = -#V"st_separator" * Cmt((1 - V"eol")^1, function (s, i, content) + -- Don't check for matching indent but if the rest is + -- fine then the line should be sane. This allows + -- cells starting with spaces. + content = content:sub(#tracklists.currentindent) + local tcb = tracklists.currentlayout.bounds + local n = 1 + local spaces_only = P" "^1 + while n < #tcb.slices do + local from = tcb.slices[n] .stop + local to = tcb.slices[n+1].start + --print(n, from, to, content) + local between = spaces_only:match(content, from) + if not between then -- Cell spanning more than one row. + -- pass + warn("sta-c", "span", from, to, i) + elseif not (between >= to) then + warn("sta-c", "false", from, to, i) + return false + end + n = n + 1 + end + warn("sta-c", "true", #tcb.slices, "", i) + return true + end) + * V"eol" + , + + st_separator = V"st_matchindent" + * C(V"st_normal_sep" + V"st_colspan_sep") + * V"eol" + , + + st_normal_sep = Cmt((V"equals"^1) * (V"spaces" * V"equals"^1)^1, function(s, i, layout) + warn("sta-s", tracklists.currentlayout.raw == layout, #layout, #tracklists.currentlayout.raw, i) + return tracklists.currentlayout.raw == layout + end) + , + + st_colspan_sep = Cmt(V"dash"^1 * (V"spaces" * V"dash"^1)^0, function(s, i, layout) + local tcb = tracklists.currentlayout.bounds + local this = help.get_st_boundaries (layout) + local start_valid = false + for start, _ in next, this.starts do + if tcb.starts[start] then + start_valid = true + local stop_valid = false + for stop, _ in next, this.stops do + if tcb.stops[stop] then -- bingo + stop_valid = true + end + end + if not stop_valid then + warn("sta-x", stop_valid, #layout, #tracklists.currentlayout.raw, i) + return false + end + end + end + warn("sta-x", start_valid, #layout, #tracklists.currentlayout.raw, i) + return start_valid + end) + , + -------------------------------------------------------------------------------- --- Grid table +-- Grid tables -------------------------------------------------------------------------------- grid_table = Ct(V"gt_first_row" @@ -235,7 +344,6 @@ local parser = P{ * V"eol" , - --gt_setindent = Cg(V"space"^0, "tableindent"), gt_setindent = Cmt(V"space"^0, function(s, i, indent) warn("tab-i", true, #indent, "set", i) tracklists.currentindent = indent @@ -256,7 +364,6 @@ local parser = P{ * V"gt_body" , - --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", "indent", #this == #matchme, #this, #matchme, i) @@ -652,7 +759,7 @@ local parser = P{ end), --bullet_stop =V"blank_line" * Cs(Cc("")) / rst.stopitemize, - bullet_stop =V"endpar" * Cs(Cc("")) / rst.stopitemize, + bullet_stop =V"end_block" * Cs(Cc("")) / rst.stopitemize, bullet_init = V"eol"^0 * V"bullet_first" * V"bullet_itemrest", @@ -758,7 +865,7 @@ local parser = P{ transition = V"eol"^0 * V"transition_line" - * V"endpar" + * V"end_block" /rst.transition, -------------------------------------------------------------------------------- @@ -808,7 +915,7 @@ local parser = P{ * C(1 - V"whitespace" - V"eol")^1)^0) * V"eol" * #(1 - V"whitespace" - "eol")) / rst.joinindented + C((1 - V"eol")^1) * V"eol" * #(V"double_dot" + V"eol") - + (1 - V"endpar")^0 * Cc("make me constant!"), + + (1 - V"end_block")^0 * Cc("make me constant!"), target = Ct((V"target_name" * (V"space"^0 * V"eol" * V"target_name")^0) * V"space"^0 @@ -824,7 +931,7 @@ local parser = P{ / rst.target, target_block = (V"anonymous_target" + V"target")^1 - * V"endpar", + * V"end_block", -------------------------------------------------------------------------------- -- Paragraphs * Inline Markup @@ -833,7 +940,8 @@ local parser = P{ paragraph = V"par_setindent" * Ct(C((1 - V"eol")^1) * V"eol" * (V"par_matchindent" * C((1 - V"eol")^1) * V"eol")^0) - * V"blank_line"^1 + --* V"blank_line"^1 + * V"end_block"^1 / rst.paragraph, par_setindent = Cmt(V"space"^0, function (s, i, indent) @@ -926,7 +1034,7 @@ local parser = P{ -- Terminal Symbols and Low-Level Elements -------------------------------------------------------------------------------- - word = (1 - V"punctuation" - V"endpar" - V"spacing" - V"eol")^1, -- TODO : no punctuation (later) + word = (1 - V"punctuation" - V"end_block" - V"spacing" - V"eol")^1, -- TODO : no punctuation (later) asterisk = P"*", double_asterisk = V"asterisk" * V"asterisk", @@ -1017,7 +1125,9 @@ local parser = P{ eol = P"\n", eof = V"eol"^0 * -P(1), - endpar = V"eol" * (V"blank_line"^1 + V"eof"), + end_block = V"blank_line"^1 + + (V"whitespace"^0 * V"eol" + * (V"whitespace"^0 * V"eol")^0 * V"eof"), -- diverse markup character sets delimiters = P"‐" + P"‑" + P"‒" + P"–" + V"emdash" + V"space", -- inline markup |