Compact Web Language (COWEL)

github.com/eisenwave/cowel

Contents

1

Introduction

2

Motivation

3

Getting started

3.1

Native installation

4

Syntax

4.1

EBNF grammar

4.2

Text

4.2.1

Whitespace

4.3

Escape sequences

4.4

Comments

4.5

Directives

4.5.1

Directive names

4.5.2

Directive arguments

4.5.2.1
Named arguments
4.5.2.2
Positional arguments
4.5.2.3
Ellipses
4.5.2.4
Groups
4.5.3

Directive content

5

Document processing

5.1

Content policies

5.2

Directive processing

5.2.1

Lazy processing

6

Themes

7

New directives

7.1

Common principles

7.1.1

Parameter notation

7.1.2

Usual argument matching

7.2

Character utilities

7.2.1

\cowel_char_by_entity — Character by HTML character reference

7.2.2

\cowel_char_by_name — Character by Unicode name

7.2.3

\cowel_char_by_num — Character by code point number

7.2.4

\cowel_char_get_num — Get digits of character

7.3

HTML utilities

7.3.1

\cowel_html_element — HTML element

7.3.2

\cowel_html_self_closing_element — Self-closing HTML element

7.4

Content policies

7.4.1

\cowel_to_html — Process with to-HTML policy

7.4.2

\cowel_no_invoke — Process with no-invoke policy

7.4.3

\cowel_actions — Process with actions policy

7.4.4

\cowel_text_only — Process with text-only policy

7.4.5

\cowel_text_as_html — Process with text-as-HTML policy

7.4.6

\cowel_source_as_text — Process with source-as-text policy

7.5

Syntax highlighting

7.5.1

\cowel_highlight — Perform syntax highlighting

7.5.2

\cowel_highlight_as — Syntax highlight override

7.5.3

\cowel_highlight_phantom — Syntax highlight phantom text

7.6

Paragraph control

7.6.1

Paragraph splitting

7.6.2

\cowel_paragraphs — Perform paragraph splitting

7.6.3

\cowel_paragraph_enter — Enter a paragraph

7.6.4

\cowel_paragraph_leave — Leave a paragraph

7.6.5

\cowel_paragraph_inherit — Activate paragraph splitting inside a directive

7.7

File management

7.7.1

\cowel_include — Include a sub-document

7.7.2

\cowel_include_text — Include text from a file

7.8

Aliases and macros

7.8.1

\cowel_alias — Define aliases for an existing directive

7.8.2

\cowel_macro — Define a macro

7.8.3

\cowel_put — Process provided macro content or arguments

7.9

Invocations

7.9.1

\cowel_invoke — Invoke a directive

8

Legacy directives

8.1

Comments

8.1.1

\comment — Comments

8.2

Text formatting

8.3

Code and syntax highlighting

8.3.1

\code — Inline code

8.3.1.1
Manual nested highlighting
8.3.1.2
Further advice on highlighting
8.3.2

\codeblock — Code blocks

8.3.3

\hl — Syntax highlight override

8.3.4

\pre — Preformatted blocks

8.3.5

\literally — Treat input literally as text

8.3.6

\unprocessed — Suppress directive processing

8.4

Math

8.4.1

\math — Inline math

8.4.2

\mathblock — Math blocks

8.5

Separators and word breaking

8.5.1

\br — Break line

8.5.2

\hr — Horizontal rule

8.5.3

\wbr — Word break opportunity

8.5.4

\nobr — Unbroken words

8.6

Special blocks

8.6.1

\Babstract — Abstract blocks

8.6.2

\Bquote — Quote blocks

8.6.3

\blockquote — Alias for \Bquote

8.6.4

\Bug — Bug blocks

8.6.5

\Bdecision — Decision blocks

8.6.6

\Bdel — Deletion blocks

8.6.7

\Bdetails — Details blocks aka spoilers

8.6.8

\details — Alias for \Bdetails

8.6.9

\Bdiff — Difference blocks

8.6.10

\Bex — Example blocks

8.6.11

\Bins — Insertion blocks

8.6.12

\Bimp — Important blocks

8.6.13

\Bnote — Note blocks

8.6.14

\Btip — Tip blocks

8.6.15

\Btodo — TODO blocks

8.6.16

\Bwarn — Warning blocks

8.7

Lists

8.7.1

\ul — Unordered lists

8.7.2

\ol — Ordered lists

8.7.3

\li — List items

8.7.4

\item — List item pseudo-directive

8.7.5

\dl — Definition lists

8.8

Tables

8.9

Headings

8.9.1

\make_contents — Make table of contents

8.10

Bibliography

8.10.1

\bib — Add bibliography entry

8.10.2

\make_bib — Generate bibliography

8.11

References

8.11.1

\ref — References

8.11.2

\mail — E-Mail addresses

8.11.3

\tel — Telephone numbers

8.12

Foreign languages

8.12.1

\script — JavaScript blocks

8.12.2

\noscript — No-JavaScript content

8.12.3

\style — CSS blocks

8.13

Sections

8.13.1

\there — Append content to section

8.13.2

\here — Copy section content

8.13.3

\hereblock — Copy section content in block

8.14

Miscellaneous

8.14.1

\div — Content division

8.14.2

\trim — Trim input

8.14.3

\text — Plaintext context

8.14.4

\p — Paragraphs

8.15

Variables and calculations

8.15.1

\Vset — Set a variable

8.15.2

\Vget — Get a variable

8.15.3

\Cadd — Perform addition

8.15.4

\Csub — Perform subtraction

8.15.5

\Cmul — Perform multiplication

8.15.6

\Cdiv — Perform division

1. Introduction

COWEL is a markup language with TeX-like syntax, intended to generate HTML documents, mainly for proposals and technical text. Many of its features are purpose-built for use in WG21, such as for writing C++ proposals.

The following COWEL code ...

Hello, \strong{strong} world!

... generates the HTML ...

Hello, <strong>strong</strong> world!

... which renders as:

Hello, strong world!

2. Motivation

Many similar tools (mpark/wg21, bikeshed, etc.) are based on Markdown. This makes them beginner-friendly, but advanced formatting requires heavy use of Markdown extensions or mixed use of Markdown and HTML tags. Metadata such as bibliographies, document information, etc. also rely on yet another format (e.g. JSON, YAML).

This makes these tools difficult to master and makes the design incoherent. Why do we need three languages glued together just to format our documents?

COWEL is the missing middle, the missing link. It makes producing HTML a natural part of the language, lets you specify metadata, and more, all in one, simple syntax.

3. Getting started

To use and install COWEL, you need to install Node.js and npm. With npm installed, open a terminal and run:

npm i -g cowel # or npm install --global cowel

See also https://www.npmjs.com/package/cowel. With COWEL installed, you can convert COWEL documents to HTML by using:

cowel run input.cow output.html

To see a list of available options and commands, run cowel by itself or cowel --help.

You may need to use sudo or otherwise elevate your permissions because -g performs a "global"/system-wide installation. This is necessary to make the cowel command available everywhere.

It is recommended to use a .cow or .cowel file extension for COWEL documents.

Many text editors support associating such file extensions with TeX or LaTeX syntax highlighting, whose syntax is similar enough to get some basic highlighting. A VSCode plugin for proper highlighting is planned, among other features.

3.1. Native installation

There also exists a more limited native CLI for development purposes. To install it and run COWEL:

git clone https://github.com/Eisenwave/cowel.git --recursive cd cowel cmake -B build cmake --build build ./build/cowel-cli input.cow output.html

The npm CLI is slightly different from the native CLI, but they share some options, such as --severity and --help. Running ./build/cowel-cli without extra arguments also brings up the help menu.

4. Syntax

COWEL has a minimalistic but powerful syntax, built on top of only three syntactical constructs:

4.1. EBNF grammar

Formally, in EBNF grammar format, a document is constructed as follows:

document = { markup-element }; markup-element = text | escape | comment | directive; text = ? Maximal character sequence excluding "\" ?; escape = "\", escapable | "\", line-terminator; escapable = "{" | "}" | "[" | "]" | "(" | ")" | "," | "." | "=" | "!" | '"' | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "/" | ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~" | "-" | " " | TAB | VT ; line-terminator = LF | CR, LF | CR (* Ambiguities are resolved in favor of a single CRLF line-terminator, instead of two CR and LF line-terminators *); comment = "\:", ? Maximal character sequence up to and including line-terminator ?; directive = "\", directive-name, [ group ], [ "{", { markup-element }, "}" ]; directive-name = directive-name-character - digit, { directive-name-character }; directive-name-character = "_" | digit | ? ASCII alphabetic character ?; digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; group = "(", group-member-list, whitespace, ")"; group-member-list = member-comments, group-member, { whitespace, ",", member-comments, group-member }, whitespace, [ "," ]; group-member = member-name, whitespace, "=", whitespace, member-value | member-value | "..."; member-name = ? HTML attribute name ? - ("\" | "(" | ")" | "{", "}" | ","); member-value = { member-value-markup-element } | group; member-value-markup-element = member-value-text | escape | comment | directive; member-value-text = ? Maximal character sequence excluding "\", ",", or balanced ")" ?; member-comments = { comment | whitespace }; whitespace = { " " | TAB | LF | CR | VT }; TAB = ? U+0009 CHARACTER TABULATION ? (* \t in C *); LF = ? U+000A END OF LINE ? (* \n in C *); CR = ? U+000D CARRIAGE RETURN ? (* \r in C *); VT = ? U+000B LINE TABULATION ? (* \v in C *);

