- 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>
205 lines
3.8 KiB
Go
205 lines
3.8 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
||
// All rights reserved.
|
||
|
||
// RTL goroutine functions for PRG code:
|
||
// GO(bBlock|cFunc, ...) — launch goroutine
|
||
// CHANNEL([nSize]) — create channel
|
||
// CHSEND(ch, xValue) — send to channel
|
||
// CHRECEIVE(ch) <20><><EFBFBD> receive from channel
|
||
// CHCLOSE(ch) — close channel
|
||
// WAITGROUP(nCount) — create WaitGroup
|
||
// WGDONE(wg) — WaitGroup.Done()
|
||
// WGWAIT(wg) — WaitGroup.Wait()
|
||
// WGADD(wg, n) — WaitGroup.Add(n)
|
||
// MUTEX() — create Mutex
|
||
// LOCK(mtx) — Mutex.Lock()
|
||
// UNLOCK(mtx) — Mutex.Unlock()
|
||
// SLEEP(nSeconds) — time.Sleep
|
||
|
||
package hbrtl
|
||
|
||
import (
|
||
"five/hbrt"
|
||
"time"
|
||
)
|
||
|
||
// GO(bBlock [, args...]) — launch goroutine with code block or function
|
||
// Returns NIL immediately (goroutine runs in background).
|
||
func GoFunc(t *hbrt.Thread) {
|
||
nParams := t.ParamCount()
|
||
t.Frame(nParams, 0)
|
||
defer t.EndProc()
|
||
|
||
first := t.Local(1)
|
||
|
||
// Collect args
|
||
args := make([]hbrt.Value, 0, nParams-1)
|
||
for i := 2; i <= nParams; i++ {
|
||
args = append(args, t.Local(i))
|
||
}
|
||
|
||
vm := t.VM()
|
||
|
||
if first.IsBlock() {
|
||
blk := first.AsBlock()
|
||
vm.GoLaunchBlock(blk, args)
|
||
} else if first.IsString() {
|
||
// Function name as string — find symbol and launch
|
||
sym := vm.FindSymbol(first.AsString())
|
||
if sym != nil && sym.Func != nil {
|
||
vm.GoLaunch(sym.Func, args)
|
||
}
|
||
}
|
||
|
||
t.RetNil()
|
||
}
|
||
|
||
// CHANNEL([nBufferSize]) → pChannel
|
||
func ChannelFunc(t *hbrt.Thread) {
|
||
nParams := t.ParamCount()
|
||
t.Frame(nParams, 0)
|
||
defer t.EndProc()
|
||
|
||
size := 0
|
||
if nParams >= 1 && !t.Local(1).IsNil() {
|
||
size = t.Local(1).AsInt()
|
||
}
|
||
t.RetVal(hbrt.MakeChannel(size))
|
||
}
|
||
|
||
// CHSEND(pChannel, xValue) → NIL
|
||
func ChSend(t *hbrt.Thread) {
|
||
t.Frame(2, 0)
|
||
defer t.EndProc()
|
||
|
||
ch := t.Local(1).AsChannel()
|
||
if ch == nil {
|
||
t.RetNil()
|
||
return
|
||
}
|
||
ch.Send(t.Local(2))
|
||
t.RetNil()
|
||
}
|
||
|
||
// CHRECEIVE(pChannel) → xValue
|
||
func ChReceive(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
ch := t.Local(1).AsChannel()
|
||
if ch == nil {
|
||
t.RetNil()
|
||
return
|
||
}
|
||
t.RetVal(ch.Receive())
|
||
}
|
||
|
||
// CHCLOSE(pChannel) → NIL
|
||
func ChClose(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
ch := t.Local(1).AsChannel()
|
||
if ch != nil {
|
||
ch.Close()
|
||
}
|
||
t.RetNil()
|
||
}
|
||
|
||
// WAITGROUP([nCount]) → pWaitGroup
|
||
func WaitGroupFunc(t *hbrt.Thread) {
|
||
nParams := t.ParamCount()
|
||
t.Frame(nParams, 0)
|
||
defer t.EndProc()
|
||
|
||
n := 0
|
||
if nParams >= 1 && !t.Local(1).IsNil() {
|
||
n = t.Local(1).AsInt()
|
||
}
|
||
t.RetVal(hbrt.MakeWaitGroup(n))
|
||
}
|
||
|
||
// WGDONE(pWaitGroup) <20><> NIL
|
||
func WgDone(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
wg := t.Local(1).AsWaitGroup()
|
||
if wg != nil {
|
||
wg.WG.Done()
|
||
}
|
||
t.RetNil()
|
||
}
|
||
|
||
// WGWAIT(pWaitGroup) → NIL
|
||
func WgWait(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
wg := t.Local(1).AsWaitGroup()
|
||
if wg != nil {
|
||
wg.WG.Wait()
|
||
}
|
||
t.RetNil()
|
||
}
|
||
|
||
// WGADD(pWaitGroup, nDelta) → NIL
|
||
func WgAdd(t *hbrt.Thread) {
|
||
t.Frame(2, 0)
|
||
defer t.EndProc()
|
||
|
||
wg := t.Local(1).AsWaitGroup()
|
||
if wg != nil {
|
||
wg.WG.Add(t.Local(2).AsInt())
|
||
}
|
||
t.RetNil()
|
||
}
|
||
|
||
// MUTEX() → pMutex
|
||
func MutexFunc(t *hbrt.Thread) {
|
||
t.Frame(0, 0)
|
||
defer t.EndProc()
|
||
t.RetVal(hbrt.MakeMutex())
|
||
}
|
||
|
||
// LOCK(pMutex) → NIL
|
||
func LockFunc(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
mu := t.Local(1).AsMutex()
|
||
if mu != nil {
|
||
mu.Mu.Lock()
|
||
}
|
||
t.RetNil()
|
||
}
|
||
|
||
// UNLOCK(pMutex) → NIL
|
||
func UnlockFunc(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
mu := t.Local(1).AsMutex()
|
||
if mu != nil {
|
||
mu.Mu.Unlock()
|
||
}
|
||
t.RetNil()
|
||
}
|
||
|
||
// SLEEP(nSeconds) → NIL
|
||
// Supports fractional seconds: Sleep(0.1) = 100ms
|
||
func SleepFunc(t *hbrt.Thread) {
|
||
t.Frame(1, 0)
|
||
defer t.EndProc()
|
||
|
||
val := t.Local(1)
|
||
var dur time.Duration
|
||
if val.IsNumInt() {
|
||
dur = time.Duration(val.AsLong()) * time.Second
|
||
} else {
|
||
dur = time.Duration(val.AsNumDouble() * float64(time.Second))
|
||
}
|
||
time.Sleep(dur)
|
||
t.RetNil()
|
||
}
|