Summary
obj\method(args) dynamic dispatch only parses as a statement. The same call as an expression (e.g. on the right-hand side of an assignment, in a comparison, as a function-call argument) errors with Expecting end-of-file Got: ( or Expecting 'End Method' Got: (.
The static form TypeName::method(obj, args) works in both positions, so currently every method that returns a value must be called via the static form even when an instance reference is available.
Minimal repro
Strict
Type Foo
Field val%
Method get%()
Return self\val
End Method
End Type
Local f.Foo = New Foo()
f\val = 42
// Statement-level dynamic dispatch -- works:
f\get() ' OK (return value discarded)
// Expression-level dynamic dispatch -- fails:
Local x% = f\get()
$ blitzcc dynexp.bb
Parsing...
dynexp.bb:17:17:17:17:Expecting end-of-file Got: (
Foo::get(f) in the same position compiles and works.
Asymmetry with the static form
| Form |
As statement |
As expression |
Foo::get(f) (static) |
✅ |
✅ |
f\get() (dynamic) |
✅ |
❌ |
MethodTest.bb demonstrates f\testMethod("buzz") only at statement level — the test suite doesn't exercise dynamic dispatch in expression position, which is presumably how the gap shipped without notice.
Why this matters
Consumers of method-with-return-value (the common case for getters, builders, value-formatters, hit-tests, row-chaining UI methods) currently can't use dynamic dispatch even when they have an instance in hand. They're forced into the static form even when the type name is otherwise redundant. Symptoms in real code:
// What I want to write:
Local entityName$ = self\threads\lookupName(kind, refID)
Local hit% = self\threads\renderChip(x, y, w, h, kind, refID, mx, my, clicked)
// What I have to write today:
Local entityName$ = Threads::lookupName(self\threads, kind, refID)
Local hit% = Threads::renderChip(self\threads, x, y, w, h, kind, refID, mx, my, clicked)
In the Loom alpha refactor, the second form propagates through every value-returning call site (row layout helpers, value formatters, lookup methods). Removing this gap would unlock consistent obj\method(args) style across an entire OO module.
Expected behaviour
obj\method(args) should parse anywhere a function/method call is valid — including expression positions. The static form already does; the dynamic form should too.
Where the gap likely lives
Method-call dispatch in src/blitzrc/compiler/parser.cpp around line 228 handles the backslash dispatch:
if (strictMode && toker->curr()=='\') {
isField = true;
}
if( toker->lookAhead(2)=='(' && isField) {
// ... method call ...
}
This branch is inside the statement-parsing path. The expression parser (exprnode.cpp / parser.cpp's parseExpr and friends) presumably needs the same dispatch added for the same shape — recognize ident\methodName(...) and emit a CallNode with the static-call name and the leading object as the first argument.
Repro environment
Summary
obj\method(args)dynamic dispatch only parses as a statement. The same call as an expression (e.g. on the right-hand side of an assignment, in a comparison, as a function-call argument) errors withExpecting end-of-file Got: (orExpecting 'End Method' Got: (.The static form
TypeName::method(obj, args)works in both positions, so currently every method that returns a value must be called via the static form even when an instance reference is available.Minimal repro
Foo::get(f)in the same position compiles and works.Asymmetry with the static form
Foo::get(f)(static)f\get()(dynamic)MethodTest.bbdemonstratesf\testMethod("buzz")only at statement level — the test suite doesn't exercise dynamic dispatch in expression position, which is presumably how the gap shipped without notice.Why this matters
Consumers of method-with-return-value (the common case for getters, builders, value-formatters, hit-tests, row-chaining UI methods) currently can't use dynamic dispatch even when they have an instance in hand. They're forced into the static form even when the type name is otherwise redundant. Symptoms in real code:
In the Loom alpha refactor, the second form propagates through every value-returning call site (row layout helpers, value formatters, lookup methods). Removing this gap would unlock consistent
obj\method(args)style across an entire OO module.Expected behaviour
obj\method(args)should parse anywhere a function/method call is valid — including expression positions. The static form already does; the dynamic form should too.Where the gap likely lives
Method-call dispatch in
src/blitzrc/compiler/parser.cpparound line 228 handles the backslash dispatch:This branch is inside the statement-parsing path. The expression parser (
exprnode.cpp/parser.cpp'sparseExprand friends) presumably needs the same dispatch added for the same shape — recognizeident\methodName(...)and emit aCallNodewith the static-call name and the leading object as the first argument.Repro environment