feat: SET commands + ErrorBlock/Break error handling
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>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "2.0",
|
||||
"lastUpdated": "2026-04-02T06:00:35.695Z",
|
||||
"lastUpdated": "2026-04-02T06:32:27.120Z",
|
||||
"activeFeatures": [
|
||||
"hbrt",
|
||||
"hbrtl",
|
||||
@@ -46,9 +46,9 @@
|
||||
"documents": {},
|
||||
"timestamps": {
|
||||
"started": "2026-03-27T11:15:10.675Z",
|
||||
"lastUpdated": "2026-04-01T02:36:49.307Z"
|
||||
"lastUpdated": "2026-04-02T06:32:27.120Z"
|
||||
},
|
||||
"lastFile": "/mnt/d/charles/five/hbrtl/strings.go"
|
||||
"lastFile": "/mnt/d/charles/five/hbrtl/missing.go"
|
||||
},
|
||||
"tests": {
|
||||
"phase": "do",
|
||||
@@ -280,7 +280,7 @@
|
||||
"session": {
|
||||
"startedAt": "2026-03-27T06:06:49.620Z",
|
||||
"onboardingCompleted": false,
|
||||
"lastActivity": "2026-04-02T06:00:35.695Z"
|
||||
"lastActivity": "2026-04-02T06:32:27.120Z"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
@@ -5952,6 +5952,36 @@
|
||||
"feature": "hbrt",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-02T06:27:16.050Z",
|
||||
"feature": "hbrtl",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-02T06:27:58.417Z",
|
||||
"feature": "hbrtl",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-02T06:29:57.154Z",
|
||||
"feature": "hbrtl",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-02T06:30:58.549Z",
|
||||
"feature": "hbrtl",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-04-02T06:32:27.120Z",
|
||||
"feature": "hbrtl",
|
||||
"phase": "do",
|
||||
"action": "updated"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -94,3 +94,102 @@ func FError(t *hbrt.Thread) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -276,25 +276,7 @@ func PCount(t *hbrt.Thread) {
|
||||
t.RetInt(int64(t.ParamCount()))
|
||||
}
|
||||
|
||||
// Break throws a recoverable error (for BEGIN SEQUENCE).
|
||||
func Break(t *hbrt.Thread) {
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
var desc string
|
||||
if nParams > 0 {
|
||||
v := t.Local(1)
|
||||
if v.IsString() {
|
||||
desc = v.AsString()
|
||||
} else {
|
||||
desc = "BREAK"
|
||||
}
|
||||
} else {
|
||||
desc = "BREAK"
|
||||
}
|
||||
// Clean up our own frame before panicking
|
||||
t.EndProcNoRecover()
|
||||
panic(&hbrt.HbError{Description: desc, GenCode: 0})
|
||||
}
|
||||
// Break moved to error.go — full implementation with BreakValue type.
|
||||
|
||||
// Array creates array of given size.
|
||||
func ArrayFunc(t *hbrt.Thread) {
|
||||
|
||||
@@ -244,6 +244,24 @@ func RegisterRTL(vm *hbrt.VM) {
|
||||
|
||||
// SET commands
|
||||
hbrt.Sym("SET", hbrt.FsPublic, SetFunc),
|
||||
hbrt.Sym("__SETDATEFORMAT", hbrt.FsPublic, SetDateFunc),
|
||||
hbrt.Sym("__SETDECIMALS", hbrt.FsPublic, SetDecimalsFunc),
|
||||
hbrt.Sym("__SETEPOCH", hbrt.FsPublic, SetEpochFunc),
|
||||
// SET constants
|
||||
hbrt.Sym("_SET_EXACT", hbrt.FsPublic, SetConstExact),
|
||||
hbrt.Sym("_SET_DELETED", hbrt.FsPublic, SetConstDeleted),
|
||||
hbrt.Sym("_SET_SOFTSEEK", hbrt.FsPublic, SetConstSoftSeek),
|
||||
hbrt.Sym("_SET_EXCLUSIVE", hbrt.FsPublic, SetConstExclusive),
|
||||
hbrt.Sym("_SET_DATEFORMAT", hbrt.FsPublic, SetConstDateFmt),
|
||||
hbrt.Sym("_SET_DECIMALS", hbrt.FsPublic, SetConstDecimals),
|
||||
hbrt.Sym("_SET_EPOCH", hbrt.FsPublic, SetConstEpoch),
|
||||
|
||||
// Error handling
|
||||
hbrt.Sym("ERRORBLOCK", hbrt.FsPublic, ErrorBlock),
|
||||
hbrt.Sym("ERRORNEW", hbrt.FsPublic, ErrorNew),
|
||||
hbrt.Sym("DOSERROR", hbrt.FsPublic, DosError),
|
||||
hbrt.Sym("FERROR", hbrt.FsPublic, FError),
|
||||
hbrt.Sym("BREAK", hbrt.FsPublic, Break),
|
||||
|
||||
// File I/O
|
||||
hbrt.Sym("FOPEN", hbrt.FsPublic, FOpen),
|
||||
|
||||
158
hbrtl/setcmd.go
158
hbrtl/setcmd.go
@@ -114,3 +114,161 @@ func GetSetExact() bool {
|
||||
func GetSetSoftSeek() bool {
|
||||
return GetSetting(SetSoftSeek).AsBool()
|
||||
}
|
||||
|
||||
// GetSetDateFormat returns SET DATE format string.
|
||||
func GetSetDateFormat() string {
|
||||
v := GetSetting(SetDateFmt)
|
||||
if v.IsString() {
|
||||
return v.AsString()
|
||||
}
|
||||
return "mm/dd/yy"
|
||||
}
|
||||
|
||||
// GetSetDecimals returns SET DECIMALS value.
|
||||
func GetSetDecimals() int {
|
||||
return GetSetting(SetDecimals).AsInt()
|
||||
}
|
||||
|
||||
// GetSetEpoch returns SET EPOCH year.
|
||||
func GetSetEpoch() int {
|
||||
v := GetSetting(SetEpoch)
|
||||
if v.IsNumeric() {
|
||||
return v.AsInt()
|
||||
}
|
||||
return 1900
|
||||
}
|
||||
|
||||
// --- Dedicated SET toggle functions ---
|
||||
// Harbour: SET EXACT ON/OFF, SET DELETED ON/OFF, etc.
|
||||
|
||||
// SetExactFunc: SET(_SET_EXACT [, lNew]) → lOld
|
||||
func SetExactFunc(t *hbrt.Thread) { setToggle(t, SetExact) }
|
||||
|
||||
// SetDeletedFunc: SET(_SET_DELETED [, lNew]) → lOld
|
||||
func SetDeletedFunc(t *hbrt.Thread) { setToggle(t, SetDeleted) }
|
||||
|
||||
// SetSoftSeekFunc: SET(_SET_SOFTSEEK [, lNew]) → lOld
|
||||
func SetSoftSeekFunc(t *hbrt.Thread) { setToggle(t, SetSoftSeek) }
|
||||
|
||||
// SetExclusiveFunc: SET(_SET_EXCLUSIVE [, lNew]) → lOld
|
||||
func SetExclusiveFunc(t *hbrt.Thread) { setToggle(t, SetExclusive) }
|
||||
|
||||
// SetFixedFunc: SET(_SET_FIXED [, lNew]) → lOld
|
||||
func SetFixedFunc(t *hbrt.Thread) { setToggle(t, SetFixed) }
|
||||
|
||||
// SetCancelFunc: SET(_SET_CANCEL [, lNew]) → lOld
|
||||
func SetCancelFunc(t *hbrt.Thread) { setToggle(t, SetCancel) }
|
||||
|
||||
// SetBellFunc: SET(_SET_BELL [, lNew]) → lOld
|
||||
func SetBellFunc(t *hbrt.Thread) { setToggle(t, SetBell) }
|
||||
|
||||
// SetConfirmFunc: SET(_SET_CONFIRM [, lNew]) → lOld
|
||||
func SetConfirmFunc(t *hbrt.Thread) { setToggle(t, SetConfirm) }
|
||||
|
||||
// SetInsertFunc: SET(_SET_INSERT [, lNew]) → lOld
|
||||
func SetInsertFunc(t *hbrt.Thread) { setToggle(t, SetInsert) }
|
||||
|
||||
// SetEscapeFunc: SET(_SET_ESCAPE [, lNew]) → lOld
|
||||
func SetEscapeFunc(t *hbrt.Thread) { setToggle(t, SetEscape) }
|
||||
|
||||
// SetWrapFunc: SET(_SET_WRAP [, lNew]) → lOld
|
||||
func SetWrapFunc(t *hbrt.Thread) { setToggle(t, SetWrap) }
|
||||
|
||||
// setToggle implements SET(n, lNew) → lOld for boolean settings
|
||||
func setToggle(t *hbrt.Thread, setID int) {
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
defer t.EndProc()
|
||||
|
||||
setMu.Lock()
|
||||
defer setMu.Unlock()
|
||||
|
||||
old, ok := settings[setID]
|
||||
if !ok {
|
||||
old = hbrt.MakeBool(false)
|
||||
}
|
||||
|
||||
if nParams >= 1 && !t.Local(1).IsNil() {
|
||||
settings[setID] = t.Local(1)
|
||||
}
|
||||
t.RetVal(old)
|
||||
}
|
||||
|
||||
// SetDateFunc: __SetDateFormat([cNew]) → cOld
|
||||
func SetDateFunc(t *hbrt.Thread) {
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
defer t.EndProc()
|
||||
|
||||
setMu.Lock()
|
||||
defer setMu.Unlock()
|
||||
|
||||
old, ok := settings[SetDateFmt]
|
||||
if !ok {
|
||||
old = hbrt.MakeString("mm/dd/yy")
|
||||
}
|
||||
|
||||
if nParams >= 1 && t.Local(1).IsString() {
|
||||
dfmt := t.Local(1).AsString()
|
||||
settings[SetDateFmt] = hbrt.MakeString(dfmt)
|
||||
}
|
||||
t.RetVal(old)
|
||||
}
|
||||
|
||||
// SetDecimalsFunc: SET DECIMALS TO n
|
||||
func SetDecimalsFunc(t *hbrt.Thread) {
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
defer t.EndProc()
|
||||
|
||||
setMu.Lock()
|
||||
defer setMu.Unlock()
|
||||
|
||||
old := settings[SetDecimals]
|
||||
if nParams >= 1 && t.Local(1).IsNumeric() {
|
||||
settings[SetDecimals] = t.Local(1)
|
||||
}
|
||||
t.RetVal(old)
|
||||
}
|
||||
|
||||
// SetEpochFunc: SET EPOCH TO n
|
||||
func SetEpochFunc(t *hbrt.Thread) {
|
||||
nParams := t.ParamCount()
|
||||
t.Frame(nParams, 0)
|
||||
defer t.EndProc()
|
||||
|
||||
setMu.Lock()
|
||||
defer setMu.Unlock()
|
||||
|
||||
old, ok := settings[SetEpoch]
|
||||
if !ok {
|
||||
old = hbrt.MakeInt(1900)
|
||||
}
|
||||
if nParams >= 1 && t.Local(1).IsNumeric() {
|
||||
settings[SetEpoch] = t.Local(1)
|
||||
}
|
||||
t.RetVal(old)
|
||||
}
|
||||
|
||||
// --- SET constants for PRG code (registered as RTL functions) ---
|
||||
|
||||
// _SET_EXACT etc. — return the SET index constant
|
||||
func SetConstExact(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetExact); t.RetValue() }
|
||||
func SetConstDeleted(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetDeleted); t.RetValue() }
|
||||
func SetConstSoftSeek(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetSoftSeek); t.RetValue() }
|
||||
func SetConstExclusive(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetExclusive); t.RetValue() }
|
||||
func SetConstDateFmt(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetDateFmt); t.RetValue() }
|
||||
func SetConstDecimals(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetDecimals); t.RetValue() }
|
||||
func SetConstEpoch(t *hbrt.Thread) { t.Frame(0, 0); defer t.EndProc(); t.PushInt(SetEpoch); t.RetValue() }
|
||||
|
||||
// init registers default DATE format and EPOCH
|
||||
func init() {
|
||||
setMu.Lock()
|
||||
if _, ok := settings[SetDateFmt]; !ok {
|
||||
settings[SetDateFmt] = hbrt.MakeString("mm/dd/yy")
|
||||
}
|
||||
if _, ok := settings[SetEpoch]; !ok {
|
||||
settings[SetEpoch] = hbrt.MakeInt(1900)
|
||||
}
|
||||
setMu.Unlock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user