feat(oop): OPERATOR overloading — + - * / == != < > <= >=

Harbour lets a class define custom behaviour for arithmetic and
comparison operators via `OPERATOR "<sym>" ARG <name> INLINE <expr>`.
Five already had the runtime slot infrastructure (ClassDef.Operators
+ AddOperator + parent-chain copy) but parser skipped the form and
the VM ops never consulted the slots.

Parser: parseOperatorDecl captures the symbol, ARG binding, and
INLINE body into a MethodDecl with IsOperator=true and OperatorOp
set to the hbrt.Op* slot. Synthesised method name is __OP_<idx>
to keep the regular method namespace clean.

Codegen: emitClassDecl routes IsOperator members through
_def.AddOperator instead of AddMethod. Inline body generation is
shared with the MESSAGE/INLINE path (34485cd).

VM: Thread.tryBinaryOp walks the LHS object's class operator slot,
pushes args with Self bound to LHS, and returns true if the slot
is populated. Wired into Plus/Minus/Mult/Divide and Equal/NotEqual/
Less/Greater/LessEqual/GreaterEqual. Falls through to built-in
behaviour when no overload exists — non-object LHS costs one tag
check per op.

Operator symbol→slot mapping keeps `=` and `==` on the same slot
(OpEqual=8) because Five's gengo routes both to t.Equal() and the
VM doesn't distinguish strict vs non-strict equality today.

Tested (/tmp/test_operator.prg): Vec2 + - == < with per-field
results all correct.

FiveSql2 43/43, Harbour compat 56/56, Go test ALL PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 15:54:44 +09:00
parent 34485cd6c8
commit 66f045b97e
6 changed files with 159 additions and 7 deletions

View File

@@ -178,6 +178,8 @@ type MethodDecl struct {
Params []*ParamDecl
IsInline bool // INLINE method
InlineBody Expr // inline expression body — `RETURN <expr>` equivalent
IsOperator bool // OPERATOR overload — OperatorOp carries the slot
OperatorOp int // operator slot (hbrt.Op* constant); valid only when IsOperator
IsSetGet bool // METHOD name(x) SETGET — getter if no arg, setter if arg
IsAccess bool // ACCESS name METHOD getterName
IsAssign bool // ASSIGN name METHOD setterName