@cavelang/canonical 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 +100 -0
- package/dist/src/canonicalize.d.ts +56 -0
- package/dist/src/canonicalize.d.ts.map +1 -0
- package/dist/src/canonicalize.js +227 -0
- package/dist/src/canonicalize.js.map +1 -0
- package/dist/src/emit.d.ts +19 -0
- package/dist/src/emit.d.ts.map +1 -0
- package/dist/src/emit.js +112 -0
- package/dist/src/emit.js.map +1 -0
- package/dist/src/index.d.ts +22 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/prelude.d.ts +15 -0
- package/dist/src/prelude.d.ts.map +1 -0
- package/dist/src/prelude.js +38 -0
- package/dist/src/prelude.js.map +1 -0
- package/dist/src/registry.d.ts +62 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +86 -0
- package/dist/src/registry.js.map +1 -0
- package/package.json +43 -0
- package/src/canonicalize.ts +272 -0
- package/src/emit.ts +116 -0
- package/src/index.ts +22 -0
- package/src/prelude.ts +42 -0
- package/src/registry.ts +115 -0
package/src/prelude.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared standard prelude (spec §5.5).
|
|
3
|
+
*
|
|
4
|
+
* Standard verbs SHOULD carry inverse declarations; emitters MAY prepend
|
|
5
|
+
* them to a document or keep them in a shared prelude. This module is that
|
|
6
|
+
* shared prelude: the spec's declaration block as CAVE text, plus a
|
|
7
|
+
* pre-built registry for callers that want inverse-aware reads without
|
|
8
|
+
* ingesting the text first.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as Registry from './registry.ts'
|
|
12
|
+
|
|
13
|
+
/** The spec §5.5 declaration block, verbatim. */
|
|
14
|
+
export const standardPrelude = `REVERSE IS verb ; declares that two verbs name the same edge read in opposite directions
|
|
15
|
+
REVERSE HAS arity: 2
|
|
16
|
+
CONTAINS REVERSE PART-OF
|
|
17
|
+
CAUSE REVERSE CAUSED-BY
|
|
18
|
+
PRECEDES REVERSE FOLLOWS
|
|
19
|
+
USES REVERSE USED-BY
|
|
20
|
+
NEEDS REVERSE NEEDED-BY
|
|
21
|
+
ENABLES REVERSE ENABLED-BY
|
|
22
|
+
BLOCKS REVERSE BLOCKED-BY
|
|
23
|
+
EXTENDS REVERSE EXTENDED-BY
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
const standardPairs: readonly [string, string][] = [
|
|
27
|
+
['CONTAINS', 'PART-OF'],
|
|
28
|
+
['CAUSE', 'CAUSED-BY'],
|
|
29
|
+
['PRECEDES', 'FOLLOWS'],
|
|
30
|
+
['USES', 'USED-BY'],
|
|
31
|
+
['NEEDS', 'NEEDED-BY'],
|
|
32
|
+
['ENABLES', 'ENABLED-BY'],
|
|
33
|
+
['BLOCKS', 'BLOCKED-BY'],
|
|
34
|
+
['EXTENDS', 'EXTENDED-BY']
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
/** Registry with the standard §5.5 inverse pairs declared. */
|
|
38
|
+
export const standardRegistry: Registry.t =
|
|
39
|
+
standardPairs.reduce<Registry.t>((registry, [primary, inverse]) => {
|
|
40
|
+
const declared = Registry.declareReverse(registry, primary, inverse)
|
|
41
|
+
return declared.registry
|
|
42
|
+
}, Registry.declareVerb(Registry.empty, 'REVERSE'))
|
package/src/registry.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verb registry (spec §5.4, §5.5).
|
|
3
|
+
*
|
|
4
|
+
* Tracks in-band verb knowledge: extension verb declarations (`MIGRATES IS
|
|
5
|
+
* verb`) and inverse pairs (`CONTAINS REVERSE PART-OF`). No verb is born
|
|
6
|
+
* with an inverse — a relation without a `REVERSE` declaration simply has
|
|
7
|
+
* no reverse name.
|
|
8
|
+
*
|
|
9
|
+
* The registry is an immutable value; declaring returns a new registry, so
|
|
10
|
+
* the canonicalization pipeline can thread it through a document and
|
|
11
|
+
* declarations take effect for subsequent lines only.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Verb } from '@cavelang/core'
|
|
15
|
+
|
|
16
|
+
/** An inverse pair. The primary is the left side of the first declaration (spec §5.5). */
|
|
17
|
+
export type Pair = {
|
|
18
|
+
readonly primary: string
|
|
19
|
+
readonly inverse: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Registry = {
|
|
23
|
+
/** Every verb of every pair → its pair. */
|
|
24
|
+
readonly pairs: ReadonlyMap<string, Pair>
|
|
25
|
+
/** Extension verbs declared via `X IS verb`. */
|
|
26
|
+
readonly declared: ReadonlySet<string>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type t = Registry
|
|
30
|
+
|
|
31
|
+
/** Registry with no declarations. */
|
|
32
|
+
export const empty: Registry = {
|
|
33
|
+
pairs: new Map(),
|
|
34
|
+
declared: new Set()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type Declared =
|
|
38
|
+
| { readonly ok: true, readonly registry: Registry }
|
|
39
|
+
| { readonly ok: false, readonly registry: Registry, readonly problem: string }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Declares `a REVERSE b`: `a` is primary, `b` its inverse (spec §5.5).
|
|
43
|
+
* Redeclaring an existing pair in either direction is a no-op; a
|
|
44
|
+
* declaration that conflicts with an existing pair is rejected — the first
|
|
45
|
+
* declaration wins.
|
|
46
|
+
*/
|
|
47
|
+
export const declareReverse = (registry: Registry, a: string, b: string): Declared => {
|
|
48
|
+
if (!Verb.isVerbToken(a) || !Verb.isVerbToken(b)) {
|
|
49
|
+
return { ok: false, registry, problem: `REVERSE operands must be UPPERCASE verbs, got ${a} REVERSE ${b}` }
|
|
50
|
+
}
|
|
51
|
+
const existingA = registry.pairs.get(a)
|
|
52
|
+
const existingB = registry.pairs.get(b)
|
|
53
|
+
const existing = existingA ?? existingB
|
|
54
|
+
if (existing !== undefined) {
|
|
55
|
+
const same =
|
|
56
|
+
(existing.primary === a && existing.inverse === b) ||
|
|
57
|
+
(existing.primary === b && existing.inverse === a)
|
|
58
|
+
if (same && existingA === existingB) {
|
|
59
|
+
return { ok: true, registry }
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
registry,
|
|
64
|
+
problem: `${a} REVERSE ${b} conflicts with existing ${existing.primary} REVERSE ${existing.inverse} — first declaration wins (spec §5.5)`
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const pair: Pair = { primary: a, inverse: b }
|
|
68
|
+
const pairs = new Map(registry.pairs)
|
|
69
|
+
pairs.set(a, pair)
|
|
70
|
+
pairs.set(b, pair)
|
|
71
|
+
return { ok: true, registry: { pairs, declared: registry.declared } }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Declares an extension verb (`X IS verb`, spec §5.4). */
|
|
75
|
+
export const declareVerb = (registry: Registry, verb: string): Registry => {
|
|
76
|
+
if (registry.declared.has(verb)) {
|
|
77
|
+
return registry
|
|
78
|
+
}
|
|
79
|
+
const declared = new Set(registry.declared)
|
|
80
|
+
declared.add(verb)
|
|
81
|
+
return { pairs: registry.pairs, declared }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @returns the canonical primary of `verb` and whether `verb` is the
|
|
86
|
+
* inverse side. An unpaired verb is its own primary.
|
|
87
|
+
*/
|
|
88
|
+
export const primaryOf = (registry: Registry, verb: string): { primary: string, isInverse: boolean } => {
|
|
89
|
+
const pair = registry.pairs.get(verb)
|
|
90
|
+
if (pair === undefined || pair.primary === verb) {
|
|
91
|
+
return { primary: verb, isInverse: false }
|
|
92
|
+
}
|
|
93
|
+
return { primary: pair.primary, isInverse: true }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @returns the opposite name of `verb` — `CONTAINS` → `PART-OF`,
|
|
98
|
+
* `PART-OF` → `CONTAINS` — or `undefined` when no inverse is declared
|
|
99
|
+
* (reverse reads then fall back to an un-named object-side scan, spec §5.5).
|
|
100
|
+
*/
|
|
101
|
+
export const inverseOf = (registry: Registry, verb: string): undefined | string => {
|
|
102
|
+
const pair = registry.pairs.get(verb)
|
|
103
|
+
if (pair === undefined) {
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
106
|
+
return pair.primary === verb ? pair.inverse : pair.primary
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** @returns `true` if `verb` was declared as an extension verb. */
|
|
110
|
+
export const isDeclared = (registry: Registry, verb: string): boolean =>
|
|
111
|
+
registry.declared.has(verb)
|
|
112
|
+
|
|
113
|
+
/** All pairs, deduplicated, in insertion order. */
|
|
114
|
+
export const allPairs = (registry: Registry): Pair[] =>
|
|
115
|
+
[...new Set(registry.pairs.values())]
|