diff --git a/hbrt/thread.go b/hbrt/thread.go index 24df575..372e4e2 100644 --- a/hbrt/thread.go +++ b/hbrt/thread.go @@ -670,6 +670,19 @@ func (t *Thread) CallerParamCount() int { return 0 } +// CallerLocal returns the n-th parameter of the calling PRG function +// (1-based). Returns NIL if no caller frame exists or n is out of range. +// Pairs with CallerParamCount for implementing the PValue() RTL. +func (t *Thread) CallerLocal(n int) Value { + if t.callSP >= 2 { + caller := &t.calls[t.callSP-2] + if n >= 1 && n <= caller.paramCount { + return caller.GetLocal(n, t.locals) + } + } + return MakeNil() +} + // PendingParams2 sets pending param count for direct block calls (AEval, ASort etc.) func (t *Thread) PendingParams2(n int) { t.pendingParams = n diff --git a/hbrtl/hash.go b/hbrtl/hash.go index ede6991..9cc7f6e 100644 --- a/hbrtl/hash.go +++ b/hbrtl/hash.go @@ -40,6 +40,22 @@ func HbHGet(t *hbrt.Thread) { t.RetValue() } +// HbHGetDef gets a value from a hash by key, returning a default if missing. +// Harbour: hb_HGetDef(hHash, xKey, xDefault) → xValue +func HbHGetDef(t *hbrt.Thread) { + t.Frame(3, 0) + defer t.EndProc() + if hh := t.Local(1).AsHash(); hh != nil { + if i := hh.Lookup(t.Local(2)); i >= 0 { + t.PushValue(hh.Values[i]) + t.RetValue() + return + } + } + t.PushValue(t.Local(3)) + t.RetValue() +} + // HbHSet sets a value in hash by key. // Harbour: hb_HSet(hHash, xKey, xValue) → hHash func HbHSet(t *hbrt.Thread) { diff --git a/hbrtl/missing.go b/hbrtl/missing.go index 322edc7..16ad8d3 100644 --- a/hbrtl/missing.go +++ b/hbrtl/missing.go @@ -278,6 +278,27 @@ func PCount(t *hbrt.Thread) { t.RetInt(int64(t.CallerParamCount())) } +// PValue returns the nth parameter of the calling PRG function. +// Harbour: PValue(nIndex[, xDefault]) → xValue +// Returns xDefault when n is out of range, or NIL if no default was given. +func PValue(t *hbrt.Thread) { + nParams := t.ParamCount() + t.Frame(nParams, 0) + defer t.EndProc() + n := int(t.Local(1).AsNumInt()) + if n >= 1 && n <= t.CallerParamCount() { + t.PushValue(t.CallerLocal(n)) + t.RetValue() + return + } + if nParams >= 2 { + t.PushValue(t.Local(2)) + } else { + t.PushNil() + } + t.RetValue() +} + // Break moved to error.go — full implementation with BreakValue type. // Array creates array of given size. diff --git a/hbrtl/register.go b/hbrtl/register.go index 954f5ca..a59fb16 100644 --- a/hbrtl/register.go +++ b/hbrtl/register.go @@ -69,6 +69,7 @@ func RegisterRTL(vm *hbrt.VM) { // Hash hbrt.Sym("HB_HASH", hbrt.FsPublic, HbHash), hbrt.Sym("HB_HGET", hbrt.FsPublic, HbHGet), + hbrt.Sym("HB_HGETDEF", hbrt.FsPublic, HbHGetDef), hbrt.Sym("HB_HSET", hbrt.FsPublic, HbHSet), hbrt.Sym("HB_HDEL", hbrt.FsPublic, HbHDel), hbrt.Sym("HB_HHASKEY", hbrt.FsPublic, HbHHasKey), @@ -120,6 +121,8 @@ func RegisterRTL(vm *hbrt.VM) { // Misc (new) hbrt.Sym("TYPE", hbrt.FsPublic, TypeFunc), hbrt.Sym("PCOUNT", hbrt.FsPublic, PCount), + hbrt.Sym("PVALUE", hbrt.FsPublic, PValue), + hbrt.Sym("HB_PVALUE", hbrt.FsPublic, PValue), hbrt.Sym("BREAK", hbrt.FsPublic, Break), hbrt.Sym("ARRAY", hbrt.FsPublic, ArrayFunc), hbrt.Sym("FCOUNT", hbrt.FsPublic, FCount),