[6a3a178] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | const types_1 = require("./types");
|
---|
| 4 | const __1 = require("..");
|
---|
| 5 | const codegen_1 = require("../codegen");
|
---|
| 6 | const ref_error_1 = require("../ref_error");
|
---|
| 7 | const names_1 = require("../names");
|
---|
| 8 | const code_1 = require("../../vocabularies/code");
|
---|
| 9 | const ref_1 = require("../../vocabularies/jtd/ref");
|
---|
| 10 | const type_1 = require("../../vocabularies/jtd/type");
|
---|
| 11 | const parseJson_1 = require("../../runtime/parseJson");
|
---|
| 12 | const util_1 = require("../util");
|
---|
| 13 | const timestamp_1 = require("../../runtime/timestamp");
|
---|
| 14 | const genParse = {
|
---|
| 15 | elements: parseElements,
|
---|
| 16 | values: parseValues,
|
---|
| 17 | discriminator: parseDiscriminator,
|
---|
| 18 | properties: parseProperties,
|
---|
| 19 | optionalProperties: parseProperties,
|
---|
| 20 | enum: parseEnum,
|
---|
| 21 | type: parseType,
|
---|
| 22 | ref: parseRef,
|
---|
| 23 | };
|
---|
| 24 | function compileParser(sch, definitions) {
|
---|
| 25 | const _sch = __1.getCompilingSchema.call(this, sch);
|
---|
| 26 | if (_sch)
|
---|
| 27 | return _sch;
|
---|
| 28 | const { es5, lines } = this.opts.code;
|
---|
| 29 | const { ownProperties } = this.opts;
|
---|
| 30 | const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties });
|
---|
| 31 | const parseName = gen.scopeName("parse");
|
---|
| 32 | const cxt = {
|
---|
| 33 | self: this,
|
---|
| 34 | gen,
|
---|
| 35 | schema: sch.schema,
|
---|
| 36 | schemaEnv: sch,
|
---|
| 37 | definitions,
|
---|
| 38 | data: names_1.default.data,
|
---|
| 39 | parseName,
|
---|
| 40 | char: gen.name("c"),
|
---|
| 41 | };
|
---|
| 42 | let sourceCode;
|
---|
| 43 | try {
|
---|
| 44 | this._compilations.add(sch);
|
---|
| 45 | sch.parseName = parseName;
|
---|
| 46 | parserFunction(cxt);
|
---|
| 47 | gen.optimize(this.opts.code.optimize);
|
---|
| 48 | const parseFuncCode = gen.toString();
|
---|
| 49 | sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${parseFuncCode}`;
|
---|
| 50 | const makeParse = new Function(`${names_1.default.scope}`, sourceCode);
|
---|
| 51 | const parse = makeParse(this.scope.get());
|
---|
| 52 | this.scope.value(parseName, { ref: parse });
|
---|
| 53 | sch.parse = parse;
|
---|
| 54 | }
|
---|
| 55 | catch (e) {
|
---|
| 56 | if (sourceCode)
|
---|
| 57 | this.logger.error("Error compiling parser, function code:", sourceCode);
|
---|
| 58 | delete sch.parse;
|
---|
| 59 | delete sch.parseName;
|
---|
| 60 | throw e;
|
---|
| 61 | }
|
---|
| 62 | finally {
|
---|
| 63 | this._compilations.delete(sch);
|
---|
| 64 | }
|
---|
| 65 | return sch;
|
---|
| 66 | }
|
---|
| 67 | exports.default = compileParser;
|
---|
| 68 | const undef = codegen_1._ `undefined`;
|
---|
| 69 | function parserFunction(cxt) {
|
---|
| 70 | const { gen, parseName, char } = cxt;
|
---|
| 71 | gen.func(parseName, codegen_1._ `${names_1.default.json}, ${names_1.default.jsonPos}, ${names_1.default.jsonPart}`, false, () => {
|
---|
| 72 | gen.let(names_1.default.data);
|
---|
| 73 | gen.let(char);
|
---|
| 74 | gen.assign(codegen_1._ `${parseName}.message`, undef);
|
---|
| 75 | gen.assign(codegen_1._ `${parseName}.position`, undef);
|
---|
| 76 | gen.assign(names_1.default.jsonPos, codegen_1._ `${names_1.default.jsonPos} || 0`);
|
---|
| 77 | gen.const(names_1.default.jsonLen, codegen_1._ `${names_1.default.json}.length`);
|
---|
| 78 | parseCode(cxt);
|
---|
| 79 | skipWhitespace(cxt);
|
---|
| 80 | gen.if(names_1.default.jsonPart, () => {
|
---|
| 81 | gen.assign(codegen_1._ `${parseName}.position`, names_1.default.jsonPos);
|
---|
| 82 | gen.return(names_1.default.data);
|
---|
| 83 | });
|
---|
| 84 | gen.if(codegen_1._ `${names_1.default.jsonPos} === ${names_1.default.jsonLen}`, () => gen.return(names_1.default.data));
|
---|
| 85 | jsonSyntaxError(cxt);
|
---|
| 86 | });
|
---|
| 87 | }
|
---|
| 88 | function parseCode(cxt) {
|
---|
| 89 | let form;
|
---|
| 90 | for (const key of types_1.jtdForms) {
|
---|
| 91 | if (key in cxt.schema) {
|
---|
| 92 | form = key;
|
---|
| 93 | break;
|
---|
| 94 | }
|
---|
| 95 | }
|
---|
| 96 | if (form)
|
---|
| 97 | parseNullable(cxt, genParse[form]);
|
---|
| 98 | else
|
---|
| 99 | parseEmpty(cxt);
|
---|
| 100 | }
|
---|
| 101 | const parseBoolean = parseBooleanToken(true, parseBooleanToken(false, jsonSyntaxError));
|
---|
| 102 | function parseNullable(cxt, parseForm) {
|
---|
| 103 | const { gen, schema, data } = cxt;
|
---|
| 104 | if (!schema.nullable)
|
---|
| 105 | return parseForm(cxt);
|
---|
| 106 | tryParseToken(cxt, "null", parseForm, () => gen.assign(data, null));
|
---|
| 107 | }
|
---|
| 108 | function parseElements(cxt) {
|
---|
| 109 | const { gen, schema, data } = cxt;
|
---|
| 110 | parseToken(cxt, "[");
|
---|
| 111 | const ix = gen.let("i", 0);
|
---|
| 112 | gen.assign(data, codegen_1._ `[]`);
|
---|
| 113 | parseItems(cxt, "]", () => {
|
---|
| 114 | const el = gen.let("el");
|
---|
| 115 | parseCode({ ...cxt, schema: schema.elements, data: el });
|
---|
| 116 | gen.assign(codegen_1._ `${data}[${ix}++]`, el);
|
---|
| 117 | });
|
---|
| 118 | }
|
---|
| 119 | function parseValues(cxt) {
|
---|
| 120 | const { gen, schema, data } = cxt;
|
---|
| 121 | parseToken(cxt, "{");
|
---|
| 122 | gen.assign(data, codegen_1._ `{}`);
|
---|
| 123 | parseItems(cxt, "}", () => parseKeyValue(cxt, schema.values));
|
---|
| 124 | }
|
---|
| 125 | function parseItems(cxt, endToken, block) {
|
---|
| 126 | tryParseItems(cxt, endToken, block);
|
---|
| 127 | parseToken(cxt, endToken);
|
---|
| 128 | }
|
---|
| 129 | function tryParseItems(cxt, endToken, block) {
|
---|
| 130 | const { gen } = cxt;
|
---|
| 131 | gen.for(codegen_1._ `;${names_1.default.jsonPos}<${names_1.default.jsonLen} && ${jsonSlice(1)}!==${endToken};`, () => {
|
---|
| 132 | block();
|
---|
| 133 | tryParseToken(cxt, ",", () => gen.break(), hasItem);
|
---|
| 134 | });
|
---|
| 135 | function hasItem() {
|
---|
| 136 | tryParseToken(cxt, endToken, () => { }, jsonSyntaxError);
|
---|
| 137 | }
|
---|
| 138 | }
|
---|
| 139 | function parseKeyValue(cxt, schema) {
|
---|
| 140 | const { gen } = cxt;
|
---|
| 141 | const key = gen.let("key");
|
---|
| 142 | parseString({ ...cxt, data: key });
|
---|
| 143 | parseToken(cxt, ":");
|
---|
| 144 | parsePropertyValue(cxt, key, schema);
|
---|
| 145 | }
|
---|
| 146 | function parseDiscriminator(cxt) {
|
---|
| 147 | const { gen, data, schema } = cxt;
|
---|
| 148 | const { discriminator, mapping } = schema;
|
---|
| 149 | parseToken(cxt, "{");
|
---|
| 150 | gen.assign(data, codegen_1._ `{}`);
|
---|
| 151 | const startPos = gen.const("pos", names_1.default.jsonPos);
|
---|
| 152 | const value = gen.let("value");
|
---|
| 153 | const tag = gen.let("tag");
|
---|
| 154 | tryParseItems(cxt, "}", () => {
|
---|
| 155 | const key = gen.let("key");
|
---|
| 156 | parseString({ ...cxt, data: key });
|
---|
| 157 | parseToken(cxt, ":");
|
---|
| 158 | gen.if(codegen_1._ `${key} === ${discriminator}`, () => {
|
---|
| 159 | parseString({ ...cxt, data: tag });
|
---|
| 160 | gen.assign(codegen_1._ `${data}[${key}]`, tag);
|
---|
| 161 | gen.break();
|
---|
| 162 | }, () => parseEmpty({ ...cxt, data: value }) // can be discarded/skipped
|
---|
| 163 | );
|
---|
| 164 | });
|
---|
| 165 | gen.assign(names_1.default.jsonPos, startPos);
|
---|
| 166 | gen.if(codegen_1._ `${tag} === undefined`);
|
---|
| 167 | parsingError(cxt, codegen_1.str `discriminator tag not found`);
|
---|
| 168 | for (const tagValue in mapping) {
|
---|
| 169 | gen.elseIf(codegen_1._ `${tag} === ${tagValue}`);
|
---|
| 170 | parseSchemaProperties({ ...cxt, schema: mapping[tagValue] }, discriminator);
|
---|
| 171 | }
|
---|
| 172 | gen.else();
|
---|
| 173 | parsingError(cxt, codegen_1.str `discriminator value not in schema`);
|
---|
| 174 | gen.endIf();
|
---|
| 175 | }
|
---|
| 176 | function parseProperties(cxt) {
|
---|
| 177 | const { gen, data } = cxt;
|
---|
| 178 | parseToken(cxt, "{");
|
---|
| 179 | gen.assign(data, codegen_1._ `{}`);
|
---|
| 180 | parseSchemaProperties(cxt);
|
---|
| 181 | }
|
---|
| 182 | function parseSchemaProperties(cxt, discriminator) {
|
---|
| 183 | const { gen, schema, data } = cxt;
|
---|
| 184 | const { properties, optionalProperties, additionalProperties } = schema;
|
---|
| 185 | parseItems(cxt, "}", () => {
|
---|
| 186 | const key = gen.let("key");
|
---|
| 187 | parseString({ ...cxt, data: key });
|
---|
| 188 | parseToken(cxt, ":");
|
---|
| 189 | gen.if(false);
|
---|
| 190 | parseDefinedProperty(cxt, key, properties);
|
---|
| 191 | parseDefinedProperty(cxt, key, optionalProperties);
|
---|
| 192 | if (discriminator) {
|
---|
| 193 | gen.elseIf(codegen_1._ `${key} === ${discriminator}`);
|
---|
| 194 | const tag = gen.let("tag");
|
---|
| 195 | parseString({ ...cxt, data: tag }); // can be discarded, it is already assigned
|
---|
| 196 | }
|
---|
| 197 | gen.else();
|
---|
| 198 | if (additionalProperties) {
|
---|
| 199 | parseEmpty({ ...cxt, data: codegen_1._ `${data}[${key}]` });
|
---|
| 200 | }
|
---|
| 201 | else {
|
---|
| 202 | parsingError(cxt, codegen_1.str `property ${key} not allowed`);
|
---|
| 203 | }
|
---|
| 204 | gen.endIf();
|
---|
| 205 | });
|
---|
| 206 | if (properties) {
|
---|
| 207 | const hasProp = code_1.hasPropFunc(gen);
|
---|
| 208 | const allProps = codegen_1.and(...Object.keys(properties).map((p) => codegen_1._ `${hasProp}.call(${data}, ${p})`));
|
---|
| 209 | gen.if(codegen_1.not(allProps), () => parsingError(cxt, codegen_1.str `missing required properties`));
|
---|
| 210 | }
|
---|
| 211 | }
|
---|
| 212 | function parseDefinedProperty(cxt, key, schemas = {}) {
|
---|
| 213 | const { gen } = cxt;
|
---|
| 214 | for (const prop in schemas) {
|
---|
| 215 | gen.elseIf(codegen_1._ `${key} === ${prop}`);
|
---|
| 216 | parsePropertyValue(cxt, key, schemas[prop]);
|
---|
| 217 | }
|
---|
| 218 | }
|
---|
| 219 | function parsePropertyValue(cxt, key, schema) {
|
---|
| 220 | parseCode({ ...cxt, schema, data: codegen_1._ `${cxt.data}[${key}]` });
|
---|
| 221 | }
|
---|
| 222 | function parseType(cxt) {
|
---|
| 223 | const { gen, schema, data, self } = cxt;
|
---|
| 224 | switch (schema.type) {
|
---|
| 225 | case "boolean":
|
---|
| 226 | parseBoolean(cxt);
|
---|
| 227 | break;
|
---|
| 228 | case "string":
|
---|
| 229 | parseString(cxt);
|
---|
| 230 | break;
|
---|
| 231 | case "timestamp": {
|
---|
| 232 | parseString(cxt);
|
---|
| 233 | const vts = util_1.useFunc(gen, timestamp_1.default);
|
---|
| 234 | const { allowDate, parseDate } = self.opts;
|
---|
| 235 | const notValid = allowDate ? codegen_1._ `!${vts}(${data}, true)` : codegen_1._ `!${vts}(${data})`;
|
---|
| 236 | const fail = parseDate
|
---|
| 237 | ? codegen_1.or(notValid, codegen_1._ `(${data} = new Date(${data}), false)`, codegen_1._ `isNaN(${data}.valueOf())`)
|
---|
| 238 | : notValid;
|
---|
| 239 | gen.if(fail, () => parsingError(cxt, codegen_1.str `invalid timestamp`));
|
---|
| 240 | break;
|
---|
| 241 | }
|
---|
| 242 | case "float32":
|
---|
| 243 | case "float64":
|
---|
| 244 | parseNumber(cxt);
|
---|
| 245 | break;
|
---|
| 246 | default: {
|
---|
| 247 | const t = schema.type;
|
---|
| 248 | if (!self.opts.int32range && (t === "int32" || t === "uint32")) {
|
---|
| 249 | parseNumber(cxt, 16); // 2 ** 53 - max safe integer
|
---|
| 250 | if (t === "uint32") {
|
---|
| 251 | gen.if(codegen_1._ `${data} < 0`, () => parsingError(cxt, codegen_1.str `integer out of range`));
|
---|
| 252 | }
|
---|
| 253 | }
|
---|
| 254 | else {
|
---|
| 255 | const [min, max, maxDigits] = type_1.intRange[t];
|
---|
| 256 | parseNumber(cxt, maxDigits);
|
---|
| 257 | gen.if(codegen_1._ `${data} < ${min} || ${data} > ${max}`, () => parsingError(cxt, codegen_1.str `integer out of range`));
|
---|
| 258 | }
|
---|
| 259 | }
|
---|
| 260 | }
|
---|
| 261 | }
|
---|
| 262 | function parseString(cxt) {
|
---|
| 263 | parseToken(cxt, '"');
|
---|
| 264 | parseWith(cxt, parseJson_1.parseJsonString);
|
---|
| 265 | }
|
---|
| 266 | function parseEnum(cxt) {
|
---|
| 267 | const { gen, data, schema } = cxt;
|
---|
| 268 | const enumSch = schema.enum;
|
---|
| 269 | parseToken(cxt, '"');
|
---|
| 270 | // TODO loopEnum
|
---|
| 271 | gen.if(false);
|
---|
| 272 | for (const value of enumSch) {
|
---|
| 273 | const valueStr = JSON.stringify(value).slice(1); // remove starting quote
|
---|
| 274 | gen.elseIf(codegen_1._ `${jsonSlice(valueStr.length)} === ${valueStr}`);
|
---|
| 275 | gen.assign(data, codegen_1.str `${value}`);
|
---|
| 276 | gen.add(names_1.default.jsonPos, valueStr.length);
|
---|
| 277 | }
|
---|
| 278 | gen.else();
|
---|
| 279 | jsonSyntaxError(cxt);
|
---|
| 280 | gen.endIf();
|
---|
| 281 | }
|
---|
| 282 | function parseNumber(cxt, maxDigits) {
|
---|
| 283 | const { gen } = cxt;
|
---|
| 284 | skipWhitespace(cxt);
|
---|
| 285 | gen.if(codegen_1._ `"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), () => parseWith(cxt, parseJson_1.parseJsonNumber, maxDigits));
|
---|
| 286 | }
|
---|
| 287 | function parseBooleanToken(bool, fail) {
|
---|
| 288 | return (cxt) => {
|
---|
| 289 | const { gen, data } = cxt;
|
---|
| 290 | tryParseToken(cxt, `${bool}`, () => fail(cxt), () => gen.assign(data, bool));
|
---|
| 291 | };
|
---|
| 292 | }
|
---|
| 293 | function parseRef(cxt) {
|
---|
| 294 | const { gen, self, definitions, schema, schemaEnv } = cxt;
|
---|
| 295 | const { ref } = schema;
|
---|
| 296 | const refSchema = definitions[ref];
|
---|
| 297 | if (!refSchema)
|
---|
| 298 | throw new ref_error_1.default("", ref, `No definition ${ref}`);
|
---|
| 299 | if (!ref_1.hasRef(refSchema))
|
---|
| 300 | return parseCode({ ...cxt, schema: refSchema });
|
---|
| 301 | const { root } = schemaEnv;
|
---|
| 302 | const sch = compileParser.call(self, new __1.SchemaEnv({ schema: refSchema, root }), definitions);
|
---|
| 303 | partialParse(cxt, getParser(gen, sch), true);
|
---|
| 304 | }
|
---|
| 305 | function getParser(gen, sch) {
|
---|
| 306 | return sch.parse
|
---|
| 307 | ? gen.scopeValue("parse", { ref: sch.parse })
|
---|
| 308 | : codegen_1._ `${gen.scopeValue("wrapper", { ref: sch })}.parse`;
|
---|
| 309 | }
|
---|
| 310 | function parseEmpty(cxt) {
|
---|
| 311 | parseWith(cxt, parseJson_1.parseJson);
|
---|
| 312 | }
|
---|
| 313 | function parseWith(cxt, parseFunc, args) {
|
---|
| 314 | partialParse(cxt, util_1.useFunc(cxt.gen, parseFunc), args);
|
---|
| 315 | }
|
---|
| 316 | function partialParse(cxt, parseFunc, args) {
|
---|
| 317 | const { gen, data } = cxt;
|
---|
| 318 | gen.assign(data, codegen_1._ `${parseFunc}(${names_1.default.json}, ${names_1.default.jsonPos}${args ? codegen_1._ `, ${args}` : codegen_1.nil})`);
|
---|
| 319 | gen.assign(names_1.default.jsonPos, codegen_1._ `${parseFunc}.position`);
|
---|
| 320 | gen.if(codegen_1._ `${data} === undefined`, () => parsingError(cxt, codegen_1._ `${parseFunc}.message`));
|
---|
| 321 | }
|
---|
| 322 | function parseToken(cxt, tok) {
|
---|
| 323 | tryParseToken(cxt, tok, jsonSyntaxError);
|
---|
| 324 | }
|
---|
| 325 | function tryParseToken(cxt, tok, fail, success) {
|
---|
| 326 | const { gen } = cxt;
|
---|
| 327 | const n = tok.length;
|
---|
| 328 | skipWhitespace(cxt);
|
---|
| 329 | gen.if(codegen_1._ `${jsonSlice(n)} === ${tok}`, () => {
|
---|
| 330 | gen.add(names_1.default.jsonPos, n);
|
---|
| 331 | success === null || success === void 0 ? void 0 : success(cxt);
|
---|
| 332 | }, () => fail(cxt));
|
---|
| 333 | }
|
---|
| 334 | function skipWhitespace({ gen, char: c }) {
|
---|
| 335 | gen.code(codegen_1._ `while((${c}=${names_1.default.json}[${names_1.default.jsonPos}],${c}===" "||${c}==="\\n"||${c}==="\\r"||${c}==="\\t"))${names_1.default.jsonPos}++;`);
|
---|
| 336 | }
|
---|
| 337 | function jsonSlice(len) {
|
---|
| 338 | return len === 1
|
---|
| 339 | ? codegen_1._ `${names_1.default.json}[${names_1.default.jsonPos}]`
|
---|
| 340 | : codegen_1._ `${names_1.default.json}.slice(${names_1.default.jsonPos}, ${names_1.default.jsonPos}+${len})`;
|
---|
| 341 | }
|
---|
| 342 | function jsonSyntaxError(cxt) {
|
---|
| 343 | parsingError(cxt, codegen_1._ `"unexpected token " + ${names_1.default.json}[${names_1.default.jsonPos}]`);
|
---|
| 344 | }
|
---|
| 345 | function parsingError({ gen, parseName }, msg) {
|
---|
| 346 | gen.assign(codegen_1._ `${parseName}.message`, msg);
|
---|
| 347 | gen.assign(codegen_1._ `${parseName}.position`, names_1.default.jsonPos);
|
---|
| 348 | gen.return(undef);
|
---|
| 349 | }
|
---|
| 350 | //# sourceMappingURL=parse.js.map |
---|