// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Collection operations for the Five runtime. // Array generation, indexing, hash generation, and code block evaluation. // Harbour: HB_P_ARRAYGEN, HB_P_ARRAYPUSH, HB_P_ARRAYPOP, etc. package hbrt import "unsafe" // ArrayGen pops n items from stack and creates an array. // Harbour: HB_P_ARRAYGEN / hb_vmArrayGen func (t *Thread) ArrayGen(n int) { items := make([]Value, n) for i := n - 1; i >= 0; i-- { items[i] = t.pop() } t.push(MakeArrayFrom(items)) } // HashGen pops n key-value pairs and creates a hash. // Stack: [key1] [val1] [key2] [val2] ... → Hash func (t *Thread) HashGen(n int) { hh := &HbHash{ Keys: make([]Value, n), Values: make([]Value, n), } for i := n - 1; i >= 0; i-- { hh.Values[i] = t.pop() hh.Keys[i] = t.pop() } t.push(Value{ info: makeInfo(tHash, 0, 0), ptr: unsafe.Pointer(hh), }) } // ArrayPush pops index and array, pushes array[index]. // Harbour: HB_P_ARRAYPUSH func (t *Thread) ArrayPush() { idx := t.pop() arr := t.pop() if !arr.IsArray() { panic(t.argError("[]", arr, idx)) } ha := arr.AsArray() n := int(idx.AsNumInt()) // Harbour: 1-based indexing if n < 1 || n > len(ha.Items) { panic(t.runtimeError("array index out of bounds")) } t.push(ha.Items[n-1]) } // ArrayPop pops value, index, array and sets array[index] = value. // Harbour: HB_P_ARRAYPOP func (t *Thread) ArrayPop() { val := t.pop() idx := t.pop() arr := t.pop() if !arr.IsArray() { panic(t.argError("[]=", arr, idx)) } ha := arr.AsArray() n := int(idx.AsNumInt()) if n < 1 || n > len(ha.Items) { panic(t.runtimeError("array index out of bounds")) } ha.Items[n-1] = val } // EvalBlock evaluates a code block on the stack with nArgs arguments. // Stack: [block] [arg1] ... [argN] → [result] func (t *Thread) EvalBlock(nArgs int) { args := make([]Value, nArgs) for i := nArgs - 1; i >= 0; i-- { args[i] = t.pop() } blockVal := t.pop() if !blockVal.IsBlock() { panic(t.argError("Eval", blockVal)) } blk := blockVal.AsBlock() // Push args for Frame for _, arg := range args { t.push(arg) } t.pendingParams = nArgs blk.Fn(t) t.push(t.retVal) } // PushBlock creates a code block and pushes it onto the stack. func (t *Thread) PushBlock(fn func(*Thread), detachedLocals int) { t.push(MakeBlock(fn, detachedLocals)) } // PushSelf pushes the current Self object (for :: access in methods). func (t *Thread) PushSelf() { t.push(t.self) }