123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const types_1 = require("./types");
- const __1 = require("..");
- const codegen_1 = require("../codegen");
- const ref_error_1 = require("../ref_error");
- const names_1 = require("../names");
- const code_1 = require("../../vocabularies/code");
- const ref_1 = require("../../vocabularies/jtd/ref");
- const type_1 = require("../../vocabularies/jtd/type");
- const parseJson_1 = require("../../runtime/parseJson");
- const util_1 = require("../util");
- const timestamp_1 = require("../../runtime/timestamp");
- const genParse = {
- elements: parseElements,
- values: parseValues,
- discriminator: parseDiscriminator,
- properties: parseProperties,
- optionalProperties: parseProperties,
- enum: parseEnum,
- type: parseType,
- ref: parseRef,
- };
- function compileParser(sch, definitions) {
- const _sch = __1.getCompilingSchema.call(this, sch);
- if (_sch)
- return _sch;
- const { es5, lines } = this.opts.code;
- const { ownProperties } = this.opts;
- const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties });
- const parseName = gen.scopeName("parse");
- const cxt = {
- self: this,
- gen,
- schema: sch.schema,
- schemaEnv: sch,
- definitions,
- data: names_1.default.data,
- parseName,
- char: gen.name("c"),
- };
- let sourceCode;
- try {
- this._compilations.add(sch);
- sch.parseName = parseName;
- parserFunction(cxt);
- gen.optimize(this.opts.code.optimize);
- const parseFuncCode = gen.toString();
- sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${parseFuncCode}`;
- const makeParse = new Function(`${names_1.default.scope}`, sourceCode);
- const parse = makeParse(this.scope.get());
- this.scope.value(parseName, { ref: parse });
- sch.parse = parse;
- }
- catch (e) {
- if (sourceCode)
- this.logger.error("Error compiling parser, function code:", sourceCode);
- delete sch.parse;
- delete sch.parseName;
- throw e;
- }
- finally {
- this._compilations.delete(sch);
- }
- return sch;
- }
- exports.default = compileParser;
- const undef = (0, codegen_1._) `undefined`;
- function parserFunction(cxt) {
- const { gen, parseName, char } = cxt;
- gen.func(parseName, (0, codegen_1._) `${names_1.default.json}, ${names_1.default.jsonPos}, ${names_1.default.jsonPart}`, false, () => {
- gen.let(names_1.default.data);
- gen.let(char);
- gen.assign((0, codegen_1._) `${parseName}.message`, undef);
- gen.assign((0, codegen_1._) `${parseName}.position`, undef);
- gen.assign(names_1.default.jsonPos, (0, codegen_1._) `${names_1.default.jsonPos} || 0`);
- gen.const(names_1.default.jsonLen, (0, codegen_1._) `${names_1.default.json}.length`);
- parseCode(cxt);
- skipWhitespace(cxt);
- gen.if(names_1.default.jsonPart, () => {
- gen.assign((0, codegen_1._) `${parseName}.position`, names_1.default.jsonPos);
- gen.return(names_1.default.data);
- });
- gen.if((0, codegen_1._) `${names_1.default.jsonPos} === ${names_1.default.jsonLen}`, () => gen.return(names_1.default.data));
- jsonSyntaxError(cxt);
- });
- }
- function parseCode(cxt) {
- let form;
- for (const key of types_1.jtdForms) {
- if (key in cxt.schema) {
- form = key;
- break;
- }
- }
- if (form)
- parseNullable(cxt, genParse[form]);
- else
- parseEmpty(cxt);
- }
- const parseBoolean = parseBooleanToken(true, parseBooleanToken(false, jsonSyntaxError));
- function parseNullable(cxt, parseForm) {
- const { gen, schema, data } = cxt;
- if (!schema.nullable)
- return parseForm(cxt);
- tryParseToken(cxt, "null", parseForm, () => gen.assign(data, null));
- }
- function parseElements(cxt) {
- const { gen, schema, data } = cxt;
- parseToken(cxt, "[");
- const ix = gen.let("i", 0);
- gen.assign(data, (0, codegen_1._) `[]`);
- parseItems(cxt, "]", () => {
- const el = gen.let("el");
- parseCode({ ...cxt, schema: schema.elements, data: el });
- gen.assign((0, codegen_1._) `${data}[${ix}++]`, el);
- });
- }
- function parseValues(cxt) {
- const { gen, schema, data } = cxt;
- parseToken(cxt, "{");
- gen.assign(data, (0, codegen_1._) `{}`);
- parseItems(cxt, "}", () => parseKeyValue(cxt, schema.values));
- }
- function parseItems(cxt, endToken, block) {
- tryParseItems(cxt, endToken, block);
- parseToken(cxt, endToken);
- }
- function tryParseItems(cxt, endToken, block) {
- const { gen } = cxt;
- gen.for((0, codegen_1._) `;${names_1.default.jsonPos}<${names_1.default.jsonLen} && ${jsonSlice(1)}!==${endToken};`, () => {
- block();
- tryParseToken(cxt, ",", () => gen.break(), hasItem);
- });
- function hasItem() {
- tryParseToken(cxt, endToken, () => { }, jsonSyntaxError);
- }
- }
- function parseKeyValue(cxt, schema) {
- const { gen } = cxt;
- const key = gen.let("key");
- parseString({ ...cxt, data: key });
- parseToken(cxt, ":");
- parsePropertyValue(cxt, key, schema);
- }
- function parseDiscriminator(cxt) {
- const { gen, data, schema } = cxt;
- const { discriminator, mapping } = schema;
- parseToken(cxt, "{");
- gen.assign(data, (0, codegen_1._) `{}`);
- const startPos = gen.const("pos", names_1.default.jsonPos);
- const value = gen.let("value");
- const tag = gen.let("tag");
- tryParseItems(cxt, "}", () => {
- const key = gen.let("key");
- parseString({ ...cxt, data: key });
- parseToken(cxt, ":");
- gen.if((0, codegen_1._) `${key} === ${discriminator}`, () => {
- parseString({ ...cxt, data: tag });
- gen.assign((0, codegen_1._) `${data}[${key}]`, tag);
- gen.break();
- }, () => parseEmpty({ ...cxt, data: value }) // can be discarded/skipped
- );
- });
- gen.assign(names_1.default.jsonPos, startPos);
- gen.if((0, codegen_1._) `${tag} === undefined`);
- parsingError(cxt, (0, codegen_1.str) `discriminator tag not found`);
- for (const tagValue in mapping) {
- gen.elseIf((0, codegen_1._) `${tag} === ${tagValue}`);
- parseSchemaProperties({ ...cxt, schema: mapping[tagValue] }, discriminator);
- }
- gen.else();
- parsingError(cxt, (0, codegen_1.str) `discriminator value not in schema`);
- gen.endIf();
- }
- function parseProperties(cxt) {
- const { gen, data } = cxt;
- parseToken(cxt, "{");
- gen.assign(data, (0, codegen_1._) `{}`);
- parseSchemaProperties(cxt);
- }
- function parseSchemaProperties(cxt, discriminator) {
- const { gen, schema, data } = cxt;
- const { properties, optionalProperties, additionalProperties } = schema;
- parseItems(cxt, "}", () => {
- const key = gen.let("key");
- parseString({ ...cxt, data: key });
- parseToken(cxt, ":");
- gen.if(false);
- parseDefinedProperty(cxt, key, properties);
- parseDefinedProperty(cxt, key, optionalProperties);
- if (discriminator) {
- gen.elseIf((0, codegen_1._) `${key} === ${discriminator}`);
- const tag = gen.let("tag");
- parseString({ ...cxt, data: tag }); // can be discarded, it is already assigned
- }
- gen.else();
- if (additionalProperties) {
- parseEmpty({ ...cxt, data: (0, codegen_1._) `${data}[${key}]` });
- }
- else {
- parsingError(cxt, (0, codegen_1.str) `property ${key} not allowed`);
- }
- gen.endIf();
- });
- if (properties) {
- const hasProp = (0, code_1.hasPropFunc)(gen);
- const allProps = (0, codegen_1.and)(...Object.keys(properties).map((p) => (0, codegen_1._) `${hasProp}.call(${data}, ${p})`));
- gen.if((0, codegen_1.not)(allProps), () => parsingError(cxt, (0, codegen_1.str) `missing required properties`));
- }
- }
- function parseDefinedProperty(cxt, key, schemas = {}) {
- const { gen } = cxt;
- for (const prop in schemas) {
- gen.elseIf((0, codegen_1._) `${key} === ${prop}`);
- parsePropertyValue(cxt, key, schemas[prop]);
- }
- }
- function parsePropertyValue(cxt, key, schema) {
- parseCode({ ...cxt, schema, data: (0, codegen_1._) `${cxt.data}[${key}]` });
- }
- function parseType(cxt) {
- const { gen, schema, data, self } = cxt;
- switch (schema.type) {
- case "boolean":
- parseBoolean(cxt);
- break;
- case "string":
- parseString(cxt);
- break;
- case "timestamp": {
- parseString(cxt);
- const vts = (0, util_1.useFunc)(gen, timestamp_1.default);
- const { allowDate, parseDate } = self.opts;
- const notValid = allowDate ? (0, codegen_1._) `!${vts}(${data}, true)` : (0, codegen_1._) `!${vts}(${data})`;
- const fail = parseDate
- ? (0, codegen_1.or)(notValid, (0, codegen_1._) `(${data} = new Date(${data}), false)`, (0, codegen_1._) `isNaN(${data}.valueOf())`)
- : notValid;
- gen.if(fail, () => parsingError(cxt, (0, codegen_1.str) `invalid timestamp`));
- break;
- }
- case "float32":
- case "float64":
- parseNumber(cxt);
- break;
- default: {
- const t = schema.type;
- if (!self.opts.int32range && (t === "int32" || t === "uint32")) {
- parseNumber(cxt, 16); // 2 ** 53 - max safe integer
- if (t === "uint32") {
- gen.if((0, codegen_1._) `${data} < 0`, () => parsingError(cxt, (0, codegen_1.str) `integer out of range`));
- }
- }
- else {
- const [min, max, maxDigits] = type_1.intRange[t];
- parseNumber(cxt, maxDigits);
- gen.if((0, codegen_1._) `${data} < ${min} || ${data} > ${max}`, () => parsingError(cxt, (0, codegen_1.str) `integer out of range`));
- }
- }
- }
- }
- function parseString(cxt) {
- parseToken(cxt, '"');
- parseWith(cxt, parseJson_1.parseJsonString);
- }
- function parseEnum(cxt) {
- const { gen, data, schema } = cxt;
- const enumSch = schema.enum;
- parseToken(cxt, '"');
- // TODO loopEnum
- gen.if(false);
- for (const value of enumSch) {
- const valueStr = JSON.stringify(value).slice(1); // remove starting quote
- gen.elseIf((0, codegen_1._) `${jsonSlice(valueStr.length)} === ${valueStr}`);
- gen.assign(data, (0, codegen_1.str) `${value}`);
- gen.add(names_1.default.jsonPos, valueStr.length);
- }
- gen.else();
- jsonSyntaxError(cxt);
- gen.endIf();
- }
- function parseNumber(cxt, maxDigits) {
- const { gen } = cxt;
- skipWhitespace(cxt);
- gen.if((0, codegen_1._) `"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), () => parseWith(cxt, parseJson_1.parseJsonNumber, maxDigits));
- }
- function parseBooleanToken(bool, fail) {
- return (cxt) => {
- const { gen, data } = cxt;
- tryParseToken(cxt, `${bool}`, () => fail(cxt), () => gen.assign(data, bool));
- };
- }
- function parseRef(cxt) {
- const { gen, self, definitions, schema, schemaEnv } = cxt;
- const { ref } = schema;
- const refSchema = definitions[ref];
- if (!refSchema)
- throw new ref_error_1.default(self.opts.uriResolver, "", ref, `No definition ${ref}`);
- if (!(0, ref_1.hasRef)(refSchema))
- return parseCode({ ...cxt, schema: refSchema });
- const { root } = schemaEnv;
- const sch = compileParser.call(self, new __1.SchemaEnv({ schema: refSchema, root }), definitions);
- partialParse(cxt, getParser(gen, sch), true);
- }
- function getParser(gen, sch) {
- return sch.parse
- ? gen.scopeValue("parse", { ref: sch.parse })
- : (0, codegen_1._) `${gen.scopeValue("wrapper", { ref: sch })}.parse`;
- }
- function parseEmpty(cxt) {
- parseWith(cxt, parseJson_1.parseJson);
- }
- function parseWith(cxt, parseFunc, args) {
- partialParse(cxt, (0, util_1.useFunc)(cxt.gen, parseFunc), args);
- }
- function partialParse(cxt, parseFunc, args) {
- const { gen, data } = cxt;
- gen.assign(data, (0, codegen_1._) `${parseFunc}(${names_1.default.json}, ${names_1.default.jsonPos}${args ? (0, codegen_1._) `, ${args}` : codegen_1.nil})`);
- gen.assign(names_1.default.jsonPos, (0, codegen_1._) `${parseFunc}.position`);
- gen.if((0, codegen_1._) `${data} === undefined`, () => parsingError(cxt, (0, codegen_1._) `${parseFunc}.message`));
- }
- function parseToken(cxt, tok) {
- tryParseToken(cxt, tok, jsonSyntaxError);
- }
- function tryParseToken(cxt, tok, fail, success) {
- const { gen } = cxt;
- const n = tok.length;
- skipWhitespace(cxt);
- gen.if((0, codegen_1._) `${jsonSlice(n)} === ${tok}`, () => {
- gen.add(names_1.default.jsonPos, n);
- success === null || success === void 0 ? void 0 : success(cxt);
- }, () => fail(cxt));
- }
- function skipWhitespace({ gen, char: c }) {
- gen.code((0, codegen_1._) `while((${c}=${names_1.default.json}[${names_1.default.jsonPos}],${c}===" "||${c}==="\\n"||${c}==="\\r"||${c}==="\\t"))${names_1.default.jsonPos}++;`);
- }
- function jsonSlice(len) {
- return len === 1
- ? (0, codegen_1._) `${names_1.default.json}[${names_1.default.jsonPos}]`
- : (0, codegen_1._) `${names_1.default.json}.slice(${names_1.default.jsonPos}, ${names_1.default.jsonPos}+${len})`;
- }
- function jsonSyntaxError(cxt) {
- parsingError(cxt, (0, codegen_1._) `"unexpected token " + ${names_1.default.json}[${names_1.default.jsonPos}]`);
- }
- function parsingError({ gen, parseName }, msg) {
- gen.assign((0, codegen_1._) `${parseName}.message`, msg);
- gen.assign((0, codegen_1._) `${parseName}.position`, names_1.default.jsonPos);
- gen.return(undef);
- }
- //# sourceMappingURL=parse.js.map
|