- 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>
168 lines
3.6 KiB
Go
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)
|
|
}
|