// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Stack introspection: PROCNAME, PROCLINE, PROCFILE, ERRORLEVEL package hbrtl import ( "five/hbrt" "os" "strconv" ) // PROCNAME([nLevel]) → cFunctionName func ProcName(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() level := 0 if nParams >= 1 && !t.Local(1).IsNil() { level = t.Local(1).AsInt() } stack := t.DebugCallStack() if level >= 0 && level < len(stack) { t.RetString(stack[level].Function) } else { t.RetString("") } } // PROCLINE([nLevel]) → nLineNumber func ProcLine(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() level := 0 if nParams >= 1 && !t.Local(1).IsNil() { level = t.Local(1).AsInt() } stack := t.DebugCallStack() if level >= 0 && level < len(stack) { t.RetInt(int64(stack[level].Line)) } else { t.RetInt(0) } } // PROCFILE([nLevel]) → cSourceFileName func ProcFile(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() level := 0 if nParams >= 1 && !t.Local(1).IsNil() { level = t.Local(1).AsInt() } stack := t.DebugCallStack() if level >= 0 && level < len(stack) { t.RetString(stack[level].Module) } else { t.RetString("") } } var exitLevel int // ERRORLEVEL([nNewLevel]) → nOldLevel func ErrorLevel(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() old := exitLevel if nParams >= 1 && !t.Local(1).IsNil() { exitLevel = t.Local(1).AsInt() } t.RetInt(int64(old)) } // TONE(nFrequency [, nDuration]) → NIL func Tone(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() // Terminal bell os.Stdout.Write([]byte{7}) t.RetNil() } // CENTER(cString, nWidth [, cFill]) → cCentered (alias: PADC) func Center(t *hbrt.Thread) { PadC(t) } // STRZERO already exists, HB_NTOS already exists // HB_NTOS(nValue) → cString (no leading spaces) — already in missing.go // FIELDPOS(cFieldName) → nPos func FieldPos(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() fname := t.Local(1).AsString() wam := getWA(t) if wam == nil { t.RetInt(0) return } area := wam.Current() if area == nil { t.RetInt(0) return } for i := 1; i <= area.FieldCount(); i++ { fi := area.GetFieldInfo(i) if eqFold(fi.Name, fname) { t.RetInt(int64(i)) return } } t.RetInt(0) } func eqFold(a, b string) bool { if len(a) != len(b) { return false } for i := 0; i < len(a); i++ { ca, cb := a[i], b[i] if ca >= 'a' && ca <= 'z' { ca -= 32 } if cb >= 'a' && cb <= 'z' { cb -= 32 } if ca != cb { return false } } return true } // FIELDBLOCK(nField) → bBlock — create block that reads field n func FieldBlockFunc(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() nField := t.Local(1).AsInt() // Create a block that calls FieldGet(nField) on the current area blk := hbrt.MakeBlock(func(t2 *hbrt.Thread) { t2.Frame(0, 0) defer t2.EndProc() wam := getWA(t2) if wam != nil { if area := wam.Current(); area != nil { val, _ := area.GetValue(nField - 1) // 1-based to 0-based t2.PushValue(val) t2.RetValue() return } } t2.RetNil() }, 0) t.RetVal(blk) } // FIELDNAME(nField) → cName func FieldNameFunc(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() nField := t.Local(1).AsInt() wam := getWA(t) if wam != nil { if area := wam.Current(); area != nil { if nField >= 1 && nField <= area.FieldCount() { fi := area.GetFieldInfo(nField - 1) t.RetString(fi.Name) return } } } t.RetString("") } // AFIELDS(@aNames [, @aTypes, @aWidths, @aDecs]) → nFieldCount func AFields(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetInt(0) return } area := wam.Current() if area == nil { t.RetInt(0) return } nFields := area.FieldCount() t.RetInt(int64(nFields)) } // DBSTRUCT() → aStruct (array of {name, type, len, dec}) func DbStruct(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetVal(hbrt.MakeArray(0)) return } area := wam.Current() if area == nil { t.RetVal(hbrt.MakeArray(0)) return } nFields := area.FieldCount() items := make([]hbrt.Value, nFields) for i := 1; i <= nFields; i++ { fi := area.GetFieldInfo(i) row := []hbrt.Value{ hbrt.MakeString(fi.Name), hbrt.MakeString(string(fi.Type)), hbrt.MakeInt(int(fi.Len)), hbrt.MakeInt(int(fi.Dec)), } items[i-1] = hbrt.MakeArrayFrom(row) } t.RetVal(hbrt.MakeArrayFrom(items)) } // HB_DATETIME([nYear, nMonth, nDay, nHour, nMin, nSec]) → tTimestamp func HbDatetime(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() // Simplified: return current date as string for now t.RetString(strconv.Itoa(nParams)) // TODO: full timestamp t.RetNil() }