Index: cortav.ct ================================================================== --- cortav.ct +++ cortav.ct @@ -99,26 +99,26 @@ ** [`~~~ \[language\] #id ~~~] ** [`~~~ title ~~~] ** [`~~~ title \[language\] ~~~] ** [`~~~ \[language\] title ~~~] ** [`~~~ title \[language\] #id ~~~] -*[*definition] ([^def-ex tab]): a line [^def-tab-enc beginning with a tab] is a multipurpose metadata syntax. the tab may be followed by an identifier, a colon, and a value string, in which case it opens a new definition; alternatively, a second tab character turns the line into a [*definition continuation], adding the remaining characters as a new line to the definition value on the previous line. when a new definition is opened on a line immediately following certain kinds of objects, such as resource, it attaches key-value metadata to that object. when a definition is not preceded by such an object, an independent [*reference] is created instad. -** a [*reference] is a general mechanism for out-of-line metadata, and references are used in many different ways -- e.g. to specify link destinations, footnote contents, abbreviations, or macros. to ensure that a definition is interpreted as a reference, rather than as metadata for an object, precede it with a blank line. +*[*definition] ([^def-ex tab]): a line [^def-tab-enc beginning with a tab] is a multipurpose metadata syntax. the tab may be followed by an identifier, a colon, and a value string, in which case it opens a new definition; alternatively, a second tab character turns the line into a [*definition continuation], adding the remaining characters as a new line to the definition value on the previous line. when a new definition is opened on a line immediately following certain kinds of objects, such as resources, embeds, or multiline macro expansions, it attaches key-value metadata to that object. when a definition is not preceded by such an object, an independent [*reference] is created instad. +** a [*reference] is a general mechanism for out-of-line metadata, and references are used in many different ways -- e.g. to specify link destinations, footnote contents, abbreviations, or macro bodies. to ensure that a definition is interpreted as a reference, rather than as metadata for an object, precede it with a blank line. def-tab-enc: in encodings without tab characters, a definition is opened by a line beginning with two blanks, and continued by a line beginning with four blanks. def-ex: [*open a new reference]: [`[!\\t][$key]: [$value]] [*continue a reference]: [`[!\\t\\t][$value]] * [*quotation] ([`<]): a line of the form [`<[$name]> [$quote]] denotes an utterance by [$name]. * [*blockquote] ([`>]): alternate blockquote syntax. can be nested by repeating the [`>] character. * [*subtitle/caption] ([`\--]): attaches a subtitle to the previous header, or caption to the previous object -* [*embed] ([`&]): embeds a referenced object. can be used to show images or repeat previously defined objects like lists or tables, optionally with a caption. -** [`$[$macro] [$arg1]|[$arg2]|[$argn]…] invokes a block-level macro with the supplied arguments -*** [`$mymacro arg 1|arg 2|arg 3] +* [*embed] ([`&]): embeds a referenced object. can be used to show images or repeat previously defined objects like lists or tables, optionally with a caption. an embed line can be followed immediately by a sequence of [*definitions] in the same way that resource definitions can, to override resource properties on a per-instance basis. note that only presentation-related properties like [$desc] can be meaningful overridden, as embed does not trigger a re-render of the parse tree; if you want to override e.g. context variables, use a multiline macro invocation instead. ** [`&[$image]] embeds an image or other block-level object. [!image] can be a reference with a url or file path, or it can be an embed section (e.g. for SVG files) ***[`&myimg All that remained of the unfortunate blood magic pageant contestants and audience (police photo)] ** [`&-[$ident] [$styled-text]] embeds a closed disclosure element containing the text of the named object (a nonprinting section or cortav resource should usually be used to store the content; it can also name an image or video, of course). in interactive outputs, this will display as a block which can be clicked on to view the full contents of the referenced object [$ident]; if [$styled-text] is present, it overrides the title of the section you are embedding (if any). in static outputs, the disclosure object will display as an enclosed box with [$styled-text] as the title text *** [`&-ex-a Prosecution Exhibit A (GRAPHIC CONTENT)] ** [`&+[$section] [$styled-text]] is like the above, but the disclosure element is open by default +* [`$[$macro] [$arg1]|[$arg2]|[$argn]…] invokes a block-level macro with the supplied arguments, and can be followed by a property override definition list the same way embed and resource lines can. note that while both [`$[$id]] and [`&[$id]] can be used to instantiate resources of type [`text/x.cortav], there is a critical difference: [`$[$id]] renders out the sub-document separately each time it is named, allowing for parameter expansion and for context variables to be overridden for each invocation. by contrast, [`&[$id]] can only insert copies of the same render; no parameters can be passed and context variables will be expanded to their value at the time the resource was defined. +** [`$mymacro arg 1|arg 2|arg 3] * [*horizontal rule] ([`\---]): inserts a horizontal rule or other context break; does not end the section. must be followed by newline. underlines can also be used in place of dashes ([`___], [`-_-], [`__-__-__] etc), as can horizontal unicode box drawing characters ([`─ ━ ┈] etc). * [*page break] ([`\^^]): for formats that support pagination, like EPUB or HTML (when printed), indicates that the rest of the current page should be blank. for formats that do not, extra margins will be inserted. does not create a new section * [*page rule] ([`\^-^]): inserts a page break for formats that support them, and a horizontal rule for formats that do not. does not create a new section. comprised of any number of horizontal rule characters surrounded by a pair of carets (e.g. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^]) * [*table cells] ([`+ |]): see [>ex.tab table examples]. * [*equations] ([`=]): block-level equations can be inserted with the [`=] sequence @@ -370,11 +370,11 @@ *** [`image/svg+xml] is handled specially for HTML files, and may or may not be compatible with other renderer backends. *** [`font/*] can be used with the HTML backend to reference a web font *** [`font/woff2] can be used with the HTML backend to reference a web font *** [`text/plain] (will be inserted as a preformatted text block) *** [`text/css] (can be used when producing HTML files to link in an extra stylesheet, either by embedding it or referencing it from the header) -*** [`text/x.cortav] (will be parsed and inserted as a formatted text block; context variables can be passed to the file by setting [`ctx.[$var]] parameters on the resource, e.g. [`ctx.recipient-name: Mr. Winthrop]) +*** [`text/x.cortav] (will be parsed and inserted as a formatted text block; context variables can be passed to the file by setting [`.[$var]] properties on the resource, e.g. [`.recipient-name: Mr. Winthrop]) *** [`application/x-troff] can be used to supply sections of text written in raw [`groff] syntax. these are ignored by other renderers. *** [`text/html] can be used to supply sections of text written in raw HTML. these are ignored by non-HTML outputs. *** any MIME-type that matches the type of file being generated by the renderer can be used to include a block of data that will be passed directly to the renderer. ** URI types: additional URI types can be added by extensions or different implementations, but every compliant implementation must support these URIs. *** [`http], [`https]/[`http+tls]: accesses resources over HTTP. add a [`file] fallback if possible for the benefit of renderers/viewers that do not have internet access abilities. @@ -609,10 +609,60 @@ ~~~ ~~~ macros #mac [cortav] ~~~ the ranuir word {gloss cor|writing}… gloss: [*[#1]] “[#2]” + +$def sur|n|socialism +$def par|n|speech + def: * [*[#1]] [!([#2])] + ** [#3] + +%% equivalent to + +@def { + * [*[#1]] [!([#2])] + ** [#3] +} +$def sur|n|socialism +$def par|n|speech + +%% we could even do the same thing abusing context variables + +@def { + * [*[#word]] [!([#pos])] + ** [#meaning] +} + +$def + .word: sur + .pos: n + .meaning: socialism +$def + .word: par + .pos: n + .meaning: speech + +%% context variables are useful because they inherit from the enclosing context +%% thus, we can exploit resource syntax to create templates with default values + +@agent { + + CODENAME :| [#1] + + CIVILIAN IDENTITY :| [#civil] + + RULES of ENGAGEMENT :| [#roe] + + DANGER LEVEL :| [#danger] +} + .civil: (unknown) + .roe: Monitor; do not engage + .danger: (unknown) + +$agent ZUCCHINI PARABLE + .civil: Zephram "Rolodex" Goldberg + .danger: Category Scarlet +$agent RHADAMANTH EXQUISITE + .roe: Eliminate with extreme prejudice; CBRN deployment authorized + .danger: [*Unquantifiable] ~~~ ~~~ tables #tab [cortav] ~~~ here is a glossary table. Index: cortav.lua ================================================================== --- cortav.lua +++ cortav.lua @@ -284,10 +284,17 @@ else return sp end elseif self.vars[var] then return self.vars[var] + elseif ctx.invocation + and ctx.invocation.props + and ctx.invocation.props['.' .. var] then + return ctx.invocation.props['.' .. var] + elseif ctx.declaration + and ctx.declaration.props['.' .. var] then + return ctx.declaration.props['.' .. var] else local sp = scanParents(var) if sp then return sp end if test then return false end return '' -- is this desirable behavior? @@ -1109,13 +1116,16 @@ 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 + if last and last.kind == 'resource' + or last.kind == 'embed' + or last.kind == 'macro' then + last.props = last.props or {} last.props[ref] = val - j:hook('rsrc_set_prop', c, last, ref, val, l) + j:hook('set_prop', c, last, ref, val, l) rsrc = last elseif last and last.kind == 'reference' and last.rsrc then last.rsrc.props[ref] = val rsrc = last.rsrc else @@ -1288,12 +1298,10 @@ } rsrc.raw = ''; if src == nil then rsrc.props.src = 'text/x.cortav' end - else - -- load the raw body, where possible end if id then if c.sec.refs[id] then c:fail('an object with id “%s” already exists in that section',id) else @@ -1300,15 +1308,16 @@ c.sec.refs[id] = rsrc end end table.insert(d, rsrc) j:hook('block_insert', c, rsrc, s) - if id == '' then --shorthand syntax + if id == nil then --shorthand syntax local embed = { kind = 'embed'; rsrc = rsrc; origin = c; + mode = 'inline'; } table.insert(d, embed) j:hook('block_insert', c, embed, s) end @@ -1406,11 +1415,11 @@ ctx:fail('unimplemented syntax mode %s', ctx.mode.kind) end mf(job, ctx, l, dest) --NOTE: you are responsible for triggering the appropriate hooks if you insert anything! end else - if l then + if l and l ~= '' then local function tryseqs(seqs, ...) for _, i in pairs(seqs) do if ((not i.seq ) or startswith(l, i.seq)) and ((not i.pred) or i.pred (l, ctx )) then i.fn(l, ctx, job, dest, ...) @@ -1549,20 +1558,22 @@ end -- the resource has been cached. check the mime-type to see if -- we need to parse it or if it is suitable as-is - if resource.mime.class == "text" then - if resource.mime.kind == "x.cortav" then - local sd, sc = r.origin.doc:sub(r.origin) - local lines = ss.str.breaklines(r.origin.doc.enc, resource.raw, {}) - for i, ln in ipairs(lines) do - sc.line = sc.line + 1 - ct.parse_line(ln, sc, sc.sec.blocks) - end - resource.doc = sd + if ss.mime 'text/x.cortav' < resource.mime then + local sd, sc = r.origin.doc:sub(r.origin) + -- we store the resource block itself in the declaration + -- slot so that its properties (e.g. context variables) + -- can affect the way the document is rendered + sc.declaration = r + local lines = ss.str.breaklines(r.origin.doc.enc, resource.raw, {}) + for i, ln in ipairs(lines) do + sc.line = sc.line + 1 + ct.parse_line(ln, sc, sc.sec.blocks) end + resource.doc = sd end end table.insert(srcs, resource) end r.srcs = srcs @@ -1578,25 +1589,27 @@ -- expand block macros for i, sec in ipairs(ctx.doc.secorder) do for n, r in pairs(sec.blocks) do if r.kind == 'macro' then - local mc = r.origin:clone() - mc.invocation = r - local mac = r.origin:ref(r.macro) + local mc = r.origin + local mac = mc:ref(r.macro) if not mac then - r.origin:fail('no such reference or resource “%s”', r.macro) + mc:fail('no such reference or resource “%s”', r.macro) end + local subdoc, subctx = ctx.doc:sub(mc) local rawbody + subctx.invocation = r if type(mac) == 'string' then rawbody = mac elseif mac.raw then rawbody = mac.raw + subctx.declaration = mac else - r.origin:fail('block macro “%s” must be either a reference or an embedded text/x.cortav resource', r.macro) + mc:fail('block macro “%s” must be either a reference or an embedded text/x.cortav resource', r.macro) end local lines = ss.str.breaklines(ctx.doc.enc, rawbody) for i, ln in ipairs(lines) do ct.parse_line(ln, subctx, subctx.sec.blocks) Index: render/html.lua ================================================================== --- render/html.lua +++ render/html.lua @@ -855,10 +855,16 @@ mime.opts = {} return string.format('data:%s;base64,%s', mime, ss.str.b64e(s.raw)) end end --figure out how to embed the given object + local function P(p) -- get prop + if b.props and b.props[p] then + return b.props[p] + end + return obj.props[p] + end local embedActs = { {ss.mime'image/*', function(s,ctr) if s == nil then return {tag = "picture", nodes = {}} else @@ -867,15 +873,15 @@ if next(ctr.nodes) == nil then idx = 1 fbimg = { elt = 'img'; --fallback attrs = { - alt = obj.props.desc or obj.props.detail or ''; - title = obj.props.detail; + alt = P'desc' or P'detail' or ''; + title = P'detail'; src = uri; - width = obj.props.width; - height = obj.props.height; + width = P'width'; + height = P'height'; }; } else idx = #ctr.nodes end table.insert(ctr.nodes, idx, { elt = 'source'; --fallback @@ -974,11 +980,11 @@ if rtype[1] < src.mime then rtype[2](src, top) end end local ft = flatten(top) - local cap = b.cap or obj.props.desc or obj.props.detail + local cap = b.cap or P'desc' or P'detail' if b.mode == 'inline' then -- TODO insert caption return ft else local prop = {}