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

262 lines
7.8 KiB
Markdown

# Five Go Interop — PRG에서 Go 패키지 직접 사용
Five의 핵심 차별점: **PRG 코드에서 Go의 전체 패키지 생태계를 직접 사용**.
## 1. IMPORT — Go 패키지 가져오기
```prg
IMPORT "strings" // Go 표준 라이브러리
IMPORT "database/sql" // 데이터베이스
IMPORT "net/http" // HTTP 서버/클라이언트
IMPORT "encoding/json" // JSON
IMPORT _ "modernc.org/sqlite" // blank import (드라이버 등록용)
IMPORT myhttp "net/http" // 별칭 import
```
PRG 파일 최상단에 선언. gengo가 Go import로 직접 변환.
## 2. 패키지 함수 호출 — `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
```
### 동작 원리
```
PRG: strings.ToUpper("hello")
↓ gengo
Go: hbrt.GoCallFunc(strings.ToUpper, _arg0)
```
- gengo가 IMPORT된 패키지 이름을 인식
- `pkg.Func(args)``hbrt.GoCallFunc()` reflect 호출로 변환
- 반환값은 자동으로 Harbour Value로 변환
### 자동 타입 변환
| Go 타입 | → Harbour 타입 |
|---------|---------------|
| `string` | String |
| `int`, `int64` | Numeric (Integer/Long) |
| `float64` | Numeric (Double) |
| `bool` | Logical |
| `[]string`, `[]int` 등 | Array |
| `map[string]interface{}` | Hash |
| `error` (nil) | NIL |
| `error` (non-nil) | String (에러 메시지) |
| `*sql.DB` 등 포인터 | Go Object (Value로 래핑) |
## 3. Go 객체 메서드 호출 — `obj:Method()`
Go 함수가 반환한 객체(포인터)는 Harbour의 `:` 문법으로 메서드 호출:
```prg
IMPORT "database/sql"
IMPORT _ "modernc.org/sqlite"
PROCEDURE Main()
LOCAL db, rows
db := sql.Open("sqlite", ":memory:") // *sql.DB 반환
db:Exec("CREATE TABLE test (id INTEGER)") // *sql.DB.Exec() 호출
db:Exec("INSERT INTO test VALUES (1)")
rows := db:Query("SELECT * FROM test") // *sql.Rows 반환
DO WHILE rows:Next() // *sql.Rows.Next()
? rows:Column(1) // 컬럼 값 읽기
ENDDO
rows:Close() // *sql.Rows.Close()
db:Close() // *sql.DB.Close()
RETURN
```
### 동작 원리
```
PRG: db:Exec("CREATE TABLE ...")
↓ gengo
Go: if hbrt.IsGoObject(_obj) {
hbrt.GoCall(_obj, "Exec", _args...) // reflect 호출
} else {
t.Send("Exec", 1) // Harbour 객체 호출
}
```
- 런타임에 Go 객체 vs Harbour 객체 자동 판별
- Go 객체: `reflect.MethodByName()` 으로 호출
- Harbour 객체: 기존 `Send()` 메커니즘
## 4. 여러 Go 객체 동시 사용
```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")
// Source에서 읽어서 Target에 쓰기
aRows := SqlScan(dbSource, "SELECT * FROM products")
FOR i := 1 TO Len(aRows)
dbTarget:Exec("INSERT INTO inventory VALUES (...)")
NEXT
dbSource:Close()
dbTarget:Close()
RETURN
// PRG 함수에서 Go 객체를 파라미터로 받아 사용
FUNCTION SqlScan(db, cSQL)
LOCAL rows, cols, aResult, aRow, i, nCols
aResult := {}
rows := db:Query(cSQL) // Go *sql.Rows 반환
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. 배열 반환 처리
Go 함수가 슬라이스를 반환하면 자동으로 Harbour 배열로 변환:
```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 — 고급 사용 (선택)
복잡한 Go 로직이 필요한 경우에만 사용:
```prg
PROCEDURE Main()
? MyGoFunc("hello")
RETURN
#pragma BEGINDUMP
import "five/hbrt"
func init() {
hbrt.HB_FUNC("MYGOFUNC", func(ctx *hbrt.HBContext) {
// 복잡한 Go 로직
s := ctx.ParC(1)
ctx.RetC(strings.ToUpper(s) + "!!!")
})
}
#pragma ENDDUMP
```
### HB_FUNC API (Harbour C API 호환)
| Harbour C | Five Go | 설명 |
|-----------|---------|------|
| `HB_FUNC(NAME)` | `hbrt.HB_FUNC("NAME", fn)` | 함수 등록 |
| `hb_pcount()` | `ctx.PCount()` | 파라미터 수 |
| `hb_parc(n)` | `ctx.ParC(n)` | 문자열 파라미터 |
| `hb_parni(n)` | `ctx.ParNI(n)` | 정수 파라미터 |
| `hb_parnl(n)` | `ctx.ParNL(n)` | Long 파라미터 |
| `hb_parnd(n)` | `ctx.ParND(n)` | Double 파라미터 |
| `hb_parl(n)` | `ctx.ParL(n)` | 논리값 |
| `hb_pards(n)` | `ctx.ParDS(n)` | 날짜 (YYYYMMDD) |
| `hb_pardl(n)` | `ctx.ParDL(n)` | 날짜 (Julian) |
| `HB_ISCHAR(n)` | `ctx.IsChar(n)` | 타입 체크 |
| `HB_ISNUM(n)` | `ctx.IsNum(n)` | 타입 체크 |
| `HB_ISLOG(n)` | `ctx.IsLog(n)` | 타입 체크 |
| `HB_ISARRAY(n)` | `ctx.IsArray(n)` | 타입 체크 |
| `HB_ISNIL(n)` | `ctx.IsNil(n)` | 타입 체크 |
| `hb_retc(s)` | `ctx.RetC(s)` | 문자열 반환 |
| `hb_retni(n)` | `ctx.RetNI(n)` | 정수 반환 |
| `hb_retnl(n)` | `ctx.RetNL(n)` | Long 반환 |
| `hb_retnd(d)` | `ctx.RetND(d)` | Double 반환 |
| `hb_retl(b)` | `ctx.RetL(b)` | 논리값 반환 |
| `hb_retds(s)` | `ctx.RetDS(s)` | 날짜 반환 |
| `hb_storc(s,n)` | `ctx.StorC(s,n)` | By-ref 저장 |
| `hb_storni(v,n)` | `ctx.StorNI(v,n)` | By-ref 저장 |
| `hb_arrayNew()` | `ctx.ArrayNew(n)` | 배열 생성 |
| `hb_arrayGet()` | `ctx.ArrayGet(v,i)` | 배열 읽기 |
| `hb_arraySet()` | `ctx.ArraySet(v,i,x)` | 배열 쓰기 |
| `hb_hashNew()` | `ctx.HashNew()` | 해시 생성 |
### Five 확장 API (Harbour에 없는 Go 전용)
| API | 설명 |
|-----|------|
| `ctx.ParDate(n)` | `time.Time` 반환 |
| `ctx.ParArray(n)` | `[]Value` 반환 |
| `ctx.ParHash(n)` | `*HbHash` 반환 |
| `ctx.RetArray(items)` | 배열 반환 |
| `ctx.RetHash(h)` | 해시 반환 |
| `ctx.RetVal(v)` | 임의 Value 반환 |
| `hbrt.WrapGo(obj)` | Go 객체 → Value |
| `hbrt.UnwrapGo(v)` | Value → Go 객체 |
| `hbrt.GoCall(v, method, args...)` | reflect 메서드 호출 |
## 7. 사용 가능한 Go 패키지 예시
| 패키지 | PRG 사용 예 |
|--------|-------------|
| `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 서버, 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` | 해시 함수 |
| `regexp` | 정규식 |
| `sort` | 정렬 |
| 외부 패키지 | `modernc.org/sqlite`, `github.com/...` 등 |
## 8. 핵심 원칙
1. **IMPORT만으로 사용**`#pragma BEGINDUMP` 불필요
2. **PRG 코드 100%** — Go 코드 0줄로 Go 기능 사용
3. **자동 타입 변환** — string/int/bool/array/hash 양방향
4. **Go 객체 투명 전달** — LOCAL 변수에 저장, `:` 로 메서드 호출
5. **Harbour 호환** — 기존 xBase 문법 그대로, Go는 백엔드