@dxos/datalog 0.0.1 → 0.12.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 +105 -0
- package/README.md +58 -1
- package/dist/lib/Ast.mjs +72 -0
- package/dist/lib/Ast.mjs.map +1 -0
- package/dist/lib/Builtin.mjs +37 -0
- package/dist/lib/Builtin.mjs.map +1 -0
- package/dist/lib/Checker.mjs +3 -0
- package/dist/lib/Engine.mjs +3 -0
- package/dist/lib/Parser.mjs +2 -0
- package/dist/lib/chunk-Checker.mjs +414 -0
- package/dist/lib/chunk-Checker.mjs.map +1 -0
- package/dist/lib/chunk-Engine.mjs +488 -0
- package/dist/lib/chunk-Engine.mjs.map +1 -0
- package/dist/lib/chunk-Parser.mjs +366 -0
- package/dist/lib/chunk-Parser.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/index.mjs +6 -0
- package/dist/types/src/Ast.d.ts +81 -0
- package/dist/types/src/Ast.d.ts.map +1 -0
- package/dist/types/src/Builtin.d.ts +35 -0
- package/dist/types/src/Builtin.d.ts.map +1 -0
- package/dist/types/src/Checker.d.ts +61 -0
- package/dist/types/src/Checker.d.ts.map +1 -0
- package/dist/types/src/Checker.test.d.ts +2 -0
- package/dist/types/src/Checker.test.d.ts.map +1 -0
- package/dist/types/src/Engine.d.ts +55 -0
- package/dist/types/src/Engine.d.ts.map +1 -0
- package/dist/types/src/Engine.test.d.ts +2 -0
- package/dist/types/src/Engine.test.d.ts.map +1 -0
- package/dist/types/src/Engine.workerd.test.d.ts +2 -0
- package/dist/types/src/Engine.workerd.test.d.ts.map +1 -0
- package/dist/types/src/Parser.d.ts +43 -0
- package/dist/types/src/Parser.d.ts.map +1 -0
- package/dist/types/src/Parser.test.d.ts +2 -0
- package/dist/types/src/Parser.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/internal/lexer.d.ts +46 -0
- package/dist/types/src/internal/lexer.d.ts.map +1 -0
- package/dist/types/src/internal/plan.d.ts +96 -0
- package/dist/types/src/internal/plan.d.ts.map +1 -0
- package/dist/types/src/internal/relation.d.ts +20 -0
- package/dist/types/src/internal/relation.d.ts.map +1 -0
- package/dist/types/src/internal/stratify.d.ts +28 -0
- package/dist/types/src/internal/stratify.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +57 -5
- package/src/Ast.ts +136 -0
- package/src/Builtin.ts +73 -0
- package/src/Checker.test.ts +99 -0
- package/src/Checker.ts +156 -0
- package/src/Engine.test.ts +239 -0
- package/src/Engine.ts +614 -0
- package/src/Engine.workerd.test.ts +33 -0
- package/src/Parser.test.ts +84 -0
- package/src/Parser.ts +180 -0
- package/src/index.ts +9 -0
- package/src/internal/lexer.ts +154 -0
- package/src/internal/plan.ts +346 -0
- package/src/internal/relation.ts +95 -0
- package/src/internal/stratify.ts +114 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
|
+
import { BaseError } from "@dxos/errors";
|
|
3
|
+
//#region src/internal/lexer.ts
|
|
4
|
+
/** A malformed token with the 1-based position where it starts. */
|
|
5
|
+
var LexError = class extends BaseError.extend("LexError", "Invalid token") {
|
|
6
|
+
reason;
|
|
7
|
+
position;
|
|
8
|
+
constructor(reason, position) {
|
|
9
|
+
super({
|
|
10
|
+
message: reason,
|
|
11
|
+
context: {
|
|
12
|
+
line: position.line,
|
|
13
|
+
column: position.column
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
this.reason = reason;
|
|
17
|
+
this.position = position;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var PUNCTUATION = {
|
|
21
|
+
":": ":",
|
|
22
|
+
"(": "(",
|
|
23
|
+
")": ")",
|
|
24
|
+
"{": "{",
|
|
25
|
+
"}": "}",
|
|
26
|
+
",": ",",
|
|
27
|
+
".": ".",
|
|
28
|
+
"!": "!"
|
|
29
|
+
};
|
|
30
|
+
var ESCAPES = {
|
|
31
|
+
"n": "\n",
|
|
32
|
+
"t": " ",
|
|
33
|
+
"r": "\r",
|
|
34
|
+
"\\": "\\",
|
|
35
|
+
"\"": "\"",
|
|
36
|
+
"'": "'"
|
|
37
|
+
};
|
|
38
|
+
/** Splits dialect source into tokens with 1-based positions. */
|
|
39
|
+
var tokenize = (source) => {
|
|
40
|
+
const tokens = [];
|
|
41
|
+
let offset = 0;
|
|
42
|
+
let line = 1;
|
|
43
|
+
let lineStart = 0;
|
|
44
|
+
const here = () => ({
|
|
45
|
+
line,
|
|
46
|
+
column: offset - lineStart + 1
|
|
47
|
+
});
|
|
48
|
+
const advance = (count) => {
|
|
49
|
+
for (let index = 0; index < count; index++) {
|
|
50
|
+
if (source[offset] === "\n") {
|
|
51
|
+
line++;
|
|
52
|
+
lineStart = offset + 1;
|
|
53
|
+
}
|
|
54
|
+
offset++;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
while (offset < source.length) {
|
|
58
|
+
const char = source[offset];
|
|
59
|
+
const rest = source.slice(offset);
|
|
60
|
+
if (/\s/.test(char)) {
|
|
61
|
+
advance(1);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (char === "%" || rest.startsWith("//")) {
|
|
65
|
+
while (offset < source.length && source[offset] !== "\n") advance(1);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (rest.startsWith("/*")) {
|
|
69
|
+
const start = here();
|
|
70
|
+
const end = source.indexOf("*/", offset + 2);
|
|
71
|
+
if (end < 0) throw new LexError("Unterminated comment", start);
|
|
72
|
+
advance(end + 2 - offset);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const position = here();
|
|
76
|
+
if (char === "\"" || char === "'") {
|
|
77
|
+
let value = "";
|
|
78
|
+
let cursor = offset + 1;
|
|
79
|
+
while (cursor < source.length && source[cursor] !== char) {
|
|
80
|
+
if (source[cursor] === "\n") throw new LexError("Unterminated string", position);
|
|
81
|
+
if (source[cursor] === "\\") {
|
|
82
|
+
const escaped = source[cursor + 1];
|
|
83
|
+
value += ESCAPES[escaped] ?? escaped ?? "";
|
|
84
|
+
cursor += 2;
|
|
85
|
+
} else value += source[cursor++];
|
|
86
|
+
}
|
|
87
|
+
if (cursor >= source.length) throw new LexError("Unterminated string", position);
|
|
88
|
+
tokens.push({
|
|
89
|
+
type: "string",
|
|
90
|
+
text: source.slice(offset, cursor + 1),
|
|
91
|
+
value,
|
|
92
|
+
position
|
|
93
|
+
});
|
|
94
|
+
advance(cursor + 1 - offset);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const numeric = /^-?\d+(\.\d+)?(?![A-Za-z_\d])/.exec(rest);
|
|
98
|
+
if (numeric) {
|
|
99
|
+
tokens.push({
|
|
100
|
+
type: "number",
|
|
101
|
+
text: numeric[0],
|
|
102
|
+
value: Number(numeric[0]),
|
|
103
|
+
position
|
|
104
|
+
});
|
|
105
|
+
advance(numeric[0].length);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const word = /^[A-Za-z0-9_][A-Za-z0-9_]*/.exec(rest);
|
|
109
|
+
if (word) {
|
|
110
|
+
const text = word[0];
|
|
111
|
+
const type = /^[A-Z_]/.test(text) ? "variable" : "identifier";
|
|
112
|
+
tokens.push({
|
|
113
|
+
type,
|
|
114
|
+
text,
|
|
115
|
+
value: type === "identifier" ? text : void 0,
|
|
116
|
+
position
|
|
117
|
+
});
|
|
118
|
+
advance(text.length);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const operator = /^(<=|>=|!=|=|<|>)/.exec(rest);
|
|
122
|
+
if (operator) {
|
|
123
|
+
tokens.push({
|
|
124
|
+
type: "operator",
|
|
125
|
+
text: operator[0],
|
|
126
|
+
position
|
|
127
|
+
});
|
|
128
|
+
advance(operator[0].length);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (rest.startsWith(":-")) {
|
|
132
|
+
tokens.push({
|
|
133
|
+
type: ":-",
|
|
134
|
+
text: ":-",
|
|
135
|
+
position
|
|
136
|
+
});
|
|
137
|
+
advance(2);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const punctuation = PUNCTUATION[char];
|
|
141
|
+
if (punctuation) {
|
|
142
|
+
tokens.push({
|
|
143
|
+
type: punctuation,
|
|
144
|
+
text: char,
|
|
145
|
+
position
|
|
146
|
+
});
|
|
147
|
+
advance(1);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
throw new LexError(`Unexpected character '${char}'`, position);
|
|
151
|
+
}
|
|
152
|
+
return tokens;
|
|
153
|
+
};
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/Parser.ts
|
|
156
|
+
var Parser_exports = /* @__PURE__ */ __exportAll({
|
|
157
|
+
ParseError: () => ParseError,
|
|
158
|
+
parse: () => parse
|
|
159
|
+
});
|
|
160
|
+
/** A syntax error with the 1-based position of the offending token. */
|
|
161
|
+
var ParseError = class extends BaseError.extend("ParseError", "Syntax error") {
|
|
162
|
+
reason;
|
|
163
|
+
position;
|
|
164
|
+
constructor(reason, position) {
|
|
165
|
+
super({
|
|
166
|
+
message: reason,
|
|
167
|
+
context: {
|
|
168
|
+
line: position.line,
|
|
169
|
+
column: position.column
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
this.reason = reason;
|
|
173
|
+
this.position = position;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var AGGREGATES = [
|
|
177
|
+
"count",
|
|
178
|
+
"min",
|
|
179
|
+
"max",
|
|
180
|
+
"sum"
|
|
181
|
+
];
|
|
182
|
+
var isAggregateFunction = (text) => AGGREGATES.some((name) => name === text);
|
|
183
|
+
var OPERATORS = [
|
|
184
|
+
"=",
|
|
185
|
+
"!=",
|
|
186
|
+
"<",
|
|
187
|
+
"<=",
|
|
188
|
+
">",
|
|
189
|
+
">="
|
|
190
|
+
];
|
|
191
|
+
var toOperator = (text) => OPERATORS.find((op) => op === text);
|
|
192
|
+
/**
|
|
193
|
+
* Parses dialect source (Soufflé-like: `head :- body.`, `not`/`!` negation, comparisons and
|
|
194
|
+
* `N = count : { … }` aggregates) into a program.
|
|
195
|
+
* @throws ParseError on the first syntax error.
|
|
196
|
+
*/
|
|
197
|
+
var parse = (source) => {
|
|
198
|
+
let tokens;
|
|
199
|
+
try {
|
|
200
|
+
tokens = tokenize(source);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
if (error instanceof LexError) throw new ParseError(error.reason, error.position);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
let cursor = 0;
|
|
206
|
+
let anonymous = 0;
|
|
207
|
+
const end = () => {
|
|
208
|
+
const last = tokens.at(-1);
|
|
209
|
+
return last ? {
|
|
210
|
+
line: last.position.line,
|
|
211
|
+
column: last.position.column + last.text.length
|
|
212
|
+
} : {
|
|
213
|
+
line: 1,
|
|
214
|
+
column: 1
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
const peek = (ahead = 0) => tokens[cursor + ahead];
|
|
218
|
+
const fail = (expected, token = peek()) => {
|
|
219
|
+
throw new ParseError(token ? `Expected ${expected} but found '${token.text}'` : `Expected ${expected} but reached end of input`, token?.position ?? end());
|
|
220
|
+
};
|
|
221
|
+
const expect = (type, description = `'${type}'`) => {
|
|
222
|
+
const token = peek();
|
|
223
|
+
if (!token || token.type !== type) return fail(description);
|
|
224
|
+
cursor++;
|
|
225
|
+
return token;
|
|
226
|
+
};
|
|
227
|
+
const term = () => {
|
|
228
|
+
const token = peek();
|
|
229
|
+
switch (token?.type) {
|
|
230
|
+
case "variable":
|
|
231
|
+
cursor++;
|
|
232
|
+
return token.text === "_" ? {
|
|
233
|
+
type: "variable",
|
|
234
|
+
name: `_${anonymous++}`,
|
|
235
|
+
anonymous: true
|
|
236
|
+
} : {
|
|
237
|
+
type: "variable",
|
|
238
|
+
name: token.text
|
|
239
|
+
};
|
|
240
|
+
case "identifier":
|
|
241
|
+
case "string":
|
|
242
|
+
case "number":
|
|
243
|
+
cursor++;
|
|
244
|
+
return {
|
|
245
|
+
type: "constant",
|
|
246
|
+
value: token.value ?? token.text
|
|
247
|
+
};
|
|
248
|
+
default: return fail("a term");
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
const atom = () => {
|
|
252
|
+
const name = expect("identifier", "a predicate name");
|
|
253
|
+
const terms = [];
|
|
254
|
+
if (peek()?.type === "(") {
|
|
255
|
+
cursor++;
|
|
256
|
+
if (peek()?.type !== ")") {
|
|
257
|
+
terms.push(term());
|
|
258
|
+
while (peek()?.type === ",") {
|
|
259
|
+
cursor++;
|
|
260
|
+
terms.push(term());
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
expect(")", "',' or ')'");
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
predicate: name.text,
|
|
267
|
+
terms
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
const aggregate = (result, position) => {
|
|
271
|
+
const name = expect("identifier", "an aggregate function");
|
|
272
|
+
if (!isAggregateFunction(name.text)) return fail("count, min, max or sum", name);
|
|
273
|
+
const target = name.text === "count" ? void 0 : term();
|
|
274
|
+
expect(":", "':'");
|
|
275
|
+
let body;
|
|
276
|
+
if (peek()?.type === "{") {
|
|
277
|
+
cursor++;
|
|
278
|
+
body = conjunction();
|
|
279
|
+
expect("}", "',' or '}'");
|
|
280
|
+
} else body = [literal()];
|
|
281
|
+
if (body.some((item) => item.type === "aggregate")) throw new ParseError("Aggregates cannot be nested", position);
|
|
282
|
+
return {
|
|
283
|
+
type: "aggregate",
|
|
284
|
+
function: name.text,
|
|
285
|
+
result,
|
|
286
|
+
target,
|
|
287
|
+
body,
|
|
288
|
+
position
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
/** `fn :` or `fn Term :` after `=`; unknown function names are reported by `aggregate`. */
|
|
292
|
+
const isAggregateAhead = () => peek()?.type === "identifier" && (peek(1)?.type === ":" || peek(2)?.type === ":");
|
|
293
|
+
const literal = () => {
|
|
294
|
+
const token = peek();
|
|
295
|
+
if (!token) return fail("a literal");
|
|
296
|
+
const position = token.position;
|
|
297
|
+
if (token.type === "!" || token.type === "identifier" && token.text === "not" && peek(1)?.type !== ":-") {
|
|
298
|
+
cursor++;
|
|
299
|
+
if (peek()?.type === "(" && token.type === "identifier") {
|
|
300
|
+
cursor++;
|
|
301
|
+
const negated = atom();
|
|
302
|
+
expect(")", "')'");
|
|
303
|
+
return {
|
|
304
|
+
type: "atom",
|
|
305
|
+
atom: negated,
|
|
306
|
+
negated: true,
|
|
307
|
+
position
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
type: "atom",
|
|
312
|
+
atom: atom(),
|
|
313
|
+
negated: true,
|
|
314
|
+
position
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
if (peek(1)?.type === "operator" || token.type !== "identifier") {
|
|
318
|
+
const left = term();
|
|
319
|
+
const operatorToken = expect("operator", "a comparison operator");
|
|
320
|
+
const operator = toOperator(operatorToken.text) ?? fail("a comparison operator", operatorToken);
|
|
321
|
+
if (operator === "=" && isAggregateAhead()) return aggregate(left, position);
|
|
322
|
+
return {
|
|
323
|
+
type: "comparison",
|
|
324
|
+
operator,
|
|
325
|
+
left,
|
|
326
|
+
right: term(),
|
|
327
|
+
position
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
type: "atom",
|
|
332
|
+
atom: atom(),
|
|
333
|
+
negated: false,
|
|
334
|
+
position
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
const conjunction = () => {
|
|
338
|
+
const literals = [literal()];
|
|
339
|
+
while (peek()?.type === ",") {
|
|
340
|
+
cursor++;
|
|
341
|
+
literals.push(literal());
|
|
342
|
+
}
|
|
343
|
+
return literals;
|
|
344
|
+
};
|
|
345
|
+
const rules = [];
|
|
346
|
+
while (cursor < tokens.length) {
|
|
347
|
+
const position = peek()?.position;
|
|
348
|
+
const head = atom();
|
|
349
|
+
let body = [];
|
|
350
|
+
if (peek()?.type === ":-") {
|
|
351
|
+
cursor++;
|
|
352
|
+
body = conjunction();
|
|
353
|
+
}
|
|
354
|
+
expect(".", "'.', ',' or ':-'");
|
|
355
|
+
rules.push({
|
|
356
|
+
head,
|
|
357
|
+
body,
|
|
358
|
+
position
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
return { rules };
|
|
362
|
+
};
|
|
363
|
+
//#endregion
|
|
364
|
+
export { Parser_exports as n, parse as r, ParseError as t };
|
|
365
|
+
|
|
366
|
+
//# sourceMappingURL=chunk-Parser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-Parser.mjs","names":[],"sources":["../../src/internal/lexer.ts","../../src/Parser.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { BaseError } from '@dxos/errors';\n\nimport type * as Ast from '../Ast.ts';\n\nexport type TokenType =\n | 'identifier'\n | 'variable'\n | 'string'\n | 'number'\n | 'operator'\n | ':-'\n | ':'\n | '('\n | ')'\n | '{'\n | '}'\n | ','\n | '.'\n | '!';\n\nexport type Token = {\n readonly type: TokenType;\n readonly text: string;\n readonly value?: Ast.Value;\n readonly position: Ast.Position;\n};\n\n/** A malformed token with the 1-based position where it starts. */\nexport class LexError extends BaseError.extend('LexError', 'Invalid token') {\n constructor(\n readonly reason: string,\n readonly position: Ast.Position,\n ) {\n super({ message: reason, context: { line: position.line, column: position.column } });\n }\n}\n\nconst PUNCTUATION: Record<string, TokenType | undefined> = {\n ':': ':',\n '(': '(',\n ')': ')',\n '{': '{',\n '}': '}',\n ',': ',',\n '.': '.',\n '!': '!',\n};\n\nconst ESCAPES: Record<string, string> = { 'n': '\\n', 't': '\\t', 'r': '\\r', '\\\\': '\\\\', '\"': '\"', \"'\": \"'\" };\n\n/** Splits dialect source into tokens with 1-based positions. */\nexport const tokenize = (source: string): Token[] => {\n const tokens: Token[] = [];\n let offset = 0;\n let line = 1;\n let lineStart = 0;\n const here = (): Ast.Position => ({ line, column: offset - lineStart + 1 });\n const advance = (count: number) => {\n for (let index = 0; index < count; index++) {\n if (source[offset] === '\\n') {\n line++;\n lineStart = offset + 1;\n }\n offset++;\n }\n };\n\n while (offset < source.length) {\n const char = source[offset];\n const rest = source.slice(offset);\n if (/\\s/.test(char)) {\n advance(1);\n continue;\n }\n if (char === '%' || rest.startsWith('//')) {\n while (offset < source.length && source[offset] !== '\\n') {\n advance(1);\n }\n continue;\n }\n if (rest.startsWith('/*')) {\n const start = here();\n const end = source.indexOf('*/', offset + 2);\n if (end < 0) {\n throw new LexError('Unterminated comment', start);\n }\n advance(end + 2 - offset);\n continue;\n }\n\n const position = here();\n if (char === '\"' || char === \"'\") {\n let value = '';\n let cursor = offset + 1;\n while (cursor < source.length && source[cursor] !== char) {\n if (source[cursor] === '\\n') {\n throw new LexError('Unterminated string', position);\n }\n if (source[cursor] === '\\\\') {\n const escaped = source[cursor + 1];\n value += ESCAPES[escaped] ?? escaped ?? '';\n cursor += 2;\n } else {\n value += source[cursor++];\n }\n }\n if (cursor >= source.length) {\n throw new LexError('Unterminated string', position);\n }\n tokens.push({ type: 'string', text: source.slice(offset, cursor + 1), value, position });\n advance(cursor + 1 - offset);\n continue;\n }\n\n // A digit run glued to letters (`2d`, `30d`) is a symbol, which keeps durations unquoted.\n const numeric = /^-?\\d+(\\.\\d+)?(?![A-Za-z_\\d])/.exec(rest);\n if (numeric) {\n tokens.push({ type: 'number', text: numeric[0], value: Number(numeric[0]), position });\n advance(numeric[0].length);\n continue;\n }\n const word = /^[A-Za-z0-9_][A-Za-z0-9_]*/.exec(rest);\n if (word) {\n const text = word[0];\n const type: TokenType = /^[A-Z_]/.test(text) ? 'variable' : 'identifier';\n tokens.push({ type, text, value: type === 'identifier' ? text : undefined, position });\n advance(text.length);\n continue;\n }\n const operator = /^(<=|>=|!=|=|<|>)/.exec(rest);\n if (operator) {\n tokens.push({ type: 'operator', text: operator[0], position });\n advance(operator[0].length);\n continue;\n }\n if (rest.startsWith(':-')) {\n tokens.push({ type: ':-', text: ':-', position });\n advance(2);\n continue;\n }\n const punctuation = PUNCTUATION[char];\n if (punctuation) {\n tokens.push({ type: punctuation, text: char, position });\n advance(1);\n continue;\n }\n throw new LexError(`Unexpected character '${char}'`, position);\n }\n return tokens;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { BaseError } from '@dxos/errors';\n\nimport type * as Ast from './Ast.ts';\nimport { LexError, type Token, tokenize } from './internal/lexer.ts';\n\n/** A syntax error with the 1-based position of the offending token. */\nexport class ParseError extends BaseError.extend('ParseError', 'Syntax error') {\n constructor(\n readonly reason: string,\n readonly position: Ast.Position,\n ) {\n super({ message: reason, context: { line: position.line, column: position.column } });\n }\n}\n\nconst AGGREGATES: ReadonlyArray<Ast.AggregateFunction> = ['count', 'min', 'max', 'sum'];\n\nconst isAggregateFunction = (text: string): text is Ast.AggregateFunction => AGGREGATES.some((name) => name === text);\n\nconst OPERATORS: ReadonlyArray<Ast.ComparisonOperator> = ['=', '!=', '<', '<=', '>', '>='];\n\nconst toOperator = (text: string): Ast.ComparisonOperator | undefined => OPERATORS.find((op) => op === text);\n\n/**\n * Parses dialect source (Soufflé-like: `head :- body.`, `not`/`!` negation, comparisons and\n * `N = count : { … }` aggregates) into a program.\n * @throws ParseError on the first syntax error.\n */\nexport const parse = (source: string): Ast.Program => {\n let tokens: Token[];\n try {\n tokens = tokenize(source);\n } catch (error) {\n if (error instanceof LexError) {\n throw new ParseError(error.reason, error.position);\n }\n throw error;\n }\n\n let cursor = 0;\n let anonymous = 0;\n const end = (): Ast.Position => {\n const last = tokens.at(-1);\n return last\n ? { line: last.position.line, column: last.position.column + last.text.length }\n : { line: 1, column: 1 };\n };\n const peek = (ahead = 0): Token | undefined => tokens[cursor + ahead];\n const fail = (expected: string, token = peek()): never => {\n throw new ParseError(\n token ? `Expected ${expected} but found '${token.text}'` : `Expected ${expected} but reached end of input`,\n token?.position ?? end(),\n );\n };\n const expect = (type: Token['type'], description = `'${type}'`): Token => {\n const token = peek();\n if (!token || token.type !== type) {\n return fail(description);\n }\n cursor++;\n return token;\n };\n\n const term = (): Ast.Term => {\n const token = peek();\n switch (token?.type) {\n case 'variable':\n cursor++;\n return token.text === '_'\n ? { type: 'variable', name: `_${anonymous++}`, anonymous: true }\n : { type: 'variable', name: token.text };\n case 'identifier':\n case 'string':\n case 'number':\n cursor++;\n return { type: 'constant', value: token.value ?? token.text };\n default:\n return fail('a term');\n }\n };\n\n const atom = (): Ast.Atom => {\n const name = expect('identifier', 'a predicate name');\n const terms: Ast.Term[] = [];\n if (peek()?.type === '(') {\n cursor++;\n if (peek()?.type !== ')') {\n terms.push(term());\n while (peek()?.type === ',') {\n cursor++;\n terms.push(term());\n }\n }\n expect(')', \"',' or ')'\");\n }\n return { predicate: name.text, terms };\n };\n\n const aggregate = (result: Ast.Term, position: Ast.Position): Ast.AggregateLiteral => {\n const name = expect('identifier', 'an aggregate function');\n if (!isAggregateFunction(name.text)) {\n return fail('count, min, max or sum', name);\n }\n const target = name.text === 'count' ? undefined : term();\n expect(':', \"':'\");\n let body: Ast.Literal[];\n if (peek()?.type === '{') {\n cursor++;\n body = conjunction();\n expect('}', \"',' or '}'\");\n } else {\n body = [literal()];\n }\n if (body.some((item) => item.type === 'aggregate')) {\n throw new ParseError('Aggregates cannot be nested', position);\n }\n return { type: 'aggregate', function: name.text, result, target, body, position };\n };\n\n /** `fn :` or `fn Term :` after `=`; unknown function names are reported by `aggregate`. */\n const isAggregateAhead = (): boolean =>\n peek()?.type === 'identifier' && (peek(1)?.type === ':' || peek(2)?.type === ':');\n\n const literal = (): Ast.Literal => {\n const token = peek();\n if (!token) {\n return fail('a literal');\n }\n const position = token.position;\n if (token.type === '!' || (token.type === 'identifier' && token.text === 'not' && peek(1)?.type !== ':-')) {\n cursor++;\n if (peek()?.type === '(' && token.type === 'identifier') {\n cursor++;\n const negated = atom();\n expect(')', \"')'\");\n return { type: 'atom', atom: negated, negated: true, position };\n }\n return { type: 'atom', atom: atom(), negated: true, position };\n }\n if (peek(1)?.type === 'operator' || token.type !== 'identifier') {\n const left = term();\n const operatorToken = expect('operator', 'a comparison operator');\n const operator = toOperator(operatorToken.text) ?? fail('a comparison operator', operatorToken);\n if (operator === '=' && isAggregateAhead()) {\n return aggregate(left, position);\n }\n return { type: 'comparison', operator, left, right: term(), position };\n }\n return { type: 'atom', atom: atom(), negated: false, position };\n };\n\n const conjunction = (): Ast.Literal[] => {\n const literals = [literal()];\n while (peek()?.type === ',') {\n cursor++;\n literals.push(literal());\n }\n return literals;\n };\n\n const rules: Ast.Rule[] = [];\n while (cursor < tokens.length) {\n const position = peek()?.position;\n const head = atom();\n let body: Ast.Literal[] = [];\n if (peek()?.type === ':-') {\n cursor++;\n body = conjunction();\n }\n expect('.', \"'.', ',' or ':-'\");\n rules.push({ head, body, position });\n }\n return { rules };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as Ast_exports } from "./Ast.mjs";
|
|
2
|
+
import { t as Builtin_exports } from "./Builtin.mjs";
|
|
3
|
+
import { n as Checker_exports } from "./chunk-Checker.mjs";
|
|
4
|
+
import { n as Engine_exports } from "./chunk-Engine.mjs";
|
|
5
|
+
import { n as Parser_exports } from "./chunk-Parser.mjs";
|
|
6
|
+
export { Ast_exports as Ast, Builtin_exports as Builtin, Checker_exports as Checker, Engine_exports as Engine, Parser_exports as Parser };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** A ground value; symbols (`dima`) and strings (`"dima"`) are the same value. */
|
|
2
|
+
export type Value = string | number;
|
|
3
|
+
/** A ground row of a relation. */
|
|
4
|
+
export type Tuple = ReadonlyArray<Value>;
|
|
5
|
+
/** Source location (1-based line and column). */
|
|
6
|
+
export type Position = {
|
|
7
|
+
readonly line: number;
|
|
8
|
+
readonly column: number;
|
|
9
|
+
};
|
|
10
|
+
export type Variable = {
|
|
11
|
+
readonly type: 'variable';
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** Set for `_`; each occurrence is a distinct, never-joined variable. */
|
|
14
|
+
readonly anonymous?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type Constant = {
|
|
17
|
+
readonly type: 'constant';
|
|
18
|
+
readonly value: Value;
|
|
19
|
+
};
|
|
20
|
+
export type Term = Variable | Constant;
|
|
21
|
+
export type Atom = {
|
|
22
|
+
readonly predicate: string;
|
|
23
|
+
readonly terms: ReadonlyArray<Term>;
|
|
24
|
+
};
|
|
25
|
+
export type ComparisonOperator = '=' | '!=' | '<' | '<=' | '>' | '>=';
|
|
26
|
+
export type AggregateFunction = 'count' | 'min' | 'max' | 'sum';
|
|
27
|
+
export type AtomLiteral = {
|
|
28
|
+
readonly type: 'atom';
|
|
29
|
+
readonly atom: Atom;
|
|
30
|
+
readonly negated: boolean;
|
|
31
|
+
readonly position?: Position;
|
|
32
|
+
};
|
|
33
|
+
export type ComparisonLiteral = {
|
|
34
|
+
readonly type: 'comparison';
|
|
35
|
+
readonly operator: ComparisonOperator;
|
|
36
|
+
readonly left: Term;
|
|
37
|
+
readonly right: Term;
|
|
38
|
+
readonly position?: Position;
|
|
39
|
+
};
|
|
40
|
+
/** `Result = fn Target : { body }`; `Target` is absent for `count`. */
|
|
41
|
+
export type AggregateLiteral = {
|
|
42
|
+
readonly type: 'aggregate';
|
|
43
|
+
readonly function: AggregateFunction;
|
|
44
|
+
readonly result: Term;
|
|
45
|
+
readonly target?: Term;
|
|
46
|
+
readonly body: ReadonlyArray<Literal>;
|
|
47
|
+
readonly position?: Position;
|
|
48
|
+
};
|
|
49
|
+
export type Literal = AtomLiteral | ComparisonLiteral | AggregateLiteral;
|
|
50
|
+
/** A rule; a fact is a rule with an empty body. */
|
|
51
|
+
export type Rule = {
|
|
52
|
+
readonly head: Atom;
|
|
53
|
+
readonly body: ReadonlyArray<Literal>;
|
|
54
|
+
readonly position?: Position;
|
|
55
|
+
};
|
|
56
|
+
export type Program = {
|
|
57
|
+
readonly rules: ReadonlyArray<Rule>;
|
|
58
|
+
};
|
|
59
|
+
/** Creates a variable term. */
|
|
60
|
+
export declare const variable: (name: string) => Variable;
|
|
61
|
+
/** Creates a constant term. */
|
|
62
|
+
export declare const constant: (value: Value) => Constant;
|
|
63
|
+
/** Creates an atom. */
|
|
64
|
+
export declare const atom: (predicate: string, terms: ReadonlyArray<Term>) => Atom;
|
|
65
|
+
/** Named (non-anonymous) variables of a term list, in order of first occurrence. */
|
|
66
|
+
export declare const variablesOf: (terms: ReadonlyArray<Term>) => string[];
|
|
67
|
+
/** Named variables a literal mentions, including those inside an aggregate body. */
|
|
68
|
+
export declare const literalVariables: (literal: Literal) => string[];
|
|
69
|
+
/** Formats a value as dialect source. */
|
|
70
|
+
export declare const formatValue: (value: Value) => string;
|
|
71
|
+
/** Formats a term as dialect source. */
|
|
72
|
+
export declare const formatTerm: (term: Term) => string;
|
|
73
|
+
/** Formats an atom as dialect source. */
|
|
74
|
+
export declare const formatAtom: (atom: Atom) => string;
|
|
75
|
+
/** Formats a literal as dialect source. */
|
|
76
|
+
export declare const formatLiteral: (literal: Literal) => string;
|
|
77
|
+
/** Formats a rule as dialect source. */
|
|
78
|
+
export declare const formatRule: (rule: Rule) => string;
|
|
79
|
+
/** Formats a program as dialect source, one rule per line. */
|
|
80
|
+
export declare const format: (program: Program) => string;
|
|
81
|
+
//# 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,35 @@
|
|
|
1
|
+
import type * as Ast from './Ast.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A binding pattern, one character per argument: `b` must be bound before the call, `f` may be
|
|
4
|
+
* free and is bound by the call. `"fb"` lets the first argument be generated from the second.
|
|
5
|
+
*/
|
|
6
|
+
export type Mode = string;
|
|
7
|
+
/** Arguments as seen by a built-in: `undefined` marks a free position of the selected mode. */
|
|
8
|
+
export type Arguments = ReadonlyArray<Ast.Value | undefined>;
|
|
9
|
+
/** A predicate implemented in code rather than by rules. */
|
|
10
|
+
export type Builtin = {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly arity: number;
|
|
13
|
+
/** Accepted binding patterns; the all-bound pattern is always accepted. */
|
|
14
|
+
readonly modes: ReadonlyArray<Mode>;
|
|
15
|
+
/**
|
|
16
|
+
* Set when the result can change with no change to the facts (a clock), so the engine
|
|
17
|
+
* re-evaluates the rules that use it on `refresh`.
|
|
18
|
+
*/
|
|
19
|
+
readonly volatile?: boolean;
|
|
20
|
+
/** Returns every full argument tuple that holds and agrees with the bound arguments. */
|
|
21
|
+
readonly evaluate: (args: Arguments) => Iterable<Ast.Tuple>;
|
|
22
|
+
};
|
|
23
|
+
/** Built-ins by name. */
|
|
24
|
+
export type Registry = ReadonlyMap<string, Builtin>;
|
|
25
|
+
/** Defines a built-in. */
|
|
26
|
+
export declare const make: (builtin: Builtin) => Builtin;
|
|
27
|
+
/** Defines a test-only built-in: holds iff `test` returns true for its fully bound arguments. */
|
|
28
|
+
export declare const predicate: (name: string, arity: number, test: (args: Ast.Tuple) => boolean, options?: {
|
|
29
|
+
volatile?: boolean;
|
|
30
|
+
}) => Builtin;
|
|
31
|
+
/** Builds a registry, rejecting duplicate names. */
|
|
32
|
+
export declare const registry: (...builtins: ReadonlyArray<Builtin>) => Registry;
|
|
33
|
+
/** True if `builtin` can be called with exactly the `bound` argument positions bound. */
|
|
34
|
+
export declare const acceptsBinding: (builtin: Builtin, bound: ReadonlyArray<boolean>) => boolean;
|
|
35
|
+
//# sourceMappingURL=Builtin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Builtin.d.ts","sourceRoot":"","sources":["../../../src/Builtin.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAErC;;;GAGG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,+FAA+F;AAC/F,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;AAE7D,4DAA4D;AAC5D,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;CAC7D,CAAC;AAEF,yBAAyB;AACzB,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,0BAA0B;AAC1B,eAAO,MAAM,IAAI,YAAa,OAAO,KAAG,OAAkB,CAAC;AAE3D,iGAAiG;AACjG,eAAO,MAAM,SAAS,SACd,MAAM,SACL,MAAM,QACP,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,OAAO,YACzB;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,KAC9B,OASD,CAAC;AAEH,oDAAoD;AACpD,eAAO,MAAM,QAAQ,gBAAiB,aAAa,CAAC,OAAO,CAAC,KAAG,QAY9D,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,cAAc,YAAa,OAAO,SAAS,aAAa,CAAC,OAAO,CAAC,KAAG,OACqC,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BaseError } from '@dxos/errors';
|
|
2
|
+
import * as Ast from './Ast.ts';
|
|
3
|
+
import type * as Builtin from './Builtin.ts';
|
|
4
|
+
export type DiagnosticCode = 'arity' | 'builtin-head' | 'undefined' | 'unsafe' | 'unstratifiable';
|
|
5
|
+
/** A static error in a program. */
|
|
6
|
+
export type Diagnostic = {
|
|
7
|
+
readonly code: DiagnosticCode;
|
|
8
|
+
readonly message: string;
|
|
9
|
+
/** Index of the offending rule. */
|
|
10
|
+
readonly rule?: number;
|
|
11
|
+
readonly position?: Ast.Position;
|
|
12
|
+
};
|
|
13
|
+
export type Options = {
|
|
14
|
+
readonly builtins?: Builtin.Registry;
|
|
15
|
+
/**
|
|
16
|
+
* Base relations and their arities. When given, a body predicate that is neither defined by a
|
|
17
|
+
* rule, declared here nor a built-in is reported as `undefined`.
|
|
18
|
+
*/
|
|
19
|
+
readonly relations?: Readonly<Record<string, number>>;
|
|
20
|
+
};
|
|
21
|
+
declare const CheckError_base: {
|
|
22
|
+
new (options?: import("@dxos/errors").BaseErrorOptions): {
|
|
23
|
+
cause?: unknown;
|
|
24
|
+
stack?: string;
|
|
25
|
+
name: "CheckError";
|
|
26
|
+
context: Record<string, unknown>;
|
|
27
|
+
readonly message: string;
|
|
28
|
+
readonly _tag: "CheckError";
|
|
29
|
+
};
|
|
30
|
+
isError(error: unknown): error is Error;
|
|
31
|
+
name: "CheckError";
|
|
32
|
+
is(error: unknown): error is BaseError;
|
|
33
|
+
wrap(options?: Omit<import("@dxos/errors").BaseErrorOptions, 'cause'> & {
|
|
34
|
+
ifTypeDiffers?: boolean;
|
|
35
|
+
}): (error: unknown) => {
|
|
36
|
+
cause?: unknown;
|
|
37
|
+
stack?: string;
|
|
38
|
+
name: "CheckError";
|
|
39
|
+
context: Record<string, unknown>;
|
|
40
|
+
readonly message: string;
|
|
41
|
+
readonly _tag: "CheckError";
|
|
42
|
+
};
|
|
43
|
+
extend<Name extends string = string>(name: Name, message?: string): any;
|
|
44
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
45
|
+
prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
|
|
46
|
+
stackTraceLimit: number;
|
|
47
|
+
};
|
|
48
|
+
/** Thrown when a program with diagnostics is evaluated. */
|
|
49
|
+
export declare class CheckError extends CheckError_base {
|
|
50
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
51
|
+
constructor(diagnostics: ReadonlyArray<Diagnostic>);
|
|
52
|
+
}
|
|
53
|
+
/** Formats a diagnostic as `line:column code: message`. */
|
|
54
|
+
export declare const formatDiagnostic: (diagnostic: Diagnostic) => string;
|
|
55
|
+
/**
|
|
56
|
+
* Checks arity consistency, built-in heads, undefined predicates, range restriction (safety) and
|
|
57
|
+
* stratification; an empty result means the program can be evaluated.
|
|
58
|
+
*/
|
|
59
|
+
export declare const check: (program: Ast.Program, options?: Options) => Diagnostic[];
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=Checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checker.d.ts","sourceRoot":"","sources":["../../../src/Checker.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,KAAK,OAAO,MAAM,cAAc,CAAC;AAI7C,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,CAAC;AAElG,mCAAmC;AACnC,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEF,2DAA2D;AAC3D,qBAAa,UAAW,SAAQ,eAAiD;IAE/E,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAEhD,YAAY,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,EAGjD;CACF;AAED,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,eAAgB,UAAU,KAAG,MAGzD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,YAAa,GAAG,CAAC,OAAO,YAAW,OAAO,KAAQ,UAAU,EAqG7E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Checker.test.d.ts","sourceRoot":"","sources":["../../../src/Checker.test.ts"],"names":[],"mappings":""}
|