- 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>
6.8 KiB
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
- IMPORT is all you need — No #pragma BEGINDUMP required
- 100% PRG code — Zero Go code to use Go features
- Automatic type conversion — string/int/bool/array/hash bidirectional
- Transparent Go objects — Store in LOCAL, call with
: - Harbour compatible — Existing xBase syntax unchanged, Go is the backend