Files
five/docs/go-interop-en.md
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

6.8 KiB

Five Go Interop — Using Go Packages Directly from PRG

Five's key differentiator: use Go's entire package ecosystem directly from PRG code.

1. IMPORT — Bring Go Packages into PRG

IMPORT "strings"                    // Go standard library
IMPORT "database/sql"               // Database
IMPORT "net/http"                   // HTTP server/client
IMPORT "encoding/json"              // JSON
IMPORT _ "modernc.org/sqlite"       // blank import (driver registration)
IMPORT myhttp "net/http"            // aliased import

Declare at the top of PRG file. gengo converts directly to Go imports.

2. Package Function Calls — pkg.Func()

IMPORT "strings"
IMPORT "strconv"
IMPORT "fmt"

PROCEDURE Main()
   LOCAL cResult, nVal, cFormatted

   cResult := strings.ToUpper("hello five!")     // "HELLO FIVE!"
   cResult := strings.ReplaceAll("a-b-c", "-", "_")  // "a_b_c"

   nVal := strconv.Atoi("42")                    // 42
   cFormatted := fmt.Sprintf("Name: %s, Age: %d", "Charles", 30)

   IF strings.HasPrefix(cResult, "HELLO")
      ? "starts with HELLO"
   ENDIF

   RETURN

How It Works

PRG:  strings.ToUpper("hello")
  ↓ gengo
Go:   hbrt.GoCallFast(_ff_strings_ToUpper, _arg0)
  • gengo detects imported package names
  • pkg.Func(args)hbrt.GoCallFast() type-specialized call
  • Return values automatically converted to Harbour Values

Automatic Type Conversion

Go Type → Harbour Type
string String
int, int64 Numeric (Integer/Long)
float64 Numeric (Double)
bool Logical
[]string, []int etc. Array
map[string]interface{} Hash
error (nil) NIL
error (non-nil) String (error message)
*sql.DB etc. (pointer) Go Object (wrapped in Value)

3. Go Object Method Calls — obj:Method()

Go function return objects are called with Harbour's : syntax:

IMPORT "database/sql"
IMPORT _ "modernc.org/sqlite"

PROCEDURE Main()
   LOCAL db, rows

   db := sql.Open("sqlite", ":memory:")
   db:Exec("CREATE TABLE test (id INTEGER)")
   db:Exec("INSERT INTO test VALUES (1)")

   rows := db:Query("SELECT * FROM test")
   DO WHILE rows:Next()
      ? rows:Column(1)
   ENDDO
   rows:Close()
   db:Close()

   RETURN

How It Works

PRG:  db:Exec("CREATE TABLE ...")
  ↓ gengo
Go:   if hbrt.IsGoObject(_obj) {
          hbrt.GoCallCached(_obj, "Exec", _args...)  // reflect + cache
      } else {
          t.Send("Exec", 1)                           // Harbour object
      }
  • Runtime auto-detection: Go object vs Harbour object
  • Go object: reflect.MethodByName() with method cache
  • Harbour object: existing Send() mechanism

4. Multiple Go Objects Simultaneously

IMPORT "database/sql"
IMPORT _ "modernc.org/sqlite"

PROCEDURE Main()
   LOCAL dbSource, dbTarget, aRows, i

   dbSource := sql.Open("sqlite", "source.db")
   dbTarget := sql.Open("sqlite", "target.db")

   aRows := SqlScan(dbSource, "SELECT * FROM products")
   FOR i := 1 TO Len(aRows)
      dbTarget:Exec("INSERT INTO inventory VALUES (...)")
   NEXT

   dbSource:Close()
   dbTarget:Close()
   RETURN

FUNCTION SqlScan(db, cSQL)
   LOCAL rows, cols, aResult, aRow, i, nCols
   aResult := {}
   rows := db:Query(cSQL)
   cols := rows:Columns()
   nCols := Len(cols)
   DO WHILE rows:Next()
      aRow := {=>}
      FOR i := 1 TO nCols
         aRow[cols[i]] := rows:Column(i)
      NEXT
      AAdd(aResult, aRow)
   ENDDO
   rows:Close()
   RETURN aResult

