feat(rtl): add hb_HGetDef and PValue / hb_PValue
Two standard Harbour functions that fivenode-style PRG code (bridge_*.prg and downstream apps) calls frequently. Without them, every reference emits an analyzer WARN and resolves to NIL at runtime. * hb_HGetDef(hHash, xKey, xDefault) — hash lookup with fallback. * PValue(nIndex[, xDefault]) — read the nth parameter of the calling PRG function. Mirrors the PCount pattern: needs the caller frame's paramCount and locals, exposed via new hbrt.Thread.CallerLocal helper that pairs with the existing CallerParamCount. Registered under PVALUE and HB_PVALUE (Harbour accepts both forms). Verified: hb_HGetDef / PValue / HB_PVALUE all return expected values for present-key, missing-key-with-default, missing-key-no-default, and out-of-range-param cases. Full regression: go test (18 packages) + Compat 56/56 + std.ch 17/17 + FRB 7/7 + FiveSql2 43/43 all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -670,6 +670,19 @@ func (t *Thread) CallerParamCount() int {
|
|||||||
return 0
|
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.)
|
// PendingParams2 sets pending param count for direct block calls (AEval, ASort etc.)
|
||||||
func (t *Thread) PendingParams2(n int) {
|
func (t *Thread) PendingParams2(n int) {
|
||||||
t.pendingParams = n
|
t.pendingParams = n
|
||||||
|
|||||||
@@ -40,6 +40,22 @@ func HbHGet(t *hbrt.Thread) {
|
|||||||
t.RetValue()
|
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.
|
// HbHSet sets a value in hash by key.
|
||||||
// Harbour: hb_HSet(hHash, xKey, xValue) → hHash
|
// Harbour: hb_HSet(hHash, xKey, xValue) → hHash
|
||||||
func HbHSet(t *hbrt.Thread) {
|
func HbHSet(t *hbrt.Thread) {
|
||||||
|
|||||||
@@ -278,6 +278,27 @@ func PCount(t *hbrt.Thread) {
|
|||||||
t.RetInt(int64(t.CallerParamCount()))
|
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.
|
// Break moved to error.go — full implementation with BreakValue type.
|
||||||
|
|
||||||
// Array creates array of given size.
|
// Array creates array of given size.
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ func RegisterRTL(vm *hbrt.VM) {
|
|||||||
// Hash
|
// Hash
|
||||||
hbrt.Sym("HB_HASH", hbrt.FsPublic, HbHash),
|
hbrt.Sym("HB_HASH", hbrt.FsPublic, HbHash),
|
||||||
hbrt.Sym("HB_HGET", hbrt.FsPublic, HbHGet),
|
hbrt.Sym("HB_HGET", hbrt.FsPublic, HbHGet),
|
||||||
|
hbrt.Sym("HB_HGETDEF", hbrt.FsPublic, HbHGetDef),
|
||||||
hbrt.Sym("HB_HSET", hbrt.FsPublic, HbHSet),
|
hbrt.Sym("HB_HSET", hbrt.FsPublic, HbHSet),
|
||||||
hbrt.Sym("HB_HDEL", hbrt.FsPublic, HbHDel),
|
hbrt.Sym("HB_HDEL", hbrt.FsPublic, HbHDel),
|
||||||
hbrt.Sym("HB_HHASKEY", hbrt.FsPublic, HbHHasKey),
|
hbrt.Sym("HB_HHASKEY", hbrt.FsPublic, HbHHasKey),
|
||||||
@@ -120,6 +121,8 @@ func RegisterRTL(vm *hbrt.VM) {
|
|||||||
// Misc (new)
|
// Misc (new)
|
||||||
hbrt.Sym("TYPE", hbrt.FsPublic, TypeFunc),
|
hbrt.Sym("TYPE", hbrt.FsPublic, TypeFunc),
|
||||||
hbrt.Sym("PCOUNT", hbrt.FsPublic, PCount),
|
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("BREAK", hbrt.FsPublic, Break),
|
||||||
hbrt.Sym("ARRAY", hbrt.FsPublic, ArrayFunc),
|
hbrt.Sym("ARRAY", hbrt.FsPublic, ArrayFunc),
|
||||||
hbrt.Sym("FCOUNT", hbrt.FsPublic, FCount),
|
hbrt.Sym("FCOUNT", hbrt.FsPublic, FCount),
|
||||||
|
|||||||
Reference in New Issue
Block a user