- 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>
197 lines
4.0 KiB
Go
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()
|
|
}
|