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

250 lines
6.8 KiB
Markdown

# 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