simple-graph-query 2.5.0 → 2.5.1
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.
|
@@ -8,7 +8,7 @@ export type BinaryRelationExample = {
|
|
|
8
8
|
pairs: Set<AtomPair>;
|
|
9
9
|
datum: IDataInstance;
|
|
10
10
|
};
|
|
11
|
-
type ExpressionNode = {
|
|
11
|
+
export type ExpressionNode = {
|
|
12
12
|
kind: "identifier";
|
|
13
13
|
name: string;
|
|
14
14
|
} | {
|
|
@@ -22,6 +22,41 @@ type ExpressionNode = {
|
|
|
22
22
|
} | {
|
|
23
23
|
kind: "closure";
|
|
24
24
|
child: ExpressionNode;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "reflexive-closure";
|
|
27
|
+
child: ExpressionNode;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "transpose";
|
|
30
|
+
child: ExpressionNode;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "comprehension";
|
|
33
|
+
varName: string;
|
|
34
|
+
domain: ExpressionNode;
|
|
35
|
+
body: ExpressionNode;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "all" | "some" | "no" | "one" | "lone";
|
|
38
|
+
varName: string;
|
|
39
|
+
domain: ExpressionNode;
|
|
40
|
+
body: ExpressionNode;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "and" | "or" | "implies" | "iff";
|
|
43
|
+
left: ExpressionNode;
|
|
44
|
+
right: ExpressionNode;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "not";
|
|
47
|
+
child: ExpressionNode;
|
|
48
|
+
} | {
|
|
49
|
+
kind: "in" | "eq" | "neq";
|
|
50
|
+
left: ExpressionNode;
|
|
51
|
+
right: ExpressionNode;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "lt" | "gt" | "lte" | "gte";
|
|
54
|
+
left: ExpressionNode;
|
|
55
|
+
right: ExpressionNode;
|
|
56
|
+
} | {
|
|
57
|
+
kind: "box-join";
|
|
58
|
+
base: ExpressionNode;
|
|
59
|
+
args: ExpressionNode[];
|
|
25
60
|
};
|
|
26
61
|
export type WhyNode = {
|
|
27
62
|
kind: ExpressionNode["kind"];
|
|
@@ -45,4 +80,3 @@ export type SynthesisWhy = {
|
|
|
45
80
|
};
|
|
46
81
|
export declare function synthesizeSelectorWithWhy(examples: AtomSelectionExample[], maxDepth?: number): SynthesisWhy;
|
|
47
82
|
export declare function synthesizeBinaryRelationWithWhy(examples: BinaryRelationExample[], maxDepth?: number): SynthesisWhy;
|
|
48
|
-
export {};
|
|
@@ -50955,10 +50955,20 @@ function nodeToString(node) {
|
|
|
50955
50955
|
switch (node.kind) {
|
|
50956
50956
|
case "identifier":
|
|
50957
50957
|
return node.name;
|
|
50958
|
+
// Unary relational operators
|
|
50958
50959
|
case "closure": {
|
|
50959
50960
|
const inner = nodeToString(node.child);
|
|
50960
50961
|
return `^${wrapForPrefix(inner)}`;
|
|
50961
50962
|
}
|
|
50963
|
+
case "reflexive-closure": {
|
|
50964
|
+
const inner = nodeToString(node.child);
|
|
50965
|
+
return `*${wrapForPrefix(inner)}`;
|
|
50966
|
+
}
|
|
50967
|
+
case "transpose": {
|
|
50968
|
+
const inner = nodeToString(node.child);
|
|
50969
|
+
return `~${wrapForPrefix(inner)}`;
|
|
50970
|
+
}
|
|
50971
|
+
// Binary relational operators
|
|
50962
50972
|
case "join": {
|
|
50963
50973
|
const left = wrapForJoin(node.left);
|
|
50964
50974
|
const right = wrapForJoin(node.right);
|
|
@@ -50970,10 +50980,59 @@ function nodeToString(node) {
|
|
|
50970
50980
|
return `(${nodeToString(node.left)} & ${nodeToString(node.right)})`;
|
|
50971
50981
|
case "difference":
|
|
50972
50982
|
return `(${nodeToString(node.left)} - ${nodeToString(node.right)})`;
|
|
50983
|
+
// Set comprehension
|
|
50984
|
+
case "comprehension":
|
|
50985
|
+
return `{${node.varName}: ${nodeToString(node.domain)} | ${nodeToString(node.body)}}`;
|
|
50986
|
+
// Quantified expressions
|
|
50987
|
+
case "all":
|
|
50988
|
+
return `(all ${node.varName}: ${nodeToString(node.domain)} | ${nodeToString(node.body)})`;
|
|
50989
|
+
case "some":
|
|
50990
|
+
return `(some ${node.varName}: ${nodeToString(node.domain)} | ${nodeToString(node.body)})`;
|
|
50991
|
+
case "no":
|
|
50992
|
+
return `(no ${node.varName}: ${nodeToString(node.domain)} | ${nodeToString(node.body)})`;
|
|
50993
|
+
case "one":
|
|
50994
|
+
return `(one ${node.varName}: ${nodeToString(node.domain)} | ${nodeToString(node.body)})`;
|
|
50995
|
+
case "lone":
|
|
50996
|
+
return `(lone ${node.varName}: ${nodeToString(node.domain)} | ${nodeToString(node.body)})`;
|
|
50997
|
+
// Logical operators
|
|
50998
|
+
case "and":
|
|
50999
|
+
return `(${nodeToString(node.left)} and ${nodeToString(node.right)})`;
|
|
51000
|
+
case "or":
|
|
51001
|
+
return `(${nodeToString(node.left)} or ${nodeToString(node.right)})`;
|
|
51002
|
+
case "implies":
|
|
51003
|
+
return `(${nodeToString(node.left)} => ${nodeToString(node.right)})`;
|
|
51004
|
+
case "iff":
|
|
51005
|
+
return `(${nodeToString(node.left)} <=> ${nodeToString(node.right)})`;
|
|
51006
|
+
case "not":
|
|
51007
|
+
return `!${wrapForPrefix(nodeToString(node.child))}`;
|
|
51008
|
+
// Relational comparison
|
|
51009
|
+
case "in":
|
|
51010
|
+
return `(${nodeToString(node.left)} in ${nodeToString(node.right)})`;
|
|
51011
|
+
case "eq":
|
|
51012
|
+
return `(${nodeToString(node.left)} = ${nodeToString(node.right)})`;
|
|
51013
|
+
case "neq":
|
|
51014
|
+
return `(${nodeToString(node.left)} != ${nodeToString(node.right)})`;
|
|
51015
|
+
// Numeric comparison
|
|
51016
|
+
case "lt":
|
|
51017
|
+
return `(${nodeToString(node.left)} < ${nodeToString(node.right)})`;
|
|
51018
|
+
case "gt":
|
|
51019
|
+
return `(${nodeToString(node.left)} > ${nodeToString(node.right)})`;
|
|
51020
|
+
case "lte":
|
|
51021
|
+
return `(${nodeToString(node.left)} <= ${nodeToString(node.right)})`;
|
|
51022
|
+
case "gte":
|
|
51023
|
+
return `(${nodeToString(node.left)} >= ${nodeToString(node.right)})`;
|
|
51024
|
+
// Box join
|
|
51025
|
+
case "box-join":
|
|
51026
|
+
return `${nodeToString(node.base)}[${node.args.map(nodeToString).join(", ")}]`;
|
|
50973
51027
|
}
|
|
50974
51028
|
}
|
|
50975
51029
|
function wrapForJoin(node) {
|
|
50976
|
-
|
|
51030
|
+
// Identifiers and prefix unary operators don't need wrapping
|
|
51031
|
+
if (node.kind === "identifier" ||
|
|
51032
|
+
node.kind === "closure" ||
|
|
51033
|
+
node.kind === "reflexive-closure" ||
|
|
51034
|
+
node.kind === "transpose" ||
|
|
51035
|
+
node.kind === "box-join") {
|
|
50977
51036
|
return nodeToString(node);
|
|
50978
51037
|
}
|
|
50979
51038
|
return `(${nodeToString(node)})`;
|
|
@@ -50997,10 +51056,16 @@ function normalizeUnaryResult(result) {
|
|
|
50997
51056
|
return null;
|
|
50998
51057
|
}
|
|
50999
51058
|
const value = tuple[0];
|
|
51000
|
-
|
|
51059
|
+
// Accept strings or numbers (integers are often returned as numbers)
|
|
51060
|
+
if (typeof value === "string") {
|
|
51061
|
+
ids.add(value);
|
|
51062
|
+
}
|
|
51063
|
+
else if (typeof value === "number") {
|
|
51064
|
+
ids.add(String(value));
|
|
51065
|
+
}
|
|
51066
|
+
else {
|
|
51001
51067
|
return null;
|
|
51002
51068
|
}
|
|
51003
|
-
ids.add(value);
|
|
51004
51069
|
}
|
|
51005
51070
|
return ids;
|
|
51006
51071
|
}
|
|
@@ -51015,10 +51080,13 @@ function normalizeBinaryResult(result) {
|
|
|
51015
51080
|
return null;
|
|
51016
51081
|
}
|
|
51017
51082
|
const [first, second] = tuple;
|
|
51018
|
-
|
|
51083
|
+
// Accept strings or numbers for both elements
|
|
51084
|
+
const firstStr = typeof first === "string" ? first : typeof first === "number" ? String(first) : null;
|
|
51085
|
+
const secondStr = typeof second === "string" ? second : typeof second === "number" ? String(second) : null;
|
|
51086
|
+
if (firstStr === null || secondStr === null) {
|
|
51019
51087
|
return null;
|
|
51020
51088
|
}
|
|
51021
|
-
ids.add(`${
|
|
51089
|
+
ids.add(`${firstStr}\u0000${secondStr}`);
|
|
51022
51090
|
}
|
|
51023
51091
|
return ids;
|
|
51024
51092
|
}
|
|
@@ -51068,6 +51136,99 @@ function classifyIdentifier(name, datums) {
|
|
|
51068
51136
|
}
|
|
51069
51137
|
return "other";
|
|
51070
51138
|
}
|
|
51139
|
+
// Build semantically meaningful join candidates based on type-relation compatibility.
|
|
51140
|
+
// For a relation with types [T1, T2, ...], we generate joins like T1.relation
|
|
51141
|
+
// which projects the relation to its range. This helps synthesize expressions
|
|
51142
|
+
// like "Node.key" when selecting integer values that are keys of nodes.
|
|
51143
|
+
function buildSemanticJoinCandidates(datums) {
|
|
51144
|
+
const candidates = [];
|
|
51145
|
+
const seenExpressions = new Set();
|
|
51146
|
+
// Get relations and types that exist across all datums
|
|
51147
|
+
const sharedRelationNames = new Set();
|
|
51148
|
+
const sharedTypeIds = new Set();
|
|
51149
|
+
if (datums.length === 0)
|
|
51150
|
+
return candidates;
|
|
51151
|
+
// Initialize with first datum
|
|
51152
|
+
const firstDatum = datums[0];
|
|
51153
|
+
firstDatum.getRelations().forEach((r) => sharedRelationNames.add(r.name));
|
|
51154
|
+
firstDatum.getTypes().forEach((t) => sharedTypeIds.add(t.id));
|
|
51155
|
+
// Intersect with remaining datums
|
|
51156
|
+
for (let i = 1; i < datums.length; i++) {
|
|
51157
|
+
const datum = datums[i];
|
|
51158
|
+
const datumRelations = new Set(datum.getRelations().map((r) => r.name));
|
|
51159
|
+
const datumTypes = new Set(datum.getTypes().map((t) => t.id));
|
|
51160
|
+
for (const name of sharedRelationNames) {
|
|
51161
|
+
if (!datumRelations.has(name))
|
|
51162
|
+
sharedRelationNames.delete(name);
|
|
51163
|
+
}
|
|
51164
|
+
for (const id of sharedTypeIds) {
|
|
51165
|
+
if (!datumTypes.has(id))
|
|
51166
|
+
sharedTypeIds.delete(id);
|
|
51167
|
+
}
|
|
51168
|
+
}
|
|
51169
|
+
// For each shared relation, check if its domain type is also shared
|
|
51170
|
+
// and create Type.relation join candidates
|
|
51171
|
+
for (const relationName of sharedRelationNames) {
|
|
51172
|
+
// Get the relation's type signature from the first datum (should be consistent)
|
|
51173
|
+
const relation = firstDatum.getRelations().find((r) => r.name === relationName);
|
|
51174
|
+
if (!relation || relation.types.length < 2)
|
|
51175
|
+
continue;
|
|
51176
|
+
const domainType = relation.types[0];
|
|
51177
|
+
// Check if the domain type (or a compatible type) is available
|
|
51178
|
+
// We look for types in the hierarchy that could be the domain
|
|
51179
|
+
for (const typeId of sharedTypeIds) {
|
|
51180
|
+
const typeObj = firstDatum.getTypes().find((t) => t.id === typeId);
|
|
51181
|
+
if (!typeObj)
|
|
51182
|
+
continue;
|
|
51183
|
+
// Check if this type is compatible with the relation's domain
|
|
51184
|
+
// Either the type itself matches, or it's in the type hierarchy
|
|
51185
|
+
const isCompatible = typeId === domainType ||
|
|
51186
|
+
typeObj.types.includes(domainType) ||
|
|
51187
|
+
// Also check if the domain type is a subtype of this type
|
|
51188
|
+
firstDatum.getTypes().find((t) => t.id === domainType)?.types.includes(typeId);
|
|
51189
|
+
if (isCompatible) {
|
|
51190
|
+
const joinNode = {
|
|
51191
|
+
kind: "join",
|
|
51192
|
+
left: { kind: "identifier", name: typeId },
|
|
51193
|
+
right: { kind: "identifier", name: relationName },
|
|
51194
|
+
};
|
|
51195
|
+
const exprString = nodeToString(joinNode);
|
|
51196
|
+
if (!seenExpressions.has(exprString)) {
|
|
51197
|
+
seenExpressions.add(exprString);
|
|
51198
|
+
candidates.push(joinNode);
|
|
51199
|
+
}
|
|
51200
|
+
}
|
|
51201
|
+
}
|
|
51202
|
+
}
|
|
51203
|
+
// Also add relation.Type joins for inverse projections (getting domain values)
|
|
51204
|
+
for (const relationName of sharedRelationNames) {
|
|
51205
|
+
const relation = firstDatum.getRelations().find((r) => r.name === relationName);
|
|
51206
|
+
if (!relation || relation.types.length < 2)
|
|
51207
|
+
continue;
|
|
51208
|
+
const rangeType = relation.types[relation.types.length - 1];
|
|
51209
|
+
for (const typeId of sharedTypeIds) {
|
|
51210
|
+
const typeObj = firstDatum.getTypes().find((t) => t.id === typeId);
|
|
51211
|
+
if (!typeObj)
|
|
51212
|
+
continue;
|
|
51213
|
+
const isCompatible = typeId === rangeType ||
|
|
51214
|
+
typeObj.types.includes(rangeType) ||
|
|
51215
|
+
firstDatum.getTypes().find((t) => t.id === rangeType)?.types.includes(typeId);
|
|
51216
|
+
if (isCompatible) {
|
|
51217
|
+
const joinNode = {
|
|
51218
|
+
kind: "join",
|
|
51219
|
+
left: { kind: "identifier", name: relationName },
|
|
51220
|
+
right: { kind: "identifier", name: typeId },
|
|
51221
|
+
};
|
|
51222
|
+
const exprString = nodeToString(joinNode);
|
|
51223
|
+
if (!seenExpressions.has(exprString)) {
|
|
51224
|
+
seenExpressions.add(exprString);
|
|
51225
|
+
candidates.push(joinNode);
|
|
51226
|
+
}
|
|
51227
|
+
}
|
|
51228
|
+
}
|
|
51229
|
+
}
|
|
51230
|
+
return candidates;
|
|
51231
|
+
}
|
|
51071
51232
|
function buildBaseNodes(datums) {
|
|
51072
51233
|
const baseNames = intersectNames(datums);
|
|
51073
51234
|
// Always include standard top-level identifiers when present in the language
|
|
@@ -51125,11 +51286,20 @@ function synthesizeExpressionNode(examples, normalizer, maxDepth = 3) {
|
|
|
51125
51286
|
if (baseNodes.length === 0) {
|
|
51126
51287
|
throw new SelectorSynthesisError("No shared identifiers available across provided data instances");
|
|
51127
51288
|
}
|
|
51289
|
+
// Check base identifiers first
|
|
51128
51290
|
for (const node of baseNodes) {
|
|
51129
51291
|
if (matchesTargets(node, evaluatedExamples, normalizer)) {
|
|
51130
51292
|
return node;
|
|
51131
51293
|
}
|
|
51132
51294
|
}
|
|
51295
|
+
// Check semantic join candidates early - these are type-aware joins like Node.key
|
|
51296
|
+
// that are likely to be what the user wants when selecting relation ranges/domains
|
|
51297
|
+
const semanticJoins = buildSemanticJoinCandidates(datums);
|
|
51298
|
+
for (const node of semanticJoins) {
|
|
51299
|
+
if (matchesTargets(node, evaluatedExamples, normalizer)) {
|
|
51300
|
+
return node;
|
|
51301
|
+
}
|
|
51302
|
+
}
|
|
51133
51303
|
const queue = [];
|
|
51134
51304
|
const queued = new Set();
|
|
51135
51305
|
const visited = new Set();
|
|
@@ -51141,6 +51311,10 @@ function synthesizeExpressionNode(examples, normalizer, maxDepth = 3) {
|
|
|
51141
51311
|
queue.push({ node, depth });
|
|
51142
51312
|
queued.add(key);
|
|
51143
51313
|
};
|
|
51314
|
+
// Mark semantic joins as already visited so we don't re-check them
|
|
51315
|
+
for (const node of semanticJoins) {
|
|
51316
|
+
visited.add(nodeToString(node));
|
|
51317
|
+
}
|
|
51144
51318
|
baseNodes.forEach((node) => enqueue(node, 0));
|
|
51145
51319
|
while (queue.length > 0) {
|
|
51146
51320
|
const current = queue.shift();
|
|
@@ -51156,18 +51330,69 @@ function synthesizeExpressionNode(examples, normalizer, maxDepth = 3) {
|
|
|
51156
51330
|
if (current.depth >= maxDepth) {
|
|
51157
51331
|
continue;
|
|
51158
51332
|
}
|
|
51159
|
-
// Unary expansions
|
|
51333
|
+
// Unary relational expansions
|
|
51160
51334
|
enqueue({ kind: "closure", child: current.node }, current.depth + 1);
|
|
51335
|
+
enqueue({ kind: "reflexive-closure", child: current.node }, current.depth + 1);
|
|
51336
|
+
enqueue({ kind: "transpose", child: current.node }, current.depth + 1);
|
|
51161
51337
|
for (const other of combinationPool) {
|
|
51162
51338
|
const leftKey = nodeToString(current.node);
|
|
51163
51339
|
const rightKey = nodeToString(other);
|
|
51340
|
+
// Set operations (commutative, so canonicalize order)
|
|
51164
51341
|
const [unionLeft, unionRight] = leftKey < rightKey ? [current.node, other] : [other, current.node];
|
|
51165
51342
|
enqueue({ kind: "union", left: unionLeft, right: unionRight }, current.depth + 1);
|
|
51166
51343
|
const [interLeft, interRight] = leftKey < rightKey ? [current.node, other] : [other, current.node];
|
|
51167
51344
|
enqueue({ kind: "intersection", left: interLeft, right: interRight }, current.depth + 1);
|
|
51345
|
+
// Difference is not commutative
|
|
51346
|
+
enqueue({ kind: "difference", left: current.node, right: other }, current.depth + 1);
|
|
51347
|
+
if (leftKey !== rightKey) {
|
|
51348
|
+
enqueue({ kind: "difference", left: other, right: current.node }, current.depth + 1);
|
|
51349
|
+
}
|
|
51350
|
+
// Join is not commutative
|
|
51168
51351
|
enqueue({ kind: "join", left: current.node, right: other }, current.depth + 1);
|
|
51169
51352
|
enqueue({ kind: "join", left: other, right: current.node }, current.depth + 1);
|
|
51170
51353
|
}
|
|
51354
|
+
// Generate set comprehensions: {v: domain | body}
|
|
51355
|
+
// For each type, try comprehensions with simple membership conditions
|
|
51356
|
+
// This allows synthesizing expressions like {n: Node | n.key in SomeSet}
|
|
51357
|
+
if (current.depth + 2 <= maxDepth) {
|
|
51358
|
+
for (const domainNode of combinationPool) {
|
|
51359
|
+
// Only use types as comprehension domains
|
|
51360
|
+
if (domainNode.kind !== "identifier")
|
|
51361
|
+
continue;
|
|
51362
|
+
const domainName = domainNode.name;
|
|
51363
|
+
const classification = classifyIdentifier(domainName, datums);
|
|
51364
|
+
if (classification !== "type")
|
|
51365
|
+
continue;
|
|
51366
|
+
const varName = "v"; // Use a simple variable name
|
|
51367
|
+
// Try: {v: Domain | v in current.node}
|
|
51368
|
+
const varNode = { kind: "identifier", name: varName };
|
|
51369
|
+
enqueue({
|
|
51370
|
+
kind: "comprehension",
|
|
51371
|
+
varName,
|
|
51372
|
+
domain: domainNode,
|
|
51373
|
+
body: { kind: "in", left: varNode, right: current.node },
|
|
51374
|
+
}, current.depth + 2);
|
|
51375
|
+
// Try: {v: Domain | v.relation in SomeSet} for relations
|
|
51376
|
+
for (const relNode of combinationPool) {
|
|
51377
|
+
if (relNode.kind !== "identifier")
|
|
51378
|
+
continue;
|
|
51379
|
+
if (classifyIdentifier(relNode.name, datums) !== "relation")
|
|
51380
|
+
continue;
|
|
51381
|
+
const joinExpr = {
|
|
51382
|
+
kind: "join",
|
|
51383
|
+
left: varNode,
|
|
51384
|
+
right: relNode,
|
|
51385
|
+
};
|
|
51386
|
+
// {v: Domain | v.relation in current.node}
|
|
51387
|
+
enqueue({
|
|
51388
|
+
kind: "comprehension",
|
|
51389
|
+
varName,
|
|
51390
|
+
domain: domainNode,
|
|
51391
|
+
body: { kind: "in", left: joinExpr, right: current.node },
|
|
51392
|
+
}, current.depth + 2);
|
|
51393
|
+
}
|
|
51394
|
+
}
|
|
51395
|
+
}
|
|
51171
51396
|
}
|
|
51172
51397
|
throw new SelectorSynthesisError("Unable to synthesize an expression matching all examples");
|
|
51173
51398
|
}
|
|
@@ -51195,14 +51420,31 @@ function buildWhyNode(node, evaluator, normalizer) {
|
|
|
51195
51420
|
result,
|
|
51196
51421
|
};
|
|
51197
51422
|
switch (node.kind) {
|
|
51423
|
+
// Leaves
|
|
51198
51424
|
case "identifier":
|
|
51199
51425
|
return base;
|
|
51426
|
+
// Unary operators
|
|
51200
51427
|
case "closure":
|
|
51428
|
+
case "reflexive-closure":
|
|
51429
|
+
case "transpose":
|
|
51430
|
+
case "not":
|
|
51201
51431
|
return { ...base, children: [buildWhyNode(node.child, evaluator, normalizer)] };
|
|
51432
|
+
// Binary operators
|
|
51202
51433
|
case "join":
|
|
51203
51434
|
case "union":
|
|
51204
51435
|
case "intersection":
|
|
51205
51436
|
case "difference":
|
|
51437
|
+
case "and":
|
|
51438
|
+
case "or":
|
|
51439
|
+
case "implies":
|
|
51440
|
+
case "iff":
|
|
51441
|
+
case "in":
|
|
51442
|
+
case "eq":
|
|
51443
|
+
case "neq":
|
|
51444
|
+
case "lt":
|
|
51445
|
+
case "gt":
|
|
51446
|
+
case "lte":
|
|
51447
|
+
case "gte":
|
|
51206
51448
|
return {
|
|
51207
51449
|
...base,
|
|
51208
51450
|
children: [
|
|
@@ -51210,6 +51452,29 @@ function buildWhyNode(node, evaluator, normalizer) {
|
|
|
51210
51452
|
buildWhyNode(node.right, evaluator, normalizer),
|
|
51211
51453
|
],
|
|
51212
51454
|
};
|
|
51455
|
+
// Quantified and comprehension expressions
|
|
51456
|
+
case "all":
|
|
51457
|
+
case "some":
|
|
51458
|
+
case "no":
|
|
51459
|
+
case "one":
|
|
51460
|
+
case "lone":
|
|
51461
|
+
case "comprehension":
|
|
51462
|
+
return {
|
|
51463
|
+
...base,
|
|
51464
|
+
children: [
|
|
51465
|
+
buildWhyNode(node.domain, evaluator, normalizer),
|
|
51466
|
+
buildWhyNode(node.body, evaluator, normalizer),
|
|
51467
|
+
],
|
|
51468
|
+
};
|
|
51469
|
+
// Box join
|
|
51470
|
+
case "box-join":
|
|
51471
|
+
return {
|
|
51472
|
+
...base,
|
|
51473
|
+
children: [
|
|
51474
|
+
buildWhyNode(node.base, evaluator, normalizer),
|
|
51475
|
+
...node.args.map((arg) => buildWhyNode(arg, evaluator, normalizer)),
|
|
51476
|
+
],
|
|
51477
|
+
};
|
|
51213
51478
|
default:
|
|
51214
51479
|
return base;
|
|
51215
51480
|
}
|
package/package.json
CHANGED