Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions agent-notes/moo-expression-assignments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# MOO expression assignments

## Goal
Fix the false undefined-local warning for `if (valid(channel = this:current_channel(player)))` and push a client PR. Q authorized implementation with "MAKE IT SO!". ToastStunt at `~/src/toaststunt` is the syntax authority.

## State
Branch `fix/moo-expression-assignments`, isolated from the user's dirty primary checkout, based on origin/master at 000c4b15. Scanner fix committed as d67759c5 and pushed. PR: https://github.com/MongooseMoo/react-client/pull/204. Scan the entire masked source with an identifier boundary and exclude property names. The normal commit hook completed typecheck and staged lint. Hosted validation is tracked on the PR's current head.

## Decisions and why
Smallest change: approximately 20 lines in the existing assignment-definition collector. Scan masked source for identifier assignment targets regardless of expression punctuation or line breaks; exclude property names using the existing previous-character helper. No new parser or semantic architecture.

ToastStunt `src/parser.y:139` accepts expressions in if conditions; `:466-486` constructs assignment expressions. The installed tree-sitter WASM parses the reported example into an assignment_expression with hasError false. Client `src/editor/moocode/semantics.ts:919` instead requires a preceding line start, whitespace or semicolon, missing the target immediately after `(`. Correct that existing rule first.

No npm package change is justified by this example. Other reported issues have not yet been supplied. Do not publish an unchanged parser package.

## Surprises
The warning is produced by the client's text-based semantic model, not tree-sitter. The current checkout is older than origin/master and contains unrelated untracked work.

## Next step
Read PR #204's current hosted CI result and report it. No new guards or test files added, per Q's standing instruction. The requested client fix is pushed and the PR is open; no merge or release is authorized as part of this client PR request. Other parser issues await examples from Q.
19 changes: 12 additions & 7 deletions src/editor/moocode/semantics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -896,9 +896,9 @@ function collectDefinitions(
const lines = masked.split(/\r\n|\r|\n/);
const lineOffsets = getLineOffsets(source);

collectAssignmentDefinitions(source, masked, definitions);
lines.forEach((line, lineIndex) => {
const lineOffset = lineOffsets[lineIndex] ?? 0;
collectAssignmentDefinitions(source, line, lineOffset, definitions);
collectForDefinitions(source, line, lineOffset, definitions);
collectForkDefinitions(source, line, lineOffset, definitions);
collectExceptDefinitions(source, line, lineOffset, definitions);
Expand All @@ -911,21 +911,26 @@ function collectDefinitions(

function collectAssignmentDefinitions(
source: string,
line: string,
lineOffset: number,
masked: string,
definitions: Array<{ name: string; occurrence: Occurrence }>,
): void {
const assignmentPattern = new RegExp(
`(^|[;\\s])(${MOO_IDENTIFIER_PATTERN_SOURCE})\\s*=(?!=|>)`,
`\\b(${MOO_IDENTIFIER_PATTERN_SOURCE})\\s*=(?!=|>)`,
'g',
);
for (const match of line.matchAll(assignmentPattern)) {
const name = match[2];
for (const match of masked.matchAll(assignmentPattern)) {
const name = match[1];
if (!name) {
continue;
}

const nameStart = lineOffset + match.index + match[0].indexOf(name);
// Assignment is an expression (ToastStunt parser.y), including in conditions
// and arguments. Property names are not local assignment targets.
const nameStart = match.index;
const previous = previousNonWhitespaceCharacter(masked, nameStart);
if (previous === '.' || previous === ':' || previous === '$') {
continue;
}
definitions.push({
name,
occurrence: occurrenceAt(source, nameStart, name.length),
Expand Down
Loading