- 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>
250 lines
6.8 KiB
Markdown
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
|