UNPKG

@shexjs/visitor

Version:

Shape Expressions Visitor and schema index generator.

333 lines (327 loc) 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ShExVisitor = void 0; function isTerm(t) { return typeof t !== "object" || "value" in t && Object.keys(t).reduce((r, k) => { return r === false ? r : ["value", "type", "language"].indexOf(k) !== -1; }, true); } function isShapeRef(expr) { return typeof expr === "string"; // test for JSON-LD @ID } // function expect (l, r) { const ls = JSON.stringify(l), rs = JSON.stringify(r); if (ls !== rs) throw Error(ls+" !== "+rs); } function visitMap(map, val) { const ret = {}; Object.keys(map).forEach(function (item) { ret[item] = val(map[item]); }); return ret; } class ShExVisitor { constructor(...ctor_args) { this.ctor_args = ctor_args; const r = this; r.visitBase = r.visitStart = r.visitClosed = r["visit@context"] = r._visitValue; r.visitRestricts = r.visitExtends = r._visitShapeExprList; r.visitExtra = r.visitAnnotations = r._visitList; r.visitAbstract = r.visitInverse = r.visitPredicate = r._visitValue; r.visitName = r.visitId = r.visitCode = r.visitMin = r.visitMax = r._visitValue; r.visitType = r.visitNodeKind = r.visitDatatype = r.visitPattern = r.visitFlags = r.visitLength = r.visitMinlength = r.visitMaxlength = r.visitMininclusive = r.visitMinexclusive = r.visitMaxinclusive = r.visitMaxexclusive = r.visitTotaldigits = r.visitFractiondigits = r._visitValue; r.visitOneOf = r.visitEachOf = r._visitGroup; r.visitShapeAnd = r.visitShapeOr = r._visitShapeGroup; r.visitInclude = r._visitValue; return r; // Expect property p with value v in object o } runtimeError(e) { throw e; } visitSchema(schema, ...args) { const ret = { type: "Schema" }; this._expect(schema, "type", "Schema"); this._maybeSet(schema, ret, "Schema", ["@context", "prefixes", "base", "imports", "startActs", "start", "shapes"], ["_base", "_prefixes", "_index", "_sourceMap"], ...args); return ret; } visitPrefixes(prefixes, ...args) { return prefixes === undefined ? undefined : visitMap(prefixes, function (val) { return val; }); } visitIRI(i, ...args) { return i; } visitImports(imports, ...args) { const _Visitor = this; return imports.map(function (imp) { return _Visitor.visitIRI(imp, args); }); } visitStartActs(startActs, ...args) { const _Visitor = this; return startActs === undefined ? undefined : startActs.map(function (act) { return _Visitor.visitSemAct(act, ...args); }); } visitSemActs(semActs, ...args) { const _Visitor = this; if (semActs === undefined) return undefined; const ret = []; Object.keys(semActs).forEach(function (label) { ret.push(_Visitor.visitSemAct(semActs[label], label, ...args)); }); return ret; } visitSemAct(semAct, label, ...args) { const ret = { type: "SemAct" }; this._expect(semAct, "type", "SemAct"); this._maybeSet(semAct, ret, "SemAct", ["name", "code"], null, ...args); return ret; } visitShapes(shapes, ...args) { const _Visitor = this; if (shapes === undefined) return undefined; return shapes.map(shapeExpr => _Visitor.visitShapeDecl(shapeExpr, ...args)); } visitShapeDecl(decl, ...args) { return this._maybeSet(decl, { type: "ShapeDecl" }, "ShapeDecl", ["id", "abstract", "restricts", "shapeExpr"], null, ...args); } visitShapeExpr(expr, ...args) { if (isShapeRef(expr)) return this.visitShapeRef(expr, ...args); switch (expr.type) { case "Shape": return this.visitShape(expr, ...args); case "NodeConstraint": return this.visitNodeConstraint(expr, ...args); case "ShapeAnd": return this.visitShapeAnd(expr, ...args); case "ShapeOr": return this.visitShapeOr(expr, ...args); case "ShapeNot": return this.visitShapeNot(expr, ...args); case "ShapeExternal": return this.visitShapeExternal(expr, ...args); default: throw Error("unexpected shapeExpr type: " + expr.type); } } visitValueExpr(expr, ...args) { return this.visitShapeExpr(expr, ...args); // call potentially overloaded visitShapeExpr } // _visitShapeGroup: visit a grouping expression (shapeAnd, shapeOr) _visitShapeGroup(expr, ...args) { this._testUnknownAttributes(expr, ["shapeExprs"], expr.type, this.visitShapeNot); const _Visitor = this; const r = { type: expr.type }; if ("id" in expr) r.id = expr.id; r.shapeExprs = expr.shapeExprs.map(function (nested) { return _Visitor.visitShapeExpr(nested, ...args); }); return r; } // _visitShapeNot: visit negated shape visitShapeNot(expr, ...args) { this._testUnknownAttributes(expr, ["shapeExpr"], "ShapeNot", this.visitShapeNot); const r = { type: expr.type }; if ("id" in expr) r.id = expr.id; r.shapeExpr = this.visitShapeExpr(expr.shapeExpr, ...args); return r; } // ### `visitNodeConstraint` deep-copies the structure of a shape visitShape(shape, ...args) { const ret = { type: "Shape" }; this._expect(shape, "type", "Shape"); this._maybeSet(shape, ret, "Shape", ["abstract", "extends", "closed", "expression", "extra", "semActs", "annotations"], null, ...args); return ret; } _visitShapeExprList(ext, ...args) { const _Visitor = this; return ext.map(function (t) { return _Visitor.visitShapeExpr(t, ...args); }); } // ### `visitNodeConstraint` deep-copies the structure of a shape visitNodeConstraint(shape, ...args) { const ret = { type: "NodeConstraint" }; this._expect(shape, "type", "NodeConstraint"); this._maybeSet(shape, ret, "NodeConstraint", ["nodeKind", "datatype", "pattern", "flags", "length", "reference", "minlength", "maxlength", "mininclusive", "minexclusive", "maxinclusive", "maxexclusive", "totaldigits", "fractiondigits", "values", "annotations", "semActs"], null, ...args); return ret; } visitShapeRef(reference, ...args) { if (typeof reference !== "string") { let ex = Exception("visitShapeRef expected a string, not " + JSON.stringify(reference)); console.warn(ex); throw ex; } return reference; } visitShapeExternal(expr, ...args) { this._testUnknownAttributes(expr, ["id"], "ShapeExternal", this.visitShapeNot); return Object.assign("id" in expr ? { id: expr.id } : {}, { type: "ShapeExternal" }); } // _visitGroup: visit a grouping expression (someOf or eachOf) _visitGroup(expr, type, ...args) { const _Visitor = this; const r = Object.assign( // pre-declare an id so it sorts to the top "id" in expr ? { id: null } : {}, { type: expr.type }); r.expressions = expr.expressions.map(function (nested) { return _Visitor.visitExpression(nested, ...args); }); return this._maybeSet(expr, r, "expr", ["id", "min", "max", "annotations", "semActs"], ["expressions"], ...args); } visitTripleConstraint(expr, ...args) { return this._maybeSet(expr, Object.assign( // pre-declare an id so it sorts to the top "id" in expr ? { id: null } : {}, { type: "TripleConstraint" }), "TripleConstraint", ["id", "inverse", "predicate", "valueExpr", "min", "max", "annotations", "semActs"], null, ...args); } visitTripleExpr(expr, ...args) { if (typeof expr === "string") return this.visitInclusion(expr); switch (expr.type) { case "TripleConstraint": return this.visitTripleConstraint(expr, ...args); case "OneOf": return this.visitOneOf(expr, ...args); case "EachOf": return this.visitEachOf(expr, ...args); default: throw Error("unexpected expression type: " + expr.type); } } visitExpression(expr, ...args) { return this.visitTripleExpr(expr, ...args); // call potentially overloaded visitTripleExpr } visitValues(values, ...args) { const _Visitor = this; return values.map(function (t) { return isTerm(t) || t.type === "Language" ? t : _Visitor.visitStemRange(t, ...args); }); } visitStemRange(t, ...args) { const _Visitor = this; // console.log(Error(t.type).stack); // this._expect(t, "type", "IriStemRange"); if (!("type" in t)) _Visitor.runtimeError(Error("expected " + JSON.stringify(t) + " to have a 'type' attribute.")); const stemRangeTypes = ["IriStem", "LiteralStem", "LanguageStem", "IriStemRange", "LiteralStemRange", "LanguageStemRange"]; if (stemRangeTypes.indexOf(t.type) === -1) _Visitor.runtimeError(Error("expected type attribute '" + t.type + "' to be in '" + stemRangeTypes + "'.")); let stem; if (isTerm(t)) { this._expect(t.stem, "type", "Wildcard"); stem = { type: t.type, stem: { type: "Wildcard" } }; } else { stem = { type: t.type, stem: t.stem }; } if (t.exclusions) { stem.exclusions = t.exclusions.map(function (c) { return _Visitor.visitExclusion(c, ...args); }); } return stem; } visitExclusion(c, ...args) { if (!isTerm(c)) { // this._expect(c, "type", "IriStem"); if (!("type" in c)) _Visitor.runtimeError(Error("expected " + JSON.stringify(c) + " to have a 'type' attribute.")); const stemTypes = ["IriStem", "LiteralStem", "LanguageStem"]; if (stemTypes.indexOf(c.type) === -1) _Visitor.runtimeError(Error("expected type attribute '" + c.type + "' to be in '" + stemTypes + "'.")); return { type: c.type, stem: c.stem }; } else { return c; } } visitInclusion(inclusion, ...args) { if (typeof inclusion !== "string") { let ex = Exception("visitInclusion expected a string, not " + JSON.stringify(inclusion)); console.warn(ex); throw ex; } return inclusion; } _maybeSet(obj, ret, context, members, ignore, ...args) { const _Visitor = this; this._testUnknownAttributes(obj, ignore ? members.concat(ignore) : members, context, this._maybeSet); members.forEach(function (member) { const methodName = "visit" + member.charAt(0).toUpperCase() + member.slice(1); if (member in obj) { const f = _Visitor[methodName]; if (typeof f !== "function") { throw Error(methodName + " not found in Visitor"); } const t = f.call(_Visitor, obj[member], ...args); if (t !== undefined) { ret[member] = t; } } }); return ret; } _visitValue(v, ...args) { return v; } _visitList(l, ...args) { return l.slice(); } _testUnknownAttributes(obj, expected, context, captureFrame) { const unknownMembers = Object.keys(obj).reduce(function (ret, k) { return k !== "type" && expected.indexOf(k) === -1 ? ret.concat(k) : ret; }, []); if (unknownMembers.length > 0) { const e = Error("unknown propert" + (unknownMembers.length > 1 ? "ies" : "y") + ": " + unknownMembers.map(function (p) { return "\"" + p + "\""; }).join(",") + " in " + context + ": " + JSON.stringify(obj)); Error.captureStackTrace(e, captureFrame); throw e; } } _expect(o, p, v) { if (!(p in o)) this.runtimeError(Error("expected " + JSON.stringify(o) + " to have a ." + p)); if (arguments.length > 2 && o[p] !== v) this.runtimeError(Error("expected " + o[p] + " to equal " + v)); } } exports.ShExVisitor = ShExVisitor; /** create indexes for schema */ class ShExIndexVisitor extends ShExVisitor { constructor () { this.index = { shapeExprs: {}, tripleExprs: {} }; } visitTripleExpr (expression, ...args) { if (typeof expression === "object" && "id" in expression) this.index.tripleExprs[expression.id] = expression; return super.visitExpression(expression, ...args); }; visitShapeDecl (shapeExpr, ...args) { if (typeof shapeExpr === "object" && "id" in shapeExpr) this.index.shapeExprs[shapeExpr.id] = shapeExpr; return super.visitShapeDecl(shapeExpr, ...args); }; static index (schema) { const v = new ShExIndexVisitor(); v.visitSchema(schema); return v.index; } } exports.ShExIndexVisitor = ShExIndexVisitor; if (typeof require !== 'undefined' && typeof exports !== 'undefined') module.exports = ShExVisitor; //# sourceMappingURL=ShExVisitor.js.map