clarity-pattern-parser 11.5.4 → 11.6.1

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 (38) hide show
  1. package/architecture.md +300 -0
  2. package/dist/grammar/Grammar.d.ts +24 -25
  3. package/dist/grammar/patterns/grammar.d.ts +99 -0
  4. package/dist/grammar/patterns/grammar.test.d.ts +1 -0
  5. package/dist/index.browser.js +2314 -2592
  6. package/dist/index.browser.js.map +1 -1
  7. package/dist/index.esm.js +2314 -2592
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +2314 -2592
  10. package/dist/index.js.map +1 -1
  11. package/grammar-guide.md +836 -0
  12. package/package.json +1 -1
  13. package/src/grammar/Grammar.test.ts +419 -1
  14. package/src/grammar/Grammar.ts +483 -515
  15. package/src/grammar/patterns/grammar.test.ts +276 -0
  16. package/src/grammar/patterns/grammar.ts +113 -37
  17. package/src/grammar/patterns.ts +6 -6
  18. package/src/grammar/patterns/anonymousPattern.ts +0 -23
  19. package/src/grammar/patterns/body.ts +0 -22
  20. package/src/grammar/patterns/comment.ts +0 -4
  21. package/src/grammar/patterns/decoratorStatement.ts +0 -85
  22. package/src/grammar/patterns/import.ts +0 -88
  23. package/src/grammar/patterns/literal.ts +0 -4
  24. package/src/grammar/patterns/literals.ts +0 -21
  25. package/src/grammar/patterns/name.ts +0 -3
  26. package/src/grammar/patterns/optionsLiteral.ts +0 -25
  27. package/src/grammar/patterns/pattern.ts +0 -29
  28. package/src/grammar/patterns/regexLiteral.ts +0 -5
  29. package/src/grammar/patterns/repeatLiteral.ts +0 -71
  30. package/src/grammar/patterns/sequenceLiteral.ts +0 -24
  31. package/src/grammar/patterns/spaces.ts +0 -16
  32. package/src/grammar/patterns/statement.ts +0 -22
  33. package/src/grammar/patterns/takeUtilLiteral.ts +0 -20
  34. package/src/grammar/spec.md +0 -331
  35. package/src/grammar_v2/patterns/Grammar.ts +0 -170
  36. package/src/grammar_v2/patterns/patterns/cpat.cpat +0 -91
  37. package/src/grammar_v2/patterns/patterns/grammar.test.ts +0 -218
  38. package/src/grammar_v2/patterns/patterns/grammar.ts +0 -103
