1
+
Added:
---
2
+
Added:
3
+
Added:
## inclusion: always
4
+
Added:
5
+
Added:
# OCaml Org Mode Parser
6
+
Added:
7
+
Added:
## Project mission
8
+
Added:
9
+
Added:
Build a compact, high-quality OCaml library whose primary
10
+
Added:
responsibility is parsing Org Mode documents into a semantic, strongly
11
+
Added:
typed abstract syntax tree.
12
+
Added:
13
+
Added:
The library is intended to serve as infrastructure for downstream
14
+
Added:
consumers such as:
15
+
Added:
16
+
Added:
* HTML renderers;
17
+
Added:
* document-analysis tools;
18
+
Added:
* Patoline input support;
19
+
Added:
* formatters and printers maintained in separate libraries.
20
+
Added:
21
+
Added:
Keep the parser focused. Do not turn it into an Org execution
22
+
Added:
environment, editor engine, document renderer, or general
23
+
Added:
document-processing framework.
24
+
Added:
25
+
Added:
## Source of truth
26
+
Added:
27
+
Added:
The current official Org Mode syntax specification is the normative
28
+
Added:
reference for syntax and parsing behavior.
29
+
Added:
30
+
Added:
<https://orgmode.org/worg/org-syntax.html>
31
+
Added:
32
+
Added:
When the specification is ambiguous:
33
+
Added:
34
+
Added:
1. prefer the documented behavior of Org Mode;
35
+
Added:
2. prefer semantic usefulness over preservation of surface spelling;
36
+
Added:
3. prefer permissive recovery over rejecting the document;
37
+
Added:
4. document any deliberate simplification;
38
+
Added:
5. add a fixture that demonstrates the chosen behavior.
39
+
Added:
40
+
Added:
The target is practical coverage of approximately 80% of commonly used
41
+
Added:
Org syntax, not exact compatibility with every behavior of Emacs Org
42
+
Added:
Mode or `org-element`.
43
+
Added:
44
+
Added:
Do not claim exact Org compatibility unless it has been demonstrated
45
+
Added:
by tests.
46
+
Added:
47
+
Added:
## Core product principles
48
+
Added:
49
+
Added:
### One primary responsibility
50
+
Added:
51
+
Added:
The library parses Org syntax.
52
+
Added:
53
+
Added:
It must not:
54
+
Added:
55
+
Added:
* render HTML, LaTeX, Patoline, Markdown, or other output formats;
56
+
Added:
* evaluate Babel source blocks;
57
+
Added:
* execute inline source code;
58
+
Added:
* resolve or fetch external links;
59
+
Added:
* expand macros;
60
+
Added:
* evaluate table formulas;
61
+
Added:
* mutate or rewrite Org documents;
62
+
Added:
* provide editor-specific incremental parsing;
63
+
Added:
* depend on an Emacs process;
64
+
Added:
* reproduce the full Org runtime environment.
65
+
Added:
66
+
Added:
Downstream libraries may interpret or render the AST.
67
+
Added:
68
+
Added:
### Semantic AST
69
+
Added:
70
+
Added:
Prioritize semantic meaning over exact source representation.
71
+
Added:
72
+
Added:
The parser does not need to be lossless. It may normalize
73
+
Added:
insignificant syntax such as:
74
+
Added:
75
+
Added:
* redundant blank lines;
76
+
Added:
* line endings;
77
+
Added:
* ordinary physical newlines within paragraphs;
78
+
Added:
* equivalent keyword capitalization where Org treats it as
79
+
Added:
insignificant.
80
+
Added:
81
+
Added:
Preserve text exactly where whitespace is semantically meaningful,
82
+
Added:
including:
83
+
Added:
84
+
Added:
* source block bodies;
85
+
Added:
* example block bodies;
86
+
Added:
* export block bodies;
87
+
Added:
* comment block bodies;
88
+
Added:
* code objects;
89
+
Added:
* verbatim objects;
90
+
Added:
* fixed-width content.
91
+
Added:
92
+
Added:
The AST is initially read-only. Do not design around arbitrary AST
93
+
Added:
editing or byte-for-byte serialization.
94
+
Added:
95
+
Added:
### Small public API
96
+
Added:
97
+
Added:
Keep the public API narrow and stable.
98
+
Added:
99
+
Added:
The initial public entry point should be conceptually equivalent to:
100
+
Added:
101
+
Added:
```ocaml
102
+
Added:
val parse : string -> Ast.document
103
+
Added:
```
104
+
Added:
105
+
Added:
A diagnostic API may also be provided:
106
+
Added:
107
+
Added:
```ocaml
108
+
Added:
val parse_with_diagnostics :
109
+
Added:
string -> Ast.document * Diagnostic.t list
110
+
Added:
```
111
+
Added:
112
+
Added:
Do not expose lexer tokens, raw parser nodes, parser stacks, mutable
113
+
Added:
cursors, or normalization internals unless a demonstrated use case
114
+
Added:
requires it.
115
+
Added:
116
+
Added:
### Strongly typed representation
117
+
Added:
118
+
Added:
Use closed OCaml variants and records for known Org constructs.
119
+
Added:
120
+
Added:
Prefer:
121
+
Added:
122
+
Added:
```ocaml
123
+
Added:
type element =
124
+
Added:
| Paragraph of inline list
125
+
Added:
| Plain_list of plain_list
126
+
Added:
| Table of table
127
+
Added:
| Drawer of drawer
128
+
Added:
| Block of block
129
+
Added:
| Keyword of keyword
130
+
Added:
| Comment of string
131
+
Added:
| Fixed_width of string list
132
+
Added:
| Horizontal_rule
133
+
Added:
```
134
+
Added:
135
+
Added:
Avoid generic stringly typed node maps such as:
136
+
Added:
137
+
Added:
```ocaml
138
+
Added:
type node = {
139
+
Added:
kind : string;
140
+
Added:
properties : (string * string) list;
141
+
Added:
children : node list;
142
+
Added:
}
143
+
Added:
```
144
+
Added:
145
+
Added:
Use ordinary strings only for genuinely open namespaces, including:
146
+
Added:
147
+
Added:
* custom block names;
148
+
Added:
* unknown keywords;
149
+
Added:
* property names;
150
+
Added:
* language identifiers;
151
+
Added:
* link protocols;
152
+
Added:
* user-defined TODO keywords.
153
+
Added:
154
+
Added:
The type system should make important structural distinctions visible
155
+
Added:
without making extensions impossible.
156
+
Added:
157
+
Added:
## Compatibility model
158
+
Added:
159
+
Added:
### Supported configuration
160
+
Added:
161
+
Added:
External Emacs configuration is unavailable and out of scope.
162
+
Added:
163
+
Added:
Start with the defaults documented by the Org syntax
164
+
Added:
specification. Allow supported file-local declarations to override
165
+
Added:
those defaults.
166
+
Added:
167
+
Added:
Examples include file-local TODO declarations such as:
168
+
Added:
169
+
Added:
```org
170
+
Added:
#+TODO: TODO WAITING | DONE CANCELLED
171
+
Added:
```
172
+
Added:
173
+
Added:
Do not read:
174
+
Added:
175
+
Added:
* user Emacs configuration;
176
+
Added:
* directory-local Emacs variables;
177
+
Added:
* active minor modes;
178
+
Added:
* loaded Emacs packages;
179
+
Added:
* environment-specific Org variables.
180
+
Added:
181
+
Added:
### File-wide declarations
182
+
Added:
183
+
Added:
Syntax-affecting file declarations may occur anywhere in a document
184
+
Added:
and may affect constructs appearing before them.
185
+
Added:
186
+
Added:
Use a lightweight whole-file pre-scan to collect supported syntax
187
+
Added:
settings before the main parse.
188
+
Added:
189
+
Added:
This does not violate the single-grammar objective. The pre-scan only
190
+
Added:
discovers document configuration; it does not construct the document
191
+
Added:
AST.
192
+
Added:
193
+
Added:
Ordinary metadata keywords such as `#+TITLE` remain document elements
194
+
Added:
and must not disappear merely because the pre-scan inspected them.
195
+
Added:
196
+
Added:
### Tracking Org syntax
197
+
Added:
198
+
Added:
Target the latest published Org syntax rather than freezing
199
+
Added:
compatibility to an old Org release.
200
+
Added:
201
+
Added:
Changes to supported syntax must be:
202
+
Added:
203
+
Added:
* isolated in focused commits;
204
+
Added:
* covered by tests;
205
+
Added:
* recorded in release notes;
206
+
Added:
* assessed for AST compatibility.
207
+
Added:
208
+
Added:
Avoid speculative support for undocumented behavior.
209
+
Added:
210
+
Added:
## Parser architecture
211
+
Added:
212
+
Added:
### Menhir prototype
213
+
Added:
214
+
Added:
Use Menhir for the initial parser prototype.
215
+
Added:
216
+
Added:
Use its conventional deterministic LR parsing mode unless a concrete
217
+
Added:
grammar problem demonstrates that another mode is necessary.
218
+
Added:
219
+
Added:
Do not introduce GLR, Earley parsing, or generalized ambiguity
220
+
Added:
handling merely because Org has complex syntax. Org usually selects a
221
+
Added:
preferred interpretation through contextual and ordered recognition
222
+
Added:
rules rather than requiring multiple parse trees.
223
+
Added:
224
+
Added:
### OCaml-native dependencies
225
+
Added:
226
+
Added:
Dependencies are acceptable when they belong to the OCaml ecosystem
227
+
Added:
and do not rely on unstable external runtimes or system libraries.
228
+
Added:
229
+
Added:
Acceptable categories include:
230
+
Added:
231
+
Added:
* Menhir;
232
+
Added:
* pure-OCaml Unicode libraries;
233
+
Added:
* pure-OCaml test libraries;
234
+
Added:
* Dune tooling.
235
+
Added:
236
+
Added:
Avoid dependencies that require:
237
+
Added:
238
+
Added:
* C stubs, unless later explicitly approved;
239
+
Added:
* system-installed parsing libraries;
240
+
Added:
* an Emacs runtime;
241
+
Added:
* external language runtimes;
242
+
Added:
* network access during parsing;
243
+
Added:
* platform-specific services.
244
+
Added:
245
+
Added:
For the prototype, treat UTF-8 text as opaque OCaml
246
+
Added:
strings. Structural Org delimiters are ASCII. Do not add Unicode
247
+
Added:
classification until a supported production demonstrably requires it.
248
+
Added:
249
+
Added:
Never corrupt, reinterpret, or normalize non-ASCII byte sequences
250
+
Added:
unnecessarily.
251
+
Added:
252
+
Added:
### One grammar, multiple syntactic scales
253
+
Added:
254
+
Added:
Org contains two principal scales:
255
+
Added:
256
+
Added:
* structural elements such as headings, lists, blocks, drawers,
257
+
Added:
tables, and paragraphs;
258
+
Added:
* inline objects such as emphasis, links, timestamps, entities, code,
259
+
Added:
and verbatim text.
260
+
Added:
261
+
Added:
They may use separate Menhir start symbols or lexer modes while
262
+
Added:
remaining part of one parser implementation and one coherent grammar.
263
+
Added:
264
+
Added:
A suitable model is:
265
+
Added:
266
+
Added:
```menhir
267
+
Added:
%start <Raw_ast.document> document
268
+
Added:
%start <Ast.inline list> inline_document
269
+
Added:
```
270
+
Added:
271
+
Added:
Do not force block-level lines and inline punctuation through one
272
+
Added:
uniform token stream when that makes the grammar less understandable.
273
+
Added:
274
+
Added:
### Line-oriented structural lexer
275
+
Added:
276
+
Added:
Use a small line-oriented structural lexer.
277
+
Added:
278
+
Added:
Classify physical lines into high-level tokens such as:
279
+
Added:
280
+
Added:
```ocaml
281
+
Added:
type token =
282
+
Added:
| Blank
283
+
Added:
| Heading of int * string
284
+
Added:
| Keyword of string * string
285
+
Added:
| Begin_block of string * string option
286
+
Added:
| End_block of string
287
+
Added:
| Drawer_begin of string
288
+
Added:
| Drawer_end
289
+
Added:
| Property of string * string
290
+
Added:
| List_item of raw_list_item
291
+
Added:
| Table_row of string
292
+
Added:
| Fixed_width of string
293
+
Added:
| Comment_line of string
294
+
Added:
| Horizontal_rule
295
+
Added:
| Text_line of string
296
+
Added:
| Eof
297
+
Added:
```
298
+
Added:
299
+
Added:
The exact constructors may evolve during the design phase.
300
+
Added:
301
+
Added:
Keep lexical recognition conservative. A malformed or uncertain
302
+
Added:
construct should normally become `Text_line`, allowing it to survive
303
+
Added:
as paragraph text.
304
+
Added:
305
+
Added:
Do not place complex document hierarchy or semantic interpretation in
306
+
Added:
the lexer.
307
+
Added:
308
+
Added:
### Raw and semantic representations
309
+
Added:
310
+
Added:
It is acceptable and encouraged to distinguish:
311
+
Added:
312
+
Added:
1. a private raw parse representation;
313
+
Added:
2. the public semantic AST.
314
+
Added:
315
+
Added:
Menhir should parse syntactic ordering and grouping. A narrow
316
+
Added:
normalization phase may then handle relationships that are unsuitable
317
+
Added:
for direct LR productions.
318
+
Added:
319
+
Added:
Examples include:
320
+
Added:
321
+
Added:
* converting flat headings into a hierarchy;
322
+
Added:
* interpreting heading title components;
323
+
Added:
* attaching planning data to headings;
324
+
Added:
* attaching property drawers to headings;
325
+
Added:
* nesting list items according to indentation;
326
+
Added:
* attaching affiliated keywords to eligible elements;
327
+
Added:
* joining paragraph lines;
328
+
Added:
* invoking inline parsing on bounded text fields.
329
+
Added:
330
+
Added:
The normalization phase must not become an undocumented second parser.
331
+
Added:
332
+
Added:
Every normalization rule must have:
333
+
Added:
334
+
Added:
* a clear invariant;
335
+
Added:
* focused unit tests;
336
+
Added:
* an explanation when the behavior is non-obvious.
337
+
Added:
338
+
Added:
### Whole-document parsing
339
+
Added:
340
+
Added:
Parse complete input strings.
341
+
Added:
342
+
Added:
Streaming and incremental reparsing are out of scope for the initial
343
+
Added:
library.
344
+
Added:
345
+
Added:
A channel-based convenience function may read the complete channel and
346
+
Added:
call the string parser, but do not expose a streaming guarantee.
347
+
Added:
348
+
Added:
## AST guidance
349
+
Added:
350
+
Added:
### Document structure
351
+
Added:
352
+
Added:
The semantic document should distinguish the content before the first
353
+
Added:
heading from the heading tree.
354
+
Added:
355
+
Added:
A representative shape is:
356
+
Added:
357
+
Added:
```ocaml
358
+
Added:
type document = {
359
+
Added:
directives : directive list;
360
+
Added:
preamble : element list;
361
+
Added:
headings : heading list;
362
+
Added:
}
363
+
Added:
```
364
+
Added:
365
+
Added:
The final field names should be chosen during design, but the
366
+
Added:
distinction must remain representable.
367
+
Added:
368
+
Added:
### Headings
369
+
Added:
370
+
Added:
A heading should expose semantic components rather than only its raw
371
+
Added:
title:
372
+
Added:
373
+
Added:
```ocaml
374
+
Added:
type heading = {
375
+
Added:
level : int;
376
+
Added:
todo : string option;
377
+
Added:
priority : char option;
378
+
Added:
commented : bool;
379
+
Added:
title : inline list;
380
+
Added:
tags : string list;
381
+
Added:
planning : planning option;
382
+
Added:
properties : property list;
383
+
Added:
contents : element list;
384
+
Added:
children : heading list;
385
+
Added:
}
386
+
Added:
```
387
+
Added:
388
+
Added:
Accept skipped heading levels permissively.
389
+
Added:
390
+
Added:
For example, a level-three heading following a level-one heading may
391
+
Added:
become a child of the most recent level-one heading even when no
392
+
Added:
level-two heading intervenes.
393
+
Added:
394
+
Added:
Do not reject a whole document because its hierarchy is
395
+
Added:
unconventional.
396
+
Added:
397
+
Added:
### Blocks
398
+
Added:
399
+
Added:
Distinguish blocks with materially different semantics.
400
+
Added:
401
+
Added:
Opaque-body blocks should include at least:
402
+
Added:
403
+
Added:
* source;
404
+
Added:
* example;
405
+
Added:
* export;
406
+
Added:
* comment.
407
+
Added:
408
+
Added:
Recursively parsed blocks should include at least:
409
+
Added:
410
+
Added:
* quote;
411
+
Added:
* center;
412
+
Added:
* custom blocks where appropriate.
413
+
Added:
414
+
Added:
Unknown `#+begin_NAME` forms should become `Custom_block`, not parser
415
+
Added:
errors.
416
+
Added:
417
+
Added:
Preserve source bodies as opaque strings. Do not parse embedded
418
+
Added:
programming languages.
419
+
Added:
420
+
Added:
### Affiliated keywords
421
+
Added:
422
+
Added:
Represent affiliated keywords as metadata attached to the element they
423
+
Added:
modify, rather than as unrelated siblings.
424
+
Added:
425
+
Added:
Do not attach an affiliated keyword across an intervening blank line
426
+
Added:
or in a context where Org would not treat it as affiliated.
427
+
Added:
428
+
Added:
Unknown ordinary keywords should remain representable as generic
429
+
Added:
keyword nodes.
430
+
Added:
431
+
Added:
### Planning and properties
432
+
Added:
433
+
Added:
Planning information and heading property drawers should be
434
+
Added:
represented directly on headings where possible.
435
+
Added:
436
+
Added:
Avoid constructing semantically impossible states, such as arbitrary
437
+
Added:
planning lines freely appearing inside every element type.
438
+
Added:
439
+
Added:
An ordinary named drawer remains a regular element.
440
+
Added:
441
+
Added:
### Inline text
442
+
Added:
443
+
Added:
Inline parsing should eventually distinguish at least:
444
+
Added:
445
+
Added:
* plain text;
446
+
Added:
* bold;
447
+
Added:
* italic;
448
+
Added:
* underline;
449
+
Added:
* strike-through;
450
+
Added:
* code;
451
+
Added:
* verbatim;
452
+
Added:
* links;
453
+
Added:
* timestamps;
454
+
Added:
* explicit line breaks.
455
+
Added:
456
+
Added:
Malformed inline markup should degrade to plain text.
457
+
Added:
458
+
Added:
Do not lose surrounding text merely because one inline construct is
459
+
Added:
malformed.
460
+
Added:
461
+
Added:
### Whitespace normalization
462
+
Added:
463
+
Added:
Unless a construct preserves literal text:
464
+
Added:
465
+
Added:
* blank lines terminate paragraphs but need not become AST nodes;
466
+
Added:
* multiple blank lines may be treated as equivalent;
467
+
Added:
* physical lines in one paragraph may be joined with one space;
468
+
Added:
* explicit Org line breaks remain explicit inline nodes;
469
+
Added:
* indentation significant to lists must be interpreted before
470
+
Added:
trimming.
471
+
Added:
472
+
Added:
Keep whitespace rules centralized rather than scattering `String.trim`
473
+
Added:
calls throughout the parser.
474
+
Added:
475
+
Added:
## Initial supported scope
476
+
Added:
477
+
Added:
The first useful release should aim to support the following.
478
+
Added:
479
+
Added:
### Structural syntax
480
+
Added:
481
+
Added:
* document preamble or zeroth section;
482
+
Added:
* heading hierarchy;
483
+
Added:
* file-local TODO declarations;
484
+
Added:
* heading TODO states;
485
+
Added:
* priorities;
486
+
Added:
* tags;
487
+
Added:
* planning information;
488
+
Added:
* property drawers;
489
+
Added:
* ordinary drawers;
490
+
Added:
* paragraphs;
491
+
Added:
* ordered lists;
492
+
Added:
* unordered lists;
493
+
Added:
* descriptive lists;
494
+
Added:
* source blocks;
495
+
Added:
* example blocks;
496
+
Added:
* export blocks;
497
+
Added:
* comment blocks;
498
+
Added:
* quote blocks;
499
+
Added:
* center blocks;
500
+
Added:
* verse blocks;
501
+
Added:
* custom blocks;
502
+
Added:
* keywords;
503
+
Added:
* affiliated keywords;
504
+
Added:
* Org tables;
505
+
Added:
* comments;
506
+
Added:
* fixed-width content;
507
+
Added:
* horizontal rules.
508
+
Added:
509
+
Added:
### Inline syntax
510
+
Added:
511
+
Added:
* plain text;
512
+
Added:
* bold;
513
+
Added:
* italic;
514
+
Added:
* underline;
515
+
Added:
* strike-through;
516
+
Added:
* code;
517
+
Added:
* verbatim;
518
+
Added:
* regular links;
519
+
Added:
* common plain links;
520
+
Added:
* angle links;
521
+
Added:
* active timestamps;
522
+
Added:
* inactive timestamps;
523
+
Added:
* timestamp ranges;
524
+
Added:
* explicit line breaks;
525
+
Added:
* basic entities or LaTeX fragments only after the core parser is
526
+
Added:
stable.
527
+
Added:
528
+
Added:
### Deferred features
529
+
Added:
530
+
Added:
Unless a spec explicitly promotes them into scope, defer:
531
+
Added:
532
+
Added:
* citations;
533
+
Added:
* radio targets;
534
+
Added:
* radio links;
535
+
Added:
* macros;
536
+
Added:
* complex footnotes;
537
+
Added:
* inline Babel calls;
538
+
Added:
* inline source blocks;
539
+
Added:
* diary sexps;
540
+
Added:
* inlinetasks;
541
+
Added:
* table formula interpretation;
542
+
Added:
* link resolution;
543
+
Added:
* entity expansion;
544
+
Added:
* Babel execution;
545
+
Added:
* language-specific source parsing;
546
+
Added:
* exact compatibility with `org-element`;
547
+
Added:
* incremental editor parsing;
548
+
Added:
* lossless printing.
549
+
Added:
550
+
Added:
Do not silently expand a feature spec to include deferred behavior.
551
+
Added:
552
+
Added:
## Permissive parsing and diagnostics
553
+
Added:
554
+
Added:
The parser should produce a useful best-effort tree for malformed
555
+
Added:
input.
556
+
Added:
557
+
Added:
Use deterministic fallback rules.
558
+
Added:
559
+
Added:
Examples:
560
+
Added:
561
+
Added:
| Input condition | Preferred result |
562
+
Added:
|-----------------------------|-------------------------------------------------------|
563
+
Added:
| Unknown ordinary line | Paragraph text |
564
+
Added:
| Malformed inline markup | Plain text |
565
+
Added:
| Unknown keyword | Generic keyword |
566
+
Added:
| Unknown begin block | Custom block |
567
+
Added:
| Invalid timestamp-like text | Plain text |
568
+
Added:
| Invalid list marker | Paragraph text |
569
+
Added:
| Isolated end-block marker | Paragraph or keyword-like text |
570
+
Added:
| Broken table row | End the table and parse the line normally |
571
+
Added:
| Unterminated opaque block | Preserve body through the appropriate boundary or EOF |
572
+
Added:
573
+
Added:
Where recovery loses likely structure, emit a warning diagnostic.
574
+
Added:
575
+
Added:
Diagnostics should initially be lightweight. Line numbers are
576
+
Added:
acceptable even though full source spans are out of scope.
577
+
Added:
578
+
Added:
Do not throw exceptions for ordinary malformed Org input.
579
+
Added:
580
+
Added:
Exceptions may be used for:
581
+
Added:
582
+
Added:
* violated internal invariants;
583
+
Added:
* impossible generated-parser states;
584
+
Added:
* programming errors;
585
+
Added:
* resource failures.
586
+
Added:
587
+
Added:
Public parsing functions should convert recoverable syntax problems
588
+
Added:
into AST nodes and diagnostics.
589
+
Added:
590
+
Added:
## Quality requirements
591
+
Added:
592
+
Added:
### Tests are part of the feature
593
+
Added:
594
+
Added:
Every supported syntax feature requires tests.
595
+
Added:
596
+
Added:
Use several levels of testing:
597
+
Added:
598
+
Added:
1. lexer classification tests;
599
+
Added:
2. grammar production tests;
600
+
Added:
3. normalization tests;
601
+
Added:
4. end-to-end document tests;
602
+
Added:
5. malformed-input recovery tests;
603
+
Added:
6. regression tests for every fixed bug.
604
+
Added:
605
+
Added:
Prefer small readable Org fixtures with explicit expected ASTs.
606
+
Added:
607
+
Added:
Use examples from the official syntax specification where licensing
608
+
Added:
and attribution permit, supplemented by original fixtures.
609
+
Added:
610
+
Added:
Do not base correctness only on snapshot tests. Important semantic
611
+
Added:
fields should be asserted explicitly.
612
+
Added:
613
+
Added:
### No premature performance engineering
614
+
Added:
615
+
Added:
Favor clear grammar and predictable behavior.
616
+
Added:
617
+
Added:
Avoid:
618
+
Added:
619
+
Added:
* repeated concatenation that is obviously quadratic;
620
+
Added:
* unrestricted backtracking;
621
+
Added:
* repeated whole-document rescans beyond the deliberate configuration
622
+
Added:
pre-scan;
623
+
Added:
* regex-heavy designs that obscure grammar rules.
624
+
Added:
625
+
Added:
Do not add complex caching, streaming, mutable arenas, or specialized
626
+
Added:
buffers without profiling.
627
+
Added:
628
+
Added:
Expected parsing complexity should normally be linear in input size,
629
+
Added:
apart from clearly bounded local analyses.
630
+
Added:
631
+
Added:
### Maintainability
632
+
Added:
633
+
Added:
Keep modules focused.
634
+
Added:
635
+
Added:
The module organization is:
636
+
Added:
637
+
Added:
```text
638
+
Added:
lib/
639
+
Added:
ast.ml (* Public semantic AST types *)
640
+
Added:
ast.mli
641
+
Added:
raw_ast.ml (* Private raw parse tree from Menhir *)
642
+
Added:
config.ml (* Document config from prescan *)
643
+
Added:
prescan.ml (* Pre-scan for TODO keywords etc. *)
644
+
Added:
lexer.ml (* Handwritten structural line lexer *)
645
+
Added:
parser.mly (* Menhir structural grammar *)
646
+
Added:
inline_lexer.ml (* Handwritten inline lexer *)
647
+
Added:
inline_parser.mly (* Menhir inline grammar *)
648
+
Added:
parse_bridge.ml (* Bridges lexers to Menhir parsers *)
649
+
Added:
normalize.ml (* Raw_ast → Ast transformation *)
650
+
Added:
diagnostic.ml (* Diagnostic types *)
651
+
Added:
org.ml (* Public API entry points *)
652
+
Added:
test/
653
+
Added:
test_main.ml (* All tests: lexer, parser, inline, integration *)
654
+
Added:
fixtures/ (* .org fixture files *)
655
+
Added:
```
656
+
Added:
657
+
Added:
This layout may evolve when the design discovers a clearer separation.
658
+
Added:
659
+
Added:
Avoid circular module dependencies.
660
+
Added:
661
+
Added:
Keep private types private through `.mli` files or Dune module
662
+
Added:
visibility.
663
+
Added:
664
+
Added:
Use descriptive names based on Org terminology.
665
+
Added:
666
+
Added:
### OCaml style
667
+
Added:
668
+
Added:
Use current stable OCaml and Dune conventions.
669
+
Added:
670
+
Added:
Prefer:
671
+
Added:
672
+
Added:
* algebraic data types;
673
+
Added:
* exhaustive pattern matching;
674
+
Added:
* immutable values;
675
+
Added:
* total helper functions where practical;
676
+
Added:
* explicit result types at module boundaries;
677
+
Added:
* small pure normalization functions;
678
+
Added:
* local mutation only when it clearly simplifies stack or cursor
679
+
Added:
processing.
680
+
Added:
681
+
Added:
Avoid:
682
+
Added:
683
+
Added:
* objects without a demonstrated need;
684
+
Added:
* polymorphic variants for the central closed AST;
685
+
Added:
* global mutable parser state;
686
+
Added:
* exceptions as ordinary control flow;
687
+
Added:
* unsafe operations;
688
+
Added:
* PPX dependencies unless explicitly justified;
689
+
Added:
* clever type-level machinery that makes the library harder to
690
+
Added:
consume.
691
+
Added:
692
+
Added:
Warnings should be treated seriously. New code should compile without
693
+
Added:
avoidable warnings.
694
+
Added:
695
+
Added:
### Documentation
696
+
Added:
697
+
Added:
Document:
698
+
Added:
699
+
Added:
* the public AST;
700
+
Added:
* parsing entry points;
701
+
Added:
* normalization rules visible to consumers;
702
+
Added:
* supported syntax;
703
+
Added:
* known deviations from Org;
704
+
Added:
* recovery behavior;
705
+
Added:
* version compatibility.
706
+
Added:
707
+
Added:
Do not document private implementation details as public guarantees.
708
+
Added:
709
+
Added:
Maintain a concise support matrix showing:
710
+
Added:
711
+
Added:
* supported;
712
+
Added:
* partially supported;
713
+
Added:
* deferred;
714
+
Added:
* intentionally unsupported.
715
+
Added:
716
+
Added:
## Kiro spec-driven workflow
717
+
Added:
718
+
Added:
### Use one coherent feature per spec
719
+
Added:
720
+
Added:
Each spec should address a bounded capability, such as:
721
+
Added:
722
+
Added:
* document and heading parsing;
723
+
Added:
* paragraph and inline text parsing;
724
+
Added:
* block parsing;
725
+
Added:
* drawer and property parsing;
726
+
Added:
* list parsing;
727
+
Added:
* table parsing;
728
+
Added:
* affiliated keywords;
729
+
Added:
* timestamps;
730
+
Added:
* diagnostics and recovery.
731
+
Added:
732
+
Added:
Do not create one enormous implementation spec for the entire Org
733
+
Added:
language.
734
+
Added:
735
+
Added:
Specs may depend on earlier foundations, but each should leave the
736
+
Added:
repository compiling and tested.
737
+
Added:
738
+
Added:
### Requirements phase
739
+
Added:
740
+
Added:
Write requirements in testable terms.
741
+
Added:
742
+
Added:
Each requirement should state:
743
+
Added:
744
+
Added:
* the syntax recognized;
745
+
Added:
* the resulting semantic representation;
746
+
Added:
* the relevant context restrictions;
747
+
Added:
* normalization behavior;
748
+
Added:
* malformed-input fallback;
749
+
Added:
* exclusions.
750
+
Added:
751
+
Added:
Use EARS-style acceptance criteria where useful.
752
+
Added:
753
+
Added:
Example:
754
+
Added:
755
+
Added:
> WHEN the parser encounters a heading whose first title token matches
756
+
Added:
> a TODO keyword declared anywhere in the same file, THE parser SHALL
757
+
Added:
> store that token as the heading TODO state.
758
+
Added:
759
+
Added:
Avoid vague requirements such as:
760
+
Added:
761
+
Added:
> The parser should handle headings correctly.
762
+
Added:
763
+
Added:
Explicitly identify deferred cases instead of leaving them ambiguous.
764
+
Added:
765
+
Added:
### Design phase
766
+
Added:
767
+
Added:
The design must explain:
768
+
Added:
769
+
Added:
* relevant AST additions;
770
+
Added:
* lexer tokens;
771
+
Added:
* Menhir productions;
772
+
Added:
* pre-scan behavior;
773
+
Added:
* normalization steps;
774
+
Added:
* recovery behavior;
775
+
Added:
* module boundaries;
776
+
Added:
* test strategy;
777
+
Added:
* compatibility implications.
778
+
Added:
779
+
Added:
Before introducing a new dependency, explain:
780
+
Added:
781
+
Added:
* why existing tools are insufficient;
782
+
Added:
* whether it is pure OCaml;
783
+
Added:
* whether it adds runtime or build-time coupling;
784
+
Added:
* whether it expands the public API.
785
+
Added:
786
+
Added:
Do not redesign unrelated modules during a feature unless the
787
+
Added:
requirement genuinely demands it.
788
+
Added:
789
+
Added:
### Task phase
790
+
Added:
791
+
Added:
Break implementation into small verifiable tasks.
792
+
Added:
793
+
Added:
A normal feature task sequence should resemble:
794
+
Added:
795
+
Added:
1. add or refine private raw types;
796
+
Added:
2. add lexer recognition;
797
+
Added:
3. add grammar productions;
798
+
Added:
4. add normalization;
799
+
Added:
5. add public AST exposure if needed;
800
+
Added:
6. add positive tests;
801
+
Added:
7. add malformed-input tests;
802
+
Added:
8. update support documentation.
803
+
Added:
804
+
Added:
Every implementation task should name:
805
+
Added:
806
+
Added:
* the expected files or modules;
807
+
Added:
* the behavior added;
808
+
Added:
* the tests that verify completion.
809
+
Added:
810
+
Added:
Do not mark a task complete while tests fail or the implementation
811
+
Added:
only contains placeholders.
812
+
Added:
813
+
Added:
### During implementation
814
+
Added:
815
+
Added:
Follow the accepted requirements and design.
816
+
Added:
817
+
Added:
When implementation reveals a design flaw:
818
+
Added:
819
+
Added:
1. stop expanding the workaround;
820
+
Added:
2. identify the inconsistency;
821
+
Added:
3. update the design or requirement;
822
+
Added:
4. then continue implementation.
823
+
Added:
824
+
Added:
Do not silently diverge from the spec.
825
+
Added:
826
+
Added:
Run focused tests after each task and the full suite before
827
+
Added:
considering the spec complete.
828
+
Added:
829
+
Added:
Keep commits and patches scoped to the active task.
830
+
Added:
831
+
Added:
### Definition of done
832
+
Added:
833
+
Added:
A feature is complete only when:
834
+
Added:
835
+
Added:
* requirements are implemented;
836
+
Added:
* the AST representation is documented;
837
+
Added:
* valid examples parse as specified;
838
+
Added:
* malformed examples recover as specified;
839
+
Added:
* tests pass;
840
+
Added:
* no deferred feature was accidentally introduced;
841
+
Added:
* no unnecessary dependency was added;
842
+
Added:
* public API changes are intentional;
843
+
Added:
* the syntax support matrix is updated.
844
+
Added:
845
+
Added:
## Decision-making rules
846
+
Added:
847
+
Added:
When several implementations are possible, prefer them in this order:
848
+
Added:
849
+
Added:
1. correct semantic behavior;
850
+
Added:
2. clear representation of Org concepts;
851
+
Added:
3. permissive and deterministic recovery;
852
+
Added:
4. small and stable public API;
853
+
Added:
5. maintainable grammar;
854
+
Added:
6. pure-OCaml portability;
855
+
Added:
7. acceptable performance;
856
+
Added:
8. minimal code size.
857
+
Added:
858
+
Added:
Do not optimize only for the smallest number of lines.
859
+
Added:
860
+
Added:
Do not sacrifice AST clarity merely to remove a private intermediate
861
+
Added:
type.
862
+
Added:
863
+
Added:
Do not generalize before at least two concrete features need the
864
+
Added:
abstraction.
865
+
Added:
866
+
Added:
## Explicit architectural decisions
867
+
Added:
868
+
Added:
Treat the following as established decisions unless the user
869
+
Added:
deliberately revises them:
870
+
Added:
871
+
Added:
* Menhir is the parser-generator prototype.
872
+
Added:
* UTF-8 is initially opaque string content.
873
+
Added:
* Parsing operates on complete documents.
874
+
Added:
* External Emacs configuration is ignored.
875
+
Added:
* supported file-local syntax configuration is honored;
876
+
Added:
* syntax-affecting file declarations may require a pre-scan;
877
+
Added:
* the parser is permissive;
878
+
Added:
* the AST is semantic rather than lossless;
879
+
Added:
* the public AST uses closed variants;
880
+
Added:
* source block bodies remain opaque;
881
+
Added:
* parsing is separate from rendering and evaluation;
882
+
Added:
* incremental reparsing is out of scope;
883
+
Added:
* exact `org-element` compatibility is not a goal;
884
+
Added:
* pure-OCaml ecosystem dependencies are acceptable;
885
+
Added:
* C and system-library dependencies require explicit approval.
886
+
Added:
887
+
Added:
When a requested change conflicts with one of these decisions, call
888
+
Added:
out the conflict rather than silently changing the architecture.
889
+
Added:
890
+
Added:
## Concrete implementation choices
891
+
Added:
892
+
Added:
The following choices were made during requirements elicitation and
893
+
Added:
are binding for the current implementation:
894
+
Added:
895
+
Added:
* **Package name**: `ocaml-org`.
896
+
Added:
* **OCaml version**: 5.5.0 (local opam switch).
897
+
Added:
* **Test framework**: Alcotest.
898
+
Added:
* **Structural lexer**: handwritten OCaml (not ocamllex). The lexer
899
+
Added:
classifies physical lines into structural tokens. It is
900
+
Added:
context-aware (knows TODO keywords from the pre-scan).
901
+
Added:
* **Structural parser**: Menhir grammar consuming structural tokens,
902
+
Added:
producing `Raw_ast.document`.
903
+
Added:
* **Inline parser**: Menhir grammar (separate `.mly` file with its own
904
+
Added:
`%start` symbol) consuming inline tokens, producing `Ast.inline
905
+
Added:
list`. Chosen for its good error messages and grammar reasoning.
906
+
Added:
* **Inline lexer**: handwritten OCaml tokenizer for bounded inline
907
+
Added:
text.
908
+
Added:
* **Build system**: Dune 3.x with full opam package metadata.
909
+
Added:
* **First working increment**: parse a representative fixture document
910
+
Added:
exercising the 80% most-used Org features, including inline objects.
911
+
Added:
* **Menhir mode**: deterministic LR (table mode with incremental API
912
+
Added:
for error reporting).
913
+
Added:
914
+
Added:
## Implementation notes
915
+
Added:
916
+
Added:
These notes document key design decisions discovered during
917
+
Added:
implementation that go beyond the original architecture.
918
+
Added:
919
+
Added:
### Inline emphasis pre-scanning
920
+
Added:
921
+
Added:
The inline lexer pre-scans the entire input for matched emphasis
922
+
Added:
delimiter pairs *before* tokenizing. For each delimiter character
923
+
Added:
(`*`, `/`, `_`, `+`), it builds a hashtable mapping positions to
924
+
Added:
`Open` or `Close` roles. Only positions with confirmed matching
925
+
Added:
pairs emit `STAR_OPEN`/`STAR_CLOSE` etc. tokens; unmatched
926
+
Added:
delimiters become `TEXT`.
927
+
Added:
928
+
Added:
This eliminates grammar ambiguity entirely. The inline Menhir
929
+
Added:
grammar has zero conflicts because it never sees unmatched
930
+
Added:
delimiters. Fallback-to-plain-text is handled at the lexer level,
931
+
Added:
not in the grammar.
932
+
Added:
933
+
Added:
This was adopted after initial attempts with fallback grammar rules
934
+
Added:
caused reduce/reduce conflicts that Menhir resolved incorrectly.
935
+
Added:
936
+
Added:
### Record label prefixing
937
+
Added:
938
+
Added:
OCaml 5.x enforces warning 30 (duplicate-definitions) strictly in
939
+
Added:
mutually recursive type groups. The public AST uses prefixed record
940
+
Added:
labels to avoid collisions:
941
+
Added:
942
+
Added:
* `li_` for list items
943
+
Added:
* `src_` for source blocks
944
+
Added:
* `kw_` for keywords
945
+
Added:
* `dir_` for directives
946
+
Added:
* `prop_` for properties
947
+
Added:
* `aff_` for affiliated keywords
948
+
Added:
949
+
Added:
### Library wrapping
950
+
Added:
951
+
Added:
The library uses Dune's automatic module wrapping. There is no
952
+
Added:
manual `ocaml_org.ml` wrapper — all modules are sub-modules of
953
+
Added:
`Ocaml_org` (e.g. `Ocaml_org.Org.parse`, `Ocaml_org.Ast`,
954
+
Added:
`Ocaml_org.Config`).
955
+
Added:
956
+
Added:
The public API entry point is `lib/org.ml` (accessed as
957
+
Added:
`Ocaml_org.Org`).
958
+
Added:
959
+
Added:
### Structural parser conflicts
960
+
Added:
961
+
Added:
The structural Menhir grammar has 5 shift/reduce conflicts from
962
+
Added:
`nonempty_list` / `list` macros where consecutive same-type elements
963
+
Added:
(e.g. multiple list items, text lines, table rows) can be extended
964
+
Added:
or ended. Menhir resolves these by preferring shift (extend the
965
+
Added:
current list), which is correct behavior.
966
+
Added:
967
+
Added:
### Default branch
968
+
Added:
969
+
Added:
The default branch is `master`.
970
+
Added: