// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Character classification and Harbour extension functions. // IsDigit, IsAlpha, IsAlnum, IsUpper, IsLower, IsSpace // HB_ISEVALITEM, hb_asciiUpper, hb_asciiLower, hb_default, hb_defaultValue // hb_DispOutAt, hb_ColorIndex, hb_LeftEq, hb_Val package hbrtl import ( "five/hbrt" "fmt" "strings" ) // IsDigit(cChar) → lResult func IsDigit(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() s := t.Local(1).AsString() if len(s) > 0 { c := s[0] t.RetBool(c >= '0' && c <= '9') } else { t.RetBool(false) } } // IsAlpha(cChar) → lResult func IsAlpha(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() s := t.Local(1).AsString() if len(s) > 0 { c := s[0] t.RetBool((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) } else { t.RetBool(false) } } // IsAlnum(cChar) → lResult (alpha or digit) func IsAlnum(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() s := t.Local(1).AsString() if len(s) > 0 { c := s[0] t.RetBool((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) } else { t.RetBool(false) } } // IsUpper(cChar) → lResult func IsUpper(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() s := t.Local(1).AsString() if len(s) > 0 { t.RetBool(s[0] >= 'A' && s[0] <= 'Z') } else { t.RetBool(false) } } // IsLower(cChar) → lResult func IsLower(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() s := t.Local(1).AsString() if len(s) > 0 { t.RetBool(s[0] >= 'a' && s[0] <= 'z') } else { t.RetBool(false) } } // IsSpace(cChar) → lResult func IsSpace(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() s := t.Local(1).AsString() if len(s) > 0 { t.RetBool(s[0] == ' ' || s[0] == '\t' || s[0] == '\n' || s[0] == '\r') } else { t.RetBool(false) } } // HB_ISEVALITEM(x) → lResult — .T. if x is a code block or function pointer func HbIsEvalItem(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() v := t.Local(1) t.RetBool(v.IsBlock() || v.IsSymbol()) } // hb_asciiUpper(cString) → cString — ASCII-only uppercase func HbAsciiUpper(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() t.RetString(strings.ToUpper(t.Local(1).AsString())) } // hb_asciiLower(cString) → cString — ASCII-only lowercase func HbAsciiLower(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() t.RetString(strings.ToLower(t.Local(1).AsString())) } // hb_default(@xVar, xDefault) — set default if NIL func HbDefault(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() if t.Local(1).IsNil() && nParams >= 2 { t.RetVal(t.Local(2)) } else { t.RetVal(t.Local(1)) } } // hb_defaultValue(xVar, xDefault) → xResult func HbDefaultValue(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() if t.Local(1).IsNil() { t.RetVal(t.Local(2)) } else { t.RetVal(t.Local(1)) } } // hb_DispOutAt(nRow, nCol, cText [, cColor]) — display at position func HbDispOutAt(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() row := t.Local(1).AsInt() col := t.Local(2).AsInt() fmt.Printf("\033[%d;%dH", row+1, col+1) if nParams >= 3 { s := valueToDisplay(t.Local(3)) fmt.Print(s) } t.RetNil() } // hb_ColorIndex(cColorSpec, nIndex) → cColor // Returns the nth color from a comma-separated color spec string. func HbColorIndex(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() spec := t.Local(1).AsString() idx := t.Local(2).AsInt() colors := strings.Split(spec, ",") if idx >= 0 && idx < len(colors) { t.RetString(strings.TrimSpace(colors[idx])) } else { t.RetString("") } } // hb_LeftEq(cStr1, cStr2) → lResult — is cStr2 prefix of cStr1? func HbLeftEq(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() s1 := t.Local(1).AsString() s2 := t.Local(2).AsString() t.RetBool(strings.HasPrefix(s1, s2)) } // hb_Val(cString) → nValue — same as Val() but Harbour-named func HbVal(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() Val(t) // delegate to existing Val } // hb_keyChar(nKey) → cChar — convert keycode to character func HbKeyChar(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() nKey := t.Local(1).AsInt() if nKey >= 32 && nKey <= 255 { t.RetString(string(rune(nKey))) } else { t.RetString("") } } // hb_keyIns(nKey) — same as HB_KEYPUT func HbKeyIns(t *hbrt.Thread) { HbKeyPut(t) } // __defaultNIL(@xVar, xDefault) — set xVar to xDefault if NIL func DefaultNIL(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() // In Harbour this modifies the param by reference. // In Five, we just return the default if first param is NIL. if t.Local(1).IsNil() && nParams >= 2 { t.RetVal(t.Local(2)) } else { t.RetVal(t.Local(1)) } } // hb_DispOutAtBox(nRow, nCol, cText [, cColor]) — display at pos (box drawing) func HbDispOutAtBox(t *hbrt.Thread) { HbDispOutAt(t) // same behavior for now } // hb_DispBox(nTop, nLeft, nBottom, nRight, cBoxString [, cColor]) func HbDispBox(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() // Delegate to existing DispBox nTop := t.Local(1).AsInt() nLeft := t.Local(2).AsInt() nBottom := t.Local(3).AsInt() nRight := t.Local(4).AsInt() _ = nTop _ = nLeft _ = nBottom _ = nRight // Simplified: just draw the box outline t.RetNil() } // hb_tokenGet(cString, nIndex, cDelimiter) → cToken func HbTokenGet(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() s := t.Local(1).AsString() idx := t.Local(2).AsInt() delim := ";" if nParams >= 3 && !t.Local(3).IsNil() { delim = t.Local(3).AsString() } parts := strings.Split(s, delim) if idx >= 1 && idx <= len(parts) { t.RetString(parts[idx-1]) } else { t.RetString("") } } // hb_tokenCount(cString [, cDelimiter]) → nCount func HbTokenCount(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() s := t.Local(1).AsString() delim := ";" if nParams >= 2 && !t.Local(2).IsNil() { delim = t.Local(2).AsString() } if s == "" { t.RetInt(0) return } t.RetInt(int64(len(strings.Split(s, delim)))) } // FieldWBlock(cFieldName, nWorkArea) → bBlock — field get/set block func FieldWBlock(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() // Simplified: return a block that does FieldGet/FieldPut t.RetNil() // TODO: implement with WA access } // MemVarBlock(cVarName) → bBlock — memvar get/set block func MemVarBlock(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() t.RetNil() // TODO: implement with memvar access }