UNPKG

tynder

Version:

TypeScript friendly Data validator for JavaScript.

228 lines 9.16 kB
"use strict"; // Copyright (c) 2019 Shellyl_N and Authors // license: ISC // https://github.com/shellyln Object.defineProperty(exports, "__esModule", { value: true }); exports.compile = exports.parse = void 0; const types_1 = require("fruitsconfits/modules/lib/types"); const parser_1 = require("fruitsconfits/modules/lib/parser"); const core_1 = require("liyad/modules/s-exp/operators/core"); const interpreters_1 = require("liyad/modules/s-exp/interpreters"); const defaults_1 = require("liyad/modules/s-exp/defaults"); const operators = require("./operators"); const resolver_1 = require("./lib/resolver"); const protection_1 = require("./lib/protection"); const compiler_1 = require("./lib/compiler"); function parseExternalDirective(s) { const z = compiler_1.externalTypeDef(types_1.parserInput(s, { /* TODO: set initial state to the context */})); if (!z.succeeded) { throw new Error('Invalid external directive.'); } return z.tokens; } function parse(s) { const z = compiler_1.program(types_1.parserInput(s, { /* TODO: set initial state to the context */})); if (!z.succeeded) { throw new Error(parser_1.formatErrorMessage(z)); } return z.tokens; } exports.parse = parse; const lisp = (() => { let config = Object.assign({}, defaults_1.defaultConfig); config.reservedNames = Object.assign({}, config.reservedNames, { Template: '$concat', }); config = core_1.default(config); config.stripComments = true; return interpreters_1.SExpression(config); })(); // tslint:disable: object-literal-key-quotes function compile(s) { const mapTyToTySet = new Map(); const schema = new Map(); let gensymCount = 0; const def = (name, ty) => { let ret = ty; const sym = typeof name === 'string' ? name : name.symbol; if (protection_1.isUnsafeVarNames(protection_1.dummyTargetObject, sym)) { throw new Error(`Unsafe symbol name is appeared: ${sym}`); } if (!mapTyToTySet.has(ret)) { const originalTypeName = ret.typeName; ret = operators.withName(operators.withTypeName(originalTypeName ? operators.withOriginalTypeName(ret, originalTypeName) : ret, sym), sym); } const tySet = mapTyToTySet.has(ret) ? mapTyToTySet.get(ret) : { ty: ret, exported: false, isDeclare: false, resolved: false }; schema.set(sym, tySet); if (!mapTyToTySet.has(ret)) { // TODO: aliases are not exported correctly mapTyToTySet.set(ret, tySet); } return ret; }; const ref = (name, ...memberNames) => { const sym = typeof name === 'string' ? name : name.symbol; if (protection_1.isUnsafeVarNames(protection_1.dummyTargetObject, sym)) { throw new Error(`Unsafe symbol name is appeared: ${sym}`); } const memberTreeSymbols = memberNames.map(x => { const ms = typeof x === 'string' ? x : x.symbol; if (protection_1.isUnsafeVarNames(protection_1.dummyTargetObject, ms)) { throw new Error(`Unsafe symbol name is appeared: ${ms}`); } return ms; }); if (!schema.has(sym)) { return (Object.assign({ kind: 'symlink', symlinkTargetName: sym, name: sym, typeName: sym, }, (0 < memberTreeSymbols.length ? { memberTree: memberTreeSymbols, } : {}))); } let ty = resolver_1.resolveMemberNames(schema.get(sym).ty, sym, memberTreeSymbols, 0); if (ty.noOutput) { ty = Object.assign({}, ty); delete ty.noOutput; } return ty; }; const redef = (original, ty) => { if (original === ty) { return ty; } // NOTE: 'ty' should already be registered to 'mapTyToTySet' and 'schema' const tySet = mapTyToTySet.has(original) ? mapTyToTySet.get(original) : { ty: original, exported: false, isDeclare: false, resolved: false }; tySet.ty = ty; mapTyToTySet.set(tySet.ty, tySet); if (ty.name) { schema.set(ty.name, tySet); } return tySet.ty; }; const exported = (ty) => { if (ty.kind === 'never' && typeof ty.passThruCodeBlock === 'string') { ty.passThruCodeBlock = `export ${ty.passThruCodeBlock}`; return ty; } else { // NOTE: 'ty' should already be registered to 'mapTyToTySet' and 'schema' const tySet = mapTyToTySet.has(ty) ? mapTyToTySet.get(ty) : { ty, exported: false, isDeclare: false, resolved: false }; tySet.exported = true; return ty; } }; const external = (...names) => { for (const name of names) { let ty = null; if (typeof name === 'string') { ty = def(name, operators.primitive('any')); } else { ty = def(name[0], name[1] ? name[1] : operators.primitive('any')); } ty.noOutput = true; } }; const asConst = (ty) => { switch (ty.kind) { case 'enum': // NOTE: `ty` may already `def`ed. ty.isConst = true; break; default: throw new Error(`It cannot set to const: ${ty.kind} ${ty.typeName || '(unnamed)'}`); } return ty; }; const asDeclare = (ty) => { // NOTE: 'ty' should already be registered to 'mapTyToTySet' and 'schema' const tySet = mapTyToTySet.has(ty) ? mapTyToTySet.get(ty) : { ty, exported: false, isDeclare: false, resolved: false }; tySet.isDeclare = true; return ty; }; const passthru = (str, docCommentText) => { const ty = { kind: 'never', passThruCodeBlock: str || '', }; if (docCommentText) { ty.docComment = docCommentText; } schema.set(`__$$$gensym_${gensymCount++}$$$__`, { ty, exported: false, isDeclare: false, resolved: false }); return ty; }; const directive = (name, body) => { switch (name) { case '@tynder-external': lisp.evaluateAST(parseExternalDirective(`external ${body} ;`)); break; case '@tynder-pass-through': passthru(body); break; default: throw new Error(`Unknown directive is appeared: ${name}`); } return []; }; lisp.setGlobals({ picked: operators.picked, omit: operators.omit, partial: operators.partial, intersect: operators.intersect, oneOf: operators.oneOf, subtract: operators.subtract, primitive: operators.primitive, primitiveValue: operators.primitiveValue, optional: operators.optional, repeated: operators.repeated, sequenceOf: operators.sequenceOf, spread: operators.spread, enumType: operators.enumType, objectType: operators.objectType, derived: operators.derived, def, ref, redef, export: exported, external, asConst, asDeclare, passthru, directive, docComment: operators.withDocComment, '@range': (minValue, maxValue) => (ty) => operators.withRange(minValue, maxValue)(ty), '@minValue': (minValue) => (ty) => operators.withMinValue(minValue)(ty), '@maxValue': (maxValue) => (ty) => operators.withMaxValue(maxValue)(ty), '@greaterThan': (greaterThan) => (ty) => operators.withGreaterThan(greaterThan)(ty), '@lessThan': (lessThan) => (ty) => operators.withLessThan(lessThan)(ty), '@minLength': (minLength) => (ty) => operators.withMinLength(minLength)(ty), '@maxLength': (maxLength) => (ty) => operators.withMaxLength(maxLength)(ty), '@match': (pattern) => (ty) => operators.withMatch(pattern)(ty), '@stereotype': (stereotype) => (ty) => operators.withStereotype(stereotype)(ty), '@constraint': (name, args) => (ty) => operators.withConstraint(name, args)(ty), '@forceCast': () => (ty) => operators.withForceCast()(ty), '@recordType': () => (ty) => operators.withRecordType()(ty), '@meta': (meta) => (ty) => operators.withMeta(meta)(ty), '@msg': (messages) => (ty) => operators.withMsg(messages)(ty), '@msgId': (messageId) => (ty) => operators.withMsgId(messageId)(ty), }); const z = parse(s); lisp.evaluateAST(z); return resolver_1.resolveSchema(schema); } exports.compile = compile; // tslint:enable: object-literal-key-quotes //# sourceMappingURL=compiler.js.map