@@ -0,0 +1,836 @@
1
+ # Clarity Pattern Parser - Grammar Guide
2
+
3
+ ## Table of Contents
4
+
5
+ 1. [Quick Start](#quick-start)
6
+ 2. [Grammar Syntax Reference](#grammar-syntax-reference)
7
+ 3. [Pattern Types](#pattern-types)
8
+ 4. [AST Output Reference](#ast-output-reference)
9
+ 5. [Expression Patterns (Operator Precedence)](#expression-patterns)
10
+ 6. [Imports and Parameters](#imports-and-parameters)
11
+ 7. [Decorators](#decorators)
12
+ 8. [Complete Examples](#complete-examples)
13
+
14
+ ---
15
+
16
+ ## Quick Start
17
+
18
+ ### Using the Tagged Template Literal
19
+
20
+ ```typescript
21
+ import { patterns } from "clarity-pattern-parser";
22
+
23
+ const { fullName } = patterns`
24
+ first-name = /[A-Z][a-z]+/
25
+ last-name = /[A-Z][a-z]+/
26
+ space = " "
27
+ full-name = first-name + space + last-name
28
+ `;
29
+
30
+ const result = fullName.exec("John Doe");
31
+
32
+ // result.ast is the AST Node (or null on failure)
33
+ // result.cursor contains error info on failure
34
+ console.log(result.ast?.value); // "John Doe"
35
+ ```
36
+
37
+ ### Using the Grammar Class
38
+
39
+ ```typescript
40
+ import { Grammar } from "clarity-pattern-parser";
41
+
42
+ const patterns = Grammar.parseString(`
43
+ digit = /\\d/
44
+ digits = (digit)+
45
+ `);
46
+
47
+ const result = patterns["digits"].exec("123");
48
+ console.log(result.ast?.value); // "123"
49
+ ```
50
+
51
+ > **Note**: In the tagged template literal, kebab-case names are auto-converted to camelCase for JavaScript access. `full-name` becomes `fullName`. In the `Grammar` class, names stay as-is.
52
+
53
+ ---
54
+
55
+ ## Grammar Syntax Reference
56
+
57
+ ### Comments
58
+
59
+ ```
60
+ # This is a comment (must be on its own line)
61
+ ```
62
+
63
+ ### Assignments
64
+
65
+ Every pattern is defined as a named assignment:
66
+
67
+ ```
68
+ pattern-name = <pattern-definition>
69
+ ```
70
+
71
+ Names must match: `/[a-zA-Z_-]+[a-zA-Z0-9_-]*/`
72
+
73
+ ### Literals
74
+
75
+ Double-quoted exact string match:
76
+
77
+ ```
78
+ greeting = "Hello"
79
+ newline = "\n"
80
+ quote = "\""
81
+ backslash = "\\"
82
+ ```
83
+
84
+ Supported escape sequences: `\n`, `\r`, `\t`, `\b`, `\f`, `\v`, `\0`, `\xHH`, `\uHHHH`, `\"`, `\\`
85
+
86
+ ### Regex
87
+
88
+ Forward-slash delimited regular expressions:
89
+
90
+ ```
91
+ digits = /\d+/
92
+ word = /[a-zA-Z_]\w*/
93
+ ```
94
+
95
+ - Do NOT include `^` or `$` anchors (they are added internally)
96
+ - Uses `gu` flags (global, unicode)
97
+ - Regex patterns match greedily from the current cursor position
98
+
99
+ ### Sequence (AND)
100
+
101
+ Concatenation with `+` operator. All parts must match in order:
102
+
103
+ ```
104
+ full-name = first-name + " " + last-name
105
+ greeting = "Hello" + space + name + "!"
106
+ ```
107
+
108
+ ### Options (OR)
109
+
110
+ Alternatives with `|` operator:
111
+
112
+ ```
113
+ # Non-greedy (first match wins):
114
+ name = "John" | "Jane" | "Bob"
115
+
116
+ # Greedy (longest match wins) using <|>:
117
+ token = identifier <|> keyword <|> operator
118
+ ```
119
+
120
+ The `|` operator tries alternatives in order and returns the first match. The `<|>` operator tries all alternatives and returns the longest match.
121
+
122
+ **Important**: When options contain self-referencing patterns (recursion), the parser automatically converts them into an `Expression` pattern for proper operator precedence handling. See [Expression Patterns](#expression-patterns).
123
+
124
+ ### Repeat
125
+
126
+ Repetition with bounds and optional dividers:
127
+
128
+ ```
129
+ # One or more:
130
+ digits = (digit)+
131
+
132
+ # Zero or more:
133
+ spaces = (space)*
134
+
135
+ # Exact count:
136
+ triple = (digit){3}
137
+
138
+ # Range (min, max):
139
+ few-digits = (digit){2,5}
140
+
141
+ # Min only (2 or more):
142
+ many = (digit){2,}
143
+
144
+ # Max only (0 to 3):
145
+ some = (digit){,3}
146
+ ```
147
+
148
+ #### With Divider
149
+
150
+ ```
151
+ # Comma-separated digits:
152
+ csv-digits = (digit, comma)+
153
+
154
+ # With trailing divider trimming:
155
+ items = (item, comma trim)+
156
+ ```
157
+
158
+ The `trim` flag allows a trailing divider at the end (but doesn't require it).
159
+
160
+ ### Optional
161
+
162
+ Append `?` to make a pattern optional in a sequence:
163
+
164
+ ```
165
+ middle-name-section = middle-name + space
166
+ full-name = first-name + space + middle-name-section? + last-name
167
+ ```
168
+
169
+ ### Not (Negative Lookahead)
170
+
171
+ Prefix with `!` in a sequence to assert a pattern does NOT match:
172
+
173
+ ```
174
+ # Match any first-name except "Jack":
175
+ full-name = !jack + first-name + space + last-name
176
+ ```
177
+
178
+ `!` does not consume input. It only asserts that the pattern doesn't match at the current position.
179
+
180
+ ### Take Until
181
+
182
+ Consume all characters until a terminating pattern:
183
+
184
+ ```
185
+ script-content = ?->| "</script"
186
+ ```
187
+
188
+ Matches everything from the current position up to (but not including) the terminator.
189
+
190
+ ### Alias
191
+
192
+ Reference an existing pattern under a new name:
193
+
194
+ ```
195
+ word = /\w+/
196
+ identifier = word # alias: same regex, new name "identifier"
197
+ ```
198
+
199
+ ### Anonymous Patterns
200
+
201
+ Inline unnamed patterns in sequences and repeats:
202
+
203
+ ```
204
+ # Inline literals and regex — fine, they get their value as the AST node name:
205
+ greeting = "Hello" + " " + /\w+/
206
+
207
+ # Complex inline patterns — avoid this:
208
+ items = ("item-a" <|> "item-b" <|> (thing)+)
209
+ ```
210
+
211
+ **Recommendation: Only use literals and regex inline.** Inline sequences, options, and repeats produce anonymous AST nodes that are hard to query or work with. If you need to find or walk specific parts of the result, give them names.
212
+
213
+ ```
214
+ # Avoid — anonymous nodes you can only navigate by index:
215
+ pairs = ("(" + /\w+/ + ")")+
216
+
217
+ # Prefer — every piece is addressable by name:
218
+ open = "("
219
+ close = ")"
220
+ word = /\w+/
221
+ pair = open + word + close
222
+ pairs = (pair)+
223
+ ```
224
+
225
+ With named patterns, you can use `ast.find(n => n.name === "word")` or `ast.findAll(n => n.name === "pair")` to pull out exactly what you need. With anonymous inline patterns, you're stuck counting children by position.
226
+
227
+ Inline literals (`"text"`) and regex (`/pattern/`) are the exception — they use their content as the node name (`"Hello"` becomes a node named `Hello`, `/\d+/` becomes a node named `\d+`), so they remain useful inline.
228
+
229
+ ### Export Names
230
+
231
+ Bare names at the end of a grammar re-export an imported pattern:
232
+
233
+ ```
234
+ import { value } from "other.cpat"
235
+ value # re-exports "value" as a top-level pattern
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Pattern Types
241
+
242
+ Each pattern type creates nodes with a specific `type` field:
243
+
244
+ | Grammar Syntax | Pattern Class | Node `type` |
245
+ |---|---|---|
246
+ | `"text"` | `Literal` | `"literal"` |
247
+ | `/regex/` | `Regex` | `"regex"` |
248
+ | `a + b` | `Sequence` | `"sequence"` |
249
+ | `a \| b` | `Options` | `"options"` |
250
+ | `a \| b` (with recursion) | `Expression` | `"expression"` |
251
+ | `(a)+` | `Repeat` | `"infinite-repeat"` or `"finite-repeat"` |
252
+ | `a?` | `Optional` | `"optional"` |
253
+ | `!a` | `Not` | `"not"` |
254
+ | `?->\| a` | `TakeUntil` | `"take-until"` |
255
+ | `alias = other` | Cloned pattern | Same as original |
256
+
257
+ ---
258
+
259
+ ## AST Output Reference
260
+
261
+ ### Node Structure
262
+
263
+ Every successful parse returns a `Node` (or `null` on failure):
264
+
265
+ ```typescript
266
+ interface ParseResult {
267
+ ast: Node | null; // The AST root node, or null if parsing failed
268
+ cursor: Cursor; // Cursor with error info if ast is null
269
+ }
270
+ ```
271
+
272
+ A `Node` has:
273
+
274
+ ```typescript
275
+ node.type // string - pattern type ("literal", "regex", "sequence", "expression", etc.)
276
+ node.name // string - the pattern name you defined in the grammar
277
+ node.value // string - the matched text (leaf nodes store directly; composite nodes concatenate children)
278
+ node.startIndex // number - 0-based inclusive start position in source text
279
+ node.endIndex // number - 0-based exclusive end position in source text
280
+ node.children // readonly Node[] - child nodes (empty for leaf nodes)
281
+ node.parent // Node | null - parent node reference
282
+ node.isLeaf // boolean - true if no children
283
+ node.hasChildren // boolean - true if has children
284
+ ```
285
+
286
+ ### Serialization
287
+
288
+ ```typescript
289
+ // Cycle-free plain object (safe for JSON):
290
+ node.toCycleFreeObject()
291
+ // Returns: { id, type, name, value, startIndex, endIndex, children: [...] }
292
+
293
+ // JSON string:
294
+ node.toJson(2) // pretty-printed with 2-space indent
295
+ ```
296
+
297
+ ### Output by Pattern Type
298
+
299
+ #### Literal
300
+
301
+ ```
302
+ greeting = "Hello"
303
+ ```
304
+
305
+ Parsing `"Hello"`:
306
+
307
+ ```
308
+ Node {
309
+ type: "literal"
310
+ name: "greeting"
311
+ value: "Hello"
312
+ startIndex: 0
313
+ endIndex: 5
314
+ children: [] # Always empty - leaf node
315
+ }
316
+ ```
317
+
318
+ #### Regex
319
+
320
+ ```
321
+ digits = /\d+/
322
+ ```
323
+
324
+ Parsing `"42"`:
325
+
326
+ ```
327
+ Node {
328
+ type: "regex"
329
+ name: "digits"
330
+ value: "42"
331
+ startIndex: 0
332
+ endIndex: 2
333
+ children: [] # Always empty - leaf node
334
+ }
335
+ ```
336
+
337
+ #### Sequence
338
+
339
+ ```
340
+ first-name = /[A-Z][a-z]+/
341
+ space = " "
342
+ last-name = /[A-Z][a-z]+/
343
+ full-name = first-name + space + last-name
344
+ ```
345
+
346
+ Parsing `"John Doe"`:
347
+
348
+ ```
349
+ Node {
350
+ type: "sequence"
351
+ name: "full-name"
352
+ value: "John Doe"
353
+ startIndex: 0
354
+ endIndex: 8
355
+ children: [
356
+ Node { type: "regex", name: "first-name", value: "John", startIndex: 0, endIndex: 4 }
357
+ Node { type: "literal", name: "space", value: " ", startIndex: 4, endIndex: 5 }
358
+ Node { type: "regex", name: "last-name", value: "Doe", startIndex: 5, endIndex: 8 }
359
+ ]
360
+ }
361
+ ```
362
+
363
+ **Key**: Sequence nodes contain one child per matched sub-pattern. Optional patterns that don't match are excluded from children.
364
+
365
+ #### Options
366
+
367
+ ```
368
+ name = "John" | "Jane"
369
+ ```
370
+
371
+ Parsing `"Jane"`:
372
+
373
+ ```
374
+ Node {
375
+ type: "literal" # The winning alternative's type, NOT "options"
376
+ name: "Jane" # The winning alternative's name
377
+ value: "Jane"
378
+ startIndex: 0
379
+ endIndex: 4
380
+ children: []
381
+ }
382
+ ```
383
+
384
+ **Key**: Options do NOT wrap the result. The node returned is the matched alternative directly.
385
+
386
+ #### Repeat
387
+
388
+ ```
389
+ digit = /\d/
390
+ digits = (digit)+
391
+ ```
392
+
393
+ Parsing `"123"`:
394
+
395
+ ```
396
+ Node {
397
+ type: "infinite-repeat"
398
+ name: "digits"
399
+ value: "123"
400
+ startIndex: 0
401
+ endIndex: 3
402
+ children: [
403
+ Node { type: "regex", name: "digit", value: "1", startIndex: 0, endIndex: 1 }
404
+ Node { type: "regex", name: "digit", value: "2", startIndex: 1, endIndex: 2 }
405
+ Node { type: "regex", name: "digit", value: "3", startIndex: 2, endIndex: 3 }
406
+ ]
407
+ }
408
+ ```
409
+
410
+ #### Repeat with Divider
411
+
412
+ ```
413
+ digit = /\d+/
414
+ comma = ","
415
+ csv = (digit, comma)+
416
+ ```
417
+
418
+ Parsing `"1,2,3"`:
419
+
420
+ ```
421
+ Node {
422
+ type: "infinite-repeat"
423
+ name: "csv"
424
+ value: "1,2,3"
425
+ children: [
426
+ Node { type: "regex", name: "digit", value: "1" }
427
+ Node { type: "literal", name: "comma", value: "," }
428
+ Node { type: "regex", name: "digit", value: "2" }
429
+ Node { type: "literal", name: "comma", value: "," }
430
+ Node { type: "regex", name: "digit", value: "3" }
431
+ ]
432
+ }
433
+ ```
434
+
435
+ **Key**: Divider nodes are interleaved between the repeated pattern nodes. With `trim`, a trailing divider is allowed but included if present.
436
+
437
+ #### Optional (in Sequence)
438
+
439
+ ```
440
+ middle = /[A-Z]\./
441
+ full = first + " " + middle? + " "? + last
442
+ ```
443
+
444
+ If `middle` matches, it appears as a child. If it doesn't match, it's simply absent from the children array. No wrapper node is created.
445
+
446
+ #### Expression (Operator Precedence)
447
+
448
+ ```
449
+ integer = /\d+/
450
+ add-expr = expr + " + " + expr
451
+ mul-expr = expr + " * " + expr
452
+ expr = mul-expr | add-expr | integer
453
+ ```
454
+
455
+ Parsing `"1 + 2 * 3"`:
456
+
457
+ ```
458
+ Node {
459
+ type: "expression"
460
+ name: "add-expr"
461
+ value: "1 + 2 * 3"
462
+ children: [
463
+ Node { type: "regex", name: "integer", value: "1" }
464
+ Node { type: "literal", name: " + ", value: " + " }
465
+ Node {
466
+ type: "expression"
467
+ name: "mul-expr"
468
+ value: "2 * 3"
469
+ children: [
470
+ Node { type: "regex", name: "integer", value: "2" }
471
+ Node { type: "literal", name: " * ", value: " * " }
472
+ Node { type: "regex", name: "integer", value: "3" }
473
+ ]
474
+ }
475
+ ]
476
+ }
477
+ ```
478
+
479
+ **Key**: The Expression pattern builds a proper precedence tree. Infix nodes have type `"expression"` and the name of the matched operator pattern. Children include the left operand, operator token(s), and right operand.
480
+
481
+ #### Prefix/Postfix Expression
482
+
483
+ ```
484
+ integer = /\d+/
485
+ unary = "-" + expr
486
+ postfix = expr + "++"
487
+ binary = expr + " + " + expr
488
+ expr = postfix | unary | binary | integer
489
+ ```
490
+
491
+ Parsing `"-10++"`:
492
+
493
+ ```
494
+ Node {
495
+ type: "expression"
496
+ name: "unary"
497
+ value: "-10++"
498
+ children: [
499
+ Node { type: "literal", name: "-", value: "-" }
500
+ Node {
501
+ type: "expression"
502
+ name: "postfix"
503
+ value: "10++"
504
+ children: [
505
+ Node { type: "regex", name: "integer", value: "10" }
506
+ Node { type: "literal", name: "++", value: "++" }
507
+ ]
508
+ }
509
+ ]
510
+ }
511
+ ```
512
+
513
+ #### Take Until
514
+
515
+ ```
516
+ content = ?->| "</script"
517
+ ```
518
+
519
+ Parsing `"function(){}</script"`:
520
+
521
+ ```
522
+ Node {
523
+ type: "take-until"
524
+ name: "content"
525
+ value: "function(){}"
526
+ startIndex: 0
527
+ endIndex: 12
528
+ children: []
529
+ }
530
+ ```
531
+
532
+ **Key**: Consumes everything BEFORE the terminator. The terminator itself is NOT included.
533
+
534
+ ### Traversal and Manipulation
535
+
536
+ ```typescript
537
+ const result = pattern.exec("some text");
538
+ const ast = result.ast;
539
+
540
+ // Find first node by predicate:
541
+ const nameNode = ast.find(n => n.name === "first-name");
542
+
543
+ // Find all nodes by predicate:
544
+ const allNames = ast.findAll(n => n.name.includes("name"));
545
+
546
+ // Walk tree (depth-first, children first):
547
+ ast.walkUp(node => {
548
+ console.log(node.name, node.value);
549
+ // Return false to stop walking
550
+ });
551
+
552
+ // Walk tree (depth-first, parent first):
553
+ ast.walkDown(node => {
554
+ console.log(node.name, node.value);
555
+ });
556
+
557
+ // Walk breadth-first:
558
+ ast.walkBreadthFirst(node => { /* ... */ });
559
+
560
+ // Get leaf nodes only:
561
+ const leaves = ast.flatten();
562
+
563
+ // Remove whitespace nodes:
564
+ ast.findAll(n => n.name === "space").forEach(n => n.remove());
565
+
566
+ // Collapse node to single value (removes children):
567
+ node.compact();
568
+
569
+ // Transform with visitor pattern:
570
+ ast.transform({
571
+ "space": (node) => { node.remove(); return node; },
572
+ "name": (node) => { /* transform */ return node; }
573
+ });
574
+
575
+ // Navigate siblings:
576
+ const next = node.nextSibling();
577
+ const prev = node.previousSibling();
578
+
579
+ // Find ancestor:
580
+ const parent = node.findAncestor(n => n.name === "expression");
581
+ ```
582
+
583
+ ---
584
+
585
+ ## Expression Patterns
586
+
587
+ Expression patterns handle operator precedence and associativity automatically. They're created implicitly when an options pattern (`|`) contains self-referencing alternatives.
588
+
589
+ ### How Precedence Works
590
+
591
+ Precedence is determined by **declaration order** - patterns listed first have **higher precedence** (bind tighter):
592
+
593
+ ```
594
+ integer = /\d+/
595
+ mul-expr = expr + " * " + expr # Higher precedence (listed first)
596
+ add-expr = expr + " + " + expr # Lower precedence (listed second)
597
+ expr = mul-expr | add-expr | integer
598
+ ```
599
+
600
+ `2 + 3 * 4` parses as `2 + (3 * 4)` because `mul-expr` has higher precedence.
601
+
602
+ ### Auto-Classification
603
+
604
+ The Expression pattern examines each alternative's structure:
605
+
606
+ | Structure | Classification | Example |
607
+ |---|---|---|
608
+ | No self-reference | **Atom** | `integer`, `"(" + expr + ")"` |
609
+ | Self-ref at end only | **Prefix** | `"-" + expr` |
610
+ | Self-ref at start only | **Postfix** | `expr + "++"` |
611
+ | Self-ref at both ends | **Infix** | `expr + " + " + expr` |
612
+
613
+ "Self-reference" means the pattern references the expression pattern itself (by name).
614
+
615
+ ### Right Associativity
616
+
617
+ By default, infix operators are left-associative. Use the `right` keyword for right-associativity:
618
+
619
+ ```
620
+ ternary = expr + " ? " + expr + " : " + expr
621
+ expr = ternary right | integer
622
+ ```
623
+
624
+ `a ? b : c ? d : e` parses as `a ? b : (c ? d : e)` instead of `(a ? b : c) ? d : e`.
625
+
626
+ ### Complete Expression Example
627
+
628
+ ```
629
+ integer = /\d+/
630
+ variable = /[a-z]/
631
+ open-paren = "("
632
+ close-paren = ")"
633
+
634
+ group = open-paren + expr + close-paren
635
+ mul-expr = expr + " * " + expr
636
+ div-expr = expr + " / " + expr
637
+ add-expr = expr + " + " + expr
638
+ sub-expr = expr + " - " + expr
639
+ neg-expr = "-" + expr
640
+ post-inc = expr + "++"
641
+
642
+ expr = mul-expr | div-expr | add-expr | sub-expr | neg-expr | post-inc | group | integer | variable
643
+ ```
644
+
645
+ ---
646
+
647
+ ## Imports and Parameters
648
+
649
+ ### Importing Patterns
650
+
651
+ ```
652
+ import { pattern-name } from "path/to/grammar.cpat"
653
+ import { old-name as new-name } from "grammar.cpat"
654
+ ```
655
+
656
+ Multiple imports:
657
+
658
+ ```
659
+ import { alpha, beta } from "greek.cpat"
660
+ import { one, two, three } from "numbers.cpat"
661
+ ```
662
+
663
+ ### Parameterized Grammars
664
+
665
+ Define parameters in the imported grammar:
666
+
667
+ ```
668
+ # divider.cpat
669
+ use params { separator }
670
+ items = (item, separator)+
671
+ ```
672
+
673
+ Pass values when importing:
674
+
675
+ ```
676
+ import { items } from "divider.cpat" with params {
677
+ separator = ","
678
+ }
679
+ ```
680
+
681
+ Parameters with defaults:
682
+
683
+ ```
684
+ use params {
685
+ separator = default-separator
686
+ }
687
+ default-separator = ","
688
+ items = (item, separator)+
689
+ ```
690
+
691
+ If a param is supplied, it overrides the default. Otherwise the default is used.
692
+
693
+ ### Providing resolveImport
694
+
695
+ ```typescript
696
+ const patterns = await Grammar.parse(grammarString, {
697
+ resolveImport: async (resource, originResource) => {
698
+ const content = await fs.readFile(resolve(originResource, resource), "utf-8");
699
+ return { expression: content, resource };
700
+ }
701
+ });
702
+
703
+ // Synchronous version:
704
+ const patterns = Grammar.parseString(grammarString, {
705
+ resolveImportSync: (resource, originResource) => {
706
+ const content = fs.readFileSync(resolve(originResource, resource), "utf-8");
707
+ return { expression: content, resource };
708
+ }
709
+ });
710
+ ```
711
+
712
+ ---
713
+
714
+ ## Decorators
715
+
716
+ Decorators modify patterns before they're finalized:
717
+
718
+ ```
719
+ # Method decorator with args:
720
+ @tokens([" ", "\t"])
721
+ whitespace = /\s+/
722
+
723
+ # Method decorator without args:
724
+ @tokens()
725
+ spaces = /\s+/
726
+
727
+ # Name decorator (no parens):
728
+ @myDecorator
729
+ pattern = /\w+/
730
+ ```
731
+
732
+ ### Built-in: @tokens
733
+
734
+ Sets the token hints used by intellisense:
735
+
736
+ ```
737
+ @tokens(["(", ")"])
738
+ parens = /[()]/
739
+ ```
740
+
741
+ ### Custom Decorators
742
+
743
+ ```typescript
744
+ import { createPatternsTemplate } from "clarity-pattern-parser";
745
+
746
+ const patterns = createPatternsTemplate({
747
+ decorators: {
748
+ myDecorator: (pattern, arg) => {
749
+ // Modify the pattern
750
+ console.log(`Decorated: ${pattern.name}`, arg);
751
+ }
752
+ }
753
+ });
754
+
755
+ const { result } = patterns`
756
+ @myDecorator({"key": "value"})
757
+ result = /\w+/
758
+ `;
759
+ ```
760
+
761
+ Decorator arguments are JSON-parsed. Supported types: arrays, objects, strings, numbers, booleans, null.
762
+
763
+ ---
764
+
765
+ ## Complete Examples
766
+
767
+ ### JSON Parser
768
+
769
+ ```
770
+ true-value = "true"
771
+ false-value = "false"
772
+ null-value = "null"
773
+ comma = ","
774
+ colon = ":"
775
+ open-bracket = "["
776
+ close-bracket = "]"
777
+ open-brace = "{"
778
+ close-brace = "}"
779
+
780
+ spaces = /\s*/
781
+ string = /"([^"\\]|\\.)*"/
782
+ number = /-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/
783
+
784
+ value = string | number | true-value | false-value | null-value | array | object
785
+
786
+ array-items = (value, /\s*,\s*/ trim)*
787
+ array = open-bracket + spaces + array-items + spaces + close-bracket
788
+
789
+ pair = spaces + string + spaces + colon + spaces + value + spaces
790
+ pairs = (pair, comma trim)*
791
+ object = open-brace + spaces + pairs + spaces + close-brace
792
+ ```
793
+
794
+ ### Simple Markup
795
+
796
+ ```
797
+ tag-name = /[a-zA-Z_-]+[a-zA-Z0-9_-]*/
798
+ space = /\s+/
799
+ opening-tag = "<" + tag-name + space? + ">"
800
+ closing-tag = "</" + tag-name + space? + ">"
801
+ child = space? + element + space?
802
+ children = (child)*
803
+ element = opening-tag + children + closing-tag
804
+ body = space? + element + space?
805
+ ```
806
+
807
+ ### Math Expression
808
+
809
+ ```
810
+ integer = /\d+/
811
+ operator = "+" | "-" | "*" | "/"
812
+ unary-operator = "+" | "-"
813
+ postfix-operator = "++" | "--"
814
+
815
+ binary-expr = expr + operator + expr
816
+ unary-expr = unary-operator + expr
817
+ postfix-expr = expr + postfix-operator
818
+
819
+ expr = postfix-expr | unary-expr | binary-expr | integer
820
+ ```
821
+
822
+ ### Recursive Array
823
+
824
+ ```
825
+ digit = /\d+/
826
+ divider = /\s*,\s*/
827
+ open-bracket = "["
828
+ close-bracket = "]"
829
+ spaces = /\s+/
830
+
831
+ items = digit | array
832
+ array-items = (items, divider trim)*
833
+ array = open-bracket + spaces? + array-items + spaces? + close-bracket
834
+ ```
835
+
836
+ Parsing `"[1, [2, 3], []]"` produces a nested AST matching the recursive structure.