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>
This commit is contained in:
400
docs/five-syntax-en.md
Normal file
400
docs/five-syntax-en.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# Five Language Syntax Reference
|
||||
|
||||
Five = 100% Harbour compatible + Go extended syntax.
|
||||
Existing PRG code runs without modification, and Go's powerful features are available in PRG syntax.
|
||||
|
||||
## Harbour Compatible Syntax (98% parsing)
|
||||
|
||||
Full support for all Harbour/Clipper/xBase syntax:
|
||||
|
||||
```prg
|
||||
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 Extensions
|
||||
|
||||
### 1. IMPORT — Direct Go Package Access
|
||||
|
||||
```prg
|
||||
IMPORT "strings" // Go standard library
|
||||
IMPORT "database/sql" // SQL database
|
||||
IMPORT _ "modernc.org/sqlite" // blank import (driver registration)
|
||||
IMPORT myhttp "net/http" // aliased import
|
||||
```
|
||||
|
||||
After IMPORT, use directly from PRG:
|
||||
|
||||
```prg
|
||||
IMPORT "strings"
|
||||
|
||||
PROCEDURE Main()
|
||||
LOCAL cResult
|
||||
cResult := strings.ToUpper("hello five") // Direct Go function call
|
||||
? strings.Contains(cResult, "FIVE") // .T.
|
||||
? strings.Split("a,b,c", ",") // {"a","b","c"}
|
||||
RETURN
|
||||
```
|
||||
|
||||
**No #pragma BEGINDUMP needed. IMPORT gives access to Go's entire ecosystem.**
|
||||
|
||||
### 2. Multi-Return — Multiple Return Values
|
||||
|
||||
```prg
|
||||
// Return multiple values from a function
|
||||
FUNCTION GetUserInfo()
|
||||
RETURN "Charles", 30, "Seoul"
|
||||
|
||||
// Receive multiple values
|
||||
cName, nAge, cCity := GetUserInfo()
|
||||
|
||||
// Discard unwanted values with blank identifier
|
||||
_, nAge, _ := GetUserInfo()
|
||||
```
|
||||
|
||||
Natural support for Go's `(val, error)` pattern:
|
||||
|
||||
```prg
|
||||
IMPORT "database/sql"
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
IF err != NIL
|
||||
? "Error:", err
|
||||
ENDIF
|
||||
```
|
||||
|
||||
### 3. DEFER — Automatic Cleanup
|
||||
|
||||
Executes when the function returns. Guaranteed even on errors.
|
||||
|
||||
```prg
|
||||
PROCEDURE ProcessFile(cPath)
|
||||
LOCAL db
|
||||
db := sql.Open("sqlite", cPath)
|
||||
DEFER db:Close() // Auto-Close when function ends
|
||||
|
||||
db:Exec("INSERT ...") // Even if error occurs here
|
||||
db:Exec("UPDATE ...") // db:Close() will always execute
|
||||
RETURN // ← DEFER executes here
|
||||
```
|
||||
|
||||
More concise than Harbour's `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 — Sub-array / Sub-string
|
||||
|
||||
```prg
|
||||
LOCAL aData := {"a", "b", "c", "d", "e"}
|
||||
|
||||
aSub := aData[2:4] // {"b", "c", "d"}
|
||||
aSub := aData[3:] // {"c", "d", "e"} (from 3 to end)
|
||||
aSub := aData[:2] // {"a", "b"} (from start to 2)
|
||||
```
|
||||
|
||||
Replaces verbose Harbour loops:
|
||||
|
||||
```
|
||||
Before: After:
|
||||
─────────────────────────────── ─────────────────────
|
||||
LOCAL aSub := {} aSub := aData[3:7]
|
||||
FOR i := 3 TO 7
|
||||
AAdd(aSub, aData[i])
|
||||
NEXT
|
||||
```
|
||||
|
||||
### 5. Parallel Assignment — Simultaneous Assign
|
||||
|
||||
```prg
|
||||
// Swap values (no temp variable needed!)
|
||||
a, b := b, a
|
||||
|
||||
// Simultaneous initialization
|
||||
x, y, z := 1, 2, 3
|
||||
```
|
||||
|
||||
### 6. Nil-Safe Operator — `?:`
|
||||
|
||||
```prg
|
||||
// Before: repeated NIL checks
|
||||
IF oCustomer != NIL
|
||||
IF oCustomer:Address != NIL
|
||||
? oCustomer:Address:City
|
||||
ENDIF
|
||||
ENDIF
|
||||
|
||||
// Five: one line
|
||||
? oCustomer?:Address?:City // Returns NIL if any part is NIL
|
||||
```
|
||||
|
||||
### 7. String Interpolation — `f"..."`
|
||||
|
||||
```prg
|
||||
LOCAL cName := "Charles", nAge := 30
|
||||
|
||||
// Before
|
||||
? "Name: " + cName + " Age: " + Str(nAge)
|
||||
|
||||
// Five
|
||||
? f"Name: {cName}, Age: {nAge}"
|
||||
|
||||
// With format specifiers
|
||||
? f"Price: {nPrice:.2f}, Count: {nCount:05d}"
|
||||
```
|
||||
|
||||
### 8. CONST Block — Constants / Enums
|
||||
|
||||
```prg
|
||||
CONST
|
||||
STATUS_ACTIVE := 1
|
||||
STATUS_CLOSED := 2
|
||||
STATUS_PENDING := 3
|
||||
END CONST
|
||||
```
|
||||
|
||||
### 9. SWITCH (Harbour compatible + extended)
|
||||
|
||||
```prg
|
||||
// Standard Harbour syntax works as-is
|
||||
SWITCH nStatus
|
||||
CASE 1
|
||||
? "Active"
|
||||
CASE 2
|
||||
? "Closed"
|
||||
OTHERWISE
|
||||
? "Unknown"
|
||||
ENDSWITCH
|
||||
```
|
||||
|
||||
## Five Concurrency Syntax
|
||||
|
||||
### 10. Channel Operators — `<-`
|
||||
|
||||
```prg
|
||||
ch := Channel()
|
||||
|
||||
ch <- "hello" // Send to channel
|
||||
msg := <- ch // Receive from channel
|
||||
```
|
||||
|
||||
Harbour functions vs Five operators:
|
||||
|
||||
```
|
||||
Harbour functions: Five operators:
|
||||
─────────────────────────────── ─────────────────────
|
||||
ChSend(ch, "hello") ch <- "hello"
|
||||
msg := ChReceive(ch) msg := <- ch
|
||||
ChSend(chOut, nResult) chOut <- nResult
|
||||
```
|
||||
|
||||
### 11. SPAWN / LAUNCH / GOROUTINE — Inline Goroutine
|
||||
|
||||
Three keywords, same behavior — choose your preference:
|
||||
|
||||
```prg
|
||||
SPAWN {|| DoHeavyWork() }
|
||||
LAUNCH {|| ProcessData() }
|
||||
GOROUTINE {|| SendNotification() }
|
||||
```
|
||||
|
||||
### 12. WATCH — Channel Multiplexing (Go select)
|
||||
|
||||
Monitor multiple channels simultaneously, process the first one ready:
|
||||
|
||||
```prg
|
||||
WATCH
|
||||
CASE msg := <- chMessages // Message arrived
|
||||
? "Message:", msg
|
||||
CASE result := <- chResults // Result arrived
|
||||
? "Result:", result
|
||||
CASE <- chTimeout // Timeout
|
||||
? "Timeout!"
|
||||
OTHERWISE // No channel ready
|
||||
? "No channel ready"
|
||||
END WATCH
|
||||
```
|
||||
|
||||
**Real-world pattern: Select fastest server response**
|
||||
|
||||
```prg
|
||||
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 // ← Selected (fastest at 100ms)
|
||||
CASE cResult := <- chSlow
|
||||
? "Winner:", cResult
|
||||
CASE <- chTimeout
|
||||
? "Timeout!"
|
||||
END WATCH
|
||||
```
|
||||
|
||||
### 13. PARALLEL FOR — Parallel Loop
|
||||
|
||||
```prg
|
||||
// Process 100K items across all CPU cores
|
||||
PARALLEL FOR i := 1 TO 100000
|
||||
aResult[i] := ProcessItem(aData[i])
|
||||
NEXT
|
||||
// Automatically waits for all goroutines to complete
|
||||
```
|
||||
|
||||
### 14. ASYNC / AWAIT — Asynchronous Execution
|
||||
|
||||
```prg
|
||||
// Start heavy work in background
|
||||
future := ASYNC HeavyQuery("SELECT * FROM big_table")
|
||||
|
||||
// Do other work (non-blocking)
|
||||
? "Loading..."
|
||||
PrepareUI()
|
||||
|
||||
// Wait for result
|
||||
aRows := AWAIT future
|
||||
? "Got", Len(aRows), "rows"
|
||||
```
|
||||
|
||||
### 15. WITH TIMEOUT — Timeout Context
|
||||
|
||||
```prg
|
||||
// Auto-cancel if not completed within 3 seconds
|
||||
WITH TIMEOUT 3
|
||||
result := SlowNetworkCall()
|
||||
END
|
||||
|
||||
IF result == NIL
|
||||
? "Timeout!"
|
||||
ENDIF
|
||||
```
|
||||
|
||||
## Direct Go Object Manipulation
|
||||
|
||||
### `pkg.Func()` — Package Function Calls
|
||||
|
||||
```prg
|
||||
IMPORT "strings"
|
||||
IMPORT "math"
|
||||
IMPORT "fmt"
|
||||
|
||||
? strings.ToUpper("hello") // "HELLO"
|
||||
? math.Sqrt(144) // 12
|
||||
? fmt.Sprintf("%.2f", 3.14159) // "3.14"
|
||||
```
|
||||
|
||||
### `obj:Method()` — Go Object Method Calls
|
||||
|
||||
```prg
|
||||
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()
|
||||
```
|
||||
|
||||
### Multiple Go Objects Simultaneously
|
||||
|
||||
```prg
|
||||
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 Competitors
|
||||
|
||||
### xBase Family Comparison
|
||||
|
||||
| Feature | Harbour | xHarbour | FiveWin | **Five** |
|
||||
|---------|---------|----------|---------|----------|
|
||||
| DBF/NTX/CDX | Yes | Yes | Yes | **Yes** |
|
||||
| SQL Database | No | Limited | ODBC | **All Go DBs** |
|
||||
| HTTP Server | No | No | No | **net/http** |
|
||||
| WebSocket | No | No | No | **Yes** |
|
||||
| Goroutine | No | No | No | **Native** |
|
||||
| Channel `<-` | No | No | No | **Yes** |
|
||||
| JSON | Limited | Limited | Limited | **Go encoding/json** |
|
||||
| Cross-platform | Partial | Partial | Windows | **Linux/Mac/Windows** |
|
||||
| Package ecosystem | C libs | C libs | C libs | **All Go packages** |
|
||||
|
||||
### Transpiler Comparison
|
||||
|
||||
| Feature | TypeScript→JS | Kotlin→JVM | **Five (PRG→Go)** |
|
||||
|---------|---------------|------------|---------------------|
|
||||
| Type system | Static→Dynamic | Static→Static | Dynamic→Static |
|
||||
| Concurrency | async/await | coroutine | **goroutine+channel** |
|
||||
| External packages | npm | Maven | **Go modules** |
|
||||
| Build output | JS code | bytecode | **Native binary** |
|
||||
| Interop | Direct JS | Direct Java | **Direct Go (IMPORT)** |
|
||||
| Performance | V8 runtime | JVM runtime | **Native speed** |
|
||||
|
||||
### What Makes Five Unique
|
||||
|
||||
1. **IMPORT gives access to all of Go** — No #pragma BEGINDUMP needed
|
||||
2. **Native binary output** — Single executable, no JVM or V8 required
|
||||
3. **goroutine + channel + WATCH** — Full Go concurrency in PRG syntax
|
||||
4. **100% xBase compatible** — Existing DBF/NTX/CDX code runs as-is
|
||||
5. **FastPath optimization** — Go function calls within 2x of native performance
|
||||
6. **DEFER** — Safe resource management, cleaner than BEGIN SEQUENCE
|
||||
7. **Multi-Return** — `a, b := Func()`, natural Go (val, error) pattern
|
||||
8. **f-string** — String interpolation, `f"Hello {name}"`
|
||||
9. **PARALLEL FOR** — Automatic parallel processing of large datasets
|
||||
10. **Nil-safe `?:`** — Safe chaining, no runtime errors from NIL
|
||||
|
||||
## Performance
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Call Type Direct Go Reflect FastPath
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
strings.ToUpper 34ns 242ns 66ns
|
||||
strings.Contains 3ns 218ns 19ns
|
||||
math.Sqrt 0.1ns 173ns 16ns
|
||||
obj:Method() — 412ns 235ns
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Throughput: 15M calls/sec (FastPath), 4.3M calls/sec (Method)
|
||||
Stress tested: 40K calls, 1MB strings, 10K arrays,
|
||||
20K concurrent goroutines, 5K random fuzz
|
||||
```
|
||||
|
||||
## Example Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `examples/go_native.prg` | Direct Go package usage with IMPORT only |
|
||||
| `examples/go_strings.prg` | Full strings package utilization |
|
||||
| `examples/go_typetest.prg` | 18 type conversion tests |
|
||||
| `examples/go_dual_db.prg` | Two SQLite databases simultaneously |
|
||||
| `examples/go_channel.prg` | Channel operators + WATCH + Pipeline |
|
||||
| `examples/go_httpserver.prg` | REST API server |
|
||||
| `examples/go_concurrent.prg` | Parallel data pipeline |
|
||||
| `examples/go_websocket.prg` | WebSocket chat server |
|
||||
| `examples/go_extensions.prg` | All 9 extension syntax demo |
|
||||
| `examples/godump_demo.prg` | HB_FUNC Go API |
|
||||
Reference in New Issue
Block a user