@cavelang/canonical 0.17.0 → 0.22.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.
@@ -11,9 +11,42 @@ import { Claim } from '@cavelang/core';
11
11
  import type * as Canonicalize from './canonicalize.ts';
12
12
  /** @returns one canonical line for a claim (no indentation). */
13
13
  export declare const emitClaim: (claim: Claim.t) => string;
14
+ /**
15
+ * A transaction annotation (spec §28.4): the full-line comment placed
16
+ * immediately above a claim line to carry its transaction id through
17
+ * canonical text. Comment lines are transparent to the grammar (§8), so
18
+ * annotated text reads unchanged everywhere; sync-aware readers pair each
19
+ * annotation with the claim line below it.
20
+ */
21
+ export declare const txComment: (tx: string) => string;
22
+ /**
23
+ * @returns the transaction id carried by a raw line when it is a §28.4
24
+ * annotation (`;@ <tx>`), `undefined` otherwise. Purely lexical — the
25
+ * caller validates the id shape.
26
+ */
27
+ export declare const txOfLine: (raw: string) => undefined | string;
28
+ export type EmitOptions = {
29
+ /**
30
+ * Per-claim annotation lines (spec §28.4): when defined for a claim
31
+ * index, the returned text is emitted verbatim as its own line directly
32
+ * above that claim, at the claim's indentation. Used by tx-carrying
33
+ * export ({@link txComment}); return `undefined` to annotate nothing.
34
+ */
35
+ readonly annotate?: (index: number) => undefined | string;
36
+ };
14
37
  /**
15
38
  * Emits a whole canonicalization result as canonical CAVE text: top-level
16
39
  * claims in claim order, children indented two spaces per level.
40
+ *
41
+ * Edges form a graph, text forms a tree, and the reconciliation is the
42
+ * *re-statement*: a claim's own children render exactly once — at its
43
+ * first appearance — and every later appearance (a row cited by several
44
+ * parents, §24.3 shared premises and `VIA` rules; or a §24.5 support
45
+ * cycle) is the claim line alone, restating the row to carry that one
46
+ * edge. With annotations the repeats share one id, so replay unions them
47
+ * back into a single row (§28.4); a component with no top-level member
48
+ * (a pure cycle) is emitted from its first claim, the cycle breaking at
49
+ * the re-statement.
17
50
  */
18
- export declare const emit: (result: Pick<Canonicalize.Result, "claims" | "edges">) => string;
51
+ export declare const emit: (result: Pick<Canonicalize.Result, "claims" | "edges">, options?: EmitOptions) => string;
19
52
  //# sourceMappingURL=emit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAA0B,MAAM,gBAAgB,CAAA;AAC9D,OAAO,KAAK,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAyCtD,gEAAgE;AAChE,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,CAAC,CAAC,KAAG,MAW1C,CAAA;AAgBD;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAI,QAAQ,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAG,MA+B5E,CAAA"}
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAA0B,MAAM,gBAAgB,CAAA;AAC9D,OAAO,KAAK,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAyCtD,gEAAgE;AAChE,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,CAAC,CAAC,KAAG,MAW1C,CAAA;AAgBD;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAI,IAAI,MAAM,KAAG,MAC3B,CAAA;AAIZ;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,KAAG,SAAS,GAAG,MAC1B,CAAA;AAEzB,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,GAAG,MAAM,CAAA;CAC1D,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,IAAI,GAAI,QAAQ,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,EAAE,UAAS,WAAgB,KAAG,MA6CvG,CAAA"}
package/dist/src/emit.js CHANGED
@@ -71,11 +71,36 @@ const conditionText = (claim) => {
71
71
  emitClaim({ ...claim, negated: false });
72
72
  return claim.negated ? `NOT ${body}` : body;
73
73
  };
