Files
five/hbrtl/strings2.go
Charles KWON OhJun 59568f3301 Five v0.9 — Harbour + Go fusion language
- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline
- Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch
- RTL: 351 Harbour-compatible functions
- RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization
- Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec)
- HB_FUNC API: Full Harbour C API compatible Go bridge
- Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT
- Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST
- Macro Compiler: Runtime AST parsing and evaluation
- Debugger: TUI debugger with source display, breakpoints, stepping
- FRB: Native + Pcode dual mode runtime binary
- Tests: 13 packages ALL PASS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:41:50 +09:00

168 lines
3.6 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// Additional string functions: RAT, STRZERO, DESCEND, HB_VALTOSTR,
// MEMOREAD, MEMOWRIT, MEMOTRAN
package hbrtl
import (
"five/hbrt"
"fmt"
"os"
"strings"
)
// RAT(cSearch, cTarget [, nOccurrence]) → nPos
// Returns position of LAST occurrence of cSearch in cTarget.
func Rat(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
search := t.Local(1).AsString()
target := t.Local(2).AsString()
if search == "" || target == "" {
t.RetInt(0)
return
}
nOccurrence := 1
if nParams >= 3 && !t.Local(3).IsNil() {
nOccurrence = t.Local(3).AsInt()
if nOccurrence < 1 {
nOccurrence = 1
}
}
// Find nth occurrence from the right
pos := -1
from := len(target)
for i := 0; i < nOccurrence; i++ {
pos = strings.LastIndex(target[:from], search)
if pos < 0 {
t.RetInt(0)
return
}
from = pos
}
t.RetInt(int64(pos + 1)) // 1-based
}
// STRZERO(nValue, nLen [, nDec]) → cString
// Converts number to string padded with leading zeros.
func StrZero(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
val := t.Local(1)
nLen := 10
nDec := 0
if nParams >= 2 && !t.Local(2).IsNil() {
nLen = t.Local(2).AsInt()
}
if nParams >= 3 && !t.Local(3).IsNil() {
nDec = t.Local(3).AsInt()
}
var s string
if nDec > 0 {
fmtStr := fmt.Sprintf("%%0%d.%df", nLen, nDec)
s = fmt.Sprintf(fmtStr, val.AsNumDouble())
} else {
fmtStr := fmt.Sprintf("%%0%dd", nLen)
s = fmt.Sprintf(fmtStr, val.AsLong())
}
// Ensure exact length
if len(s) > nLen {
s = strings.Repeat("*", nLen)
}
t.RetString(s)
}
// DESCEND(xValue) → xDescended
// For strings: flips each byte (255-byte). For numbers: negates. For dates: max-date.
func Descend(t *hbrt.Thread) {
t.Frame(1, 0)
defer t.EndProc()
v := t.Local(1)
switch {
case v.IsString():
s := v.AsString()
buf := make([]byte, len(s))
for i := 0; i < len(s); i++ {
buf[i] = 255 - s[i]
}
t.RetString(string(buf))
case v.IsNumeric():
t.RetVal(hbrt.MakeNumInt(-v.AsLong()))
case v.IsDate():
// Max Julian (2^24) minus date
t.RetVal(hbrt.MakeNumInt(5373484 - v.AsJulian()))
default:
t.RetNil()
}
}
// HB_VALTOSTR(xValue) → cString
// Converts any value to its string representation.
func HbValToStr(t *hbrt.Thread) {
t.Frame(1, 0)
defer t.EndProc()
t.RetString(valueToDisplay(t.Local(1)))
}
// MEMOREAD(cFileName) → cContents
// Reads entire file into a string.
func MemoRead(t *hbrt.Thread) {
t.Frame(1, 0)
defer t.EndProc()
fname := t.Local(1).AsString()
data, err := os.ReadFile(fname)
if err != nil {
t.RetString("")
return
}
t.RetString(string(data))
}
// MEMOWRIT(cFileName, cString [, lAddEOF]) → lSuccess
// Writes string to file.
func MemoWrit(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
fname := t.Local(1).AsString()
content := t.Local(2).AsString()
err := os.WriteFile(fname, []byte(content), 0644)
t.RetBool(err == nil)
}
// MEMOTRAN(cMemoText [, cSoftCR [, cHardCR]]) → cString
// Replaces soft/hard CR in memo fields.
func MemoTran(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
text := t.Local(1).AsString()
softCR := ";"
hardCR := ";"
if nParams >= 2 && !t.Local(2).IsNil() {
softCR = t.Local(2).AsString()
}
if nParams >= 3 && !t.Local(3).IsNil() {
hardCR = t.Local(3).AsString()
}
// Replace soft CR (141+10) then hard CR (13+10)
text = strings.ReplaceAll(text, "\x8d\n", softCR)
text = strings.ReplaceAll(text, "\r\n", hardCR)
t.RetString(text)
}