cortav  Check-in [a8358d587d]

Overview
Comment:better footnotes, update syntax def, delete broken old span
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a8358d587d209c4a3690fcda3edda8bb2f6ed24aab1581cf04c097dfc9489e90
User & Date: lexi on 2022-09-10 13:31:23
Other Links: manifest | tags
Context
2022-09-10
17:28
add remote fetch via curl, shim native bindings check-in: ee086767f3 user: lexi tags: trunk
13:31
better footnotes, update syntax def, delete broken old span check-in: a8358d587d user: lexi tags: trunk
03:16
defuck xref structure check-in: 0ef3dd0c77 user: lexi tags: trunk
Changes

Modified cortav.ct from [fb6019b4ad] to [fe93ee725b].

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
...
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
...
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
*[*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. 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
* [*cross-references] ([`=>] [`⇒]): inserts a block-level link. has two forms for the sake of gemtext compatibility. [$styled-text] is a descriptive text of the destination. especially useful for menus and gemtext output.
** the cortav syntax is [`=>[$ident] [$styled-text]], where [$ident] is an identifier; links to the same destination as [` \[>[$ident] [$styled-text]\]] would
** the compatibility syntax is [`=> [$uri] [$styled-text]] (note the space before [$uri]!). instead of taking an identifier for an object in the document, it directly accepts a URI. note that this is not formally equivalent to gemtext's link syntax, which also allows paths in place of URIs; [`cortav] does not. the gemtext line ["=> /somewhere] would need to be expressed as ["=> file:/somewhere], and ["=> /somewhere?key=val] as ["http:/somewhere?key=val] (or ["gemini:/somewhere?key=val], if the result is to be served over a gemini server).
* [*empty lines] (that is, lines consisting of nothing but whitespace) constitute a [!break], which terminates multiline objects that do not have a dedicated termination sequence, for example lists and asides.

##onspans styled text
most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested.

* strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color.
................................................................................
* emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics
* custom style {span .|id|[$styled-text]}: applies a specially defined font style. for example, if you have defined [`caution] to mean "demibold italic underline", cortav will try to apply the proper weight and styling within the constraints of the current font to the span [$styled-text]. see the [>fonts-sty fonts section] for more information about this mechanism.
* literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace
* variable {obj $|styled-text}: indicates to the reader that its text is a placeholder, rather than a literal representation. generally rendered in italic monospace, ideally of a different color
* underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text  [!is] distinct from links, but underlining non-links is still a violation of convention.
* strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion
* insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body.
** consider using a macro definition [`\edit: [~[#1]][+[#2]]] to save typing if you are doing editing work
* link [` \[>[$ref] [$styled-text]\]]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link.
* footnote {span ^|ref|[$styled-text]}: annotates the text with a defined footnote. in interactive output media [`\[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism\]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found.
* superscript {obj '|[$styled-text]}
* subscript {obj ,|[$styled-text]}
* raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket, and backslashes.
* raw literal [` \["[$raw-text]\]]: shorthand for a raw inside a literal, that is ["[`[\\…]]]
* macro [` \{[$name] [$arguments]}]: invokes a [>ex.mac macro] inline, specified with a reference. if the result of macro expansion contains newlines, they will be treated as line breaks, rather than paragraph breaks as they would be in a multiline context.
* argument {obj #|var}: in macros only, inserts the [$var]-th argument. otherwise, inserts a context variable provided by the renderer.
* raw argument {obj ##|var}: like above, but does not evaluate [$var].
................................................................................
	.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
................................................................................
+ english :+ ranuir + zia ţai  + thaliste        +
| honor   :| tef    | pang     | mbecheve        |
| rakewyrm:| hirvag | hi phang | nache umwelinde |
| eat     :| fese   | dzia     | rotechqa        |

and now the other way around!

+:english  :| honor |
+:ranuir   :| tef   |
+:zia ţai  :| pang  |
+:thalishte:| mbecheve |
~~~

##extns extensions
the cortav specification also specifies a number of extensions that do not have to be supported for a renderer to be compliant. the extension mechanism supports the following directives.

* inhibits: prevents an extension from being used even where available
................................................................................

the highlighter should make use of semantic HTML tags like [`<var>] where possible.

###lua lua
renderers with a lua interpreter available can evaluate lua code:
* [`%lua use [!file]]: evaluates [$file] and makes its definitions available
* [`\[%lua raw [!script]\]]: evaluates [$script] and emits the string it returns (if any) in raw span context.
* [` \[%lua exp [!script]\]]: evaluates [$script] and emits the string it returns (if any) in expanded span context.
* [`%lua raw [!script]]: evaluates [$script] and emits the string array it returns (if any) in raw block context.
* [`%lua exp [!script]]: evaluates [$script] and emits the string array it returns (if any) in expanded block context.

the interpreter should provide a [`cortav] table with the objects:
* [`ctx]: contains context variables

used files should return a table with the following members
................................................................................
[`ts] enables the directives:
* [`%[*ts] class [$scope level] ([$styled-text])]: indicates a classification level for either the whole document (scope [$doc]) or the next section (scope [$sec]). if the ts level is below [$level], the section will be redacted or rendering will fail with an error, as appropriate. if styled-text is included, this will be treated as the name of the classification level.
* [`%[*ts] word [$scope word] ([$styled-text])]: indicates a codeword clearance that must be present for the text to render. if styled-text is present, this will be used to render the name of the codeword instead of [$word].
* [`%[*when] ts level [$level]]
* [`%[*when] ts word [$word]]

[`ts] enables the spans:
* [` \[🔒#[$level] [$styled-text]\]]: redacts the span if the security level is below that specified.
* [` \[🔒.[$word] [$styled-text]\]]: redacts the span if the specified codeword clearance is not enabled.
(the padlock emoji is shorthand for [`%[*ts]].)

[`ts] redacts spans securely; that is, they are simply replaced with an indicator that they have been redacted, without visually leaking the length of the redacted text. redacted sections are simply omitted.

~~~#ts-example example [cortav] ~~~
%ts word doc sorrowful-pines SORROWFUL PINES

................................................................................
[*do note] that no cortav implementation needs to concern itself with character class data. this functionality is provided in the reference implementation strictly as an (optional) extension to the spec to improve usability, not as a normative requirement.

	corran: http://ʞ.cc/fic/spirals/society
	tengwar: https://en.wikipedia.org/wiki/Tengwar

###refimpl-switches switches
[`cortav.lua] offers various switches to control its behavior.
+ long                      + short + function                                    +
| [`--out [$file]]              :|:[`-o]:| sets the output file (default stdout)       |
| [`--log [$file]]              :|:[`-l]:| sets the log file (default stderr)          |
| [`--define [$var] [$val]]     :|:[`-d]:| sets the context variable [$var] to [$val]  |
| [`--mode-set [$mode]]         :|:[`-y]:| activates the [>refimpl-mode mode] with ID [!mode]
| [`--mode-clear [$mode]]       :|:[`-n]:| disables the mode with ID [!mode]           |
| [`--mode [$id] [$val]]        :|:[`-m]:| configures mode [$id] with the value [$val] |
| [`--mode-set-weak [$mode]]    :|:[`-Y]:| activates the [>refimpl-mode mode] with ID [$mode] if the source file does not specify otherwise







|








|
|
|



|







 







|
|
|







 







|
|

|







 







|
|
|







 







|







 







|
|







 







|







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
...
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
...
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
*[*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. 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
* [*cross-references] ([`=>] [`⇒]): inserts a block-level link. has two forms for the sake of gemtext compatibility. [$styled-text] is a descriptive text of the destination. especially useful for menus and gemtext output.
** the cortav syntax is [`=>[$ident] [$styled-text]], where [$ident] is an identifier; links to the same destination as [`\[>[$ident] [$styled-text]\]] would
** the compatibility syntax is [`=> [$uri] [$styled-text]] (note the space before [$uri]!). instead of taking an identifier for an object in the document, it directly accepts a URI. note that this is not formally equivalent to gemtext's link syntax, which also allows paths in place of URIs; [`cortav] does not. the gemtext line ["=> /somewhere] would need to be expressed as ["=> file:/somewhere], and ["=> /somewhere?key=val] as ["http:/somewhere?key=val] (or ["gemini:/somewhere?key=val], if the result is to be served over a gemini server).
* [*empty lines] (that is, lines consisting of nothing but whitespace) constitute a [!break], which terminates multiline objects that do not have a dedicated termination sequence, for example lists and asides.

##onspans styled text
most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested.

* strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color.
................................................................................
* emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics
* custom style {span .|id|[$styled-text]}: applies a specially defined font style. for example, if you have defined [`caution] to mean "demibold italic underline", cortav will try to apply the proper weight and styling within the constraints of the current font to the span [$styled-text]. see the [>fonts-sty fonts section] for more information about this mechanism.
* literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace
* variable {obj $|styled-text}: indicates to the reader that its text is a placeholder, rather than a literal representation. generally rendered in italic monospace, ideally of a different color
* underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text  [!is] distinct from links, but underlining non-links is still a violation of convention.
* strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion
* insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body.
** consider using a macro definition ["edit: [~[#1]][+[#2]]] to save typing if you are doing editing work
* link [`\[>[$ref] [$styled-text]\]]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link.
* footnote {span ^|[$ref]|[$styled-text]}: annotates the text with a defined footnote. in interactive output media ["[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. [$ref] can be the ID of a reference, in which case the reference value is parsed as [`cortav] markup to form the body of the footnote; it can also be the ID of a resource, which can be of any MIME type compatible with the current renderer, as as [`text/x.cortav], [`text/plain], or [`image/png].
* superscript {obj '|[$styled-text]}
* subscript {obj ,|[$styled-text]}
* raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket, and backslashes.
* raw literal [` \["[$raw-text]\]]: shorthand for a raw inside a literal, that is ["[`[\\…]]]
* macro [` \{[$name] [$arguments]}]: invokes a [>ex.mac macro] inline, specified with a reference. if the result of macro expansion contains newlines, they will be treated as line breaks, rather than paragraph breaks as they would be in a multiline context.
* argument {obj #|var}: in macros only, inserts the [$var]-th argument. otherwise, inserts a context variable provided by the renderer.
* raw argument {obj ##|var}: like above, but does not evaluate [$var].
................................................................................
	.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
................................................................................
+ english :+ ranuir + zia ţai  + thaliste        +
| honor   :| tef    | pang     | mbecheve        |
| rakewyrm:| hirvag | hi phang | nache umwelinde |
| eat     :| fese   | dzia     | rotechqa        |

and now the other way around!

+:english  :| honor    |
+:ranuir   :| tef      |
+:zia ţai  :| pang     |
+:thalishte:| mbecheve |
~~~

##extns extensions
the cortav specification also specifies a number of extensions that do not have to be supported for a renderer to be compliant. the extension mechanism supports the following directives.

* inhibits: prevents an extension from being used even where available
................................................................................

the highlighter should make use of semantic HTML tags like [`<var>] where possible.

###lua lua
renderers with a lua interpreter available can evaluate lua code:
* [`%lua use [!file]]: evaluates [$file] and makes its definitions available
* [`\[%lua raw [!script]\]]: evaluates [$script] and emits the string it returns (if any) in raw span context.
* [`\[%lua exp [!script]\]]: evaluates [$script] and emits the string it returns (if any) in expanded span context.
* [`%lua raw [!script]]: evaluates [$script] and emits the string array it returns (if any) in raw block context.
* [`%lua exp [!script]]: evaluates [$script] and emits the string array it returns (if any) in expanded block context.

the interpreter should provide a [`cortav] table with the objects:
* [`ctx]: contains context variables

used files should return a table with the following members
................................................................................
[`ts] enables the directives:
* [`%[*ts] class [$scope level] ([$styled-text])]: indicates a classification level for either the whole document (scope [$doc]) or the next section (scope [$sec]). if the ts level is below [$level], the section will be redacted or rendering will fail with an error, as appropriate. if styled-text is included, this will be treated as the name of the classification level.
* [`%[*ts] word [$scope word] ([$styled-text])]: indicates a codeword clearance that must be present for the text to render. if styled-text is present, this will be used to render the name of the codeword instead of [$word].
* [`%[*when] ts level [$level]]
* [`%[*when] ts word [$word]]

[`ts] enables the spans:
* [`\[🔒#[$level] [$styled-text]\]]: redacts the span if the security level is below that specified.
* [`\[🔒.[$word] [$styled-text]\]]: redacts the span if the specified codeword clearance is not enabled.
(the padlock emoji is shorthand for [`%[*ts]].)

[`ts] redacts spans securely; that is, they are simply replaced with an indicator that they have been redacted, without visually leaking the length of the redacted text. redacted sections are simply omitted.

~~~#ts-example example [cortav] ~~~
%ts word doc sorrowful-pines SORROWFUL PINES

................................................................................
[*do note] that no cortav implementation needs to concern itself with character class data. this functionality is provided in the reference implementation strictly as an (optional) extension to the spec to improve usability, not as a normative requirement.

	corran: http://ʞ.cc/fic/spirals/society
	tengwar: https://en.wikipedia.org/wiki/Tengwar

###refimpl-switches switches
[`cortav.lua] offers various switches to control its behavior.
+ long                           + short + function                                    +
| [`--out [$file]]              :|:[`-o]:| sets the output file (default stdout)       |
| [`--log [$file]]              :|:[`-l]:| sets the log file (default stderr)          |
| [`--define [$var] [$val]]     :|:[`-d]:| sets the context variable [$var] to [$val]  |
| [`--mode-set [$mode]]         :|:[`-y]:| activates the [>refimpl-mode mode] with ID [!mode]
| [`--mode-clear [$mode]]       :|:[`-n]:| disables the mode with ID [!mode]           |
| [`--mode [$id] [$val]]        :|:[`-m]:| configures mode [$id] with the value [$val] |
| [`--mode-set-weak [$mode]]    :|:[`-Y]:| activates the [>refimpl-mode mode] with ID [$mode] if the source file does not specify otherwise

Modified cortav.lua from [c03c132cce] to [648cf1722d].

738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756

757
758

759
760
761

762
763
764
765
766
767
768
769
770
771
772
773
...
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
		}
	end
	ct.spanctls = {
		{seq = '!', parse = formatter 'emph'};
		{seq = '*', parse = formatter 'strong'};
		{seq = '~', parse = formatter 'strike'};
		{seq = '+', parse = formatter 'insert'};
		{seq = '"', parse = rawcode};
		-- deprecated
			{seq = '`\\', parse = rawcode};
			{seq = '\\\\', parse = rawcode};
		{seq = '\\', parse = function(s, c) -- raw
			return {
				kind = 'raw';
				spans = {s};
				origin = c:clone();
			}
		end};
		{seq = '`', parse = formatter 'literal'};

		{seq = '$', parse = formatter 'variable'};
		{seq = '^', parse = function(s,c) --footnotes

			local r, t = s:match '^([^%s]+)%s*(.-)$'
			return {
				kind = 'footnote';

				ref = r;
				spans = ct.parse_span(t, c);
				origin = c:clone();
			}
		-- TODO support for footnote sections
		end};
		{seq = '=', parse = function(s,c) --math mode
			local tx = {
				['%*'] = '×';
				['/'] = '÷';
			}
			for k,v in pairs(tx) do s = s:gsub(k,v) end
................................................................................
		end};
		{seq = '&', parse = function(s, c)
			local r, t = s:match '^([^%s]+)%s*(.-)$'
			return {
				kind = 'deref';
				spans = (t and t ~= "") and ct.parse_span(t, c) or {};
				ref = r;
				origin = c:clone();
			}
		end};
		{seq = '^', parse = function(s, c)
			local fn, t = s:match '^([^%s]+)%s*(.-)$'
			return {
				kind = 'footnote';
				spans = (t and t~='') and ct.parse_span(t, c) or {};
				ref = fn;
				origin = c:clone();
			}
		end};
		{seq = '>', parse = insert_link};
		{seq = '→', parse = insert_link};
		{seq = '🔗', parse = insert_link};
		{seq = '##', parse = insert_var_ref(true)};







<
<
<
<








>

|
>
|


>
|
<


<







 







<
<
<
<
<
<
<
<
<







738
739
740
741
742
743
744




745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761

762
763

764
765
766
767
768
769
770
...
786
787
788
789
790
791
792









793
794
795
796
797
798
799
		}
	end
	ct.spanctls = {
		{seq = '!', parse = formatter 'emph'};
		{seq = '*', parse = formatter 'strong'};
		{seq = '~', parse = formatter 'strike'};
		{seq = '+', parse = formatter 'insert'};




		{seq = '\\', parse = function(s, c) -- raw
			return {
				kind = 'raw';
				spans = {s};
				origin = c:clone();
			}
		end};
		{seq = '`', parse = formatter 'literal'};
		{seq = '"', parse = rawcode};
		{seq = '$', parse = formatter 'variable'};
		{seq = '^', parse = function(s, c)
		-- TODO support for footnote sections
			local fn, t = s:match '^([^%s]+)%s*(.-)$'
			return {
				kind = 'footnote';
				spans = (t and t~='') and ct.parse_span(t, c) or {};
				ref = fn;

				origin = c:clone();
			}

		end};
		{seq = '=', parse = function(s,c) --math mode
			local tx = {
				['%*'] = '×';
				['/'] = '÷';
			}
			for k,v in pairs(tx) do s = s:gsub(k,v) end
................................................................................
		end};
		{seq = '&', parse = function(s, c)
			local r, t = s:match '^([^%s]+)%s*(.-)$'
			return {
				kind = 'deref';
				spans = (t and t ~= "") and ct.parse_span(t, c) or {};
				ref = r;









				origin = c:clone();
			}
		end};
		{seq = '>', parse = insert_link};
		{seq = '→', parse = insert_link};
		{seq = '🔗', parse = insert_link};
		{seq = '##', parse = insert_var_ref(true)};

Modified desk/cortav.xml from [dc63948a6a] to [a3601c692e].

6
7
8
9
10
11
12
13
14
15
16




17
18
19
20
21
22
23
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  ? Kate/kwrite-compatible syntax definition for the cortav markup format
  > ln cortav.xml $HOME/.local/share/org.kde.syntax-highlighting/syntax/
  ! NOTE: the Kate syntax engine cannot capture all the syntactic properties
          of Cortav. we do the best we can, but note the following important
          discrepancies:

            1) the inline resource syntax allows a wide range of complex
               brackets pairs such as <!--:[ ]:--!>; the Kate syntax only
               accounts for the pair { }
-->
<language name='Cortav' version='1' kateversion='2.4' section='Markup' extensions='*.ct'>




	<highlighting>
		<list name='extension-directives'>
			<item>uses</item>
			<item>needs</item>
			<item>inhibits</item>
		</list>
		<list name='meta-directives'>
................................................................................
			</context>

			<context name='span-del' attribute='Deleted Text' lineEndContext='#pop'>
				<IncludeRules context='span'/>
			</context>

			<context name='span-cue' attribute='Span Cue' lineEndContext='#pop' fallthroughContext="error">
				<StringDetect attribute='Span Cue' String='`\' context='#pop!flat-span' />
				<StringDetect attribute='Span Cue' String='"' context='#pop!flat-span' />

				<DetectChar   attribute='Span Cue' char='!' context='#pop!span-emph' />
				<DetectChar   attribute='Span Cue' char='*' context='#pop!span-strong' />
				<DetectChar   attribute='Span Cue' char='~' context='#pop!span-del' />

				<AnyChar      attribute='Span Cue' String='`$+🔒' context='#pop!span' />







|


|
>
>
>
>







 







<







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
162
163
164
165
166
167
168

169
170
171
172
173
174
175
  ? Kate/kwrite-compatible syntax definition for the cortav markup format
  > ln cortav.xml $HOME/.local/share/org.kde.syntax-highlighting/syntax/
  ! NOTE: the Kate syntax engine cannot capture all the syntactic properties
          of Cortav. we do the best we can, but note the following important
          discrepancies:

            1) the inline resource syntax allows a wide range of complex
               brackets pairs such as <!-:[ ]:-!>; the Kate syntax only
               accounts for the pair { }
-->
<language name='Cortav'
			 section='Markup'
			 extensions='*.ct'
			 version='1'
			 kateversion='2.4'>
	<highlighting>
		<list name='extension-directives'>
			<item>uses</item>
			<item>needs</item>
			<item>inhibits</item>
		</list>
		<list name='meta-directives'>
................................................................................
			</context>

			<context name='span-del' attribute='Deleted Text' lineEndContext='#pop'>
				<IncludeRules context='span'/>
			</context>

			<context name='span-cue' attribute='Span Cue' lineEndContext='#pop' fallthroughContext="error">

				<StringDetect attribute='Span Cue' String='"' context='#pop!flat-span' />

				<DetectChar   attribute='Span Cue' char='!' context='#pop!span-emph' />
				<DetectChar   attribute='Span Cue' char='*' context='#pop!span-strong' />
				<DetectChar   attribute='Span Cue' char='~' context='#pop!span-del' />

				<AnyChar      attribute='Span Cue' String='`$+🔒' context='#pop!span' />

Modified ext/toc.lua from [38c6bac1b2] to [823e0f174d].

221
222
223
224
225
226
227

228

229
230
231
232
233

234

235
236
237
238
239
240
241
					local sec = secptr.ref
					if sec.heading_node then -- does this section have a label?
						local ent = tag('li',nil,
							 catenate{tag('a', {href='#'..getSafeID(sec)},
								sr.htmlSpan(sec.heading_node.spans, sec.heading_node, sec))})
						if secptr.depth > #stack then
							local n = {tag = 'ol', attrs={}, nodes={ent}}

							table.insert(top().nodes[#top().nodes].nodes, n)

							table.insert(stack, n)
						else
							if secptr.depth < #stack then
								for j=#stack,secptr.depth+1,-1 do stack[j] = nil end
							end

							table.insert(top().nodes, ent)

						end

						-- now we need to assemble a list of items within the
						-- section worthy of an entry on their own. currently
						-- this is only anchors created with %toc mark|name
						local innerlinks = {}
						local noteworthy = { anchor = true }







>
|
>





>
|
>







221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
					local sec = secptr.ref
					if sec.heading_node then -- does this section have a label?
						local ent = tag('li',nil,
							 catenate{tag('a', {href='#'..getSafeID(sec)},
								sr.htmlSpan(sec.heading_node.spans, sec.heading_node, sec))})
						if secptr.depth > #stack then
							local n = {tag = 'ol', attrs={}, nodes={ent}}
							if top() then
								table.insert(top().nodes[#top().nodes].nodes, n)
							end
							table.insert(stack, n)
						else
							if secptr.depth < #stack then
								for j=#stack,secptr.depth+1,-1 do stack[j] = nil end
							end
							if top() then
								table.insert(top().nodes, ent)
							end
						end

						-- now we need to assemble a list of items within the
						-- section worthy of an entry on their own. currently
						-- this is only anchors created with %toc mark|name
						local innerlinks = {}
						local noteworthy = { anchor = true }

Modified render/html.lua from [4a60de4c59] to [e235e554e3].

55
56
57
58
59
60
61











62
63
64
65
66
67
68
..
98
99
100
101
102
103
104






105
106
107
108
109
110
111
...
145
146
147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
...
761
762
763
764
765
766
767

768
769
770
771
772
773
774
...
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
....
1189
1190
1191
1192
1193
1194
1195
1196




















1197
1198
1199
1200
1201

1202
1203
1204
1205
1206
1207
1208
....
1215
1216
1217
1218
1219
1220
1221

1222





1223
1224
1225
1226
1227


1228
1229

1230
1231
1232
1233
1234
1235
1236
1237
			li {
				padding: 0.1em 0;
			}
		]];
		list_ordered = [[]];
		list_unordered = [[]];
		footnote = [[











			aside.footnote {
				font-family: 90%;
				grid-template-columns: 1em 1fr min-content;
				grid-template-rows: 1fr min-content;
				position: fixed;
				padding: 1em;
				background: @tone(0.03);
................................................................................
					backdrop-filter: blur(0px);
				}
				aside.footnote:target ~ #cover {
					opacity: 100%;
					pointer-events: all;
					backdrop-filter: blur(5px);
				}






			}
			@media print {
				aside.footnote {
					display: grid;
					position: relative;
				}
				aside.footnote:first-of-type {
................................................................................
				}
			}
			aside.footnote > div.number {
				text-align:right;
				grid-row: 1/2;
				grid-column: 1/2;
			}
			aside.footnote > div.text {
				grid-row: 1/2;
				grid-column: 2/4;
				padding-left: 1em;
				overflow-y: auto;

			}
			aside.footnote > div.text > p:first-child {
				margin-top: 0;
			}
		]];
		header = [[
			body { padding: 0 2.5em !important }
			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; }
................................................................................
		end
		function span_renderers.footnote(f,b,s)
			local linkattr = {}
			if opts.epub then
				linkattr['epub:type'] = 'noteref'
			else
				addStyle 'footnote'

			end
			local source, sid, ssec = b.origin:ref(f.ref)
			local cnc = getSafeID(ssec) .. ' ' .. sid
			local fn
			if footnotes[cnc] then
				fn = footnotes[cnc]
			else
................................................................................
						table.insert(secnodes, block_renderers[bl.kind](bl,sec))
					end
				end
				if next(secnodes) then
					if doc.secorder[2] then --#secs>1?
						-- only wrap in a section if >1 section
						table.insert(ir, tag('section',
													{id = getSafeID(sec)},
													secnodes))
					else
						ir = secnodes
					end
				end
			end
			return ir
		end
................................................................................
	do local fnsorted = {}
		for _, fn in pairs(footnotes) do
			fnsorted[fn.num] = fn
		end

		for _, fn in ipairs(fnsorted) do
			local tag = tagproc.toIR.tag
			local body = {nodes={}}




















			local ftir = {}
			for l in fn.source:gmatch('([^\n]*)') do
				ct.parse_line(l, fn.origin, ftir)
			end
			renderBlocks(ftir,body)

			local fattr = {id=fn.id}
			if opts.epub then
				---UUUUUUGHHH
				local npfx = string.format('(%u) ', fn.num)
				if next(body.nodes) then
					local n = body.nodes[1]
					repeat
................................................................................
						else
							n.nodes[1] = {tag='p',attrs={},nodes={npfx}}
							break
						end
					until false

				else

					body.nodes[1] = {tag='p',attrs={},nodes={npfx}}





				end
				fattr['epub:type'] = 'footnote'
			else
				fattr.class = 'footnote'
			end


			local note = tag('aside', fattr, opts.epub and body.nodes or {
				tag('div',{class='number'}, tostring(fn.num)),

				tag('div',{class='text'}, body.nodes),
				tag('a',{href='#0'},'⤫')
			})
			table.insert(ir, note)
		end
	end
	if next(footnotes) and not opts.epub then
		table.insert(ir, tagproc.toIR.tag('div',{id='cover'},''))







>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>







 







|




>

|







 







>







 







|
|







 







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
>







 







>
|
>
>
>
>
>





>
>


>
|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
...
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
....
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
....
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
			li {
				padding: 0.1em 0;
			}
		]];
		list_ordered = [[]];
		list_unordered = [[]];
		footnote = [[
			@media screen {
				a[href].fnref {
					text-decoration-style: dashed;
					color: @tone(0.7 45);
					text-decoration-color: @tone/0.4(0.7 45);
				}
				a[href]:hover.fnref {
					color: @tone(0.9 45);
					text-decoration-color: @tone/0.7(0.7 45);
				}
			}
			aside.footnote {
				font-family: 90%;
				grid-template-columns: 1em 1fr min-content;
				grid-template-rows: 1fr min-content;
				position: fixed;
				padding: 1em;
				background: @tone(0.03);
................................................................................
					backdrop-filter: blur(0px);
				}
				aside.footnote:target ~ #cover {
					opacity: 100%;
					pointer-events: all;
					backdrop-filter: blur(5px);
				}
			}
			@media screen and (max-width: calc(@width + 20em)) {
				aside.footnote {
					left: 1em;
					right: 1em;
				}
			}
			@media print {
				aside.footnote {
					display: grid;
					position: relative;
				}
				aside.footnote:first-of-type {
................................................................................
				}
			}
			aside.footnote > div.number {
				text-align:right;
				grid-row: 1/2;
				grid-column: 1/2;
			}
			aside.footnote > .text {
				grid-row: 1/2;
				grid-column: 2/4;
				padding-left: 1em;
				overflow-y: auto;
				margin-top: 0;
			}
			aside.footnote > .text > :first-child {
				margin-top: 0;
			}
		]];
		header = [[
			body { padding: 0 2.5em !important }
			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; }
................................................................................
		end
		function span_renderers.footnote(f,b,s)
			local linkattr = {}
			if opts.epub then
				linkattr['epub:type'] = 'noteref'
			else
				addStyle 'footnote'
				linkattr.class = 'fnref'
			end
			local source, sid, ssec = b.origin:ref(f.ref)
			local cnc = getSafeID(ssec) .. ' ' .. sid
			local fn
			if footnotes[cnc] then
				fn = footnotes[cnc]
			else
................................................................................
						table.insert(secnodes, block_renderers[bl.kind](bl,sec))
					end
				end
				if next(secnodes) then
					if doc.secorder[2] then --#secs>1?
						-- only wrap in a section if >1 section
						table.insert(ir, tag('section',
							{id = getSafeID(sec)},
							secnodes))
					else
						ir = secnodes
					end
				end
			end
			return ir
		end
................................................................................
	do local fnsorted = {}
		for _, fn in pairs(footnotes) do
			fnsorted[fn.num] = fn
		end

		for _, fn in ipairs(fnsorted) do
			local tag = tagproc.toIR.tag
			local body
			if type(fn.source) == 'table' then
				if fn.source.kind == 'resource' then
					local fake_embed = {
						kind = 'embed';
						rsrc = fn.source;
						origin = fn.origin;
						mode = 'inline';
					}
					local rendered = astproc.toIR.block_renderers.embed(
						fake_embed, fn.origin.sec
					)
					if not rendered then
						fn.origin:fail('unacceptable resource mime type “%s” for footnote target “%s”', fn.source.mime, fn.source.id or '(anonymous)')
					end
					body = rendered
				else
					fn.origin:fail('footnote span links to block “%s” of unacceptable kind “%s”', fn.source.kind)
				end
			else
				body = {tag='div',nodes={}}
				local ftir = {}
				for l in fn.source:gmatch('([^\n]*)') do
					ct.parse_line(l, fn.origin, ftir)
				end
				renderBlocks(ftir,body)
			end
			local fattr = {id=fn.id}
			if opts.epub then
				---UUUUUUGHHH
				local npfx = string.format('(%u) ', fn.num)
				if next(body.nodes) then
					local n = body.nodes[1]
					repeat
................................................................................
						else
							n.nodes[1] = {tag='p',attrs={},nodes={npfx}}
							break
						end
					until false

				else
					if body.tag == 'div' then
						body.nodes[1] = {tag='p',attrs={},nodes={npfx}}
					elseif body.tag == 'pre' then
						body.nodes[1] = npfx .. body.nodes[1]
					else
						body = {tag='div', nodes = {npfx, body}}
					end
				end
				fattr['epub:type'] = 'footnote'
			else
				fattr.class = 'footnote'
			end
			body.attrs = body.attrs or {}
			body.attrs.class = 'text'
			local note = tag('aside', fattr, opts.epub and body.nodes or {
				tag('div',{class='number'}, tostring(fn.num)),
				body,
-- 				tag('div',{class='text'}, body.nodes),
				tag('a',{href='#0'},'⤫')
			})
			table.insert(ir, note)
		end
	end
	if next(footnotes) and not opts.epub then
		table.insert(ir, tagproc.toIR.tag('div',{id='cover'},''))