diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 4d43076..be36dfe 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -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", diff --git a/hbrtl/register.go b/hbrtl/register.go index 2bc872f..daace7f 100644 --- a/hbrtl/register.go +++ b/hbrtl/register.go @@ -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) {