feat(rtl): DO(xTarget, [args...]) — dynamic dispatch

Harbour's DO() accepts a string (looked up as a function name), a
code block (evaluated with args), or a symbol, and invokes it. Used
for plugin systems and dynamic dispatch idioms like
`DO(cHandler, oRequest)`.

Five already had stmtDo rewrite `DO(...)` at statement-level to a
function-call expression, so callers in expression position just
work — but gengo refused to emit DO as a function call because it
was on the reserved-word guard list (which existed to catch stray
ENDIF/ENDDO from bad IF nesting). Remove DO from that list; the
statement form is still handled upstream by parseDoProc, so the
guard loses nothing.

rtlDo implements the dispatch:
  - String target → VM.FindSymbol + t.Function
  - Block target → EvalBlock path (same as Eval)
  - Anything else → NIL

Tested (/tmp/test_do.prg):
  DO("Greet", "World") → "hello, World"
  DO({|x,y| x*y+1}, 5, 6) → 31
  DO(NIL) → NIL (ValType "U")

FiveSql2 43/43, Harbour compat 56/56, Go test ALL PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 16:33:09 +09:00
parent 327f75bb45
commit 2a662525b3
2 changed files with 65 additions and 2 deletions

View File

@@ -2736,7 +2736,7 @@ func (g *Generator) emitAssignExpr(e *ast.AssignExpr) {
func isReservedWord(name string) bool {
switch name {
case "IF", "ELSE", "ELSEIF", "ENDIF",
"DO", "WHILE", "ENDDO",
"WHILE", "ENDDO",
"FOR", "NEXT", "TO", "STEP",
"RETURN", "FUNCTION", "PROCEDURE",
"LOCAL", "STATIC", "PRIVATE", "PUBLIC",