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

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. 핵심 원칙

  1. IMPORT만으로 사용#pragma BEGINDUMP 불필요
  2. PRG 코드 100% — Go 코드 0줄로 Go 기능 사용
  3. 자동 타입 변환 — string/int/bool/array/hash 양방향
  4. Go 객체 투명 전달 — LOCAL 변수에 저장, : 로 메서드 호출
  5. Harbour 호환 — 기존 xBase 문법 그대로, Go는 백엔드