| `#define EMPTY ""` followed by newline and `main(){return EMPTY;}` | The define boundary search sees empty literal text as EOF and inserts `;` before the value; expression parsing fails. | Accept; constant is `Value::String("")`. Also accept the directive at EOF without a final newline. |
| `main(){a[""] = 1;}` | Assignment lookahead sees empty literal text inside brackets and returns false; expression parsing leaves `=` where `;` is expected. | Accept; emit `EnsureLocalArray(a), Load(a), C(""), C(1), StoreIndex`. |
| `main(){a[b[""]] = 1;}` | The same false EOF occurs at bracket depth two. | Accept; nested index scanning reaches both actual closing brackets and finds `=`. |
| `main(){return a["["];}` | Raw lookahead classifies the string `"["` as the second bracket of an indirect call; `expect("[")` rejects it. | Accept; expression fragment `Load(a), C("["), LoadIndex`. |
| `main(){return 1 "+" 2;}` | String text is treated as `Binary::Add`; invalid source is accepted. | Reject at the string token when `;` is expected. |
| `main(){return 1 "&&" 2;}` | String text enters short-circuit generation; invalid source is accepted. | Reject without emitting a short-circuit branch for that literal. |
| `main(){for(i "++";;)break;}` | `assignment` consumes raw string text as increment; the whole function is accepted. | Reject with `expected assignment operator` before consuming the literal. |
| `main(){for(i ^ 1;;)break;}` | A plain binary `^` is accepted as compound assignment through the fallback mapping. | Reject; the existing assignment vocabulary contains `^=`, not `^`. |
| `main(){for(i` with EOF immediately after `i` | `ident` reaches EOF; assignment consumes the empty token as an operator, then `emit(Load)` calls `location` with `pos == tokens.len()`: an out-of-bounds panic. | Return `expected assignment operator` at EOF without advancing past it. |
| `main(){switch(0){case -"1":break;}}` | The token before consuming minus is non-string; the following string payload is fed to `number("-1")`, emitting an integer case label. | Reject the signed string; preserve ordinary string labels and signed integer labels. |
| `main(){prof_begin "(" "label");}` | Raw lookahead plus `pos += 2` skips the string as if it were `(`; invalid source is accepted and erased. | Reject; profile-statement recognition requires actual punctuation. |
Positive companion witnesses, also traced manually: `main(){return "";}`,
`main(){return "+";}`, `main(){return "[";}`,
`main(){a["]"] = 1;}`, `main(){a["["] += 1;}`,
`main(){for(i=0;i<1;i++)break;}`,
`main(){switch(0){case -1:break;case "1":break;}}`, and
`main(){prof_begin("label");prof_end("label");}` retain their existing parsing.
6:C(undefined), 7:Return`. Both paths supply one return value. For `1 && 2`,
the expression fragment has `JumpFalse(8)` at pc 3 and `Jump(9)` at pc 7;
pc 8 supplies zero, and pc 9 is the consuming instruction. Both paths leave
one normalized boolean. These are hand-computed indices, not disassembly.
During developer statement elimination, the existing linker builds `keep` and
a prefix-count `remap` of length `old_len + 1`. Surviving jumps are mapped to
the count of surviving instructions before their old targets, including an
old end target. Example `main(){if(1)println("x");return 2;}` resolves the
catalog's developer `println` statement to elision: old pc 1's `JumpFalse(5)`
becomes `JumpFalse(2)` after removing old pc 2–4 (argument, call, pop). The
branch lands at `C(2)`. No jump representation or elision logic is modified.
[`arguments` and `invocation`][calls-base] count each parsed expression once.
For `f(a,b){return a;} main(){return f(1,2);}`, argument order is
`C(1), C(2), Call(f,2,false)`. Receiver calls put the receiver below arguments;
indirect calls put the function reference between receiver and arguments.
Special event calls emit receiver, event name, then payload; their count
excludes the first two operands.
## 6. Consistency with the existing instruction contract
The read-only comparison with [`runtime::instruction` and
`stack_effect`](../crates/sim/src/script/runtime/mod.rs) confirms the relevant
orders: binary operators pop RHS then LHS; `LoadIndex` consumes receiver/key;
`StoreIndex` consumes receiver/key/value; `DupPair` supplies the two additional
operands needed for compound index assignment. Calls consume `argc` values
plus an optional receiver; indirect calls also consume the function reference.
Notify/AwaitMatch consume payload plus receiver/name. `JumpFalse` consumes the
condition, and each explicit/implicit return generated here has a value.
The patch changes recognition of source tokens, not the IR schema or opcodes.
## Unconfirmed questions and deliberate limits
These observations are separate from the proven token-kind failures and are
not repaired in this PR. They require a compatibility decision or further evidence.
| Question and minimal witness | Manual observation | Why no defect claim/fix |
| --- | --- | --- |
| `main(anim){return anim;}` | Parameter setup reserves a local slot, but `load("anim")` emits `Global::Anim`; the parameter checks explicitly reserve only self/level/game. | The project's intended policy for this additional global name is not documented; possible reserved-name diagnostic gap. |
| `X=1; main(){foreach(X in []){}return X;}` | Foreach writes a local slot named x, while expression reads resolve the module constant first. Ordinary assignment would reject x. | Possible missing constant-name validation; whether foreach should reject or shadow needs an explicit language rule. |
| `/ # main(){return 1;} # / main(){return 2;}` | Developer exclusion matches adjacent tokens after whitespace/comments have been discarded, so it excludes the first function despite spaced delimiter characters. | It is unclear whether adjacency of characters, rather than tokens, is a promised rule of this implementation. |
| `/# main(){return "unterminated; } #/ main(){}` | String lexing faults before the developer token-removal pass can discard the disabled region. | Disabled-region lexical validity is unspecified here. |
| `main(){return "\q";}` | The escape fallback removes the backslash and keeps q. | Permissive unknown escapes may be intentional; no compatible version was consulted to assert otherwise. |
No claim of a complete grammar audit or full IW4 conformance follows from this
review. Integer wrap, function scope, duplicate parameter handling, native
thread restrictions, and the limited literal grammar are existing policies,
not targets for adding language features.
## Static verification and remaining limits
- The lexer still appends exactly one non-string EOF token for successful
production lexing. `Token::is` distinguishes it from an empty literal.
- Assignment lookahead uses `get`; bracket depth changes only on non-string
delimiters. Empty/bracket-containing strings advance the scan without
changing depth. Real EOF exits; exhausting lookahead returns false.
- Assignment consumes an operator only after matching the same existing
whitelist used by lookahead. EOF, quoted operators, and bare binary operators
return a fault first. The later `op[..1]` slice is consequently a nonempty
ASCII operator, and its XOR fallback is reachable only for `^=`.
- Binary parsing exits on a string without advancing, allowing its caller to
diagnose the unexpected token; valid unquoted operators retain their old
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.