Files
fivenode_go/hbrtl_ext/dispatch/dispatch.go
Charles KWON OhJun b213f594aa 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>
2026-05-27 11:16:44 +09:00

43 lines
1.1 KiB
Go

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