- 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>
7.8 KiB
7.8 KiB
Five Go Interop — PRG에서 Go 패키지 직접 사용
Five의 핵심 차별점: PRG 코드에서 Go의 전체 패키지 생태계를 직접 사용.
1. IMPORT — Go 패키지 가져오기
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()
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의 : 문법으로 메서드 호출:
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 객체 동시 사용
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 배열로 변환:
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 로직이 필요한 경우에만 사용:
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. 핵심 원칙
- IMPORT만으로 사용 —
#pragma BEGINDUMP불필요 - PRG 코드 100% — Go 코드 0줄로 Go 기능 사용
- 자동 타입 변환 — string/int/bool/array/hash 양방향
- Go 객체 투명 전달 — LOCAL 변수에 저장,
:로 메서드 호출 - Harbour 호환 — 기존 xBase 문법 그대로, Go는 백엔드