From d0d6117176d68b2345d36e81ccdaa447e9caa724 Mon Sep 17 00:00:00 2001 From: Nathanael Sensfelder Date: Tue, 22 Dec 2020 04:08:34 +0100 Subject: Moving to Hugo. --- config.toml | 12 + content/_index.md | 56 +++ content/fate_v1/_index.md | 162 ++++++++ content/fate_v1/aliases/default.md | 41 +++ content/fate_v1/computations/_index.md | 138 +++++++ content/fate_v1/computations/collections/_index.md | 98 +++++ .../fate_v1/computations/conditionals/_index.md | 54 +++ content/fate_v1/computations/cons/_index.md | 22 ++ .../computations/lambda_functions/_index.md | 30 ++ content/fate_v1/computations/references/_index.md | 18 + content/fate_v1/computations/rich_text/_index.md | 11 + content/fate_v1/declarations/_index.md | 5 + content/fate_v1/declarations/events/_index.md | 18 + content/fate_v1/declarations/files/_index.md | 48 +++ content/fate_v1/declarations/sequences/_index.md | 113 ++++++ .../fate_v1/declarations/text_effects/_index.md | 31 ++ content/fate_v1/declarations/types/_index.md | 61 +++ content/fate_v1/declarations/variables/_index.md | 72 ++++ content/fate_v1/extensions/_index.md | 4 + content/fate_v1/instructions/_index.md | 48 +++ content/fate_v1/instructions/collections/_index.md | 72 ++++ .../fate_v1/instructions/conditionals/_index.md | 27 ++ content/fate_v1/instructions/loops/_index.md | 34 ++ .../fate_v1/instructions/player_choices/_index.md | 31 ++ content/fate_v1/instructions/references/_index.md | 7 + content/images/discord.svg | 70 ++++ content/images/github.svg | 71 ++++ content/images/reddit.svg | 63 ++++ content/images/tonkadur_logo_black.svg | 97 +++++ content/images/tonkadur_logo_black_as_path.svg | 117 ++++++ content/images/tonkadur_logo_white.svg | 98 +++++ content/images/tonkadur_logo_white_as_path.svg | 118 ++++++ content/learn/01.start/_index.md | 43 +++ content/learn/02.choices/_index.md | 71 ++++ content/learn/03.variables/default.md | 90 +++++ content/learn/04.sequences/default.md | 123 +++++++ content/learn/05.files/default.md | 113 ++++++ content/learn/06.pointers/default.md | 171 +++++++++ content/learn/07.structures/default.md | 201 ++++++++++ content/learn/08.collections/default.md | 408 +++++++++++++++++++++ content/learn/09.lambdas/default.md | 366 ++++++++++++++++++ content/learn/10.conditions/default.md | 402 ++++++++++++++++++++ content/learn/_index.md | 8 + content/made_with_tonkadur/_index.md | 3 + content/made_with_tonkadur/closed_source/_index.md | 5 + content/made_with_tonkadur/open_source/_index.md | 5 + content/setup/_index.md | 75 ++++ content/wyrd_v1/_index.md | 56 +++ content/wyrd_v1/computations/_index.md | 99 +++++ content/wyrd_v1/extensions/_index.md | 4 + content/wyrd_v1/instructions/_index.md | 75 ++++ layouts/shortcodes/fatecode.html | 2 + layouts/shortcodes/rawhtml.html | 2 + static/css/prism.css | 321 ++++++++++++++++ static/js/highlight.pack.js | 9 + 55 files changed, 4499 insertions(+) create mode 100644 config.toml create mode 100644 content/_index.md create mode 100644 content/fate_v1/_index.md create mode 100644 content/fate_v1/aliases/default.md create mode 100644 content/fate_v1/computations/_index.md create mode 100644 content/fate_v1/computations/collections/_index.md create mode 100644 content/fate_v1/computations/conditionals/_index.md create mode 100644 content/fate_v1/computations/cons/_index.md create mode 100644 content/fate_v1/computations/lambda_functions/_index.md create mode 100644 content/fate_v1/computations/references/_index.md create mode 100644 content/fate_v1/computations/rich_text/_index.md create mode 100644 content/fate_v1/declarations/_index.md create mode 100644 content/fate_v1/declarations/events/_index.md create mode 100644 content/fate_v1/declarations/files/_index.md create mode 100644 content/fate_v1/declarations/sequences/_index.md create mode 100644 content/fate_v1/declarations/text_effects/_index.md create mode 100644 content/fate_v1/declarations/types/_index.md create mode 100644 content/fate_v1/declarations/variables/_index.md create mode 100644 content/fate_v1/extensions/_index.md create mode 100644 content/fate_v1/instructions/_index.md create mode 100644 content/fate_v1/instructions/collections/_index.md create mode 100644 content/fate_v1/instructions/conditionals/_index.md create mode 100644 content/fate_v1/instructions/loops/_index.md create mode 100644 content/fate_v1/instructions/player_choices/_index.md create mode 100644 content/fate_v1/instructions/references/_index.md create mode 100644 content/images/discord.svg create mode 100644 content/images/github.svg create mode 100644 content/images/reddit.svg create mode 100644 content/images/tonkadur_logo_black.svg create mode 100644 content/images/tonkadur_logo_black_as_path.svg create mode 100644 content/images/tonkadur_logo_white.svg create mode 100644 content/images/tonkadur_logo_white_as_path.svg create mode 100644 content/learn/01.start/_index.md create mode 100644 content/learn/02.choices/_index.md create mode 100644 content/learn/03.variables/default.md create mode 100644 content/learn/04.sequences/default.md create mode 100644 content/learn/05.files/default.md create mode 100644 content/learn/06.pointers/default.md create mode 100644 content/learn/07.structures/default.md create mode 100644 content/learn/08.collections/default.md create mode 100644 content/learn/09.lambdas/default.md create mode 100644 content/learn/10.conditions/default.md create mode 100644 content/learn/_index.md create mode 100644 content/made_with_tonkadur/_index.md create mode 100644 content/made_with_tonkadur/closed_source/_index.md create mode 100644 content/made_with_tonkadur/open_source/_index.md create mode 100644 content/setup/_index.md create mode 100644 content/wyrd_v1/_index.md create mode 100644 content/wyrd_v1/computations/_index.md create mode 100644 content/wyrd_v1/extensions/_index.md create mode 100644 content/wyrd_v1/instructions/_index.md create mode 100644 layouts/shortcodes/fatecode.html create mode 100644 layouts/shortcodes/rawhtml.html create mode 100755 static/css/prism.css create mode 100755 static/js/highlight.pack.js diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..61e69cd --- /dev/null +++ b/config.toml @@ -0,0 +1,12 @@ +baseURL = "https://tonkadur.of.tacticians.online/" +languageCode = "en-us" +title = "Tonkadur" +theme = "hugo-theme-learn" +[params] + custom_css = ["css/prism.css"] + +# For search functionality +[outputs] +home = [ "HTML", "RSS", "JSON"] + + diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..6d1c67a --- /dev/null +++ b/content/_index.md @@ -0,0 +1,56 @@ +--- +title: "Main" +--- +# Welcome to Tonkadur's website! +Tonkadur is a tool to create game narratives. + +### Find out all about Tonkadur + * **Plain-text format.** Just write in your favorite editor and use your usual tools. + * **Extensive description language (Fate).** You shouldn't feel restrained in what you can write. + * **Strong typing.** Reduce the likeliness of errors in your descriptions. + * **Very simple and small interpreted language (Wyrd).** Easily add support for Tonkadur to your engine. + * **LISP inspired syntax.** No weird symbols everywhere. No annoying indentation restrictions. + +Tonkadur provides a compiler from Fate to Wyrd, letting you freely describe +your stories using a feature rich language without having to worry about the +implications when it comes time to add support for it to your engine. + +### Sample: +{{< fatecode >}}(define_sequence in_your_room () + (ifelse + (is_member visited_your_room progress) + (text_effect narrator + You room is still a mess. You don't have time to clean things up, + though. + ) + (text_effect narrator + You room is a mess. You recall having been through every drawer while + preparing your bag yesterday. While still unclear on how you are + supposed to pack all the necessary things for what promises to be at + least a year-long journey inside a small backpack, you cannot avoid + but wasting more time contemplating the piles of items that didn't + make the cut. + ) + ) + (add visited_your_room progress) + (player_choice + ( + ( Look for healing items ) + (jump_to look_for_healing_items) + ) + ( + ( No time! Let's go adventuring! ) + (jump_to leave_your_room) + ) + ) +) +{{< /fatecode >}} + +### Known alternatives +* [Inkle's Ink](https://www.inklestudios.com/ink/). +* [Yarn Spinner](https://yarnspinner.dev/). +* [DLG](https://github.com/iLambda/language-dlg). Nowhere near as popular as the +other two alternatives, but its approach shares more similarities with Tonkadur. + + +### Syntax Highlight Test: diff --git a/content/fate_v1/_index.md b/content/fate_v1/_index.md new file mode 100644 index 0000000..8b90a7a --- /dev/null +++ b/content/fate_v1/_index.md @@ -0,0 +1,162 @@ +--- +title: "Fate (Version 1)" +menuTitle: "Fate" +--- + +When using Tonkadur, Fate is the language the author writes in. This language is +meant to provide the writer with as many tools as possible, so that they do not +feel constrained by the limitations of the language, nor find writing complex +narratives tedious. + +## A few warnings first + +Fate uses strong typing. This is intended to make detection of mistakes easier, +but may lead to some frustrations for authors only used to languages using +dynamic typing. + +Fate does not have any special features for multi-seating/multi-player +narratives. This is by design, as adding proper support for this would make the +underlying language more complex, and thus would make it harder to create an +interpreter for it. This is however something considered for an extension +(see +[this GitHub issue about it](https://github.com/nsensfel/tonkadur/issues/5)). + +You'll need to learn parentheses management, if you make anything complex. Fate +uses a LISP inspired syntax, so parentheses are everywhere. The syntax gives you +free reign over indentation, but so many parentheses does require at least some +discipline in order to be readable. + +Fate is meant to be compiled. This does add an extra step to your existing +process. + +Fate is very much a new language. There isn't many (any?) tools for it. + +If for some reason you want to perform memory allocation, there is no automatic +garbage collection, so you will have to free whatever you allocate. + +In what may seem weird for narrative scripting, Fate does not feature string +manipulation operations. Strings can be made into rich text, with complex +attributes, but you will not be able to use things like regular expressions +(or anything other than simple concatenation, really) with just the base +language. This is because of these operations would need to be implemented +directly by the interpreter anyway, and their use isn't actually common in +a narrative description, which would make requiring support for them of +any Tonkadur interpreter problematic. + +## Some nice features + +If you haven't closed the tab yet, here are some reasons why you *might* want +to use Fate. + +It *is* a LISP inspired syntax. If that's what you like, you might even enjoy +this take on it. There's no fiddling with indentation, no wondering what symbol +does what. It's all about parentheses indicating something is performed on a +list of other things. It's not a purely functional language, though. In fact, +it's a mix of both declarative and imperative programming: sequences (or +procedures) are imperative. Computations are functional. There are lambda +functions, but they're strongly typed. Collections and structures can only be +modified through specific instructions, so it's not quite really a LISP-like +language. + +There are pointers. Oh, come on, it's a plus, *right?* This makes it a lot +easier to refer to things. There's no pointer arithmetic, so you don't have to +worry about the scary stuff. Also, this lets you allocate memory, which is +useful when you want a unspecified amount of distinct instances of something. +Want to describe a story where the player can push a button as many time as +they choose, create a clone of a creature every time they do so, yet let them +fight any of these creature as their distinct instance? Well, with memory +allocation and pointers, it's easily done. Didn't you know? When writing a +narrative, the RAM's the limit. So yeah, maybe don't forget to `free` the +thousands of monster the player will no doubt create. + +You can do recursion. Wait! I *am* listing the nice features! Do people really +not like recursion and pointers? Oh, well... Still, you can do recursions with +both procedures and lambda functions. Not a fan of recursion? That's alright, +you also have imperative loops: `for`, `while`, `do_while`, and even +`for_each`. + +## The basics + +#### Procedures (or Sequences) and Instructions +Procedures are lists of instructions and things to display. Instructions can be +applied to values, but they themselves do not return any value. Any value not +part of an instruction found in a procedure is displayed. Any value being part +of an instruction is **not** displayed. + +One way to think about it is to consider procedures as being actually two types +of things: sequences and procedures. A sequence is then a scene in the story, +and a procedure is just a construct called upon to perform instructions. + +Procedures can take arguments. Values are passed by copy. + +Procedures can be called in two ways: a `call` or a `jump`. A `call` will +perform the procedure before continuing with the current sequence of +instructions. A `jump` will perform the procedure **instead of** the +**current** sequence of instructions. + +Procedures can be called before their definition. The compiler will simply tell +you if the definition ends up being incompatible or missing. + +#### Computations +Computations are operations returning a value. Computations do not modify +anything, they simply return the result of an operation. + +A special type of computation, called *lambda function*, allows the creation of +a computation that will be performed only when called upon. Those can take +parameters. What's the use of something as advanced in a narrative scripting +language? It's convenient when you want to express something like a character's +name who depends on what the player figured out. Sure, you *could* make each +instance of the name feature a series of tests to find which value to use, or +you could just use a lambda function and just put +`(eval name_of_mysterious_character)` to get the right value. + +One last thing about lambda functions, which really are the only potentially +complicated thing about computations: these *are* computations, so you can use +them as such. They also cannot modify anything, as they only contain +computations and not instructions. + +#### Declarations and First Level Instructions +Declarations and First Level Instructions are a special type of instruction +which can only be done from outside a procedure. This distinction means that, +for example, you cannot define a procedure from within a procedure. + +#### Events +Sometimes, you might need to communicate something to the interpreter which +cannot be expressed in Fate. For example, you might want to say: "pause for 30 +seconds", or "play this music". This kind of thing has been considered to not +be generic enough to mandate every interpreter supports it. Instead, events are +used. + +Events are first declared, by being given a name and a list of types +corresponding to the parameters they take. Then, the `(event ...)` instruction +can be used to indicate that a certain event should occur. This will pause the +execution, and make the interpreter react to the event before continuing. + +The effect of an event is purely the interpreter's responsibility. It cannot be +described in Fate. Thus, you will need to refer to the interpreter you use to +see what events are available. + +#### Types +The basic `string`, `int`, `float`, and `bool` types are what one would expect. + +`rich_text` corresponds to text decorations, see the text effect sub-section. + +Two collection types are available: `(list [TYPE])` is a list of `[TYPE]` +elements, and `(set [COMPARABLE])` is a set of `[COMPARABLE]` elements. + +`(ptr [TYPE])` is an address to a value of type `[TYPE]`. + +Structures can be defined. They may contain other structures, but cannot be +recursive: only already defined types can be used. Pointers cannot be used to +resolve this conundrum in this version of Fate, but it may be added in the next +version (see [this GitHub +issue](https://github.com/nsensfel/tonkadur/issues/6)). + +#### Player Choices +Inputs from the players are limited to the `(player_choice ...)` instruction. +It presents the player with `rich text` options to choose from, which execute +an associated list of instructions if chosen. + +More complicated inputs, such as retrieving a `string` or an `int` from the +player require the definition and use of events. + diff --git a/content/fate_v1/aliases/default.md b/content/fate_v1/aliases/default.md new file mode 100644 index 0000000..9a6bd8a --- /dev/null +++ b/content/fate_v1/aliases/default.md @@ -0,0 +1,41 @@ +--- +title: Aliases +--- +Nearly all computations and instructions have aliases, making it easier to +write in the language when not used to it. This page provides most of them. As +a general rule, all underscores (`_`) are optional, all `declare` can be +replaced by `define` and `def`. + +* `abs`: `absolute`. +* `and`: `/\`. +* `>=`: `greater_equal_than`, `ge`. +* `/`: `divide`, `div`. +* `=`: `==`, `equals`, `eq`. +* `declare_alias_type`: `declare_sub_type`, `typedef`. +* `declare_structure_type`: `declare_structure`, `declare_dict_type`, + `declare_dict`. +* `declare_event_type`: `declare_event`. +* `declare_sequence`: `declare_seq`, `declare_procedure`, `declare_proc`. +* `ignore_error`: `ignore_warning`. +* `free`: `release`, `destroy`. +* `implies`: `=>`, `->`. +* `is_member`: `contains`, `has`. +* `=<`: `<=`, `lower_equal_than`, `le`. +* `<`: `lower_than`, `lt`. +* `-`: `minus` +* `min`: `minimum`. +* `max`: `maximum`. +* `eval`: `evaluate`. +* `%`: `mod`, `modulo`. +* `new`: `reserve`, `create`. +* `not`: `~`, `!`. +* `add_element`: `add`. +* `one_in`: `exactly_one_in`, `exactly_one`, `one`. +* `remove_at`: `remove_element_at`, `remove_elem_at`. +* `set`: `set_value`, `set_val`, `set_variable`, `set_var`. +* `var`: `variable`. +* `visit`: `call`, `call_sequence`, `call_procedure`, `call_seq`, `call_proc`, + `visit_sequence`, `visit_procedure`, `visit_seq`, `visit_proc`. +* `jump_to`: `continue_as`, `continue_to`, `continue_with`, `jump`, `go_to`, + `exec`. And you can suffix `_proc`, `_procedure`, `_seq`, or `_sequence` to + any of these. diff --git a/content/fate_v1/computations/_index.md b/content/fate_v1/computations/_index.md new file mode 100644 index 0000000..9312712 --- /dev/null +++ b/content/fate_v1/computations/_index.md @@ -0,0 +1,138 @@ +--- +title: Computations +--- +Computations are values. They may read from the memory, but do not modify it +(with a single exception). + +### TEXT +{{< fatecode >}}(text [C0 = COMPUTATION] ... [CN = COMPUTATION]){{< /fatecode >}} + +Returns a `text` node containing the text representation of `C0` ... `CN`. + +### VARIABLE | REFERENCE +{{< fatecode >}}(var {String}){{< /fatecode >}} + +Returns the value of the variable `{String}`, or a reference to it if +applicable. Structure members can be accessed by using `.` in `{String}`. + +### STRUCTURE FIELD ACCESS +{{< fatecode >}}{Structure Var Name}.{Field Name}{{< /fatecode >}} +{{< fatecode >}}(field [STRUCTURE VAR] {String}){{< /fatecode >}} + +Accesses the `{String}` field of the structure ``. Using `.` to +access fields is recommended over the use of this operator. + +### STRUCTURE FIELD VALUE +{{< fatecode >}}(get_field [STRUCTURE] {String}){{< /fatecode >}} +Returns the value of the `{String}` field of the structure `[STRUCTURE]`. + +### TEMPORARY VARIABLES +{{< fatecode >}}(let (({V0 = String} [C0 = COMPUTATION]) ... ({VN = String} [CN = COMPUTATION])) [R = COMPUTATION]){{< /fatecode >}} + +Defines a hierarchical level and local variables `V0` ... `VN` with values `C0` ... `CN`, and returns the value of `[R]`. + +### CAST +{{< fatecode >}}(cast [TYPE] ){{< /fatecode >}} + +Transforms `` into a value of type `[TYPE]`. Note that the variable +shorthand cannot be used for ``. The following type changes are +allowed: +* `[FLOAT]` to `[FLOAT]`, `[INT]`, and `[STRING]`. +* `[INT]` to `[FLOAT]`, `[INT]`, and `[STRING]`. +* `[BOOL]` to `[BOOL]` and `[STRING]`. +* `[STRING]` to `[BOOL]` (`true` and `false`), `[FLOAT]`, `[INT]`, and`[STRING]`. + +### RANDOM NUMBER +{{< fatecode >}}(rand [I0 = INT] [IN = INT]){{< /fatecode >}} + +Returns a random number between `I0` and `IN` (inclusive). + +## Basic Operators +### BOOL OPERATORS +{{< fatecode >}}(and [B0 = BOOL] ... [BN = BOOL]){{< /fatecode >}} + +Standard conjunction (minimum of 2 arguments). + +{{< fatecode >}}(or [B0 = BOOL] ... [BN = BOOL]){{< /fatecode >}} + +Standard disjunction (minimum of 2 arguments). + +{{< fatecode >}}(not [BOOL]){{< /fatecode >}} + +Standard negation. + +{{< fatecode >}}(implies [B0 = BOOL] [B1 = BOOL]){{< /fatecode >}} + +Standard implication. + + +{{< fatecode >}}(one_in [B0 = BOOL] ... [BN = BOOL]){{< /fatecode >}} + +true if, and only if, exactly one of the operands is true. + +### MATH OPERATORS +All operands must be of the same type, which is also the type returned by the +operation. + +{{< fatecode >}}(+ [N0 = NUMBER] ... [NN = NUMBER]){{< /fatecode >}} + +Standard addition (minimum of 2 arguments). + +{{< fatecode >}}(- [N0 = NUMBER] ... [NN = NUMBER]){{< /fatecode >}} + +Standard substraction (minimum of 2 arguments). + +{{< fatecode >}}(* [N0 = NUMBER] ... [NN = NUMBER]){{< /fatecode >}} + +Standard multiplication (minimum of 2 arguments). + +{{< fatecode >}}(/ [N0 = NUMBER] [N1 = NUMBER]){{< /fatecode >}} + +Standard division. Note that a division on integers is indeed a integer +division. + +{{< fatecode >}}(^ [N0 = NUMBER] [N1 = NUMBER]){{< /fatecode >}} + +Standard exponentiation. + +{{< fatecode >}}(% [I0 = INT] [I1 = INT]){{< /fatecode >}} + +Standard modulo operation. + +{{< fatecode >}}(min [N0 = NUMBER] ... [NN = NUMBER]){{< /fatecode >}} + +Lowest value among the operands. + +{{< fatecode >}}(max [N0 = NUMBER] ... [NN = NUMBER]){{< /fatecode >}} + +Highest value among the operands. + +{{< fatecode >}}(clamp [N0 = NUMBER] [N1 = NUMBER] [N2 = NUMBER]){{< /fatecode >}} + +Equivalent to `(min N0 (max N1 N2))`. + + +{{< fatecode >}}(abs [NUMBER]){{< /fatecode >}} + +Positive value of `[NUMBER]`. + +### COMPARISON OPERATORS +{{< fatecode >}}(= [C0 = COMPUTATION] ... [CN = COMPUTATION]){{< /fatecode >}} + +True if, and only if, all operands are equal. + +{{< fatecode >}}(< [C0 = COMPARABLE] [C1 = COMPARABLE]){{< /fatecode >}} + +True if, and only if, `C0` is strictly lower than `C1`. + +{{< fatecode >}}(=< [C0 = COMPARABLE] [C1 = COMPARABLE]){{< /fatecode >}} + +True if, and only if, `C0` is lower or equal to/than `C1`. + +{{< fatecode >}}(> [C0 = COMPARABLE] [C1 = COMPARABLE]){{< /fatecode >}} + +True if, and only if, `C0` is strictly higher than `C1`. + +{{< fatecode >}}(>= [C0 = COMPARABLE] [C1 = COMPARABLE]){{< /fatecode >}} + +True if, and only if, `C0` is higher or equal to/than `C1`. diff --git a/content/fate_v1/computations/collections/_index.md b/content/fate_v1/computations/collections/_index.md new file mode 100644 index 0000000..63c62cc --- /dev/null +++ b/content/fate_v1/computations/collections/_index.md @@ -0,0 +1,98 @@ +--- +title: Collections +--- +### ACCESS +{{< fatecode >}}(access [COLLECTION|COLLECTION PTR] [INT]){{< /fatecode >}} + +Returns the value of the `[INT]`th element in `[COLLECTION|COLLECTION PTR]`. + +### ACCESS CONSTANT INDEX +{{< fatecode >}}[(COLLECTION|COLLECTION PTR) VAR].{Integer}{{< /fatecode >}} + +Returns a variable corresponding to the `[INT]`th element in +`[(COLLECTION|COLLECTION PTR) VAR]`. + +### ACCESS POINTER +{{< fatecode >}}(access_pointer [(COLLECTION|COLLECTION PTR) VAR] [INT]){{< /fatecode >}} + +Returns a pointer to the `[INT]`th element in `[(COLLECTION|COLLECTION PTR) +VAR]`. + +### ADD ELEMENT AT +{{< fatecode >}}(add_element_at [INT] [COMPUTATION*] [LIST]){{< /fatecode >}} + +Returns a copy of `[LIST]` with `[COMPUTATION*]` added at index `[INT]`. Note +that `[COMPUTATION*]` does not allow use of the variable shorthand. + +### ADD ELEMENT +{{< fatecode >}}(add_element [COMPUTATION*] [COLLECTION]){{< /fatecode >}} + +Returns a copy of `[COLLECTION]` with `[COMPUTATION*]` added. If `[COLLECTION]` +is a `[LIST]`, then the element is added at the end of the list. +Note that `[COMPUTATION*]` does not allow use of the variable shorthand. + +### ADDING MEMBERS +{{< fatecode >}}(add_all_elements [C0 = COLLECTION] [C1 = COLLECTION]){{< /fatecode >}} + +Returns a copy of `[C1]`, with all the elements of `[C2]` added. If `[C1]` +is a `[LIST]`, the new members are added at the end of the list. + +### COUNT +{{< fatecode >}}(count [COMPUTATION*] [COLLECTION]){{< /fatecode >}} + +Returns the number of occurrences of `[COMPUTATION*]` in `[COLLECTION]`. +Note that `[COMPUTATION*]` does not allow use of the variable shorthand. + +### INDEX OF +{{< fatecode >}}(index_of [COMPUTATION] [COLLECTION]){{< /fatecode >}} + +Returns the index of the first occurrence of `[COMPUTATION]` in `[COLLECTION]`, +starting at 0. + +### IS MEMBER +{{< fatecode >}}(is_member [COMPUTATION] [COLLECTION]){{< /fatecode >}} + +Returns true if, and only if, `[COMPUTATION]` is in `[COLLECTION]`. + +### SIZE +{{< fatecode >}}(size [COLLECTION]){{< /fatecode >}} + +Returns the size (`[INT]`) of `[COLLECTION]`. + +### IS EMPTY +{{< fatecode >}}(is_empty [COLLECTION]){{< /fatecode >}} + +Returns true if, and only if `[COLLECTION]` is empty. + +### FILTER ELEMENTS +{{< fatecode >}}(filter [LAMBDA BOOL (X)] [X COLLECTION]){{< /fatecode >}} +{{< fatecode >}}(filter [LAMBDA BOOL (X Y0 ... YN)] [X COLLECTION] [Y0 COMPUTATION*] ... [YN COMPUTATION*]){{< /fatecode >}} + +Returns a copy of `[X COLLECTION]` in which only the elements for which +`` returns `true` remain. If the lambda function needs extra +parameters, use the second syntax, which adds those parameters at the end of the +`(filter ...)` call. Note that the variable shorthand cannot be used for these +extra parameters. + +### FILTER ELEMENTS (INDEXED) +{{< fatecode >}}(indexed_filter [LAMBDA BOOL (INT X)] [X COLLECTION]){{< /fatecode >}} +{{< fatecode >}}(indexed_filter [LAMBDA BOOL (INT X Y0 ... YN)] [X COLLECTION] [Y0 COMPUTATION*] ... [YN COMPUTATION*]){{< /fatecode >}} + +Returns a copy of `[INT X COLLECTION]` in which only the elements for which +`` (with `INT` being the element's index) returns `true` +remain. If the lambda function needs extra parameters, use the second syntax, +which adds those parameters at the end of the `(indexed_filter ...)` call. Note +that the variable shorthand cannot be used for these extra parameters. + +### FOLD OVER COLLECTION +{{< fatecode >}}(foldl [LAMBDA X (X Y)] [X COMPUTATION*] [Y COLLECTION]){{< /fatecode >}} +{{< fatecode >}}(foldl [LAMBDA X (X Y Z0 ... ZN)] [X COMPUTATION*] [Y COLLECTION] [Z0 COMPUTATION*] ... [ZN COMPUTATION*]){{< /fatecode >}} +{{< fatecode >}}(foldr [LAMBDA X (X Y)> [X COMPUTATION*] [Y COLLECTION]){{< /fatecode >}} +{{< fatecode >}}(foldr [LAMBDA X (X Y Z0 ... ZN)] [X COMPUTATION*] [Y COLLECTION] [Z0 COMPUTATION*] ... [ZN COMPUTATION*]){{< /fatecode >}} + +Returns the result of iterating `` over `[Y COLLECTION]`, with +`[X COMPUTATION*]` being the initial value. The direction of the iteration is +by ascending index order when using `foldl`, and the opposite order when using +`foldr`. Extra parameters for the lambda function can be passed as extra +parameters of the call. Note that the variable shorthand cannot be used for +those extra parameters, nor for the initial value. diff --git a/content/fate_v1/computations/conditionals/_index.md b/content/fate_v1/computations/conditionals/_index.md new file mode 100644 index 0000000..2953489 --- /dev/null +++ b/content/fate_v1/computations/conditionals/_index.md @@ -0,0 +1,54 @@ +--- +title: Conditionals +--- +This page presents the computation operators that allow a choice depending on +some condition. All possible returned values must be of the same type. + +### IF-ELSE +{{< fatecode >}}(if_else [BOOL] [C0 = COMPUTATION] [C1 = COMPUTATION]){{< /fatecode >}} + +Returns `C0` is `[BOOL]` yields true, `C1` otherwise. + +### COND +{{< fatecode >}}(cond ([B0 = BOOL] [C0 = COMPUTATION]) ... ([BN = BOOL] [CN = COMPUTATION])){{< /fatecode >}} + +Returns ``, such that `` is the first to hold true. If there is not such +`Bi`, returns `[CN]`. + +### SWITCH +{{< fatecode >}}(switch [T = COMPUTATION] ([V0 = COMPUTATION] [C0 = COMPUTATION]) ... ([VN = BOOL] [CN = COMPUTATION]) [D = COMPUTATION]){{< /fatecode >}}a + +Returns the first `Ci` such that `Vi` is equal to `T`. If there is not such +`Vi`, returns `[D]`. + +## Examples +{{< fatecode >}}(cond + ((false) (false)) + ((false) (false)) + ((true) + (cond + ((false) (false)) + ((true) (not (is_member 3 test_list))) + ((true) (false)) + ) + ) +) +{{< /fatecode >}} + +{{< fatecode >}}(switch 3 + (0 (false)) + (1 (false)) + (3 (true)) + (2 (false)) + (false) +) +{{< /fatecode >}} + +{{< fatecode >}}(if_else (true) + (if_else (false) + (assert (false) FAILED: instruction ifelse E) + (set test_var (true)) + ) + (assert (false) FAILED: instruction ifelse F) +) +{{< /fatecode >}} diff --git a/content/fate_v1/computations/cons/_index.md b/content/fate_v1/computations/cons/_index.md new file mode 100644 index 0000000..5ece27b --- /dev/null +++ b/content/fate_v1/computations/cons/_index.md @@ -0,0 +1,22 @@ +--- +title: Cons +--- +Fate features a *construct* computation, making it possible to create an +anonymous structure by creating pairs of computations. Unlike collections, these +pairs do not have to be made of computations of the same type. + +### PAIRING +{{< fatecode >}}(cons [C0 = COMPUTATION*] [C1 = COMPUTATION*]){{< /fatecode >}} + +Returns the value corresponding to a pair made of `[C0]` and `[C1]`. Note that +the variable shorthand cannot be used for either parameter. + +### RETRIEVING THE FIRST ITEM +{{< fatecode >}}(car [CONS]){{< /fatecode >}} + +Returns the value corresponding to the first item in the `[CONS]` pair. + +### RETRIEVING THE SECOND ITEM +{{< fatecode >}}(cdr [CONS]){{< /fatecode >}} + +Returns the value corresponding to the second item in the `[CONS]` pair. diff --git a/content/fate_v1/computations/lambda_functions/_index.md b/content/fate_v1/computations/lambda_functions/_index.md new file mode 100644 index 0000000..35485a3 --- /dev/null +++ b/content/fate_v1/computations/lambda_functions/_index.md @@ -0,0 +1,30 @@ +--- +title: Lambda Functions +--- +Lambda functions are values that correspond to a computation not yet performed. +These can take arguments. Defining a lambda function returns the value that +corresponds to the function itself, the `eval` computation must be used to +obtain the value that the function computes. + +### DEFINITION +{{< fatecode >}}(lambda (([T0 = TYPE] {S0 = String}) ... ([TN = TYPE] {SN = String})) [COMPUTATION]){{< /fatecode >}} + +Returns a lambda function taking `S0` ... `SN` of types `T0` ... `TN` as +arguments and evaluating to `[COMPUTATION]`. + +### EVALUATION +{{< fatecode >}}(eval [REFERENCE] [C0 = COMPUTATION] ... [CN = COMPUTATION]){{< /fatecode >}} + +Returns the result of evaluating the lambda function at `[REFERENCE]` given the +parameters `C0` ... `CN`. + +## Examples +{{< fatecode >}}(lambda ( (int i) ) + (+ (var i) 1) +) +{{< /fatecode >}} + +{{< fatecode >}}(lambda ( (int i) ) + (* (eval int_to_int (var i)) 2) +) +{{< /fatecode >}} diff --git a/content/fate_v1/computations/references/_index.md b/content/fate_v1/computations/references/_index.md new file mode 100644 index 0000000..ba5716a --- /dev/null +++ b/content/fate_v1/computations/references/_index.md @@ -0,0 +1,18 @@ +--- +title: References +--- +### VALUE ACCESS +{{< fatecode >}}(at [ADDRESS]){{< /fatecode >}} + +Returns the variable at `[ADDRESS]`. + +### ALLOCATION +{{< fatecode >}}(new [TYPE]){{< /fatecode >}} + +Returns the address of a new variable of type `[TYPE]`. Don't forget to call +`free` on it once you're done. + +### ADDRESS +{{< fatecode >}}(ptr [COMPUTATION VARIABLE]){{< /fatecode >}} + +Returns the address of `[COMPUTATION VARIABLE]`. diff --git a/content/fate_v1/computations/rich_text/_index.md b/content/fate_v1/computations/rich_text/_index.md new file mode 100644 index 0000000..7351a5c --- /dev/null +++ b/content/fate_v1/computations/rich_text/_index.md @@ -0,0 +1,11 @@ +--- +title: Rich Text +--- +### RICH TEXT +{{< fatecode >}}(rich_text [TEXT]){{< /fatecode >}} + +### ADD TEXT EFFECT +{{< fatecode >}}(add_text_effect ({String} [P0 = COMPUTATION] ... [PN = COMPUTATION]) [RICH TEXT]){{< /fatecode >}} + +### NEW LINE +{{< fatecode >}}(newline){{< /fatecode >}} diff --git a/content/fate_v1/declarations/_index.md b/content/fate_v1/declarations/_index.md new file mode 100644 index 0000000..013d404 --- /dev/null +++ b/content/fate_v1/declarations/_index.md @@ -0,0 +1,5 @@ +--- +title: "Declarations" +menuTitle: "Declarations" +chapter: true +--- diff --git a/content/fate_v1/declarations/events/_index.md b/content/fate_v1/declarations/events/_index.md new file mode 100644 index 0000000..5493cd7 --- /dev/null +++ b/content/fate_v1/declarations/events/_index.md @@ -0,0 +1,18 @@ +--- +title: Events +--- +Events are how a Fate narrative can communicate to the interpreter that +something which cannot be expressed in Fate needs to be performed. The execution +is paused until the event is resolved by the interpreter. To avoid mistakes, any +event type must be declared before use. + +#### EVENT +{{< fatecode >}}(declare_event_type {string} [C0 = TYPE] ... [CN = TYPE]){{< /fatecode >}} +**Effect:** An event with the name `{string}` and taking parameters of types +`[C0]`, ..., `[CN]` can be used. + +## Examples +* `(declare_event_type user_string_input rich_text (ptr string))` +* `(declare_event_type wait int)` +* `(declare_event_type set_background_to string)` +* `(declare_event_type rumble)` diff --git a/content/fate_v1/declarations/files/_index.md b/content/fate_v1/declarations/files/_index.md new file mode 100644 index 0000000..05c6836 --- /dev/null +++ b/content/fate_v1/declarations/files/_index.md @@ -0,0 +1,48 @@ +--- +title: File Management +--- +Fate narratives have one main file, but this file can use the contents of other +files, and these files in turn can also use the contents of other files. Three +issues arise then: some things must be declared before they are used, nothing +should be declared multiple times, and where are the files? + +The absolute path to each file is computed in order to detect whether it has +already been loaded. The given paths are expected to be relative. They are then +resolved by attempting access from the following, in order: the current file's +directory; the include directories passed as parameter to the executable; the +calling executable's directory. + +Dependency cycles will raise compiling errors. + +The first "instruction" on each file must be `(fate_version 1)`. Values cannot +be placed before either, obviously. + +{{< fatecode >}}(require {string}){{< /fatecode >}} + +**Effect:** If the file at `{string}` has not yet been loaded, load it. + +{{< fatecode >}}(include {string}){{< /fatecode >}} + +**Effect:** Load the `{string}`, even if it has already been loaded. + +#### Examples +* `(require bonus_sequence.fate)` +* `(include types/plant.fate)` +* `(require ../guild/merchants.fate)` +* `(include ../what/../are/../you/../doing/../there.fate)` +* `(require oh/../oh/../oh/../this_is_fine.fate)` + +Example of Fate file: +{{< fatecode >}};;; A story of great importance +(fate_version 1) + +(require include/events.fate) +(require include/text_effects.fate) + +(require scenes/the_holiday_forest.fate) + +`Twas a long time ago... Longer now that it seems... In a place that can't be +referenced here because I'm pretty sure the transcript is copyrighted. + +(jump_to not_the_tree_door_i_would_have_chosen) +{{< /fatecode >}} diff --git a/content/fate_v1/declarations/sequences/_index.md b/content/fate_v1/declarations/sequences/_index.md new file mode 100644 index 0000000..8bdb30e --- /dev/null +++ b/content/fate_v1/declarations/sequences/_index.md @@ -0,0 +1,113 @@ +--- +title: Sequences and Procedures +--- +Sequences and procedures are the same thing. These are named lists of +instructions and values, which can be called upon at any point where +instructions can be used. They can take parameters. + +Procedures do not return values. It is however possible to emulate something +similar, by passing a pointer as a parameter and storing a "return" value into +it. + +These are also intended to be used to describe scenes. + +The execution of a sequence can be terminated by using the `(done)` +instruction. The execution of the narrative can be terminated by using the +`(end)` instruction. + +Any value not part of an instruction will simply be displayed when it is +reached during the procedure's execution. + +Sequences can be used before their definition, the compiler will raise an error +if the use ends up being incorrect. + +Execution of a sequence can be started in two ways: `(call sequence_name)` will +execute the sequence then continue with the execution of the current +instruction list; `(jump_to sequence_name)` will replace the current +instruction list by the execution of the sequence. If one were to ignore +variables, the `(jump_to sequence_name)` instruction is similar to performing +`(call sequence_name) (done)`. + +{{< fatecode >}}(define_sequence {String} (([C0 = TYPE] {V0 = String}) ... ([CN = TYPE] {VN = String})) ... ){{< /fatecode >}} +**Effect:** Defines the sequence `{String}`, with variables `V0` ... `VN` of types `C0` ...`CN` as parameters, and instructions `I0` ... `IM` as content. + +**Acceptable Aliases:** `declare_sequence`, `def_seq`, `define_procedure`, `declare_procedure`, `def_proc`. + +#### Examples +{{< fatecode >}}(define_sequence in_your_room () + (ifelse + (is_member visited_your_room progress) + (text_effect narrator + You room is still a mess. You don't have time to clean things up, + though. + ) + (text_effect narrator + You room is a mess. You recall having been through every drawer while + preparing your bag yesterday. While still unclear on how you are + supposed to pack all the necessary things for what promises to be at + least a year-long journey inside a small backpack, you cannot avoid + but wasting more time contemplating the piles of items that didn't + make the cut. + ) + ) + (add visited_your_room progress) + (player_choice + ( + ( Look for healing items ) + (jump_to look_for_healing_items) + ) + ( + ( No time! Let's go adventuring! ) + (jump_to leave_your_room) + ) + ) +) +{{< /fatecode >}} + +{{< fatecode >}}(define_sequence index_of_loop + ( + ((ptr int) result_holder) + ((list int) collection) + (int target) + ) + (local int collection_size) + (local int i) + + (set collection_size (size collection)) + + (for (set i 0) (< (var i) (var collection_size)) (set i (+ (var i) 1)) + (if (= (access collection (var i)) (var target)) + ( + (set (at result_holder) (var i)) + (done) + ) + ) + ) + (set (at result_holder) -1) +) +{{< /fatecode >}} + + +{{< fatecode >}}(define_sequence index_of_jump + ( + ((ptr int) result_holder) + ((list int) collection) + (int target) + (int i) + (int collection_size) + ) + (ifelse (= (var i) (var collection_size)) + (set (at result_holder) -1) + (ifelse (= (access collection (var i)) (var target)) + (set (at result_holder) (var i)) + (jump index_of_jump + (var result_holder) + (var collection) + (var target) + (+ (var i) 1) + (var collection_size) + ) + ) + ) +) +{{< /fatecode >}} diff --git a/content/fate_v1/declarations/text_effects/_index.md b/content/fate_v1/declarations/text_effects/_index.md new file mode 100644 index 0000000..3bf7a08 --- /dev/null +++ b/content/fate_v1/declarations/text_effects/_index.md @@ -0,0 +1,31 @@ +--- +title: Text Effects +--- +Text effects are attributes that can be given to rich text elements. The +effects themselves can take parameters. To avoid errors that would be difficult +to detect, Tonkadur expects text effects to be declared before being used. +Note that multiple text effects can be applied to the same rich text elements, +so there is no need to create text effects that combine other text effects. + +Two text effects cannot have the same name, even if their parameter types +differ. + +Because text effects are handled by the interpreter, it is recommended to +overlay their use by lambda functions. This way, each interpreter can simply +expose its available text effects in a file, and the definition of the lambda +functions can thus be changed according to which interpreter is used without +having to go through the whole document. Furthermore, the name of text effects +exposed by the interpreter might not match the name that would make the most +sense to use within the narrative. + +### TEXT EFFECT +{{< fatecode >}}(declare_text_effect {Identifier} [T0 = TYPE] ... [TN = TYPE]){{< /fatecode >}} +Declares the text effect `{Identifier}`, with parameters of type `[T0]` ... +`[TN]`. + +## Examples +* `(declare_text_effect bold)` +* `(declare_text_effect speaker string)` +* `(declare_text_effect color int int int)` +* `(declare_text_effect font string)` +* `(declare_text_effect speaker_emotion string int)` diff --git a/content/fate_v1/declarations/types/_index.md b/content/fate_v1/declarations/types/_index.md new file mode 100644 index 0000000..9a3677f --- /dev/null +++ b/content/fate_v1/declarations/types/_index.md @@ -0,0 +1,61 @@ +--- +title: Types +--- +Fate is a strongly typed language. + +## Base Types +There are a few base types already defined: + +* `int`: an integer, which is a number *without* fractional component (e.g. `-3`, `0`, `3`). +* `float`: a number *with* a fractional component (e.g. `-3.14`, `0.0`, `3.9931`). +* `bool`: a Boolean (i.e. `(true)` or `(false)`). +* `string`: a list of characters, not including newlines (e.g. `bob`, + `something else`, `日本のもの`, or `الاشياء العربية`). This cannot include + computations: only hardcoded strings. + +* `text`: a list of computations, interpreted as text, which may have + attributes. + +Pointers are also available: +* `(ptr [TYPE])`: a pointer to a memory element of type `[TYPE]` (e.g. `(ptr int)`, `(ptr (ptr string))`). +If you are not familiar with pointers, a pointer is a value corresponding to an address containing a memory element. +Accessing the value of the pointer yields the address, accessing the value pointed to by the value of the pointer yields the memory element. +Pointers to pointers can be made, in which case that memory element is also an address to yet another memory element. +Pointers still have to point to a definite type. Unlike in C, you cannot create a pointer to an unspecified type. + +Two collection types are available: +* `(list [TYPE])` +* `(set [COMPARABLE TYPE])` + +Lambda computations are available: +* `(lambda ( ... ))` is a type corresponding + to a lambda function returning a value of type `r` and taking parameters of + types `a0` ... `an`. + +### Common Type Groupings +* `[NUMBER]` corresponds to `int`, `float`. +* `[COLLECTION]` corresponds to `(list [TYPE])` and `(set [COMPARABLE TYPE])`. +* `[PRIMITIVE]` `int`, `float`, `bool`, `string`, `rich_text`. +* `[COMPARABLE]` corresponds to `int`, `float`, `bool`, `string`, `rich_text`, + and `(ptr [TYPE])`. This indicates types for which operators such as `<` are + defined. + +## Defining Your Own Types + +### Aliasing +{{< fatecode >}}(declare_alias_type [TYPE] {String}){{< /fatecode >}}. +**Effect:** Declares the type `{String}`. If `[TYPE]` is not a `[PRIMITIVE]`, + `[TYPE]` and `{String}` are now two equivalent types. If `[TYPE]` is a + `[PRIMITIVE]`, `{String}` is a subtype of `[TYPE]`. + +### Structures +{{< fatecode >}}(declare_structure_type {String} ( {f0 = String}) ... ( {fn = String})){{< /fatecode >}}. + +## Examples + +{{< fatecode >}}(define_structure_type player + (creature creature) + ((list (ptr item)) inventory) + (int money) +) +{{< /fatecode >}} diff --git a/content/fate_v1/declarations/variables/_index.md b/content/fate_v1/declarations/variables/_index.md new file mode 100644 index 0000000..153135c --- /dev/null +++ b/content/fate_v1/declarations/variables/_index.md @@ -0,0 +1,72 @@ +--- +title: Variables +--- +Variables are what hold values. Each variable has a type, which cannot be +changed. + +There are two variable scopes: local and global. A global variable can only be +declared outside of any sequence. A local variable can also be declared within a +sequence. Local variables can only be accessed within their context. In effect, +a local variable declared outside of a sequence cannot be accessed in any +sequence, and local variables cannot be accessed within lambda functions. Local +variables can override global variables within their context. + +The sole exception to accessing a local variable outside its context is done +through the use of pointers. Local variables live as long as the context +that declared them does. Accessed one through a pointer past that point is +likely to result in a runtime error and is, in any case, not to be done. + +Each sequence and lambda function defines its own context and thus does not +share local variable with the others. Instructions which themselves contain +instructions define hierarchies. Local variables defined within a hierarchical +level can be accessed in that level and inner level, but are not shared with +outer levels. For example: + +{{< fatecode >}}(if_else (var my_test) + ( + (local int my_var) ;; my_var (a) + (set my_var 32) + ) + ( + ;; my_var (a) is not defined here. + (local int my_var) ;; my_var (b) + (set my_var 42) + (if (var my_other_test) + ( + (local int my_other_var) + ;; Both my_other_var and my_var (b) are defined here + ) + ) + ;; only my_var (b) is defined here + ) +) +{{< /fatecode >}} + +Generic instruction lists do not generate a new level of hierarchy: + +{{< fatecode >}}( + (local int my_var) +) +;; my_var is still defined. +{{< /fatecode >}} + +### LOCAL VARIABLE +{{< fatecode >}}(local [TYPE] {Identifier}){{< /fatecode >}} +Declares the local variable `{Identifier}` of type `[TYPE]`. + +### GLOBAL VARIABLE +{{< fatecode >}}(global [TYPE] {Identifier}){{< /fatecode >}} +Declares the global variable `{Identifier}` of type `[TYPE]`. + +## Example +{{< fatecode >}}(local string name_of_dog) + +(global (ptr int) index_of_result) + +;; Here is an amusing use of variables: +(global int something_unexpected) +(local int something_unexpected) +;; something_unexpected will not be the same variable within processes (which +;; will use the global variable) and outside of them (which will use the local +;; variable). For code readability reasons, I do not recommend doing this. +{{< /fatecode >}} diff --git a/content/fate_v1/extensions/_index.md b/content/fate_v1/extensions/_index.md new file mode 100644 index 0000000..01f7127 --- /dev/null +++ b/content/fate_v1/extensions/_index.md @@ -0,0 +1,4 @@ +--- +title: Extensions +--- +This page not available yet, sorry. diff --git a/content/fate_v1/instructions/_index.md b/content/fate_v1/instructions/_index.md new file mode 100644 index 0000000..51066d1 --- /dev/null +++ b/content/fate_v1/instructions/_index.md @@ -0,0 +1,48 @@ +--- +title: Instructions +--- +Instructions do not return values, but modify the memory in some way or +interact with the interpreter. Computations are valid instructions, and will be +automatically converted into `[TEXT]` to be displayed. + +### ASSERT +{{< fatecode >}}(assert [BOOL] [TEXT]){{< /fatecode >}} + +Raises the exception `[TEXT]` to the interpreter if `[BOOL]` yields +false. + +### DONE +{{< fatecode >}}(done){{< /fatecode >}} + +Completes the execution of the current sequence. + +### END +{{< fatecode >}}(end){{< /fatecode >}} + +Completes the execution of the script. + +### SET VALUE +{{< fatecode >}}(set [REFERENCE] [COMPUTATION]){{< /fatecode >}} + +Gives the value `[COMPUTATION]` to `[REFERENCE]`. + +### VISIT SEQUENCE +{{< fatecode >}}(visit {String} [C0 = COMPUTATION] ... [CN = COMPUTATION]){{< /fatecode >}} + +Visits the sequence named `{String}`, with `C0` ... `CN` as arguments. That +sequence does not need to already have been defined. Visiting a sequence means +that the execution of the current sequence continues once the visited sequence +has completed. + +### JUMP TO SEQUENCE +{{< fatecode >}}(jump_to {String} [C0 = COMPUTATION] ... [CN = COMPUTATION]){{< /fatecode >}} + +Jumps to the sequence named `{String}`, with `C0` ... `CN` as arguments. That +sequence does not need to already have been defined. Jumping to a sequence means +that the execution of the current sequence is replaced by that of the target +sequence. + +### INSTRUCTION LIST +{{< fatecode >}}([C0 = INSTRUCTION] ... [CN = INSTRUCTION]){{< /fatecode >}} + +Instruction corresponding to the execution of `[C0]` ... `[CN]` in order. diff --git a/content/fate_v1/instructions/collections/_index.md b/content/fate_v1/instructions/collections/_index.md new file mode 100644 index 0000000..8d646ee --- /dev/null +++ b/content/fate_v1/instructions/collections/_index.md @@ -0,0 +1,72 @@ +--- +title: Collections +--- +Fate supports two types of collections: `[LIST]`, which are basic, unordered +lists; and `[SET]`, which are ordered lists, but only useable with +`[COMPARABLE]` elements. + +### ADDING A MEMBER +{{< fatecode >}}(add_element! [COMPUTATION*] [COLLECTION VAR]){{< /fatecode >}} + +Adds `[COMPUTATION*]` to `[COLLECTION VAR]`. If `[COLLECTION VAR]` is a +`[LIST]`, the new member is added at the end of the list. Note that +`[COMPUTATION*]` does not support use of the variable shorthand. + +### ADDING A MEMBER AT INDEX +{{< fatecode >}}(add_element_at! [INT] [COMPUTATION*] [LIST VAR]){{< /fatecode >}} + +Adds `[COMPUTATION*]` to `[LIST VAR]` at index `[INT]`. If `[INT]` is less than +0, the element is added at the start of the list, and if `[INT]` is greater or +equal to the size of the list, the element is added at the end of the list. Note +that `[COMPUTATION*]` does not support use of the variable shorthand. + +### ADDING MEMBERS +{{< fatecode >}}(add_all_elements! [COLLECTION] [COLLECTION VAR]){{< /fatecode >}} + +Adds all the elements of `[COLLECTION]` to `[COLLECTION VAR]`. If +`[COLLECTION VAR]` is a `[LIST]`, the new members are added at the end of the +list. + +### EMPTYING COLLECTIONS +{{< fatecode >}}(clear [COLLECTION]){{< /fatecode >}} + +Removes all members of `[COLLECTION]`. + +### REMOVING MEMBER +{{< fatecode >}}(remove [COMPUTATION] [COLLECTION]){{< /fatecode >}} + +Removes the first member of `[COLLECTION]` equal to `[COMPUTATION]`. + +### REMOVING MEMBERS +{{< fatecode >}}(remove_all [COMPUTATION] [COLLECTION]){{< /fatecode >}} + +Removes all instances of `[COMPUTATION]` from `[COLLECTION]`. + +### REMOVING AT INDEX +{{< fatecode >}}(remove_at [INT] [COLLECTION]){{< /fatecode >}} + +Removes the element of `[COLLECTION]` at `[INT]`. + +### REVERSING LISTS +{{< fatecode >}}(reverse [LIST]){{< /fatecode >}} + +Reverses the order of the members of `[LIST]`. + +### FILTER ELEMENTS +{{< fatecode >}}(filter! [X COLLECTION VAR]){{< /fatecode >}} +{{< fatecode >}}(filter! [X COLLECTION VAR] [Y0 COMPUTATION*] ... [YN COMPUTATION*]){{< /fatecode >}} +Modifies `[X COLLECTION VAR]` so that only the elements for which +`` returns `true` remain. If the lambda function needs extra +parameters, use the second syntax, which adds those parameters at the end of the +`(filter! ...)` call. Note that the variable shorthand cannot be used for these +extra parameters. + +### FILTER ELEMENTS (INDEXED) +{{< fatecode >}}(indexed_filter! [X COLLECTION VAR]){{< /fatecode >}} +{{< fatecode >}}(indexed_filter! [X COLLECTION VAR] [Y0 COMPUTATION*] ... [YN COMPUTATION*]){{< /fatecode >}} +Modifies `[X COLLECTION VAR]` so that only the elements for which +`` (with the `INT` being the element's index) returns +`true` remain. If the lambda function needs extra parameters, use the second +syntax, which adds those parameters at the end of the `(indexed_filter! ...)` +call. Note that the variable shorthand cannot be used for these extra +parameters. diff --git a/content/fate_v1/instructions/conditionals/_index.md b/content/fate_v1/instructions/conditionals/_index.md new file mode 100644 index 0000000..e129b1b --- /dev/null +++ b/content/fate_v1/instructions/conditionals/_index.md @@ -0,0 +1,27 @@ +--- +title: Conditionals +--- +Allow the control of whether to execute instructions or not according to a +computation. Every conditional branch defines its hierarchy level, from the +local variables' point of view. + +### IF +{{< fatecode >}}(if [BOOL] [INSTRUCTION]){{< /fatecode >}} + +Executes `[INSTRUCTION]` if, and only if, `[BOOL]` yields true. + +### IF-ELSE +{{< fatecode >}}(if_else [BOOL] ){{< /fatecode >}} + +Executes `` if `[BOOL]` yields true, but `` if it does not. + +### COND +{{< fatecode >}}(cond ([C0 = BOOL] [I0 = INSTRUCTION]) ... ([CN = BOOL] [IN = INSTRUCTION])){{< /fatecode >}} + +Executes `[II]`, such that `[CI]` is the first listed boolean to yield true. + +### SWITCH +{{< fatecode >}}(switch [T = COMPUTATION] ([C0 = COMPUTATION] [I0 = INSTRUCTION]) ... ([CN = COMPUTATION] [IN = INSTRUCTION]) [DEFAULT = INSTRUCTION]){{< /fatecode >}} + +Executes `[II]`, such that `[CI]` is the first listed computation to be equal +to `[T]`. Executes `[DEFAULT]` if there is no such `[CI]`. diff --git a/content/fate_v1/instructions/loops/_index.md b/content/fate_v1/instructions/loops/_index.md new file mode 100644 index 0000000..03c0f3b --- /dev/null +++ b/content/fate_v1/instructions/loops/_index.md @@ -0,0 +1,34 @@ +--- +title: Loops +--- +Allow the repetition of a group of instructions according to a computation. +Every loop body defines its hierarchy level, from the local variables' point of +view. + +### WHILE +{{< fatecode >}}(while [BOOL] [I0 = INSTRUCTION] ... [IM = INSTRUCTION]){{< /fatecode >}} + +Executes `[I0]` ... `[IM]` if, and as long as, `[BOOL]` yields true. + +### DO WHILE +{{< fatecode >}}(do_while [BOOL] [I0 = INSTRUCTION] ... [IM = INSTRUCTION]){{< /fatecode >}} + +Executes `[I0]` ... `[IM]`, and does so again as long as, `[BOOL]` yields +true. + +### FOR +{{< fatecode >}}(for
 [BOOL]  [I0 = INSTRUCTION] ... [IM = INSTRUCTION]){{< /fatecode >}}
