feat(dispatch): file-name routing via auto-renamed Main symbols (AOT)
Lets app/api/foo.prg keep its idiomatic `FUNCTION Main()` shape while
multiple such files compile into one binary. fnode auto-renames each
library file's Main into a unique symbol derived from the basename:
app/api/hello.prg -> HELLO__MAIN
app/api/admin-stats.prg -> ADMIN_STATS__MAIN (hyphen -> underscore)
Three moving parts:
cmd/fnode/main.go
parseOne for every PRG, then rename Main on every file except
the first (the entry). crossFile map updated so the analyzer
treats the renamed symbol as declared.
hbrtl_ext/dispatch/dispatch.go
New HB_FUNC FNODE_CALL(cFuncName) that does VM.FindSymbol +
PushSymbol/Function dance and discards the return value. Same
pattern pgserver's callPRG helper uses internally.
app/bridge_server.prg
BridgeDispatch now derives the symbol name from hReq["path"]
( /api/foo[.prg] -> FOO__MAIN ), invokes FNODE_CALL, and
maps "not found" errors to HTTP 404 (other errors -> 500).
Hardcoded /api/hello and /api/echo handlers replaced by the
path-driven model.
Verified end-to-end with app/api/hello.prg and app/api/admin-stats.prg:
GET /api/hello.prg -> 200 + JSON from HELLO__MAIN
GET /api/hello -> 200 (extension optional)
GET /api/admin-stats.prg?from=2026 -> 200 from ADMIN_STATS__MAIN
with query string echoed
GET /api/nope -> 404 "function not found"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
42
hbrtl_ext/dispatch/dispatch.go
Normal file
42
hbrtl_ext/dispatch/dispatch.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Package dispatch exposes one helper for PRG code: a dynamic
|
||||
// "call this function by name" hook that pairs with fnode's auto-
|
||||
// renamed Main symbols. Lets the bridge dispatcher route an HTTP
|
||||
// path to the right Main without needing a hard-coded `DO CASE`.
|
||||
//
|
||||
// PRG surface
|
||||
//
|
||||
// cErr := FNODE_CALL(cFuncName) -> "" on success, error text on failure
|
||||
//
|
||||
// The called function runs on the same Thread; output is left in the
|
||||
// bridge_capi per-thread output buffer (use _OUT_GET to read it).
|
||||
// Return values from the called function are discarded — call the
|
||||
// generated symbol directly when you need its return value.
|
||||
package dispatch
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"five/hbrt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
hbrt.HB_FUNC("FNODE_CALL", fnodeCall)
|
||||
}
|
||||
|
||||
func fnodeCall(ctx *hbrt.HBContext) {
|
||||
if ctx.PCount() < 1 || !ctx.IsChar(1) {
|
||||
ctx.RetC("FNODE_CALL: function name required")
|
||||
return
|
||||
}
|
||||
name := strings.ToUpper(ctx.ParC(1))
|
||||
sym := ctx.T.VM().FindSymbol(name)
|
||||
if sym == nil {
|
||||
ctx.RetC("function not found: " + ctx.ParC(1))
|
||||
return
|
||||
}
|
||||
ctx.T.PushSymbol(sym)
|
||||
ctx.T.PushNil() // self placeholder
|
||||
ctx.T.Function(0)
|
||||
_ = ctx.T.Pop2() // discard return value
|
||||
ctx.RetC("")
|
||||
}
|
||||
Reference in New Issue
Block a user