74
+ /**
75
+ * A transaction annotation (spec §28.4): the full-line comment placed
76
+ * immediately above a claim line to carry its transaction id through
77
+ * canonical text. Comment lines are transparent to the grammar (§8), so
78
+ * annotated text reads unchanged everywhere; sync-aware readers pair each
79
+ * annotation with the claim line below it.
80
+ */
81
+ export const txComment = (tx) => `;@ ${tx}`;
82
+ const txLineRe = /^\s*;@\s+(\S+)\s*$/;
83
+ /**
84
+ * @returns the transaction id carried by a raw line when it is a §28.4
85
+ * annotation (`;@ <tx>`), `undefined` otherwise. Purely lexical — the
86
+ * caller validates the id shape.
87
+ */
88
+ export const txOfLine = (raw) => txLineRe.exec(raw)?.[1];
74
89
  /**
75
90
  * Emits a whole canonicalization result as canonical CAVE text: top-level
76
91
  * claims in claim order, children indented two spaces per level.
92
+ *
93
+ * Edges form a graph, text forms a tree, and the reconciliation is the
94
+ * *re-statement*: a claim's own children render exactly once — at its
95
+ * first appearance — and every later appearance (a row cited by several
96
+ * parents, §24.3 shared premises and `VIA` rules; or a §24.5 support
97
+ * cycle) is the claim line alone, restating the row to carry that one
98
+ * edge. With annotations the repeats share one id, so replay unions them
99
+ * back into a single row (§28.4); a component with no top-level member
100
+ * (a pure cycle) is emitted from its first claim, the cycle breaking at
101
+ * the re-statement.
77
102
  */
78
- export const emit = (result) => {
103
+ export const emit = (result, options = {}) => {
79
104
  const childEdges = new Map();
80
105
  const isChild = new Set();
81
106
  for (const edge of result.edges) {
@@ -89,15 +114,24 @@ export const emit = (result) => {
89
114
  }
90
115
  }
91
116
  const lines = [];
117
+ const expanded = new Set();
92
118
  const emitAt = (index, depth, role) => {
93
119
  const { claim } = result.claims[index];
94
120
  const indent = ' '.repeat(depth);
121
+ const annotation = options.annotate?.(index);
122
+ if (annotation !== undefined) {
123
+ lines.push(`${indent}${annotation}`);
124
+ }
95
125
  if (role === undefined || role === 'QUALIFIES') {
96
126
  lines.push(`${indent}${emitClaim(claim)}`);
97
127
  }
98
128
  else {
99
129
  lines.push(`${indent}${role} ${conditionText(claim)}`);
100
130
  }
131
+ if (expanded.has(index)) {
132
+ return;
133
+ }
134
+ expanded.add(index);
101
135
  for (const edge of childEdges.get(index) ?? []) {
102
136
  emitAt(edge.child, depth + 1, edge.role);
103
137
  }
@@ -107,6 +141,11 @@ export const emit = (result) => {
107
141
  emitAt(index, 0, undefined);
108
142
  }
109
143
  });
144
+ result.claims.forEach((_, index) => {
145
+ if (!expanded.has(index)) {
146
+ emitAt(index, 0, undefined);
147
+ }
148
+ });
110
149
  return lines.length === 0 ? '' : `${lines.join('\n')}\n`;
111
150
  };
