@ldclabs/kip-lang 2.0.1 → 2.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/CHANGELOG.md +64 -0
- package/dist/ast.d.ts +18 -2
- package/dist/ast.d.ts.map +1 -1
- package/dist/budget.d.ts +6 -5
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +10 -6
- package/dist/budget.js.map +1 -1
- package/dist/exec-ast.d.ts +18 -1
- package/dist/exec-ast.d.ts.map +1 -1
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +80 -5
- package/dist/formatter.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +21 -1
- package/dist/lower.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +48 -1
- package/dist/parser.js.map +1 -1
- package/dist/semantics.d.ts.map +1 -1
- package/dist/semantics.js +60 -10
- package/dist/semantics.js.map +1 -1
- package/dist/token.d.ts +3 -0
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js +6 -0
- package/dist/token.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/ast.ts +19 -0
- package/src/budget.ts +10 -6
- package/src/exec-ast.ts +18 -0
- package/src/formatter.ts +79 -5
- package/src/index.ts +2 -0
- package/src/lower.ts +26 -1
- package/src/parser.ts +61 -2
- package/src/semantics.ts +65 -11
- package/src/token.ts +6 -0
- package/src/version.ts +1 -1
package/src/parser.ts
CHANGED
|
@@ -74,6 +74,7 @@ import type {
|
|
|
74
74
|
ArchiveStatement,
|
|
75
75
|
TombstoneStatement,
|
|
76
76
|
PurgeStatement,
|
|
77
|
+
PurgePayloadStatement,
|
|
77
78
|
MergeConceptStatement,
|
|
78
79
|
DescribeStatement,
|
|
79
80
|
DescribeTargetKind,
|
|
@@ -1907,10 +1908,13 @@ class Parser {
|
|
|
1907
1908
|
}
|
|
1908
1909
|
}
|
|
1909
1910
|
|
|
1910
|
-
private parsePurgeStatement(): PurgeStatement {
|
|
1911
|
+
private parsePurgeStatement(): PurgeStatement | PurgePayloadStatement {
|
|
1911
1912
|
const leadingComments = this.collectLeadingComments()
|
|
1912
1913
|
const start = this.currentPos()
|
|
1913
|
-
this.expectKeywordWithSpace(TokenType.Purge)
|
|
1914
|
+
const purge = this.expectKeywordWithSpace(TokenType.Purge)
|
|
1915
|
+
if (this.check(TokenType.Payload)) {
|
|
1916
|
+
return this.parsePurgePayloadTail(start, purge, leadingComments)
|
|
1917
|
+
}
|
|
1914
1918
|
const target = this.parseTargetRef()
|
|
1915
1919
|
|
|
1916
1920
|
let where: WhereClause | undefined
|
|
@@ -1954,6 +1958,49 @@ class Parser {
|
|
|
1954
1958
|
}
|
|
1955
1959
|
}
|
|
1956
1960
|
|
|
1961
|
+
/**
|
|
1962
|
+
* `PURGE PAYLOAD` erases Evidence bytes while the element survives
|
|
1963
|
+
* (Spec §60.6), so there is no REFERENCE POLICY clause; the confirmation
|
|
1964
|
+
* literal is the same as element purge.
|
|
1965
|
+
*/
|
|
1966
|
+
private parsePurgePayloadTail(
|
|
1967
|
+
start: Position,
|
|
1968
|
+
purge: Token,
|
|
1969
|
+
leadingComments: string[]
|
|
1970
|
+
): PurgePayloadStatement {
|
|
1971
|
+
this.expectSecondWord(TokenType.Payload, purge)
|
|
1972
|
+
const target = this.parseTargetRef()
|
|
1973
|
+
|
|
1974
|
+
let where: WhereClause | undefined
|
|
1975
|
+
if (this.check(TokenType.Where)) {
|
|
1976
|
+
this.expectKeywordWithSpace(TokenType.Where)
|
|
1977
|
+
this.dialect = 'raw'
|
|
1978
|
+
where = this.parseWhereClause()
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
const limit = this.check(TokenType.Limit) ? this.parseLimitClause() : undefined
|
|
1982
|
+
|
|
1983
|
+
this.expect(TokenType.Confirm)
|
|
1984
|
+
const confirmTok = this.current()
|
|
1985
|
+
const confirm = this.parseStringLiteral()
|
|
1986
|
+
if (confirm.parsed !== 'PURGE') {
|
|
1987
|
+
this.error(
|
|
1988
|
+
`PURGE PAYLOAD must be confirmed with the exact literal "PURGE", got ${confirmTok.value}`,
|
|
1989
|
+
confirmTok
|
|
1990
|
+
)
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
return {
|
|
1994
|
+
kind: 'PurgePayloadStatement',
|
|
1995
|
+
target,
|
|
1996
|
+
where,
|
|
1997
|
+
limit,
|
|
1998
|
+
confirm,
|
|
1999
|
+
range: { start, end: this.endPos() },
|
|
2000
|
+
leadingComments: leadingComments.length ? leadingComments : undefined
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1957
2004
|
private parseMergeConcept(): MergeConceptStatement {
|
|
1958
2005
|
const leadingComments = this.collectLeadingComments()
|
|
1959
2006
|
const start = this.currentPos()
|
|
@@ -2123,6 +2170,8 @@ class Parser {
|
|
|
2123
2170
|
|
|
2124
2171
|
let target: ListTargetKind
|
|
2125
2172
|
let status: ScalarValue | undefined
|
|
2173
|
+
let element: ScalarValue | undefined
|
|
2174
|
+
let depth: ScalarValue | undefined
|
|
2126
2175
|
|
|
2127
2176
|
switch (tok.type) {
|
|
2128
2177
|
case TokenType.Spaces:
|
|
@@ -2160,6 +2209,14 @@ class Parser {
|
|
|
2160
2209
|
target = 'EPISTEMIC_POLICIES'
|
|
2161
2210
|
break
|
|
2162
2211
|
}
|
|
2212
|
+
case TokenType.Dependents:
|
|
2213
|
+
// Bounded reverse provenance closure (Spec §63.5): the operand is
|
|
2214
|
+
// required, the DEPTH bound is optional and defaults engine-side to 1.
|
|
2215
|
+
this.expectSecondWord(TokenType.Dependents, list)
|
|
2216
|
+
target = 'DEPENDENTS'
|
|
2217
|
+
element = this.parseScalarValue()
|
|
2218
|
+
if (this.match(TokenType.Depth)) depth = this.parseScalarValue()
|
|
2219
|
+
break
|
|
2163
2220
|
default:
|
|
2164
2221
|
this.error(`Unknown LIST target '${tok.value}'`, tok)
|
|
2165
2222
|
throw new ParseAbort()
|
|
@@ -2174,6 +2231,8 @@ class Parser {
|
|
|
2174
2231
|
kind: 'ListStatement',
|
|
2175
2232
|
target,
|
|
2176
2233
|
status,
|
|
2234
|
+
element,
|
|
2235
|
+
depth,
|
|
2177
2236
|
limit,
|
|
2178
2237
|
cursor,
|
|
2179
2238
|
range: { start, end: this.endPos() },
|
package/src/semantics.ts
CHANGED
|
@@ -11,7 +11,8 @@ import type {
|
|
|
11
11
|
ScalarValue,
|
|
12
12
|
MutateStatement,
|
|
13
13
|
AssertStatement,
|
|
14
|
-
SetFacetClause
|
|
14
|
+
SetFacetClause,
|
|
15
|
+
SetStructuralClause
|
|
15
16
|
} from './ast.js'
|
|
16
17
|
import type { Range } from './token.js'
|
|
17
18
|
|
|
@@ -54,6 +55,7 @@ const ASSERTION_STATES = new Set([
|
|
|
54
55
|
'expired'
|
|
55
56
|
])
|
|
56
57
|
const ACTIVITY_TERMINAL = new Set(['completed', 'failed', 'cancelled'])
|
|
58
|
+
const EVIDENCE_ROLES = new Set(['support', 'challenge', 'context'])
|
|
57
59
|
|
|
58
60
|
/** Signals the Profile fixes to `[0,1]`; none of them is truth. */
|
|
59
61
|
const UNIT_INTERVAL_FIELDS = new Set([
|
|
@@ -96,11 +98,11 @@ function analyzeMutationClause(stmt: Statement, diags: Diagnostic[]): void {
|
|
|
96
98
|
break
|
|
97
99
|
|
|
98
100
|
case 'CreateConceptStatement':
|
|
99
|
-
for (const facet of stmt.setFacets) checkFacet(facet, diags)
|
|
100
|
-
break
|
|
101
|
-
|
|
102
101
|
case 'UpsertConceptStatement':
|
|
102
|
+
case 'CreateEvidenceStatement':
|
|
103
|
+
case 'CreateActivityStatement':
|
|
103
104
|
for (const facet of stmt.setFacets) checkFacet(facet, diags)
|
|
105
|
+
checkStructural(stmt.setStructural, diags)
|
|
104
106
|
break
|
|
105
107
|
|
|
106
108
|
case 'CreateAssertionStatement':
|
|
@@ -108,30 +110,62 @@ function analyzeMutationClause(stmt: Statement, diags: Diagnostic[]): void {
|
|
|
108
110
|
checkAssignmentValues(stmt.setFields.assignments, diags)
|
|
109
111
|
}
|
|
110
112
|
for (const facet of stmt.setFacets) checkFacet(facet, diags)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
case 'CreateEvidenceStatement':
|
|
114
|
-
case 'CreateActivityStatement':
|
|
115
|
-
for (const facet of stmt.setFacets) checkFacet(facet, diags)
|
|
113
|
+
checkStructural(stmt.setStructural, diags)
|
|
116
114
|
break
|
|
117
115
|
|
|
118
116
|
case 'UpdateStatement':
|
|
119
117
|
for (const action of stmt.actions) {
|
|
120
118
|
if (action.kind === 'SetFacetClause') checkFacet(action, diags)
|
|
119
|
+
// Core fields are Core-typed wherever they are written, so an UPDATE
|
|
120
|
+
// that sets one gets the same check `CREATE ASSERTION` already gets.
|
|
121
|
+
else if (action.kind === 'SetFieldsClause') {
|
|
122
|
+
checkAssignmentValues(action.assignments, diags)
|
|
123
|
+
} else if (action.kind === 'SetStructuralClause') {
|
|
124
|
+
checkStructural(action, diags)
|
|
125
|
+
}
|
|
121
126
|
}
|
|
122
127
|
if (stmt.where) checkWhere(stmt.where, !!stmt.limit, diags)
|
|
123
128
|
break
|
|
124
129
|
|
|
130
|
+
// `EXPECT STATE` is checkable only where the target's kind is fixed by the
|
|
131
|
+
// statement itself. RETRACT and SUPERSEDE always name an Assertion, so the
|
|
132
|
+
// Assertion lifecycle registry applies. ARCHIVE and TOMBSTONE take any
|
|
133
|
+
// element — a Concept is not `retracted` and may well be `archived` — and
|
|
134
|
+
// Core registers no element-lifecycle vocabulary, so checking them here
|
|
135
|
+
// rejected commands the Specification admits.
|
|
125
136
|
case 'RetractAssertionStatement':
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
if (stmt.expectState) {
|
|
138
|
+
checkEnum(stmt.expectState.value, ASSERTION_STATES, 'EXPECT STATE', diags)
|
|
139
|
+
}
|
|
140
|
+
if (stmt.where) checkWhere(stmt.where, !!stmt.limit, diags)
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
case 'SupersedeAssertionStatement':
|
|
128
144
|
if (stmt.expectState) {
|
|
129
145
|
checkEnum(stmt.expectState.value, ASSERTION_STATES, 'EXPECT STATE', diags)
|
|
130
146
|
}
|
|
131
147
|
break
|
|
132
148
|
|
|
149
|
+
case 'ArchiveStatement':
|
|
150
|
+
case 'TombstoneStatement':
|
|
151
|
+
if (stmt.where) checkWhere(stmt.where, !!stmt.limit, diags)
|
|
152
|
+
break
|
|
153
|
+
|
|
133
154
|
case 'TransitionActivityStatement':
|
|
134
155
|
checkEnum(stmt.to, ACTIVITY_TERMINAL, 'TRANSITION ACTIVITY TO', diags)
|
|
156
|
+
checkStructural(stmt.finalize.find(
|
|
157
|
+
(c): c is SetStructuralClause => c.kind === 'SetStructuralClause'
|
|
158
|
+
), diags)
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
// Spec §52.7 names seven statements whose WHERE can select an unbounded
|
|
162
|
+
// set and which SHOULD therefore carry a LIMIT. Warning on the read and on
|
|
163
|
+
// UPDATE while staying silent on the removal ladder had it backwards: an
|
|
164
|
+
// over-broad PURGE is the one that cannot be undone.
|
|
165
|
+
case 'PurgeStatement':
|
|
166
|
+
case 'PurgePayloadStatement':
|
|
167
|
+
case 'SetRetentionStatement':
|
|
168
|
+
if (stmt.where) checkWhere(stmt.where, !!stmt.limit, diags)
|
|
135
169
|
break
|
|
136
170
|
}
|
|
137
171
|
}
|
|
@@ -176,6 +210,26 @@ function checkFacet(clause: SetFacetClause, diags: Diagnostic[]): void {
|
|
|
176
210
|
checkAssignmentValues(clause.assignments, diags)
|
|
177
211
|
}
|
|
178
212
|
|
|
213
|
+
/**
|
|
214
|
+
* `role` on an `("evidence", ...)` citation comes from the Core registry
|
|
215
|
+
* (Spec §20.13, §56.2), so a misspelling is as checkable here as a bad
|
|
216
|
+
* `stance`. Other structural fields carry engine- or package-defined options,
|
|
217
|
+
* which only the Schema Environment can judge.
|
|
218
|
+
*/
|
|
219
|
+
function checkStructural(
|
|
220
|
+
clause: SetStructuralClause | undefined,
|
|
221
|
+
diags: Diagnostic[]
|
|
222
|
+
): void {
|
|
223
|
+
if (!clause) return
|
|
224
|
+
for (const edge of clause.assignments) {
|
|
225
|
+
if (edge.field.kind === 'ParameterRef' || edge.field.parsed !== 'evidence') {
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
const role = edge.options?.entries.find((e) => e.key === 'role')
|
|
229
|
+
if (role) checkEnum(role.value, EVIDENCE_ROLES, 'Evidence role', diags)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
179
233
|
function checkAssignmentValues(
|
|
180
234
|
object: ObjectLiteral,
|
|
181
235
|
diags: Diagnostic[]
|
package/src/token.ts
CHANGED
|
@@ -65,6 +65,7 @@ export enum TokenType {
|
|
|
65
65
|
Archive = 'ARCHIVE',
|
|
66
66
|
Tombstone = 'TOMBSTONE',
|
|
67
67
|
Purge = 'PURGE',
|
|
68
|
+
Payload = 'PAYLOAD',
|
|
68
69
|
Confirm = 'CONFIRM',
|
|
69
70
|
Reference = 'REFERENCE',
|
|
70
71
|
Policy = 'POLICY',
|
|
@@ -113,6 +114,8 @@ export enum TokenType {
|
|
|
113
114
|
Facets = 'FACETS',
|
|
114
115
|
Types = 'TYPES',
|
|
115
116
|
Policies = 'POLICIES',
|
|
117
|
+
Dependents = 'DEPENDENTS',
|
|
118
|
+
Depth = 'DEPTH',
|
|
116
119
|
Compatibility = 'COMPATIBILITY',
|
|
117
120
|
From = 'FROM',
|
|
118
121
|
Error = 'ERROR',
|
|
@@ -245,6 +248,7 @@ export const KEYWORDS: ReadonlyMap<string, TokenType> = new Map([
|
|
|
245
248
|
['ARCHIVE', TokenType.Archive],
|
|
246
249
|
['TOMBSTONE', TokenType.Tombstone],
|
|
247
250
|
['PURGE', TokenType.Purge],
|
|
251
|
+
['PAYLOAD', TokenType.Payload],
|
|
248
252
|
['CONFIRM', TokenType.Confirm],
|
|
249
253
|
['REFERENCE', TokenType.Reference],
|
|
250
254
|
['POLICY', TokenType.Policy],
|
|
@@ -293,6 +297,8 @@ export const KEYWORDS: ReadonlyMap<string, TokenType> = new Map([
|
|
|
293
297
|
['FACETS', TokenType.Facets],
|
|
294
298
|
['TYPES', TokenType.Types],
|
|
295
299
|
['POLICIES', TokenType.Policies],
|
|
300
|
+
['DEPENDENTS', TokenType.Dependents],
|
|
301
|
+
['DEPTH', TokenType.Depth],
|
|
296
302
|
['COMPATIBILITY', TokenType.Compatibility],
|
|
297
303
|
['FROM', TokenType.From],
|
|
298
304
|
['ERROR', TokenType.Error],
|
package/src/version.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* has to load in runtimes with no filesystem; `test/lower.test.mjs` asserts the
|
|
8
8
|
* two stay in step.
|
|
9
9
|
*/
|
|
10
|
-
export const PARSER_VERSION = '2.0
|
|
10
|
+
export const PARSER_VERSION = '2.1.0'
|
|
11
11
|
|
|
12
12
|
/** The KIP specification revision this grammar targets. */
|
|
13
13
|
export const KIP_SPEC_REVISION = '2.0-draft'
|