questmark 0.0.36 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.github/workflows/docs.yml +56 -0
  2. package/bin/questmark +1 -1
  3. package/dist/browser/bundle.js +28365 -0
  4. package/dist/node/QuestVM.d.ts +1 -2
  5. package/dist/node/QuestVM.js +81 -83
  6. package/dist/node/QuestVM.js.map +1 -1
  7. package/dist/node/cli.js +33 -49
  8. package/dist/node/cli.js.map +1 -1
  9. package/dist/node/index.d.ts +2 -2
  10. package/dist/node/index.js +2 -14
  11. package/dist/node/index.js.map +1 -1
  12. package/dist/node/parseMarkdown.d.ts +18 -7
  13. package/dist/node/parseMarkdown.js +81 -77
  14. package/dist/node/parseMarkdown.js.map +1 -1
  15. package/docs/explanation/how-questmark-works.md +123 -0
  16. package/docs/explanation/why-questmark.md +72 -0
  17. package/docs/how-to/compile-to-bytecode.md +62 -0
  18. package/docs/how-to/play-a-document.md +57 -0
  19. package/docs/how-to/use-the-library.md +98 -0
  20. package/docs/index.md +61 -0
  21. package/docs/reference/api.md +135 -0
  22. package/docs/reference/cli.md +66 -0
  23. package/docs/reference/language.md +213 -0
  24. package/docs/tutorials/first-conversation.md +219 -0
  25. package/package.json +33 -31
  26. package/readme.md +55 -80
  27. package/tsconfig.json +8 -3
  28. package/tsconfig.webpack.json +9 -0
  29. package/webpack.config.cjs +49 -0
  30. package/dist/browser/QuestVM.d.ts +0 -13
  31. package/dist/browser/QuestVM.js +0 -89
  32. package/dist/browser/QuestVM.js.map +0 -1
  33. package/dist/browser/cli.d.ts +0 -2
  34. package/dist/browser/cli.js +0 -80
  35. package/dist/browser/cli.js.map +0 -1
  36. package/dist/browser/index.d.ts +0 -2
  37. package/dist/browser/index.js +0 -15
  38. package/dist/browser/index.js.map +0 -1
  39. package/dist/browser/parseMarkdown.d.ts +0 -25
  40. package/dist/browser/parseMarkdown.js +0 -413
  41. package/dist/browser/parseMarkdown.js.map +0 -1
