@the_dissidents/libemmm 0.0.1 → 0.0.3

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.
package/README.md CHANGED
@@ -1,6 +1,287 @@
1
- # emmm
2
- [![Node.js CI](https://github.com/the-dissidents/emmm/actions/workflows/node.js.yml/badge.svg)](https://github.com/the-dissidents/emmm/actions/workflows/node.js.yml)
1
+ # libemmm
3
2
 
4
- > *Legible, simple, consistent and extensible*
3
+ This package contains the parser and language server for the `emmm` markup language. Will include renderers in the future.
5
4
 
6
- A better markup language for typesetting articles.
5
+ ```sh
6
+ npm install @the_dissidents/libemmm
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ `emmm` is an extensible language. The parser by itself only handles the basic syntax; it accepts a `Configuration` object that defines most of the features.
12
+
13
+ ```typescript
14
+ import * as emmm from '@the_dissidents/libemmm';
15
+ let config = new emmm.Configuration(emmm.BuiltinConfiguration);
16
+ // add definitions to config here
17
+ ```
18
+
19
+ The parser reads from a very simple scanner interface that only goes forward, without backtracking. Usually you can use the default implementation. The parser returns a `Document` object.
20
+
21
+ ```typescript
22
+ let scanner = new emmm.SimpleScanner(source);
23
+ let doc = emmm.parse(scanner, config);
24
+ ```
25
+
26
+ - `doc.root` is the AST root node.
27
+ - `doc.context` is a `ParseContext` object containing the state of the language that the extensions need to know, such as variables and modifier definitions. This is its state at the end of the parse.
28
+ - `doc.messages` is the array of diagnostic messages.
29
+ - You may want to call `doc.debugPrint(source)` to get a pretty-printed debug string of the AST.
30
+
31
+ ## A Semi-Technical Reference to `emmm` Syntax
32
+
33
+ ![AST Structure](./doc-images/ast.svg)
34
+
35
+ Block-level entities are usually separated by a blank line (two newline characters). One newline does not create a new block, and is preserved along with other whitespaces inside the block.
36
+
37
+ ```
38
+ This is a paragraph.
39
+
40
+ This is another paragraph.
41
+ Still in the same paragraph, but after a newline.
42
+ ```
43
+
44
+ A block that is not modified can be either a **paragraph** or a **preformatted block**, depending on the modifier that encloses it; if there is no modifier enclosing it, it is a normal paragraph. The contents of preformatted blocks are treated as plain text. No parsing of modifiers and escape sequences is performed. Whitespaces and newlines at the beginning of a block is usually ignored, but in preformatted blocks, only the first newline (if any) is ignored.
45
+
46
+ The construct `[.foo]` or `[.foo args]` before a block signals a **block modifier**, with `args` being an optional `:`-separated list of arguments (more on that later). It always starts a new block, even when at a position normally not expected to do so (but this will trigger a warning).
47
+
48
+ Some block modifiers don't accept any content. For those that accept, their scope is limited to *the immediately following block*, unless a pair of brackets (`:--` and `--:`) is used to group blocks together.
49
+
50
+ ```
51
+ [.foo] This is under foo (whitespace after ] is optional).
52
+ This the second line of the same paragraph under foo.
53
+
54
+ [.foo]
55
+ This is another block under foo. Note that the initial newline is ignored.
56
+
57
+ This paragraph is NOT under foo, [.foo] but this immediately starts a new one under foo (expect a warning here).
58
+
59
+ [.foo] Similarly, this is a block of foo ...
60
+ [.foo] ... and this is another block of foo.
61
+
62
+ [.foo]
63
+ [.foo] However, this is foo inside foo, since the outer foo hadn't encountered any block before the parser met the inner foo, which became the content of the outer one.
64
+
65
+ [.foo]
66
+ :--
67
+ Use brackets to group together multiple blocks:
68
+
69
+ This is still in foo.
70
+
71
+ [.foo] This foo is also in foo.
72
+
73
+ [.foo] :--
74
+ You can have nested brackets. Not exactly beautiful looking, though.
75
+
76
+ Note that closing brackets have to be on its own line, but the opening ones do not. But you must have a newline after it.
77
+ --:
78
+ --:
79
+ ```
80
+
81
+ You can also use the brackets without a modifier. However, this has little effect.
82
+
83
+ Suppose the modifier `[.pre]` accepts a preformatted block:
84
+
85
+ ```
86
+ [.pre] Preformatted content, suitable for code and ASCII art. Always treated as plain text, even if I write [.foo] or [/foo] or \[.
87
+
88
+ However, like in a normal paragraph, a blank line creates a new block so this is no longer in the pre. Use brackets:
89
+
90
+ [.pre]
91
+ :--
92
+ export { DebugLevel } from './debug';
93
+
94
+ export function setDebugLevel(level: DebugLevel) {
95
+ debug.level = level;
96
+ }
97
+ --:
98
+ ```
99
+
100
+ Use a `;` before `]` to signify empty content. Modifiers that don't accept content can also be written with `;]`, but this is not required.
101
+
102
+ ```
103
+ [.foo;]
104
+ [.pre;]
105
+ ```
106
+
107
+ In normal paragraphs, use a slash `\` to **escape** the character immediately after it, so that it will not be interpreted as a special character (e.g. the beginning of a modifier).
108
+
109
+ **Inline modifiers** are similar to block modifiers, but occur in paragraphs. They are written as `[/baa]` or `[/baa args]`. If accepting content, use `[;]` to mark the end of their scope.
110
+
111
+ ```
112
+ Behold a baa: [/baa]content of baa[;].
113
+ This one is without content: [/baa;].
114
+ Baa inside a baa: [/baa]one [/baa]two[;] three[;].
115
+ ```
116
+
117
+ Some modifiers **expand** to something. For example, the built-in inline modifier `[/$]` expands to the value of a variable.
118
+
119
+ **System modifiers** are very similar to block modifiers in terms of parsing, except they begins with `[-` and never expand to anything. They modify the state of the `ParseContent`, e.g. assigning variables or creating new modifiers.
120
+
121
+ > The AST definiton specifies that `SystemModifierNode`s can appear as either block-level or inline-level entities. The reason behind this is that we may want them to appear inside `[-define-inline]` definitions and thus expanding into inline entities:
122
+ > ```
123
+ > [-define-inline foo]
124
+ > :--
125
+ > [-var xyz:123]
126
+ > xyz is now 123
127
+ > --:
128
+ > ```
129
+ > However, in parsing they are treated only as block-level modifiers, meaning that it's not supported to use them inline *directly*. Also note that inside `[-define-inline]` definitions they are still technically distinct blocks, only transformed into inline entities at expand time. **This is indeed awkward. We will change it if we think of a better approach.**
130
+
131
+ The **arguments** for modifiers are basically `:`-delimited sequences. Each argument can contain **interpolations**, whose syntaxes are defined by an opening string and a closing string (there isn't a fixed form). For example, the built-in interpolator for variable reference opens with `$(` and closes with `)`. Interpolations expand to plain strings. They can also be nested.
132
+
133
+ As in paragraphs, use `\` to **escape** characters in arguments.
134
+
135
+ ```
136
+ [/baa anything can be arguments:they can even
137
+ span
138
+ many
139
+
140
+ lines:but colons (\:), semicolons (\;) and square brackets (\[\]) need escaping;]
141
+
142
+ Suppose the variables are "x" = "y", "y" = "1".
143
+
144
+ [.foo $(x)] Argument is "y"
145
+ [.foo $(x)$(y)] Argument is "y1"
146
+ [.foo $($(x))] Argument is "1"
147
+ [.foo $(invalid)] Will fail
148
+ ```
149
+
150
+ A colon before the first argument states explicitly the beginning of that argument, so that any following whitespaces are not trimmed. In fact, it is not even required to have *any* whitespaces after the modifier name, and the built-in `[/$]` makes use of this (you can write `[/$myvar]` instead of `[/$ myvar]`). However, omitting the space in most other cases is, obviously, not recommended.
151
+
152
+ ```
153
+ [.foo abc] Argument is "abc"
154
+ [.foo: abc] Argument is " abc"
155
+ [.fooabc] Argument is "abc" (argh!)
156
+ ```
157
+
158
+ ## A Synopsis of the Built-in Configuration
159
+
160
+ ### System modifiers
161
+
162
+ [**-define-block** *name*:*args...*]
163
+ [**-define-block** *name*:*args...*:(*slot*)]
164
+ [**-define-inline** *name*:*args...*]
165
+ [**-define-inline** *name*:*args...*:(*slot*)]
166
+
167
+ > Define a new modifier. The first argument is the name. If more than one argument exist, and the last is enclosed in `()`, it is taken as the **slot name** (more on that later). The rest in the middle are names for the arguments.
168
+ >
169
+ > Take content as the definition of the new modifier.
170
+
171
+ [**-var** *id*:*value*]
172
+
173
+ > Assigns `value` to a variable.
174
+ >
175
+ > You can't reassign arguments, only variables. Since arguments always take precedence over variables, "reassigning" them has no effect inside a definition and can only confuse the rest of the code.
176
+
177
+ [**-define-block-prefix** *prefix*]
178
+ [**-define-block-prefix** *prefix*:(*slot*)]
179
+
180
+ > Not implemented yet
181
+
182
+ [**-define-inline-shorthand** *prefix*]
183
+ [**-define-inline-shorthand** *prefix*:(*slot*):*postfix*]
184
+ [**-define-inline-shorthand** *prefix*:*arg1*:*mid1*:*arg2*:*mid2*...]
185
+ [**-define-inline-shorthand** *prefix*:*arg1*:*mid1*:*arg2*:*mid2*...:(*slot*):*postfix*]
186
+
187
+ > Defines an inline shorthand. A shorthand notation consists of a prefix, zero or more pairs of argument and middle part, and optionally a slot and a postfix. You must specify a slot name if you want to use one, although you can specify an empty one using `()`. You may also specify an *empty* last argument, i.e. a `:` before the `]` that ends the modifier head, to make the postfix stand out better.
188
+ > ```
189
+ > [-inline-shorthand:\[!:url:|:():\]:] content
190
+ > ```
191
+ > This creates: `[!` argument:url `|` slot `]`
192
+ > ```
193
+ > [-inline-shorthand:\[!:url:|:text:\]:] content
194
+ > ```
195
+ > This creates: `[!` argument:url `|` argument:text `]`
196
+ >
197
+ > Note the first shorthand has a slot, while the second doesn't. This means you can't put formatted content as text in the second shorthand.
198
+
199
+ [**-use** *module-name*]
200
+
201
+ > Activates the definitions in a module for the whole document; see `[.use]`.
202
+
203
+ ### Block modifiers
204
+
205
+ [**.slot**]
206
+ [**.slot** *name*]
207
+
208
+ > Only used in block modifier definitons. When the new modifier is being used, expands to its content. You can use the slot name to specify *which* modifier's content you mean, in case of ambiguity. By default it refers to the nearest one.
209
+ > ```
210
+ > [-define-block p:(0)]
211
+ > [-define-block q]
212
+ > :--
213
+ > [.slot]
214
+ > [.slot 0]
215
+ > --:
216
+ >
217
+ > This expands to nothing but defines the block modifier q:
218
+ > [.p] 123
219
+ >
220
+ > This expands to two paragraphs containing "456" and "123":
221
+ > [.q] 456
222
+ > ```
223
+ > Note the first, unnamed `.slot` refers to the slot of `q`.
224
+
225
+ [**.module** *module-name*]
226
+
227
+ > Causes the definitions in the content to become part of a **module**. They don't take effect outside the `[.module]` modifier *unless* activated by a `[.use]` or `[-use]` modifier.
228
+
229
+ [**.use** *module-name*]
230
+
231
+ > Activates the definitions in a module for the content *within this modifier*. Use `[-use]` to activate for the whole document instead.
232
+
233
+ ### Inline modifiers
234
+
235
+ [**/slot**]
236
+ [**/slot** *name*]
237
+
238
+ > Same as `[.slot]`, but for inline modifier definitions.
239
+
240
+ [**/$** *id*]
241
+
242
+ > Expands to the value of an argument or a variable. Arguments *always* take precedence over variables.
243
+
244
+ [**/print** *argument...*]
245
+
246
+ > Expands to the value of the arguments, separated by nothing, as plain text.
247
+
248
+ ### Interpolators
249
+
250
+ **$(** *varid* **)**
251
+
252
+ > Same as `[/$varid]`, but as an interpolation.
253
+
254
+ **$eval(** *expression* **)**
255
+
256
+ > Not implemented yet
257
+
258
+ ---
259
+
260
+ For strange edge cases of the basic syntax and the built-in configuration, see the test modules. Note that they may not be *very* readable; we're considering moving to another format.
261
+
262
+ ## Diagnostic Messages
263
+
264
+ |Code|Error|Suggestions|
265
+ |---:|-----|-|
266
+ | 1 | Syntax error: expecting <...>
267
+ | 2 | Undefined modifier <...> | *did you mean ... ?*
268
+ | | | *did you forget to escape?*
269
+ | 3 | Unclosed inline modifier
270
+ | 4 | Argument count mismatch, <...> expected
271
+ | 5 | Failed to expand argument
272
+ | 6 | Invalid argument
273
+ | 7 | Invalid entity in inline modifier definition
274
+ | 8 | Reached recursion limit (<...>)
275
+ | 9 | Slot modifier used outside a definition
276
+ | 10 | Nested module definitions not allowed
277
+ | 11 | Cannot use the same module inside its definition
278
+ | 12 | A definition cannot be at once normal and preformatted
279
+
280
+ |Code|Warning|Suggestions|
281
+ |---:|-------|-|
282
+ | 1 |Unnecessary newline(s)| *remove*
283
+ | ~~2~~ | ~~Block should begin in a new line to avoid confusion~~| ~~*add a line break*~~
284
+ | 3 | Content should begin in a new line to avoid confusion | *add a line break*
285
+ | 4 | Modifier already defined, overwriting
286
+ | 5 | Undefined variable, will expand to empty string
287
+ | 6 | Using this module will overwrite: <...>