- 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>
234 lines
4.9 KiB
Go
234 lines
4.9 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// Five pcode interpreter — executes pcode bytecode on a Thread.
|
|
// Each opcode directly calls the corresponding Thread method,
|
|
// so pcode execution is semantically identical to gengo-compiled code.
|
|
|
|
package hbrt
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
// ExecPcode runs a pcode function on the given thread.
|
|
func ExecPcode(t *Thread, fn *PcodeFunc, mod *PcodeModule) {
|
|
code := fn.Code
|
|
pc := 0 // program counter
|
|
|
|
t.Frame(fn.Params, fn.Locals)
|
|
defer t.EndProc()
|
|
|
|
for pc < len(code) {
|
|
op := code[pc]
|
|
pc++
|
|
|
|
switch op {
|
|
case PcOpNop:
|
|
// do nothing
|
|
|
|
// --- Stack ---
|
|
case PcOpPushNil:
|
|
t.PushNil()
|
|
case PcOpPushTrue:
|
|
t.PushBool(true)
|
|
case PcOpPushFalse:
|
|
t.PushBool(false)
|
|
case PcOpPushInt:
|
|
v := int64(binary.LittleEndian.Uint64(code[pc:]))
|
|
pc += 8
|
|
t.PushLong(v)
|
|
case PcOpPushDouble:
|
|
bits := binary.LittleEndian.Uint64(code[pc:])
|
|
pc += 8
|
|
t.PushDouble(math.Float64frombits(bits), 0, 0)
|
|
case PcOpPushString:
|
|
slen := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.PushString(string(code[pc : pc+slen]))
|
|
pc += slen
|
|
case PcOpPushBool:
|
|
t.PushBool(code[pc] != 0)
|
|
pc++
|
|
case PcOpPushLocal:
|
|
idx := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.PushLocal(idx)
|
|
case PcOpPopLocal:
|
|
idx := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.PopLocal(idx)
|
|
case PcOpPop:
|
|
t.Pop()
|
|
case PcOpDup:
|
|
t.Dup()
|
|
|
|
// --- Arithmetic ---
|
|
case PcOpPlus:
|
|
t.Plus()
|
|
case PcOpMinus:
|
|
t.Minus()
|
|
case PcOpMult:
|
|
t.Mult()
|
|
case PcOpDivide:
|
|
t.Divide()
|
|
case PcOpMod:
|
|
t.Modulus()
|
|
case PcOpPower:
|
|
t.Power()
|
|
case PcOpNegate:
|
|
t.Negate()
|
|
|
|
// --- Comparison ---
|
|
case PcOpEqual:
|
|
t.Equal()
|
|
case PcOpNotEqual:
|
|
t.NotEqual()
|
|
case PcOpLess:
|
|
t.Less()
|
|
case PcOpGreater:
|
|
t.Greater()
|
|
case PcOpLessEq:
|
|
t.LessEqual()
|
|
case PcOpGreaterEq:
|
|
t.GreaterEqual()
|
|
case PcOpInString:
|
|
t.InString()
|
|
|
|
// --- Logical ---
|
|
case PcOpAnd:
|
|
t.And()
|
|
case PcOpOr:
|
|
t.Or()
|
|
case PcOpNot:
|
|
t.Not()
|
|
|
|
// --- Flow control ---
|
|
case PcOpJump:
|
|
offset := int32(binary.LittleEndian.Uint32(code[pc:]))
|
|
pc += 4
|
|
pc += int(offset)
|
|
case PcOpJumpFalse:
|
|
offset := int32(binary.LittleEndian.Uint32(code[pc:]))
|
|
pc += 4
|
|
if !t.PopLogical() {
|
|
pc += int(offset)
|
|
}
|
|
case PcOpJumpTrue:
|
|
offset := int32(binary.LittleEndian.Uint32(code[pc:]))
|
|
pc += 4
|
|
if t.PopLogical() {
|
|
pc += int(offset)
|
|
}
|
|
case PcOpReturn:
|
|
return
|
|
case PcOpRetValue:
|
|
t.RetValue()
|
|
return
|
|
|
|
// --- Frame ---
|
|
case PcOpFrame:
|
|
// Already called at function entry; skip if re-encountered
|
|
pc += 4 // params + locals
|
|
case PcOpEndProc:
|
|
return
|
|
|
|
// --- Function calls ---
|
|
case PcOpPushSymbol:
|
|
slen := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
name := string(code[pc : pc+slen])
|
|
pc += slen
|
|
sym := t.VM().FindSymbol(name)
|
|
t.PushSymbol(sym)
|
|
case PcOpPushNilArg:
|
|
t.PushNil()
|
|
case PcOpFunction:
|
|
nArgs := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.Function(nArgs)
|
|
case PcOpDo:
|
|
nArgs := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.Do(nArgs)
|
|
|
|
// --- Self / OOP ---
|
|
case PcOpPushSelf:
|
|
t.PushSelf()
|
|
case PcOpPushSelfField:
|
|
slen := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
name := string(code[pc : pc+slen])
|
|
pc += slen
|
|
t.PushSelfField(name)
|
|
case PcOpSetSelfField:
|
|
slen := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
name := string(code[pc : pc+slen])
|
|
pc += slen
|
|
t.SetSelfField(name)
|
|
case PcOpSend:
|
|
slen := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
name := string(code[pc : pc+slen])
|
|
pc += slen
|
|
nArgs := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.Send(name, nArgs)
|
|
|
|
// --- Array ---
|
|
case PcOpArrayGen:
|
|
count := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
t.ArrayGen(count)
|
|
case PcOpArrayPush:
|
|
t.ArrayPush()
|
|
case PcOpArrayPop:
|
|
t.ArrayPop()
|
|
|
|
// --- Block ---
|
|
case PcOpPushBlock:
|
|
codeLen := int(binary.LittleEndian.Uint32(code[pc:]))
|
|
pc += 4
|
|
blockCode := make([]byte, codeLen)
|
|
copy(blockCode, code[pc:pc+codeLen])
|
|
pc += codeLen
|
|
nDetached := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
|
|
// Create a Go function that interprets the block's pcode
|
|
blockFn := &PcodeFunc{Code: blockCode}
|
|
modCopy := mod
|
|
t.PushBlock(func(t2 *Thread) {
|
|
ExecPcode(t2, blockFn, modCopy)
|
|
}, nDetached)
|
|
|
|
// --- Local ops ---
|
|
case PcOpLocalAddInt:
|
|
idx := int(binary.LittleEndian.Uint16(code[pc:]))
|
|
pc += 2
|
|
val := int32(binary.LittleEndian.Uint32(code[pc:]))
|
|
pc += 4
|
|
t.LocalAddInt(idx, int64(val))
|
|
case PcOpInc:
|
|
t.Inc()
|
|
case PcOpDec:
|
|
t.Dec()
|
|
|
|
case PcOpPopLogical:
|
|
t.PopLogical()
|
|
|
|
case PcOpLine:
|
|
pc += 2 // skip line number (for debugging)
|
|
|
|
case PcOpHalt:
|
|
return
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unknown pcode opcode: 0x%02X at pc=%d", op, pc-1))
|
|
}
|
|
}
|
|
}
|