DBFArea auto-manages FPT memo files: - Create/Open: auto-creates/opens FPT when memo fields exist - PutValue: string on MEMO field auto-writes to FPT - GetValue: MEMO field auto-reads from FPT, returns string - Close: auto-closes FPT Documentation: Value methods, MEMVAR, SET, ErrorBlock, MEMO added to five-syntax-ko.md and five-syntax-en.md (+480 lines) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
18 KiB
18 KiB
Five Language Syntax Reference
Five = Harbour 100% 호환 + Go 확장 문법. 기존 PRG 코드 수정 없이 실행되고, Go의 강력한 기능을 PRG 문법으로 사용.
Harbour 호환 문법 (98% 파싱)
기존 Harbour/Clipper/xBase 문법 전체 지원:
FUNCTION, PROCEDURE, RETURN, LOCAL, STATIC, PRIVATE, PUBLIC
IF/ELSEIF/ELSE/ENDIF, DO CASE/CASE/OTHERWISE/ENDCASE
FOR/NEXT, FOR EACH/NEXT, DO WHILE/ENDDO
BEGIN SEQUENCE/RECOVER/END, SWITCH/CASE/ENDSWITCH
CLASS/DATA/METHOD/ACCESS/ASSIGN/ENDCLASS
USE, SELECT, SEEK, SKIP, GO, APPEND, REPLACE, DELETE, PACK
@ SAY/GET/READ, MENU TO, SET, INDEX ON
Five Go 확장 문법
1. IMPORT — Go 패키지 직접 사용
IMPORT "strings" // Go 표준 라이브러리
IMPORT "database/sql" // SQL 데이터베이스
IMPORT _ "modernc.org/sqlite" // blank import (드라이버)
IMPORT myhttp "net/http" // 별칭 import
IMPORT하면 PRG에서 바로 사용:
IMPORT "strings"
PROCEDURE Main()
LOCAL cResult
cResult := strings.ToUpper("hello five") // Go 함수 직접 호출
? strings.Contains(cResult, "FIVE") // .T.
? strings.Split("a,b,c", ",") // {"a","b","c"}
RETURN
#pragma BEGINDUMP 불필요. IMPORT만으로 Go 전체 생태계 접근.
2. Multi-Return — 다중 반환값
// 함수에서 여러 값 반환
FUNCTION GetUserInfo()
RETURN "Charles", 30, "Seoul"
// 받는 쪽
cName, nAge, cCity := GetUserInfo()
// 불필요한 값 무시
_, nAge, _ := GetUserInfo()
Go의 (val, error) 패턴 자연스럽게 지원:
IMPORT "database/sql"
db, err := sql.Open("sqlite", ":memory:")
IF err != NIL
? "Error:", err
ENDIF
3. DEFER — 자동 정리
함수가 끝날 때 자동 실행. 에러가 나도 보장.
PROCEDURE ProcessFile(cPath)
LOCAL db
db := sql.Open("sqlite", cPath)
DEFER db:Close() // 함수 끝나면 자동 Close
db:Exec("INSERT ...") // 여기서 에러 나도
db:Exec("UPDATE ...") // db:Close()는 반드시 실행
RETURN // ← 여기서 DEFER 실행
Harbour의 BEGIN SEQUENCE/RECOVER보다 간결:
Before (Harbour): After (Five):
─────────────────────────────── ─────────────────────
BEGIN SEQUENCE db := SqlOpen(...)
db := SqlOpen(...) DEFER db:Close()
db:Exec(...) db:Exec(...)
RECOVER RETURN
db:Close()
END SEQUENCE
db:Close()
4. Slice — 부분 배열/문자열
LOCAL aData := {"a", "b", "c", "d", "e"}
aSub := aData[2:4] // {"b", "c", "d"}
aSub := aData[3:] // {"c", "d", "e"} (3번부터 끝까지)
aSub := aData[:2] // {"a", "b"} (처음부터 2번까지)
Harbour의 반복 루프 대체:
Before: After:
─────────────────────────────── ─────────────────────
LOCAL aSub := {} aSub := aData[3:7]
FOR i := 3 TO 7
AAdd(aSub, aData[i])
NEXT
5. Parallel Assignment — 동시 할당
// 값 교환 (swap)
a, b := b, a // temp 변수 불필요!
// 동시 초기화
x, y, z := 1, 2, 3
6. Nil-Safe Operator — ?:
// 기존: NIL 체크 반복
IF oCustomer != NIL
IF oCustomer:Address != NIL
? oCustomer:Address:City
ENDIF
ENDIF
// Five: 한 줄로
? oCustomer?:Address?:City // NIL이면 NIL 반환, 에러 없음
7. String Interpolation — f"..."
LOCAL cName := "Charles", nAge := 30
// 기존
? "Name: " + cName + " Age: " + Str(nAge)
// Five
? f"Name: {cName}, Age: {nAge}"
// 포맷 지정
? f"Price: {nPrice:.2f}, Count: {nCount:05d}"
8. CONST Block — 상수/열거형
CONST
STATUS_ACTIVE := 1
STATUS_CLOSED := 2
STATUS_PENDING := 3
END CONST
9. SWITCH (Harbour 호환 + 확장)
// 기존 Harbour 문법 그대로 동작
SWITCH nStatus
CASE 1
? "Active"
CASE 2
? "Closed"
OTHERWISE
? "Unknown"
ENDSWITCH
Five 동시성 문법
10. 채널 연산자 — <-
ch := Channel()
ch <- "hello" // 채널로 전송 (send)
msg := <- ch // 채널에서 수신 (receive)
Harbour 함수 vs Five 연산자:
Harbour 함수: Five 연산자:
─────────────────────────────── ─────────────────────
ChSend(ch, "hello") ch <- "hello"
msg := ChReceive(ch) msg := <- ch
ChSend(chOut, nResult) chOut <- nResult
11. SPAWN / LAUNCH / GOROUTINE — 인라인 goroutine
// 3가지 키워드, 같은 동작
SPAWN {|| DoHeavyWork() }
LAUNCH {|| ProcessData() }
GOROUTINE {|| SendNotification() }
12. WATCH — 채널 멀티플렉싱 (Go select)
여러 채널을 동시 감시, 먼저 준비된 채널 처리:
WATCH
CASE msg := <- chMessages // 메시지 도착
? "Message:", msg
CASE result := <- chResults // 결과 도착
? "Result:", result
CASE <- chTimeout // 타임아웃
? "Timeout!"
OTHERWISE // 아무 채널도 준비 안 됨
? "No channel ready"
END WATCH
실전 패턴: 가장 빠른 서버 응답 선택
SPAWN {|| DelayAndSend(0.1, chFast, "Fast Server") }
SPAWN {|| DelayAndSend(2.0, chSlow, "Slow Server") }
SPAWN {|| DelayAndSend(3.0, chTimeout, "TIMEOUT") }
WATCH
CASE cResult := <- chFast
? "Winner:", cResult // ← 100ms로 가장 빨라서 선택됨
CASE cResult := <- chSlow
? "Winner:", cResult
CASE <- chTimeout
? "Timeout!"
END WATCH
13. PARALLEL FOR — 병렬 루프
// 10만 건을 CPU 코어 수만큼 병렬 처리
PARALLEL FOR i := 1 TO 100000
aResult[i] := ProcessItem(aData[i])
NEXT
// 자동으로 모든 goroutine 완료 대기
14. ASYNC / AWAIT — 비동기 실행
// 무거운 작업을 백그라운드에서 시작
future := ASYNC HeavyQuery("SELECT * FROM big_table")
// 다른 작업 수행 (비동기)
? "Loading..."
PrepareUI()
// 결과 대기
aRows := AWAIT future
? "Got", Len(aRows), "rows"
15. WITH TIMEOUT — 타임아웃 컨텍스트
// 3초 안에 완료되지 않으면 자동 취소
WITH TIMEOUT 3
result := SlowNetworkCall()
END
IF result == NIL
? "Timeout!"
ENDIF
Go 객체 직접 조작
pkg.Func() — 패키지 함수 호출
IMPORT "strings"
IMPORT "math"
IMPORT "fmt"
? strings.ToUpper("hello") // "HELLO"
? math.Sqrt(144) // 12
? fmt.Sprintf("%.2f", 3.14159) // "3.14"
obj:Method() — Go 객체 메서드 호출
IMPORT "database/sql"
db := sql.Open("sqlite", ":memory:")
db:Exec("CREATE TABLE test (id INTEGER)")
rows := db:Query("SELECT * FROM test")
DO WHILE rows:Next()
? rows:Column(1)
END
rows:Close()
db:Close()
여러 Go 객체 동시 사용
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()
Five vs 경쟁 언어
xBase 계열 비교
| 기능 | Harbour | xHarbour | FiveWin | Five |
|---|---|---|---|---|
| DBF/NTX/CDX | ✅ | ✅ | ✅ | ✅ |
| SQL Database | ❌ | 제한적 | ODBC | ✅ 모든 Go DB |
| HTTP Server | ❌ | ❌ | ❌ | ✅ net/http |
| WebSocket | ❌ | ❌ | ❌ | ✅ |
| Goroutine | ❌ | ❌ | ❌ | ✅ 네이티브 |
| Channel | ❌ | ❌ | ❌ | ✅ <- 연산자 |
| JSON | 제한적 | 제한적 | 제한적 | ✅ Go encoding/json |
| 크로스플랫폼 | △ | △ | Windows | ✅ Linux/Mac/Windows |
| 패키지 생태계 | C lib | C lib | C lib | ✅ Go 전체 |
다른 트랜스파일러 비교
| 기능 | TypeScript→JS | Kotlin→JVM | Five (PRG→Go) |
|---|---|---|---|
| 타입 시스템 | 정적→동적 | 정적→정적 | 동적→정적 |
| 동시성 | async/await | coroutine | goroutine+channel |
| 외부 패키지 | npm | Maven | Go modules |
| 컴파일 결과 | JS 코드 | bytecode | 네이티브 바이너리 |
| 인터롭 | JS 직접 | Java 직접 | Go 직접 (IMPORT) |
| 성능 | V8 런타임 | JVM 런타임 | 네이티브 속도 |
Five만의 차별점
- IMPORT만으로 Go 생태계 전체 접근 — #pragma BEGINDUMP 불필요
- 네이티브 바이너리 — JVM, V8 없이 단일 실행 파일
- goroutine + channel + WATCH — PRG 문법으로 Go 동시성 100%
- xBase 100% 호환 — 기존 DBF/NTX/CDX 코드 그대로 실행
- FastPath 최적화 — Go 함수 호출이 native의 2x 이내 성능
- DEFER — 리소스 안전 관리, BEGIN SEQUENCE보다 간결
- Multi-Return —
a, b := Func(), Go의 (val, error) 패턴 - f-string — 문자열 보간,
f"Hello {name}" - PARALLEL FOR — 대량 데이터 자동 병렬 처리
- Nil-safe
?:— 안전한 체이닝, 런타임 에러 방지
수학: Harbour RTL + Go math 동시 사용
IMPORT "math"
PROCEDURE Main()
// Harbour RTL — 간단, IMPORT 불필요
? Abs(-42.5) // 42.5
? Sqrt(144) // 12
? Round(3.14159, 2) // 3.14
// Go math — 완전, 60개+ 함수
? math.Sin(math.Pi / 6) // 0.5
? math.Pow(2, 10) // 1024
? math.Hypot(3, 4) // 5
// 조합 사용
? (1 / Sqrt(2 * math.Pi)) * Exp(-0.5 * math.Pow(0, 2))
RETURN
| Harbour RTL | Go math | |
|---|---|---|
| 함수 수 | 9개 | 60개+ |
| IMPORT | 불<EFBFBD><EFBFBD><EFBFBD>요 | IMPORT "math" |
| 삼각함수 | 없음 | Sin, Cos, Tan, ... |
| 상수 | 없음 | Pi, E, Phi, ... |
Value 타입 메서드 (Five 확장)
Five는 기본 타입에 52개의 내장 메서드를 지원합니다. 체이닝 가능:
String 메서드 (20개)
LOCAL cStr := " Hello World "
? cStr:Trim() // "Hello World"
? cStr:Upper() // " HELLO WORLD "
? cStr:Lower() // " hello world "
? cStr:Left(7) // " Hello"
? cStr:Right(7) // "orld "
? cStr:SubStr(3, 5) // "Hello"
? cStr:At("World") // 9
? cStr:Len() // 15
? cStr:Replicate(2) // " Hello World Hello World "
? cStr:Reverse() // " dlroW olleH "
? cStr:IsAlpha() // .F. (starts with space)
? cStr:IsDigit() // .F.
? cStr:IsEmpty() // .F.
? cStr:Trim():Upper():Left(5) // "HELLO" — chaining
Array 메서드 (14개)
LOCAL aList := {3, 1, 4, 1, 5}
? aList:Len() // 5
? aList:Sort() // {1, 1, 3, 4, 5}
? aList:Find(4) // 3 (1-based)
? aList:Push(9) // {1,1,3,4,5,9}
? aList:Pop() // 9
? aList:First() // 1
? aList:Last() // 5
? aList:Join(",") // "1,1,3,4,5"
? aList:Reverse() // {5,4,3,1,1}
? aList:Unique() // {5,4,3,1}
? aList:Slice(2, 4) // {4,3}
// Map/Filter/Each with code blocks
LOCAL aDoubled := {1,2,3}:Map({|x| x * 2}) // {2,4,6}
LOCAL aEven := {1,2,3,4}:Filter({|x| x % 2 == 0}) // {2,4}
{1,2,3}:Each({|x| QOut(x)}) // prints each
Numeric 메서드 (6개)
LOCAL nVal := 3.14159
? nVal:Round(2) // 3.14
? nVal:Abs() // 3.14159
? nVal:Int() // 3
? nVal:Str(10, 4) // " 3.1416"
? nVal:IsZero() // .F.
? (-5):Abs() // 5
Hash 메서드 (7개)
LOCAL hData := {"name" => "Charles", "age" => 30}
? hData:Keys() // {"name","age"}
? hData:Values() // {"Charles",30}
? hData:Len() // 2
? hData:HasKey("name") // .T.
? hData:Remove("age") // {"name" => "Charles"}
? hData:Merge({"city" => "Seoul"})
Any 타입 메서드 (5개)
LOCAL xVal := "hello"
? xVal:Type() // "C"
? xVal:Clone() // deep copy
? xVal:IsNil() // .F.
? xVal:ToString() // "hello"
? xVal:ValType() // "C"
MEMVAR — PUBLIC/PRIVATE 변수
Harbour 호환 메모리 변수 시스템. PUBLIC은 전역, PRIVATE은 함수 스코프.
// PUBLIC — 프로그램 전체에서 접근
PUBLIC gAppName
gAppName := "Five Application"
PROCEDURE Main()
LOCAL cLocal := "local only"
// PRIVATE — 현재 함수 + 하위 함수에서 접근, 리턴 시 복원
PRIVATE nTemp := 100
SubFunc()
? nTemp // 100 (SubFunc의 PRIVATE이 복원됨)
? gAppName // "Five Application" (PUBLIC)
RETURN
PROCEDURE SubFunc()
PRIVATE nTemp := 999 // shadows caller's nTemp
? nTemp // 999
RETURN // nTemp restored to 100
MEMVAR 스코프 규칙
| 종류 | 수명 | 가시성 | 섀도잉 |
|---|---|---|---|
| PUBLIC | 프로그램 종료까지 | 전체 | PRIVATE이 숨길 수 있음 |
| PRIVATE | 선언 함수 리턴까지 | 선언 함수 + 하위 | 중첩 PRIVATE 가능 |
| LOCAL | 선언 함수 리턴까지 | 선언 함수만 | MEMVAR와 독립 |
| STATIC | 프로그램 종료까지 | 선언 함수만 | MEMVAR와 독립 |
매크로에서 MEMVAR 접근
PUBLIC cName := "Charles"
LOCAL cVar := "cName"
? &cVar // "Charles" — 매크로가 MEMVAR 검색
SET 명령어 시스템
Harbour 호환 SET 설정. 47+ 설정 지원:
// Boolean 토글
SET EXACT ON // 문자열 완전 일치 비교
SET DELETED ON // 삭제 레코드 숨김
SET SOFTSEEK ON // SEEK 실패 시 가장 가까운 레코드
SET EXCLUSIVE OFF // 공유 모드
SET CONFIRM ON // GET 입력 시 확인 필요
// 값 설정
SET DATE FORMAT "yyyy-mm-dd" // 날짜 형식
SET DECIMALS TO 4 // 소수점 자릿수
SET EPOCH TO 2000 // 2자리 년도 해석 기준
// SET() 함수로 프로그래밍 방식 접근
LOCAL lOld := SET(_SET_EXACT, .T.) // 설정하고 이전 값 반환
? SET(_SET_EXACT) // .T.
SET 상수
_SET_EXACT // 1 문자열 정확 비교
_SET_FIXED // 2 고정 소수점
_SET_DECIMALS // 3 소수점 자릿수
_SET_DATEFORMAT // 4 날짜 형식
_SET_EPOCH // 5 년도 기준
_SET_DELETED // 8 삭제 레코드 필터
_SET_EXCLUSIVE // 11 독점 모드
_SET_SOFTSEEK // 12 소프트 검색
ErrorBlock / Break — 에러 처리
Harbour 호환 구조적 에러 처리:
BEGIN SEQUENCE / RECOVER
LOCAL bOldError
LOCAL oErr
// 에러 핸들러 설정
bOldError := ErrorBlock({|e| Break(e)})
BEGIN SEQUENCE
// 에러가 발생할 수 있는 코드
USE "nonexistent.dbf"
RECOVER USING oErr
// oErr는 에러 객체 (Hash)
? oErr["DESCRIPTION"] // 에러 설명
? oErr["OPERATION"] // 실패한 연산
? oErr["SUBSYSTEM"] // 서브시스템 이름
? oErr["GENCODE"] // 일반 에러 코드
END SEQUENCE
// 이전 핸들러 복원
ErrorBlock(bOldError)
ErrorBlock
// 현재 에러 핸들러 가져오기
LOCAL bHandler := ErrorBlock()
// 새 핸들러 설정 (이전 핸들러 반환)
LOCAL bOld := ErrorBlock({|e| MyErrorHandler(e)})
FUNCTION MyErrorHandler(oErr)
? "Error:", oErr["DESCRIPTION"]
? "Operation:", oErr["OPERATION"]
BREAK oErr // BEGIN SEQUENCE의 RECOVER로 전달
RETURN NIL
ErrorNew
LOCAL oErr := ErrorNew()
oErr["SUBSYSTEM"] := "MYAPP"
oErr["DESCRIPTION"] := "Custom error"
oErr["OPERATION"] := "MyFunc"
oErr["GENCODE"] := 1001
oErr["SEVERITY"] := 2 // ES_ERROR
MEMO 필드 — 투명한 읽기/쓰기
Five는 DBF의 MEMO 필드를 투명하게 처리합니다. FPT 파일이 자동으로 생성/열림:
// MEMO 필드가 있는 테이블 생성 — FPT 자동 생성
USE "notes" NEW
APPEND BLANK
REPLACE NAME WITH "Charles"
REPLACE NOTES WITH "This is a long memo text..." // FPT에 자동 저장
? NOTES // "This is a long memo text..." — FPT에서 자동 읽기
// 큰 메모도 문제없음
REPLACE NOTES WITH REPLICATE("Large data. ", 1000) // ~12KB
? LEN(NOTES) // 12000
MEMO 내부 동작
| 동작 | 자동 처리 |
|---|---|
| DBF 생성 (M 필드 포함) | FPT 파일 자동 생성 |
| DBF 열기 (M 필드 포함) | FPT 파일 자동 열기 |
| REPLACE memo WITH text | FPT에 쓰기 → 블록 번호 DBF에 저장 |
| ? memo | 블록 번호로 FPT 읽기 → 문자열 반환 |
| DBF 닫기 | FPT 자동 닫기 |
예제 파일
| 파일 | 설명 |
|---|---|
examples/go_native.prg |
IMPORT만으로 Go 패키지 직접 사용 |
examples/go_strings.prg |
strings 패키지 전체 활용 |
examples/go_typetest.prg |
18가지 타입 변환 테스트 |
examples/go_dual_db.prg |
두 SQLite DB 동시 사용 |
examples/go_channel.prg |
채널 연산자 + WATCH + Pipeline |
examples/go_httpserver.prg |
REST API 서버 |
examples/go_concurrent.prg |
병렬 데이터 파이프라인 |
examples/go_websocket.prg |
WebSocket 채팅 서버 |
examples/go_extensions.prg |
9가지 확장 문법 데모 |
examples/go_math_compare.prg |
Harbour RTL vs Go math 비교 |
examples/godump_demo.prg |
HB_FUNC Go API |