UNPKG

jinaga

Version:

Data management for web and mobile applications.

208 lines 8.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.skeletonOfSpecification = exports.emptySkeleton = void 0; exports.emptySkeleton = { facts: [], inputs: [], edges: [], notExistsConditions: [], outputs: [] }; function withFact(skeleton, factType) { const factIndex = skeleton.facts.length + 1; const fact = { factIndex, factType }; skeleton = Object.assign(Object.assign({}, skeleton), { facts: [...skeleton.facts, fact] }); return { skeleton, factIndex }; } function withInput(skeleton, factName, factType, inputIndex) { const { skeleton: skeletonWithFact, factIndex } = withFact(skeleton, factType); const input = { factIndex, inputIndex }; return Object.assign(Object.assign({}, skeletonWithFact), { inputs: [...skeletonWithFact.inputs, input] }); } function withEdge(skeleton, predecessorFactIndex, successorFactIndex, roleName, path) { const edgeIndex = skeleton.edges.length + countEdges(skeleton.notExistsConditions) + 1; const edge = { edgeIndex, predecessorFactIndex, successorFactIndex, roleName }; if (path.length === 0) { return Object.assign(Object.assign({}, skeleton), { edges: [...skeleton.edges, edge] }); } else { const notExistsConditions = notExistsWithEdge(skeleton.notExistsConditions, edge, path); return Object.assign(Object.assign({}, skeleton), { notExistsConditions }); } } function withOutput(skeleton, factIndex) { const output = { factIndex }; return Object.assign(Object.assign({}, skeleton), { outputs: [...skeleton.outputs, output] }); } function withNotExistsCondition(skeleton, path) { const { notExistsConditions: newNotExistsConditions, path: newPath } = notExistsWithCondition(skeleton.notExistsConditions, path); const newSkeleton = Object.assign(Object.assign({}, skeleton), { notExistsConditions: newNotExistsConditions }); return { skeleton: newSkeleton, path: newPath }; } function notExistsWithEdge(notExistsConditions, edge, path) { if (path.length === 1) { return notExistsConditions.map((c, i) => i === path[0] ? { edges: [...c.edges, edge], notExistsConditions: c.notExistsConditions } : c); } else { return notExistsConditions.map((c, i) => i === path[0] ? { edges: c.edges, notExistsConditions: notExistsWithEdge(c.notExistsConditions, edge, path.slice(1)) } : c); } } function notExistsWithCondition(notExistsConditions, path) { if (path.length === 0) { path = [notExistsConditions.length]; notExistsConditions = [ ...notExistsConditions, { edges: [], notExistsConditions: [] } ]; return { notExistsConditions, path }; } else { const { notExistsConditions: newNotExistsConditions, path: newPath } = notExistsWithCondition(notExistsConditions[path[0]].notExistsConditions, path.slice(1)); notExistsConditions = notExistsConditions.map((c, i) => i === path[0] ? { edges: c.edges, notExistsConditions: newNotExistsConditions } : c); path = [path[0], ...newPath]; return { notExistsConditions, path }; } } function countEdges(notExistsConditions) { return notExistsConditions.reduce((count, c) => count + c.edges.length + countEdges(c.notExistsConditions), 0); } function skeletonOfSpecification(specification) { const givenFacts = specification.given.reduce((acc, label, i) => (Object.assign(Object.assign({}, acc), { [label.name]: { type: label.type, inputIndex: i } })), {}); const { skeleton } = addEdges(exports.emptySkeleton, givenFacts, {}, [], specification.matches); return skeleton; } exports.skeletonOfSpecification = skeletonOfSpecification; function addEdges(skeleton, givenFacts, knownFacts, path, matches) { for (const match of matches) { for (const condition of match.conditions) { if (condition.type === "path") { ({ skeleton, knownFacts } = addPathCondition(skeleton, givenFacts, knownFacts, path, match.unknown, condition)); } else if (condition.type === "existential") { if (condition.exists) { // Include the edges of the existential condition into the current skeleton. const { skeleton: newSkeleton } = addEdges(skeleton, givenFacts, knownFacts, path, condition.matches); skeleton = newSkeleton; } else { // Apply the where clause and continue with the tuple where it is true. const { skeleton: skeletonWithNotExist, path: conditionalPath } = withNotExistsCondition(skeleton, path); const { skeleton: newSkeletonWithNotExists } = addEdges(skeletonWithNotExist, givenFacts, knownFacts, conditionalPath, condition.matches); skeleton = newSkeletonWithNotExists; } } } } return { skeleton, knownFacts }; } function addPathCondition(skeleton, givenFacts, knownFacts, path, unknown, condition) { const given = givenFacts[condition.labelRight]; if (given) { // If the right-hand side is a given, and not yet a known fact, // then add it to the feed. if (!knownFacts[condition.labelRight]) { skeleton = withInput(skeleton, condition.labelRight, given.type, given.inputIndex); knownFacts = Object.assign(Object.assign({}, knownFacts), { [condition.labelRight]: { factIndex: skeleton.facts.length, factType: given.type } }); } } // Determine whether we have already written the output. const knownFact = knownFacts[unknown.name]; const roleCount = condition.rolesLeft.length + condition.rolesRight.length; // Walk up the right-hand side. // This generates predecessor joins from a given or prior label. const fact = knownFacts[condition.labelRight]; if (!fact) { throw new Error(`Label ${condition.labelRight} not found. Known labels: ${Object.keys(knownFacts).join(", ")}`); } let factType = fact.factType; let factIndex = fact.factIndex; for (let i = 0; i < condition.rolesRight.length; i++) { const role = condition.rolesRight[i]; if (i === roleCount - 1 && knownFact) { // If we have already written the output, we can use the fact index. skeleton = withEdge(skeleton, knownFact.factIndex, factIndex, role.name, path); factIndex = knownFact.factIndex; } else { // If we have not written the fact, we need to write it now. const { skeleton: skeletonWithFact, factIndex: predecessorFactIndex } = withFact(skeleton, role.predecessorType); skeleton = withEdge(skeletonWithFact, predecessorFactIndex, factIndex, role.name, path); factIndex = predecessorFactIndex; } factType = role.predecessorType; } const rightType = factType; // Walk up the left-hand side. // We will need to reverse this walk to generate successor joins. factType = unknown.type; const newEdges = []; for (const role of condition.rolesLeft) { newEdges.push({ roleName: role.name, successorType: factType }); factType = role.predecessorType; } if (factType !== rightType) { throw new Error(`Type mismatch: ${factType} is compared to ${rightType}`); } newEdges.reverse().forEach(({ roleName, successorType }, i) => { if (condition.rolesRight.length + i === roleCount - 1 && knownFact) { skeleton = withEdge(skeleton, factIndex, knownFact.factIndex, roleName, path); factIndex = knownFact.factIndex; } else { const { skeleton: skeletonWithFact, factIndex: successorFactIndex } = withFact(skeleton, successorType); skeleton = withEdge(skeletonWithFact, factIndex, successorFactIndex, roleName, path); factIndex = successorFactIndex; } }); // If we have not captured the known fact, add it now. if (!knownFact) { knownFacts = Object.assign(Object.assign({}, knownFacts), { [unknown.name]: { factIndex, factType: unknown.type } }); // If we have not written the output, write it now. // Only write the output if we are not inside of an existential condition. if (path.length === 0) { skeleton = withOutput(skeleton, factIndex); } } return { skeleton, knownFacts }; } //# sourceMappingURL=skeleton.js.map