@@ -86,8 +86,11 @@ ext = ss.exnkind 'extension error'; enc = ss.exnkind('encoding error', function(msg, ...) return string.format('[%s]' .. msg, ...) end); + rdr = ss.exnkind('could not render', function(msg, ...) + return string.format('(backend %s)'..msg, ...) + end); } ct.ctx = declare { mk = function(src) return {src = src} end; @@ -144,8 +147,25 @@ construct = function(self, id, depth) self.id = id self.depth = depth end; + fns = { + visible = function(self) + if self.kind == 'nonprinting' then return false end + local invisibles = { + ['break'] = true; + reference = true; + resource = true; + directive = true; + } + for k,b in pairs(self.blocks) do + if not (invisibles[b.kind] or b.invisible) then return true end + -- extensions that add invisible nodes to the AST must + -- mark them as such for rendering to work properly! + end + return false + end; + } } ct.doc = declare { ident = 'doc'; @@ -752,9 +772,9 @@ break end end if not found then - ctx:fail('no recognized control sequence in [%s]', substr) + buf = buf .. c end elseif c == '\n' then flush() table.insert(spans,{kind='line-break',origin=ctx:clone()}) @@ -989,9 +1009,11 @@ local str = l:match '^\t\t(.-)%s*$' last.val = last.val .. '\n' .. str c.sec.refs[last.key] = last.val end}; - {seq = '\t', fn = blockwrap(function(l,c,j,d) + {seq = '\t', pred = function(l) + return (l:match '\t+([^:]+):%s*(.*)$') + end; fn = blockwrap(function(l,c,j,d) local ref, val = l:match '\t+([^:]+):%s*(.*)$' local last = d[#d] local rsrc if last and last.kind == 'resource' then @@ -1188,9 +1210,11 @@ job:hook('line_end',ctx,l) end function ct.parse(file, src, mode, setup) - + -- this object is threaded down through the parse tree + -- and copied to store information like the origin of the + -- element in the source code local ctx = ct.ctx.mk(src) ctx.line = 0 ctx.doc = ct.doc.mk() ctx.doc.src = src @@ -1270,4 +1294,23 @@ ctx.doc.stage = nil ctx.doc.docjob:hook('meddle_ast') return ctx.doc end + +function ct.expand_var(v) + local val + if v.pos then + if not v.origin.invocation then + v.origin:fail 'positional arguments can only be used in a macro invocation' + elseif not v.origin.invocation.args[v.pos] then + v.origin.invocation.origin:fail('macro invocation %s missing positional argument #%u', v.origin.invocation.macro, v.pos) + end + val = v.origin.invocation.args[v.pos] + else + val = v.origin.doc:context_var(v.var, v.origin) + end + if v.raw then + return val, true + else + return ct.parse_span(val, v.origin), false + end +end