geneea-nlp-client
Version:
The TypeScript Client for Geneea Interpretor G3 API.
107 lines (106 loc) • 4.23 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TreeBuilder = exports.Tree = void 0;
const common_1 = require("./../../common/common");
const node_1 = require("./node");
/** A temporary class used during the construction of a tree of tokens or tecto-tokens. */
class Tree {
constructor(root, tokens) {
this.root = root;
this.tokens = tokens;
}
}
exports.Tree = Tree;
class TreeBuilder {
constructor() {
this.nodes = [];
/** Dependency edges between nodes. */
this.deps = new Map();
}
/**
* Record a single token as a node of the tree.
* @param node Token to add. Its index must be correct, parent and children fields are ignored.
* @returns The builder to allow chained calls.
*/
addNode(node) {
this.nodes.push(node);
return this;
}
/**
* Record a collection of tokens as nodes of the tree.
* @param nodes Tokens to add. Their index must be correct, parent and children fields are ignored.
* @returns The builder to allow chained calls.
*/
addNodes(nodes) {
for (const n of nodes)
this.nodes.push(n);
return this;
}
/**
* Record a dependency edge. The tokens connected by the edge might be added later.
* @param childIdx Index of the child token (note: tokens are indexed within their sentence).
* @param parentIdx Index of the parent token (note: tokens are indexed within their sentence);
* @returns The builder to allow chained calls.
*/
addDependency(childIdx, parentIdx) {
if (childIdx < 0)
throw new Error(`Negative node index ${childIdx}.`);
if (parentIdx < 0)
throw new Error(`Negative node index ${parentIdx}`);
if (childIdx === parentIdx)
throw new Error("Dependency edge cannot be reflexive.");
if (this.deps.has(childIdx) && this.deps.get(childIdx) !== parentIdx)
throw new Error(`Node ${childIdx} has multiple parents: [${this.nodes[parentIdx].id}, ${this.nodes[this.deps.get(childIdx)].id}].`);
this.deps.set(childIdx, parentIdx);
return this;
}
/** All nodes are hanged to the first one. */
addDummyDependencies() {
if (this.deps.size !== 0)
throw new Error("Dummy dependencies cannot be added when other dependencies have been specified.");
if (this.nodes.length === 0)
return;
node_1.NodeUtils.sort(this.nodes);
for (const n of this.nodes.slice(1))
this.addDependency(n.idx, 0);
}
fillParents() {
const maxIdx = this.nodes.length - 1;
for (const [c, p] of this.deps) {
if (c > maxIdx)
throw new Error(`The child of the dependency edge ${c} -> ${p} is out of range (max=${maxIdx}).`);
if (p > maxIdx)
throw new Error(`The parent of the dependency edge ${c} -> ${p} is out of range (max=${maxIdx}).`);
this.nodes[c].parent = this.nodes[p];
}
}
findRoot() {
const roots = this.nodes.filter((n) => n.isRoot());
if (roots.length === 0)
throw new Error("No root.");
if (roots.length > 1)
throw new Error(`Multiple roots: ${roots.map((r) => r.id)}.`);
return roots[0];
}
fillChildren() {
for (const n of this.nodes) {
n.children = this.nodes.filter((x) => x.parent === n);
}
}
/** Builds a tree based on the current state. Should not be called more than once. */
build() {
if (this.nodes.length === 0)
return null;
// Creates an ordered dependency tree based on the contents this builder.
node_1.NodeUtils.sort(this.nodes);
if (this.nodes[0].idx !== 0 ||
!(0, common_1.isSequential)(this.nodes.map((x) => x.idx))) {
throw new Error("Indexes are not sequential.");
}
this.fillParents();
const root = this.findRoot(); // exactly one root check; addDependency checks for multiple parents => tree
this.fillChildren();
return new Tree(root, this.nodes);
}
}
exports.TreeBuilder = TreeBuilder;