@@ -0,0 +1,57 @@
1
+ # How to play a Questmark document
2
+
3
+ This guide shows you how to run a Questmark document interactively.
4
+
5
+ ## From the project directory
6
+
7
+ After cloning the repository and running `npm install`, play any local
8
+ document with:
9
+
10
+ ```bash
11
+ npm start -- --input path/to/document.md
12
+ ```
13
+
14
+ The document must have one of the extensions `.md`, `.qmd`, `.md.html`, or
15
+ `.qmd.html` (or be a compiled `.json` — see
16
+ [compile a document to bytecode](compile-to-bytecode.md)).
17
+
18
+ ## From the published CLI
19
+
20
+ If you don't have a checkout of the repository, the CLI is published on npm:
21
+
22
+ ```bash
23
+ npx questmark --input path/to/document.md
24
+ ```
25
+
26
+ ## From a URL
27
+
28
+ The input path can be an `http://` or `https://` URL, so you can play a
29
+ document that's hosted online:
30
+
31
+ ```bash
32
+ npx questmark --input https://ghcdn.rawgit.org/jorisvddonk/questmark/master/examples/self-describing.md
33
+ ```
34
+
35
+ ## During a play session
36
+
37
+ - The interpreter prints the current state's text, then shows a menu of the
38
+ options currently available.
39
+ - Use the arrow keys to navigate and `Enter` to choose.
40
+ - Text that appears *after* an option line in the source is only shown once
41
+ that option has been chosen.
42
+ - Options can be hidden by preconditions, or appear only once when marked
43
+ `@once` — if an option is missing from the menu, that's the story telling
44
+ you it isn't available.
45
+
46
+ ## Troubleshooting
47
+
48
+ - **"Missing input!"** — you didn't pass `--input`. Pass the path or URL of a
49
+ document.
50
+ - **The menu loops forever** — you chose an option that has neither a link nor
51
+ an `exit`/`goto` effect. With the default `loopback_to_options` behaviour the
52
+ interpreter returns to the menu. Change the behaviour in the document header
53
+ (see the [language reference](../reference/language.md)), or give the option
54
+ a link or an effect.
55
+ - **The interpreter quits immediately** — the document uses the `exit`
56
+ `no_link_behaviour`, or the state you entered runs an `exit` instruction.
57
+ This is often intended.
@@ -0,0 +1,98 @@
1
+ # How to embed Questmark in your own application
2
+
3
+ The library exposes two pieces: `parseMarkdown`, which compiles a Questmark
4
+ document into a Tzo VM state, and `QuestVM`, which interprets that state. This
5
+ guide wires them together in a small Node.js application.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install questmark
11
+ ```
12
+
13
+ ## Compile and run a document
14
+
15
+ ```js
16
+ const { parseMarkdown, QuestVM } = require("questmark");
17
+ const fs = require("fs");
18
+
19
+ const markdown = fs.readFileSync("conversation.md", "utf8");
20
+ const { qvmState } = parseMarkdown(markdown);
21
+
22
+ const vm = new QuestVM(
23
+ // emitFunction: called every time the document emits text.
24
+ (body) => process.stdout.write(String(body)),
25
+
26
+ // getResponseFunction: called whenever the document asks the player
27
+ // to choose. Return a Promise of the chosen option's id.
28
+ async (choices) => {
29
+ choices.forEach((c) => console.log(`${c.id}: ${c.title}`));
30
+ const id = Number(await askUser("Your choice: "));
31
+ return id;
32
+ },
33
+
34
+ // additionalFunctions: optional extra Tzo opcodes.
35
+ {}
36
+ );
37
+
38
+ vm.loadVMState(qvmState);
39
+ vm.run();
40
+ ```
41
+
42
+ ## What the callbacks do
43
+
44
+ - **emitFunction** — invoked by the `emit` opcode with the text (or number) the
45
+ document wants to show. It can be called several times while rendering a
46
+ single state (for example `` You have `"cookies" getContext emit` cookies! ``).
47
+ Concatenate the calls to reconstruct the full text.
48
+
49
+ - **getResponseFunction** — invoked by the `getResponse` opcode with the list
50
+ of options currently available, as `{ title, id }` objects. The ids are
51
+ positional (0, 1, 2, ...). Return a `Promise` resolving to the chosen id;
52
+ the VM resumes execution from that option's effect.
53
+
54
+ - **additionalFunctions** — a map of extra opcode names to functions. Opcode
55
+ names beginning with `_` are ignored at compile time, so you can use
56
+ underscore-prefixed calls in documents as hooks for your own logic.
57
+
58
+ ## Running in a browser
59
+
60
+ The webpack build produces a browser bundle at `dist/browser/bundle.js`. Load
61
+ it with a `<script>` tag and use the global `Questmark` object the same way:
62
+
63
+ ```html
64
+ <script src="questmark/dist/browser/bundle.js"></script>
65
+ <script>
66
+ const { parseMarkdown, QuestVM } = Questmark;
67
+ // ...same API as above, wiring emitFunction to the DOM instead of stdout.
68
+ </script>
69
+ ```
70
+
71
+ ## Handling errors
72
+
73
+ `QuestVM` extends `EventEmitter`. Runtime errors in the interpreter are emitted
74
+ on the `eventBus` under the `error` event:
75
+
76
+ ```js
77
+ vm.eventBus.on("error", (err) => {
78
+ console.error("Interpreter error:", err);
79
+ vm.quit();
80
+ });
81
+ ```
82
+
83
+ The `eventBus` also emits `emit` and `response` events for every text emission
84
+ and option registration, which can be useful for logging or debug UIs.
85
+
86
+ ## A note on preconditions
87
+
88
+ Context lookups with `getContext` throw if the key is not present, so always
89
+ initialize every variable you plan to read in the document's
90
+ `initial-context` (see the [language reference](../reference/language.md)).
91
+
92
+ ## Further reading
93
+
94
+ - [API reference](../reference/api.md) — exact signatures and types.
95
+ - [Tutorial: write your first conversation](../tutorials/first-conversation.md) —
96
+ the basics of the language.
97
+ - The repository's `test/` directory contains more complete examples of
98
+ driving a `QuestVM` programmatically.
package/docs/index.md ADDED
@@ -0,0 +1,61 @@
1
+ ---
2
+ layout: home
3
+
4
+ hero:
5
+ name: Questmark
6
+ text: Dialogue trees and hypertext fiction in Markdown
7
+ tagline: A language, compiler, interpreter, and TypeScript library for conversation trees and interactive fiction — built on the Markdown everyone already knows.
8
+ actions:
9
+ - theme: brand
10
+ text: Start the tutorial
11
+ link: /tutorials/first-conversation
12
+ - theme: alt
13
+ text: Why Questmark?
14
+ link: /explanation/why-questmark
15
+
16
+ features:
17
+ - icon: 📝
18
+ title: Markdown-native
19
+ details: Every properly-structured Markdown document with headers and links is already a Questmark document. Prose stays readable; behaviour is added with small backticked code snippets.
20
+ - icon: 🧭
21
+ title: Conversation trees
22
+ details: States and options model dialogue graphs — like Star Control 2 — and quests, hypertext fiction, text adventures, and visual novels — like Space Rangers 2.
23
+ - icon: 🧠
24
+ title: Context and side-effects
25
+ details: A context bag tracks what the player has done, gating options with preconditions and driving branching narrative and in-world side effects.
26
+ - icon: ⚙️
27
+ title: Compiles to Tzo bytecode
28
+ details: Documents compile to a portable Tzo VMState that can be stored, shipped, and interpreted in a game, a CLI, or a browser.
29
+ ---
30
+
31
+ ## Documentation
32
+
33
+ Questmark's docs follow the [Diátaxis](https://diataxis.fr/) framework, which
34
+ splits documentation into four complementary modes of use:
35
+
36
+ | Mode | Audience | Purpose | Start here |
37
+ | --- | --- | --- | --- |
38
+ | [Tutorials](tutorials/first-conversation) | Learners | Learning-oriented lessons that take you by the hand | [Your first conversation](tutorials/first-conversation) |
39
+ | [How-to guides](how-to/play-a-document) | Doers | Goal-oriented steps for solving real problems | [Play a document](how-to/play-a-document) |
40
+ | [Reference](reference/language) | Everyone | Information-oriented descriptions of the machinery | [Language reference](reference/language) |
41
+ | [Explanation](explanation/why-questmark) | Everyone | Understanding-oriented background and context | [Why Questmark?](explanation/why-questmark) |
42
+
43
+ ## Quick orientation
44
+
45
+ - **New to Questmark?** Start with the [tutorial](tutorials/first-conversation).
46
+ It takes about ten minutes and gets you writing and playing your first
47
+ conversation.
48
+ - **Trying to get something done?** The [how-to guides](how-to/play-a-document)
49
+ show you how to play a document, compile one to bytecode, and embed Questmark
50
+ in your own app.
51
+ - **Need a fact?** The [reference](reference/language) pages describe the
52
+ language, the CLI, and the library API precisely and completely.
53
+ - **Want to understand the design?** The [explanation](explanation/why-questmark)
54
+ pages cover why Questmark exists and how it works under the hood.
55
+
56
+ ## Related projects
57
+
58
+ - [Tzo](https://github.com/jorisvddonk/tzo) — the stack-machine bytecode and VM
59
+ that Questmark compiles to.
60
+ - [questmark-webrenderer](https://github.com/jorisvddonk/questmark-webrenderer) —
61
+ render and play Questmark documents in the browser.
@@ -0,0 +1,135 @@
1
+ # Questmark API reference
2
+
3
+ The library entry point (`src/index.ts`, built to `dist/node/`) exports the
4
+ compiler and the interpreter. All of the API below is available from the
5
+ package entry point (`import ... from "questmark"` / `require("questmark")`).
6
+
7
+ ## `parseMarkdown(fileContents: string)`
8
+
9
+ Compiles a Questmark document into a Tzo VM state.
10
+
11
+ **Returns** `{ parsedMDFile, qvmState }`
12
+
13
+ - `parsedMDFile` — the parsed document as a unist tree, with position
14
+ information removed.
15
+ - `qvmState` — a `TzoVMState` (see below) ready to load into a `QuestVM`.
16
+
17
+ **Throws** on malformed option headers (for example an unknown
18
+ `no_link_behaviour`) or Tzo tokenizer errors.
19
+
20
+ ## `QuestVM`
21
+
22
+ `class QuestVM extends VM` — an interpreter for compiled Questmark documents.
23
+ It extends Tzo's `VM` and adds the `emit`, `response`, and `getResponse`
24
+ opcodes that implement text output and choices.
25
+
26
+ ### Constructor
27
+
28
+ ```ts
29
+ new QuestVM(
30
+ emitFunction: (body: string | number) => void,
31
+ getResponseFunction: (choices: Choice[]) => Promise<number>,
32
+ additionalFunctions?: Functions
33
+ )
34
+ ```
35
+
36
+ - `emitFunction` — called by the `emit` opcode for each piece of text output.
37
+ - `getResponseFunction` — called by the `getResponse` opcode whenever the
38
+ document asks the player to choose. Receives the currently available options
39
+ and must return a `Promise` resolving to the chosen option's `id`.
40
+ - `additionalFunctions` — a map of extra Tzo opcode names to functions, merged
41
+ into the function table.
42
+
43
+ ### Events
44
+
45
+ `eventBus` is an `EventEmitter` with these events:
46
+
47
+ | Event | Payload | When |
48
+ | --- | --- | --- |
49
+ | `emit` | `string \| number` | Every text emission. |
50
+ | `response` | `{ response, pc }` | Every option registration. |
51
+ | `error` | `Error` | Runtime errors in the response flow. |
52
+
53
+ ### Inherited methods (from Tzo `VM`)
54
+
55
+ | Method | Description |
56
+ | --- | --- |
57
+ | `loadVMState(tzoVMState)` | Load a compiled VM state and prepare it for execution. |
58
+ | `run()` | Execute the program until the VM exits or suspends. |
59
+ | `quit()` | Mark the VM as exited. |
60
+ | `suspend()` | Pause execution (the `pause` opcode / `getResponse`). |
61
+ | `tick()` | Execute a single instruction. |
62
+
63
+ ### Properties
64
+
65
+ `stack`, `context`, `programList`, `labelMap`, `programCounter`, `exit`,
66
+ `pause` — the interpreter state.
67
+
68
+ ## `Choice`
69
+
70
+ ```ts
71
+ interface Choice {
72
+ title: string;
73
+ id: number;
74
+ }
75
+ ```
76
+
77
+ An option offered to the player. `id` is the option's positional index within
78
+ the current state's choice list.
79
+
80
+ ## `NoLinkBehaviour`
81
+
82
+ ```ts
83
+ enum NoLinkBehaviour {
84
+ EXIT = 0,
85
+ LOOPBACK_TO_OPTIONS = 1
86
+ }
87
+ ```
88
+
89
+ Behaviour when a chosen option has no link and no effect. Exported for host
90
+ applications; documents configure this via the header flag
91
+ `options.no_link_behaviour`.
92
+
93
+ ## `TzoVMState`
94
+
95
+ The compiled representation produced by `parseMarkdown` and consumed by
96
+ `loadVMState`:
97
+
98
+ ```ts
99
+ interface TzoVMState {
100
+ stack: any[];
101
+ context: { [key: string]: string | number };
102
+ programList: Instruction[];
103
+ labelMap: { [label: string]: number };
104
+ programCounter: number;
105
+ exit: boolean;
106
+ pause: boolean;
107
+ }
108
+ ```
109
+
110
+ ## `Tokenizer`
111
+
112
+ Re-exported from Tzo. Parses a string of Tzo source code into an array of
113
+ instructions:
114
+
115
+ ```ts
116
+ new Tokenizer().parse("1 2 +") // -> [push-number 1, push-number 2, invoke +]
117
+ ```
118
+
119
+ ## Full usage example
120
+
121
+ ```js
122
+ const { parseMarkdown, QuestVM } = require("questmark");
123
+ const { qvmState } = parseMarkdown(md);
124
+
125
+ const vm = new QuestVM(
126
+ (text) => console.log(text),
127
+ async (choices) => {
128
+ const index = await pickFrom(choices);
129
+ return choices[index].id;
130
+ },
131
+ {}
132
+ );
133
+ vm.loadVMState(qvmState);
134
+ vm.run();
135
+ ```
@@ -0,0 +1,66 @@
1
+ # Questmark CLI reference
2
+
3
+ The Questmark command line interface compiles and interprets Questmark
4
+ documents.
5
+
6
+ ## Usage
7
+
8
+ ```
9
+ questmark [options]
10
+ ```
11
+
12
+ Run it from a checkout with `npm start -- [options]`, or from the published
13
+ package with `npx questmark [options]`.
14
+
15
+ ## Options
16
+
17
+ | Option | Description |
18
+ | --- | --- |
19
+ | `-V, --version` | Print the version number. |
20
+ | `-c, --clear` | Clear the console on each state. *(Declared but not yet implemented.)* |
21
+ | `--input <path>` | Load a source `.md`, `.qmd`, `.qmd.html`, `.md.html`, or `.json` file. `path` can be a local filesystem path or an `http(s)://` URL. |
22
+ | `--output <path>` | Emit the compiled VMState as JSON to `path`. |
23
+ | `--no-run` | Do not interpret the input; only parse and (optionally) emit output. |
24
+ | `-h, --help` | Display help. |
25
+
26
+ `--input` is required. Without it the CLI prints `Missing input!` and exits
27
+ with status 1.
28
+
29
+ ## Examples
30
+
31
+ Play a local document:
32
+
33
+ ```bash
34
+ questmark --input examples/space_alien.md
35
+ ```
36
+
37
+ Compile a document to bytecode without running it:
38
+
39
+ ```bash
40
+ questmark --input examples/space_alien.md --output vmstate.json --no-run
41
+ ```
42
+
43
+ Play a compiled document:
44
+
45
+ ```bash
46
+ questmark --input vmstate.json
47
+ ```
48
+
49
+ Play a document hosted online:
50
+
51
+ ```bash
52
+ questmark --input https://example.com/document.md
53
+ ```
54
+
55
+ ## Exit codes
56
+
57
+ - `0` — success.
58
+ - `1` — missing `--input`, or a runtime/interpretation error.
59
+
60
+ ## Behaviour
61
+
62
+ - `.json` inputs are treated as pre-compiled VM states and interpreted directly.
63
+ - `.md`, `.qmd`, `.md.html`, and `.qmd.html` inputs are compiled with
64
+ `parseMarkdown` before interpretation.
65
+ - Any other extension is an error.
66
+ - Inputs beginning with `http://` or `https://` are fetched before processing.
@@ -0,0 +1,213 @@
1
+ # Questmark language reference
2
+
3
+ Questmark is a dialect of Markdown for dialogue trees and interactive fiction.
4
+ A document is a series of `#`-headed sections called **states**, connected by
5
+ links and code. The compiler turns the document into Tzo bytecode, which an
6
+ interpreter such as `QuestVM` executes.
7
+
8
+ ## Document structure
9
+
10
+ ```markdown
11
+ # QUESTMARK-OPTIONS-HEADER
12
+
13
+ { "questmark-spec": "1.0", "initial-state": "start" }
14
+
15
+ # start
16
+
17
+ Some text.
18
+
19
+ * An option
20
+ * Another option
21
+ ```
22
+
23
+ - The `QUESTMARK-OPTIONS-HEADER` section is optional. When present, its
24
+ indented code block must contain valid JSON (see below).
25
+ - Every other `# heading` defines a state.
26
+ - Text before the first list item is the state's text.
27
+ - List items are the state's **options**.
28
+
29
+ ## Options header
30
+
31
+ The `QUESTMARK-OPTIONS-HEADER` section configures the document. Its fields:
32
+
33
+ | Field | Type | Meaning |
34
+ | --- | --- | --- |
35
+ | `questmark-spec` | string | Spec version. Currently `"1.0"`. |
36
+ | `initial-state` | string | Name of the state to start in. |
37
+ | `initial-context` | object | Initial values for the context bag. |
38
+ | `options` | object | Interpreter behaviour flags, below. |
39
+
40
+ ### `options` flags
41
+
42
+ | Flag | Values | Default | Meaning |
43
+ | --- | --- | --- | --- |
44
+ | `no_link_behaviour` | `"loopback_to_options"`, `"exit"` | `"loopback_to_options"` | What happens when a chosen option has neither a link nor an `exit`/`goto` effect. |
45
+ | `whitespace` | `"retain"` | normal | Retain original source whitespace instead of the parsed text. |
46
+ | `emit_on` | `"end_of_line"` | `"classic"` | Only emit text when a line ends, instead of on every text node. |
47
+ | `strip_leading_newlines` | `true` | `false` | Strip leading newlines from emitted text. |
48
+
49
+ ## States
50
+
51
+ A state is a `#`-headed section. Its name is the heading text. `#` characters
52
+ inside the heading are removed from the name.
53
+
54
+ A state contains:
55
+
56
+ - **Text** — paragraphs shown to the player on entry.
57
+ - **Options** — the list of things the player can do.
58
+ - **Code** — backticked Tzo code (see below), executed in place.
59
+
60
+ ### State text and code
61
+
62
+ Plain paragraphs are emitted as text when the player enters the state.
63
+ Inline code in the text is executed as Tzo code at that point:
64
+
65
+ ```markdown
66
+ You have `"cookies" getContext emit` cookies!
67
+ ```
68
+
69
+ This pushes the value of `cookies`, then emits it, so the player sees
70
+ `You have 3 cookies!`.
71
+
72
+ A code block with language `comment` is ignored entirely and never reaches the
73
+ player or the interpreter:
74
+
75
+ ````markdown
76
+ ```comment
77
+ This is a note for the human writers. It is never executed or shown.
78
+ ```
79
+ ````
80
+
81
+ ## Options
82
+
83
+ Options are list items under a state's text. An option can have:
84
+
85
+ - a **text** (the label shown in the menu),
86
+ - a **link** (a `#state` target to jump to when chosen),
87
+ - **precondition** code (backticks before the text; the option is hidden when
88
+ the code leaves a non-positive value),
89
+ - a **directive** (backticks before the text starting with `@`),
90
+ - **effect** code (backticks after the text; executed when chosen),
91
+ - **post-option text** (paragraphs after the option line; emitted when chosen).
92
+
93
+ Examples:
94
+
95
+ ```markdown
96
+ * A simple statement with no effect
97
+ * [A link to another state](#other)
98
+ * A statement that quits when chosen `exit`
99
+ * `"key" getContext` A conditional option (hidden unless "key" is positive)
100
+ * `@once` An option that can only be chosen once
101
+ * `@once` `"a" getContext` Once, and only when "a" is positive
102
+ ```
103
+
104
+ When a player chooses an option, the interpreter:
105
+
106
+ 1. runs the option's effect code,
107
+ 2. emits the option's post-option text (interleaved with the effect),
108
+ 3. follows the option's link (`goto`),
109
+ 4. or, if there is no link and no effect, applies `no_link_behaviour`.
110
+
111
+ ### Preconditions
112
+
113
+ Backticked code before an option's text is a precondition. The interpreter
114
+ executes it and offers the option only if the code leaves a value greater than
115
+ zero on the stack. Preconditions are how you build branching, story-aware menus
116
+ (see [context](#context)).
117
+
118
+ Note that `getContext` throws if the key is not present in the context, so
119
+ every key read in a precondition should be initialized in `initial-context`.
120
+
121
+ ### The `@once` directive
122
+
123
+ An option marked `@once` is only offered once per play session. After the
124
+ player chooses it, it disappears from the menu. The interpreter implements this
125
+ with an internal context key (`__once__N`).
126
+
127
+ ## Context
128
+
129
+ The **context** is a bag of named variables (numbers and strings) that tracks
130
+ what the player has done. It is initialized from `initial-context` and mutated
131
+ by effect code.
132
+
133
+ Common patterns:
134
+
135
+ ```markdown
136
+ `1 "door_open" setContext` ; store the number 1 under "door_open"
137
+ `"door_open" getContext` ; push the value of "door_open"
138
+ `"door_open" hasContext` ; push 1 if the key exists, else 0
139
+ `"door_open" delContext` ; remove the key
140
+ ```
141
+
142
+ Use the `emit` opcode to show a context value in text, and `getContext` in a
143
+ precondition to gate options on it.
144
+
145
+ ## Tzo code
146
+
147
+ Code inside backticks is Tzo bytecode written in reverse-Polish order: operands
148
+ first, operator last. Strings are double-quoted. Example:
149
+
150
+ ```
151
+ "NORMAL_HELLO_" 2 randInt 65 + charCode rconcat goto
152
+ ```
153
+
154
+ ### Opcodes
155
+
156
+ The standard opcode set (provided by Tzo and QuestVM):
157
+
158
+ | Opcode | Stack effect | Description |
159
+ | --- | --- | --- |
160
+ | `emit` | `(value)` | Emit text/number to the player. |
161
+ | `exit` | | End the conversation. |
162
+ | `goto` | `(label \| pc)` | Jump to a label or program position. |
163
+ | `pause` | | Suspend the VM. |
164
+ | `getContext` | `(key) -> value` | Push the value of a context key (throws if absent). |
165
+ | `hasContext` | `(key) -> 0 \| 1` | Push whether the key exists. |
166
+ | `setContext` | `(key value)` | Store `value` under `key`. |
167
+ | `delContext` | `(key)` | Remove `key` from the context. |
168
+ | `eq` | `(a b) -> 0 \| 1` | Push `1` if `a == b`. |
169
+ | `gt` | `(a b) -> 0 \| 1` | Push `1` if `a > b`. |
170
+ | `lt` | `(a b) -> 0 \| 1` | Push `1` if `a < b`. |
171
+ | `and` | `(a b) -> 0 \| 1` | Logical and. |
172
+ | `or` | `(a b) -> 0 \| 1` | Logical or. |
173
+ | `not` | `(a) -> 0 \| 1` | Logical not. |
174
+ | `jgz` | `(a)` | Skip the next instruction if `a > 0`. |
175
+ | `jz` | `(a)` | Skip the next instruction if `a == 0`. |
176
+ | `+` / `plus` | `(a b) -> a+b` | Addition. |
177
+ | `-` / `min` | `(a b) -> a-b` | Subtraction. |
178
+ | `*` / `mul` | `(a b) -> a*b` | Multiplication. |
179
+ | `concat` | `(a b) -> "a"+"b"` | Concatenate (topmost value first). |
180
+ | `rconcat` | `(a b) -> "b"+"a"` | Concatenate (topmost value last). |
181
+ | `dup` | `(a) -> a a` | Duplicate the top of stack. |
182
+ | `pop` | `(a)` | Discard the top of stack. |
183
+ | `stacksize` | `() -> n` | Push the stack depth. |
184
+ | `randInt` | `(max) -> 0..max-1` | Push a random integer. |
185
+ | `charCode` | `(n) -> char` | Push the character for code point `n`. |
186
+ | `nop` | | Do nothing. |
187
+ | `{` / `}` | | Block delimiters used by `jgz`/`jz` to skip code. |
188
+ | `ppc` | `() -> pc` | Push the current program counter (used internally). |
189
+
190
+ The QuestVM adds the choice machinery, used by the compiler internally:
191
+
192
+ | Opcode | Description |
193
+ | --- | --- |
194
+ | `response` | Register the current option's effect position as a choice. |
195
+ | `getResponse` | Suspend the VM and ask the host for a choice. |
196
+
197
+ `optionEnabled`, `optionDisabled`, `disableOption`, and `enableOption` are
198
+ deprecated and should not be used.
199
+
200
+ Functions not registered in the VM are an error at runtime, unless their name
201
+ starts with `_`, in which case they are silently treated as a no-op (handy as
202
+ markers or hooks for host applications).
203
+
204
+ ## Limitations and notes
205
+
206
+ - The spec is still informal, and the feature set is still evolving.
207
+ - `getContext` on an uninitialized key throws; initialize keys up front.
208
+ - An option that has neither a link nor an effect loops back to the menu by
209
+ default (`loopback_to_options`), which can appear to loop forever if the
210
+ option is always available. Set `no_link_behaviour` to `"exit"` or give the
211
+ option a link/effect if that's not what you want.
212
+ - Comments inside option text are not yet supported by the interpreter; a
213
+ backtick in an option label produces a warning.