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:
@@ -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",
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
package hbrtl
|
||||
|
||||
import (
|
||||
"five/hbrt"
|
||||
"five/hbrdd"
|
||||
"five/hbrt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RegisterRTL registers all standard library functions with the VM.
|
||||
@@ -87,6 +88,7 @@ func RegisterRTL(vm *hbrt.VM) {
|
||||
|
||||
// Eval
|
||||
hbrt.Sym("EVAL", hbrt.FsPublic, rtlEval),
|
||||
hbrt.Sym("DO", hbrt.FsPublic, rtlDo),
|
||||
|
||||
// String (new)
|
||||
hbrt.Sym("AT", hbrt.FsPublic, At),
|
||||
@@ -698,6 +700,67 @@ func rtlQQOut(t *hbrt.Thread) {
|
||||
t.RetNil()
|
||||
}
|
||||
|
||||
// rtlDo — Harbour DO(xTarget, [arg1, ...]) → xResult.
|
||||
//
|
||||
// Dispatches the named function, code block, or symbol dynamically.
|
||||
// String target: looked up in the VM symbol table (uppercased, like
|
||||
// compile-time calls) and invoked. Block target: evaluated with the
|
||||
// remaining args just like Eval. Anything else returns NIL.
|
||||
//
|
||||
// Reference: harbour-core/src/rtl/do.c HB_FUNC( DO ).
|
||||
func rtlDo(t *hbrt.Thread) {
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
defer t.EndProc()
|
||||
|
||||
if nParams < 1 {
|
||||
t.RetNil()
|
||||
return
|
||||
}
|
||||
|
||||
target := t.Local(1)
|
||||
nArgs := nParams - 1
|
||||
|
||||
switch {
|
||||
case target.IsString():
|
||||
name := strings.ToUpper(target.AsString())
|
||||
vm := t.VM()
|
||||
if vm == nil {
|
||||
t.RetNil()
|
||||
return
|
||||
}
|
||||
sym := vm.FindSymbol(name)
|
||||
if sym == nil {
|
||||
panic(&hbrt.HbError{
|
||||
Description: "DO: unknown function " + target.AsString(),
|
||||
Operation: "DO",
|
||||
SubSystem: "BASE",
|
||||
})
|
||||
}
|
||||
t.PushSymbol(sym)
|
||||
t.PushNil() // self placeholder
|
||||
for i := 2; i <= nParams; i++ {
|
||||
t.PushValue(t.Local(i))
|
||||
}
|
||||
t.Function(nArgs)
|
||||
// Function() leaves the callee's return value on the stack.
|
||||
t.RetValue()
|
||||
|
||||
case target.IsBlock():
|
||||
blk := target.AsBlock()
|
||||
for i := 2; i <= nParams; i++ {
|
||||
t.PushValue(t.Local(i))
|
||||
}
|
||||
t.PendingParams2(nArgs)
|
||||
blk.Fn(t)
|
||||
t.PushValue(t.GetRetValue())
|
||||
t.RetValue()
|
||||
|
||||
default:
|
||||
t.RetNil()
|
||||
}
|
||||
}
|
||||
|
||||
// rtlEval evaluates a code block.
|
||||
// Harbour: Eval(bBlock, [xArg1, ...]) → xResult
|
||||
func rtlEval(t *hbrt.Thread) {
|
||||
|
||||
Reference in New Issue
Block a user