diff --git a/lib/compile/jtd/parse.ts b/lib/compile/jtd/parse.ts index d42c8ea1d6..dee24033a3 100644 --- a/lib/compile/jtd/parse.ts +++ b/lib/compile/jtd/parse.ts @@ -8,7 +8,7 @@ import N from "../names" import {isOwnProperty, hasPropFunc} from "../../vocabularies/code" import {hasRef} from "../../vocabularies/jtd/ref" import {intRange, IntType} from "../../vocabularies/jtd/type" -import {parseJson, parseJsonNumber, parseJsonString} from "../../runtime/parseJson" +import {parseJson, parseJsonNumber, parseJsonString, skipWhitespace} from "../../runtime/parseJson" import {func} from "../util" import validTimestamp from "../timestamp" @@ -84,6 +84,7 @@ function parserFunction(cxt: ParseCxt, parseName: Name): void { gen.assign(N.jsonPos, _`${N.jsonPos} || 0`) gen.const(N.jsonLen, _`${N.json}.length`) parseCode(cxt) + whitespace(gen) gen.if(N.jsonPart, () => gen.return(_`[${N.data}, ${N.jsonPos}]`)) gen.if(_`${N.jsonPos} === ${N.jsonLen}`, () => gen.return(N.data)) jsonSyntaxError(cxt) @@ -298,6 +299,7 @@ function parseEnum(cxt: ParseCxt): void { function parseNumber(cxt: ParseCxt, maxDigits?: number): void { const {gen} = cxt + gen.assign(N.jsonPos, _`${func(gen, skipWhitespace)}(${N.json}, ${N.jsonPos})`) gen.if( _`"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), @@ -360,6 +362,7 @@ function parseToken(cxt: ParseCxt, tok: string): void { function tryParseToken(cxt: ParseCxt, tok: string, fail: GenParse, success?: GenParse): void { const {gen} = cxt const n = tok.length + whitespace(gen) gen.if( _`${jsonSlice(n)} === ${tok}`, () => { @@ -370,6 +373,10 @@ function tryParseToken(cxt: ParseCxt, tok: string, fail: GenParse, success?: Gen ) } +function whitespace(gen: CodeGen): void { + gen.assign(N.jsonPos, _`${func(gen, skipWhitespace)}(${N.json}, ${N.jsonPos})`) +} + function jsonSlice(len: number | Name): Code { return len === 1 ? _`${N.json}[${N.jsonPos}]` diff --git a/lib/runtime/parseJson.ts b/lib/runtime/parseJson.ts index 0623a322af..3ef8d28f26 100644 --- a/lib/runtime/parseJson.ts +++ b/lib/runtime/parseJson.ts @@ -122,6 +122,14 @@ export function parseJsonString(s: string, pos: number): [string, number] { parseJsonString.code = _`require("ajv/dist/runtime/parseJson").parseJsonString` +export function skipWhitespace(s: string, pos: number): number { + let c: string + while (((c = s[pos]), c === " " || c === "\n" || c === "\r" || c === "\t")) pos++ + return pos +} + +skipWhitespace.code = _`require("ajv/dist/runtime/parseJson").skipWhitespace` + function unexpectedEnd(): never { throw new SyntaxError("Unexpected end of JSON input") } diff --git a/spec/jtd-schema.spec.ts b/spec/jtd-schema.spec.ts index 208c760aa4..0bea0b140a 100644 --- a/spec/jtd-schema.spec.ts +++ b/spec/jtd-schema.spec.ts @@ -111,7 +111,7 @@ describe("JSON Type Definition", () => { } }) - describe("parse", () => { + describe.only("parse", () => { const ajv = new _AjvJTD() for (const testName in jtdValidationTests) { @@ -123,12 +123,14 @@ describe("JSON Type Definition", () => { const parse = ajv.compileParser(schema) // console.log(schema, instance, `"${JSON.stringify(instance)}"`, parse.toString()) assert.deepStrictEqual(parse(JSON.stringify(instance)), instance) + assert.deepStrictEqual(parse(` ${JSON.stringify(instance, null, 2)} `), instance) }) } else { it(`should throw exception on invalid JSON string`, () => { const parse = ajv.compileParser(schema) // console.log(parse.toString()) assert.throws(() => parse(JSON.stringify(instance))) + assert.throws(() => parse(` ${JSON.stringify(instance, null, 2)} `)) }) } })