From a490a70da1ca2b479065c6618207bf4789db6b4f Mon Sep 17 00:00:00 2001 From: Harttle Date: Sun, 19 Jan 2025 17:52:28 +0800 Subject: [PATCH] fix: consistent range syntax parsing, #791 --- src/parser/tokenizer.ts | 6 ++-- src/render/expression.spec.ts | 29 +++++++++++++++---- test/integration/{util => misc}/error.spec.ts | 0 3 files changed, 28 insertions(+), 7 deletions(-) rename test/integration/{util => misc}/error.spec.ts (100%) diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index 89c4c3e233..2f638258f7 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -391,9 +391,11 @@ export class Tokenizer { if (this.peek() !== '(') return ++this.p const lhs = this.readValueOrThrow() - this.p += 2 + this.skipBlank() + this.assert(this.read() === '.' && this.read() === '.', 'invalid range syntax') const rhs = this.readValueOrThrow() - ++this.p + this.skipBlank() + this.assert(this.read() === ')', 'invalid range syntax') return new RangeToken(this.input, begin, this.p, lhs, rhs, this.file) } diff --git a/src/render/expression.spec.ts b/src/render/expression.spec.ts index 8c73fdf070..5f4b0866be 100644 --- a/src/render/expression.spec.ts +++ b/src/render/expression.spec.ts @@ -24,11 +24,6 @@ describe('Expression', function () { expect(await toPromise(create('"foo"').evaluate(ctx, false))).toBe('foo') expect(await toPromise(create('false').evaluate(ctx, false))).toBe(false) }) - it('should eval range expression', async function () { - const ctx = new Context({ two: 2 }) - expect(await toPromise(create('(2..4)').evaluate(ctx, false))).toEqual([2, 3, 4]) - expect(await toPromise(create('(two..4)').evaluate(ctx, false))).toEqual([2, 3, 4]) - }) it('should eval literal', async function () { expect(await toPromise(create('2.4').evaluate(ctx, false))).toBe(2.4) expect(await toPromise(create('"foo"').evaluate(ctx, false))).toBe('foo') @@ -221,6 +216,30 @@ describe('Expression', function () { }) }) + describe('range', function () { + const ctx = new Context({ two: 2, num: { one: 1, two: 2 } }) + it('should eval range expression', async function () { + expect(await toPromise(create('(2..4)').evaluate(ctx, false))).toEqual([2, 3, 4]) + expect(await toPromise(create('(two..4)').evaluate(ctx, false))).toEqual([2, 3, 4]) + }) + it('should allow property access expression as variables', async function () { + expect(await toPromise(create('(num.one..num.two)').evaluate(ctx))).toEqual([1, 2]) + expect(await toPromise(create('(num.one .. two)').evaluate(ctx))).toEqual([1, 2]) + }) + it('should allow blanks in range', async function () { + expect(await toPromise(create('(3 ..5)').evaluate(ctx))).toEqual([3, 4, 5]) + expect(await toPromise(create('(3 .. 5)').evaluate(ctx))).toEqual([3, 4, 5]) + expect(await toPromise(create('( 3 .. 5 )').evaluate(ctx))).toEqual([3, 4, 5]) + }) + it('should throw if .. not matched', async function () { + expect(() => create('(3.5')).toThrow('invalid range syntax') + expect(() => create('(3 5')).toThrow('invalid range syntax') + }) + it('should throw if ( not patched', async function () { + expect(() => create('(3..5')).toThrow('invalid range syntax') + }) + }) + describe('sync', function () { it('should eval literal', function () { expect(toValueSync(create('2.4').evaluate(ctx, false))).toBe(2.4) diff --git a/test/integration/util/error.spec.ts b/test/integration/misc/error.spec.ts similarity index 100% rename from test/integration/util/error.spec.ts rename to test/integration/misc/error.spec.ts