// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // memvar.go — PUBLIC/PRIVATE variable system for Five. // // Harbour: src/vm/memvars.c — dynamic scoping for PUBLIC/PRIVATE variables. // // PUBLIC variables: // - Visible from anywhere in the program // - Persist until program ends or explicitly RELEASEd // - Created with: PUBLIC cName, nAge // // PRIVATE variables: // - Visible in declaring function and all functions it calls // - Automatically released when declaring function returns // - Created with: PRIVATE cName, nAge // - Can shadow PUBLIC vars of the same name // // Access: // M->varname — explicit memvar access // &cVarName — macro access (runtime lookup) // undeclared name — if not LOCAL/STATIC, falls back to memvar package hbrt import ( "strings" "sync" ) // MemvarScope indicates PUBLIC or PRIVATE. type MemvarScope int const ( MemvarPublic MemvarScope = iota MemvarPrivate ) // memvarEntry stores one memvar with its scope and owning call depth. type memvarEntry struct { Value Value Scope MemvarScope CallDepth int // for PRIVATE: call stack depth when declared } // MemvarTable manages all PUBLIC/PRIVATE variables for a thread. type MemvarTable struct { mu sync.RWMutex vars map[string]*memvarEntry // uppercase name → entry privStack []privFrame // stack of PRIVATE scopes for cleanup } // privFrame records which PRIVATE vars were created at a given call depth. type privFrame struct { callDepth int names []string // uppercase names to release on return saved map[string]*memvarEntry // previous values (for shadowing) } // NewMemvarTable creates an empty memvar table. func NewMemvarTable() *MemvarTable { return &MemvarTable{ vars: make(map[string]*memvarEntry), } } // --- PUBLIC --- // SetPublic creates or updates a PUBLIC memvar. func (m *MemvarTable) SetPublic(name string, val Value) { m.mu.Lock() defer m.mu.Unlock() upper := strings.ToUpper(name) m.vars[upper] = &memvarEntry{Value: val, Scope: MemvarPublic} } // --- PRIVATE --- // BeginPrivateScope starts a new PRIVATE scope at the given call depth. // Called at the start of a function that declares PRIVATE vars. func (m *MemvarTable) BeginPrivateScope(callDepth int) { m.mu.Lock() defer m.mu.Unlock() m.privStack = append(m.privStack, privFrame{ callDepth: callDepth, saved: make(map[string]*memvarEntry), }) } // SetPrivate creates or shadows a PRIVATE memvar. func (m *MemvarTable) SetPrivate(name string, val Value, callDepth int) { m.mu.Lock() defer m.mu.Unlock() upper := strings.ToUpper(name) // Save previous value for restore on scope exit if len(m.privStack) > 0 { frame := &m.privStack[len(m.privStack)-1] if _, exists := frame.saved[upper]; !exists { // Save old value (or nil if didn't exist) if old, ok := m.vars[upper]; ok { oldCopy := *old frame.saved[upper] = &oldCopy } else { frame.saved[upper] = nil // marker: didn't exist before } frame.names = append(frame.names, upper) } } m.vars[upper] = &memvarEntry{Value: val, Scope: MemvarPrivate, CallDepth: callDepth} } // EndPrivateScope restores PRIVATE vars from the most recent scope. // Called when a function returns. func (m *MemvarTable) EndPrivateScope() { m.mu.Lock() defer m.mu.Unlock() if len(m.privStack) == 0 { return } frame := m.privStack[len(m.privStack)-1] m.privStack = m.privStack[:len(m.privStack)-1] // Restore saved values for _, name := range frame.names { if saved, ok := frame.saved[name]; ok { if saved == nil { // Didn't exist before — remove delete(m.vars, name) } else { // Restore previous value m.vars[name] = saved } } } } // --- Access --- // Get retrieves a memvar value by name. Returns (value, true) or (nil, false). func (m *MemvarTable) Get(name string) (Value, bool) { m.mu.RLock() defer m.mu.RUnlock() upper := strings.ToUpper(name) if e, ok := m.vars[upper]; ok { return e.Value, true } return MakeNil(), false } // Set updates an existing memvar value (PUBLIC or PRIVATE). func (m *MemvarTable) Set(name string, val Value) bool { m.mu.Lock() defer m.mu.Unlock() upper := strings.ToUpper(name) if e, ok := m.vars[upper]; ok { e.Value = val return true } return false } // Exists checks if a memvar exists. func (m *MemvarTable) Exists(name string) bool { m.mu.RLock() defer m.mu.RUnlock() _, ok := m.vars[strings.ToUpper(name)] return ok } // Release removes a memvar by name. func (m *MemvarTable) Release(name string) { m.mu.Lock() defer m.mu.Unlock() delete(m.vars, strings.ToUpper(name)) } // ReleaseAll removes all memvars. func (m *MemvarTable) ReleaseAll() { m.mu.Lock() defer m.mu.Unlock() m.vars = make(map[string]*memvarEntry) m.privStack = nil } // Names returns all memvar names. func (m *MemvarTable) Names() []string { m.mu.RLock() defer m.mu.RUnlock() names := make([]string, 0, len(m.vars)) for k := range m.vars { names = append(names, k) } return names } // Count returns the number of memvars. func (m *MemvarTable) Count() int { m.mu.RLock() defer m.mu.RUnlock() return len(m.vars) }