Files
five/hbrt/vm.go
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

162 lines
3.8 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
package hbrt
import "sync"
// VM is the shared state across all threads.
type VM struct {
mu sync.RWMutex
modules []*Module
symbols map[string]*Symbol
statics map[string][]Value
waFactory func() interface{} // creates WorkAreaManager for new threads
onExit func() // called when Run() finishes (restore terminal etc.)
Debugger *Debugger // nil = no debugging; set by five debug command
}
// SetWAFactory sets the factory for creating WorkAreaManagers.
func (vm *VM) SetWAFactory(f func() interface{}) {
vm.waFactory = f
}
// SetOnExit sets a callback for when Run() finishes.
func (vm *VM) SetOnExit(f func()) {
vm.onExit = f
}
// Library modules registered via init()
var libModules []*Module
var dynamicFuncs []Symbol // from HB_FUNC() in #pragma BEGINDUMP
// RegisterLibModule registers a module from a library PRG file.
// Called by init() in generated library code.
func RegisterLibModule(m *Module) {
libModules = append(libModules, m)
}
// RegisterDynamicFunc registers a Go function callable from PRG.
// Called from init() in #pragma BEGINDUMP code via HB_FUNC().
func RegisterDynamicFunc(name string, fn func(*Thread)) {
dynamicFuncs = append(dynamicFuncs, Symbol{
Name: name,
Scope: FsPublic | FsLocal,
Func: fn,
})
}
// RegisterLibModules registers any pending lib modules and dynamic functions.
func (vm *VM) RegisterLibModules() {
for _, m := range libModules {
vm.RegisterModule(m)
}
libModules = nil
// Register HB_FUNC dynamic functions from #pragma BEGINDUMP
for i := range dynamicFuncs {
sym := &dynamicFuncs[i]
vm.RegisterSymbol(sym)
}
dynamicFuncs = nil
}
// NewVM creates a new VM instance.
func NewVM() *VM {
return &VM{
modules: make([]*Module, 0),
symbols: make(map[string]*Symbol),
statics: make(map[string][]Value),
}
}
// RegisterModule registers a module's symbols with the VM.
func (vm *VM) RegisterModule(m *Module) {
vm.mu.Lock()
defer vm.mu.Unlock()
vm.modules = append(vm.modules, m)
for i := range m.Symbols {
sym := &m.Symbols[i]
vm.symbols[sym.Name] = sym
}
}
// RegisterSymbol registers a single symbol.
func (vm *VM) RegisterSymbol(sym *Symbol) {
vm.mu.Lock()
defer vm.mu.Unlock()
vm.symbols[sym.Name] = sym
}
// UnregisterSymbol removes a symbol by name. Returns the old symbol if any.
func (vm *VM) UnregisterSymbol(name string) *Symbol {
vm.mu.Lock()
defer vm.mu.Unlock()
old := vm.symbols[name]
delete(vm.symbols, name)
return old
}
// SymbolNames returns all registered symbol names.
func (vm *VM) SymbolNames() []string {
vm.mu.RLock()
defer vm.mu.RUnlock()
names := make([]string, 0, len(vm.symbols))
for n := range vm.symbols {
names = append(names, n)
}
return names
}
// FindSymbol looks up a symbol by name.
func (vm *VM) FindSymbol(name string) *Symbol {
vm.mu.RLock()
defer vm.mu.RUnlock()
return vm.symbols[name]
}
// NewThread creates a new Thread attached to this VM.
func (vm *VM) NewThread() *Thread {
return NewThread(vm)
}
// Run starts execution from the named function.
func (vm *VM) Run(funcName string) Value {
// Register any library modules from init()
for _, m := range libModules {
vm.RegisterModule(m)
}
libModules = nil
sym := vm.FindSymbol(funcName)
if sym == nil {
panic("function not found: " + funcName)
}
if sym.Func == nil {
panic("function has no implementation: " + funcName)
}
t := vm.NewThread()
// Auto-initialize WorkAreaManager if not set
if t.WA == nil && vm.waFactory != nil {
t.WA = vm.waFactory()
}
// Copy statics to thread
vm.mu.RLock()
for k, v := range vm.statics {
t.statics[k] = v
}
vm.mu.RUnlock()
// Call the function, ensure cleanup on exit
defer func() {
if vm.onExit != nil {
vm.onExit()
}
}()
sym.Func(t)
return t.retVal
}