Skip to content

Commit e4bfa34

Browse files
committed
feat(parser): new parser
1 parent 93ecef9 commit e4bfa34

15 files changed

+625
-148
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@ coverage/
66
*.log
77
cjs/
88
.gen/
9-
src/parser/parser.mjs

.nycrc

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"src/**/*.mjs"
66
],
77
"exclude": [
8-
"**/__*__/**",
9-
"src/parser/parser.mjs"
8+
"**/__*__/**"
109
],
1110
"reporter": [
1211
"lcovonly",

eslint.config.js

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import globals from 'globals';
88
export default [
99
{
1010
files: ['**/*.mjs', '**/*.js'],
11-
ignores: ['src/parser/parser.mjs'],
1211
languageOptions: {
1312
ecmaVersion: 2023,
1413
parser: babelParser,

package-lock.json

-35
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nimma",
3-
"version": "0.4.2",
3+
"version": "0.5.0",
44
"description": "Scalable JSONPath engine.",
55
"keywords": [
66
"json",
@@ -28,8 +28,8 @@
2828
"require": "./cjs/index.cjs"
2929
},
3030
"./parser": {
31-
"import": "./src/parser/parser.mjs",
32-
"require": "./cjs/parser/parser.cjs"
31+
"import": "./src/parser/index.mjs",
32+
"require": "./cjs/parser/index.cjs"
3333
},
3434
"./parser/jsep": {
3535
"import": "./src/parser/jsep.mjs",
@@ -53,7 +53,6 @@
5353
"url": "https://github.com/P0lip/nimma"
5454
},
5555
"scripts": {
56-
"prebuild": "peggy --format es -o src/parser/parser.mjs src/parser/parser.peg",
5756
"build": "rollup -c",
5857
"lint": "ls-lint && eslint --cache --cache-location .cache/ src && prettier --log-level error --ignore-path .gitignore --check --cache --cache-location .cache/.prettier src",
5958
"test": "c8 mocha --config .mocharc ./src/**/__tests__/**/*.test.mjs && karma start karma.conf.cjs --log-level=error",
@@ -86,7 +85,6 @@
8685
"lodash-es": "^4.17.21",
8786
"mocha": "^10.2.0",
8887
"mocha-each": "^2.0.1",
89-
"peggy": "^3.0.2",
9088
"prettier": "^3.2.5",
9189
"rollup": "^4.9.6"
9290
},

src/codegen/baseline/__tests__/parse-filter-expression.test.mjs

+1-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ describe('parseFilterExpression', () => {
128128

129129
it('throws upon unknown shorthand', () => {
130130
expect(print.bind(null, `?(@foo())`)).to.throw(
131-
SyntaxError,
132-
`Unsupported shorthand '@foo'`,
131+
`Unsupported shorthand "@foo"`,
133132
);
134133
});
135134
});

src/codegen/baseline/generators.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ function processAtIdentifier(tree, name) {
431431
);
432432
}
433433

434-
throw new SyntaxError(`Unsupported shorthand '${name}'`);
434+
throw Error(`Unsupported shorthand "${name}"`);
435435
}
436436
}
437437

src/core/__tests__/index.test.mjs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* eslint-disable no-undef */
22
import { expect } from 'chai';
33

4-
import { ParserError } from '../../runtime/errors/index.mjs';
54
import Nimma from '../index.mjs';
65

76
describe('Core', () => {
@@ -13,10 +12,9 @@ describe('Core', () => {
1312
try {
1413
fn();
1514
} catch (e) {
16-
expect(e.errors[0]).to.be.instanceof(ParserError);
17-
expect(e.errors[0].cause.name).to.eq('SyntaxError');
15+
expect(e.errors[0]).to.be.instanceof(SyntaxError);
1816
expect(e.errors[0].message).to.eq(
19-
'Expected "^", "~", or end of input but "." found.',
17+
'Expected "^", "~", or end of input but "." found at 4',
2018
);
2119
}
2220
});

src/parser/__tests__/parser.test.mjs

+106-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import forEach from 'mocha-each';
33

4-
import { parse } from '../parser.mjs';
4+
import parse from '../index.mjs';
55

66
describe('Parser', () => {
77
it('goessner samples', () => {
@@ -200,6 +200,37 @@ describe('Parser', () => {
200200
});
201201

202202
it('filter expressions', () => {
203+
expect(parse('$[(@.length-1)]')).to.deep.equal([
204+
{
205+
type: 'SliceExpression',
206+
value: [-1, Infinity, 1],
207+
deep: false,
208+
},
209+
]);
210+
expect(parse('$[( @.length - 2 )]')).to.deep.equal([
211+
{
212+
type: 'SliceExpression',
213+
value: [-2, Infinity, 1],
214+
deep: false,
215+
},
216+
]);
217+
expect(parse('$[( @[ "length" ] - 10 )]')).to.deep.equal([
218+
{
219+
type: 'SliceExpression',
220+
value: [-10, Infinity, 1],
221+
deep: false,
222+
},
223+
]);
224+
expect(parse('$[( @["length"] - 5 )]')).to.deep.equal([
225+
{
226+
type: 'SliceExpression',
227+
value: [-5, Infinity, 1],
228+
deep: false,
229+
},
230+
]);
231+
});
232+
233+
it('script filter expressions', () => {
203234
expect(parse('$[?(@property === "@.schema")]')).to.deep.equal([
204235
{
205236
type: 'ScriptFilterExpression',
@@ -346,6 +377,36 @@ describe('Parser', () => {
346377
},
347378
);
348379

380+
it('skips whitespaces', () => {
381+
expect(parse('$.[ name ] [?( @.abc )]\t ..@@test( )')).to.deep.equal([
382+
{
383+
type: 'MemberExpression',
384+
value: 'name',
385+
deep: true,
386+
},
387+
{
388+
type: 'ScriptFilterExpression',
389+
value: ' @.abc ',
390+
deep: false,
391+
},
392+
{
393+
type: 'ScriptFilterExpression',
394+
value: '@@test( )',
395+
deep: true,
396+
},
397+
]);
398+
});
399+
400+
it.skip('handles escapable', () => {
401+
expect(parse(`$["'name\\"'","test\\\\",'"a']`)).to.deep.equal([
402+
{
403+
type: 'MultipleMemberExpression',
404+
value: ['name"', 'test\\'],
405+
deep: false,
406+
},
407+
]);
408+
});
409+
349410
describe('invalid expressions', () => {
350411
it('empty expression or does not start with $', () => {
351412
expect(() => parse('')).to.throw('Expected "$" but end of input found.');
@@ -355,56 +416,85 @@ describe('Parser', () => {
355416

356417
it('invalid member expression', () => {
357418
expect(() => parse('$info')).to.throw(
358-
'Expected ".", "..", "^", "~", or end of input but "i" found.',
419+
'Expected ".", "..", "^", "~", or end of input but "i" found at 1.',
359420
);
360421
expect(() => parse('$.')).to.throw(
361-
'Expected "*", "@", "[", [$_\\-], [0-9], or [A-Za-z] but end of input found.',
422+
'Expected valid name but end of input found at 2.',
362423
);
363424
});
364425

365426
it('key expression used in the wrong place', () => {
366427
expect(() => parse('$.name~.a')).to.throw(
367-
'Expected "^", "~", or end of input but "." found.',
428+
'Expected "^", "~", or end of input but "." found at 7.',
368429
);
369430
});
370431

371432
it('unclosed quotes', () => {
372433
expect(() => parse('$.name["a]')).to.throw(
373-
`Expected "\\"" or [^"] but end of input found.`,
434+
`Expected """ but end of input found at 10.`,
374435
);
375436
expect(() => parse('$.name["\']')).to.throw(
376-
`Expected "\\"" or [^"] but end of input found.`,
437+
`Expected """ but end of input found at 10.`,
377438
);
378439
});
379440

380441
it('invalid step in slice expressions', () => {
381442
expect(() => parse('$.name[::test]')).to.throw(
382-
'Expected "-" or [0-9] but "t" found.',
443+
'Expected "-" or [0-9] but "t" found at 9.',
444+
);
445+
expect(() => parse('$.name[::-]')).to.throw(
446+
'Expected [0-9] but "]" found at 10.',
383447
);
384448
});
385449

386450
it('invalid shorthands', () => {
387-
expect(() => parse('$..@@()')).to.throw('Expected [a-z] but "(" found.');
451+
expect(() => parse('$..@@()')).to.throw(
452+
'Expected [a-z] but "(" found at 5.',
453+
);
388454
expect(() => parse('$..@@test)')).to.throw(
389-
'Expected "()" or [a-z] but ")" found.',
455+
'Expected "(" but ")" found at 9.',
390456
);
391457
expect(() => parse('$..@@test(')).to.throw(
392-
'Expected "()" or [a-z] but "(" found.',
393-
);
394-
expect(() => parse('$..@@test)')).to.throw(
395-
'Expected "()" or [a-z] but ")" found.',
458+
'Expected ")" but end of input found at 10.',
396459
);
397460
expect(() => parse('$..@')).to.throw(
398-
'Expected "@" or [a-z] but end of input found.',
461+
'Expected [a-z] but end of input found at 4.',
462+
);
463+
});
464+
465+
it('invalid filter expressions', () => {
466+
expect(() => parse('$[(')).to.throw(
467+
'Expected "@" but end of input found at 3.',
468+
);
469+
expect(() => parse('$[(@')).to.throw(
470+
'Expected "." or "[" but end of input found at 4.',
471+
);
472+
expect(() => parse('$[(@.len - 1)]')).to.throw(
473+
'Expected "length" but "len - " found at 11.',
474+
);
475+
expect(() => parse('$[(@length - 1)]')).to.throw(
476+
'Expected "." or "[" but "l" found at 4.',
477+
);
478+
expect(() => parse('$[(@[length]-2)]')).to.throw(
479+
`Expected """ or "'" at 5.`,
480+
);
481+
expect(() => parse('$[(@.length + 1))')).to.throw(
482+
'Expected "-" but "+" found at 12.',
483+
);
484+
expect(() => parse('$[(@.length - -5))')).to.throw(
485+
'Expected positive number but "-5" found at 14.',
486+
);
487+
expect(() => parse('$[(@.length - 0))')).to.throw(
488+
'Expected positive number but "0" found at 14.',
399489
);
400490
});
401491

402492
it('unclosed brackets', () => {
403493
expect(() => parse('$.name[0')).to.throw(
404-
'Expected "\'", ",", ":", "\\"", "]", [$_\\-], [0-9], or [A-Za-z] but end of input found.',
494+
'Expected "]" but end of input found at 8.',
405495
);
406496
expect(() => parse('$.store["[name]"')).to.throw(
407-
'Expected "\'", ",", "\\"", "]", [$_\\-], [0-9], or [A-Za-z] but end of input found.',
497+
'Expected "]" but end of input found at 16.',
408498
);
409499
});
410500
});

src/parser/index.mjs

+1-12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1 @@
1-
import { ParserError } from '../runtime/errors/index.mjs';
2-
import * as parser from './parser.mjs';
3-
4-
const { parse } = parser;
5-
6-
export default function (input) {
7-
try {
8-
return parse(input);
9-
} catch (e) {
10-
throw new ParserError(e.message, input, { cause: e });
11-
}
12-
}
1+
export { parser as default } from './parser.mjs';

0 commit comments

Comments
 (0)