- 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>
346 lines
7.9 KiB
Go
346 lines
7.9 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// CLASS system for the Five runtime.
|
|
// Harbour: classes.c — object = array with uiClass, methods in class registry.
|
|
//
|
|
// Key concepts:
|
|
// - Class = definition (name, DATA fields, METHODs, parent)
|
|
// - Object = HbArray with Class > 0 (fields stored in Items[])
|
|
// - Send = method dispatch: obj:method(args) → lookup class → call func
|
|
// - :: = Self access (current object in method context)
|
|
// - INHERIT FROM = parent class embedding
|
|
// - Operator overloading: HB_OO_OP_PLUS etc.
|
|
//
|
|
// Reference:
|
|
// /mnt/d/harbour-core/include/hbapicls.h
|
|
// /mnt/d/harbour-core/src/vm/classes.c
|
|
package hbrt
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// ClassDef defines a class (DATA fields + METHODs).
|
|
// Harbour: internal class structure in classes.c
|
|
type ClassDef struct {
|
|
ID uint16
|
|
Name string
|
|
Parent *ClassDef // INHERIT FROM
|
|
Fields []ClassField // DATA declarations (ordered)
|
|
Methods map[string]MethodFunc // method name → function
|
|
Operators [MaxOperator + 1]MethodFunc // operator overloading
|
|
fieldMap map[string]int // field name → index
|
|
}
|
|
|
|
// ClassField describes a DATA member.
|
|
type ClassField struct {
|
|
Name string
|
|
Init Value // default INIT value
|
|
AsType string // optional type hint
|
|
}
|
|
|
|
// MethodFunc is the signature for class methods.
|
|
// The method receives the thread; Self is available via thread context.
|
|
type MethodFunc func(t *Thread)
|
|
|
|
// Operator IDs matching Harbour's HB_OO_OP_*
|
|
const (
|
|
OpPlus = 0
|
|
OpMinus = 1
|
|
OpMult = 2
|
|
OpDivide = 3
|
|
OpMod = 4
|
|
OpPower = 5
|
|
OpInc = 6
|
|
OpDec = 7
|
|
OpEqual = 8
|
|
OpExactEqual = 9
|
|
OpNotEqual = 10
|
|
OpLess = 11
|
|
OpLessEqual = 12
|
|
OpGreater = 13
|
|
OpGreaterEqual = 14
|
|
OpAssign = 15
|
|
OpInString = 16
|
|
OpInclude = 17
|
|
OpNot = 18
|
|
OpAnd = 19
|
|
OpOr = 20
|
|
OpArrayIndex = 21
|
|
MaxOperator = 21
|
|
)
|
|
|
|
// --- Class Registry ---
|
|
|
|
var (
|
|
classRegMu sync.RWMutex
|
|
classReg = map[string]*ClassDef{}
|
|
classList []*ClassDef // index = classID - 1
|
|
)
|
|
|
|
// RegisterClass registers a class definition.
|
|
// Returns the assigned class ID (1-based).
|
|
func RegisterClass(cls *ClassDef) uint16 {
|
|
classRegMu.Lock()
|
|
defer classRegMu.Unlock()
|
|
|
|
cls.ID = uint16(len(classList) + 1)
|
|
classList = append(classList, cls)
|
|
classReg[strings.ToUpper(cls.Name)] = cls
|
|
|
|
// Build field index map
|
|
cls.fieldMap = make(map[string]int, len(cls.Fields))
|
|
for i, f := range cls.Fields {
|
|
cls.fieldMap[strings.ToUpper(f.Name)] = i
|
|
}
|
|
|
|
return cls.ID
|
|
}
|
|
|
|
// FindClass looks up a class by name.
|
|
func FindClass(name string) *ClassDef {
|
|
classRegMu.RLock()
|
|
defer classRegMu.RUnlock()
|
|
return classReg[strings.ToUpper(name)]
|
|
}
|
|
|
|
// GetClass looks up a class by ID.
|
|
func GetClass(id uint16) *ClassDef {
|
|
classRegMu.RLock()
|
|
defer classRegMu.RUnlock()
|
|
if int(id) > 0 && int(id) <= len(classList) {
|
|
return classList[id-1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// --- Class builder (fluent API for generated code) ---
|
|
|
|
// NewClassDef creates a new class definition builder.
|
|
func NewClassDef(name string) *ClassDef {
|
|
return &ClassDef{
|
|
Name: name,
|
|
Methods: make(map[string]MethodFunc),
|
|
}
|
|
}
|
|
|
|
// InheritFrom sets the parent class.
|
|
func (c *ClassDef) InheritFrom(parentName string) *ClassDef {
|
|
parent := FindClass(parentName)
|
|
if parent != nil {
|
|
c.Parent = parent
|
|
// Copy parent fields first
|
|
c.Fields = append(append([]ClassField{}, parent.Fields...), c.Fields...)
|
|
// Copy parent methods (child can override)
|
|
for name, fn := range parent.Methods {
|
|
if _, exists := c.Methods[name]; !exists {
|
|
c.Methods[name] = fn
|
|
}
|
|
}
|
|
// Copy parent operators
|
|
for i, fn := range parent.Operators {
|
|
if c.Operators[i] == nil {
|
|
c.Operators[i] = fn
|
|
}
|
|
}
|
|
}
|
|
return c
|
|
}
|
|
|
|
// AddData adds a DATA field to the class.
|
|
func (c *ClassDef) AddData(name string, init Value) *ClassDef {
|
|
c.Fields = append(c.Fields, ClassField{Name: name, Init: init})
|
|
return c
|
|
}
|
|
|
|
// AddMethod adds a METHOD to the class.
|
|
func (c *ClassDef) AddMethod(name string, fn MethodFunc) *ClassDef {
|
|
c.Methods[strings.ToUpper(name)] = fn
|
|
return c
|
|
}
|
|
|
|
// AddOperator sets an operator overload.
|
|
func (c *ClassDef) AddOperator(op int, fn MethodFunc) *ClassDef {
|
|
if op >= 0 && op <= MaxOperator {
|
|
c.Operators[op] = fn
|
|
}
|
|
return c
|
|
}
|
|
|
|
// Register registers this class and returns the class ID.
|
|
func (c *ClassDef) Register() uint16 {
|
|
return RegisterClass(c)
|
|
}
|
|
|
|
// FieldIndex returns the 0-based field index by name, or -1.
|
|
func (c *ClassDef) FieldIndex(name string) int {
|
|
if c.fieldMap == nil {
|
|
return -1
|
|
}
|
|
if idx, ok := c.fieldMap[strings.ToUpper(name)]; ok {
|
|
return idx
|
|
}
|
|
// Check parent
|
|
if c.Parent != nil {
|
|
return c.Parent.FieldIndex(name)
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// --- Object creation ---
|
|
|
|
// NewObject creates a new object instance of a class.
|
|
// Harbour: object = array with uiClass set.
|
|
func NewObject(classID uint16) Value {
|
|
cls := GetClass(classID)
|
|
if cls == nil {
|
|
return MakeNil()
|
|
}
|
|
|
|
obj := MakeObject(classID, len(cls.Fields))
|
|
arr := obj.AsArray()
|
|
|
|
// Initialize fields with default values
|
|
for i, f := range cls.Fields {
|
|
arr.Items[i] = f.Init
|
|
}
|
|
|
|
return obj
|
|
}
|
|
|
|
// --- Method dispatch ---
|
|
|
|
// Send dispatches a method call on an object.
|
|
// Harbour: hb_objGetMethod + call
|
|
// Stack: [object] [arg1] ... [argN] → call method → [result]
|
|
func (t *Thread) Send(methodName string, nArgs int) {
|
|
// Collect args
|
|
args := make([]Value, nArgs)
|
|
for i := nArgs - 1; i >= 0; i-- {
|
|
args[i] = t.pop()
|
|
}
|
|
objVal := t.pop() // object
|
|
|
|
if !objVal.IsObject() {
|
|
// Not an object — try as property access on non-object
|
|
panic(t.runtimeError(fmt.Sprintf("not an object for method %s", methodName)))
|
|
}
|
|
|
|
arr := objVal.AsArray()
|
|
cls := GetClass(arr.Class)
|
|
if cls == nil {
|
|
panic(t.runtimeError(fmt.Sprintf("unknown class ID %d", arr.Class)))
|
|
}
|
|
|
|
upperMethod := strings.ToUpper(methodName)
|
|
|
|
// Check for data field access (getter)
|
|
if nArgs == 0 {
|
|
if idx := cls.FieldIndex(methodName); idx >= 0 {
|
|
t.push(arr.Items[idx])
|
|
return
|
|
}
|
|
}
|
|
|
|
// Check for data field setter: _FIELDNAME convention
|
|
if nArgs == 1 && strings.HasPrefix(upperMethod, "_") {
|
|
fieldName := upperMethod[1:]
|
|
if idx := cls.FieldIndex(fieldName); idx >= 0 {
|
|
arr.Items[idx] = args[0]
|
|
t.push(args[0])
|
|
return
|
|
}
|
|
}
|
|
|
|
// Look up method
|
|
fn, ok := cls.Methods[upperMethod]
|
|
if !ok {
|
|
panic(t.runtimeError(fmt.Sprintf("unknown method %s in class %s", methodName, cls.Name)))
|
|
}
|
|
|
|
// Set up Self context
|
|
oldSelf := t.self
|
|
t.self = objVal
|
|
|
|
// Push args for Frame
|
|
for _, arg := range args {
|
|
t.push(arg)
|
|
}
|
|
|
|
t.pendingParams = nArgs
|
|
fn(t)
|
|
|
|
// Restore Self
|
|
t.self = oldSelf
|
|
|
|
// Push return value
|
|
t.push(t.retVal)
|
|
}
|
|
|
|
// SendAssign dispatches a setter: obj:prop := value
|
|
// Generated for ::fieldName := value
|
|
func (t *Thread) SendAssign(fieldName string) {
|
|
val := t.pop()
|
|
objVal := t.pop()
|
|
|
|
if !objVal.IsObject() {
|
|
panic(t.runtimeError("not an object for assignment"))
|
|
}
|
|
|
|
arr := objVal.AsArray()
|
|
cls := GetClass(arr.Class)
|
|
if cls == nil {
|
|
return
|
|
}
|
|
|
|
if idx := cls.FieldIndex(fieldName); idx >= 0 {
|
|
arr.Items[idx] = val
|
|
}
|
|
}
|
|
|
|
// Send0 dispatches a no-arg method (getter).
|
|
func (t *Thread) Send0(methodName string) {
|
|
t.Send(methodName, 0)
|
|
}
|
|
|
|
// PushSelfField pushes a field from the current Self object.
|
|
// Used by :: access in methods.
|
|
func (t *Thread) PushSelfField(fieldName string) {
|
|
if t.self.IsNil() {
|
|
t.push(MakeNil())
|
|
return
|
|
}
|
|
arr := t.self.AsArray()
|
|
cls := GetClass(arr.Class)
|
|
if cls != nil {
|
|
if idx := cls.FieldIndex(fieldName); idx >= 0 {
|
|
t.push(arr.Items[idx])
|
|
return
|
|
}
|
|
}
|
|
t.push(MakeNil())
|
|
}
|
|
|
|
// SetSelfField sets a field on the current Self object.
|
|
func (t *Thread) SetSelfField(fieldName string) {
|
|
val := t.pop()
|
|
if t.self.IsNil() {
|
|
return
|
|
}
|
|
arr := t.self.AsArray()
|
|
cls := GetClass(arr.Class)
|
|
if cls != nil {
|
|
if idx := cls.FieldIndex(fieldName); idx >= 0 {
|
|
arr.Items[idx] = val
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetSelf returns the current Self value.
|
|
func (t *Thread) GetSelf() Value {
|
|
return t.self
|
|
}
|