At the top-level in the document, there are no other syntactical features, meaning that as long as some bit of code doesn't contain a U+005C (\), it's all interpreted as plaintext. This makes COWEL exceptionally suited to nest other languages inside of it in code blocks etc.

4.2. Text

Text (text in the grammar) is a markup-element which consists of as many characters as possible, excluding U+005C (\).

text = ? Maximal character sequence excluding "\" ?;

In most situations, text is not processed specially. However, the output may depend on the context (§5.1. Content policies). For example, < in text will render as a < character, but will be converted to &lt; when fed into a To-HTML Policy. Furthermore, text is subject to special effects like syntax highlighting and paragraph splitting.

4.2.1. Whitespace

COWEL translates text in whitespace directly into HTML in most situations. Additional styling with CSS can be applied to interpret that whitespace literally (like in <pre> elements) or to have it merged into spaces, which happens by default.

At the document top-level, §7.6.1. Paragraph splitting takes place.

COWEL markup:

This displays on one line because line breaks render like spaces.

Generated HTML:

<p>This displays on one line because line breaks render like spaces.</p>

Rendered output:

This displays on one line because line breaks render like spaces.

4.3. Escape sequences

COWEL supports multiple escape sequences, all of which begin with a \ (like directives), but are followed by some special character. The meaning of the escape sequences is described in the table below.

Escape sequence Meaning
\{, \}, \\, \(, \), \,, \., \= Expands to the second character in the escape sequence. These sequences are stable.
\!, \", \#, \$, \%, \&, \', \*, \+, \-, \/, \;, \<, \>, \?, \@, \[, \], \^, \|, \~, \ U+0009 CHARACTER TABULATION, \ U+0020 SPACE Expands to the second character in the escape sequence. These sequences are unstable, meaning that they could be given a different meaning in the future. Use with caution!
\ U+000A LINE FEED, \ U+000B VERTICAL TABULATION, \ U+000D CARRIAGE RETURN, \ U+000A U+000D (CRLF escape) Expands to nothing; feature is stable

In EBNF grammar, an escape is constructed like:

escape = "", escapable | "", line-terminator; escapable = "{" | "}" | "[" | "]" | "(" | ")" | "," | "." | "=" | "!" | '"' | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "/" | ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~" | "-" | " " | TAB | VT ; line-terminator = LF | CR, LF | CR (* Ambiguities are resolved in favor of a single CRLF line-terminator, instead of two CR and LF line-terminators *);

If there is an ambiguity between a CRLF escape and a simple U+000D CARRIAGE RETURN escape, this ambiguity is resolved in favor of a CRLF escape.

The purpose of most escapes is to prevent a character from having special meaning. For example, the \, escape prevents a comma from being interpreted as a separator between directive arguments.

The purpose of the newline escapes is to remove unwanted newlines and to join long content that spans multiple lines, but without introducing any whitespace in the markup.

4.4. Comments

Comments begin with \: and extend until the end of the line. In almost every context, comments are discarded instead of being emitted into the output HTML.

This makes them useful for adding information to the COWEL markup.

Comments, just like any other content, are actually part of the AST and don't simply get discarded during parsing. This means that a comment wrapped in §7.4.6. \cowel_source_as_text — Process with source-as-text policy will render as expected:

\cowblock{\cowel_source_as_text{ \: A comment ... }}

This renders as:

\: A comment ...

In EBNF grammar, a comment is constructed as follows:

comment = "\:", ? Maximal character sequence up to and including line-terminator ?;

Comments include a line-terminator, which means that a trailing comment won't add any lines (not even blank lines) to the generated markup.

We can even join words by putting a comment between:

Hello\: this comment will absorb the newline World Hello \: this comment also does, but is preceded by a space World

Generated HTML:

HelloWorld Hello World

4.5. Directives

A directive is a way of generating plaintext, HTML, both, or neither. You can think of it as a "command" or "function call".

Every directive has three parts:

In EBNF grammar, a directive is constructed as follows:

directive = "", directive-name, [ group ], [ "{", { markup-element }, "}" ]; directive-name = directive-name-character - digit, { directive-name-character }; directive-name-character = "_" | digit | ? ASCII alphabetic character ?;

4.5.1. Directive names

A directive name selects a specific directive to be processed. The name begins with a U+005C (\) character, followed by a sequence of ASCII alphanumeric characters or U+005F (_). However, the first character after U+005C (\) cannot be an ASCII digit.

You can use an empty block of content to "cut a name short":

Thisisavery\wbr{}longword.

Doing so is equivalent to writing \wbr and longword immediately following one another.

Grammatically, the arguments are a group, which is constructed as follows:

group = "(", group-member-list, whitespace, ")"; group-member-list = member-comments, group-member, { whitespace, ",", member-comments, group-member }, whitespace, [ "," ]; group-member = member-name, whitespace, "=", whitespace, member-value | member-value | "...";

4.5.2. Directive arguments

Directive arguments can be positional arguments, named arguments, or ellipses. Arguments are separated by commas. Unlike anywhere else, whitespace surrounding directive arguments is ignored. The value of positional or named argument is either a markup sequence or a group.

Often, arguments are converted to HTML attributes.

\b(id = abc){Bold text.}

Generated HTML:

<b id=abc>Bold text.</b>
4.5.2.1. Named arguments

Named arguments are of the form name = value, where spaces anywhere around name and value are ignored. Named arguments are matched to directive parameters before any positional arguments, meaning that positional arguments fill the remaining unmatched arguments.

