-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutorial.papyri
701 lines (526 loc) · 26.3 KB
/
tutorial.papyri
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
@include `defs.lib`
@implicit(language=`papyri`)
@page(`Tutorial`)...
@h1 {Papyri Tutorial}
@href(`./`) Papyri 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.
Papyri compiles to HTML, and almost all HTML syntax is also valid in Papyri:
```
You <em>can</em> write Papyri like this, but HTML is a bit clunky.
```
However, it's much more convenient when writing Papyri to use @b functions to mark up your content:
```
It's better to write Papyri like @emph this, instead.
```
@h2 {Functions}
Here are a few built-in functions:
[
{`@b` or `@bold`: bold text.#
@footnote {These wrap inline content in a `<b>` or `<strong>` tag respectively.}},
{`@i` or `@emph`: italic text.#
@footnote {These wrap inline content in an `<i>` or `<em>` tag respectively.}},
{`@underline`: take a wild guess.},
{`@h1` through `@h6`: headings.},
]
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 to group multiple words together:
```
For example, with no braces only @emph one word is emphasised, but you can use
braces to emphasise @emph {multiple words} like this.
```
The item which the function applies to is called its @b contents. The contents of a function may be
the result of another function:
```
If you absolutely @underline @b must stress something...
```
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:
```
@h1 {A @i {top-level heading} with nested markup}
```
@h2 {Arguments}
Some functions accept more arguments than just their "contents". For example, here's how you can
write a link in Papyri:
```
@href(`https://kaya3.github.io/papyri/tutorial.html`) {Click here!}
```
Arguments to a function are written in parentheses. In this case, the `@href` function has one
argument: the link URL, a string literal written in backticks (the `` ` `` symbol).
Some functions have @b {named arguments}:
```
@image(alt={Initech corporate logo}) `resources/logo.png`
```
For the `@image` function, both the `alt` argument and the contents are strings, although the `alt`
argument here is written as text inside braces instead of backticks. Simple string values can also
be written without backticks:
```
@image(alt=Logo) `resources/logo.png`
```
Here, the string `Logo` 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.
@h2 {Page Template}
An HTML page needs some structure around the main content --- a doctype, `<html>`, `<head>` and
`<body>` tags, and a `<title>`, at least. Fortunately, you can wrap your page in this boilerplate
code using the `@page` function. This function takes a named `title` argument, and the "contents" are
the page contents:
```
@page(title={A Very Interesting Page})...
If there was anything here, then this page would be very interesting.
```
The `@page` function builds a page using the default Papyri theme, which is the one you see on this
page.
This example introduces another piece of syntax: when used for a function's "contents", an ellipsis
`...` means the remainder of the document (or the remainder of the current group or HTML tag).
@h2 {Variables}
You can declare variables using the keyword `@let`. The syntax for `@let` is like a function: it takes
any number of named arguments, which become variables you can refer to as `$var_name` within the
"contents" of the `@let` expression.
In practice, you usually want to use the `...` 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).
```
@let(position={Director of Redundancies Director}, company={Initech})...
Please submit your TPS reports to the $position at $company.
```
Variables can appear within text, or can also be used as function arguments, including the "contents"
argument:
```
@let(url=`../index.html`, link_text={Back to the home page})...
@href($url) $link_text
```
@h2 {String Templates}
Additionally, string variables can be used in @b {string templates}, to build a string value out of
fixed and variable parts:
```
@image(alt="$company corporate logo") `resources/logo.png`
```
String templates can also be used directly in HTML tags:
```
@let(panel_kind=`info`)...
<div class="panel $panel_kind">
You may find this information useful.
</div>
```
String templates are delimited either by single-quotes `'`, or double-quotes `"`. 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 `\"` or `\'`.
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:
```
<div class="{$panel_kind}_panel"> ... </div>
```
@h2 {More Formatting}
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:
```
This content has two paragraphs.
They get split apart because of the blank line in between.
```
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.
```
Items on the menu include:
[
{Spam},
{Spam and eggs},
{Eggs and spam},
]
```
For numbered lists, simply use the built-in `@numbered` function:
```
@numbered [
{Acquire underpants},
{???},
{Profit},
]
```
@h2 {Code Formatting}
Papyri makes it easy for documents to include properly-formatted source code. The `@code` and
`@code_block` 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 `` ` ``
anywhere that text is expected, these functions are automatically called. For example:
````
This paragraph is text, but `this part` is formatted as inline code. There is
also a block-level code listing below it:
```
This part is a code listing, because it has three backticks instead of one.
The indentation will be stripped automatically, based on how the first
non-empty line is indented.
```
````
For syntax highlighting, put the following line#
@footnote {`@implicit` is like `@let`, except it also allows the variable to be passed implicitly
to the `@code` and `@code_block` functions which know to accept it. Implicit parameters are
beyond the scope of this tutorial.}
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:
```
@implicit(language=`papyri`)...
```
For formatting program input or output (as opposed to source code), use the built-in functions `@kbd`
and `@samp` respectively. These output `<kbd>` and `<samp>` tags instead of `<code>` tags, and do not
use syntax highlighting.
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 `` `like this` ``, you can just use sufficiently wide delimiters,
@code ``` `` `like this` `` ```. (The surrounding whitespace will be trimmed.)
Three or more backticks make a code listing; if you need that many backticks but you @emph {don't}
want a block-level listing, you can call `@code` explicitly to make it formatted inline:
````
Calling `@code` with triple-backticks to format inline code containing
double-backticks: @code ``` `` `like this` `` ```.
````
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.#
@footnote {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` with a named `language`
argument, like ``@code(language=`java`) `inline.java.code();` ``.}
This overrides the "implicit" language declaration, if there is one.
````
@implicit(language=`python`)...
Most of the code in this document, like `[2 * x for x in range(5)]`, is going
to be written in Python, but the following listing shows how that might be
written in Java:
```java
List<Integer> numbers = new ArrayList<>();
for(int x = 0; x < 5; x++) {
numbers.add(2 * x);
}
```
````
@h2 {Comments}
Papyri has two kinds of comments:
[
{Line comments, beginning with `#` until the end of the line.},
{HTML-style multiline comments, `<!-- ... -->`.},
]
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 @emph do work inside string templates.
Comments are never preserved in the compiled HTML, even if they are HTML-style multiline comments.
Line comments (those beginning with `#`) 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#
@footnote {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.}
using the built-in `@footnote` function, it makes sense to put it on a separate line, and write `#` at
the end of the previous line to prevent a space being inserted before the footnote marker:
```
This sentence is true.#
@footnote {Actually, it's more complicated than that.}
```
@h2 {Special Characters}
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:
@let(urls=@dict::new(
nbsp = `https://en.wikipedia.org/wiki/Non-breaking_space`,
word_joiner = `https://en.wikipedia.org/wiki/Word_joiner`,
).) [
{The quote characters `'` and `"` will be converted automatically into "better-looking" Unicode
quotes.#
@footnote {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: `{"}` always
makes a left-quote, and `{}"` always makes a right-quote.}},
{Two dashes `--` make an en-dash, and three `---` make an em-dash.},
{Three dots `...` become an ellipsis.},
{A tilde `~` makes a @href($urls::nbsp) {non-breaking space}, and a pipe `|` makes a
"@href($urls::word_joiner) {word joiner}" --- an invisible character which prevents text from
wrapping at that point.},
{The arrows `<-`, `->` and `<->` become <-, -> and <->.},
{Mathematical symbols: `<=`, `>=` and `!=` become <=, >= and !=, `+/-` becomes +/-.},
{The copyright symbol (c) is written like `(c)`.},
]
Some characters like `$`, `@` 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: `\$` and `\@` give the
raw characters, and `\\` gives a raw backslash if you want that. The same works for preventing text
substitutions: for example, `\~` gives a literal tilde.
Other characters can be written with hexadecimal Unicode escape sequences or HTML entities:
[
{`\x61`, `\u0061` and `\U00000061` for two, four or eight hexadecimal digits.},
{Named entities such as `"`, decimal entities like `a`, or hexadecimal entities like
`a`.},
]
Escape sequences and entities don't work inside string literals (with backticks), but they do work in
string templates.
@h2 {Declaring Functions}
So far we've seen that Papyri is a markup language, but here's where we start to see that it's a
@emph programmable markup language. You can declare your own functions, to mark up your documents in
customisable ways:
```
@fn panel($_panel_kind: str) $v -> {
<div class="panel $_panel_kind">$v</div>
}
```
The `@fn` 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 `$_panel_kind` which must be of type `str` (i.e. a string), plus the "contents"
parameter, which is named `$v`. There is no type annotation for `$v`, which is equivalent to a type
annotation `$v: any` (i.e. any type is allowed). The function body then appears after the arrow, `->`.
Now that the function is declared, it can be called like any other:
```
@panel(`info`) {
You may find this information useful.
}
```
In this example, `$_panel_kind` is a positional parameter because its name begins with an underscore.
If we wrote it as `$panel_kind` then it would be a named parameter, so we would call the function like
this:
```
@panel(panel_kind=`info`) { ... }
```
It's recommended to use named parameters unless it will be obvious from context what the argument is
for.
@h2 {Types}
In the previous section we mentioned two types: `str` and `any`. Papyri has the following primitive
types:
@let(urls=@dict::new(
block_level = `https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements`,
inline = `https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements`,
).) [
{`any` --- any type of value.},
{`html` --- HTML content, including text and tags.},
{`block` --- @href($urls::block_level) {block-level} HTML content.},
{`inline` --- @href($urls::inline) inline HTML content.},
{`str` --- a string.},
{`int` --- an integer, such as `5` or `-23`.#
@footnote {Values must be in the signed 64-bit range, -2@sup{63}~<=~@i x~<=~2@sup{63}-1.}},
{`bool` --- a Boolean, either `True` or `False`.},
{`function` --- a function. Functions in Papyri are first-class values; you can refer to a function
`@func_name` (as opposed to calling it) by writing `$func_name` instead.},
{`none` --- 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 '`.`' or an empty pair of braces `{}`.#
@footnote {Note that if you have a variable of type `none` and you try to output it, you won't
get a '`.`' character in the output --- you'll get nothing. So '`.`' has a different meaning
in text than it has when used as a value.}},
]
Additionally, there are three kinds of compound type:
[
{`T list` --- a list of values, written like `[1, 2, 3]` in square brackets with commas.},
{`T dict` --- a dictionary of values, which can be created like `@dict::new(x=3, y=4).`. The keys
in a dictionary are always strings.},
{`T?` --- an optional value; either `T` or `none`.#
@footnote {This is not a true "option type" --- if `none` is already assignable to `T` then
`T?` is just the same as `T`. In practice this means types like `str??`, `any?` and `none?`
will be simplified to `str?`, `any` and `none` respectively.}},
]
Here `T` can be any type, primitive or otherwise; so for example, `str list` means a list of strings,
`int dict` means a dictionary of integers, and `any list?` means an optional list which can contain
values of any type (and possibly of different types to each other).
Type annotations for parameters are used to check the arguments and @b coerce them to the appropriate
types, where possible. For example, `int` and `bool` values can be coerced to `str`, and values of any
type can be coerced to `html`, or to `block` (by wrapping inline content in a `<p>` tag if necessary).
To explicitly convert from one type to another, you can use the `@block::from`, `@inline::from` and
`@str::from` functions, which are built-in. For example, `@str::from 123` will make the string `` `123` ``,
and `@block::from {Foo bar}` will make the HTML tag `<p>Foo bar</p>`. These functions only perform the
same coercions as are done for any other arguments, so e.g. `@str::from <p>Foo bar</p>` will not work.
@h2 {Optional Parameters and Default Values}
Function parameters can have default values:
```
@fn greet($greeting: inline = {Hello}) $name: inline -> {
$greeting, $name!
}
```
This way we can call the function with or without the `greeting` argument:
[
{`@greet Alice` --- gives "Hello, Alice!".},
{`@greet(greeting={Hi}) Bob` --- gives "Hi, Bob!".},
]
Note that `greeting` 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 `@greet() Alice` with parentheses; that is allowed, but discouraged.
A parameter with a default value is optional; a parameter can also be made optional by
declaring it with a question mark `?` --- note that this goes before the type annotation, if there is
one:
```
@fn say_goodbye($extra?: inline) $v -> {
Goodbye, $name! $extra
}
```
A parameter made optional this way has a default value of '`.`' (the null/empty value), and its type
is implicitly an optional type (i.e. `str?` instead of `str`). One way to use parameters like this is
as optional tag attributes: the syntax `?=` in an HTML tag means the attribute will only be present if
the value is not null/empty.
```
@fn filler_text($css_class?: str) $v: inline -> {
<span class?=$css_class>
Lorem ipsum, dolor sit amet.
</span>
}
```
A function's "contents" parameter cannot have a default value and cannot be optional. However, you can
use an optional type like `html?` to allow the function to be called with either some contents like
`@my_function {Contents...}`, or no contents like `@my_function.`.
@h2 {Spreads}
Parameters and arguments can be @b spread. A single asterisk `*` spreads positional arguments, and a
double asterisk `**` spreads named arguments. These both work when declaring a function and also when
calling it:
[
{`@fn my_function(*$_args) $v -> { ... }` --- this declares a function which accepts any number of
positional arguments, which will become a list named `$_args`.},
{`@fn my_function(**$args) $v -> { ... }` --- this declares a function which accepts any number of
named arguments, which will become a dictionary named `$args`.},
{`@my_function(*$args) { ... }` --- this calls the function with positional arguments unpacked from
`$args`, which must be a list.},
{`@my_function(**$args) { ... }` --- this calls the function with named arguments unpacked from
`$args`, which must be a dictionary.},
]
Spread parameters can also have type declarations; for example, the following function accepts any
number of positional arguments, but they must all be integers.
```
@fn my_function(*$_args: int list) $v -> { ... }
```
Note that a positional spread parameter must still have a name beginning with an underscore, because
it's positional.
@h2 {Pattern Matching}
Except for function calls, Papyri has only one control flow structure, called `@match`. 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 `@match` to pluralise a word
depending on a number:
```
@let(thing={cow}, amount=2)...
@match $amount {
0 -> {No {$thing}s.},
1 -> {One $thing.},
_ -> {$amount {$thing}s.},
}
```
The match rules must be written inside braces, with commas between them; each rule is a pattern, an
arrow `->`, and a replacement. The first matching pattern applies; you must ensure that some pattern
always does apply (the compiler reports a warning if `$amount` doesn't match any of them). In this
example, the patterns `0` and `1` are integer literals, and the pattern `_` is a "wildcard" which
always matches.
Other literal values can be used as patterns: integers, strings with backticks, `True` and `False`, and
'`.`' which matches the the null/empty value. We can also match based on a value's type:
```
@match $v {
_: none -> {Nothing.},
_: bool -> {A Boolean.},
_: int -> {An integer.},
_: string -> {A string.},
_: html -> {Some HTML content.},
}
```
Things get more interesting when we start matching on @emph parts of a value. For example, here's a
recursive function which reverses a list:
```
@fn reverse_list $a: any list -> {
@match $a {
[] -> [],
[$head, *$tail] -> [*@reverse_list($tail), $head],
}
}
```
The pattern `[]` 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 `$head`, and the spread
pattern `*$tail` binds the remaining items to the variable `$tail`, as another list.
We can also match on parts of strings, using @b {template patterns}:
```
@match `Hello, world!` {
"Hello, $thing!" -> $thing,
_ -> @raise `No greeting found`,
}
```
In case the pattern does not match, we can use the built-in `@raise` function to report an error
message.
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:
```
@match $v {
[$x: int, "$a:$b"] -> {
Found an integer $x and two string parts "$a" and "$b".
},
}
```
@h2 {HTML Tag Patterns}
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:
```
@let(tag=<span id=`some_span`> ... </span>)...
@match $tag {
<span id=$id> _ </span> -> {The id is "$id".},
<span> _ </span> -> {No id found.},
_ -> @raise `Doesn't seem to be a <span> tag.`,
}
```
The first pattern here matches a `<span>` tag with an `id` attribute, and any contents. The second
pattern matches a `<span>` 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:
```
@match $tag {
<span id=$id **_> _ </span> -> {The id is "$id".},
<span **_> _ </span> -> {No id found.},
_ -> @raise `Not a <span> tag.`,
}
```
Here, the attribute spread pattern is `**_` because we don't care what other attributes the tag has, so
we just ignore them with the wildcard pattern `_`.
We can get more general than this: it's possible to match @emph {any kind of tag} by matching the tag
name as a wildcard:
```
@match $tag {
<_ **_> _ </> -> {Some HTML tag, don't know what.},
_ -> @raise `Not an HTML tag.`,
}
```
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 `</>`, with the tag name omitted, since we don't
know what it is!#
@footnote {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, `<blockquote> ... </>` is valid in
Papyri. (It would not be valid in pure HTML.)}
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:
```
@match $tag {
<$tag_name **$attrs> $contents </> -> { ... },
_ -> @raise `Not an HTML tag.`,
}
```
Using the variables captured by the match pattern, we can construct an altered version of the tag. For
example:
[
{`$contents` --- get rid of the tag and its attributes, but keep its contents.},
{`<$tag_name **$attrs>Contents replaced.</>` --- replace the contents of the tag with something
else.},
{``<div **$attrs id=`new_tag`>$contents</>`` --- change the tag name to `<div>` and give it an `id`
attribute, but keep the rest the same.},
{``<$tag_name **$attrs style=`opacity: 0.5;`>$contents</>`` --- add a CSS style attribute to the
tag, with no other changes.},
]
In practice, this capability is rarely needed. For many purposes, you can use built-in functions
instead:
[
{``@attributes(id=`new_tag`, hidden=True) $tag`` --- adds attributes to a tag.},
{``@class(`new_css_class`) $tag`` --- adds a CSS class to a tag, without removing its existing CSS
class if there is one.},
{``@style(`opacity: 0.5;`) $tag`` --- adds CSS style to a tag, without removing any style it might
already have. (Make sure to include the trailing semicolon.)},
]
@h2 {Imports and Exports}
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.
To export from the current file, use the `@export` keyword:
[
{`@export(name=$value).` --- exports `$value` with the name `name`.},
{`@export @let(name=$value)...` --- exports `$value` with the name `name`, and also makes `$name`
available in the enclosed scope.},
{`@export @fn foo() $v -> { ... }` --- declares a function `@foo` in the current scope, and also
exports it with the name `foo`.},
]
To import another Papyri source file:
[
{``@import `filename` `` --- imports `filename.papyri` 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.},
{``@include `filename` `` --- includes `filename.papyri`, 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.},
]
@h2 {Conclusion}
That's all for now. There are a few other things, but they aren't that important.
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
@href(`https://github.com/kaya3/papyri/blob/master/tutorial.papyri`) {Papyri source for this page} is a
good place to look next.