-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial.html
134 lines (134 loc) · 76.6 KB
/
tutorial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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
<!DOCTYPE html>
<html><head><title>Papyri: a programmable markup language</title><meta charset="utf-8"><link rel="stylesheet" type="text/css" href="./papyri.css"></head><body><header><nav data-title="Navigation"><ul><li><a href="./">Home</a></li><li><p><a href="./tutorial.html">Tutorial</a></p><ul><li><a href="#functions"> Functions</a></li><li><a href="#arguments"> Arguments</a></li><li><a href="#page_template"> Page Template</a></li><li><a href="#variables"> Variables</a></li><li><a href="#string_templates"> String Templates</a></li><li><a href="#more_formatting"> More Formatting</a></li><li><a href="#code_formatting"> Code Formatting</a></li><li><a href="#comments"> Comments</a></li><li><a href="#special_characters"> Special Characters</a></li><li><a href="#declaring_functions"> Declaring Functions</a></li><li><a href="#types"> Types</a></li><li><a href="#optional_parameters_and_default_values"> Optional Parameters and Default Values</a></li><li><a href="#spreads"> Spreads</a></li><li><a href="#pattern_matching"> Pattern Matching</a></li><li><a href="#html_tag_patterns"> HTML Tag Patterns</a></li><li><a href="#imports_and_exports"> Imports and Exports</a></li><li><a href="#conclusion"> Conclusion</a></li></ul></li></ul></nav></header><article><h1><a class="anchor" href=""><i class="anchor-icon fas fa-link"></i> Papyri Tutorial</a></h1><p><a href="./">Papyri</a> is a programmable markup language. Being a “markup” language means that you can write your content mostly as normal, and you only need to use Papyri’s syntax when you want to “mark up” things that should be presented differently.</p><p>Papyri compiles to HTML, and almost all HTML syntax is also valid in Papyri:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">You <span class="op"><</span><span class="name">em</span><span class="op">></span>can<span class="op"></em></span> write Papyri like this<span class="op">,</span> but HTML is a bit clunky<span class="op">.</span></span>
</code></pre><p>However, it’s much more convenient when writing Papyri to use <b>functions</b> to mark up your content:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">It<span class="op">'</span>s better to write Papyri like <span class="decorator">@emph</span> this<span class="op">,</span> instead<span class="op">.</span></span>
</code></pre><h2 id="functions"><a class="anchor" href="#functions"><i class="anchor-icon fas fa-hashtag"></i> Functions</a></h2><p>Here are a few built-in functions:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="decorator">@b</span></code> or <code class="syntax-highlight lang-papyri"><span class="decorator">@bold</span></code>: bold text.<a class="footnote-ref"></a><span class="footnote panel">These wrap inline content in a <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">b</span><span class="op">></span></code> or <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">strong</span><span class="op">></span></code> tag respectively.</span></li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@i</span></code> or <code class="syntax-highlight lang-papyri"><span class="decorator">@emph</span></code>: italic text.<a class="footnote-ref"></a><span class="footnote panel">These wrap inline content in an <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">i</span><span class="op">></span></code> or <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">em</span><span class="op">></span></code> tag respectively.</span></li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@underline</span></code>: take a wild guess.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@h1</span></code> through <code class="syntax-highlight lang-papyri"><span class="decorator">@h6</span></code>: headings.</li></ul><p>These functions apply simply to the next thing you write after the function name. If you want to apply a function to more than one word, you can use <b>braces</b> to group multiple words together:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">For example<span class="op">,</span> with no braces only <span class="decorator">@emph</span> one word is emphasised<span class="op">,</span> but you can use</span>
<span class="line" data-line-no="2">braces to emphasise <span class="decorator">@emph</span> <span class="lparen" data-paren-no="1">{</span>multiple words<span class="rparen" data-paren-no="1">}</span> like this<span class="op">.</span></span>
</code></pre><p>The item which the function applies to is called its <b>contents</b>. The contents of a function may be the result of another function:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">If you absolutely <span class="decorator">@underline</span> <span class="decorator">@b</span> must stress something<span class="op">...</span></span>
</code></pre><p>Braces are also needed if a word contains things that aren’t letters in the Latin alphabet, such as apostrophes, hyphens, or accents. You can group anything else, too, including other functions:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@h1</span> <span class="lparen" data-paren-no="1">{</span>A <span class="decorator">@i</span> <span class="lparen" data-paren-no="2">{</span>top-level heading<span class="rparen" data-paren-no="2">}</span> with nested markup<span class="rparen" data-paren-no="1">}</span></span>
</code></pre><h2 id="arguments"><a class="anchor" href="#arguments"><i class="anchor-icon fas fa-hashtag"></i> Arguments</a></h2><p>Some functions accept more arguments than just their “contents”. For example, here’s how you can write a link in Papyri:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@href</span><span class="lparen" data-paren-no="1">(</span><span class="string">`</span><a href="https://kaya3.github.io/papyri/tutorial.html">https://kaya3.github.io/papyri/tutorial.html</a><span class="string">`</span><span class="rparen" data-paren-no="1">)</span> <span class="lparen" data-paren-no="2">{</span>Click here<span class="op">!</span><span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>Arguments to a function are written in parentheses. In this case, the <code class="syntax-highlight lang-papyri"><span class="decorator">@href</span></code> function has one argument: the link URL, a string literal written in backticks (the <code class="syntax-highlight lang-papyri"><span class="string">`</span></code> symbol).</p><p>Some functions have <b>named arguments</b>:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@image</span><span class="lparen" data-paren-no="1">(</span>alt<span class="op">=</span><span class="lparen" data-paren-no="2">{</span>Initech corporate logo<span class="rparen" data-paren-no="2">}</span><span class="rparen" data-paren-no="1">)</span> <span class="string">`resources/logo.png`</span></span>
</code></pre><p>For the <code class="syntax-highlight lang-papyri"><span class="decorator">@image</span></code> function, both the <code class="syntax-highlight lang-papyri">alt</code> argument and the contents are strings, although the <code class="syntax-highlight lang-papyri">alt</code> argument here is written as text inside braces instead of backticks. Simple string values can also be written without backticks:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@image</span><span class="lparen" data-paren-no="1">(</span>alt<span class="op">=</span>Logo<span class="rparen" data-paren-no="1">)</span> <span class="string">`resources/logo.png`</span></span>
</code></pre><p>Here, the string <code class="syntax-highlight lang-papyri">Logo</code> is a single word, so it doesn’t need backticks to count as a string. Still, it’s recommended to use backticks for a string value, as this is more explicit.</p><h2 id="page_template"><a class="anchor" href="#page_template"><i class="anchor-icon fas fa-hashtag"></i> Page Template</a></h2><p>An HTML page needs some structure around the main content — a doctype, <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">html</span><span class="op">></span></code>, <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">head</span><span class="op">></span></code> and <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">body</span><span class="op">></span></code> tags, and a <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">title</span><span class="op">></span></code>, at least. Fortunately, you can wrap your page in this boilerplate code using the <code class="syntax-highlight lang-papyri"><span class="decorator">@page</span></code> function. This function takes a named <code class="syntax-highlight lang-papyri">title</code> argument, and the “contents” are the page contents:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@page</span><span class="lparen" data-paren-no="1">(</span>title<span class="op">=</span><span class="lparen" data-paren-no="2">{</span>A Very Interesting Page<span class="rparen" data-paren-no="2">}</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3">If there was anything here<span class="op">,</span> then this page would be very interesting<span class="op">.</span></span>
</code></pre><p>The <code class="syntax-highlight lang-papyri"><span class="decorator">@page</span></code> function builds a page using the default Papyri theme, which is the one you see on this page.</p><p>This example introduces another piece of syntax: when used for a function’s “contents”, an ellipsis <code class="syntax-highlight lang-papyri"><span class="op">...</span></code> means the remainder of the document (or the remainder of the current group or HTML tag).</p><h2 id="variables"><a class="anchor" href="#variables"><i class="anchor-icon fas fa-hashtag"></i> Variables</a></h2><p>You can declare variables using the keyword <code class="syntax-highlight lang-papyri"><span class="keyword">@let</span></code>. The syntax for <code class="syntax-highlight lang-papyri"><span class="keyword">@let</span></code> is like a function: it takes any number of named arguments, which become variables you can refer to as <code class="syntax-highlight lang-papyri"><span class="name">$var_name</span></code> within the “contents” of the <code class="syntax-highlight lang-papyri"><span class="keyword">@let</span></code> expression.</p><p>In practice, you usually want to use the <code class="syntax-highlight lang-papyri"><span class="op">...</span></code> syntax for the “contents”, so the new variables stay in scope for the remainder of the document (or the remainder of the current group or HTML tag).</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@let</span><span class="lparen" data-paren-no="1">(</span>position<span class="op">=</span><span class="lparen" data-paren-no="2">{</span>Director of Redundancies Director<span class="rparen" data-paren-no="2">}</span><span class="op">,</span> company<span class="op">=</span><span class="lparen" data-paren-no="3">{</span>Initech<span class="rparen" data-paren-no="3">}</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3">Please submit your TPS reports to the <span class="name">$position</span> at <span class="name">$company</span><span class="op">.</span></span>
</code></pre><p>Variables can appear within text, or can also be used as function arguments, including the “contents” argument:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@let</span><span class="lparen" data-paren-no="1">(</span>url<span class="op">=</span><span class="string">`../index.html`</span><span class="op">,</span> link_text<span class="op">=</span><span class="lparen" data-paren-no="2">{</span>Back to the home page<span class="rparen" data-paren-no="2">}</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3"><span class="decorator">@href</span><span class="lparen" data-paren-no="3">(</span><span class="name">$url</span><span class="rparen" data-paren-no="3">)</span> <span class="name">$link_text</span></span>
</code></pre><h2 id="string_templates"><a class="anchor" href="#string_templates"><i class="anchor-icon fas fa-hashtag"></i> String Templates</a></h2><p>Additionally, string variables can be used in <b>string templates</b>, to build a string value out of fixed and variable parts:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@image</span><span class="lparen" data-paren-no="1">(</span>alt<span class="op">="</span><span class="name">$company</span> corporate logo<span class="op">"</span><span class="rparen" data-paren-no="1">)</span> <span class="string">`resources/logo.png`</span></span>
</code></pre><p>String templates can also be used directly in HTML tags:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@let</span><span class="lparen" data-paren-no="1">(</span>panel_kind<span class="op">=</span><span class="string">`info`</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3"><span class="op"><</span><span class="name">div</span> class<span class="op">="</span>panel <span class="name">$panel_kind</span><span class="op">"></span></span>
<span class="line" data-line-no="4"> You may find this information useful<span class="op">.</span></span>
<span class="line" data-line-no="5"><span class="op"></div></span></span>
</code></pre><p>String templates are delimited either by single-quotes <code class="syntax-highlight lang-papyri"><span class="op">'</span></code>, or double-quotes <code class="syntax-highlight lang-papyri"><span class="op">"</span></code>. It’s recommended to only use single-quotes if you want to build a string containing double-quote characters; for a string containing both kinds of quotes, you can use the escape sequences <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\"</span></code> or <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\'</span></code>.</p><p>In case you need to join variables together with literal text that looks like part of a variable name, you must use braces to disambiguate:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="op"><</span><span class="name">div</span> class<span class="op">="</span><span class="lparen" data-paren-no="1">{</span><span class="name">$panel_kind</span><span class="rparen" data-paren-no="1">}</span>_panel<span class="op">"></span> <span class="op">...</span> <span class="op"></div></span></span>
</code></pre><h2 id="more_formatting"><a class="anchor" href="#more_formatting"><i class="anchor-icon fas fa-hashtag"></i> More Formatting</a></h2><p>Besides functions, there are a few other bits of Papyri syntax which can be used to format content in particular ways. The most basic of these is a paragraph break, which is achieved just by writing a blank line:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">This content has two paragraphs<span class="op">.</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3">They get split apart because of the blank line in between<span class="op">.</span></span>
</code></pre><p>Bulleted lists can be written using square brackets: the list items will normally need to be contained in braces, and they must have commas between them.</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">Items on the menu include<span class="op">:</span></span>
<span class="line" data-line-no="2"><span class="lparen" data-paren-no="1">[</span></span>
<span class="line" data-line-no="3"> <span class="lparen" data-paren-no="2">{</span>Spam<span class="rparen" data-paren-no="2">}</span><span class="op">,</span></span>
<span class="line" data-line-no="4"> <span class="lparen" data-paren-no="3">{</span>Spam and eggs<span class="rparen" data-paren-no="3">}</span><span class="op">,</span></span>
<span class="line" data-line-no="5"> <span class="lparen" data-paren-no="4">{</span>Eggs and spam<span class="rparen" data-paren-no="4">}</span><span class="op">,</span></span>
<span class="line" data-line-no="6"><span class="rparen" data-paren-no="1">]</span></span>
</code></pre><p>For numbered lists, simply use the built-in <code class="syntax-highlight lang-papyri"><span class="decorator">@numbered</span></code> function:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@numbered</span> <span class="lparen" data-paren-no="1">[</span></span>
<span class="line" data-line-no="2"> <span class="lparen" data-paren-no="2">{</span>Acquire underpants<span class="rparen" data-paren-no="2">}</span><span class="op">,</span></span>
<span class="line" data-line-no="3"> <span class="lparen" data-paren-no="3">{</span><span class="op">???</span><span class="rparen" data-paren-no="3">}</span><span class="op">,</span></span>
<span class="line" data-line-no="4"> <span class="lparen" data-paren-no="4">{</span>Profit<span class="rparen" data-paren-no="4">}</span><span class="op">,</span></span>
<span class="line" data-line-no="5"><span class="rparen" data-paren-no="1">]</span></span>
</code></pre><h2 id="code_formatting"><a class="anchor" href="#code_formatting"><i class="anchor-icon fas fa-hashtag"></i> Code Formatting</a></h2><p>Papyri makes it easy for documents to include properly-formatted source code. The <code class="syntax-highlight lang-papyri"><span class="decorator">@code</span></code> and <code class="syntax-highlight lang-papyri"><span class="decorator">@code_block</span></code> functions format inline code and code listings respectively; however, you normally don’t need to explicitly call these functions, because when you write a string literal with backticks <code class="syntax-highlight lang-papyri"><span class="string">`</span></code> anywhere that text is expected, these functions are automatically called. For example:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">This paragraph is text<span class="op">,</span> but <span class="string">`this part`</span> is formatted as inline code<span class="op">.</span> There is</span>
<span class="line" data-line-no="2">also a block-level code listing below it<span class="op">:</span></span>
<span class="line" data-line-no="3"></span>
<span class="line" data-line-no="4"><span class="string">```</span></span>
<span class="line" data-line-no="5"><span class="string"> This part is a code listing, because it has three backticks instead of one.</span></span>
<span class="line" data-line-no="6"><span class="string"> The indentation will be stripped automatically, based on how the first</span></span>
<span class="line" data-line-no="7"><span class="string"> non-empty line is indented.</span></span>
<span class="line" data-line-no="8"><span class="string">```</span></span>
</code></pre><p>For syntax highlighting, put the following line<a class="footnote-ref"></a><span class="footnote panel"><code class="syntax-highlight lang-papyri"><span class="keyword">@implicit</span></code> is like <code class="syntax-highlight lang-papyri"><span class="keyword">@let</span></code>, except it also allows the variable to be passed implicitly to the <code class="syntax-highlight lang-papyri"><span class="decorator">@code</span></code> and <code class="syntax-highlight lang-papyri"><span class="decorator">@code_block</span></code> functions which know to accept it. Implicit parameters are beyond the scope of this tutorial.</span> somewhere near the start of your Papyri source file, before any of the code you want highlighted, with the name of the language the code is written in:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@implicit</span><span class="lparen" data-paren-no="1">(</span>language<span class="op">=</span><span class="string">`papyri`</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
</code></pre><p>For formatting program input or output (as opposed to source code), use the built-in functions <code class="syntax-highlight lang-papyri"><span class="decorator">@kbd</span></code> and <code class="syntax-highlight lang-papyri"><span class="decorator">@samp</span></code> respectively. These output <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">kbd</span><span class="op">></span></code> and <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">samp</span><span class="op">></span></code> tags instead of <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">code</span><span class="op">></span></code> tags, and do not use syntax highlighting.</p><p>Papyri allows you to use as many backticks as you want to delimit string literals, so if you need to write code which contains backticks <code class="syntax-highlight lang-papyri"><span class="string">`like this`</span></code>, you can just use sufficiently wide delimiters, <code class="syntax-highlight lang-papyri"><span class="string">`` `like this` ``</span></code>. (The surrounding whitespace will be trimmed.)</p><p>Three or more backticks make a code listing; if you need that many backticks but you <em>don’t</em> want a block-level listing, you can call <code class="syntax-highlight lang-papyri"><span class="decorator">@code</span></code> explicitly to make it formatted inline:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">Calling <span class="string">`@code`</span> with triple-backticks to format inline code containing</span>
<span class="line" data-line-no="2">double-backticks<span class="op">:</span> <span class="decorator">@code</span> <span class="string">``` `` `like this` `` ```</span><span class="op">.</span></span>
</code></pre><p>For documents with code in multiple languages, you can select the language in a code listing by writing the language name on the first line of the code block, immediately after the opening backticks.<a class="footnote-ref"></a><span class="footnote panel">This can also be done to get syntax highlighting only in code listings, and not in inline code. If you need inline code to have a different language, use <code class="syntax-highlight lang-papyri"><span class="decorator">@code</span></code> with a named <code class="syntax-highlight lang-papyri">language</code> argument, like <code class="syntax-highlight lang-papyri"><span class="decorator">@code</span><span class="lparen" data-paren-no="1">(</span>language<span class="op">=</span><span class="string">`java`</span><span class="rparen" data-paren-no="1">)</span> <span class="string">`inline.java.code();`</span></code>.</span> This overrides the “implicit” language declaration, if there is one.</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@implicit</span><span class="lparen" data-paren-no="1">(</span>language<span class="op">=</span><span class="string">`python`</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3">Most of the code in this document<span class="op">,</span> like <span class="string">`[2 * x for x in range(5)]`</span><span class="op">,</span> is going</span>
<span class="line" data-line-no="4">to be written in Python<span class="op">,</span> but the following listing shows how that might be</span>
<span class="line" data-line-no="5">written in Java<span class="op">:</span></span>
<span class="line" data-line-no="6"></span>
<span class="line" data-line-no="7"><span class="string">```java</span></span>
<span class="line" data-line-no="8"><span class="string"> List<Integer> numbers = new ArrayList<>();</span></span>
<span class="line" data-line-no="9"><span class="string"> for(int x = 0; x < 5; x++) {</span></span>
<span class="line" data-line-no="10"><span class="string"> numbers.add(2 * x);</span></span>
<span class="line" data-line-no="11"><span class="string"> }</span></span>
<span class="line" data-line-no="12"><span class="string">```</span></span>
</code></pre><h2 id="comments"><a class="anchor" href="#comments"><i class="anchor-icon fas fa-hashtag"></i> Comments</a></h2><p>Papyri has two kinds of comments:</p><ul><li>Line comments, beginning with <code class="syntax-highlight lang-papyri"><span class="comment">#</span></code> until the end of the line.</li><li>HTML-style multiline comments, <code class="syntax-highlight lang-papyri"><span class="comment"><!-- ... --></span></code>.</li></ul><p>Comments can be used to make remarks about the document, or to temporarily suppress parts of it from the output. Comments don’t work inside string literals, but they <em>do</em> work inside string templates.</p><p>Comments are never preserved in the compiled HTML, even if they are HTML-style multiline comments.</p><p>Line comments (those beginning with <code class="syntax-highlight lang-papyri"><span class="comment">#</span></code>) also suppress whitespace at the start of the next line. This can be useful when you want content written across multiple lines to be compiled without a space being inserted in between: for example, when you write a footnote<a class="footnote-ref"></a><span class="footnote panel">Yes, these aren’t really “footnotes”, because they appear in the middle of the document instead of in a footer. But this is the web, not a printed document, so we can get away with that by hiding the “footnote” until the user clicks to see it.</span> using the built-in <code class="syntax-highlight lang-papyri"><span class="decorator">@footnote</span></code> function, it makes sense to put it on a separate line, and write <code class="syntax-highlight lang-papyri"><span class="comment">#</span></code> at the end of the previous line to prevent a space being inserted before the footnote marker:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1">This sentence is true<span class="op">.</span><span class="comment">#</span></span>
<span class="line" data-line-no="2"><span class="comment"> </span><span class="decorator">@footnote</span> <span class="lparen" data-paren-no="1">{</span>Actually<span class="op">,</span> it<span class="op">'</span>s more complicated than that<span class="op">.</span><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><h2 id="special_characters"><a class="anchor" href="#special_characters"><i class="anchor-icon fas fa-hashtag"></i> Special Characters</a></h2><p>Nice-looking formatting often requires symbols that you can’t easily type on a keyboard, such as dashes and fancy quote marks. When writing regular text in Papyri, you can get some of the most useful ones by character substitutions:</p><ul><li>The quote characters <code class="syntax-highlight lang-papyri"><span class="op">'</span></code> and <code class="syntax-highlight lang-papyri"><span class="op">"</span></code> will be converted automatically into “better-looking” Unicode quotes.<a class="footnote-ref"></a><span class="footnote panel">The Papyri compiler will try to infer whether a quote should face left or right. It usually gets it right, but if you need to override it, you can use braces: <code class="syntax-highlight lang-papyri"><span class="lparen" data-paren-no="1">{</span><span class="op">"</span><span class="rparen" data-paren-no="1">}</span></code> always makes a left-quote, and <code class="syntax-highlight lang-papyri"><span class="lparen" data-paren-no="1">{</span><span class="rparen" data-paren-no="1">}</span><span class="op">"</span></code> always makes a right-quote.</span></li><li>Two dashes <code class="syntax-highlight lang-papyri">--</code> make an en-dash, and three <code class="syntax-highlight lang-papyri">---</code> make an em-dash.</li><li>Three dots <code class="syntax-highlight lang-papyri"><span class="op">...</span></code> become an ellipsis.</li><li>A tilde <code class="syntax-highlight lang-papyri">~</code> makes a <a href="https://en.wikipedia.org/wiki/Non-breaking_space">non-breaking space</a>, and a pipe <code class="syntax-highlight lang-papyri"><span class="op">|</span></code> makes a “<a href="https://en.wikipedia.org/wiki/Word_joiner">word joiner</a>” — an invisible character which prevents text from wrapping at that point.</li><li>The arrows <code class="syntax-highlight lang-papyri"><span class="op"><</span>-</code>, <code class="syntax-highlight lang-papyri"><span class="op">-></span></code> and <code class="syntax-highlight lang-papyri"><span class="op"><-></span></code> become ←, → and ↔.</li><li>Mathematical symbols: <code class="syntax-highlight lang-papyri"><span class="op"><=</span></code>, <code class="syntax-highlight lang-papyri"><span class="op">>=</span></code> and <code class="syntax-highlight lang-papyri"><span class="op">!=</span></code> become ≤, ≥ and ≠, <code class="syntax-highlight lang-papyri">+/-</code> becomes ±.</li><li>The copyright symbol © is written like <code class="syntax-highlight lang-papyri"><span class="lparen" data-paren-no="1">(</span>c<span class="rparen" data-paren-no="1">)</span></code>.</li></ul><p>Some characters like <code class="syntax-highlight lang-papyri">$</code>, <code class="syntax-highlight lang-papyri">@</code> and braces have special meanings in Papyri, but sometimes you may want to write them literally as text. You can do this by escaping them with backslashes: <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\$</span></code> and <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\@</span></code> give the raw characters, and <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\\</span></code> gives a raw backslash if you want that. The same works for preventing text substitutions: for example, <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\~</span></code> gives a literal tilde.</p><p>Other characters can be written with hexadecimal Unicode escape sequences or HTML entities:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="keyword-literal">\x61</span></code>, <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\u0061</span></code> and <code class="syntax-highlight lang-papyri"><span class="keyword-literal">\U00000061</span></code> for two, four or eight hexadecimal digits.</li><li>Named entities such as <code class="syntax-highlight lang-papyri"><span class="keyword-literal">&quot;</span></code>, decimal entities like <code class="syntax-highlight lang-papyri"><span class="keyword-literal">&#97;</span></code>, or hexadecimal entities like <code class="syntax-highlight lang-papyri"><span class="keyword-literal">&#x61;</span></code>.</li></ul><p>Escape sequences and entities don’t work inside string literals (with backticks), but they do work in string templates.</p><h2 id="declaring_functions"><a class="anchor" href="#declaring_functions"><i class="anchor-icon fas fa-hashtag"></i> Declaring Functions</a></h2><p>So far we’ve seen that Papyri is a markup language, but here’s where we start to see that it’s a <em>programmable</em> markup language. You can declare your own functions, to mark up your documents in customisable ways:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@fn</span> <span class="name-def">panel</span><span class="lparen" data-paren-no="1">(</span><span class="name">$_panel_kind</span><span class="op">:</span> str<span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span></span>
<span class="line" data-line-no="2"> <span class="op"><</span><span class="name">div</span> class<span class="op">="</span>panel <span class="name">$_panel_kind</span><span class="op">"></span><span class="name">$v</span><span class="op"></div></span></span>
<span class="line" data-line-no="3"><span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>The <code class="syntax-highlight lang-papyri"><span class="keyword">@fn</span></code> keyword is used to declare a function. Its parameters are declared inside the parentheses, except for the “contents” parameter which is declared afterwards. In this example, there is one parameter named <code class="syntax-highlight lang-papyri"><span class="name">$_panel_kind</span></code> which must be of type <code class="syntax-highlight lang-papyri">str</code> (i.e. a string), plus the “contents” parameter, which is named <code class="syntax-highlight lang-papyri"><span class="name">$v</span></code>. There is no type annotation for <code class="syntax-highlight lang-papyri"><span class="name">$v</span></code>, which is equivalent to a type annotation <code class="syntax-highlight lang-papyri"><span class="name">$v</span><span class="op">:</span> any</code> (i.e. any type is allowed). The function body then appears after the arrow, <code class="syntax-highlight lang-papyri"><span class="op">-></span></code>.</p><p>Now that the function is declared, it can be called like any other:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@panel</span><span class="lparen" data-paren-no="1">(</span><span class="string">`info`</span><span class="rparen" data-paren-no="1">)</span> <span class="lparen" data-paren-no="2">{</span></span>
<span class="line" data-line-no="2"> You may find this information useful<span class="op">.</span></span>
<span class="line" data-line-no="3"><span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>In this example, <code class="syntax-highlight lang-papyri"><span class="name">$_panel_kind</span></code> is a positional parameter because its name begins with an underscore. If we wrote it as <code class="syntax-highlight lang-papyri"><span class="name">$panel_kind</span></code> then it would be a named parameter, so we would call the function like this:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="decorator">@panel</span><span class="lparen" data-paren-no="1">(</span>panel_kind<span class="op">=</span><span class="string">`info`</span><span class="rparen" data-paren-no="1">)</span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>It’s recommended to use named parameters unless it will be obvious from context what the argument is for.</p><h2 id="types"><a class="anchor" href="#types"><i class="anchor-icon fas fa-hashtag"></i> Types</a></h2><p>In the previous section we mentioned two types: <code class="syntax-highlight lang-papyri">str</code> and <code class="syntax-highlight lang-papyri">any</code>. Papyri has the following primitive types:</p><ul><li><code class="syntax-highlight lang-papyri">any</code> — any type of value.</li><li><code class="syntax-highlight lang-papyri">html</code> — HTML content, including text and tags.</li><li><code class="syntax-highlight lang-papyri">block</code> — <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements">block-level</a> HTML content.</li><li><code class="syntax-highlight lang-papyri">inline</code> — <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements">inline</a> HTML content.</li><li><code class="syntax-highlight lang-papyri">str</code> — a string.</li><li><code class="syntax-highlight lang-papyri">int</code> — an integer, such as <code class="syntax-highlight lang-papyri"><span class="number">5</span></code> or <code class="syntax-highlight lang-papyri"><span class="number">-23</span></code>.<a class="footnote-ref"></a><span class="footnote panel">Values must be in the signed 64-bit range, −2<sup>63</sup> ≤ <i>x</i> ≤ 2<sup>63</sup>−1.</span></li><li><code class="syntax-highlight lang-papyri">bool</code> — a Boolean, either <code class="syntax-highlight lang-papyri"><span class="keyword-literal">True</span></code> or <code class="syntax-highlight lang-papyri"><span class="keyword-literal">False</span></code>.</li><li><code class="syntax-highlight lang-papyri">function</code> — a function. Functions in Papyri are first-class values; you can refer to a function <code class="syntax-highlight lang-papyri"><span class="decorator">@func_name</span></code> (as opposed to calling it) by writing <code class="syntax-highlight lang-papyri"><span class="name">$func_name</span></code> instead.</li><li><code class="syntax-highlight lang-papyri">none</code> — a null or empty value. This is used for the “contents” parameter of a function with no contents; it can be written as a literal ‘<code class="syntax-highlight lang-papyri"><span class="op">.</span></code>’ or an empty pair of braces <code class="syntax-highlight lang-papyri"><span class="lparen" data-paren-no="1">{</span><span class="rparen" data-paren-no="1">}</span></code>.<a class="footnote-ref"></a><span class="footnote panel">Note that if you have a variable of type <code class="syntax-highlight lang-papyri">none</code> and you try to output it, you won’t get a ‘<code class="syntax-highlight lang-papyri"><span class="op">.</span></code>’ character in the output — you’ll get nothing. So ‘<code class="syntax-highlight lang-papyri"><span class="op">.</span></code>’ has a different meaning in text than it has when used as a value.</span></li></ul><p>Additionally, there are three kinds of compound type:</p><ul><li><code class="syntax-highlight lang-papyri">T list</code> — a list of values, written like <code class="syntax-highlight lang-papyri"><span class="lparen" data-paren-no="1">[</span><span class="number">1</span><span class="op">,</span> <span class="number">2</span><span class="op">,</span> <span class="number">3</span><span class="rparen" data-paren-no="1">]</span></code> in square brackets with commas.</li><li><code class="syntax-highlight lang-papyri">T dict</code> — a dictionary of values, which can be created like <code class="syntax-highlight lang-papyri"><span class="decorator">@dict</span><span class="op">::</span>new<span class="lparen" data-paren-no="1">(</span>x<span class="op">=</span><span class="number">3</span><span class="op">,</span> y<span class="op">=</span><span class="number">4</span><span class="rparen" data-paren-no="1">)</span><span class="op">.</span></code>. The keys in a dictionary are always strings.</li><li><code class="syntax-highlight lang-papyri">T<span class="op">?</span></code> — an optional value; either <code class="syntax-highlight lang-papyri">T</code> or <code class="syntax-highlight lang-papyri">none</code>.<a class="footnote-ref"></a><span class="footnote panel">This is not a true “option type” — if <code class="syntax-highlight lang-papyri">none</code> is already assignable to <code class="syntax-highlight lang-papyri">T</code> then <code class="syntax-highlight lang-papyri">T<span class="op">?</span></code> is just the same as <code class="syntax-highlight lang-papyri">T</code>. In practice this means types like <code class="syntax-highlight lang-papyri">str<span class="op">??</span></code>, <code class="syntax-highlight lang-papyri">any<span class="op">?</span></code> and <code class="syntax-highlight lang-papyri">none<span class="op">?</span></code> will be simplified to <code class="syntax-highlight lang-papyri">str<span class="op">?</span></code>, <code class="syntax-highlight lang-papyri">any</code> and <code class="syntax-highlight lang-papyri">none</code> respectively.</span></li></ul><p>Here <code class="syntax-highlight lang-papyri">T</code> can be any type, primitive or otherwise; so for example, <code class="syntax-highlight lang-papyri">str list</code> means a list of strings, <code class="syntax-highlight lang-papyri">int dict</code> means a dictionary of integers, and <code class="syntax-highlight lang-papyri">any list<span class="op">?</span></code> means an optional list which can contain values of any type (and possibly of different types to each other).</p><p>Type annotations for parameters are used to check the arguments and <b>coerce</b> them to the appropriate types, where possible. For example, <code class="syntax-highlight lang-papyri">int</code> and <code class="syntax-highlight lang-papyri">bool</code> values can be coerced to <code class="syntax-highlight lang-papyri">str</code>, and values of any type can be coerced to <code class="syntax-highlight lang-papyri">html</code>, or to <code class="syntax-highlight lang-papyri">block</code> (by wrapping inline content in a <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">p</span><span class="op">></span></code> tag if necessary).</p><p>To explicitly convert from one type to another, you can use the <code class="syntax-highlight lang-papyri"><span class="decorator">@block</span><span class="op">::</span>from</code>, <code class="syntax-highlight lang-papyri"><span class="decorator">@inline</span><span class="op">::</span>from</code> and <code class="syntax-highlight lang-papyri"><span class="decorator">@str</span><span class="op">::</span>from</code> functions, which are built-in. For example, <code class="syntax-highlight lang-papyri"><span class="decorator">@str</span><span class="op">::</span>from <span class="number">123</span></code> will make the string <code class="syntax-highlight lang-papyri"><span class="string">`123`</span></code>, and <code class="syntax-highlight lang-papyri"><span class="decorator">@block</span><span class="op">::</span>from <span class="lparen" data-paren-no="1">{</span>Foo bar<span class="rparen" data-paren-no="1">}</span></code> will make the HTML tag <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">p</span><span class="op">></span>Foo bar<span class="op"></p></span></code>. These functions only perform the same coercions as are done for any other arguments, so e.g. <code class="syntax-highlight lang-papyri"><span class="decorator">@str</span><span class="op">::</span>from <span class="op"><</span><span class="name">p</span><span class="op">></span>Foo bar<span class="op"></p></span></code> will not work.</p><h2 id="optional_parameters_and_default_values"><a class="anchor" href="#optional_parameters_and_default_values"><i class="anchor-icon fas fa-hashtag"></i> Optional Parameters and Default Values</a></h2><p>Function parameters can have default values:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@fn</span> <span class="name-def">greet</span><span class="lparen" data-paren-no="1">(</span><span class="name">$greeting</span><span class="op">:</span> inline <span class="op">=</span> <span class="lparen" data-paren-no="2">{</span>Hello<span class="rparen" data-paren-no="2">}</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$name</span><span class="op">:</span> inline <span class="op">-></span> <span class="lparen" data-paren-no="3">{</span></span>
<span class="line" data-line-no="2"> <span class="name">$greeting</span><span class="op">,</span> <span class="name">$name</span><span class="op">!</span></span>
<span class="line" data-line-no="3"><span class="rparen" data-paren-no="3">}</span></span>
</code></pre><p>This way we can call the function with or without the <code class="syntax-highlight lang-papyri">greeting</code> argument:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="decorator">@greet</span> Alice</code> — gives “Hello, Alice!”.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@greet</span><span class="lparen" data-paren-no="1">(</span>greeting<span class="op">=</span><span class="lparen" data-paren-no="2">{</span>Hi<span class="rparen" data-paren-no="2">}</span><span class="rparen" data-paren-no="1">)</span> Bob</code> — gives “Hi, Bob!”.</li></ul><p>Note that <code class="syntax-highlight lang-papyri">greeting</code> must be passed as a named argument, since its name doesn’t begin with an underscore. Note also that when calling the function with no argument (other than the “contents”), you don’t need to write it like <code class="syntax-highlight lang-papyri"><span class="decorator">@greet</span><span class="lparen" data-paren-no="1">(</span><span class="rparen" data-paren-no="1">)</span> Alice</code> with parentheses; that is allowed, but discouraged.</p><p>A parameter with a default value is optional; a parameter can also be made optional by declaring it with a question mark <code class="syntax-highlight lang-papyri"><span class="op">?</span></code> — note that this goes before the type annotation, if there is one:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@fn</span> <span class="name-def">say_goodbye</span><span class="lparen" data-paren-no="1">(</span><span class="name">$extra</span><span class="op">?:</span> inline<span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span></span>
<span class="line" data-line-no="2"> Goodbye<span class="op">,</span> <span class="name">$name</span><span class="op">!</span> <span class="name">$extra</span></span>
<span class="line" data-line-no="3"><span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>A parameter made optional this way has a default value of ‘<code class="syntax-highlight lang-papyri"><span class="op">.</span></code>’ (the null/empty value), and its type is implicitly an optional type (i.e. <code class="syntax-highlight lang-papyri">str<span class="op">?</span></code> instead of <code class="syntax-highlight lang-papyri">str</code>). One way to use parameters like this is as optional tag attributes: the syntax <code class="syntax-highlight lang-papyri"><span class="op">?=</span></code> in an HTML tag means the attribute will only be present if the value is not null/empty.</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@fn</span> <span class="name-def">filler_text</span><span class="lparen" data-paren-no="1">(</span><span class="name">$css_class</span><span class="op">?:</span> str<span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span><span class="op">:</span> inline <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span></span>
<span class="line" data-line-no="2"> <span class="op"><</span><span class="name">span</span> class<span class="op">?=</span><span class="name">$css_class</span><span class="op">></span></span>
<span class="line" data-line-no="3"> Lorem ipsum<span class="op">,</span> dolor sit amet<span class="op">.</span></span>
<span class="line" data-line-no="4"> <span class="op"></span></span></span>
<span class="line" data-line-no="5"><span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>A function’s “contents” parameter cannot have a default value and cannot be optional. However, you can use an optional type like <code class="syntax-highlight lang-papyri">html<span class="op">?</span></code> to allow the function to be called with either some contents like <code class="syntax-highlight lang-papyri"><span class="decorator">@my_function</span> <span class="lparen" data-paren-no="1">{</span>Contents<span class="op">...</span><span class="rparen" data-paren-no="1">}</span></code>, or no contents like <code class="syntax-highlight lang-papyri"><span class="decorator">@my_function</span><span class="op">.</span></code>.</p><h2 id="spreads"><a class="anchor" href="#spreads"><i class="anchor-icon fas fa-hashtag"></i> Spreads</a></h2><p>Parameters and arguments can be <b>spread</b>. A single asterisk <code class="syntax-highlight lang-papyri"><span class="op">*</span></code> spreads positional arguments, and a double asterisk <code class="syntax-highlight lang-papyri"><span class="op">**</span></code> spreads named arguments. These both work when declaring a function and also when calling it:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="keyword">@fn</span> <span class="name-def">my_function</span><span class="lparen" data-paren-no="1">(</span><span class="op">*</span><span class="name">$_args</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></code> — this declares a function which accepts any number of positional arguments, which will become a list named <code class="syntax-highlight lang-papyri"><span class="name">$_args</span></code>.</li><li><code class="syntax-highlight lang-papyri"><span class="keyword">@fn</span> <span class="name-def">my_function</span><span class="lparen" data-paren-no="1">(</span><span class="op">**</span><span class="name">$args</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></code> — this declares a function which accepts any number of named arguments, which will become a dictionary named <code class="syntax-highlight lang-papyri"><span class="name">$args</span></code>.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@my_function</span><span class="lparen" data-paren-no="1">(</span><span class="op">*</span><span class="name">$args</span><span class="rparen" data-paren-no="1">)</span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></code> — this calls the function with positional arguments unpacked from <code class="syntax-highlight lang-papyri"><span class="name">$args</span></code>, which must be a list.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@my_function</span><span class="lparen" data-paren-no="1">(</span><span class="op">**</span><span class="name">$args</span><span class="rparen" data-paren-no="1">)</span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></code> — this calls the function with named arguments unpacked from <code class="syntax-highlight lang-papyri"><span class="name">$args</span></code>, which must be a dictionary.</li></ul><p>Spread parameters can also have type declarations; for example, the following function accepts any number of positional arguments, but they must all be integers.</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@fn</span> <span class="name-def">my_function</span><span class="lparen" data-paren-no="1">(</span><span class="op">*</span><span class="name">$_args</span><span class="op">:</span> int list<span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>Note that a positional spread parameter must still have a name beginning with an underscore, because it’s positional.</p><h2 id="pattern_matching"><a class="anchor" href="#pattern_matching"><i class="anchor-icon fas fa-hashtag"></i> Pattern Matching</a></h2><p>Except for function calls, Papyri has only one control flow structure, called <code class="syntax-highlight lang-papyri"><span class="keyword">@match</span></code>. It can be used to create functions which do different things depending on their arguments, and it can also be used to extract parts out strings, lists or HTML tags. For example, we can use <code class="syntax-highlight lang-papyri"><span class="keyword">@match</span></code> to pluralise a word depending on a number:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@let</span><span class="lparen" data-paren-no="1">(</span>thing<span class="op">=</span><span class="lparen" data-paren-no="2">{</span>cow<span class="rparen" data-paren-no="2">}</span><span class="op">,</span> amount<span class="op">=</span><span class="number">2</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3"><span class="keyword">@match</span> <span class="name">$amount</span> <span class="lparen" data-paren-no="3">{</span></span>
<span class="line" data-line-no="4"> <span class="number">0</span> <span class="op">-></span> <span class="lparen" data-paren-no="4">{</span>No <span class="lparen" data-paren-no="5">{</span><span class="name">$thing</span><span class="rparen" data-paren-no="5">}</span>s<span class="op">.</span><span class="rparen" data-paren-no="4">}</span><span class="op">,</span></span>
<span class="line" data-line-no="5"> <span class="number">1</span> <span class="op">-></span> <span class="lparen" data-paren-no="6">{</span>One <span class="name">$thing</span><span class="op">.</span><span class="rparen" data-paren-no="6">}</span><span class="op">,</span></span>
<span class="line" data-line-no="6"> <span class="op">_</span> <span class="op">-></span> <span class="lparen" data-paren-no="7">{</span><span class="name">$amount</span> <span class="lparen" data-paren-no="8">{</span><span class="name">$thing</span><span class="rparen" data-paren-no="8">}</span>s<span class="op">.</span><span class="rparen" data-paren-no="7">}</span><span class="op">,</span></span>
<span class="line" data-line-no="7"><span class="rparen" data-paren-no="3">}</span></span>
</code></pre><p>The match rules must be written inside braces, with commas between them; each rule is a pattern, an arrow <code class="syntax-highlight lang-papyri"><span class="op">-></span></code>, and a replacement. The first matching pattern applies; you must ensure that some pattern always does apply (the compiler reports a warning if <code class="syntax-highlight lang-papyri"><span class="name">$amount</span></code> doesn’t match any of them). In this example, the patterns <code class="syntax-highlight lang-papyri"><span class="number">0</span></code> and <code class="syntax-highlight lang-papyri"><span class="number">1</span></code> are integer literals, and the pattern <code class="syntax-highlight lang-papyri"><span class="op">_</span></code> is a “wildcard” which always matches.</p><p>Other literal values can be used as patterns: integers, strings with backticks, <code class="syntax-highlight lang-papyri"><span class="keyword-literal">True</span></code> and <code class="syntax-highlight lang-papyri"><span class="keyword-literal">False</span></code>, and ‘<code class="syntax-highlight lang-papyri"><span class="op">.</span></code>’ which matches the the null/empty value. We can also match based on a value’s type:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@match</span> <span class="name">$v</span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="op">_:</span> none <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span>Nothing<span class="op">.</span><span class="rparen" data-paren-no="2">}</span><span class="op">,</span></span>
<span class="line" data-line-no="3"> <span class="op">_:</span> bool <span class="op">-></span> <span class="lparen" data-paren-no="3">{</span>A Boolean<span class="op">.</span><span class="rparen" data-paren-no="3">}</span><span class="op">,</span></span>
<span class="line" data-line-no="4"> <span class="op">_:</span> int <span class="op">-></span> <span class="lparen" data-paren-no="4">{</span>An integer<span class="op">.</span><span class="rparen" data-paren-no="4">}</span><span class="op">,</span></span>
<span class="line" data-line-no="5"> <span class="op">_:</span> string <span class="op">-></span> <span class="lparen" data-paren-no="5">{</span>A string<span class="op">.</span><span class="rparen" data-paren-no="5">}</span><span class="op">,</span></span>
<span class="line" data-line-no="6"> <span class="op">_:</span> html <span class="op">-></span> <span class="lparen" data-paren-no="6">{</span>Some HTML content<span class="op">.</span><span class="rparen" data-paren-no="6">}</span><span class="op">,</span></span>
<span class="line" data-line-no="7"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><p>Things get more interesting when we start matching on <em>parts</em> of a value. For example, here’s a recursive function which reverses a list:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@fn</span> <span class="name-def">reverse_list</span> <span class="name">$a</span><span class="op">:</span> any list <span class="op">-></span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="keyword">@match</span> <span class="name">$a</span> <span class="lparen" data-paren-no="2">{</span></span>
<span class="line" data-line-no="3"> <span class="lparen" data-paren-no="3">[</span><span class="rparen" data-paren-no="3">]</span> <span class="op">-></span> <span class="lparen" data-paren-no="4">[</span><span class="rparen" data-paren-no="4">]</span><span class="op">,</span></span>
<span class="line" data-line-no="4"> <span class="lparen" data-paren-no="5">[</span><span class="name">$head</span><span class="op">,</span> <span class="op">*</span><span class="name">$tail</span><span class="rparen" data-paren-no="5">]</span> <span class="op">-></span> <span class="lparen" data-paren-no="6">[</span><span class="op">*</span><span class="decorator">@reverse_list</span><span class="lparen" data-paren-no="7">(</span><span class="name">$tail</span><span class="rparen" data-paren-no="7">)</span><span class="op">,</span> <span class="name">$head</span><span class="rparen" data-paren-no="6">]</span><span class="op">,</span></span>
<span class="line" data-line-no="5"> <span class="rparen" data-paren-no="2">}</span></span>
<span class="line" data-line-no="6"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><p>The pattern <code class="syntax-highlight lang-papyri"><span class="lparen" data-paren-no="1">[</span><span class="rparen" data-paren-no="1">]</span></code> matches an empty list, for the base case of the recursion. The other pattern matches a list with at least one item; the first item will be bound to the variable <code class="syntax-highlight lang-papyri"><span class="name">$head</span></code>, and the spread pattern <code class="syntax-highlight lang-papyri"><span class="op">*</span><span class="name">$tail</span></code> binds the remaining items to the variable <code class="syntax-highlight lang-papyri"><span class="name">$tail</span></code>, as another list.</p><p>We can also match on parts of strings, using <b>template patterns</b>:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@match</span> <span class="string">`Hello, world!`</span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="op">"</span>Hello<span class="op">,</span> <span class="name">$thing</span><span class="op">!"</span> <span class="op">-></span> <span class="name">$thing</span><span class="op">,</span></span>
<span class="line" data-line-no="3"> <span class="op">_</span> <span class="op">-></span> <span class="decorator">@raise</span> <span class="string">`No greeting found`</span><span class="op">,</span></span>
<span class="line" data-line-no="4"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><p>In case the pattern does not match, we can use the built-in <code class="syntax-highlight lang-papyri"><span class="decorator">@raise</span></code> function to report an error message.</p><p>Patterns can be combined in nearly arbitrary ways: the example below matches a list with two items, but only if the first one is an integer and the second one is a string with a colon in the middle:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@match</span> <span class="name">$v</span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="lparen" data-paren-no="2">[</span><span class="name">$x</span><span class="op">:</span> int<span class="op">,</span> <span class="op">"</span><span class="name">$a</span><span class="op">:</span><span class="name">$b</span><span class="op">"</span><span class="rparen" data-paren-no="2">]</span> <span class="op">-></span> <span class="lparen" data-paren-no="3">{</span></span>
<span class="line" data-line-no="3"> Found an integer <span class="name">$x</span> and two string parts <span class="op">"</span><span class="name">$a</span><span class="op">"</span> and <span class="op">"</span><span class="name">$b</span><span class="op">".</span></span>
<span class="line" data-line-no="4"> <span class="rparen" data-paren-no="3">}</span><span class="op">,</span></span>
<span class="line" data-line-no="5"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><h2 id="html_tag_patterns"><a class="anchor" href="#html_tag_patterns"><i class="anchor-icon fas fa-hashtag"></i> HTML Tag Patterns</a></h2><p>Because values in Papyri can also represent HTML content, we can write patterns which match HTML tags, and extract parts of them. Here’s an example:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@let</span><span class="lparen" data-paren-no="1">(</span>tag<span class="op">=<</span><span class="name">span</span> id<span class="op">=</span><span class="string">`some_span`</span><span class="op">></span> <span class="op">...</span> <span class="op"></span></span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></span>
<span class="line" data-line-no="2"></span>
<span class="line" data-line-no="3"><span class="keyword">@match</span> <span class="name">$tag</span> <span class="lparen" data-paren-no="2">{</span></span>
<span class="line" data-line-no="4"> <span class="op"><</span><span class="name">span</span> id<span class="op">=</span><span class="name">$id</span><span class="op">></span> <span class="op">_</span> <span class="op"></span></span> <span class="op">-></span> <span class="lparen" data-paren-no="3">{</span>The id is <span class="op">"</span><span class="name">$id</span><span class="op">".</span><span class="rparen" data-paren-no="3">}</span><span class="op">,</span></span>
<span class="line" data-line-no="5"> <span class="op"><</span><span class="name">span</span><span class="op">></span> <span class="op">_</span> <span class="op"></span></span> <span class="op">-></span> <span class="lparen" data-paren-no="4">{</span>No id found<span class="op">.</span><span class="rparen" data-paren-no="4">}</span><span class="op">,</span></span>
<span class="line" data-line-no="6"> <span class="op">_</span> <span class="op">-></span> <span class="decorator">@raise</span> <span class="string">`Doesn't seem to be a <span> tag.`</span><span class="op">,</span></span>
<span class="line" data-line-no="7"><span class="rparen" data-paren-no="2">}</span></span>
</code></pre><p>The first pattern here matches a <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">span</span><span class="op">></span></code> tag with an <code class="syntax-highlight lang-papyri">id</code> attribute, and any contents. The second pattern matches a <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">span</span><span class="op">></span></code> tag with no attributes, and any contents. If we wanted these patterns to also match tags with other attributes, we could use a spread pattern:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@match</span> <span class="name">$tag</span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="op"><</span><span class="name">span</span> id<span class="op">=</span><span class="name">$id</span> <span class="op">**_></span> <span class="op">_</span> <span class="op"></span></span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span>The id is <span class="op">"</span><span class="name">$id</span><span class="op">".</span><span class="rparen" data-paren-no="2">}</span><span class="op">,</span></span>
<span class="line" data-line-no="3"> <span class="op"><</span><span class="name">span</span> <span class="op">**_></span> <span class="op">_</span> <span class="op"></span></span> <span class="op">-></span> <span class="lparen" data-paren-no="3">{</span>No id found<span class="op">.</span><span class="rparen" data-paren-no="3">}</span><span class="op">,</span></span>
<span class="line" data-line-no="4"> <span class="op">_</span> <span class="op">-></span> <span class="decorator">@raise</span> <span class="string">`Not a <span> tag.`</span><span class="op">,</span></span>
<span class="line" data-line-no="5"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><p>Here, the attribute spread pattern is <code class="syntax-highlight lang-papyri"><span class="op">**_</span></code> because we don’t care what other attributes the tag has, so we just ignore them with the wildcard pattern <code class="syntax-highlight lang-papyri"><span class="op">_</span></code>.</p><p>We can get more general than this: it’s possible to match <em>any kind of tag</em> by matching the tag name as a wildcard:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@match</span> <span class="name">$tag</span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="op"><_</span> <span class="op">**_></span> <span class="op">_</span> <span class="op"></></span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span>Some HTML tag<span class="op">,</span> don<span class="op">'</span>t know what<span class="op">.</span><span class="rparen" data-paren-no="2">}</span><span class="op">,</span></span>
<span class="line" data-line-no="3"> <span class="op">_</span> <span class="op">-></span> <span class="decorator">@raise</span> <span class="string">`Not an HTML tag.`</span><span class="op">,</span></span>
<span class="line" data-line-no="4"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><p>This pattern matches a tag of any name with any attributes, and any contents, i.e. it always matches any HTML tag. Note that the closing tag must be written as <code class="syntax-highlight lang-papyri"><span class="op"></></span></code>, with the tag name omitted, since we don’t know what it is!<a class="footnote-ref"></a><span class="footnote panel">In this context this syntax is necessary, but the same syntax is also allowed as a shorthand for a closing tag in other contexts. For example, <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">blockquote</span><span class="op">></span> <span class="op">...</span> <span class="op"></></span></code> is valid in Papyri. (It would not be valid in pure HTML.)</span></p><p>If we want to make use of the tag name, attributes and/or contents, we can go further and bind them to variables. This allows us to transform tags in arbitrary ways:</p><pre class="panel listing"><code class="syntax-highlight lang-papyri"><span class="line" data-line-no="1"><span class="keyword">@match</span> <span class="name">$tag</span> <span class="lparen" data-paren-no="1">{</span></span>
<span class="line" data-line-no="2"> <span class="op"><</span><span class="name">$tag_name</span> <span class="op">**</span><span class="name">$attrs</span><span class="op">></span> <span class="name">$contents</span> <span class="op"></></span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span><span class="op">,</span></span>
<span class="line" data-line-no="3"> <span class="op">_</span> <span class="op">-></span> <span class="decorator">@raise</span> <span class="string">`Not an HTML tag.`</span><span class="op">,</span></span>
<span class="line" data-line-no="4"><span class="rparen" data-paren-no="1">}</span></span>
</code></pre><p>Using the variables captured by the match pattern, we can construct an altered version of the tag. For example:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="name">$contents</span></code> — get rid of the tag and its attributes, but keep its contents.</li><li><code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">$tag_name</span> <span class="op">**</span><span class="name">$attrs</span><span class="op">></span>Contents replaced<span class="op">.</></span></code> — replace the contents of the tag with something else.</li><li><code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">div</span> <span class="op">**</span><span class="name">$attrs</span> id<span class="op">=</span><span class="string">`new_tag`</span><span class="op">></span><span class="name">$contents</span><span class="op"></></span></code> — change the tag name to <code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">div</span><span class="op">></span></code> and give it an <code class="syntax-highlight lang-papyri">id</code> attribute, but keep the rest the same.</li><li><code class="syntax-highlight lang-papyri"><span class="op"><</span><span class="name">$tag_name</span> <span class="op">**</span><span class="name">$attrs</span> style<span class="op">=</span><span class="string">`opacity: 0.5;`</span><span class="op">></span><span class="name">$contents</span><span class="op"></></span></code> — add a CSS style attribute to the tag, with no other changes.</li></ul><p>In practice, this capability is rarely needed. For many purposes, you can use built-in functions instead:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="decorator">@attributes</span><span class="lparen" data-paren-no="1">(</span>id<span class="op">=</span><span class="string">`new_tag`</span><span class="op">,</span> hidden<span class="op">=</span><span class="keyword-literal">True</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$tag</span></code> — adds attributes to a tag.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@class</span><span class="lparen" data-paren-no="1">(</span><span class="string">`new_css_class`</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$tag</span></code> — adds a CSS class to a tag, without removing its existing CSS class if there is one.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@style</span><span class="lparen" data-paren-no="1">(</span><span class="string">`opacity: 0.5;`</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$tag</span></code> — adds CSS style to a tag, without removing any style it might already have. (Make sure to include the trailing semicolon.)</li></ul><h2 id="imports_and_exports"><a class="anchor" href="#imports_and_exports"><i class="anchor-icon fas fa-hashtag"></i> Imports and Exports</a></h2><p>If you are writing a set of documents, there may be some functions or other declarations which you want to reuse across multiple documents. Or, if a single document is quite long you might want to split it across several source files while still compiling it to a single HTML file.</p><p>To export from the current file, use the <code class="syntax-highlight lang-papyri"><span class="keyword">@export</span></code> keyword:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="keyword">@export</span><span class="lparen" data-paren-no="1">(</span>name<span class="op">=</span><span class="name">$value</span><span class="rparen" data-paren-no="1">)</span><span class="op">.</span></code> — exports <code class="syntax-highlight lang-papyri"><span class="name">$value</span></code> with the name <code class="syntax-highlight lang-papyri">name</code>.</li><li><code class="syntax-highlight lang-papyri"><span class="keyword">@export</span> <span class="keyword">@let</span><span class="lparen" data-paren-no="1">(</span>name<span class="op">=</span><span class="name">$value</span><span class="rparen" data-paren-no="1">)</span><span class="op">...</span></code> — exports <code class="syntax-highlight lang-papyri"><span class="name">$value</span></code> with the name <code class="syntax-highlight lang-papyri">name</code>, and also makes <code class="syntax-highlight lang-papyri"><span class="name">$name</span></code> available in the enclosed scope.</li><li><code class="syntax-highlight lang-papyri"><span class="keyword">@export</span> <span class="keyword">@fn</span> <span class="name-def">foo</span><span class="lparen" data-paren-no="1">(</span><span class="rparen" data-paren-no="1">)</span> <span class="name">$v</span> <span class="op">-></span> <span class="lparen" data-paren-no="2">{</span> <span class="op">...</span> <span class="rparen" data-paren-no="2">}</span></code> — declares a function <code class="syntax-highlight lang-papyri"><span class="decorator">@foo</span></code> in the current scope, and also exports it with the name <code class="syntax-highlight lang-papyri">foo</code>.</li></ul><p>To import another Papyri source file:</p><ul><li><code class="syntax-highlight lang-papyri"><span class="decorator">@import</span> <span class="string">`filename`</span></code> — imports <code class="syntax-highlight lang-papyri">filename<span class="op">.</span>papyri</code> as a library, relative to the current file’s path. The exports from that file are returned as a dictionary; HTML output from that file, if there is any, is ignored.</li><li><code class="syntax-highlight lang-papyri"><span class="decorator">@include</span> <span class="string">`filename`</span></code> — includes <code class="syntax-highlight lang-papyri">filename<span class="op">.</span>papyri</code>, relative to the current file’s path, into the current document; its HTML output is included, and its exports are included as variables in the current scope.</li></ul><h2 id="conclusion"><a class="anchor" href="#conclusion"><i class="anchor-icon fas fa-hashtag"></i> Conclusion</a></h2><p>That’s all for now. There are a few other things, but they aren’t that important.</p><p>Papyri is still under development, so this tutorial will get updated as new features are added to the language. In the meantime, if you want to see more examples of how to write documents in Papyri, the <a href="https://github.com/kaya3/papyri/blob/master/tutorial.papyri">Papyri source for this page</a> is a good place to look next.</p></article><script type="text/javascript" src="./papyri.js"></script></body></html>