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>
122 lines
2.6 KiB
Go
122 lines
2.6 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// Hash functions for the Five runtime library.
|
|
// Harbour: hb_Hash, hb_HGet, hb_HSet, hb_HDel, hb_HHasKey, etc.
|
|
package hbrtl
|
|
|
|
import "five/hbrt"
|
|
|
|
// HbHash creates a hash from key-value pairs.
|
|
// Harbour: hb_Hash(key1, val1, key2, val2, ...) → hHash
|
|
func HbHash(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
|
|
h := hbrt.MakeHash()
|
|
hh := h.AsHash()
|
|
for i := 1; i <= nParams-1; i += 2 {
|
|
hh.Set(t.Local(i), t.Local(i+1))
|
|
}
|
|
t.PushValue(h)
|
|
t.RetValue()
|
|
}
|
|
|
|
// HbHGet gets a value from a hash by key.
|
|
// Harbour: hb_HGet(hHash, xKey) → xValue
|
|
func HbHGet(t *hbrt.Thread) {
|
|
t.Frame(2, 0)
|
|
defer t.EndProc()
|
|
hh := t.Local(1).AsHash()
|
|
if hh != nil {
|
|
if i := hh.Lookup(t.Local(2)); i >= 0 {
|
|
t.PushValue(hh.Values[i])
|
|
t.RetValue()
|
|
return
|
|
}
|
|
}
|
|
t.PushNil()
|
|
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) {
|
|
t.Frame(3, 0)
|
|
defer t.EndProc()
|
|
hVal := t.Local(1)
|
|
if hh := hVal.AsHash(); hh != nil {
|
|
hh.Set(t.Local(2), t.Local(3))
|
|
}
|
|
t.PushValue(hVal)
|
|
t.RetValue()
|
|
}
|
|
|
|
// HbHDel deletes a key from hash.
|
|
// Harbour: hb_HDel(hHash, xKey) → hHash
|
|
func HbHDel(t *hbrt.Thread) {
|
|
t.Frame(2, 0)
|
|
defer t.EndProc()
|
|
hVal := t.Local(1)
|
|
if hh := hVal.AsHash(); hh != nil {
|
|
hh.Delete(t.Local(2))
|
|
}
|
|
t.PushValue(hVal)
|
|
t.RetValue()
|
|
}
|
|
|
|
// HbHHasKey checks if hash contains a key.
|
|
// Harbour: hb_HHasKey(hHash, xKey) → lExists
|
|
func HbHHasKey(t *hbrt.Thread) {
|
|
t.Frame(2, 0)
|
|
defer t.EndProc()
|
|
hh := t.Local(1).AsHash()
|
|
t.PushBool(hh != nil && hh.Has(t.Local(2)))
|
|
t.RetValue()
|
|
}
|
|
|
|
// HbHKeys returns an array of hash keys.
|
|
// Harbour: hb_HKeys(hHash) → aKeys
|
|
func HbHKeys(t *hbrt.Thread) {
|
|
t.Frame(1, 0)
|
|
defer t.EndProc()
|
|
hh := t.Local(1).AsHash()
|
|
if hh != nil {
|
|
t.PushValue(hbrt.MakeArrayFrom(hh.Keys))
|
|
} else {
|
|
t.PushValue(hbrt.MakeArray(0))
|
|
}
|
|
t.RetValue()
|
|
}
|
|
|
|
// HbHValues returns an array of hash values.
|
|
// Harbour: hb_HValues(hHash) → aValues
|
|
func HbHValues(t *hbrt.Thread) {
|
|
t.Frame(1, 0)
|
|
defer t.EndProc()
|
|
hh := t.Local(1).AsHash()
|
|
if hh != nil {
|
|
t.PushValue(hbrt.MakeArrayFrom(hh.Values))
|
|
} else {
|
|
t.PushValue(hbrt.MakeArray(0))
|
|
}
|
|
t.RetValue()
|
|
}
|