112
151
  //# sourceMappingURL=emit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"emit.js","sourceRoot":"","sources":["../../src/emit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"emit.js","sourceRoot":"","sources":["../../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAG9D,MAAM,WAAW,GAAG,CAAC,OAAsB,EAAsB,EAAE;IACjE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACzC,KAAK,WAAW;YACd,OAAO,GAAG,OAAO,CAAC,SAAS,KAAK,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAA;QAC/D,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACpC,KAAK,MAAM;YACT,OAAO,SAAS,CAAA;IACpB,CAAC;AACH,CAAC,CAAA;AAED,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAY,EAAE;IAC5C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAChD,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,CAAA;IACtC,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC,CAAA;IAC3B,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAClD,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAU,EAAE;IAClD,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IAC3D,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACrB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;IAC9B,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,CAAC,KAAc,EAAU,EAAE;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;QACrE,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACjE,SAAS,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAA;IACzC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC7C,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAU,EAAU,EAAE,CAC9C,MAAM,EAAE,EAAE,CAAA;AAEZ,MAAM,QAAQ,GAAG,oBAAoB,CAAA;AAErC;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAsB,EAAE,CAC1D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;AAYzB;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,MAAqD,EAAE,UAAuB,EAAE,EAAU,EAAE;IAC/G,MAAM,UAAU,GAAG,IAAI,GAAG,EAA+B,CAAA;IACzD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;QACrC,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAClC,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,IAAuC,EAAQ,EAAE;QAC7F,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAE,CAAA;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACjC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAA;QAC5C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,UAAU,EAAE,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC5C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACxD,CAAC;QACD,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAM;QACR,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnB,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC,CAAA;IACD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC,CAAC,CAAA;IACF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;AAC1D,CAAC,CAAA"}
@@ -17,6 +17,7 @@
17
17
  export * as Registry from './registry.ts';
18
18
  export { canonicalize, canonicalizeText } from './canonicalize.ts';
19
19
  export type { Edge, EdgeRole, Entry, Problem, Result } from './canonicalize.ts';
20
- export { emit, emitClaim } from './emit.ts';
20
+ export { emit, emitClaim, txComment, txOfLine } from './emit.ts';
21
+ export type { EmitOptions } from './emit.ts';
21
22
  export { standardPrelude, standardRegistry } from './prelude.ts';
22
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAClE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/E,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAClE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/E,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAChE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
package/dist/src/index.js CHANGED
@@ -16,6 +16,6 @@
16
16
  */
17
17
  export * as Registry from "./registry.js";
18
18
  export { canonicalize, canonicalizeText } from "./canonicalize.js";
19
- export { emit, emitClaim } from "./emit.js";
19
+ export { emit, emitClaim, txComment, txOfLine } from "./emit.js";
20
20
  export { standardPrelude, standardRegistry } from "./prelude.js";
21
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEhE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cavelang/canonical",
3
- "version": "0.17.0",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "description": "CAVE canonicalization — verb registry, REVERSE inverse resolution, continuation expansion, canonical emitter.",
6
6
  "license": "CC0-1.0",
@@ -32,8 +32,8 @@
32
32
  "access": "public"
33
33
  },
