From f31be80bb0d83d9f36b52620517480f621210da6 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Wed, 29 Oct 2025 21:37:42 -0700 Subject: [PATCH] fix dotget --- src/parser/scopeTracker.ts | 33 +++++++++++++++++++++++++++++++-- src/parser/tests/basics.test.ts | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/parser/scopeTracker.ts b/src/parser/scopeTracker.ts index af2a32c..7ce09e0 100644 --- a/src/parser/scopeTracker.ts +++ b/src/parser/scopeTracker.ts @@ -2,7 +2,7 @@ import { ContextTracker, InputStream } from '@lezer/lr' import * as terms from './shrimp.terms' export class Scope { - constructor(public parent: Scope | null, public vars = new Set()) {} + constructor(public parent: Scope | null, public vars = new Set()) { } has(name: string): boolean { return this.vars.has(name) || (this.parent?.has(name) ?? false) @@ -42,7 +42,7 @@ export class Scope { // Tracker context that combines Scope with temporary pending identifiers class TrackerContext { - constructor(public scope: Scope, public pendingIds: string[] = []) {} + constructor(public scope: Scope, public pendingIds: string[] = []) { } } // Extract identifier text from input stream @@ -75,6 +75,12 @@ export const trackScope = new ContextTracker({ return new TrackerContext(context.scope, [...context.pendingIds, text]) } + // Track identifiers in array destructuring: [ a b ] = ... + if (!inParams && term === terms.Identifier && isArrayDestructuring(input)) { + const text = readIdentifierText(input, input.pos, stack.pos) + return new TrackerContext(Scope.add(context.scope, text), context.pendingIds) + } + return context }, @@ -98,3 +104,26 @@ export const trackScope = new ContextTracker({ hash: (context) => context.scope.hash(), }) + +// Check if we're parsing array destructuring: [ a b ] = ... +const isArrayDestructuring = (input: InputStream): boolean => { + let pos = 0 + + // Find closing bracket + while (pos < 200 && input.peek(pos) !== 93 /* ] */) { + if (input.peek(pos) === -1) return false // EOF + pos++ + } + + if (input.peek(pos) !== 93 /* ] */) return false + pos++ + + // Skip whitespace + while (input.peek(pos) === 32 /* space */ || + input.peek(pos) === 9 /* tab */ || + input.peek(pos) === 10 /* \n */) { + pos++ + } + + return input.peek(pos) === 61 /* = */ +} \ No newline at end of file diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index b9584ad..a9fac46 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -630,6 +630,24 @@ describe('Array destructuring', () => { Number 1 Number 2`) }) + + + test('works with dotget', () => { + expect('[ a ] = [ [1 2 3] ]; a.1').toMatchTree(` + Assign + Array + Identifier a + Eq = + Array + Array + Number 1 + Number 2 + Number 3 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot a + Number 1`) + }) }) describe('Conditional ops', () => {