5. Array Return Handling

Go functions returning slices automatically convert to Harbour arrays:

IMPORT "strings"

PROCEDURE Main()
   LOCAL aParts, i

   aParts := strings.Split("one,two,three", ",")

   ? Len(aParts)        // 3
   ? aParts[1]          // "one"
   ? aParts[2]          // "two"
   ? aParts[3]          // "three"

   FOR i := 1 TO Len(aParts)
      ? "  [" + Str(i, 1) + "]", aParts[i]
   NEXT
   RETURN

6. #pragma BEGINDUMP — Advanced Use (Optional)

Only needed for complex Go logic:

PROCEDURE Main()
   ? MyGoFunc("hello")
   RETURN

#pragma BEGINDUMP
import "five/hbrt"

func init() {
   hbrt.HB_FUNC("MYGOFUNC", func(ctx *hbrt.HBContext) {
      s := ctx.ParC(1)
      ctx.RetC(strings.ToUpper(s) + "!!!")
   })
}
#pragma ENDDUMP

HB_FUNC API (Harbour C API Compatible)

Harbour C Five Go Description
HB_FUNC(NAME) hbrt.HB_FUNC("NAME", fn) Register function
hb_pcount() ctx.PCount() Parameter count
hb_parc(n) ctx.ParC(n) String parameter
hb_parni(n) ctx.ParNI(n) Integer parameter
hb_parnl(n) ctx.ParNL(n) Long parameter
hb_parnd(n) ctx.ParND(n) Double parameter
hb_parl(n) ctx.ParL(n) Logical parameter
hb_pards(n) ctx.ParDS(n) Date (YYYYMMDD)
HB_ISCHAR(n) ctx.IsChar(n) Type check
HB_ISNUM(n) ctx.IsNum(n) Type check
hb_retc(s) ctx.RetC(s) Return string
hb_retni(n) ctx.RetNI(n) Return integer
hb_retnd(d) ctx.RetND(d) Return double
hb_retl(b) ctx.RetL(b) Return logical
hb_storc(s,n) ctx.StorC(s,n) By-ref store
hb_arrayNew() ctx.ArrayNew(n) Create array
hb_arrayGet() ctx.ArrayGet(v,i) Array read
hb_hashNew() ctx.HashNew() Create hash

Five Extension API (Go-specific, not in Harbour)

API Description
ctx.ParDate(n) Returns time.Time
ctx.ParArray(n) Returns []Value
ctx.ParHash(n) Returns *HbHash
ctx.RetArray(items) Return array
ctx.RetHash(h) Return hash
ctx.RetVal(v) Return any Value
hbrt.WrapGo(obj) Go object → Value
hbrt.UnwrapGo(v) Value → Go object
hbrt.GoCall(v, method, args...) Reflect method call

7. Available Go Packages

Package PRG Usage Example
strings strings.ToUpper(), strings.Split(), strings.Contains()
strconv strconv.Atoi(), strconv.FormatFloat()
fmt fmt.Sprintf()
database/sql sql.Open()db:Exec(), db:Query()
net/http HTTP server, REST API
encoding/json JSON encode/decode
os os.ReadFile(), os.Stat()
path/filepath filepath.Join(), filepath.Glob()
time time.Now(), time.Since()
crypto/sha256 Hash functions
regexp Regular expressions
sort Sorting
External modernc.org/sqlite, github.com/... etc.

8. Core Principles

  1. IMPORT is all you need — No #pragma BEGINDUMP required
  2. 100% PRG code — Zero Go code to use Go features
  3. Automatic type conversion — string/int/bool/array/hash bidirectional
  4. Transparent Go objects — Store in LOCAL, call with :
  5. Harbour compatible — Existing xBase syntax unchanged, Go is the backend