34
34
  "dependencies": {
35
- "@cavelang/core": "0.17.0",
36
- "@cavelang/parser": "0.17.0"
35
+ "@cavelang/parser": "0.22.0",
36
+ "@cavelang/core": "0.22.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsc -b",
package/src/emit.ts CHANGED
@@ -78,11 +78,51 @@ const conditionText = (claim: Claim.t): string => {
78
78
  return claim.negated ? `NOT ${body}` : body
79
79
  }
80
80
 
81
+ /**
82
+ * A transaction annotation (spec §28.4): the full-line comment placed
83
+ * immediately above a claim line to carry its transaction id through
84
+ * canonical text. Comment lines are transparent to the grammar (§8), so
85
+ * annotated text reads unchanged everywhere; sync-aware readers pair each
86
+ * annotation with the claim line below it.
87
+ */
88
+ export const txComment = (tx: string): string =>
89
+ `;@ ${tx}`
90
+
91
+ const txLineRe = /^\s*;@\s+(\S+)\s*$/
92
+
93
+ /**
94
+ * @returns the transaction id carried by a raw line when it is a §28.4
95
+ * annotation (`;@ <tx>`), `undefined` otherwise. Purely lexical — the
96
+ * caller validates the id shape.
97
+ */
98
+ export const txOfLine = (raw: string): undefined | string =>
99
+ txLineRe.exec(raw)?.[1]
100
+
101
+ export type EmitOptions = {
102
+ /**
103
+ * Per-claim annotation lines (spec §28.4): when defined for a claim
104
+ * index, the returned text is emitted verbatim as its own line directly
105
+ * above that claim, at the claim's indentation. Used by tx-carrying
106
+ * export ({@link txComment}); return `undefined` to annotate nothing.
107
+ */
108
+ readonly annotate?: (index: number) => undefined | string
109
+ }
110
+
81
111
  /**
82
112
  * Emits a whole canonicalization result as canonical CAVE text: top-level
83
113
  * claims in claim order, children indented two spaces per level.
114
+ *
115
+ * Edges form a graph, text forms a tree, and the reconciliation is the
116
+ * *re-statement*: a claim's own children render exactly once — at its
117
+ * first appearance — and every later appearance (a row cited by several
118
+ * parents, §24.3 shared premises and `VIA` rules; or a §24.5 support
119
+ * cycle) is the claim line alone, restating the row to carry that one
120
+ * edge. With annotations the repeats share one id, so replay unions them
121
+ * back into a single row (§28.4); a component with no top-level member
122
+ * (a pure cycle) is emitted from its first claim, the cycle breaking at
123
+ * the re-statement.
84
124
  */
85
- export const emit = (result: Pick<Canonicalize.Result, 'claims' | 'edges'>): string => {
125
+ export const emit = (result: Pick<Canonicalize.Result, 'claims' | 'edges'>, options: EmitOptions = {}): string => {
86
126
  const childEdges = new Map<number, Canonicalize.Edge[]>()
87
127
  const isChild = new Set<number>()
88
128
  for (const edge of result.edges) {
@@ -95,14 +135,23 @@ export const emit = (result: Pick<Canonicalize.Result, 'claims' | 'edges'>): str
95
135
  }
96
136
  }
97
137
  const lines: string[] = []
138
+ const expanded = new Set<number>()
98
139
  const emitAt = (index: number, depth: number, role: undefined | Canonicalize.EdgeRole): void => {
99
140
  const { claim } = result.claims[index]!
100
141
  const indent = ' '.repeat(depth)
142
+ const annotation = options.annotate?.(index)
143
+ if (annotation !== undefined) {
144
+ lines.push(`${indent}${annotation}`)
145
+ }
101
146
  if (role === undefined || role === 'QUALIFIES') {
102
147
  lines.push(`${indent}${emitClaim(claim)}`)
103
148
  } else {
104
149
  lines.push(`${indent}${role} ${conditionText(claim)}`)
105
150
  }
151
+ if (expanded.has(index)) {
152
+ return
153
+ }
154
+ expanded.add(index)
106
155
  for (const edge of childEdges.get(index) ?? []) {
107
156
  emitAt(edge.child, depth + 1, edge.role)
108
157
  }
@@ -112,5 +161,10 @@ export const emit = (result: Pick<Canonicalize.Result, 'claims' | 'edges'>): str
112
161
  emitAt(index, 0, undefined)
113
162
  }
114
163
  })
164
+ result.claims.forEach((_, index) => {
165
+ if (!expanded.has(index)) {
166
+ emitAt(index, 0, undefined)
167
+ }
168
+ })
115
169
  return lines.length === 0 ? '' : `${lines.join('\n')}\n`
116
170
  }
package/src/index.ts CHANGED
@@ -18,5 +18,6 @@
18
18
  export * as Registry from './registry.ts'
19
19
  export { canonicalize, canonicalizeText } from './canonicalize.ts'
20
20
  export type { Edge, EdgeRole, Entry, Problem, Result } from './canonicalize.ts'
21
- export { emit, emitClaim } from './emit.ts'
21
+ export { emit, emitClaim, txComment, txOfLine } from './emit.ts'
22
+ export type { EmitOptions } from './emit.ts'
22
23
  export { standardPrelude, standardRegistry } from './prelude.ts'