SET commands (setcmd.go):
- SetDateFunc: __SetDateFormat([cNew]) → cOld
- SetDecimalsFunc: SET DECIMALS TO n
- SetEpochFunc: SET EPOCH TO n
- 11 toggle functions: SetExact, SetDeleted, SetSoftSeek, SetExclusive,
SetFixed, SetCancel, SetBell, SetConfirm, SetInsert, SetEscape, SetWrap
- SET constants: _SET_EXACT, _SET_DELETED, etc. for PRG code
- GetSetDateFormat(), GetSetDecimals(), GetSetEpoch() helpers
- Default: DATE="mm/dd/yy", EPOCH=1900, DECIMALS=2
Error handling (error.go):
- Break(xValue): panics with BreakValue, caught by BEGIN SEQUENCE
- BreakBlock(): returns {|e| Break(e)} code block
- LaunchError(): dispatches error through ErrorBlock handler
- RuntimeError(): creates + launches standard runtime error
- IsBreak(): checks if recovered panic is a BreakValue
- createErrorHash(): builds Harbour-compatible error hash
Registration: ErrorBlock, ErrorNew, DosError, FError, Break + all SET functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
196 lines
5.1 KiB
Go
196 lines
5.1 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// Error handling functions: ERRORBLOCK, ERRORNEW, DOSERROR, FERROR
|
|
// Harbour error system: Error object + ErrorBlock callback chain.
|
|
|
|
package hbrtl
|
|
|
|
import (
|
|
"five/hbrt"
|
|
)
|
|
|
|
var (
|
|
errorBlock hbrt.Value // current error handler block
|
|
lastDosErr int // last OS error code
|
|
lastFErr int // last file error code
|
|
)
|
|
|
|
// ERRORBLOCK([bNewBlock]) → bOldBlock
|
|
// Gets/sets the error handler code block.
|
|
func ErrorBlock(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
|
|
old := errorBlock
|
|
if old.IsNil() {
|
|
old = hbrt.MakeNil()
|
|
}
|
|
|
|
if nParams >= 1 && !t.Local(1).IsNil() {
|
|
errorBlock = t.Local(1)
|
|
}
|
|
|
|
t.RetVal(old)
|
|
}
|
|
|
|
// ERRORNEW() → oError
|
|
// Creates a new Error object as a hash with standard Harbour error properties.
|
|
func ErrorNew(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
|
|
keys := []hbrt.Value{
|
|
hbrt.MakeString("ARGS"), hbrt.MakeString("CANDEFAULT"),
|
|
hbrt.MakeString("CANRETRY"), hbrt.MakeString("CANSUBSTITUTE"),
|
|
hbrt.MakeString("CARGO"), hbrt.MakeString("DESCRIPTION"),
|
|
hbrt.MakeString("FILENAME"), hbrt.MakeString("GENCODE"),
|
|
hbrt.MakeString("OPERATION"), hbrt.MakeString("OSCODE"),
|
|
hbrt.MakeString("SEVERITY"), hbrt.MakeString("SUBCODE"),
|
|
hbrt.MakeString("SUBSYSTEM"), hbrt.MakeString("TRIES"),
|
|
}
|
|
vals := []hbrt.Value{
|
|
hbrt.MakeNil(), hbrt.MakeBool(false),
|
|
hbrt.MakeBool(false), hbrt.MakeBool(false),
|
|
hbrt.MakeNil(), hbrt.MakeString(""),
|
|
hbrt.MakeString(""), hbrt.MakeInt(0),
|
|
hbrt.MakeString(""), hbrt.MakeInt(0),
|
|
hbrt.MakeInt(2), hbrt.MakeInt(0),
|
|
hbrt.MakeString(""), hbrt.MakeInt(0),
|
|
}
|
|
|
|
h := &hbrt.HbHash{Keys: keys, Values: vals}
|
|
order := make([]int, len(keys))
|
|
for i := range order {
|
|
order[i] = i
|
|
}
|
|
h.Order = order
|
|
|
|
t.RetVal(hbrt.MakeHashFrom(h))
|
|
}
|
|
|
|
// DOSERROR([nNewCode]) → nOldCode
|
|
func DosError(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
|
|
old := lastDosErr
|
|
if nParams >= 1 && !t.Local(1).IsNil() {
|
|
lastDosErr = t.Local(1).AsInt()
|
|
}
|
|
t.RetInt(int64(old))
|
|
}
|
|
|
|
// FERROR() → nLastFileError
|
|
func FError(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
t.RetInt(int64(lastFErr))
|
|
}
|
|
|
|
// SetFError sets the file error code (called internally by file I/O functions).
|
|
func SetFError(code int) {
|
|
lastFErr = code
|
|
}
|
|
|
|
// --- Break / Error dispatch ---
|
|
|
|
// BreakValue is a special panic value for Break().
|
|
// Harbour: HB_BREAK — unwinds to nearest BEGIN SEQUENCE.
|
|
type BreakValue struct {
|
|
Value hbrt.Value
|
|
}
|
|
|
|
// Break(xValue) → panics with BreakValue, caught by BEGIN SEQUENCE/RECOVER.
|
|
func Break(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
// Don't defer EndProc — we're panicking
|
|
|
|
var val hbrt.Value
|
|
if nParams >= 1 {
|
|
val = t.Local(1)
|
|
} else {
|
|
val = hbrt.MakeNil()
|
|
}
|
|
panic(BreakValue{Value: val})
|
|
}
|
|
|
|
// BreakBlock returns a block that calls Break(). Harbour: {|e| Break(e)}
|
|
func BreakBlock(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
|
|
t.PushBlock(func(bt *hbrt.Thread) {
|
|
bt.Frame(1, 0)
|
|
val := bt.Local(1)
|
|
panic(BreakValue{Value: val})
|
|
}, 0)
|
|
t.RetValue()
|
|
}
|
|
|
|
// LaunchError creates and dispatches an error through ErrorBlock.
|
|
// Harbour: hb_errLaunch — calls error handler, returns action.
|
|
// Actions: 0=default, 1=retry, 0xFFFF=break
|
|
func LaunchError(t *hbrt.Thread, oErr hbrt.Value) hbrt.Value {
|
|
if errorBlock.IsNil() || !errorBlock.IsBlock() {
|
|
// No error handler — Break with error object
|
|
panic(BreakValue{Value: oErr})
|
|
}
|
|
|
|
// Call error handler block: errorBlock:Eval(oErr)
|
|
blk := errorBlock.AsBlock()
|
|
t.PushValue(oErr)
|
|
t.PendingParams2(1)
|
|
blk.Fn(t)
|
|
return t.Pop2()
|
|
}
|
|
|
|
// RuntimeError creates and launches a runtime error.
|
|
// Harbour: hb_errRT_BASE
|
|
func RuntimeError(t *hbrt.Thread, subSystem string, genCode, subCode int,
|
|
description, operation string) {
|
|
// Create error object
|
|
oErr := createErrorHash(subSystem, genCode, subCode, description, operation)
|
|
LaunchError(t, oErr)
|
|
}
|
|
|
|
func createErrorHash(subSystem string, genCode, subCode int,
|
|
description, operation string) hbrt.Value {
|
|
h := &hbrt.HbHash{}
|
|
addKV := func(k string, v hbrt.Value) {
|
|
h.Keys = append(h.Keys, hbrt.MakeString(k))
|
|
h.Values = append(h.Values, v)
|
|
}
|
|
addKV("SUBSYSTEM", hbrt.MakeString(subSystem))
|
|
addKV("GENCODE", hbrt.MakeInt(genCode))
|
|
addKV("SUBCODE", hbrt.MakeInt(subCode))
|
|
addKV("DESCRIPTION", hbrt.MakeString(description))
|
|
addKV("OPERATION", hbrt.MakeString(operation))
|
|
addKV("SEVERITY", hbrt.MakeInt(2)) // ES_ERROR
|
|
addKV("CANRETRY", hbrt.MakeBool(false))
|
|
addKV("CANDEFAULT", hbrt.MakeBool(false))
|
|
addKV("CANSUBSTITUTE", hbrt.MakeBool(false))
|
|
addKV("TRIES", hbrt.MakeInt(0))
|
|
addKV("CARGO", hbrt.MakeNil())
|
|
addKV("ARGS", hbrt.MakeNil())
|
|
addKV("FILENAME", hbrt.MakeString(""))
|
|
addKV("OSCODE", hbrt.MakeInt(0))
|
|
return hbrt.MakeHashFrom(h)
|
|
}
|
|
|
|
// GetErrorBlock returns the current error block (for internal use).
|
|
func GetErrorBlock() hbrt.Value {
|
|
return errorBlock
|
|
}
|
|
|
|
// IsBreak checks if a recovered panic is a BreakValue.
|
|
func IsBreak(r interface{}) (hbrt.Value, bool) {
|
|
if bv, ok := r.(BreakValue); ok {
|
|
return bv.Value, true
|
|
}
|
|
return hbrt.MakeNil(), false
|
|
}
|