# 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 ```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()` ```prg 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: ```prg 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 ```prg 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: ```prg 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: ```prg 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