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

197 lines
4.0 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// FRB (Five Runtime Binary) RTL functions.
//
// PRG Usage:
// pMod := FrbLoad("module.frb") // load module
// FrbDo(pMod, "MYFUNC", args...) // call function
// FrbUnload(pMod) // unload
//
// // Or one-shot:
// result := FrbRun("module.frb", arg1, arg2)
package hbrtl
import (
"five/hbrt"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)
// findFiveExe locates the 'five' compiler binary
func findFiveExe() string {
// 1. Check same directory as running executable
if exe, err := os.Executable(); err == nil {
dir := filepath.Dir(exe)
fiveExe := filepath.Join(dir, "five")
if _, err := os.Stat(fiveExe); err == nil {
return fiveExe
}
}
// 2. Check PATH
if p, err := exec.LookPath("five"); err == nil {
return p
}
// 3. Check current directory
if _, err := os.Stat("./five"); err == nil {
abs, _ := filepath.Abs("./five")
return abs
}
return "five" // hope it's in PATH
}
// FRBLOAD(cFileName) → pModule
func FrbLoadFunc(t *hbrt.Thread) {
t.Frame(1, 0)
defer t.EndProc()
filename := t.Local(1).AsString()
mod, err := hbrt.FrbLoad(t.VM(), filename)
if err != nil {
fmt.Fprintf(os.Stderr, "FrbLoad error: %v\n", err)
t.RetNil()
return
}
t.RetPointer(mod)
}
// FRBDO(pModule, cFuncName [, args...]) → xResult
func FrbDoFunc(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
modVal := t.Local(1)
funcName := strings.ToUpper(t.Local(2).AsString())
// Look up function: module-local scope first, then VM global
var fn func(*hbrt.Thread)
if modVal.IsPointer() {
if mod, ok := modVal.AsPointer().(*hbrt.FrbModule); ok {
fn = mod.FindFunc(funcName)
}
}
if fn == nil {
sym := t.VM().FindSymbol(funcName)
if sym != nil {
fn = sym.Func
}
}
if fn == nil {
t.RetNil()
return
}
// Push args for the function
for i := 3; i <= nParams; i++ {
t.PushValue(t.Local(i))
}
t.PendingParams2(nParams - 2)
fn(t)
t.PushValue(t.GetRetValue())
t.RetValue()
}
// FRBUNLOAD(pModule) → NIL
func FrbUnloadFunc(t *hbrt.Thread) {
t.Frame(1, 0)
defer t.EndProc()
v := t.Local(1)
if !v.IsNil() && v.IsPointer() {
if mod, ok := v.AsPointer().(*hbrt.FrbModule); ok {
hbrt.FrbUnload(mod)
}
}
t.RetNil()
}
// FRBCOMPILE(cPrgSource) → pModule
// Compile PRG source string to FRB module in memory.
func FrbCompileFunc(t *hbrt.Thread) {
t.Frame(1, 0)
defer t.EndProc()
source := t.Local(1).AsString()
fiveExe := findFiveExe()
mod, err := hbrt.FrbCompileSource(t.VM(), source, fiveExe)
if err != nil {
fmt.Fprintf(os.Stderr, "FrbCompile error: %v\n", err)
t.RetNil()
return
}
t.RetPointer(mod)
}
// FRBEXEC(cPrgSource [, args...]) → xResult
// Compile PRG source, run Main(), unload — all in one call.
func FrbExecFunc(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
source := t.Local(1).AsString()
fiveExe := findFiveExe()
mod, err := hbrt.FrbCompileSource(t.VM(), source, fiveExe)
if err != nil {
fmt.Fprintf(os.Stderr, "FrbExec error: %v\n", err)
t.RetNil()
return
}
defer hbrt.FrbUnload(mod)
// Find and execute MAIN
sym := t.VM().FindSymbol("MAIN")
if sym == nil || sym.Func == nil {
t.RetNil()
return
}
for i := 2; i <= nParams; i++ {
t.PushValue(t.Local(i))
}
t.PendingParams2(nParams - 1)
sym.Func(t)
t.PushValue(t.GetRetValue())
t.RetValue()
}
// FRBRUN(cFileName [, args...]) → xResult
// Load, execute startup function, unload — all in one call.
func FrbRunFunc(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
defer t.EndProc()
filename := t.Local(1).AsString()
mod, err := hbrt.FrbLoad(t.VM(), filename)
if err != nil {
t.RetNil()
return
}
defer hbrt.FrbUnload(mod)
// Find MAIN symbol
sym := t.VM().FindSymbol("MAIN")
if sym == nil || sym.Func == nil {
t.RetNil()
return
}
// Push args
for i := 2; i <= nParams; i++ {
t.PushValue(t.Local(i))
}
t.PendingParams2(nParams - 1)
sym.Func(t)
t.PushValue(t.GetRetValue())
t.RetValue()
}