Files
five/hbrtl/hash.go
Charles KWON OhJun ad6cc0bcee 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>
2026-05-27 09:55:19 +09:00

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()
}