Files
five/hbrtl/regex.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

135 lines
2.9 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// Regular expression functions using Go's regexp package.
// HB_REGEXCOMP, HB_REGEXMATCH, HB_REGEXSPLIT, HB_REGEXALL, HB_REGEXREPLACE
package hbrtl
import (
"five/hbrt"
"regexp"
)
// HB_REGEXCOMP(cPattern [, lCaseSensitive]) → pRegex
// Returns compiled regex as a pointer value.
func HbRegexComp(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
pattern := t.Local(1).AsString()
// Default case-sensitive; if 2nd param is .F., add (?i) prefix
if nParams >= 2 && !t.Local(2).IsNil() && !t.Local(2).AsBool() {
pattern = "(?i)" + pattern
}
re, err := regexp.Compile(pattern)
if err != nil {
t.RetNil()
return
}
t.RetPointer(re)
}
// HB_REGEXMATCH(cPattern|pRegex, cString [, lCaseSensitive]) → lMatch
func HbRegexMatch(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
re := getRegex(t, 1, nParams >= 3)
if re == nil {
t.RetBool(false)
return
}
str := t.Local(2).AsString()
t.RetBool(re.MatchString(str))
}
// HB_REGEXSPLIT(cPattern|pRegex, cString [, lCaseSensitive]) → aResult
func HbRegexSplit(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
re := getRegex(t, 1, nParams >= 3)
if re == nil {
t.RetNil()
return
}
str := t.Local(2).AsString()
parts := re.Split(str, -1)
items := make([]hbrt.Value, len(parts))
for i, p := range parts {
items[i] = hbrt.MakeString(p)
}
t.RetVal(hbrt.MakeArrayFrom(items))
}
// HB_REGEXALL(cPattern|pRegex, cString [, lCaseSensitive]) → aMatches
func HbRegexAll(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
re := getRegex(t, 1, nParams >= 3)
if re == nil {
t.RetNil()
return
}
str := t.Local(2).AsString()
matches := re.FindAllString(str, -1)
items := make([]hbrt.Value, len(matches))
for i, m := range matches {
items[i] = hbrt.MakeString(m)
}
t.RetVal(hbrt.MakeArrayFrom(items))
}
// HB_REGEXREPLACE(cPattern|pRegex, cString, cReplace [, lCaseSensitive]) → cResult
func HbRegexReplace(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
re := getRegex(t, 1, nParams >= 4)
if re == nil {
t.RetString("")
return
}
str := t.Local(2).AsString()
repl := t.Local(3).AsString()
t.RetString(re.ReplaceAllString(str, repl))
}
// getRegex extracts or compiles a regex from param at given index.
func getRegex(t *hbrt.Thread, paramIdx int, hasCaseParam bool) *regexp.Regexp {
v := t.Local(paramIdx)
if v.IsPointer() {
if re, ok := v.AsPointer().(*regexp.Regexp); ok {
return re
}
}
// String pattern — compile on the fly
pattern := v.AsString()
if hasCaseParam {
caseParam := t.Local(paramIdx + 2) // 3rd or 4th param
if !caseParam.IsNil() && !caseParam.AsBool() {
pattern = "(?i)" + pattern
}
}
re, err := regexp.Compile(pattern)
if err != nil {
return nil
}
return re
}