@cavelang/parser 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/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @cavelang/parser
2
+
3
+ CAVE text → AST. Implements the syntax layer of the
4
+ [CAVE specification](../../README.md#the-specification): lexical rules (§4), line shapes
5
+ (§3), indentation kinds (§8) and the normative grammar (§16). Built on
6
+ [`@prelude/parser`](https://www.npmjs.com/package/@prelude/parser)
7
+ combinators.
8
+
9
+ The parser is deliberately *pre-semantic*: inverse verbs stay as written
10
+ (`packages/api PART-OF monorepo` keeps verb `PART-OF`), continuation lines
11
+ keep their missing endpoint, `UNLESS` stays `UNLESS`. The §13.4
12
+ canonicalization pipeline lives in `@cavelang/canonical`.
13
+
14
+ ```ts
15
+ import { parseDocument, parse } from '@cavelang/parser'
16
+
17
+ const doc = parseDocument(`
18
+ server CAUSE crash @ 80%
19
+ WHEN load > ~1000 req/s
20
+ WHEN NOT cache/enabled
21
+ `)
22
+ doc.lines // classified lines with depth + parent links
23
+ doc.diagnostics // never throws — problems are collected
24
+
25
+ parse('a USES b') // strict variant: throws on any diagnostic
26
+ ```
27
+
28
+ ## Pipeline
29
+
30
+ 1. **Split physical lines**, measure indentation (`document.ts`).
31
+ 2. **Split off the comment** at the first `;` outside quotes/backticks
32
+ (`Token.splitComment`).
33
+ 3. **Tokenize** into words / `"text"` literals / `` `code` `` literals
34
+ (`token.ts`, `@prelude/parser` combinators).
35
+ 4. **Classify** the line per §8's three-kind table: qualifier (starts with
36
+ `WHEN`/`UNLESS`/`VIA`/`BECAUSE`), continuation (starts with a bare
37
+ relational verb), or full claim.
38
+ 5. **Parse the token stream** (`line.ts`): subject, verb, `NOT`, payload,
39
+ then metadata items.
40
+ 6. **Resolve parents**: each structural line links to the nearest
41
+ less-indented structural line above.
42
+
43
+ ## Payload classification
44
+
45
+ | Shape | Result |
46
+ |---|---|
47
+ | `attr: value` | attribute payload (any verb — §21 uses `NEEDS test: …`) |
48
+ | `HAS attr value` (colonless, value numeric/date) | attribute payload + legacy diagnostic (§3.4) |
49
+ | numeric/date value (any verb) | metric payload — `latency IS 30ms`, `load EXCEEDS 1000 req/s` |
50
+ | nothing (verb `EXISTS`) | `none` payload (§5.2 bare existence) |
51
+ | single term | relational object |
52
+ | words only | relational object phrase (entity whitespace normalized later) |
53
+
54
+ The payload/metadata boundary is lexical: a metadata item is unambiguous at
55
+ its first token (`@ctx`, `@` + spaced percentage, `#tag`, `+/-`, `(Nσ)`,
56
+ `!`), so payload is everything before the first one (§4.3).
57
+
58
+ ## Qualifier payloads
59
+
60
+ `qualifier_payload` is loose in the grammar; three shapes cover the spec's
61
+ examples and are tried in order:
62
+
63
+ 1. full claim — `WHEN memory-leak EXISTS @ 60%` (§10.2)
64
+ 2. comparison — `WHEN load > ~1000 req/s`; ops `>` `<` `>=` `<=` `=` `!=`
65
+ 3. bare entity — `WHEN cache-miss`, `WHEN NOT cache/enabled`
66
+
67
+ ## Design decisions
68
+
69
+ - **Never throw, always classify.** `parseDocument` returns an entry for
70
+ *every* physical line; broken ones become `invalid` with a diagnostic.
71
+ This honors the robust-extraction goal (§1.6) and makes the parser usable
72
+ as a linter.
73
+ - **Classification tiebreak.** A line starting with a verb-shaped token is
74
+ classified using the known standard vocabulary (standard verbs, `HAS`,
75
+ `REVERSE`, the §5.5 inverse names): second token `REVERSE` → claim
76
+ (declaration); second token a *known* verb → claim with an uppercase
77
+ subject (`API NEEDS auth`); first token known → continuation, even when
78
+ the object is ALL-CAPS (`USES JWT`, `PART-OF ORG`, `USES GPU cluster`);
79
+ neither known → claim (`API MIGRATES postgres`). The residual ambiguity —
80
+ an *extension*-verb continuation with an ALL-CAPS object — is inherently
81
+ registry-dependent and the parser stays registry-free; write the subject
82
+ explicitly there.
83
+ - **`@` disambiguation is exactly one character of lookahead** (§6.3):
84
+ the token `@` alone expects a following percentage (confidence);
85
+ `@anything` is a context.
86
+ - **No escape sequences in literals** — the spec defines none. A quoted
87
+ literal runs to the next matching delimiter; an unterminated one degrades
88
+ to a plain word.
89
+ - **Metadata problems don't kill lines.** `a USES b stray` parses with a
90
+ diagnostic; only structural failures (missing verb/object) invalidate a
91
+ line. Confidence must end in `%` and stay ≤ 100 (`@ 2026`, `@ 90`,
92
+ `@ 250%` are diagnosed rather than silently clamped); repeating a
93
+ non-repeatable metadata item (`@ N%`, `+/-`, `(Nσ)`, `!`) is diagnosed
94
+ with last-wins retention (§3.2 allows repetition only for contexts and
95
+ tags); a glued attribute colon (`expiry:3600s`) splits into the attribute
96
+ form with a diagnostic, since payload `:` is reserved (§4.3).
97
+
98
+ ## Tests
99
+
100
+ ```
101
+ pnpm --filter @cavelang/parser test
102
+ ```
103
+
104
+ Every syntax example from spec §3–§8, §16 and the §21 worked example is a
105
+ test case.
@@ -0,0 +1,122 @@
1
+ /**
2
+ * CAVE abstract syntax (spec §16).
3
+ *
4
+ * The parser turns text into `Line` values — pure syntax, before
5
+ * canonicalization. Inverse verbs are *not* resolved here (`packages/api
6
+ * PART-OF monorepo` parses with verb `PART-OF`); continuation lines keep
7
+ * their missing endpoint; qualifier payloads keep their surface shape.
8
+ * `@cavelang/canonical` applies the spec §13.4 pipeline on top of this AST.
9
+ */
10
+ import type { Claim, Value, Tag, Context, Verb } from '@cavelang/core';
11
+ /** Line metadata suffixes (spec §6), all optional. */
12
+ export type Meta = {
13
+ readonly contexts: readonly Context.t[];
14
+ readonly tags: readonly Tag.t[];
15
+ /** `@ N%` as decimal; `undefined` means the default 100%. */
16
+ readonly conf?: number;
17
+ readonly importance: boolean;
18
+ /** `+/- delta` (spec §7.2). */
19
+ readonly delta?: Value.t;
20
+ /** `(Nσ)` override (spec §7.2). */
21
+ readonly sigmaLevel?: number;
22
+ /** `; comment` (spec §6.4). */
23
+ readonly comment?: string;
24
+ };
25
+ /** Empty metadata. */
26
+ export declare const emptyMeta: Meta;
27
+ /** Claim body without a subject — what a continuation line carries (spec §8.3). */
28
+ export type Body = {
29
+ readonly verb: string;
30
+ readonly negated: boolean;
31
+ readonly payload: Claim.Payload;
32
+ readonly meta: Meta;
33
+ };
34
+ /** Full claim: subject + body. */
35
+ export type Full = Body & {
36
+ readonly subject: Claim.Term;
37
+ };
38
+ /** Comparison operators accepted in qualifier payloads (`WHEN load > ~1000 req/s`). */
39
+ export type ComparisonOp = '>' | '<' | '>=' | '<=' | '=' | '!=';
40
+ export declare const comparisonOps: readonly ComparisonOp[];
41
+ /**
42
+ * Qualifier payload (spec §8.2). The grammar leaves `qualifier_payload`
43
+ * loose; three surface shapes cover the spec's examples:
44
+ *
45
+ * - full claim: `WHEN memory-leak EXISTS @ 60%`
46
+ * - bare entity: `WHEN cache-miss`, `WHEN NOT cache/enabled`
47
+ * - comparison: `WHEN load > ~1000 req/s`
48
+ *
49
+ * `negated` records a `NOT` immediately after the qualifier verb.
50
+ */
51
+ export type QualifierPayload = {
52
+ readonly kind: 'claim';
53
+ readonly negated: boolean;
54
+ readonly claim: Full;
55
+ } | {
56
+ readonly kind: 'entity';
57
+ readonly negated: boolean;
58
+ readonly term: Claim.Term;
59
+ readonly meta: Meta;
60
+ } | {
61
+ readonly kind: 'comparison';
62
+ readonly negated: boolean;
63
+ readonly left: Claim.Term;
64
+ readonly op: ComparisonOp;
65
+ readonly value: Value.t;
66
+ readonly meta: Meta;
67
+ };
68
+ /**
69
+ * A parsed line. `depth` is the indentation width in characters; `parent` is
70
+ * the index (into the document's `lines`) of the nearest less-indented
71
+ * structural line above (spec §8). `raw` is the line exactly as written.
72
+ */
73
+ export type Line = {
74
+ readonly kind: 'blank';
75
+ readonly line: number;
76
+ readonly raw: string;
77
+ } | {
78
+ readonly kind: 'comment';
79
+ readonly line: number;
80
+ readonly raw: string;
81
+ readonly text: string;
82
+ } | {
83
+ readonly kind: 'claim';
84
+ readonly line: number;
85
+ readonly raw: string;
86
+ readonly depth: number;
87
+ readonly parent?: number;
88
+ readonly claim: Full;
89
+ } | {
90
+ readonly kind: 'continuation';
91
+ readonly line: number;
92
+ readonly raw: string;
93
+ readonly depth: number;
94
+ readonly parent?: number;
95
+ readonly body: Body;
96
+ } | {
97
+ readonly kind: 'qualifier';
98
+ readonly line: number;
99
+ readonly raw: string;
100
+ readonly depth: number;
101
+ readonly parent?: number;
102
+ readonly qualifier: Verb.Qualifier;
103
+ readonly payload: QualifierPayload;
104
+ } | {
105
+ readonly kind: 'invalid';
106
+ readonly line: number;
107
+ readonly raw: string;
108
+ readonly message: string;
109
+ };
110
+ export type t = Line;
111
+ /** Parser problem tied to a 1-based line number. */
112
+ export type Diagnostic = {
113
+ readonly line: number;
114
+ readonly message: string;
115
+ readonly raw: string;
116
+ };
117
+ /** Parsed document: every input line classified, plus collected problems. */
118
+ export type Document = {
119
+ readonly lines: readonly Line[];
120
+ readonly diagnostics: readonly Diagnostic[];
121
+ };
122
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/ast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * CAVE abstract syntax (spec §16).
3
+ *
4
+ * The parser turns text into `Line` values — pure syntax, before
5
+ * canonicalization. Inverse verbs are *not* resolved here (`packages/api
6
+ * PART-OF monorepo` parses with verb `PART-OF`); continuation lines keep
7
+ * their missing endpoint; qualifier payloads keep their surface shape.
8
+ * `@cavelang/canonical` applies the spec §13.4 pipeline on top of this AST.
9
+ */
10
+ /** Empty metadata. */
11
+ export const emptyMeta = { contexts: [], tags: [], importance: false };
12
+ export const comparisonOps = ['>', '<', '>=', '<=', '=', '!='];
13
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/ast.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAmBH,sBAAsB;AACtB,MAAM,CAAC,MAAM,SAAS,GACpB,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAA;AAkB/C,MAAM,CAAC,MAAM,aAAa,GACxB,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Document parser (spec §8, §16).
3
+ *
4
+ * Splits input into physical lines, measures indentation, classifies each
5
+ * line (blank / comment / claim / continuation / qualifier), and resolves
6
+ * each indented line's parent — the nearest less-indented structural line
7
+ * above (spec §8).
8
+ *
9
+ * Classification of an indented line follows spec §8's table, decided by
10
+ * what the line starts with:
11
+ *
12
+ * - qualifier verb (`WHEN`/`UNLESS`/`VIA`/`BECAUSE`) → qualifier
13
+ * - bare relational verb → continuation
14
+ * - full triple → grouped claim
15
+ *
16
+ * One ambiguity needs a tiebreak: `API NEEDS auth` starts with a token that
17
+ * is lexically verb-shaped. If the *second* token is also verb-shaped (and
18
+ * not `NOT`), the line is a full triple with an uppercase subject —
19
+ * `CONTAINS REVERSE PART-OF` and `API NEEDS auth` both land here; otherwise
20
+ * it is a continuation.
21
+ *
22
+ * `parseDocument` never throws: broken lines become `invalid` entries and
23
+ * every problem is a diagnostic. `parse` is the strict variant.
24
+ */
25
+ import type * as Ast from './ast.ts';
26
+ /**
27
+ * Parses a CAVE document. Never throws; problems surface as diagnostics and
28
+ * `invalid` lines.
29
+ */
30
+ export declare const parseDocument: (input: string) => Ast.Document;
31
+ /**
32
+ * Strict parse: like {@link parseDocument} but throws an `Error` listing
33
+ * every diagnostic when the document has any.
34
+ */
35
+ export declare const parse: (input: string) => readonly Ast.Line[];
36
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAA;AAgDpC;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,GAAG,CAAC,QAgGjD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,SAAS,GAAG,CAAC,IAAI,EAStD,CAAA"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Document parser (spec §8, §16).
3
+ *
4
+ * Splits input into physical lines, measures indentation, classifies each
5
+ * line (blank / comment / claim / continuation / qualifier), and resolves
6
+ * each indented line's parent — the nearest less-indented structural line
7
+ * above (spec §8).
8
+ *
9
+ * Classification of an indented line follows spec §8's table, decided by
10
+ * what the line starts with:
11
+ *
12
+ * - qualifier verb (`WHEN`/`UNLESS`/`VIA`/`BECAUSE`) → qualifier
13
+ * - bare relational verb → continuation
14
+ * - full triple → grouped claim
15
+ *
16
+ * One ambiguity needs a tiebreak: `API NEEDS auth` starts with a token that
17
+ * is lexically verb-shaped. If the *second* token is also verb-shaped (and
18
+ * not `NOT`), the line is a full triple with an uppercase subject —
19
+ * `CONTAINS REVERSE PART-OF` and `API NEEDS auth` both land here; otherwise
20
+ * it is a continuation.
21
+ *
22
+ * `parseDocument` never throws: broken lines become `invalid` entries and
23
+ * every problem is a diagnostic. `parse` is the strict variant.
24
+ */
25
+ import { Verb } from '@cavelang/core';
26
+ import * as Line from "./line.js";
27
+ import * as Token from "./token.js";
28
+ const indentOf = (raw) => {
29
+ let depth = 0;
30
+ let tabs = false;
31
+ while (depth < raw.length && (raw[depth] === ' ' || raw[depth] === '\t')) {
32
+ tabs ||= raw[depth] === '\t';
33
+ depth += 1;
34
+ }
35
+ return { depth, rest: raw.slice(depth), tabs };
36
+ };
37
+ const classify = (tokens, depth) => {
38
+ const head = tokens[0];
39
+ if (head.kind === 'word' && Verb.isQualifier(head.text)) {
40
+ return depth > 0 ?
41
+ 'qualifier' :
42
+ { error: `qualifier verb ${head.text} at top level — qualifiers attach to a parent claim (spec §8.2)` };
43
+ }
44
+ if (head.kind === 'word' && Verb.isVerbToken(head.text)) {
45
+ const second = tokens[1];
46
+ const secondWord = second?.kind === 'word' ? second.text : undefined;
47
+ // Tiebreak between "continuation" and "full triple with an uppercase
48
+ // subject", using the known standard vocabulary (see the README):
49
+ // CONTAINS REVERSE PART-OF → claim (declaration)
50
+ // NEEDS NOT downtime → continuation (NOT is a modifier)
51
+ // API NEEDS auth → claim (second token is a known verb)
52
+ // USES JWT / PART-OF ORG → continuation (first known, second not)
53
+ // API MIGRATES postgres → claim (neither known — subject wins)
54
+ const kind = secondWord === Verb.REVERSE ? 'claim' :
55
+ secondWord !== undefined && secondWord !== 'NOT' && Verb.isVerbToken(secondWord) ?
56
+ (Verb.isKnown(secondWord) ? 'claim' : Verb.isKnown(head.text) ? 'continuation' : 'claim') :
57
+ 'continuation';
58
+ if (kind === 'continuation' && depth === 0) {
59
+ return { error: `continuation line at top level — nothing to inherit a subject from (spec §8.3)` };
60
+ }
61
+ return kind;
62
+ }
63
+ return 'claim';
64
+ };
65
+ /**
66
+ * Parses a CAVE document. Never throws; problems surface as diagnostics and
67
+ * `invalid` lines.
68
+ */
69
+ export const parseDocument = (input) => {
70
+ const lines = [];
71
+ const diagnostics = [];
72
+ const stack = [];
73
+ const rawLines = input.split(/\r?\n/);
74
+ const problem = (line, raw, message) => {
75
+ diagnostics.push({ line, message, raw });
76
+ };
77
+ rawLines.forEach((raw, at) => {
78
+ const lineNo = at + 1;
79
+ const { depth, rest, tabs } = indentOf(raw);
80
+ if (tabs) {
81
+ problem(lineNo, raw, 'tab in indentation — use spaces');
82
+ }
83
+ if (rest === '') {
84
+ lines.push({ kind: 'blank', line: lineNo, raw });
85
+ return;
86
+ }
87
+ if (rest.startsWith(';')) {
88
+ lines.push({ kind: 'comment', line: lineNo, raw, text: rest.slice(1).trim() });
89
+ return;
90
+ }
91
+ const { head, comment } = Token.splitComment(rest);
92
+ const tokens = Token.tokenize(head);
93
+ if (tokens.length === 0) {
94
+ lines.push({ kind: 'comment', line: lineNo, raw, text: comment ?? '' });
95
+ return;
96
+ }
97
+ const kind = classify(tokens, depth);
98
+ if (typeof kind === 'object') {
99
+ problem(lineNo, raw, kind.error);
100
+ lines.push({ kind: 'invalid', line: lineNo, raw, message: kind.error });
101
+ return;
102
+ }
103
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
104
+ stack.pop();
105
+ }
106
+ const parent = stack[stack.length - 1]?.index;
107
+ if (parent === undefined && kind !== 'claim') {
108
+ const message = `${kind} line has no parent claim above (spec §8)`;
109
+ problem(lineNo, raw, message);
110
+ lines.push({ kind: 'invalid', line: lineNo, raw, message });
111
+ return;
112
+ }
113
+ const index = lines.length;
114
+ const push = (line, problems) => {
115
+ lines.push(line);
116
+ stack.push({ index, depth });
117
+ for (const message of problems) {
118
+ problem(lineNo, raw, message);
119
+ }
120
+ };
121
+ switch (kind) {
122
+ case 'claim': {
123
+ const result = Line.parseClaim(tokens, comment);
124
+ if (!result.ok) {
125
+ problem(lineNo, raw, result.message);
126
+ lines.push({ kind: 'invalid', line: lineNo, raw, message: result.message });
127
+ return;
128
+ }
129
+ push({ kind: 'claim', line: lineNo, raw, depth, ...parent !== undefined ? { parent } : {}, claim: result.value }, result.problems);
130
+ return;
131
+ }
132
+ case 'continuation': {
133
+ const result = Line.parseBody(tokens, comment);
134
+ if (!result.ok) {
135
+ problem(lineNo, raw, result.message);
136
+ lines.push({ kind: 'invalid', line: lineNo, raw, message: result.message });
137
+ return;
138
+ }
139
+ push({ kind: 'continuation', line: lineNo, raw, depth, parent: parent, body: result.value }, result.problems);
140
+ return;
141
+ }
142
+ case 'qualifier': {
143
+ const qualifier = tokens[0].text;
144
+ const result = Line.parseQualifierPayload(tokens.slice(1), comment);
145
+ if (!result.ok) {
146
+ problem(lineNo, raw, result.message);
147
+ lines.push({ kind: 'invalid', line: lineNo, raw, message: result.message });
148
+ return;
149
+ }
150
+ push({ kind: 'qualifier', line: lineNo, raw, depth, parent: parent, qualifier, payload: result.value }, result.problems);
151
+ return;
152
+ }
153
+ }
154
+ });
155
+ return { lines, diagnostics };
156
+ };
157
+ /**
158
+ * Strict parse: like {@link parseDocument} but throws an `Error` listing
159
+ * every diagnostic when the document has any.
160
+ */
161
+ export const parse = (input) => {
162
+ const { lines, diagnostics } = parseDocument(input);
163
+ if (diagnostics.length > 0) {
164
+ const detail = diagnostics
165
+ .map(diagnostic => ` line ${diagnostic.line}: ${diagnostic.message}`)
166
+ .join('\n');
167
+ throw new Error(`CAVE parse failed with ${diagnostics.length} problem(s):\n${detail}`);
168
+ }
169
+ return lines;
170
+ };
171
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAErC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAEnC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAkD,EAAE;IAC/E,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,IAAI,GAAG,KAAK,CAAA;IAChB,OAAO,KAAK,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACzE,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,CAAA;QAC5B,KAAK,IAAI,CAAC,CAAA;IACZ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAA;AAChD,CAAC,CAAA;AAID,MAAM,QAAQ,GAAG,CAAC,MAA0B,EAAE,KAAa,EAAkC,EAAE;IAC7F,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;IACvB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC;YAChB,WAAW,CAAC,CAAC;YACb,EAAE,KAAK,EAAE,kBAAkB,IAAI,CAAC,IAAI,iEAAiE,EAAE,CAAA;IAC3G,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QACxB,MAAM,UAAU,GAAG,MAAM,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;QACpE,qEAAqE;QACrE,kEAAkE;QAClE,oDAAoD;QACpD,iEAAiE;QACjE,qEAAqE;QACrE,uEAAuE;QACvE,qEAAqE;QACrE,MAAM,IAAI,GACR,UAAU,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACvC,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;gBAChF,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC3F,cAAc,CAAA;QAClB,IAAI,IAAI,KAAK,cAAc,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAE,KAAK,EAAE,gFAAgF,EAAE,CAAA;QACpG,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAID;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAa,EAAgB,EAAE;IAC3D,MAAM,KAAK,GAAe,EAAE,CAAA;IAC5B,MAAM,WAAW,GAAqB,EAAE,CAAA;IACxC,MAAM,KAAK,GAAY,EAAE,CAAA;IACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACrC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,GAAW,EAAE,OAAe,EAAQ,EAAE;QACnE,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1C,CAAC,CAAA;IACD,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,CAAA;QACrB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC3C,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,iCAAiC,CAAC,CAAA;QACzD,CAAC;QACD,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;YAChD,OAAM;QACR,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YAC9E,OAAM;QACR,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QAClD,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAA;YACvE,OAAM;QACR,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QACpC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YAChC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;YACvE,OAAM;QACR,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;YACnE,KAAK,CAAC,GAAG,EAAE,CAAA;QACb,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,KAAK,CAAA;QAC7C,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,GAAG,IAAI,2CAA2C,CAAA;YAClE,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;YAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;YAC3D,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;QAC1B,MAAM,IAAI,GAAG,CAAC,IAAc,EAAE,QAA2B,EAAQ,EAAE;YACjE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChB,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;YAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;YAC/B,CAAC;QACH,CAAC,CAAA;QACD,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;oBACpC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;oBAC3E,OAAM;gBACR,CAAC;gBACD,IAAI,CACF,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAC3G,MAAM,CAAC,QAAQ,CAChB,CAAA;gBACD,OAAM;YACR,CAAC;YACD,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;oBACpC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;oBAC3E,OAAM;gBACR,CAAC;gBACD,IAAI,CACF,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAO,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,EACvF,MAAM,CAAC,QAAQ,CAChB,CAAA;gBACD,OAAM;YACR,CAAC;YACD,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,SAAS,GAAI,MAAM,CAAC,CAAC,CAAsB,CAAC,IAAsB,CAAA;gBACxE,MAAM,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;gBACnE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;oBACpC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;oBAC3E,OAAM;gBACR,CAAC;gBACD,IAAI,CACF,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,EAClG,MAAM,CAAC,QAAQ,CAChB,CAAA;gBACD,OAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAA;AAC/B,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAuB,EAAE;IAC1D,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAA;IACnD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,WAAW;aACvB,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;aACrE,IAAI,CAAC,IAAI,CAAC,CAAA;QACb,MAAM,IAAI,KAAK,CAAC,0BAA0B,WAAW,CAAC,MAAM,iBAAiB,MAAM,EAAE,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@cavelang/parser` — CAVE text → AST (spec §3, §4, §8, §16).
3
+ *
4
+ * Line-oriented parser for the normative CAVE language, built on
5
+ * `@prelude/parser` combinators. Produces pure syntax: inverse verbs,
6
+ * continuations and `UNLESS` are resolved later by `@cavelang/canonical`
7
+ * (spec §13.4).
8
+ *
9
+ * ```ts
10
+ * import { parseDocument } from '@cavelang/parser'
11
+ *
12
+ * const { lines, diagnostics } = parseDocument('auth/middleware USES jwt @ 90%')
13
+ * ```
14
+ */
15
+ export * as Ast from './ast.ts';
16
+ export * as Line from './line.ts';
17
+ export * as Token from './token.ts';
18
+ export { parseDocument, parse } from './document.ts';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@cavelang/parser` — CAVE text → AST (spec §3, §4, §8, §16).
3
+ *
4
+ * Line-oriented parser for the normative CAVE language, built on
5
+ * `@prelude/parser` combinators. Produces pure syntax: inverse verbs,
6
+ * continuations and `UNLESS` are resolved later by `@cavelang/canonical`
7
+ * (spec §13.4).
8
+ *
9
+ * ```ts
10
+ * import { parseDocument } from '@cavelang/parser'
11
+ *
12
+ * const { lines, diagnostics } = parseDocument('auth/middleware USES jwt @ 90%')
13
+ * ```
14
+ */
15
+ export * as Ast from "./ast.js";
16
+ export * as Line from "./line.js";
17
+ export * as Token from "./token.js";
18
+ export { parseDocument, parse } from "./document.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Token-stream line parser (spec §3.2, §16).
3
+ *
4
+ * Parses the token list of one line into claim/continuation/qualifier
5
+ * bodies. The payload/metadata boundary is decided lexically: metadata items
6
+ * are unambiguous at their first token (spec §4.3 reserved characters), so
7
+ * everything before the first metadata token is payload.
8
+ */
9
+ import type * as Ast from './ast.ts';
10
+ import type { Token } from './token.ts';
11
+ export type Result<T> = {
12
+ readonly ok: true;
13
+ readonly value: T;
14
+ readonly problems: readonly string[];
15
+ } | {
16
+ readonly ok: false;
17
+ readonly message: string;
18
+ };
19
+ /** @returns `true` if `token` starts a metadata item (spec §4.3). */
20
+ export declare const isMetaStart: (token: Token) => boolean;
21
+ /**
22
+ * Parses metadata tokens (spec §6). Never fails — unrecognized tokens are
23
+ * reported as problems and skipped, honoring the robust-extraction goal
24
+ * (spec §1.6).
25
+ */
26
+ export declare const parseMeta: (tokens: readonly Token[], comment?: string) => {
27
+ meta: Ast.Meta;
28
+ problems: string[];
29
+ };
30
+ /** Parses `verb [NOT] payload metadata` — a continuation body (spec §8.3). */
31
+ export declare const parseBody: (tokens: readonly Token[], comment?: string) => Result<Ast.Body>;
32
+ /** Parses `subject verb [NOT] payload metadata` — a full claim line (spec §16). */
33
+ export declare const parseClaim: (tokens: readonly Token[], comment?: string) => Result<Ast.Full>;
34
+ /**
35
+ * Parses a qualifier payload (spec §8.2): a full claim, a comparison, or a
36
+ * bare entity — tried in that order.
37
+ */
38
+ export declare const parseQualifierPayload: (tokens: readonly Token[], comment?: string) => Result<Ast.QualifierPayload>;
39
+ //# sourceMappingURL=line.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line.d.ts","sourceRoot":"","sources":["../../src/line.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAA;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC,MAAM,MAAM,MAAM,CAAC,CAAC,IAChB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAUpD,qEAAqE;AACrE,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,KAAG,OAOxC,CAAA;AAgCH;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,KAAK,EAAE,EAAE,UAAU,MAAM,KAAG;IAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAuG1G,CAAA;AA2ED,8EAA8E;AAC9E,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,KAAK,EAAE,EAAE,UAAU,MAAM,KAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAkBrF,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,UAAU,GAAI,QAAQ,SAAS,KAAK,EAAE,EAAE,UAAU,MAAM,KAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAatF,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,SAAS,KAAK,EAAE,EAAE,UAAU,MAAM,KAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CA0C7G,CAAA"}