+
+Executes `
`, then, if and as long as `[BOOL]` yields true, executes
+`[I0]` ... `[IM]` followed by ``.
+
+### FOR EACH
+{{< fatecode >}}(foreach [COLLECTION] {String} [I0 = INSTRUCTION] ... [IM = INSTRUCTION]){{< /fatecode >}}
+
+Executes `[I0]` ... `[IM]` for each member of `[COLLECTION]`, in order. The current
+member is stored in a new local variable named `{String}`.
+
+### BREAK
+{{< fatecode >}}(break){{< /fatecode >}}
+
+Exits the current loop.
diff --git a/content/fate_v1/instructions/player_choices/_index.md b/content/fate_v1/instructions/player_choices/_index.md
new file mode 100644
index 0000000..a0dba20
--- /dev/null
+++ b/content/fate_v1/instructions/player_choices/_index.md
@@ -0,0 +1,31 @@
+---
+title: Player Choices
+---
+Player choices are the main way to interact with the user, by presenting them
+with a list of `[RICH TEXT]` choices, and executing a list of instructions
+associated to the choice they have made.
+
+### CHOICE OPTION
+{{< fatecode >}}([TEXT] [I0 = INSTRUCTION] ... [IN = INSTRUCTION]){{< /fatecode >}}
+
+Adds a choice showing `[RICH TEXT]` to the user, and executing `[I0]` ... `[IN]`
+if chosen.
+
+### CHOICE PROMPT
+{{< fatecode >}}(player_choice [C0 = CHOICE] ... [C1 = CHOICE]){{< /fatecode >}}
+
+Prompts the user to choose between `C0` ... `C1`. `[CHOICE]`. `[CHOICE]` is
+either an option as shown above, a [conditional](../conditionals), or a
+`for_each` (see [loops](../loops)) with `[CHOICE]` instead of `[INSTRUCTION]`.
+
+### INTEGER PROMPT
+{{< fatecode >}}(prompt_integer [INT REFERENCE] [MIN = INT] [MAX = INT] [TEXT]){{< /fatecode >}}
+
+Prompts the user for an integer between `[MIN]` and `[MAX]` by displaying the
+message `[TEXT]`. The result is stored in `[INT REFERENCE]`.
+
+### STRING PROMPT
+{{< fatecode >}}(prompt_string [STRING REFERENCE] [MIN = INT] [MAX = INT] [TEXT]){{< /fatecode >}}
+
+Prompts the user for a string of size between `[MIN]` and `[MAX]` by displaying
+the message `[TEXT]`. The result is stored in `[STRING REFERENCE]`.
diff --git a/content/fate_v1/instructions/references/_index.md b/content/fate_v1/instructions/references/_index.md
new file mode 100644
index 0000000..9beb132
--- /dev/null
+++ b/content/fate_v1/instructions/references/_index.md
@@ -0,0 +1,7 @@
+---
+title: References
+---
+### DE-ALLOCATION
+{{< fatecode >}}(free [POINTER]){{< /fatecode >}}
+
+Removes the memory element at `[POINTER]` from the memory.
diff --git a/content/images/discord.svg b/content/images/discord.svg
new file mode 100644
index 0000000..3d4cb8e
--- /dev/null
+++ b/content/images/discord.svg
@@ -0,0 +1,70 @@
+
+
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+  
+  
+  
+  
+
diff --git a/content/images/github.svg b/content/images/github.svg
new file mode 100644
index 0000000..081fab1
--- /dev/null
+++ b/content/images/github.svg
@@ -0,0 +1,71 @@
+
+
+
+
+  
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+    
+      
+    
+  
+
diff --git a/content/images/reddit.svg b/content/images/reddit.svg
new file mode 100644
index 0000000..dd4332c
--- /dev/null
+++ b/content/images/reddit.svg
@@ -0,0 +1,63 @@
+
+
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+  
+  
+
diff --git a/content/images/tonkadur_logo_black.svg b/content/images/tonkadur_logo_black.svg
new file mode 100644
index 0000000..3745257
--- /dev/null
+++ b/content/images/tonkadur_logo_black.svg
@@ -0,0 +1,97 @@
+
+
+
+
+  
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+        
+      onkadur
+      T
+    
+  
+
diff --git a/content/images/tonkadur_logo_black_as_path.svg b/content/images/tonkadur_logo_black_as_path.svg
new file mode 100644
index 0000000..e09be31
--- /dev/null
+++ b/content/images/tonkadur_logo_black_as_path.svg
@@ -0,0 +1,117 @@
+
+
+
+
+  
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+        
+      
+        
+        
+        
+        
+        
+        
+        
+      
+      
+        
+      
+    
+  
+
diff --git a/content/images/tonkadur_logo_white.svg b/content/images/tonkadur_logo_white.svg
new file mode 100644
index 0000000..f17e213
--- /dev/null
+++ b/content/images/tonkadur_logo_white.svg
@@ -0,0 +1,98 @@
+
+
+
+
+  
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+        
+      onkadur
+      T
+    
+  
+
diff --git a/content/images/tonkadur_logo_white_as_path.svg b/content/images/tonkadur_logo_white_as_path.svg
new file mode 100644
index 0000000..04c3039
--- /dev/null
+++ b/content/images/tonkadur_logo_white_as_path.svg
@@ -0,0 +1,118 @@
+
+
+
+
+  
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+        
+      
+        
+        
+        
+        
+        
+        
+        
+      
+      
+        
+      
+    
+  
+
diff --git a/content/learn/01.start/_index.md b/content/learn/01.start/_index.md
new file mode 100644
index 0000000..1741772
--- /dev/null
+++ b/content/learn/01.start/_index.md
@@ -0,0 +1,43 @@
+---
+menuTitle: "Starting"
+title: "Writing a story in Fate"
+weight: 1
+---
+* Create a folder for your story. This is not mandatory, but helps keeping
+  track of the created files.
+* Open up your favorite text editor, and write in `main.fate`:
+
+{{< fatecode >}}(fate_version 1)
+Once upon a time, starting a story with these words wasn't considered
+a cliche. Starting in a tavern might also not be seen as very
+original.  Having the main character be an street orphan, raised by
+some mysterious sage all to end up as a mercenary with an uncommonly
+strong sense of honor probably isn't going to lead to any praises for
+novelty either. Maybe you should drink to that.
+(newline)
+Or maybe you shouldn't. This isn't your first mug. Not your second
+either.  Drinking to forget that you are a stereotypical hero isn't
+going to solve anything. Worse, the alcoholic trait is part of the
+image.
+(newline)
+As you contemplate your own pointless description, your gaze leaves
+what turns out to be an already empty glass in your hand and finds the
+barman.
+(end)
+{{< /fatecode >}}
+
+This is a very minimal story in Fate. Let's look at what is it made of:
+
+* The `(fate_version 1)` line must be at the top of every file. It is just
+  there to inform the compiler of the used language version. It has no effect on
+  the output.
+* The text is mostly printed out as-is: newlines, tabs and spaces are
+  considered to be a single space. No matter how many of them follow. This
+  makes indentation have no effect on the output text. Furthermore such
+  characters preceding text are ignored.
+* `(newline)` inserts a newline in the output.
+* `(end)` signals the end of the story. It needs to be there.
+
+With this, you know how to make a static, purely textual story in Fate. Our poor
+protagonist needs a refill, so we need to introduce
+[Player Choices](/learn/choices) next.
diff --git a/content/learn/02.choices/_index.md b/content/learn/02.choices/_index.md
new file mode 100644
index 0000000..a5ba4d7
--- /dev/null
+++ b/content/learn/02.choices/_index.md
@@ -0,0 +1,71 @@
+---
+menuTitle: "Player Choices"
+title: "Asking the Player"
+weight: 2
+---
+In [the previous step](/learn/start), we saw a minimal text story written in Fate.
+Now, we need to interact with the player.
+
+There are three ways to interact with the player:
+* Asking them to choose between options.
+* Prompting them for an integer.
+* Prompting them for a string.
+
+The last two require notions that haven't been introduced yet, so let's give
+the player a simple choice instead:
+
+**main.fate:**
+{{< fatecode >}}(fate_version 1)
+Once upon a time, starting a story with these words wasn't considered
+a cliche. Starting in a tavern might also not be seen as very
+original.  Having the main character be an street orphan, raised by
+some mysterious sage all to end up as a mercenary with an uncommonly
+strong sense of honor probably isn't going to lead to any praises for
+novelty either. Maybe you should drink to that.
+(newline)
+Or maybe you shouldn't. This isn't your first mug. Not your second
+either.  Drinking to forget that you are a stereotypical hero isn't
+going to solve anything. Worse, the alcoholic trait is part of the
+image.
+(newline)
+As you contemplate your own pointless description, your gaze leaves
+what turns out to be an already empty glass in your hand and finds the
+barman.
+
+(player_choice
+   (
+      ( Ask the barman for a refill )
+      Staring straight at the barman, you raise your glass and
+      proclaim:
+      (newline)
+      "This soon-to-be world savior needs more booze!"
+   )
+   (
+      ( Fall asleep )
+      Deciding to break away from the expected storyline, you promptly
+      fall asleep.
+   )
+   (
+      ( Resolve whether P=NP )
+      Sadly, the output for this choice would require some concepts
+      that haven't been introduced yet, so let's never mention it
+      again.
+   )
+)
+
+(end)
+{{< /fatecode >}}
+
+In this version, the player is able to interact with the story: once
+`player_choice` is reached, the output stops and the player is presented with
+options. Here, three choices are available. A choice is made of a label and a
+list of instructions. The label is the text which is displayed to player (for
+example `Fall asleep`). The list of instructions is what will be performed if
+that choice is selected. Putting text where instructions are expected simply
+outputs the text. In fact, all the content of the previous step was
+instructions. Once a choice has been made and the instructions have been
+performed, the story continues past the `player_choice` construct.
+
+But something critical is missing. Indeed, how can you get a refill without
+money? Just how much money does our hero have? Will it be enough to quench that
+terrible thirst? Let's [introduce variables](/learn/variables).
diff --git a/content/learn/03.variables/default.md b/content/learn/03.variables/default.md
new file mode 100644
index 0000000..6c2cdf4
--- /dev/null
+++ b/content/learn/03.variables/default.md
@@ -0,0 +1,90 @@
+---
+menuTitle: Variables
+title: "Adding Variables"
+weight: 3
+---
+In [the previous step](/learn/start), we introduced player choices. Dynamic
+content is here, but it is not going far without variables.
+
+Fate is a strongly typed language, meaning that variables **must** be assigned
+a precise type and cannot deviate from it.
+
+Variables have to be declared before being used. Let us keep things simple for
+now, and declare variables using the `local` instruction. The alternative is
+`global`. The difference between the two being about access from other
+contexts, something that is introduced in the next chapter.
+
+We are trying to add a variable that corresponds to money. An `int` is thus
+appropriate.
+
+**main.fate:**
+
+         (fate_version 1)
+
+         (local int hero_money)
+         (local int price_of_booze)
+
+         (set hero_money 42)
+         (set price_of_booze 12)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               Staring straight at the barman, you raise your glass and
+               proclaim:
+               (newline)
+               "This soon-to-be world savior needs more booze!"
+               (newline)
+               The barman's lack of reaction is disappointing, but seeing the
+               beer being poured does help improve the mood.
+               (newline)
+               Satisfied, you hand the barman (var price_of_booze) copper coins.
+               (set hero_money
+                  (- (var hero_money) (var price_of_booze))
+               )
+            )
+            (
+               ( Fall asleep )
+               Deciding to break away from the expected storyline, you promptly
+               fall asleep.
+               (newline)
+               ...
+               (newline)
+               Upon waking up, your hard-trained reflexes inform you that
+               someone stole all your money.
+               (set hero_money 0)
+            )
+         )
+
+         (end)
+
+* `(local int hero_money)` declares an `int` variable with the name
+  `hero_money`.
+* `(set hero_money 42)` sets the value of the `hero_money` variable to 42.
+* `(var price_of_booze)` returns the current value of the `price_of_booze`
+  variable.
+* `(- (var hero_money) (var price_of_booze))` returns the result of subtracting
+   the value of `price_of_booze` to the value of `hero_money`. All operators
+   use a prefixed form.
+
+`local`, `set`, `player_choice`, and `end`, are instructions. Instructions do
+not return any value. Thus, they do not add to the printed text.
+
+Having to continue the story from within the `player_choice` structure is going
+to get tedious very fast. Let's [introduce sequences](/learn/sequences).
diff --git a/content/learn/04.sequences/default.md b/content/learn/04.sequences/default.md
new file mode 100644
index 0000000..cecbff8
--- /dev/null
+++ b/content/learn/04.sequences/default.md
@@ -0,0 +1,123 @@
+---
+menuTitle: Sequences
+title: "Introducing Sequences"
+weight: 4
+---
+In [the previous step](/learn/variables), we introduced variables. The story is
+starting to have branches, but writing them from within a `player_choice`
+construct is awkward. To resolve this, *sequences* are introduced.
+
+Sequences are named lists of instructions. They do *not* have to be defined
+before being used, but the definition must be found at some point. Since
+instructions do not return any value, neither do sequences.
+
+Sequences define their own context, meaning that `local` variables from outside
+the sequence cannot be accessed and, conversely, `local` variables from the
+sequence cannot be accessed outside of it.
+
+Entering a sequence can be done in two ways:
+* By visiting, in which case the sequence is executed and the story continues.
+* By jumping, in which case the sequence replaces the current story execution.
+
+When in doubt, prefer visiting to jumping, as the latter is mainly intended for
+optimization purposes.
+
+Sequences can be entered again from themselves, making recursion possible.
+
+Sequences can take parameters.
+
+
+**main.fate:**
+
+         (fate_version 1)
+
+         (global int hero_money)
+
+         (set hero_money 42)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero_money) (var cost))
+            )
+         )
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+         )
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero_money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+         (end)
+
+* `(visit get_a_refill)` makes a visit to the sequence `get_a_refill`. Since
+   that sequence does not take parameters, none need to be provided.
+* `(jump_to fall_asleep)` stops the execution of the main instruction list and
+  proceeds to using the sequence `fall_asleep` instead. Here again, no
+  arguments are expected by `fall_asleep`. Notice how the `fall_asleep` sequence
+  ends with `(end)`: since there is no return from it, the original `(end)`
+  would not be reached and thus a new one is required to end the story.
+* `(visit pay (var price_of_booze))` makes a visit to the `pay` sequence, which
+  does require a parameter.
+* `(global int hero_money)` has replaced `(local int hero_money)`, because that
+  variable needs to be accessible from within the sequences.
+* `(local int price_of_booze)` has been moved to the `get_a_refill` sequence,
+  as there is no reason to have it be defined across the whole story.
+* The `pay` sequence cannot directly access the `price_of_booze` variable, as
+  it is `local` and from another sequence, hence the use of a parameter to
+  communicate the correct amount.
+
+With this, the `player_choice` have become much more readable. However, the file
+itself is starting to become hard to read. The solution is then to [split the
+content into multiple files](/learn/files).
diff --git a/content/learn/05.files/default.md b/content/learn/05.files/default.md
new file mode 100644
index 0000000..da78206
--- /dev/null
+++ b/content/learn/05.files/default.md
@@ -0,0 +1,113 @@
+---
+menuTitle: Files
+title: "Splitting into Multiple Files"
+weight: 5
+---
+In [the previous step](/learn/sequences), we introduced sequences. This made
+branching the story easier, but having many sequences in a file made the flow
+difficult to read. To resolve this, the content is going to be split into
+multiple files.
+
+By using `(require path_to_file)`, the content of `path_to_file` is explored,
+but only if that file has not already been explored.
+
+* Create a new file, `data.fate`, with the following content:
+
+         (fate_version 1)
+
+         (global int hero_money)
+
+         (set hero_money 42)
+
+* Create a new file, `actions.fate`, with the following content:
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero_money) (var cost))
+            )
+         )
+
+* Create a new file, `get_a_refill.fate`, with the following content:
+
+         (fate_version 1)
+
+         (require actions.fate)
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+         )
+
+* Create a new file, `falling_asleep.fate`, with the following content:
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero_money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+**main.fate:**
+
+         (fate_version 1)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (require get_a_refill.fate)
+         (require falling_asleep.fate)
+
+         (end)
+
+With this, the story is much more easy to follow. Let's continue by looking
+at [the actually-not-scary-at-all pointers](/learn/pointers).
diff --git a/content/learn/06.pointers/default.md b/content/learn/06.pointers/default.md
new file mode 100644
index 0000000..a4108bb
--- /dev/null
+++ b/content/learn/06.pointers/default.md
@@ -0,0 +1,171 @@
+---
+menuTitle: Pointers
+title: "Addressing Pointers"
+weight: 6
+---
+In [the previous step](/learn/files), we split the story into multiple files
+to make it more readable. Now, we'll see pointers, because they were needed for
+two features that were glossed over in what was presented before:
+* Using sequences as imperative procedures.
+* Prompting the user for content.
+
+A pointer is a value that indicates a location in memory where some
+data is stored. This can be used as a value that will tell some instruction
+where to put data.
+
+Pointers have types. For example, `(ptr int)` is the type corresponding to
+pointers to `int` data.
+
+To compute a pointer to a variable `v`, simply write `(ptr v)`.
+
+Given a pointer `p`, the variable being pointed to can be referred to using
+`(at p)`.
+
+**data.fate:**
+
+         (fate_version 1)
+
+         (global int hero_money)
+         (global string hero_name)
+
+         (set hero_money 42)
+
+**get_a_refill.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+         (require actions.fate)
+
+         (define_sequence lower_price_of_booze
+            (
+               ((ptr int) price_pointer)
+               (int decrease)
+            )
+            Great! The price of booze just lowered from (at price_pointer)
+            (set (at price_pointer)
+               (-
+                  (at price_pointer)
+                  (var decrease)
+               )
+            )
+            to (at price_pointer)!
+         )
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+            (newline)
+            The barman sighs, then asks:
+            (prompt_string (ptr hero_name) 2 64 What is your name, then, hero?)
+            (var hero_name)?
+            (newline)
+            The barman looks surprised.
+            (newline)
+            (visit lower_price_of_booze (ptr price_of_booze) 4)
+            (newline)
+            "I have heard of you, (var hero_name)," the barman exclaims, "I have
+            a quest for you!"
+            (newline)
+            It's your turn to sigh.
+            (newline)
+            The barman hands you a bag, and says:
+            (newline)
+            "Take this pre-payment and head to the smithy."
+            (newline)
+         )
+
+* `(prompt_string (ptr hero_name) 2 64 What is your name, then, hero?)` prompts
+  the player with `What is your name, then, hero?` and expects a string input
+  with a size between `2` and `64` characters. This input is stored at `(ptr
+  hero_name)`, which means that `hero_name` takes that value.
+* The `lower_price_of_booze` sequence shows how pointers can be used to modify
+  variables outside of a sequence's range.
+* `(var price_of_booze)` is equivalent to `(at (ptr price_of_booze))`.
+
+Our hero, who'll obviously end up being the lost heir of some royal family,
+should already have good equipment. It would be useful to have a character
+sheet, so [let's create one](/learn/structures).
+
+----
+
+## Unchanged Files
+**actions.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero_money) (var cost))
+            )
+         )
+
+**falling_asleep.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero_money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+**main.fate:**
+
+         (fate_version 1)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (require get_a_refill.fate)
+         (require falling_asleep.fate)
+
+         (end)
diff --git a/content/learn/07.structures/default.md b/content/learn/07.structures/default.md
new file mode 100644
index 0000000..a87af08
--- /dev/null
+++ b/content/learn/07.structures/default.md
@@ -0,0 +1,201 @@
+---
+menuTitle: Structures
+title: "Structuring the Data"
+weight: 7
+---
+In [the previous step](/learn/pointers), we added pointers, which made it
+possible to change data from other contexts/sequences. As we add more and more
+data, it becomes clear that some structuring is needed.
+
+Structures are types. Types must be declared before being used. This also
+prevents recursive types.
+
+The fields of a structure are initialized at the same time as the structure.
+
+Fields of a structure can be accessed in two ways: using the
+`struct_var.field_name` notation, or the `(field struct_var field_name)` one.
+If `struct_ptr` is a pointer to a structure, `struct_ptr.field_name` will also
+work.
+
+To set the value of a structure's fields, one can use the `set` instruction or,
+to set multiple fields at once, the `set_fields!` one. Note that omitting the
+`!` at the end of `set_fields` is also valid Fate, but performs a computation
+instead of a instruction: the structure is not modified, but a copy with the
+modifications performed is returned.
+
+**data.fate:**
+
+         (fate_version 1)
+
+         (declare_structure weapon
+            (text name)
+            (int attack)
+            (int precision)
+         )
+
+         (declare_structure armor
+            (text name)
+            (int defense)
+         )
+
+         (declare_structure character
+            (string name)
+            (int money)
+            (weapon weapon)
+            (armor armor)
+         )
+
+         (global character hero)
+
+         (set_fields! hero.weapon
+            (name (text "Legendary" sword))
+            (attack 3)
+            (precision 50)
+         )
+
+         (set_fields! hero.armor
+            (name (text "Refined" attire))
+            (defense 1)
+         )
+
+         (set hero.money 42)
+
+* `(text "Refined" attire)` generates a `text` value containing a textual
+  representation of the values passed as parameter. `"Refined" attire` is, by
+  itself, a `string`, and thus incompatible with the `text` field without a
+  conversion taking place.
+* Because `hero_money` and `hero_name` have been removed in favor of a
+  structure, they should be replaced in the other files by `hero.money` and
+  `hero.name`.
+
+With this, it is time for our hero to get some proper gear, [let's see what
+collection is available at the smithy](/learn/collections).
+
+----
+
+## (Mostly) Unchanged Files
+
+**get_a_refill.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+         (require actions.fate)
+
+         (define_sequence lower_price_of_booze
+            (
+               ((ptr int) price_pointer)
+               (int decrease)
+            )
+            Great! The price of booze just lowered from (at price_pointer)
+            (set (at price_pointer)
+               (-
+                  (at price_pointer)
+                  (var decrease)
+               )
+            )
+            to (at price_pointer)!
+         )
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+            (newline)
+            The barman sighs, then asks:
+            (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+            (var hero.name)?
+            (newline)
+            The barman looks surprised.
+            (newline)
+            (visit lower_price_of_booze (ptr price_of_booze) 4)
+            (newline)
+            "I have heard of you, (var hero.name)," the barman exclaims, "I have
+            a quest for you!"
+            (newline)
+            It's your turn to sigh.
+            (newline)
+            The barman hands you a bag, and says:
+            (newline)
+            "Take this pre-payment and head to the smithy."
+            (newline)
+         )
+
+**actions.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero.money) (var cost))
+            )
+         )
+
+**falling_asleep.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero.money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+**main.fate:**
+
+         (fate_version 1)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (require get_a_refill.fate)
+         (require falling_asleep.fate)
+
+         (end)
diff --git a/content/learn/08.collections/default.md b/content/learn/08.collections/default.md
new file mode 100644
index 0000000..b4bae98
--- /dev/null
+++ b/content/learn/08.collections/default.md
@@ -0,0 +1,408 @@
+---
+menuTitle: Collections
+title: "Collections"
+weight: 8
+---
+In [the previous step](/learn/structures), we added structures, which made
+having some amounts of data more manageable. Still, what about when we need to
+have hundreds of elements? Collections are here to manage that.
+
+Fate has three types of collections:
+* Sets, which only contain a single instance of each element, can only be used
+  to store elements of one of the comparable types (`string`, `text`, `int`,
+  `float`, `bool`) and pointers. The elements are automatically sorted in
+  ascending order.
+* Lists, which can be made for one of any type.
+* Cons, which are pairs of two elements of any types.
+
+There are [quite a few computations](/fate_v1/computations/collections) and
+[instructions](/fate_v1/instructions/collections) available to handle
+collections.
+
+Let's add a new file, `smithy_inventory.fate`:
+         (fate_version 1)
+
+         (require data.fate)
+
+         (global (list (cons weapon int)) smithy_weapons)
+         (global (list (cons weapon int)) smithy_armors)
+
+         (add!
+            (cons
+               (set_fields (default weapon)
+                  (name (text An Iron Rod))
+                  (attack 10)
+                  (precision 70)
+               )
+               176
+            )
+            smithy_weapons
+         )
+         (add!
+            (cons
+               (set_fields (default weapon)
+                  (name (text A Magnificient Brick))
+                  (attack 6)
+                  (precision 90)
+               )
+               110
+            )
+            smithy_weapons
+         )
+
+         (add!
+            (cons
+               (set_fields (default armor)
+                  (name (text A raincoat?!))
+                  (defense 7)
+               )
+               160
+            )
+            smithy_armors
+         )
+         (add!
+            (cons
+               (set_fields (default armor)
+                  (name (text A nice cape))
+                  (defense 3)
+               )
+               50
+            )
+            smithy_armors
+         )
+
+We'll also need the actual smithy scene, so let's put in another file,
+`smithy.fate`.
+
+**NOTE:** Don't worry if it looks awful at the moment, the next chapters are
+going to introduce a lot of things to make it *much*, *much* easier to write.
+
+**smithy.fate:**
+
+         (fate_version 1)
+
+         (require smithy_inventory.fate)
+
+         (define_sequence visit_smithy ()
+            ;; This thing is going to show up every time, which isn't great.
+            As you approach the smithy, you notice that no one's there. All the
+            wares are out for selling. It's almost as if this story didn't need
+            more examples of lengthy dialogues.
+            (newline)
+            ;; We'll want to start here the next time we enter this sequence.
+            You have (var hero.money) coins.
+            (newline)
+            What will you look at?
+            (player_choice
+               (
+                  ( Let's see the weapons )
+                  (jump_to see_weapons)
+               )
+               (
+                  ( Let's see the armors )
+                  (jump_to see_armors)
+               )
+               (
+                  ( Nothing, let's go back to the bar )
+               )
+            )
+         )
+
+         (define_sequence see_weapons ()
+            ;; We'll soon replace this mess with something way better.
+
+            (local text weapon_a_label)
+            (local text weapon_b_label)
+
+            (visit get_weapon_label
+               (access smithy_weapons 0)
+               (ptr weapon_a_label)
+            )
+            (visit get_weapon_label
+               (access smithy_weapons 1)
+               (ptr weapon_b_label)
+            )
+
+            (player_choice
+               (
+                  ( (var weapon_a_label) )
+                  (visit buy_weapon (access smithy_weapons 0))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( (var weapon_a_label) )
+                  (visit buy_weapon (access smithy_weapons 1))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( Nevermind )
+                  (jump_to visit_smithy)
+               )
+            )
+         )
+
+         (define_sequence see_armors ()
+            ;; We'll soon replace this mess with something way better.
+
+            (local text armor_a_label)
+            (local text armor_b_label)
+
+            (visit get_armor_label
+               (access smithy_armors 0)
+               (ptr armor_a_label)
+            )
+            (visit get_armor_label
+               (access smithy_armors 1)
+               (ptr armor_b_label)
+            )
+
+            (player_choice
+               (
+                  ( (var armor_a_label) )
+                  (visit buy_armor (access smithy_armors 0))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( (var armor_a_label) )
+                  (visit buy_armor (access smithy_armors 1))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( Nevermind )
+                  (jump_to visit_smithy)
+               )
+            )
+         )
+
+         ;; A terrible way to get labels
+         (define_sequence get_weapon_label
+            (
+               ((cons weapon int) weapon_offer)
+               ((ptr text) label)
+            )
+            (local weapon weapon)
+            (local int price)
+
+            (set weapon (car weapon_offer))
+            (set price (cdr weapon_offer))
+
+            (set (at label)
+               (
+                   Buy "(var weapon.name)" \(attack: (var weapon.attack),
+                   precision: (var weapon.precision)\) for (var price) coins.
+               )
+            )
+         )
+
+         ;; A terrible way to get labels
+         (define_sequence get_armor_label
+            (
+               ((cons armor int) armor_offer)
+               ((ptr text) label)
+            )
+            (local armor armor)
+            (local int price)
+
+            (set armor (car armor_offer))
+            (set price (cdr armor_offer))
+
+            (set (at label)
+               (
+                   Buy "(var armor.name)" \(defense: (var armor.defense)\),
+                   for (var price) coins.
+               )
+            )
+         )
+
+         (define_sequence buy_weapon ( ((cons weapon int) weapon) )
+            ;; We can't even deny a sell yet...
+            (set hero.weapon (car weapon))
+            Equipped (var hero.weapon.name).
+            (newline)
+         )
+
+         (define_sequence buy_armor ( ((cons armor int) armor) )
+            ;; We can't even deny a sell yet...
+            (set hero.armor (car armor))
+            Equipped (var hero.armor.name).
+            (newline)
+         )
+
+* `(list (cons weapon int))` indicates a list of `weapon` and `int` pairs.
+* `(add! something smithy_weapons)` adds `something` to the `smithy_weapons`
+  collection. Without the `!`, this would be a computation returning a copy of
+  `smithy_weapons` with the added weapon, but no modification of the
+  `smithy_weapons` collection itself would occur.
+* `(cons something something_else)` creates a pair with these two elements.
+* `(car weapon_offer)` returns the first element of the `weapon_offer` pair.
+* `(cdr weapon_offer)` returns the second element of the `weapon_offer` pair.
+
+Overall, the `smithy.fate` file is a mess. Let's start cleaning it up. We'll
+use loops, conditionals, and lambda functions to make it much cleaner. Let's
+start with the least expected one: [lambda functions](/learn/lambdas).
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+
+         (fate_version 1)
+
+         (declare_structure weapon
+            (text name)
+            (int attack)
+            (int precision)
+         )
+
+         (declare_structure armor
+            (text name)
+            (int defense)
+         )
+
+         (declare_structure character
+            (string name)
+            (int money)
+            (weapon weapon)
+            (armor armor)
+         )
+
+         (global character hero)
+
+         (set_fields! hero.weapon
+            (name (text "Legendary" sword))
+            (attack 3)
+            (precision 50)
+         )
+
+         (set_fields! hero.armor
+            (name (text "Refined" attire))
+            (defense 1)
+         )
+
+         (set hero.money 42)
+
+**get_a_refill.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+         (require actions.fate)
+
+         (define_sequence lower_price_of_booze
+            (
+               ((ptr int) price_pointer)
+               (int decrease)
+            )
+            Great! The price of booze just lowered from (at price_pointer)
+            (set (at price_pointer)
+               (-
+                  (at price_pointer)
+                  (var decrease)
+               )
+            )
+            to (at price_pointer)!
+         )
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+            (newline)
+            The barman sighs, then asks:
+            (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+            (var hero.name)?
+            (newline)
+            The barman looks surprised.
+            (newline)
+            (visit lower_price_of_booze (ptr price_of_booze) 4)
+            (newline)
+            "I have heard of you, (var hero.name)," the barman exclaims, "I have
+            a quest for you!"
+            (newline)
+            It's your turn to sigh.
+            (newline)
+            The barman hands you a bag, and says:
+            (newline)
+            "Take this pre-payment and head to the smithy."
+            (newline)
+         )
+
+**actions.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero.money) (var cost))
+            )
+         )
+
+**falling_asleep.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero.money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+**main.fate:**
+
+         (fate_version 1)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (require get_a_refill.fate)
+         (require falling_asleep.fate)
+
+         (end)
diff --git a/content/learn/09.lambdas/default.md b/content/learn/09.lambdas/default.md
new file mode 100644
index 0000000..a2e6c24
--- /dev/null
+++ b/content/learn/09.lambdas/default.md
@@ -0,0 +1,366 @@
+---
+menuTitle: Lambda Functions
+title: "Lambda Functions"
+weight: 9
+---
+In [the previous step](/learn/collections), we added collections, which made
+having large amounts of data possible. We also ended up with a really ugly file,
+`smithy.fate`, which had way too much code to be readable. We'll start by
+removing the label generation sequences. Instead, we'll use lambda functions.
+
+A lambda function is computation description that can be stored for later use.
+It can also take parameters. Since it's a computation, it cannot alter the
+memory by itself, nor can it contain any instructions.
+
+**smithy.fate:**
+
+         (fate_version 1)
+
+         (require smithy_inventory.fate)
+
+         (global (lambda text ((cons weapon int))) get_weapon_offer_label)
+         (global (lambda text ((cons armor int))) get_armor_offer_label)
+
+         (set get_weapon_offer_label
+            (lambda
+               ( ((cons weapon int) offer) )
+               (let
+                  (
+                     (weapon (car offer))
+                     (price (cdr offer))
+                  )
+                  (text
+                     Buy "(var weapon.name)" \(attack: (var weapon.attack),
+                     precision: (var weapon.precision)\) for (var price) coins.
+                  )
+               )
+            )
+         )
+
+         (set get_armor_offer_label
+            (lambda
+               ( ((cons armor int) offer) )
+               (let
+                  (
+                     (armor (car offer))
+                     (price (cdr offer))
+                  )
+                  (text
+                     Buy "(var armor.name)" \(defense: (var armor.defense)\),
+                     for (var price) coins.
+                  )
+               )
+            )
+         )
+
+         (define_sequence visit_smithy ()
+            ;; This thing is going to show up every time, which isn't great.
+            As you approach the smithy, you notice that no one's there. All the
+            wares are out for selling. It's almost as if this story didn't need
+            more examples of lengthy dialogues.
+            (newline)
+            ;; We'll want to start here the next time we enter this sequence.
+            You have (var hero.money) coins.
+            (newline)
+            What will you look at?
+            (player_choice
+               (
+                  ( Let's see the weapons )
+                  (jump_to see_weapons)
+               )
+               (
+                  ( Let's see the armors )
+                  (jump_to see_armors)
+               )
+               (
+                  ( Nothing, let's go back to the bar )
+               )
+            )
+         )
+
+         (define_sequence see_weapons ()
+            ;; Still can be improved.
+            (player_choice
+               (
+                  ( (eval get_weapon_offer_label (access smithy_weapons 0)) )
+                  (visit buy_weapon (access smithy_weapons 0))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( (eval get_weapon_offer_label (access smithy_weapons 1)) )
+                  (visit buy_weapon (access smithy_weapons 1))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( Nevermind )
+                  (jump_to visit_smithy)
+               )
+            )
+         )
+
+         (define_sequence see_armors ()
+            ;; Still can be improved.
+            (player_choice
+               (
+                  ( (eval get_armor_offer_label (access smithy_armors 0)) )
+                  (visit buy_armor (access smithy_armors 0))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( (eval get_armor_offer_label (access smithy_armors 1)) )
+                  (visit buy_armor (access smithy_armors 1))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( Nevermind )
+                  (jump_to visit_smithy)
+               )
+            )
+         )
+
+         (define_sequence buy_weapon ( ((cons weapon int) weapon) )
+            ;; We can't even deny a sell yet...
+            (set hero.weapon (car weapon))
+            Equipped (var hero.weapon.name).
+            (newline)
+         )
+
+         (define_sequence buy_armor ( ((cons armor int) armor) )
+            ;; We can't even deny a sell yet...
+            (set hero.armor (car armor))
+            Equipped (var hero.armor.name).
+            (newline)
+         )
+
+* `(lambda return_type (param_type0 param_type1 ...))` is the type corresponding
+  to a lambda function returning a value of type `return_type` and taking as
+  parameters values of types `param_type0`, `param_type1`, ...
+* `(eval variable_name param0 param1 ...)` computes and returns the result of
+  the lambda function stored in `variable_name` by giving it as parameter the
+  values `param0`, `param1`, ...
+* `(let ( (name0 val0) ... ) computation)` computes and returns `computation` as
+  if `name0` was a defined variable of value `val0`.
+
+Lambda functions are *very* useful. They can be used to manipulate lists in many
+ways. They can also be used to abstract away some complicated computation.
+
+This was a first step toward cleaning up `smithy.fate`. Next, we'll use
+[conditions](/learn/conditions) to improve things further.
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+
+         (fate_version 1)
+
+         (declare_structure weapon
+            (text name)
+            (int attack)
+            (int precision)
+         )
+
+         (declare_structure armor
+            (text name)
+            (int defense)
+         )
+
+         (declare_structure character
+            (string name)
+            (int money)
+            (weapon weapon)
+            (armor armor)
+         )
+
+         (global character hero)
+
+         (set_fields! hero.weapon
+            (name (text "Legendary" sword))
+            (attack 3)
+            (precision 50)
+         )
+
+         (set_fields! hero.armor
+            (name (text "Refined" attire))
+            (defense 1)
+         )
+
+         (set hero.money 42)
+
+**get_a_refill.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+         (require actions.fate)
+
+         (define_sequence lower_price_of_booze
+            (
+               ((ptr int) price_pointer)
+               (int decrease)
+            )
+            Great! The price of booze just lowered from (at price_pointer)
+            (set (at price_pointer)
+               (-
+                  (at price_pointer)
+                  (var decrease)
+               )
+            )
+            to (at price_pointer)!
+         )
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+            (newline)
+            The barman sighs, then asks:
+            (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+            (var hero.name)?
+            (newline)
+            The barman looks surprised.
+            (newline)
+            (visit lower_price_of_booze (ptr price_of_booze) 4)
+            (newline)
+            "I have heard of you, (var hero.name)," the barman exclaims, "I have
+            a quest for you!"
+            (newline)
+            It's your turn to sigh.
+            (newline)
+            The barman hands you a bag, and says:
+            (newline)
+            "Take this pre-payment and head to the smithy."
+            (newline)
+         )
+
+**actions.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero.money) (var cost))
+            )
+         )
+
+**falling_asleep.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero.money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+**main.fate:**
+
+         (fate_version 1)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (require get_a_refill.fate)
+         (require falling_asleep.fate)
+
+         (end)
+
+**smithy_inventory.fate:**
+         (fate_version 1)
+
+         (require data.fate)
+
+         (global (list (cons weapon int)) smithy_weapons)
+         (global (list (cons weapon int)) smithy_armors)
+
+         (add!
+            (cons
+               (set_fields (default weapon)
+                  (name (text An Iron Rod))
+                  (attack 10)
+                  (precision 70)
+               )
+               176
+            )
+            smithy_weapons
+         )
+         (add!
+            (cons
+               (set_fields (default weapon)
+                  (name (text A Magnificient Brick))
+                  (attack 6)
+                  (precision 90)
+               )
+               110
+            )
+            smithy_weapons
+         )
+
+         (add!
+            (cons
+               (set_fields (default armor)
+                  (name (text A raincoat?!))
+                  (defense 7)
+               )
+               160
+            )
+            smithy_armors
+         )
+         (add!
+            (cons
+               (set_fields (default armor)
+                  (name (text A nice cape))
+                  (defense 3)
+               )
+               50
+            )
+            smithy_armors
+         )
diff --git a/content/learn/10.conditions/default.md b/content/learn/10.conditions/default.md
new file mode 100644
index 0000000..8a4f339
--- /dev/null
+++ b/content/learn/10.conditions/default.md
@@ -0,0 +1,402 @@
+---
+menuTitle: Conditionals
+title: "Expressing Conditions"
+weight: 10
+---
+In [the previous step](/learn/lambdas), we added lambda functions, which made
+it possible to store computation descriptions for later use. The `smithy.fate`
+is still a mess, and we couldn't handle the hero not having enough money for a
+purchase. Let's add conditions to our story.
+
+Fate has quite a few conditional constructs. `if_else`, `switch`, and `cond`
+can be used both as instruction and computation. `if` is only for instructions.
+* `if_else` selects between two options according to a given `bool` value.
+* `switch` has a list of options, each of which is associated to a value.
+   It selects the first listed option whose value is equal to the targeted
+   value. When used as a computation, a default option with no associated value
+   must be provided.
+* `cond` has a list of options, each of which is associated to a `bool`
+  computation. The first listed option for which the computation yields `(true)`
+  is chosen. In the case of a computation, the last option is chosen even as a
+  default value. With instructions, if no option has its condition satisfied,
+  nothing happens.
+* `if` is only for instructions, and applies its content only if the condition
+  is verified.
+
+All of these can be used within a `player_choice` to control what options are
+available.
+
+**smithy.fate:**
+
+         (fate_version 1)
+
+         (require smithy_inventory.fate)
+
+         (global (lambda text ((cons weapon int))) get_weapon_offer_label)
+         (global (lambda text ((cons armor int))) get_armor_offer_label)
+
+         (set get_weapon_offer_label
+            (lambda
+               ( ((cons weapon int) offer) )
+               (let
+                  (
+                     (weapon (car offer))
+                     (price (cdr offer))
+                  )
+                  (text
+                     Buy "(var weapon.name)" \(attack: (var weapon.attack),
+                     precision: (var weapon.precision)\) for (var price) coins.
+                  )
+               )
+            )
+         )
+
+         (set get_armor_offer_label
+            (lambda
+               ( ((cons armor int) offer) )
+               (let
+                  (
+                     (armor (car offer))
+                     (price (cdr offer))
+                  )
+                  (text
+                     Buy "(var armor.name)" \(defense: (var armor.defense)\),
+                     for (var price) coins.
+                  )
+               )
+            )
+         )
+
+         (global bool has_visited_smithy)
+
+         (set has_visited_smithy (false))
+
+         (define_sequence visit_smithy ()
+            (if (not (var has_visited_smithy))
+               As you approach the smithy, you notice that no one's there. All
+               the wares are out for selling. It's almost as if this story
+               didn't need more examples of lengthy dialogues.
+               (newline)
+               (set has_visited_smithy (true))
+            )
+            You have (var hero.money) coins.
+            (newline)
+            What will you look at?
+            (player_choice
+               (
+                  ( Let's see the weapons )
+                  (jump_to see_weapons)
+               )
+               (
+                  ( Let's see the armors )
+                  (jump_to see_armors)
+               )
+               (
+                  ( Nothing, let's go back to the bar )
+               )
+            )
+         )
+
+         (define_sequence see_weapons ()
+            ;; Still can be improved.
+            (player_choice
+               (
+                  ( (eval get_weapon_offer_label (access smithy_weapons 0)) )
+                  (visit buy_weapon (access smithy_weapons 0))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( (eval get_weapon_offer_label (access smithy_weapons 1)) )
+                  (visit buy_weapon (access smithy_weapons 1))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( Nevermind )
+                  (jump_to visit_smithy)
+               )
+            )
+         )
+
+         (define_sequence see_armors ()
+            ;; Still can be improved.
+            (player_choice
+               (
+                  ( (eval get_armor_offer_label (access smithy_armors 0)) )
+                  (visit buy_armor (access smithy_armors 0))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( (eval get_armor_offer_label (access smithy_armors 1)) )
+                  (visit buy_armor (access smithy_armors 1))
+                  (jump_to visit_smithy)
+               )
+               (
+                  ( Nevermind )
+                  (jump_to visit_smithy)
+               )
+            )
+         )
+
+         (define_sequence buy_weapon ( ((cons weapon int) weapon) )
+            ;; We can't even deny a sell yet...
+            (local int money_after)
+
+            (set money_after (- (var hero.money) (cdr weapon)))
+
+            (if_else (< (var money_after) 0)
+               (
+                  You can't afford that.
+                  (newline)
+                  You would need (abs (var money_after)) more coins.
+               )
+               (
+                  (set hero.weapon (car weapon))
+                  (set hero.money (var money_after))
+                  Equipped (var hero.weapon.name).
+               )
+            )
+            (newline)
+         )
+
+         (define_sequence buy_armor ( ((cons armor int) armor) )
+            ;; We can't even deny a sell yet...
+            (local int money_after)
+
+            (set money_after (- (var hero.money) (cdr armor)))
+
+            (if_else (< (var money_after) 0)
+               (
+                  You can't afford that.
+                  (newline)
+                  You would need (abs (var money_after)) more coins.
+               )
+               (
+                  (set hero.armor (car armor))
+                  (set hero.money (var money_after))
+                  Equipped (var hero.armor.name).
+               )
+            )
+            (newline)
+         )
+
+
+This was a first step toward cleaning up `smithy.fate`. Next, we'll use
+[conditions](/learn/conditions) to improve things further.
+
+----
+
+## Unchanged Files
+
+**data.fate:**
+
+         (fate_version 1)
+
+         (declare_structure weapon
+            (text name)
+            (int attack)
+            (int precision)
+         )
+
+         (declare_structure armor
+            (text name)
+            (int defense)
+         )
+
+         (declare_structure character
+            (string name)
+            (int money)
+            (weapon weapon)
+            (armor armor)
+         )
+
+         (global character hero)
+
+         (set_fields! hero.weapon
+            (name (text "Legendary" sword))
+            (attack 3)
+            (precision 50)
+         )
+
+         (set_fields! hero.armor
+            (name (text "Refined" attire))
+            (defense 1)
+         )
+
+         (set hero.money 42)
+
+**get_a_refill.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+         (require actions.fate)
+
+         (define_sequence lower_price_of_booze
+            (
+               ((ptr int) price_pointer)
+               (int decrease)
+            )
+            Great! The price of booze just lowered from (at price_pointer)
+            (set (at price_pointer)
+               (-
+                  (at price_pointer)
+                  (var decrease)
+               )
+            )
+            to (at price_pointer)!
+         )
+
+         (define_sequence get_a_refill ()
+            (local int price_of_booze)
+
+            (set price_of_booze 12)
+
+            Staring straight at the barman, you raise your glass and proclaim:
+            (newline)
+            "This soon-to-be world savior needs more booze!"
+            (newline)
+            The barman's lack of reaction is disappointing, but seeing the beer
+            being poured does help improve the mood.
+            (newline)
+            Satisfied, you hand the barman (var price_of_booze) copper coins.
+            (visit pay (var price_of_booze))
+            (newline)
+            The barman sighs, then asks:
+            (prompt_string (ptr hero.name) 2 64 What is your name, then, hero?)
+            (var hero.name)?
+            (newline)
+            The barman looks surprised.
+            (newline)
+            (visit lower_price_of_booze (ptr price_of_booze) 4)
+            (newline)
+            "I have heard of you, (var hero.name)," the barman exclaims, "I have
+            a quest for you!"
+            (newline)
+            It's your turn to sigh.
+            (newline)
+            The barman hands you a bag, and says:
+            (newline)
+            "Take this pre-payment and head to the smithy."
+            (newline)
+         )
+
+**actions.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence pay ( (int cost) )
+            (set hero_money
+               (- (var hero.money) (var cost))
+            )
+         )
+
+**falling_asleep.fate:**
+
+         (fate_version 1)
+
+         (require data.fate)
+
+         (define_sequence fall_asleep ()
+            Deciding to break away from the expected storyline, you promptly
+            fall asleep.
+            (newline)
+            ...
+            (newline)
+            Upon waking up, your hard-trained reflexes inform you that someone
+            stole all your money.
+            (set hero.money 0)
+            (newline)
+            This set-back was more than you could take. You give up on this
+            barely coherent story.
+            (end)
+         )
+
+**main.fate:**
+
+         (fate_version 1)
+
+         Once upon a time, starting a story with these words wasn't considered
+         a cliche. Starting in a tavern might also not be seen as very
+         original.  Having the main character be an street orphan, raised by
+         some mysterious sage all to end up as a mercenary with an uncommonly
+         strong sense of honor probably isn't going to lead to any praises for
+         novelty either. Maybe you should drink to that.
+         (newline)
+         Or maybe you shouldn't. This isn't your first mug. Not your second
+         either.  Drinking to forget that you are a stereotypical hero isn't
+         going to solve anything. Worse, the alcoholic trait is part of the
+         image.
+         (newline)
+         As you contemplate your own pointless description, your gaze leaves
+         what turns out to be an already empty glass in your hand and finds the
+         barman.
+
+         (player_choice
+            (
+               ( Ask the barman for a refill )
+               (visit get_a_refill)
+            )
+            (
+               ( Fall asleep )
+               (jump_to fall_asleep)
+            )
+         )
+
+         (require get_a_refill.fate)
+         (require falling_asleep.fate)
+
+         (end)
+
+**smithy_inventory.fate:**
+         (fate_version 1)
+
+         (require data.fate)
+
+         (global (list (cons weapon int)) smithy_weapons)
+         (global (list (cons weapon int)) smithy_armors)
+
+         (add!
+            (cons
+               (set_fields (default weapon)
+                  (name (text An Iron Rod))
+                  (attack 10)
+                  (precision 70)
+               )
+               176
+            )
+            smithy_weapons
+         )
+         (add!
+            (cons
+               (set_fields (default weapon)
+                  (name (text A Magnificient Brick))
+                  (attack 6)
+                  (precision 90)
+               )
+               110
+            )
+            smithy_weapons
+         )
+
+         (add!
+            (cons
+               (set_fields (default armor)
+                  (name (text A raincoat?!))
+                  (defense 7)
+               )
+               160
+            )
+            smithy_armors
+         )
+         (add!
+            (cons
+               (set_fields (default armor)
+                  (name (text A nice cape))
+                  (defense 3)
+               )
+               50
+            )
+            smithy_armors
+         )
diff --git a/content/learn/_index.md b/content/learn/_index.md
new file mode 100644
index 0000000..62706fc
--- /dev/null
+++ b/content/learn/_index.md
@@ -0,0 +1,8 @@
+---
+title: "Starting"
+menuTitle: "Learn"
+chapter: true
+---
+# Writing a story in Fate.
+This chapter provides an example of narrative written in Fate. Concepts are
+introduced progressively.
diff --git a/content/made_with_tonkadur/_index.md b/content/made_with_tonkadur/_index.md
new file mode 100644
index 0000000..4e1a654
--- /dev/null
+++ b/content/made_with_tonkadur/_index.md
@@ -0,0 +1,3 @@
+---
+title: Made with Tonkadur
+---
diff --git a/content/made_with_tonkadur/closed_source/_index.md b/content/made_with_tonkadur/closed_source/_index.md
new file mode 100644
index 0000000..5bb1e01
--- /dev/null
+++ b/content/made_with_tonkadur/closed_source/_index.md
@@ -0,0 +1,5 @@
+---
+title: Closed Source Games using Tonkadur
+menuTitle: Closed Source Games
+---
+!!!! This page **is** available, there's just nothing to list in there for now.
diff --git a/content/made_with_tonkadur/open_source/_index.md b/content/made_with_tonkadur/open_source/_index.md
new file mode 100644
index 0000000..0cc4bf9
--- /dev/null
+++ b/content/made_with_tonkadur/open_source/_index.md
@@ -0,0 +1,5 @@
+---
+menuTitle: Open Source Games
+title: Open Source Games using Tonkadur
+---
+!!!! This page **is** available, there's just nothing to list in there for now.
diff --git a/content/setup/_index.md b/content/setup/_index.md
new file mode 100644
index 0000000..1aad3fc
--- /dev/null
+++ b/content/setup/_index.md
@@ -0,0 +1,75 @@
+---
+title: "Setup"
+---
+# Setting Up Tonkadur
+There are two parts to set up: the compiler, and the interpreter. The compiler
+is provided and does not require any modification. The interpreter depends on
+the software you wish to integrate Tonkadur to. Examples of interpreters are
+available, but expect some modifications to be required. This should not prove
+to be difficult however, as the whole approach is design with the idea of
+keeping the interpreter small and simple.
+
+## Setting Up the Compiler
+
+### Installation
+**Dependencies:**
+* Java
+
+Download the latest release of Tonkadur on [this
+page](https://github.com/nsensfel/tonkadur/releases).
+
+### Use
+The Tonkadur compiler is invoked using `java -jar tonkadur.jar [option] `.
+`java -jar tonkadur.jar` will yield the complete list of valid options.
+
+`` corresponds to a Fate file. By default, the generated Wyrd file will
+have the same name with a suffix.
+
+## Setting Up the Interpreter
+It is recommended to look up existing interpreters. Not only might you be able
+to just use an existing one, but having examples of interpreters will help you
+create your own if you can't find one that fits your needs.
+
+**Known open source Wyrd interpreters:**
+* [nsensfel/tonkadur-python-interpreter](https://github.com/nsensfel/tonkadur-python-interpreter): A minimal Python3 interpreter, with JSON input.
+
+### Creating Your Own Interpreter
+If you end up needing to create your own interpreter, here are some clues on how
+to proceed.
+
+A Wyrd interpreter keeps track of:
+* **The Memory:** typically a mapping of strings to values. These values may be
+of any Wyrd type.
+* **The Program Counter:** an integer corresponding to the next Wyrd instruction
+to execute.
+* **Available Choices:** A list of `RICH_TEXT`.
+* **Last Chosen Choice:** Index of the last chosen choice.
+* **The Code:** A list of instructions.
+
+Additionally, the following can be kept track of:
+* **Types:** The default value for each type, making it easier to create new
+  memory elements of a given type.
+* **Allocated Data Counter:** The number of dynamically allocated memory
+  elements, making it easy to generate unique names for new memory elements.
+
+A few pitfalls:
+* **Copy values on assignment:** `(set_value a b)` does not make `a` become `b`,
+  but rather assigns a copy of the current value of `b` to `a`.
+* **Addresses may be already computed:** An `address` computation can take an
+   address as its `address` parameter. In other words, while in most cases an
+   `address` computation is a string that must be put in a list to become an
+   actual address, it can also sometimes already be a list and must then stay
+   unchanged.
+* **Beware of references:** When performing a computation that accesses memory
+  elements, be sure not to actually be returning the memory element itself after
+  it has been modified, but rather a modified copy of the memory element. This
+  is only problematic for memory elements whose type is modified by reference in
+  the interpreter's programming language. An example would be relative address
+  computation, which appends an element to a list. Computing a relative address
+  must not modify the base address, it only returns a list equivalent to the
+  base address but with an extra element.
+
+A set of tests can found on [this
+page](https://github.com/nsensfel/tonkadur/tree/master/data/tests), and will let
+you test your interpreter more easily. They have to be compiled to Wyrd first,
+however.
diff --git a/content/wyrd_v1/_index.md b/content/wyrd_v1/_index.md
new file mode 100644
index 0000000..4d902e2
--- /dev/null
+++ b/content/wyrd_v1/_index.md
@@ -0,0 +1,56 @@
+---
+menuTitle: Wyrd
+title: Wyrd (Version 1)
+---
+Wyrd is the language in which the narrative is given to the game engine. It is
+purposefully kept small and simple to facilitate porting Tonkadur to a new
+engine.
+
+The memory is seen as a table mapping strings to values. These values may also
+be tables. Thus a reference is a list of strings corresponding to a move from
+one table to the next.
+
+The program is a list of instructions. These instructions may use computations
+as parameters. They sometimes use hard-coded strings parameters as well.
+Instructions cannot take instructions as parameters. Instructions are not
+associated to any value.
+
+An integer, called _Program Counter_ is used to indicate the current
+instruction. In most cases, this integer is incremented by one after every
+instruction. There is an instruction to modify the value of the Program Counter,
+which allows conditional jumps and loops to be described.
+
+Computations are values and operations returning values. These are solely used
+as parameters of instructions. They do not alter memory (with one exception)
+and do not interact with the Program Counter in any way. An execution cannot be
+stopped during the evaluation of a computation: it is part of its parent
+instruction and is thus completed exactly when that instruction is performed.
+Computations may _read_ from the memory, as they may need to fetch the value
+associated with an address or traverse tables. All computations have a return
+type.
+
+Wyrd does not have the notion of sequence or that lambda functions. It does not
+even associate player choices with lists of actions. It's all done by carefully
+managing the Program Counter.
+
+Lambda functions are stored as an `INT` corresponding to a line in the program.
+
+## Types
+* `ADDRESS` (or `POINTER`, or `REFERENCE`), a list of `STRING` (note: not a
+   `STRING COLLECTION`).
+* `BOOL`. This should be changed to `BOOL` soon, for consistency's sake.
+* `[SOMETHING] COLLECTION`, table mapping `STRING` to `[SOMETHING]`.
+* `FLOAT`.
+* `INT`.
+* `RICH TEXT`, a list of `STRINGS` with attributes attached.
+* `STRING`.
+* `STRUCTURE` (or `DICTIONARY`), table mapping `STRING` to values of any type.
+* `{String}`, a hard-coded string.
+
+#### Aliases sometimes used to refer to types
+* `? ADDRESS`: an `ADDRESS` pointing to a particular type.
+* `BASE TYPE`: `INT`, `FLOAT`, `BOOL`, `STRING`.
+* `COMPARABLE`: `INT`, `FLOAT`, `BOOL`, `STRING`, `RICH TEXT`, `ADDRESS`.
+* `COLLECTION`: any `? COLLECTION`.
+* `COMPUTATION`: any type.
+* `NUMBER`: `INT` or `FLOAT`.
diff --git a/content/wyrd_v1/computations/_index.md b/content/wyrd_v1/computations/_index.md
new file mode 100644
index 0000000..028a82e
--- /dev/null
+++ b/content/wyrd_v1/computations/_index.md
@@ -0,0 +1,99 @@
+---
+title: Computations
+---
+This page presents all the computations that can be performed in Wyrd.
+Computations may access the current memory, but, with one exception, do not
+change it. The one exception is `(new t)`. All computations return values.
+The terms 'value' and 'computation' are interchangeable.
+
+## CONSTANT
+{{< fatecode >}}(const [BASE TYPE] {string}){{< /fatecode >}}
+Returns the `[BASE TYPE]` represented by `{string}`.
+
+## CAST
+{{< fatecode >}}(cast [COMPUTATION] [BASE TYPE]){{< /fatecode >}}
+Converts `[COMPUTATION]` to `[BASE TYPE]`, returns the result.
+
+Note:
+* Converting from `FLOAT` to `INT` returns `floor(v)`.
+* Converting from `BOOL` to `STRING` yields either `True` or `False`.
+
+The following must be supported:
+* `[FLOAT]` to `[FLOAT]`, `[INT]`, and `[STRING]`.
+* `[INT]` to `[FLOAT]`, `[INT]`, and `[STRING]`.
+* `[BOOL]` to `[BOOL]` and `[STRING]`.
+* `[STRING]` to `[BOOL]` (`true` and `false`), `[FLOAT]`, `[INT]`, and`[STRING]`.
+
+## IF-ELSE
+{{< fatecode >}}(if_else [BOOL] [C0 = COMPUTATION] [C1 = COMPUTATION]){{< /fatecode >}}
+Returns `C0` if `[BOOL]` holds _true_, `C1` otherwise. `C0` and `C1` both
+have the same type.
+
+## EQUALS
+{{< fatecode >}}(equals [C0 = COMPUTATION] [C1 = COMPUTATION]){{< /fatecode >}}
+Returns a `BOOL`, _true_ if `C0` and `C1` hold the same value. `C0` and `C1`
+are both of the same type.
+
+## MATHEMATICAL OPERATORS
+{{< fatecode >}}(divide [C0 = NUMBER] [C1 = NUMBER]){{< /fatecode >}}
+
+{{< fatecode >}}(minus [C0 = NUMBER] [C1 = NUMBER]){{< /fatecode >}}
+
+{{< fatecode >}}(plus [C0 = NUMBER] [C1 = NUMBER]){{< /fatecode >}}
+
+{{< fatecode >}}(power [C0 = NUMBER] [C1 = NUMBER]){{< /fatecode >}}
+
+{{< fatecode >}}(times [C0 = NUMBER] [C1 = NUMBER]){{< /fatecode >}}
+The operation returns a value of the same type as `C0` and `C1` (both `C0` and
+`C1` are also of the same type). Thus, `(divide C0 C1)` is an integer division
+(the remainder is discarded) if `C0` and `C1` are of type `INT`, and a standard
+division if they are of type `FLOAT`.
+
+{{< fatecode >}}(rand [C0 = INT] [C1 = INT]){{< /fatecode >}}
+Returns a random value between `C0` and `C1`, inclusive. Raises a runtime error
+if `C0 > C1`.
+
+## MATHEMATICAL COMPARISON
+{{< fatecode >}}(less_than [C0 = COMPARABLE] [C1 = COMPARABLE]){{< /fatecode >}}
+Returns a `[BOOL]` indicating if `C0` is strictly less than `C1`.
+`C0` and `C1` are both of the same type.
+
+## LOGICAL OPERATORS
+{{< fatecode >}}(and [C0 = BOOL] [C1 = BOOL]){{< /fatecode >}}
+
+{{< fatecode >}}(not [C0 = BOOL]){{< /fatecode >}}
+
+## SIZE
+{{< fatecode >}}(size [COLLECTION ADDRESS]){{< /fatecode >}}
+Returns the number of elements held by the collection at
+`[COLLECTION ADDRESS]`.
+
+## REFERENCES
+{{< fatecode >}}(address [COMPUTATION]){{< /fatecode >}}
+Returns an `ADDRESS` to the memory element at address `[COMPUTATION]`. Raises a
+runtime error if `[COMPUTATION]` is not a memory element.
+
+{{< fatecode >}}(relative_address  [STRING]){{< /fatecode >}}
+Returns a `REFERENCE` to member `[STRING]` of `R0`.
+
+{{< fatecode >}}(value_of [ADDRESS]){{< /fatecode >}}
+Returns the value held at the memory location `[ADDRESS]`.
+
+{{< fatecode >}}(new [TYPE]){{< /fatecode >}}
+Returns an `[TYPE] ADDRESS` to a newly allocated memory element of
+type `[TYPE]`.
+
+## RICH TEXT
+{{< fatecode >}}(newline){{< /fatecode >}}
+Returns a `RICH TEXT` value corresponding to a newline.
+
+{{< fatecode >}}(rich_text [S0 = STRING] ... [SN = STRING]){{< /fatecode >}}
+Returns a single value `RICH TEXT` representing the elements `S0` ... `S1`.
+
+{{< fatecode >}}(add_rich_text_effect ({string} [V0 = COMPUTATION] ... [VN = COMPUTATION]) [RICH TEXT]){{< /fatecode >}}
+Returns a `RICH TEXT` value of `[RICH TEXT]` with the given effect enabled.
+
+## LAST USER CHOICE
+{{< fatecode >}}(get_last_user_choice){{< /fatecode >}}
+Returns the number corresponding to the option last chosen by the user in a
+`(resolve_choices)`, or `-1` if there is no such value.
diff --git a/content/wyrd_v1/extensions/_index.md b/content/wyrd_v1/extensions/_index.md
new file mode 100644
index 0000000..a21559c
--- /dev/null
+++ b/content/wyrd_v1/extensions/_index.md
@@ -0,0 +1,4 @@
+---
+title: Extensions
+---
+Not available in Version 1.
diff --git a/content/wyrd_v1/instructions/_index.md b/content/wyrd_v1/instructions/_index.md
new file mode 100644
index 0000000..9d43a5a
--- /dev/null
+++ b/content/wyrd_v1/instructions/_index.md
@@ -0,0 +1,75 @@
+---
+title: Instructions
+---
+This page presents all the instructions that can be performed in Wyrd.
+Instructions do not return values. With one exception, all instructions increase
+the Program Counter by 1.
+
+
+## ADD CHOICE
+{{< fatecode >}}(add_choice [RICH TEXT]){{< /fatecode >}}
+Adds a new option for the next `resolve_choices` instruction. The new option
+presents the player with `[RICH TEXT]`.
+
+## INTEGER PROMPT
+{{< fatecode >}}(prompt_integer [INT REFERENCE] [MIN = INT] [MAX = INT] [RICH TEXT]){{< /fatecode >}}
+
+Prompts the user for an integer between `[MIN]` and `[MAX]` by displaying the
+message `[RICH TEXT]`. The result is stored in `[INT REFERENCE]`.
+
+## STRING PROMPT
+{{< fatecode >}}(prompt_string [STRING REFERENCE] [MIN = INT] [MAX = INT] [RICH TEXT]){{< /fatecode >}}
+
+Prompts the user for a string of size between `[MIN]` and `[MAX]` by displaying
+the message `[RICH TEXT]`. The result is stored in `[STRING REFERENCE]`.
+
+
+## ASSERT
+{{< fatecode >}}(assert [BOOL] [RICH TEXT]){{< /fatecode >}}
+If `[BOOL]` isn't _true_, raise a runtime error containing `[RICH TEXT]`.
+
+
+## DISPLAY
+{{< fatecode >}}(display [RICH TEXT]){{< /fatecode >}}
+Displays `[RICH TEXT]` to the player.
+
+
+## END
+{{< fatecode >}}(end){{< /fatecode >}}
+Marks the end of the narration. Interrupts the execution.
+
+
+## EVENT CALL
+{{< fatecode >}}(event_call {string} [C0 = COMPUTATION] ... [CN = COMPUTATION]){{< /fatecode >}}
+Interrupts execution, informs the interpreter that the given event `{String}`
+was triggered with the parameters `C0 ... CN`.
+
+
+## REMOVE
+{{< fatecode >}}(remove [ADDRESS]){{< /fatecode >}}
+The memory at `[ADDRESS]` is freed.
+
+
+## RESOLVE CHOICES
+{{< fatecode >}}(resolve_choices){{< /fatecode >}}
+Present the player with all options that where added using `add_choice` since
+the last `resolve_choices`. The execution is paused and should be resumed by
+the interpreter setting the Program Counter according to the chosen option.
+
+
+## SET PC
+{{< fatecode >}}(set_pc [INT]){{< /fatecode >}}
+Sets the Program Counter to `[INT]`. The program counter is not automatically
+increased by 1 in this case.
+
+
+## SET VALUE
+{{< fatecode >}}(set_value [ADDRESS] [COMPUTATION]){{< /fatecode >}}
+Sets the memory pointed by `[ADDRESS]` to `[COMPUTATION]`.
+`[COMPUTATION]` is passed by value, not reference (i.e. no aliasing can occur
+without it being done explicitly through pointers).
+
+## INITIALIZE MEMORY ELEMENT
+{{< fatecode >}}(initialize [ADDRESS] [TYPE]){{< /fatecode >}}
+Initializes a memory element at `[ADDRESS]` with the default value for the type
+`[TYPE]`.
diff --git a/layouts/shortcodes/fatecode.html b/layouts/shortcodes/fatecode.html
new file mode 100644
index 0000000..77bd69c
--- /dev/null
+++ b/layouts/shortcodes/fatecode.html
@@ -0,0 +1,2 @@
+
+
{{.Inner}}
diff --git a/layouts/shortcodes/rawhtml.html b/layouts/shortcodes/rawhtml.html new file mode 100644 index 0000000..b90bea2 --- /dev/null +++ b/layouts/shortcodes/rawhtml.html @@ -0,0 +1,2 @@ + +{{.Inner}} diff --git a/static/css/prism.css b/static/css/prism.css new file mode 100755 index 0000000..066264f --- /dev/null +++ b/static/css/prism.css @@ -0,0 +1,321 @@ +/* PrismJS 1.22.0 +https://prismjs.com/download.html#themes=prism-tomorrow&languages=scheme&plugins=line-highlight+line-numbers+toolbar+copy-to-clipboard+match-braces */ +/** + * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML + * Based on https://github.com/chriskempson/tomorrow-theme + * @author Rose Pritchard + */ + +code[class*="language-"], +pre[class*="language-"] { + color: #ccc; + background: none; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #2d2d2d; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.block-comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #999; +} + +.token.punctuation { + color: #ccc; +} + +.token.tag, +.token.attr-name, +.token.namespace, +.token.deleted { + color: #e2777a; +} + +.token.function-name { + color: #6196cc; +} + +.token.boolean, +.token.number, +.token.function { + color: #f08d49; +} + +.token.property, +.token.class-name, +.token.constant, +.token.symbol { + color: #f8c555; +} + +.token.selector, +.token.important, +.token.atrule, +.token.keyword, +.token.builtin { + color: #cc99cd; +} + +.token.string, +.token.char, +.token.attr-value, +.token.regex, +.token.variable { + color: #7ec699; +} + +.token.operator, +.token.entity, +.token.url { + color: #67cdcc; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.token.inserted { + color: green; +} + +pre[data-line] { + position: relative; + padding: 1em 0 1em 3em; +} + +.line-highlight { + position: absolute; + left: 0; + right: 0; + padding: inherit 0; + margin-top: 1em; /* Same as .prism’s padding-top */ + + background: hsla(24, 20%, 50%,.08); + background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); + + pointer-events: none; + + line-height: inherit; + white-space: pre; +} + + .line-highlight:before, + .line-highlight[data-end]:after { + content: attr(data-start); + position: absolute; + top: .4em; + left: .6em; + min-width: 1em; + padding: 0 .5em; + background-color: hsla(24, 20%, 50%,.4); + color: hsl(24, 20%, 95%); + font: bold 65%/1.5 sans-serif; + text-align: center; + vertical-align: .3em; + border-radius: 999px; + text-shadow: none; + box-shadow: 0 1px white; + } + + .line-highlight[data-end]:after { + content: attr(data-end); + top: auto; + bottom: .4em; + } + +.line-numbers .line-highlight:before, +.line-numbers .line-highlight:after { + content: none; +} + +pre[id].linkable-line-numbers span.line-numbers-rows { + pointer-events: all; +} +pre[id].linkable-line-numbers span.line-numbers-rows > span:before { + cursor: pointer; +} +pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before { + background-color: rgba(128, 128, 128, .2); +} + +pre[class*="language-"].line-numbers { + position: relative; + padding-left: 3.8em; + counter-reset: linenumber; +} + +pre[class*="language-"].line-numbers > code { + position: relative; + white-space: inherit; +} + +.line-numbers .line-numbers-rows { + position: absolute; + pointer-events: none; + top: 0; + font-size: 100%; + left: -3.8em; + width: 3em; /* works for line-numbers below 1000 lines */ + letter-spacing: -1px; + border-right: 1px solid #999; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} + + .line-numbers-rows > span { + display: block; + counter-increment: linenumber; + } + + .line-numbers-rows > span:before { + content: counter(linenumber); + color: #999; + display: block; + padding-right: 0.8em; + text-align: right; + } + +div.code-toolbar { + position: relative; +} + +div.code-toolbar > .toolbar { + position: absolute; + top: .3em; + right: .2em; + transition: opacity 0.3s ease-in-out; + opacity: 0; +} + +div.code-toolbar:hover > .toolbar { + opacity: 1; +} + +/* Separate line b/c rules are thrown out if selector is invalid. + IE11 and old Edge versions don't support :focus-within. */ +div.code-toolbar:focus-within > .toolbar { + opacity: 1; +} + +div.code-toolbar > .toolbar .toolbar-item { + display: inline-block; +} + +div.code-toolbar > .toolbar a { + cursor: pointer; +} + +div.code-toolbar > .toolbar button { + background: none; + border: 0; + color: inherit; + font: inherit; + line-height: normal; + overflow: visible; + padding: 0; + -webkit-user-select: none; /* for button */ + -moz-user-select: none; + -ms-user-select: none; +} + +div.code-toolbar > .toolbar a, +div.code-toolbar > .toolbar button, +div.code-toolbar > .toolbar span { + color: #bbb; + font-size: .8em; + padding: 0 .5em; + background: #f5f2f0; + background: rgba(224, 224, 224, 0.2); + box-shadow: 0 2px 0 0 rgba(0,0,0,0.2); + border-radius: .5em; +} + +div.code-toolbar > .toolbar a:hover, +div.code-toolbar > .toolbar a:focus, +div.code-toolbar > .toolbar button:hover, +div.code-toolbar > .toolbar button:focus, +div.code-toolbar > .toolbar span:hover, +div.code-toolbar > .toolbar span:focus { + color: inherit; + text-decoration: none; +} + +.token.punctuation.brace-hover, +.token.punctuation.brace-selected { + outline: solid 1px; +} + +.rainbow-braces .token.punctuation.brace-level-1, +.rainbow-braces .token.punctuation.brace-level-5, +.rainbow-braces .token.punctuation.brace-level-9 { + color: #E50; + opacity: 1; +} +.rainbow-braces .token.punctuation.brace-level-2, +.rainbow-braces .token.punctuation.brace-level-6, +.rainbow-braces .token.punctuation.brace-level-10 { + color: #0B3; + opacity: 1; +} +.rainbow-braces .token.punctuation.brace-level-3, +.rainbow-braces .token.punctuation.brace-level-7, +.rainbow-braces .token.punctuation.brace-level-11 { + color: #26F; + opacity: 1; +} +.rainbow-braces .token.punctuation.brace-level-4, +.rainbow-braces .token.punctuation.brace-level-8, +.rainbow-braces .token.punctuation.brace-level-12 { + color: #E0E; + opacity: 1; +} + diff --git a/static/js/highlight.pack.js b/static/js/highlight.pack.js new file mode 100755 index 0000000..2c9deb0 --- /dev/null +++ b/static/js/highlight.pack.js @@ -0,0 +1,9 @@ +/* PrismJS 1.22.0 +https://prismjs.com/download.html#themes=prism-tomorrow&languages=scheme&plugins=line-highlight+line-numbers+toolbar+copy-to-clipboard+match-braces */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,_={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof M?new M(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);y+=m.value.length,m=m.next){var k=m.value;if(t.length>n.length)return;if(!(k instanceof M)){var b,x=1;if(h){if(!(b=W(p,y,n,f)))break;var w=b.index,A=b.index+b[0].length,P=y;for(P+=m.value.length;P<=w;)m=m.next,P+=m.value.length;if(P-=m.value.length,y=P,m.value instanceof M)continue;for(var S=m;S!==t.tail&&(Pl.reach&&(l.reach=N);var j=m.prev;O&&(j=z(t,j,O),y+=O.length),I(t,j,x);var C=new M(o,g?_.tokenize(E,g):E,d,E);m=z(t,j,C),L&&z(t,m,L),1"+a.content+""},!u.document)return u.addEventListener&&(_.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(_.highlight(r,_.languages[t],t)),a&&u.close()},!1)),_;var e=_.util.currentScript();function t(){_.manual||_.highlightAll()}if(e&&(_.filename=e.src,e.hasAttribute("data-manual")&&(_.manual=!0)),!_.manual){var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)}return _}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.scheme={comment:/;.*|#;\s*\((?:[^()]|\([^()]*\))*\)|#\|(?:[^#|]|#(?!\|)|\|(?!#)|#\|(?:[^#|]|#(?!\|)|\|(?!#))*\|#)*\|#/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0},symbol:{pattern:/'[^()#'\s]+/,greedy:!0},character:{pattern:/#\\(?:[ux][a-fA-F\d]+\b|[-a-zA-Z]+\b|\S)/,greedy:!0,alias:"string"},"lambda-parameter":[{pattern:/((?:^|[^'`#])\(lambda\s+)(?:[^|()'\s]+|\|(?:[^\\|]|\\.)*\|)/,lookbehind:!0},{pattern:/((?:^|[^'`#])\(lambda\s+\()[^()']+/,lookbehind:!0}],keyword:{pattern:/((?:^|[^'`#])\()(?:begin|case(?:-lambda)?|cond(?:-expand)?|define(?:-library|-macro|-record-type|-syntax|-values)?|defmacro|delay(?:-force)?|do|else|export|except|guard|if|import|include(?:-ci|-library-declarations)?|lambda|let(?:rec)?(?:-syntax|-values|\*)?|let\*-values|only|parameterize|prefix|(?:quasi-?)?quote|rename|set!|syntax-(?:case|rules)|unless|unquote(?:-splicing)?|when)(?=[()\s]|$)/,lookbehind:!0},builtin:{pattern:/((?:^|[^'`#])\()(?:abs|and|append|apply|assoc|ass[qv]|binary-port\?|boolean=?\?|bytevector(?:-append|-copy|-copy!|-length|-u8-ref|-u8-set!|\?)?|caar|cadr|call-with-(?:current-continuation|port|values)|call\/cc|car|cdar|cddr|cdr|ceiling|char(?:->integer|-ready\?|\?|<\?|<=\?|=\?|>\?|>=\?)|close-(?:input-port|output-port|port)|complex\?|cons|current-(?:error|input|output)-port|denominator|dynamic-wind|eof-object\??|eq\?|equal\?|eqv\?|error|error-object(?:-irritants|-message|\?)|eval|even\?|exact(?:-integer-sqrt|-integer\?|\?)?|expt|features|file-error\?|floor(?:-quotient|-remainder|\/)?|flush-output-port|for-each|gcd|get-output-(?:bytevector|string)|inexact\??|input-port(?:-open\?|\?)|integer(?:->char|\?)|lcm|length|list(?:->string|->vector|-copy|-ref|-set!|-tail|\?)?|make-(?:bytevector|list|parameter|string|vector)|map|max|member|memq|memv|min|modulo|negative\?|newline|not|null\?|number(?:->string|\?)|numerator|odd\?|open-(?:input|output)-(?:bytevector|string)|or|output-port(?:-open\?|\?)|pair\?|peek-char|peek-u8|port\?|positive\?|procedure\?|quotient|raise|raise-continuable|rational\?|rationalize|read-(?:bytevector|bytevector!|char|error\?|line|string|u8)|real\?|remainder|reverse|round|set-c[ad]r!|square|string(?:->list|->number|->symbol|->utf8|->vector|-append|-copy|-copy!|-fill!|-for-each|-length|-map|-ref|-set!|\?|<\?|<=\?|=\?|>\?|>=\?)?|substring|symbol(?:->string|\?|=\?)|syntax-error|textual-port\?|truncate(?:-quotient|-remainder|\/)?|u8-ready\?|utf8->string|values|vector(?:->list|->string|-append|-copy|-copy!|-fill!|-for-each|-length|-map|-ref|-set!|\?)?|with-exception-handler|write-(?:bytevector|char|string|u8)|zero\?)(?=[()\s]|$)/,lookbehind:!0},operator:{pattern:/((?:^|[^'`#])\()(?:[-+*%/]|[<>]=?|=>?)(?=[()\s]|$)/,lookbehind:!0},number:{pattern:RegExp(function(r){for(var e in r)r[e]=r[e].replace(/<[\w\s]+>/g,function(e){return"(?:"+r[e].trim()+")"});return r[e]}({"":"\\d+(?:/\\d+)?|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?","":"[+-]?|[+-](?:inf|nan)\\.0","":"[+-](?:|(?:inf|nan)\\.0)?i","":"(?:@|)?|","":"(?:#d(?:#[ei])?|#[ei](?:#d)?)?","":"[0-9a-f]+(?:/[0-9a-f]+)?","":"[+-]?|[+-](?:inf|nan)\\.0","":"[+-](?:|(?:inf|nan)\\.0)?i","":"(?:@|)?|","":"#[box](?:#[ei])?|(?:#[ei])?#[box]","":"(^|[\\s()])(?:|)(?=[()\\s]|$)"}),"i"),lookbehind:!0},boolean:{pattern:/(^|[\s()])#(?:[ft]|false|true)(?=[()\s]|$)/,lookbehind:!0},function:{pattern:/((?:^|[^'`#])\()(?:[^|()'\s]+|\|(?:[^\\|]|\\.)*\|)(?=[()\s]|$)/,lookbehind:!0},identifier:{pattern:/(^|[\s()])\|(?:[^\\|]|\\.)*\|(?=[()\s]|$)/,lookbehind:!0,greedy:!0},punctuation:/[()']/}; +!function(){if("undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector){var t,s=function(){if(void 0===t){var e=document.createElement("div");e.style.fontSize="13px",e.style.lineHeight="1.5",e.style.padding="0",e.style.border="0",e.innerHTML=" 
 ",document.body.appendChild(e),t=38===e.offsetHeight,document.body.removeChild(e)}return t},l=!0,a=0;Prism.hooks.add("before-sanity-check",function(e){var t=e.element.parentNode,n=t&&t.getAttribute("data-line");if(t&&n&&/pre/i.test(t.nodeName)){var i=0;g(".line-highlight",t).forEach(function(e){i+=e.textContent.length,e.parentNode.removeChild(e)}),i&&/^( \n)+$/.test(e.code.slice(-i))&&(e.code=e.code.slice(0,-i))}}),Prism.hooks.add("complete",function e(t){var n=t.element.parentNode,i=n&&n.getAttribute("data-line");if(n&&i&&/pre/i.test(n.nodeName)){clearTimeout(a);var r=Prism.plugins.lineNumbers,o=t.plugins&&t.plugins.lineNumbers;if(b(n,"line-numbers")&&r&&!o)Prism.hooks.add("line-numbers",e);else u(n,i)(),a=setTimeout(c,1)}}),window.addEventListener("hashchange",c),window.addEventListener("resize",function(){g("pre[data-line]").map(function(e){return u(e)}).forEach(v)})}function g(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function b(e,t){return t=" "+t+" ",-1<(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)}function v(e){e()}function u(u,e,c){var t=(e="string"==typeof e?e:u.getAttribute("data-line")).replace(/\s+/g,"").split(",").filter(Boolean),d=+u.getAttribute("data-line-offset")||0,f=(s()?parseInt:parseFloat)(getComputedStyle(u).lineHeight),m=b(u,"line-numbers"),p=m?u:u.querySelector("code")||u,h=[];t.forEach(function(e){var t=e.split("-"),n=+t[0],i=+t[1]||n,r=u.querySelector('.line-highlight[data-range="'+e+'"]')||document.createElement("div");if(h.push(function(){r.setAttribute("aria-hidden","true"),r.setAttribute("data-range",e),r.className=(c||"")+" line-highlight"}),m&&Prism.plugins.lineNumbers){var o=Prism.plugins.lineNumbers.getLine(u,n),a=Prism.plugins.lineNumbers.getLine(u,i);if(o){var s=o.offsetTop+"px";h.push(function(){r.style.top=s})}if(a){var l=a.offsetTop-o.offsetTop+a.offsetHeight+"px";h.push(function(){r.style.height=l})}}else h.push(function(){r.setAttribute("data-start",n),n span",u).forEach(function(e,t){var n=t+a;e.onclick=function(){var e=i+"."+n;l=!1,location.hash=e,setTimeout(function(){l=!0},1)}})}}return function(){h.forEach(v)}}function c(){var e=location.hash.slice(1);g(".temporary.line-highlight").forEach(function(e){e.parentNode.removeChild(e)});var t=(e.match(/\.([\d,-]+)$/)||[,""])[1];if(t&&!document.getElementById(e)){var n=e.slice(0,e.lastIndexOf(".")),i=document.getElementById(n);if(i)i.hasAttribute("data-line")||i.setAttribute("data-line",""),u(i,t,"temporary ")(),l&&document.querySelector(".temporary.line-highlight").scrollIntoView()}}}(); +!function(){if("undefined"!=typeof self&&self.Prism&&self.document){var o="line-numbers",a=/\n(?!$)/g,e=Prism.plugins.lineNumbers={getLine:function(e,n){if("PRE"===e.tagName&&e.classList.contains(o)){var t=e.querySelector(".line-numbers-rows");if(t){var i=parseInt(e.getAttribute("data-start"),10)||1,r=i+(t.children.length-1);n");(i=document.createElement("span")).setAttribute("aria-hidden","true"),i.className="line-numbers-rows",i.innerHTML=l,t.hasAttribute("data-start")&&(t.style.counterReset="linenumber "+(parseInt(t.getAttribute("data-start"),10)-1)),e.element.appendChild(i),u([t]),Prism.hooks.run("line-numbers",e)}}}),Prism.hooks.add("line-numbers",function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0})}function u(e){if(0!=(e=e.filter(function(e){var n=t(e)["white-space"];return"pre-wrap"===n||"pre-line"===n})).length){var n=e.map(function(e){var n=e.querySelector("code"),t=e.querySelector(".line-numbers-rows");if(n&&t){var i=e.querySelector(".line-numbers-sizer"),r=n.textContent.split(a);i||((i=document.createElement("span")).className="line-numbers-sizer",n.appendChild(i)),i.innerHTML="0",i.style.display="block";var s=i.getBoundingClientRect().height;return i.innerHTML="",{element:e,lines:r,lineHeights:[],oneLinerHeight:s,sizer:i}}}).filter(Boolean);n.forEach(function(e){var i=e.sizer,n=e.lines,r=e.lineHeights,s=e.oneLinerHeight;r[n.length-1]=void 0,n.forEach(function(e,n){if(e&&1