diff --git a/src/parser/scopeTracker.ts b/src/parser/scopeTracker.ts index 7c292ac..3ac9921 100644 --- a/src/parser/scopeTracker.ts +++ b/src/parser/scopeTracker.ts @@ -42,15 +42,12 @@ export class Scope { let pendingIdentifiers: string[] = [] let isInParams = false -// Term ID for 'fn' keyword - verified by parsing and inspecting the tree -const FN_KEYWORD = 33 - export const trackScope = new ContextTracker({ start: new Scope(null, new Set()), shift(context, term, stack, input) { // Track fn keyword to enter param capture mode - if (term === FN_KEYWORD) { + if (term === terms.Fn) { isInParams = true pendingIdentifiers = [] return context @@ -58,7 +55,17 @@ export const trackScope = new ContextTracker({ // Capture identifiers if (term === terms.Identifier) { - const text = input.read(input.pos, stack.pos) + // Build text by peeking backwards from stack.pos to input.pos + let text = '' + const start = input.pos + const end = stack.pos + for (let i = start; i < end; i++) { + const offset = i - input.pos + const ch = input.peek(offset) + if (ch === -1) break + text += String.fromCharCode(ch) + } + // Capture ALL identifiers when in params if (isInParams) { @@ -76,7 +83,7 @@ export const trackScope = new ContextTracker({ reduce(context, term, stack, input) { // Add assignment variable to scope if (term === terms.Assign && pendingIdentifiers.length > 0) { - const newContext = context.add(pendingIdentifiers[0]) + const newContext = context.add(pendingIdentifiers[0]!) pendingIdentifiers = [] return newContext } @@ -100,7 +107,7 @@ export const trackScope = new ContextTracker({ } // Clear stale identifiers after non-assignment statements - if (term === terms.DotGet || term === terms.FunctionCallOrIdentifier) { + if (term === terms.DotGet || term === terms.FunctionCallOrIdentifier || term === terms.FunctionCall) { pendingIdentifiers = [] } diff --git a/src/parser/tests/dot-get.test.ts b/src/parser/tests/dot-get.test.ts index 3442186..3cb7fd6 100644 --- a/src/parser/tests/dot-get.test.ts +++ b/src/parser/tests/dot-get.test.ts @@ -6,6 +6,14 @@ describe('DotGet', () => { expect('readme.txt').toMatchTree(`Word readme.txt`) }) + test('readme.txt is Word when used in function', () => { + expect('echo readme.txt').toMatchTree(` + FunctionCall + Identifier echo + PositionalArg + Word readme.txt`) + }) + test('obj.prop is DotGet when obj is assigned', () => { expect('obj = 5; obj.prop').toMatchTree(` Assign