Argument names can consist of any character, except ASCII control characters, U+0020 SPACE, U+0022 ("), U+0027 ('), U+002C (,), U+002F (/), U+003D (=), U+003E (>), U+005B ([), U+005C (\), U+005D (]), U+007B ({), U+007D (}), and noncharacters.

If you want to provide a positional argument that is name = value instead, you can do so by writing:

\: "name = value" as a positional argument \xyz(name \\= value) \: named argument with name "name" and value "value" \xyz(name = value)

Only the literal = character can be used for named arguments, not its escaped variant \=.

Grammatically, a member-name is constructed as follows:

member-name = ? HTML attribute name ? - ("\" | "(" | ")" | "{", "}" | ",");
4.5.2.2. Positional arguments

Positional arguments are content just like any other content, except that an unbalanced closing U+005D (]) or a U+002C (,) ends the content.

You can use escape sequences to continue otherwise ended content.

\xyz(hello, world) \: Two positional arguments \xyz(hello\, world) \: One positional argument containing a comma. \xyz(hello()world) \: OK, () is balanced and included in single argument \xyz(hello\)world) \: OK \xyz(hello\(world) \: OK \xyz(hello(world) \: error: unterminated arguments

Grammatically, a positional argument is a member-value, constructed as follows:

member-value = { member-value-markup-element } | group; member-value-markup-element = member-value-text | escape | comment | directive; member-value-text = ? Maximal character sequence excluding "\", ",", or balanced ")" ?; member-comments = { comment | whitespace };
4.5.2.3. Ellipses

Ellipsis arguments (spelled ...) are placeholders which expand to arguments provided to a surrounding macro. See also §7.8.2. \cowel_macro — Define a macro and §7.8.3. \cowel_put — Process provided macro content or arguments.

COWEL markup:

\: Define a macro for generating <div> elements \cowel_macro(div){\cowel_html_element(div, ...){\cowel_put}} \div(id = abc){inner text}

Generated HTML:

<div id=abc>inner text</div>

Specifically, any positional argument which begins with ... (ignoring leading whitespace and comments) is considered an ellipsis. The text ... is not considered an ellipsis if it appears anywhere else or has preceding content within an argument.

An error is raised if

To literally provide the text ... as an argument, you can use the escape sequence \... (only the first dot is part of the escape).

\cowel_macro(m){\x(...)} \: OK, all macro arguments are forwarded to \x \cowel_macro(e){\x(...text)} \: error: content following an ellipsis \m(...) \: error: ellipsis is not expanded from a macro \m(\...) \: OK \m(\.\.\.) \: OK
4.5.2.4. Groups

Groups make it possible to group any of the aforementioned constructs. Groups begin with ( and end with ). They can appear both in positional and in named arguments.

\d((x), ()) \: groups as positional arguments \d(n = (x, y)) \: group as named argument \d(((()))) \: nested empty groups

4.5.3. Directive content

The directive content is the primary input to a directive. It is delimited by a '{' and ends with a matching closing '}'. This means that you can have opening and closing braces inside text, but they need to be balanced.

COWEL markup:

\b{Bold text { with braces }.}

Generated HTML:

<b>Bold text { with braces }.</b>

Escape sequences don't participate in this "brace matching", so they can be used to literally produce a brace character.

COWEL markup:

\b{Bold text \{ with brace.}

Generated HTML:

<b>Bold text { with brace.</b>

Unlike in the directive arguments, whitespace is not trimmed.

COWEL markup:

\b{bold} \i{ italic }

Generated HTML:

<b>bold</b> <i> italic </i>

Only one such block of content can be provided, unlike in TeX, which has a similar syntax, but permits multiple blocks. Each block is separated by a pair of braces.

5. Document processing

A COWEL document is processed from top to bottom, left to right, where each piece of content is fed into the current content policy.

There exists a top-level content policy which takes its input and writes it to the output HTML file.

5.1. Content policies

A content policy dictates how each pice of content (text, comments, escapes, directives) is transformed into HTML or consumed in some other way. For example, most content policies ignore comments, process directives, etc., but other policies can process all these pieces of content into text.

Content policies are piped, meaning that the output of one policy is used as the input to another policy. In a COWEL document, it is common to create long pipes of content policies. These ultimately terminate in the top-level content policy or in a policy which captures the output and writes it to memory for further processing.

The following builtin content policies exist:

To-HTML policy
Comments are ignored, escape sequences are expanded, text is sanitized and converted to "inner HTML" text for elements, and directives are processed as usual. Received HTML is taken literally, and received plaintext is sanitized like text.
Highlight policy
Like a to-HTML policy, but any text content and any received plaintext has syntax highlighting applied to it.
Paragraphs policy
Like a to-HTML policy, but surrounds given text content in paragraphs, and treats blank lines in text as paragraph separators. See §7.6.1. Paragraph splitting for details.
No-invoke policy
Like a to-HTML policy, but directives are not processed. Instead, the source code of directives is taken, sanitized, and fed as HTML into the surrounding policy.
Actions policy
Ignores text, escape sequences, and comments, and passes any other content (e.g. directives) to the surrounding policy.
Text-only policy
Comments are ignored, escape sequences are expanded, text is taken literally, and directives are processed as usual. HTML content fed into this policy is ignored.
Text-as-HTML policy
Like a text-only policy, but instead of feeding its parent policy with the generated plaintext, all plaintext is reinterpreted as HTML before being fed into the parent policy.
Source-as-text policy
The source code of all processed content is interpreted as plaintext and fed into the surrounding policy. This includes comments.

To process directives as usual means that when a directive is encountered, a definition matching the directive name is looked up, and the definition is invoked with the arguments and content of the directive.

The top-level content policy within a document is a paragraph split content policy, feeding content into an HTML content policy.

5.2. Directive processing

The job of directives is to feed the current content policy with content. Unlike text or escape sequences, directives can do so programmatically.

In the COWEL snippet \b{x}, the \b directive feeds the current content policy with

Content policies differ in how they treat the content they're fed:

5.2.1. Lazy processing

Unlike in typical programming languages, where the input to a function is processed at the call site and the resulting values are passed into the function, COWEL directives have absolute control over their inputs and how they are processed.

Say you have a JavaScript function:

function f(x) { console.log(x); } f(2 + 2);

f has no idea that its input was originally 2 + 2. It only sees the value 4 and prints it, "oblivious to the outside".

COWEL follows a different model:

\comment{\b{x}}

No <b> tag is ever generated because \b{x} is never processed. \comment is not magical feature; it simply has control over all of its contents and arguments, and it can choose not to process them.

You can even make a \comment2 directive that works just like \comment yourself:

\cowel_macro(comment2){}

6. Themes

COWEL outputs documents that can be viewed in light mode and dark mode.

When the document is opened with support for JavaScript, there is an button in the top-right that can be used to switch between three modes:

Forced light/dark mode works by adding a light or dark class to the <html> element in the DOM.

If you want to customize colors with CSS, remember to make them respond to theme and OS preference changes.

For reference, you can look at the CSS in the COWEL source code to see how it's done properly.

7. New directives

Find below a list of builtin directives. That is, directives that you can always use because they are "built into the language".

These builtin directives should rarely be used directly, but through macro packages which utilize them.

Even if one wanted to use them, builtin directive have long names which make them impractical for direct use in markup. However, those long names are self-document, unambiguous, and guaranteed not to collide with any user directives.

7.1. Common principles

There are a few common principles, which all builtin directives have in common:

7.1.1. Parameter notation

While COWEL does not yet support a syntax for defining directives with parameters of a specific type, this documentation does for the builtin directives. This notation consists of

The type notation type arg is equivalent to type(arg); in other words, type is a unary prefix operator. The possible types are as follows:

any
Placeholder for any other type.
integer
An integer, such as 123.
boolean
An boolean; either true or false.
string
A sequence of content.
markup
A sequence of content which is processed lazily, i.e. it is not evaluated where the directive is invoked, but at some later point (or not at all). This is essentially lazy string.
pack(element: type)
Multiple occurrences of the same element, zero or more times.
group(members: pack type)
A group of zero or more members, possibly of different types.
optional(t: type)
Same as t, but can be omitted in usual argument matching (§7.1.2. Usual argument matching).
lazy(t: type)
Same as t, but not processed during argument matching, but within the directive.
named(t: type)
A named argument with value type t.

The description of \cowel_html_element looks as follows:

cowel_html_element(
  name: string,
  attr: optional group pack named lazy string,
){...}

Or equivalently:

cowel_html_element(
  name: string,
  attr: optional(group(pack(named(lazy(string))))),
){...}

This describes that \cowel_html_element can be invoked as follows, among various options:

\cowel_html_element(name = div, attr = (id = abc))

div matches the name parameter, and the content div is treated as a string value.

(id = abc) matches the attr parameter, and within that group, there is a single-element pack of named arguments of type string with lazy processing. lazy processing implies that even if we were to reverse the order of name and attr, name would still be processed first.

7.1.2. Usual argument matching

The usual argument matching allows matching positional and named arguments to parameters of directives. An error is raised if positional and named arguments are mixed, unless all positional arguments appear contiguously at the start of the argument list.

\: OK, two positional arguments \cowel_html_element(div, (id = abc)) \: OK, two named arguments \cowel_html_element(name = div, attr = (id = abc)) \: OK, all positional arguments appear at the start \cowel_html_element(name = div, (id = abc)) \: error: positional argument following named arguments \cowel_html_element(name = div, (id = abc))

The usual argument matching is performed as follows:

  1. Positional arguments match parameters in the same order. An error is raised if there are more positional arguments than parameters.
  2. The following named arguments match parameters with the same name. An error is raised if a named argument does not match any parameter.
  3. If any parameter has not been matched by an argument, the parameter is matched by its default argument value if there is one, otherwise, if it has optional type, it is matched by nothing, and otherwise an error is raised.

Let \d be a directive with parameters x, y, and z:

\: In each of these calls, \: a, b, and c match x, y, and z, respectively: \d(z = c, y = b, x = a) \: OK, all named arguments \d(a, b, c) \: OK, all positional arguments \d(a, z = c, y = b) \: OK, positional arguments followed by named arguments

The usual argument matching is essentially identical to function calls in Kotlin, but more restrictive when it comes to mixing positional and named arguments.

7.2. Character utilities

It is quite common that a text document should include special characters. While COWEL is built on UTF-8 and in theory, one could just write the character directly into the source code, not every text editor handles special characters well, not to mention that some characters are invisible when rendered.

For portability and convenience COWEL has builtin directives for generating and querying characters.

7.2.1. \cowel_char_by_entity — Character by HTML character reference

cowel_char_by_entity(){...}

The \cowel_char_by_entity directive takes the name of a character and outputs the respective code points as text. The input to a \c directive is provided as content, which consists of an HTML entity without the leading '&' and trailing ';'. All arguments are ignored.

The '&' character can be produced in a number of ways:

7.2.2. \cowel_char_by_name — Character by Unicode name

cowel_char_by_name(){...}

The input to a \cowel_char_by_num directive is text content which should be a Unicode name for a code point. That is, the name of a code point, or one of its "control", "alternate", or "correction" aliases.

\N{DIGIT ZERO} generates '0' U+0030 DIGIT ZERO.

7.2.3. \cowel_char_by_num — Character by code point number

cowel_char_by_num(){...}

The input to a \cowel_char_by_num directive is text content which should be a sequence of hexadecimal numbers specifying a Unicode scalar value; that is, a code point which UTF-8 permits to be encoded. All arguments are ignored.

\U{30} generates '0' U+0030 DIGIT ZERO.

Use of \cowel_char_by_name can lead to much more descriptive, human-friendly code, compared to \cowel_char_by_num.

The main issue with \cowel_char_by_num is that it is very non-descriptive in source code. It simply contains a handful of digits, and unless the reader is a Unicode expert who happens to know that sequence, they cannot easily tell what the purpose of the character is.

7.2.4. \cowel_char_get_num — Get digits of character

cowel_char_get_num(
  zfill: integer = 0,
  base: integer = 16,
  lower: boolean = false,
){...}

The \cowel_char_get_num directive is essentially the inverse of the \cowel_char_by_num directive. Its input is also plaintext content. Rather than producing a code point from a digit sequence, it produces a digit sequence from its input code point.

This can be fine-tuned with the following parameters:

zfill
The minimum amount of digits that should be produced. This is an integer argument in range [0, 1024], and defaults to zero. If the amount of digits is less than zfill, it will be left-padded with additional '0' characters.
base
The base of the output digit sequence, in range [2, 16]. This defaults to 16, meaning that the digits are printed in hexadecimal.
lower
A yes/no plaintext argument, defaulting to no. If yes is specified, digits beyond '9' (i.e. 'A' to 'F') will be output in lower case instead.

COWEL markup:

U+\cowel_char_get_num(4){x}

This renders as:

U+0078

Note that U+0078 is the usual way in which Unicode code points are represented. That is, U+, followed by at least four hexadecimal digits. It is also possible to define a macro if we need this frequently:

\cowel_macro(code_point){U+\cowel_char_get_num{\cowel_put}} \code_point{x}

\cowel_char_get_num can also be combined with \cowel_char_by_name to produce human-readable descriptions of code points, while also keeping our source code readable:

\macro(named_char){U+\cowel_char_get_num(4){\N{\put}} \cowel_put} \named_char{SECTION SIGN}

This renders as:

U+00A7 SECTION SIGN

\cowel_char_get_num uses only the first code point within the input text, even though the entire input text is processed into plaintext.

If we provide more than a single code point, some input is discarded:

\cowel_char_get_num{abc}

This renders as

61

... which corresponds to a, whereas b and c are discarded.

7.3. HTML utilities

7.3.1. \cowel_html_element — HTML element

cowel_html_element(
  name: string,
  attr: optional group pack named lazy string,
){...}

The \cowel_html_element directive generates content, surrounded in HTML opening and closing HTML tags. That is, it

The name of the HTML element is contained in the name parameter, and the attributes within the HTML element are contained within the attr parameter.

COWEL markup:

This is \cowel_html_element(span, (id = abc)){a span}.

Generated HTML:

This is <span id=abc>a span</span>.

A \cowel_html_element directive produces an error if the given tag name is not a valid HTML tag name, or if any of the argument names are not valid HTML attribute names.

7.3.2. \cowel_html_self_closing_element — Self-closing HTML element

cowel_html_self_closing_element(
  name: string,
  attr: optional group pack named lazy string,
)

The \cowel_html_self_closing_element directive generates a self-closing HTML element. That is, an element with no content, and where there is no closing tag. Content is ignored.

The name of the HTML element is contained in the name parameter, and the attributes within the HTML element are contained within the attr parameter.

COWEL markup:

Horizontal rule: \cowel_html_self_closing_element(hr, (id = abc)){ignored}

Generated HTML:

Horizontal rule: <hr id=abc />

7.4. Content policies

As explained in §5.1. Content policies, all content within the document and produced by directives is fed into a content policy, which, in turn, feeds content into some parent content policy.

The table below lists directives which process content using some content policy.

Directive Content policy Notes
\cowel_to_html HTML policy
\cowel_highlight highlight policy see §7.5.1. \cowel_highlight — Perform syntax highlighting
\cowel_paragraphs paragraphs policy see §7.6.2. \cowel_paragraphs — Perform paragraph splitting
\cowel_no_invoke no-invoke policy
\cowel_actions no-invoke policy
\cowel_text_only text-only policy
\cowel_text_as_html text-as-html policy
\cowel_source_as_text source-as-text policy

7.4.1. \cowel_to_html — Process with to-HTML policy

cowel_to_html(){...}

\cowel_to_html simply processes the provided content using the to-HTML policy.

The purpose of this policy/directive is mainly to feed its parent policy pure HTML content, rather than a mixture of plaintext and HTML. This also suppresses paragraph splitting, syntax highlighting, and other such "special treatments" of plaintext, since none of those operate on HTML.

Since HTML is not syntax-highlighted, we can insert special characters like U+00AD SOFT HYPHEN into code snippets, without interfering with syntax highlighting:

Use of \cowel_html_element(code){ \cowel_highlight(cpp){reinterpret_\ \cowel_to_html{\cowel_char_by_name{SOFT HYPHEN}}\ cast} } is discouraged.

This renders as:

Use of reinterpret_cast is discouraged.

Or, if line wrapping takes place in the middle of the highlighted keyword, it renders as:

Use of reinterpret_-
cast
is discouraged.

7.4.2. \cowel_no_invoke — Process with no-invoke policy

cowel_no_invoke(){...}

\cowel_no_invoke processes the provided content using the no-invoke policy.

COWEL markup:

\cowel_no_invoke{Undefined directive: \awoo}

Generated HTML:

Undefined directive: \awoo

Under the no-invoke policy selected by no_invoke, \awoo is not invoked, but its source code is fed into the current policy as plaintext. This is particularly useful when writing code blocks which contain character escapes (e.g. containing "\n" in C++) because such escapes should not treated as COWEL directives.

COWEL comments and COWEL escape sequences are processed as usual. That is, comments are ignored, and escapes are expanded.

7.4.3. \cowel_actions — Process with actions policy

cowel_actions(){...}

\cowel_actions processes the provided content using the actions policy.

This is particularly useful in macros and surrounding macros because it prevents whitespace from being emitted into the HTML file. Normally, whitespace is preserved, unless explicitly eliminated (e.g. with newline escapes or commas).

COWEL markup:

no escapes: \cowel_macro(a) \cowel_macro(b) \cowel_macro(c) end. newline escapes: \cowel_macro(h)\ \cowel_macro(j)\ \cowel_macro(k)\ end. actions: \cowel_actions{ \: Documentation for x: \cowel_macro(x) \: In here, we can add as much whitespace as we want. \: It won't add anything to the generated HTML 😃 \cowel_macro(y) Text is also ignored, just like whitespace, but it is recommended to use comments even in actions because it expresses intent more clearly. \cowel_macro(z) }\ end.

Generated HTML:

no escapes: end. newline escapes: end. actions: end.

Note that nine macros have also been defined by the COWEL markup, but macro definitions produce no output.

7.4.4. \cowel_text_only — Process with text-only policy

cowel_text_only(){...}

\cowel_text_only processes the provided content using the text-only policy.

COWEL markup:

Hello, \cowel_html_element(strong){strong} world! \cowel_text_only{Hello, \cowel_html_element(strong){strong} world!}

Generated HTML:

Hello, <strong>strong</strong> world! Hello, strong world!

As can be seen, only plaintext "makes it through", whereas HTML is stripped.

This policy is used internally when processing arguments in various places, such as in the strong positional argument to \cowel_html_element in the example above.

It is mainly useful in situations where it makes no sense to have non-plaintext content.

7.4.5. \cowel_text_as_html — Process with text-as-HTML policy

cowel_text_as_html(){...}

\cowel_no_invoke processes the provided content using the text-as-HTML policy.

COWEL markup:

\cowel_text_as_html{Hello, <strong>strong</strong> world!}

Generated HTML:

Hello, <strong>strong</strong> world!

As can be seen, the provided text is reinterpreted as HTML.

This directive should be used with great caution because it can easily result in producing malformed HTML. While there are legitimate uses (e.g. inline SVG images and other large HTML blocks), \cowel_html_element and other safer directives should be preferred.

7.4.6. \cowel_source_as_text — Process with source-as-text policy

cowel_source_as_text(){...}

\cowel_no_invoke processes the provided content using the source-as-text policy.

COWEL markup:

\cowel_source_as_text{ \: Comment ... Hello \source_as_text \{ ... \} }

Generated HTML:

\: Comment ... Hello \source_as_text \{ ... \}

This directive is mainly useful for writing COWEL code blocks, where all content within is treated literally, including comments.

It can be seen as a more extreme form of \cowel_no_invoke, and is more niche in its applications.

7.5. Syntax highlighting

Syntax highlighting improves the readability of inline code and code snippets by applying different formatting (usually colors) based on the syntactical meaning of text. Many examples can be found in this document.

7.5.1. \cowel_highlight — Perform syntax highlighting

cowel_highlight(
  lang: string,
){...}

The \cowel_highlight processes content using the highlight policy, which wraps parts of code in HTML tags for the purpose of syntax highlighting.

\cowel_highlight has a single lang parameter. This specifies the name of language (e.g. cowel) in which the content is to be highlighted. The name has to be one of the language short names supported by the µlight syntax highlighter, which is the highlighter that COWEL uses internally.

7.5.2. \cowel_highlight_as — Syntax highlight override

The \cowel_highlight_as directive forces certain syntax highlighting to be applied to some text; it is a manual override for when automatic syntax highlighting is insufficient or broken. This is done by wrapping the given content in HTML tags for syntax highlighting and feeding the provided content, converted to HTML into the surrounding policy, so it is not subject to automatic highlighting.

\cowel_highlight_as has a single name parameter, which is the µlight long name for a highlight type.

You can obtain a list of possible long names at ulight.h from the definition of enum ulight_highlight_type. The long name is the enumerator name without the ULIGHT_HL prefix, all lowercase, and with hyphens instead of underscores.

It is possible to mix automatic syntax highlighting with manual overrides:

\: _Int128 is manually highlighted as a type keyword, \: and x is automatically highlighted as an identifier. \cowel_html_element(code){\ \cowel_highlight(c){\cowel_highlight_as(keyword-type){_Int128} x}\ }

This generates the HTML (subject to change):

<code><h- data-h=kw_type>_Int128</h-> <h- data-h=id>x</h-></code>

It renders as _Int128 x.

While it is also possible to produce the highlighting tags using \cowel_html_element(h-,data-h=kw_type){_Int128}, the "short names" (e.g. kw_type) are not stable, i.e. they are more likely to change. Only µlight long names should be used directly.

7.5.3. \cowel_highlight_phantom — Syntax highlight phantom text

cowel_highlight_phantom(){...}

The \cowel_highlight_phantom directive can be used to further customize syntax highlighting. It outputs phantom text, which is fed into the syntax highlighter, but is not part of the output content.

The provided content is processed using a text-only policy (§5.1. Content policies). If the current policy is a highlight policy, the text output by the former is turned into phantom text within the latter. Otherwise, the generated text is discarded. All arguments are ignored.

Using \cowel_highlight_phantom, we can control whether a JSON string is interpreted as a markup key, or as a string value:

In JSON, there are \cowel_html_element(code){\cowel_highlight(json){\cowel_highlight_phantom{\{}"keys"}} and \cowel_html_element(code){\cowel_highlight(json){"values"}}.

Using the highlight theme of this document, this renders as:

In JSON, there are "keys" and "values".

Notice that the color of "keys" is different because it is highlighted as if it was positioned like:

{"keys"

7.6. Paragraph control

Dividing content into paragraphs manually would take a huge amount of effort, so COWEL provides means of doing so semi-automatically. Text within in a paragraphs policy (§5.1. Content policies) is automatically wrapped in paragraphs (<p>...</p>), divided at blank lines.

While content expanded from macros and from \cowel_include is fed into the surrounding content policy (which may be a paragraphs policy) directly, directives are generally treated as "black boxes" for the purpose of paragraph splitting, and they neither begin nor end a paragraph. The following paragraph control directives define interactions between paragraph splitting and directives.

7.6.1. Paragraph splitting

At the top-level in the document and within the content of certain directives, paragraph splitting takes place. All content in COWEL is either inline content, block content, or meta content. Text and escape sequences are inline content, and directives are sometimes inline and sometimes block content. Certain directives (e.g. \comment) that don't generate anything rendered are meta content.

The process of paragraph splitting works as follows:

COWEL markup:

First paragraph. Second paragraph. \blockquote{This is a block directive.}

Generated HTML:

<p>First paragraph.</p> <p>Second paragraph.</p> <blockquote>This is a block directive.</blockquote>

7.6.2. \cowel_paragraphs — Perform paragraph splitting

cowel_paragraphs(){...}

The \cowel_paragraphs directive creates a paragraphs policy (§5.1. Content policies) and feeds all provided content into that policy. The beginning and end of the provided content are considered to be outside a paragraph. All arguments are ignored.

COWEL markup:

\cowel_paragraphs{ First paragraph. Second paragraph. }

Generated HTML:

<p>First paragraph.</p> <p>Second paragraph.</p>

At a top-level within the document, paragraph splitting takes place implicitly, as if the whole document was wrapped in \cowel_paragraphs.

However, this does not extend recursively, so if one wants to have paragraph splitting inside say, \cowel_html_­element, \cowel_paragraphs has to be used.

7.6.3. \cowel_paragraph_enter — Enter a paragraph

cowel_paragraph_enter()

If the current content policy is a paragraphs policy and and the paragraph state is "outside", the paragraph state becomes "inside", and a <p> tag is emitted. Otherwise, has no effect.

COWEL markup, assuming a surrounding paragraphs policy:

\cowel_char_by_name{DIGIT ONE}. First paragraph \cowel_paragraph_enter\cowel_char_by_name{DIGIT TWO}. Second paragraph

Generated HTML:

1<p>. First paragraph</p> <p>2. Second paragraph</p>

As explained, directives are "black boxes" for paragraph splitting, so when the paragraphs policy processes \cowel_char_by_name, it does not automatically begin a paragraph.

\cowel_paragraph_enter should rarely be used directly, but from within macros.

For example, the user can define an \N directive which combines \cowel_para­graph_enter and \cowel_­char_by_name.

7.6.4. \cowel_paragraph_leave — Leave a paragraph

cowel_paragraph_leave()

If the current content policy is a paragraphs policy and and the paragraph state is "inside", the paragraph state becomes "outside", and a closing </p> tag is emitted. Otherwise, has no effect.

COWEL markup, assuming a surrounding paragraphs policy:

Paragraph 1: \cowel_html_self_closing_element(hr) Paragraph 2: \cowel_paragraph_leave\cowel_html_self_closing_element(hr)

Generated HTML:

<p>Paragraph 1: <hr/></p> <p>Paragraph 2: </p><hr/>

As explained, directives are "black boxes" for paragraph splitting, so when the paragraphs policy processes \cowel_html_self_closing_element, it does not automatically leave a paragraph. This would be necessary because horizontal rules (<hr/>) are not supposed to be inside a paragraph.

\cowel_paragraph_leave should rarely be used directly, but from within macros.

For example, the user can define a \hr directive which combines \cowel_para­graph_leave and \cowel_­html_self_closing_element.

7.6.5. \cowel_paragraph_inherit — Activate paragraph splitting inside a directive

cowel_paragraph_inherit()

There is no support for programmatic directives yet, so this directive is currently useless. There are plans to add a WASM scripting API from which \cowel_paragraph_inherit may be used to alter behavior.

As already stated, paragraph splitting generally treats directives as "black boxes". However, content expanded from \cowel_include and macros inherits the surrounding paragraph for splitting, i.e. any content produced by such directives is fed directly into the paragraphs policy. \cowel_paragraph_inherit can be used inside programmatic directives defined by the user to achieve the same behavior.

7.7. File management

Once projects get larger, it often becomes difficult to manage everything in a single file. Also, tooling may benefit from having separate files. For example, keeping separate SVG files or CSS files may have better IDE support than writing these out directly inside of COWEL.

COWEL provides directives to load content from other files.

7.7.1. \cowel_include — Include a sub-document

The \cowel_include directive loads the content from another COWEL document, and processes it using the current content policy. The path to the document is provided as content to \cowel_include, and is assumed to be relative to the current file. All arguments are ignored.

If the current content policy is a paragraphs policy (§5.1. Content policies), paragraph splitting of the included sub-document takes place as if its contents were expanded directly where \cowel_include appears. In other words, \cowel_include inherits the paragraph context, or is not a "black box" to paragraph splitting.

Say we have another test.cow files containing:

Hello, includes! Next paragraph.

When using \cowel_include, the contents of the other file are substituted where we have written \cowel_include, which means that imported content also plays nicely with paragraph splitting:

First paragraph. \cowel_include{test.cow}

Generated HTML:

<p>First paragraph. Hello, includes!</p> <p>Next paragraph.</p>

It is common practice to collect macros and other reusable components in separate documents, and to \cowel_include them when needed. This can help you avoid copying boilerplate between multiple documents.

It is also common to split documents into multiple files that are combined using a sequence of \cowel_includes.

7.7.2. \cowel_include_text — Include text from a file

cowel_include_text(){...}

The \cowel_include_text directive reads the UTF-8 text contents of another file and feeds that text into the current content policy. The path to the document is provided as content to \cowel_include_text, and is assumed to be relative to the current file.

Say we have a JavaScript file example.js which we want to keep separate from the document. We can then include it into a \script block to use it as a script, or we can include it into a \codeblock to display its contents.

\codeblock(js){ // code included from example.js: \cowel_include_text{example.js} }

Assuming that example.js is located in the same directory as the document and that loading the file succeeds, this renders as:

// code included from example.js: function sqr(x) { return x * x; }

While \cowel_include and \cowel_include_text are similar, there are some stark differences:

7.8. Aliases and macros

7.8.1. \cowel_alias — Define aliases for an existing directive

cowel_alias(
  names: pack string,
){...}

The \cowel_alias directive defines one or more aliases for an existing directive. The content of \cowel_alias is converted to text, and must be the name of an existing directive or directive alias. Each positional argument is converted to text, and results in the definition of a new directive alias. After processing \cowel_alias, the target can also be invoked using the alias name.

A fatal error is raised if

One or multiple aliases can be defined as follows:

\cowel_alias(N){cowel_char_by_name} \N{DIGIT ZERO} \: OK, equivalent to \cowel_char_by_name{DIGIT ZERO} \cowel_alias(incl, include){cowel_include} \incl{file.cow} \: OK, equivalent to \cowel_include{file.cow} \include{file.cow} \: OK, same \cowel_alias{cowel_to_html} \: OK, but no effect \cowel_alias{\N{???}} \: error: generation of target name failed \cowel_alias{??} \: error: invalid target name \cowel_alias{undefined} \: error: target not found \cowel_alias(?){cowel_alias} \: error: invalid alias name \cowel_alias(N){N} \: error: redefinition of N

7.8.2. \cowel_macro — Define a macro

cowel_macro(
  names: pack string,
){...}

The \cowel_macro directive defines a block of content which can be processed at a later point in the document. The provided content is not processed, but remembered. Each positional argument is converted to text, and results in the definition of a new macro. After processing \cowel_macro, the target can also be invoked using the macro name. Named arguments are ignored.

A fatal error occurs if

COWEL markup:

\cowel_macro(m,x){Hello, macros!}\ \m \x

Generated HTML:

Hello, macros! Hello, macros!

When a macro is processed, the behavior is as if \cowel_paragraph_inherit (§7.6.5. \cowel_paragraph_inherit — Activate paragraph splitting inside a directive) was invoked immediately before processing the content in the macro definition. This means that blank lines in the macro definition may also result in paragraph splits outside the macro.

7.8.3. \cowel_put — Process provided macro content or arguments

cowel_put(
  else: lazy any,
){...}

The \cowel_put directive processes content which was provided when a macro was invoked.

The content provided to \cowel_put is converted to text, and designates

\cowel_put has the following effect:

COWEL markup:

\: \cowel_put designates content \cowel_macro(content){\cowel_put}\ \content{Content} \: \cowel_put designates first positional argument \cowel_macro(pos){\cowel_put{0}}\ \pos(Positional) \: \cowel_put designates named argument with name "n" \cowel_macro(named){\cowel_put{n}}\ \named(n = Named) \: \cowel_put designates the first positional argument and has a fallback \: Note that equivalently, we could use \cowel_put(Failure){0}. \cowel_macro(try){\cowel_put(else=Failure){0}}\ \try(Success) \try \: All together: \cowel_macro(m){\cowel_put{greeting}, \cowel_put\cowel_put{0}}\ \m(greeting = Hello, !){macros}

Generated HTML:

Content Positional Named Success Failure Hello, macros!

The behavior is erroneous if \cowel_put is not expanded from a macro, such as when \cowel_put appears at a top-level in the document.

\cowel_macro is not processed in any special way, meaning that it cannot be used for "macro substitution" directly:

\cowel_macro(m){\cowel_source_as_text{\cowel_put}}\ \m

Generated HTML:

\cowel_put

However, \cowel_put accesses the inputs to the macro invocation which expands \cowel_put:

\cowel_macro(m){\cowel_put}\ \m{\cowel_put}

This results in a fatal error because when \m is processed, the \cowel_put in the macro definition expands to the top-level \cowel_put. That \cowel_put in \m{\cowel_put} is not expanded from any macro. This functionality is implemented by tracking the "stack frame" where content was expanded from (or none, in the case of the document top level). This also makes it possible to deeply nest \cowel_put within other directives while still referring to parameters of the macro invocation:

\cowel_macro(m){\cowel_to_html{\cowel_put}}\ \: OK, equivalent to \cowel_to_html{...} \: Notably, the expanded \cowel_put refers to the content provided to \m, \: not to the content provided to \cowel_to_html (which would be \cowel_put). \m{...}

Expanding \cowel_put with the same input multiple times results in multiple processing of the same provided content. Sometimes this is useful, but it can have unintended side effects:

\cowel_macro(twice){\cowel_put\cowel_put}\ \: error: multiple definitions of alias "\N" for "\cowel_char_by_name" \twice{\cowel_alias(N){cowel_char_by_name}}

7.9. Invocations

7.9.1. \cowel_invoke — Invoke a directive

cowel_invoke(
  name: string,
){...}

The \cowel_invoke directive invokes a directive by name. This process is called a dynamic invocation. Most invocations are static invocations.

The name of the invoked directive is provided as the first positional argument. All other arguments are ignored. The content provided to \cowel_invoke is the content provided to the invoked directive.

Any directive \abc can be invoked statically or dynamically:

\: Static invocation of directive named "abc". \abc{...} \: Static invocation of directive named "cowel_invoke", \: which performs an equivalent dynamic invocation of "abc". \cowel_invoke(abc){...}

It is also planned to allow providing arguments to the invoked directive, but it may be better to add "argument groups" to the syntax first, so that they can be bundled up in an args group. To avoid changing how \cowel_invoke works in the future, argument forwarding is not supported at all right now.

8. Legacy directives

The following builtin directives are all considered deprecated, but not all have a suitable replacement yet. These directives were created during the first iteration of COWEL, before plans were made to create a small set of builtin directives, with macro packages that provide additional functionality.

The documentation for these directives is not actively maintained.

8.1. Comments

In addition to the \: syntax for single-line comments, there exists a \comment directive which discards all of its contents. This can be used for multi-line comments.

8.1.1. \comment — Comments

The \comment directive does not process its arguments or content. It outputs no plaintext or HTML.

While comments don't output anything, §7.6.1. Paragraph splitting is based on blank lines in the source, so comments don't split paragraphs:

Text \comment{This is \directive{comment} content.} Text

Generated HTML:

<p>Text Text</p>

8.2. Text formatting

COWEL allows for basic text formatting using various directives. Many of the formatting directives are some direct equivalent of an HTML element. For these, there is a fixed HTML element that will always be used. You can rely on this when adding custom CSS.

Directive HTML Renders as
\b{...} <b>...</b> Bold text
\cite{...} <cite>...</cite> Name of cited work
\del{...} <del>...</del> Deleted text
\dfn{...} <dfn>...</dfn> Definition
\em{...} <em>...</em> Emphasized text
\gterm{...} unspecified grammar-term
\i{...} <i>...</i> Italic text
\ins{...} <ins>...</ins> Inserted text
\kbd{...} <kbd>...</kbd> Ctrl + Keyboard key
\mark{...} <mark>...</mark> Marked/highlighted
\o{...} unspecified Oblique text
\q{...} <q>...</q> Quoted text
\s{...} <s>...</s> Struck text
\samp{...} <samp>...</samp> Sample output
\sans{...} unspecified Sans-serif font
\serif{...} unspecified Serif font
\small{...} <small>...</small> Small text
\span{...} <span>...</span> (No change in formatting)
\sub{...} <sub>...</sub> Subscript
\sup{...} <sup>...</sup> Superscript
\strong{...} <strong>...</strong> Strong text
\tt{...} unspecified Teletype/monospace font
\var{...} <var>...</var> Variable name
\u{...} <u>...</u> Underlined text

All such formatting directives convert all named arguments directly into HTML attributes.

By default, a substantial amount of directives are styled the same way. For example, \cite, \var, \i and \em are all italic. Both \strong and \b are bold. Both \tt and \samp use teletype font.

However, you can customize the style; see §8.12.3. \style — CSS blocks.

The difference between oblique (\o) and italic (\i) text is that oblique text is merely slanted, while italic text is fundamentally a different font with different characters.

8.3. Code and syntax highlighting

COWEL uses µlight for syntax highlighting. While the set of supported language is relatively small, the highlighter is ultra-light, extremely fast, and deals with modern C++ features correctly.

8.3.1. \code — Inline code

The \code directive produced syntax-highlighted text in code font.

Arguments
  • lang (plaintext) — the syntax highlighting language.
  • nested (true/false) — whether to omit <code> tags.
Input content
The input to a \code directive is a plaintext context. However, there are special rules for formatting directives and pure plaintext directives (see below).
HTML output
In an HTML context, the \code takes the input source code, applies syntax highlighting, and outputs the result surrounded by <code>...</code>.
Plaintext output
In a plaintext context, \code simply outputs the input source code.
Display style
Inline

COWEL markup:

\code(cpp){123}

This applies C++ syntax highlighting, and generates HTML. The <h-> tags are an implementation detail and may be subject to change.

<code><h- data-h=num>123</h-></code>
8.3.1.1. Manual nested highlighting

The nested parameter is a plaintext context, which accepts either true or false, where false is the default. When enabled, the surrounding <code> tags are omitted, which makes it suitable for nesting languages within other \code or \codeblock directives.

You can nest JSON inside of a C++ string literal as follows:

Call: \code(cpp){out.write_json("\code(json,nested=true){{"x":123,"y":true,"z":null}}")}

This renders as follows:

Call: out.write_json("{"x":123,"y":true,"z":null}")

Note that by default, you may often not even notice a visual difference between using the nested parameter and not using it. However, the difference would be obvious once you want to style <code> elements to have borders, background color, padding, and other such styling features. nested=true directly emits the highlighting HTML elements, which is more composable.

8.3.1.2. Further advice on highlighting

Writing \code directives directly is often too tedious, so you'll likely want to define a macro to make this easier:

\: Defines a \js directive that can be used in place of \code(js) from now on: \macro(\js){\code(js){\put}} Let's highlight \js{var} in JavaScript.

8.3.2. \codeblock — Code blocks

The \codeblock directive works exactly like \code, but it is a block directive, not an inline directive.

Additionally, code blocks render using borders and a dark background by default. This can be controlled using the borders.

\codeblock(js){ // Borders enabled let x = 0; } \codeblock(js, borders = false){ // Borders disabled let x = 0; }

This renders as:

// Borders enabled let x = 0; // Borders disabled let x = 0;

8.3.3. \hl — Syntax highlight override

The \hl directive forces certain syntax highlighting to be applied to some text; it is a manual override for when automatic syntax highlighting is insufficient or broken. It displays as inline content and wraps its content (which is an HTML context) in the appropriate syntax highlighting tags.

It has a single name argument, which is the µlight long name for a highlight type. You can obtain a list of possible long names at ulight.h from the definition of enum ulight_highlight_type. The long name is the enumerator name without the ULIGHT_HL prefix, all lowercase, and with hyphens instead of underscores.

It is possible to mix automatic syntax highlighting with manual overrides:

\: _Int128 is manually highlighted as a type keyword, \: and x is automatically highlighted as an identifier. \code(c){\hl(keyword-type){_Int128} x}

This generates the HTML (subject to change):

<code><h- data=h=kw_type>_Int128</h-> <h- data-h=id>x</h-></code>

It renders as _Int128 x.

While it is also possible to produce the highlighting tags using \html-h-(data-h=kw_type){_Int128}, the "short names" (kw_type) are not stable, i.e. they are more likely to change. Only µlight long names should be used directly.

8.3.4. \pre — Preformatted blocks

The \pre directive contains pre-formatted content. It can be used to contain code, however, there is no syntax highlighting in a \pre block.

COWEL markup:

\pre{ Hello, world! ============= a b c }

This renders as:

Hello, world!
=============
    a b c

Unlike \codeblock, \pre currently does not support the borders=no option. This will be added in the future.

8.3.5. \literally — Treat input literally as text

The \literally directive treats its input source code as plain text, even if it contains directives or escape sequences. It outputs the given input source code as text.

Its content is an unprocessed context, and it takes no arguments.

COWEL markup:

\pre{\literally{ \: Even comments are preserved! This \comment{comment} \{ is taken literally \}! }}

This renders as:

\: Even comments are preserved!
This \comment{comment} \{ is taken literally \}!

\put pseudo-directives in macros are still processed, even inside \literally blocks because \macro replaces any appearances of \put with the given content, disregarding the semantics of any directives nested within.

8.3.6. \unprocessed — Suppress directive processing

The \unprocessed directive works just like \literally, except that escape sequences are processed into their escaped characters.

COWEL markup:

\pre{\unprocessed{ \: Unlike in \literally, comments are ignored. This \comment{comment} \{ is unprocessed \}! }}

This renders as:

This \comment{comment} { is unprocessed }!

\unprocessed and (to a lesser extent) \literally are very useful for nesting code inside COWEL that already contains underscores. For example, \n is an escape sequence for newline characters in many languages, and it may be annoying if it gets interpreted as an \n directive:

\codeblock(cpp){\unprocessed{ std::string_view s = "Several\nLines\nof\nCode."; }}

This renders as:

std::string_view s = "Several\nLines\nof\nCode.";

Notice that COWEL technically parses a \nLines directive instead of just \n, which is the part that turns into a newline escape sequence in the C++ code block. However, that is ultimately irrelevant for syntax highlighting because the source characters of \nLines are taken as text, and then split up according to C++ syntax.

\put pseudo-directives in macros are still processed, even inside \unprocessed blocks because \macro replaces any appearances of \put with the given content, disregarding the semantics of any directives nested within.

8.4. Math

In recent years, browser support for MathML within HTML has become widespread. COWEL relies entirely on the browser for properly rendering math as MathML, which is very simple and lightweight, but not as portable as "baking" the math into an SVG.

If you have existing MathML, you can embed their content using \cowel_text_as_html:

Math \cowel_text_as_html{ <math display=inline> <mfrac> <mi>x</mi> <mn>2</mn> </mfrac> </math> } in a sentence.

This renders as:

Math x 2 in a sentence.

You can also keep the MathML separate within an XML document and use \cowel_include_text:

Math \cowel_text_as_html{\cowel_include_text{math.mml}} in a sentence.

However, hand-writing MathML would be extremely tedious and verbose, so COWEL offers some convenience directives, listed below.

8.4.1. \math — Inline math

A \math directive surrounds its content with <math display=inline>...</math> tags, and displays as inline content.

Its content is an HTML context, and within it, additional pseudo-directives like \mi or \mn corresponding to MathML elements can be used. Essentially, this allows you to build MathML using COWEL syntax.

See Mozilla's MathML elements reference for a list of supported elements/directives.

The previous example in §8.4. Math could also be written like:

Math \math{ \mfrac{\mi{x}\mn{2}} } in a sentence.

If you frequently use math inside plain sentences, you can define a convenience macro:

\macro(\mathsqr{...}){\math{\msup{\mi{\put}\mn{2}}}} The sum of \mathsqr{x} and \mathsqr{y}

This renders as:

The sum of x2 and y2

8.4.2. \mathblock — Math blocks

The \mathblock directive functions almost exactly as the \math directive, but it displays as block content instead of inline content, and it produces an opening <math display=block> tag.

8.5. Separators and word breaking

8.5.1. \br — Break line

The \br directive produces a line break, and corresponds to the HTML element <br/>. It displays as inline content, and its input content is ignored.

\br is particularly useful when line breaks are treated same as spaces, which is the case in most HTML content.

8.5.2. \hr — Horizontal rule

The \hr directive produces a horizontal rule, and corresponds to the HTML element <hr/>. It displays as block content, and its input content is ignored.

Here is an example:


8.5.3. \wbr — Word break opportunity

The \wbr directive produces a word break opportunity, and corresponds to the HTML element <wbr>. It displays as inline content, and its input content is ignored.

<wbr> elements can be inserted into the middle of long words, which, if they don't fit on one line, will be broken at the point of the <wbr> element (without a hyphen at the end of the line).

Since µlight only supports UTF-8 output, \wbr is equivalent to \U{200B}, which outputs a U+200B ZERO-WIDTH SPACE code point.

If you want to provide a word break hint but have a hyphen at the end of the line, use \U{AD} or \c{shy} to output a U+00AD SOFT HYPHEN.

8.5.4. \nobr — Unbroken words

The \nobr directive is a formatting directive which prevents word breaks within its content. Its content is an HTML context. This is done by applying a white-space: nowrap; style its content.

Another way to prevent word-breaking is to use non-breaking spaces. You can insert these using

8.6. Special blocks

Often, the content we write falls into some special category like "example", "note", etc. COWEL supports a large number of special block directives, which wrap their content in a block, with background color, borders, etc.

The input to all special block directives is an HTML context, they all convert their arguments to HTML attributes, and they all display as block content.

8.6.1. \Babstract — Abstract blocks

Abstracts provide a summary of the document.

8.6.2. \Bquote — Quote blocks

The \blockquote directive directly corresponds to the <blockquote> element.

Quotes blocks or "block quotes" display quoted text.

8.6.3. \blockquote — Alias for \Bquote

The \blockquote directive is equivalent to the \Bquote directive.

8.6.4. \Bug — Bug blocks

A bug block contains the description of a bug. This often includes a code block which demonstrates how to reproduce the bug.

8.6.5. \Bdecision — Decision blocks

A decision block indicates that a decision needs to be made.

8.6.6. \Bdel — Deletion blocks

A deletion block acts as a wrapper for a large amount of deleted content. It is typically used when individual \del directives would be too tedious to use.

8.6.7. \Bdetails — Details blocks aka spoilers

The \Bdetails directive directly corresponds to the <details> element. Within \Bdetails, the \summary directive can be used to specify summary text.

This is a summary. Click to open! A details block contains details which need to be revealed by the user. This is also referred to as "spoiler".

8.6.8. \details — Alias for \Bdetails

The \details directive is equivalent to the \Bdetails directive.

8.6.9. \Bdiff — Difference blocks

A difference block contains changes, where some content within is typically deleted or inserted.

8.6.10. \Bex — Example blocks

An example block contains examples.

8.6.11. \Bins — Insertion blocks

A deletion block acts as a wrapper for a large amount of inserted content. It is typically used when individual \ins directives would be too tedious to use.

8.6.12. \Bimp — Important blocks

Important blocks contain especially important information.

8.6.13. \Bnote — Note blocks

Note blocks contain less important information, which can often be skipped over by readers.

8.6.14. \Btip — Tip blocks

Tip blocks contain useful advice.

8.6.15. \Btodo — TODO blocks

TODO blocks contain actions that remain to be done. They may indicate that a part of a project is incomplete.

8.6.16. \Bwarn — Warning blocks

Warning blocks warn the reader of some hazard or potential mistake.

8.7. Lists

8.7.1. \ul — Unordered lists

The \ul directive corresponds to the <ul> element, and produces an unordered list.

8.7.2. \ol — Ordered lists

The \ol directive corresponds to the <ol> element, and produces an ordered list.

8.7.3. \li — List items

The \li directive corresponds to the <li> element, and produces a list item with <ul> or <ol>.

COWEL markup:

\ul{ \li{first bullet} \li{second bullet} } \hr \ol{ \li{first bullet} \li{second bullet} }

This renders as:


  1. first bullet
  2. second bullet

8.7.4. \item — List item pseudo-directive

This directive is deprecated and will be removed in a future version.

Within \ul and \ol, the \item pseudo-directive can also be used instead of \li.

8.7.5. \dl — Definition lists

The \dl directive corresponds to the <dl> element, and produces a definition list. Within that list, you can use

8.8. Tables

Tables are simply produced by using the \table, \thead, \tbody, \tfoot, \tr, \th, \td, \colgroup, \col, and \caption directives to produce the corresponding HTML tags with the same name.

To perform advanced styling, like controlling alignment within columns, use a \style directive.

8.9. Headings

Headings can be produced using the \h1, \h2, \h3, \h4, \h5, and \h6 directives to produce the corresponding HTML tags with the same name.

Headings take a listed argument, which is a yes/no plaintext argument. Unless its value is no, headings are numbered and added to the table of contents (§8.9.1. \make_contents — Make table of contents) automatically.

Headings also take a show-number argument, which is a yes/no plaintext argument. Unless this is no, the heading number will be included in the title, in the format 1.2.3. Title. However, even if show-number is no, the number heading is internally numbered as usual, and its number can be seen in the table of contents.

Other arguments to these headings are converted into attributes of the corresponding HTML element. An id argument can be provided explicitly.

If none is provided, an id is synthesized from the content within the heading.

Headings can be referenced using the \ref directive:

\h2{Heading with synthesized id} \h2(id=xyz){Heading with manual id} \comment{These can be referenced as follows} \ref(#heading-with-synthesized-id) \ref(#xzy)

8.9.1. \make_contents — Make table of contents

The \make_contents directive generates the table of contents, which is comprised of all the headings within the document. It takes no arguments and its content is ignored. It displays as a block.

The table of contents is is populated by headings (§8.9. Headings) as the document is processed.

Since the table of contents should usually not be listed within itself, an unlisted heading may be used:

\h2(listed=no){Contents} \make_contents

A similar effect can be achieved using \cowel_html_element[h2] (§7.3.1. \cowel_html_element — HTML element). However, using \cowel_html_element[h2] will also not emit a link icon in the gutter, and it will not synthesize an id for that heading.

The underlying contents can be obtained with \here(std.contents). \make_contents additionally wraps those contents in HTML elements to chang their visual appearance.

8.10. Bibliography

In technical writing it is common for documents to reference other papers, and it is expected that references are properly cited. To support this,

COWEL provides directives for adding bibliography entries, and for generating a bibliography automatically.

8.10.1. \bib — Add bibliography entry

The \bib directive adds a single entry to the bibliography. Its content is ignored, and it displays as a meta directive.

It has a number of parameters, each of which is a plaintext context:

id
The identifier of the bibliography entry. This can later be referenced using \ref(id).
author
The name of the author(s). This is in no particular format, and if you want to provide multiple authors, you can do so via a comma-separated list here.
title
The title of the cited work.
date
The date of publication. No particular format is enforced; this is simply a string, so you can choose some local format.
publisher
The name of the publisher.
link
The primary link to the document. This should typically be a short link because it is copied into each \ref to the bibliography entry.
long-link
A "long link" to the document. If present, this will be rendered within the bibliography, and it is typically meant to be a semantic or more stable link than the link. You can also think of it as a "link for pretty printing".
issue-link
A link to issue tracking for the document. For example, in WG21 circles, this may be a link to the GitHub issue where a paper is tracked.

Every argument except the id is optional, although you will hardly get meaningful bibliography output if only the id is specified.

8.10.2. \make_bib — Generate bibliography

The \make_bib directive generates a bibliography. This is typically done towards the bottom of the document, but can be done anywhere.

It takes no arguments, its content is ignored, and it displays as a block.

The \make_bib directive is populated by uses of \bib directives in order of use. That is, the bibliography is not automatically sorted for you; the order in which \bib directives are used is also the order in which the bibliography entries are generated.

Similar to \make_contents, it is a wrapper for the underlying std.bib section.

8.11. References

8.11.1. \ref — References

The \ref directive takes a single to argument, which can be a URL, anchor, or something else. It produces an <a> tag with some content inside.

The content is an HTML context, and is what actually gets displayed. If no content is provided, it can be synthesized from the following types of references:

COWEL markup:

\ref(mail:john@us.gov)

HTML output (<a> tag may have additional attributes):

<a href=mail:john@us.gov>john@us.gov</a>

C++ standard draft links can be synthesized into human-readable descriptions.

\ref(https://eel.is/c++draft/expr#1)

This renders as:

8.11.2. \mail — E-Mail addresses

A \mail directive behaves the same \ref directive that is given a mailto URL. However, the input is provided as content instead of an argument, and the displayed text cannot be customized.

The following two lines produce the same output:

Please contact \mail{john@us.gov}. Please contact \ref(mailto:john@us.gov).

8.11.3. \tel — Telephone numbers

A \tel directive behaves the same \ref directive that is given a tel URL. However, the input is provided as content instead of an argument, and the displayed text cannot be customized.

The following two lines produce the same output:

Please contact \tel{+1234}. Please contact \ref(tel:+1234).

8.12. Foreign languages

8.12.1. \script — JavaScript blocks

The content of a \script directive is a plaintext context, where the input is treated as JavaScript code and surrounded in <script>/*...*/</script> tags.

\script displays as meta content (like \comment).

COWEL markup:

\script{ console.log("hello"); }

Generated HTML:

<script> console.log("hello"); </script>

8.12.2. \noscript — No-JavaScript content

The \noscript directive corresponds to the <noscript> element. Its content is an HTML context, and will only be shown by the browser when JavaScript is disabled.

8.12.3. \style — CSS blocks

The content of a \style directive is a plaintext context, where the input is treated as JavaScript code and surrounded in <style>/*...*/</style> tags.

\style displays as meta content (like \comment).

COWEL markup:

\style{ body { color: red; } }

Generated HTML:

<style> body { color: red; } </style>

8.13. Sections

Often, you want to produce content in one place, but have it display in another place within the document. A classic example is collecting headings automatically in a table of contents, or copying the content of a heading into a preview when referenced by \ref(#id).

8.13.1. \there — Append content to section

The \there directive displays as meta content. Basically, it stashes away its input content (which is an HTML context) somewhere else.

It takes a single section argument which specifies the name of the section. The section argument is a plaintext context.

By default, all document content is placed within a <main> element, inside <body>, inside html. \there allows you to write outside of the <main> element:

You can change the title of the document as follows:

\there(std.head){ \html-title{This is the document title!} }

8.13.2. \here — Copy section content

The \here directive inserts the content from another section at its location. It displays as inline content.

It takes a single section argument which specifies the name of the section. The section argument is a plaintext context.

All references produced by \here are resolved in a post-processing step, which allows forward-references.

COWEL markup:

\there(sec){before/} (\here(sec)) \there(sec){/after}

Generated HTML:

(before//after)

8.13.3. \hereblock — Copy section content in block

The \hereblock directive functions exactly like the \here directive, but displays as block content instead of inline content.

8.14. Miscellaneous

8.14.1. \div — Content division

The \div directive surrounds its contents in <div>...</div> tags. It displays as a block, and its content is an HTML context. \div's named arguments are turned into HTML attributes.

8.14.2. \trim — Trim input

The content of a \text directive is an HTML context (§5.1. Content policies). Its arguments are ignored. \text is a formatting directive.

The process of trimming eliminates leading and trailing whitespace in the input, at the COWEL source level.

Every\trim{ day}

This renders as:

Everyday

\trim is primarily useful inside of macros. We might not want to space-separate content if some of it is empty. For example, we could define a \Note macro where the user can optionally provide a number for that note:

\macro(\Note){\trim{Note \put}:} \Note Something something. \Note{1} Something else.

This renders as:

Note: Something something.

Note 1: Something else.

Notice that because \put expands to no content, the trailing space after Note is eliminated. If it wasn't eliminated, we would end up with {Note :}, but there should never be a space before the colon.

8.14.3. \text — Plaintext context

The content of a \text directive is a plaintext context (§5.1. Content policies). Its arguments are ignored.

It simply outputs the input that it was given as text. Therefore, it acts as a sort of wrapper for plaintext. Notably, this forces the generation of plaintext even when \text is within an HTML context.

8.14.4. \p — Paragraphs

The \p directive surrounds its content in <p>...</p> tags.

8.15. Variables and calculations

While COWEL is not intended to be general purpose programming language, and more advanced interactive features are better suited for JavaScript, it does provide a few ways to perform calculations.

8.15.1. \Vset — Set a variable

\Vset sets the value of a variable to some provided plaintext. It displays as a meta directive.

It takes a single var argument, which is a plaintext context for the name of the variable. The directive content is also a plaintext context, and provides the value of the variable.

COWEL markup:

\: Sets x = 123. \Vset(x){123} \: Accesses the value of x, which is 123. \Vget(x)

This renders as:

123

8.15.2. \Vget — Get a variable

\Vget obtains the value of a variable, and displays as inline. Its content is ignored.

It takes a single var argument, which is a plaintext context for the name of the variable. See above for an example.

If the variable denoted by the var argument does not exist, \Vget expands to nothing.

8.15.3. \Cadd — Perform addition

The \Cadd directive performs an integer addition between its (nameless) arguments. Its content is ignored, and it displays as inline.

Each of its arguments is a plaintext context, where the argument is interpreted as an integer, and then summed.

COWEL markup:

\Cadd(1, 2, 3, -1) \: 1 + 2 + 3 + (-1)

This renders as:

5

You can also combine \Cadd with \Vget and \Vset to store the results of your calculations:

\: Reset paragraph number \Vset(index){0} \: Define a macro for automatically incrementing paragraph numbers. \macro(\p_num){ \Vset(index){\Cadd(\Vget(index),1)} \Vget(index) } Paragraph \p_num Paragraph \p_num

This renders as:

Paragraph 1

Paragraph 2

\Cadd and other calculation directives are mainly intended for incrementing paragraph numbers and other basic tasks.

8.15.4. \Csub — Perform subtraction

The \Csub directive behaves like the \Cadd directive, except that it performs subtraction instead of addition.

8.15.5. \Cmul — Perform multiplication

The \Cmul directive behaves like the \Cadd directive, except that it performs multiplication instead of addition.

8.15.6. \Cdiv — Perform division

The \Cdiv directive behaves like the \Cdiv directive, except that it performs division instead of addition.

Division by zero results in an error.