// 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() }