@plurnk/plurnk-grammar 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.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/SPEC.md +625 -0
- package/bin/plurnk.ts +43 -0
- package/package.json +49 -0
- package/plurnk.md +116 -0
- package/src/ast.ts +348 -0
- package/src/error-strategy.ts +140 -0
- package/src/errors.ts +25 -0
- package/src/generated/plurnkLexer.ts +419 -0
- package/src/generated/plurnkParser.ts +625 -0
- package/src/generated/plurnkParserVisitor.ts +65 -0
- package/src/index.ts +142 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PossumTech Laboratories, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# plurnk-grammar
|
|
2
|
+
|
|
3
|
+
Parser for the Plurnk protocol — a HEREDOC-style DSL for LLM agents.
|
|
4
|
+
|
|
5
|
+
## install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install @plurnk/plurnk-grammar
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node ≥ 23.6 (native TypeScript support).
|
|
12
|
+
|
|
13
|
+
## use
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { parse } from "plurnk-grammar";
|
|
17
|
+
const result = parse(input);
|
|
18
|
+
// result.items: Array<{kind:"statement"|"error"|"text", ...}>
|
|
19
|
+
// result.unparsedTail?: { from, reason }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Discriminate on `item.kind`. For `statement` items, narrow on `statement.op` (one of `FIND READ EDIT COPY MOVE SHOW HIDE SEND EXEC`) to access per-OP typed fields. Full API: [SPEC.md §12](SPEC.md#12-public-api).
|
|
23
|
+
|
|
24
|
+
## cli
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
plurnk [file] parse to JSON; file or stdin
|
|
28
|
+
plurnk --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Exit `0` on clean parse, `1` on any error or unparsed tail.
|
|
32
|
+
|
|
33
|
+
## syntax
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
<<OPsuffix [signal]? (path)? <L>? : body? :OPsuffix
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
| slot | shape |
|
|
40
|
+
|----------|----------------------------------------------------|
|
|
41
|
+
| `OP` | `FIND READ EDIT COPY MOVE SHOW HIDE SEND EXEC` |
|
|
42
|
+
| `suffix` | `[A-Za-z0-9_]*` glued to `OP`; used for nesting |
|
|
43
|
+
| `[…]` | optional CSV; per-OP semantics |
|
|
44
|
+
| `(…)` | optional URI |
|
|
45
|
+
| `<L>` | optional `<N>` or `<N-M>`; N, M ∈ signed integers |
|
|
46
|
+
| `:body:` | optional; opaque between fences |
|
|
47
|
+
|
|
48
|
+
| OP | signal | body | line marker |
|
|
49
|
+
|------|------------------|-----------------------|--------------------|
|
|
50
|
+
| FIND | tag filter | matcher | result-set range |
|
|
51
|
+
| READ | tag filter | matcher | per-entry lines |
|
|
52
|
+
| EDIT | tags | content (empty=clear) | entry lines |
|
|
53
|
+
| COPY | tags-to-apply | destination URI | entry lines |
|
|
54
|
+
| MOVE | tags-to-apply | destination URI | entry lines |
|
|
55
|
+
| SHOW | tag filter | matcher | result-set range |
|
|
56
|
+
| HIDE | tag filter | matcher | result-set range |
|
|
57
|
+
| SEND | HTTP status int | payload (JSON conv.) | n/a |
|
|
58
|
+
| EXEC | runtime tag | command or code | n/a |
|
|
59
|
+
|
|
60
|
+
Matcher body dialect by leading char: `//` xpath · `/…/flags` regex · `$` jsonpath · else glob.
|
|
61
|
+
|
|
62
|
+
Path scheme detection: `[a-z][a-z0-9+.-]*://` → URL (fully decomposed); else local (raw). Bare paths default to `file://` at runtime.
|
|
63
|
+
|
|
64
|
+
Nesting: outer body may contain inner `<<OP:…:OP` statements; outer must use a non-empty suffix so its close `:OPsuffix` is distinct.
|
|
65
|
+
|
|
66
|
+
## examples
|
|
67
|
+
|
|
68
|
+
1. List all xml files containing the admin user role.
|
|
69
|
+
<<FIND(config/**/*.xml)://user[@role='admin']:FIND
|
|
70
|
+
|
|
71
|
+
2. Read hello in every language
|
|
72
|
+
<<READ(lang/??.json):$.greeting:READ
|
|
73
|
+
|
|
74
|
+
3. Write a known entry to the index
|
|
75
|
+
<<EDIT[philosophy,existentialism](known://philosophy/existentialism/meaning):The meaning of life is 42:EDIT
|
|
76
|
+
|
|
77
|
+
4. Read an entry in full
|
|
78
|
+
<<READ(https://www.britannica.com/biography/Donald-Rumsfeld)::READ
|
|
79
|
+
|
|
80
|
+
5. Read lines 426–465 of a long article
|
|
81
|
+
<<READ(https://en.wikipedia.org/wiki/Donald_Rumsfeld)<426-465>::READ
|
|
82
|
+
|
|
83
|
+
6. Create an unknown entry with tags
|
|
84
|
+
<<EDIT[france,geography](unknown://countries/france/capital):What is the capital of France?:EDIT
|
|
85
|
+
|
|
86
|
+
7. Create a multi-line plan
|
|
87
|
+
<<EDIT[plan,france,task](known://plan):
|
|
88
|
+
- [ ] Decompose prompt into unknowns
|
|
89
|
+
- [ ] Discover capital of France
|
|
90
|
+
- [ ] Deliver
|
|
91
|
+
:EDIT
|
|
92
|
+
|
|
93
|
+
8. Mark a plan step complete (single-line replace)
|
|
94
|
+
<<EDIT(known://plan)<2>:- [x] Discover capital of France:EDIT
|
|
95
|
+
|
|
96
|
+
9. Replace a range of lines
|
|
97
|
+
<<EDIT(known://countries/france/capital)<4-5>:
|
|
98
|
+
The capital of France is Paris, on the river Seine.
|
|
99
|
+
Paris has been the continuous capital of France since 987 CE.
|
|
100
|
+
:EDIT
|
|
101
|
+
|
|
102
|
+
10. Append content to an existing entry
|
|
103
|
+
<<EDIT(known://countries/france/capital)<-1>:[Wikipedia: Paris](https://en.wikipedia.org/wiki/Paris):EDIT
|
|
104
|
+
|
|
105
|
+
11. Prepend content to an existing entry
|
|
106
|
+
<<EDIT(known://countries/france/capital)<0>:[Wikipedia: Paris](https://en.wikipedia.org/wiki/Paris):EDIT
|
|
107
|
+
|
|
108
|
+
12. Clear entry contents (empty body between two colons)
|
|
109
|
+
<<EDIT(known://countries/france/capital)::EDIT
|
|
110
|
+
|
|
111
|
+
13. Archive every distilled fetch log
|
|
112
|
+
<<HIDE(log://1/*/*/get)::HIDE
|
|
113
|
+
|
|
114
|
+
14. Restore archived entries by tag filter
|
|
115
|
+
<<SHOW[france](known://**)::SHOW
|
|
116
|
+
|
|
117
|
+
15. Rename a draft entry
|
|
118
|
+
<<MOVE(known://draft):known://final/answer:MOVE
|
|
119
|
+
|
|
120
|
+
16. Run a shell command in the project root
|
|
121
|
+
<<EXEC(./):node --test:EXEC
|
|
122
|
+
|
|
123
|
+
17. Continue the loop
|
|
124
|
+
<<SEND[102]:decomposed prompt; plan initialized:SEND
|
|
125
|
+
|
|
126
|
+
18. Deliver the final answer
|
|
127
|
+
<<SEND[200]:Paris:SEND
|
|
128
|
+
|
|
129
|
+
19. Search logs for timeout errors (case-insensitive regex body)
|
|
130
|
+
<<FIND(log://**/error):/timeout|deadline exceeded/i:FIND
|
|
131
|
+
|
|
132
|
+
20. Find entries whose content begins with "Paris" (glob body)
|
|
133
|
+
<<FIND(known://countries/**):Paris*:FIND
|
|
134
|
+
|
|
135
|
+
21. List the first 20 entries under a broad path (result-set pagination)
|
|
136
|
+
<<FIND(known://**)<1-20>::FIND
|
|
137
|
+
|
|
138
|
+
22. Read the first five lines of a local file (bare path → file://)
|
|
139
|
+
<<READ(./README.md)<1-5>::READ
|
|
140
|
+
|
|
141
|
+
23. Copy a draft entry to a dated archive location
|
|
142
|
+
<<COPY(known://draft):known://archive/2026-05-14/draft:COPY
|
|
143
|
+
|
|
144
|
+
24. Run an inline node script
|
|
145
|
+
<<EXEC[node](./):
|
|
146
|
+
const sum = [1, 2, 3].reduce((a, b) => a + b, 0);
|
|
147
|
+
console.log(sum);
|
|
148
|
+
:EXEC
|
|
149
|
+
|
|
150
|
+
25. Restore entries tagged france that contain "Paris" (combined filters)
|
|
151
|
+
<<SHOW[france](known://countries/**):Paris*:SHOW
|
|
152
|
+
|
|
153
|
+
26. Archive the second hundred of stale fetch logs (pagination)
|
|
154
|
+
<<HIDE(log://**/get)<101-200>::HIDE
|
|
155
|
+
|
|
156
|
+
27. Deliver a structured answer (JSON body)
|
|
157
|
+
<<SEND[200]:{"answer":"Paris","confidence":0.95}:SEND
|
|
158
|
+
|
|
159
|
+
28. Report a client error (JSON body the model can traverse with jsonpath)
|
|
160
|
+
<<SEND[400]:{"reason":"unrecognized OP","got":"FOOBAR","expected":["FIND","READ","EDIT","COPY","MOVE","SHOW","HIDE","SEND","EXEC"]}:SEND
|
|
161
|
+
|
|
162
|
+
29. Report a server error with explicit recipient
|
|
163
|
+
<<SEND[503](log://errors):{"reason":"git unavailable","command":"git status"}:SEND
|
|
164
|
+
|
|
165
|
+
30. Direct an informational message at a named agent
|
|
166
|
+
<<SEND[102](agent://supervisor):decomposition complete; awaiting clearance:SEND
|
|
167
|
+
|
|
168
|
+
31. Quote a plurnk operation inside another (nesting via suffix discipline)
|
|
169
|
+
<<EDITouter(known://demo):
|
|
170
|
+
The following is a quoted plurnk operation, preserved verbatim:
|
|
171
|
+
<<EDIT(known://inner):hello world:EDIT
|
|
172
|
+
:EDITouter
|
|
173
|
+
|
|
174
|
+
## error format
|
|
175
|
+
|
|
176
|
+
Errors are JSON-serializable. Shape: `{ line, column, source, message }` where `source` ∈ `lexer | parser | visitor`. Messages use protocol vocabulary (`unrecognized character '<<' in path`, `expected close tag; got end of input`).
|
|
177
|
+
|
|
178
|
+
## spec
|
|
179
|
+
|
|
180
|
+
[SPEC.md](SPEC.md) — full grammar specification: canonical form, per-OP semantics, matcher dialects, path decomposition, error model, whitespace rules, implementation notes.
|
|
181
|
+
|
|
182
|
+
## license
|
|
183
|
+
|
|
184
|
+
MIT.
|