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

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))
}
}
}