Files
five/compiler/ast/ast.go
CharlesKWON 66f045b97e 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>
2026-04-18 15:54:44 +09:00

943 lines
28 KiB
Go

// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
// All rights reserved.
// AST node definitions for the Five language.
//
// Design references:
// - Harbour: HB_EXPR (hbcompdf.h:349) — expression union with ExprType discriminant
// - Harbour: HB_HFUNC (hbcompdf.h:497) — function with separated pLocals/pStatics/pFields/pMemvars
// - tsgo: Node with Kind discriminant + nodeData interface (internal/ast/ast.go)
//
// Key Harbour rules applied:
// - LOCAL/STATIC/FIELD declarations must appear at function top, before executable code
// - FuncDecl separates Decls (declarations) from Body (executable statements)
// - (expr)->field for dynamic alias access (HB_ET_ALIASEXPR)
// - &variable for macro (6 subtypes from Harbour: VAR, SYMBOL, ALIASED, EXPR, LIST, PARE)
package ast
import "five/compiler/token"
// --- Interfaces ---
// Node is the base interface for all AST nodes.
type Node interface {
Pos() token.Position
End() token.Position
}
// Expr represents an expression node (produces a value).
type Expr interface {
Node
exprNode()
}
// Stmt represents a statement node (performs an action).
type Stmt interface {
Node
stmtNode()
}
// Decl represents a declaration node (LOCAL, STATIC, FIELD, etc.).
type Decl interface {
Node
declNode()
}
// --- Program (top-level) ---
// File represents a single .prg source file.
type File struct {
Name string // filename
Imports []*ImportDecl
Decls []Decl // top-level: FUNCTION, PROCEDURE, CLASS, etc.
}
func (f *File) Pos() token.Position {
if len(f.Decls) > 0 {
return f.Decls[0].Pos()
}
return token.Position{}
}
func (f *File) End() token.Position {
if len(f.Decls) > 0 {
return f.Decls[len(f.Decls)-1].End()
}
return token.Position{}
}
// --- Declarations ---
// ImportDecl: IMPORT "package/path" or IMPORT _ "package/path"
type ImportDecl struct {
ImportPos token.Position
Alias string // "" = normal, "_" = blank import, "name" = alias
Path string // package path
}
func (d *ImportDecl) Pos() token.Position { return d.ImportPos }
func (d *ImportDecl) End() token.Position { return d.ImportPos }
func (d *ImportDecl) declNode() {}
// FuncDecl represents FUNCTION or PROCEDURE.
// Harbour: HB_HFUNC — pLocals, pStatics, pFields separated from pcode.
// LOCAL/STATIC/FIELD must appear before executable code.
type FuncDecl struct {
FuncPos token.Position
Name string
IsProc bool // PROCEDURE (no return value)
Params []*ParamDecl // declared parameters
Decls []Decl // LOCAL, STATIC, FIELD — must come first
Body []Stmt // executable statements — after declarations
EndPos token.Position
}
func (d *FuncDecl) Pos() token.Position { return d.FuncPos }
func (d *FuncDecl) End() token.Position { return d.EndPos }
func (d *FuncDecl) declNode() {}
// ParamDecl represents a function parameter.
type ParamDecl struct {
NamePos token.Position
Name string
ByRef bool // @param or passed by reference
AsType string // optional type hint: AS NUMERIC, AS STRING, etc.
}
func (d *ParamDecl) Pos() token.Position { return d.NamePos }
func (d *ParamDecl) End() token.Position { return d.NamePos }
func (d *ParamDecl) declNode() {}
// VarDecl represents LOCAL, STATIC, PRIVATE, PUBLIC, FIELD declarations.
// Harbour: LOCAL must be at function top (before executable code).
// PRIVATE/PUBLIC can appear anywhere (runtime memvar).
type VarDecl struct {
DeclPos token.Position
Scope VarScope
Vars []*VarInit // one or more: LOCAL a := 1, b := 2, c
}
func (d *VarDecl) Pos() token.Position { return d.DeclPos }
func (d *VarDecl) End() token.Position {
if len(d.Vars) > 0 {
return d.Vars[len(d.Vars)-1].NamePos
}
return d.DeclPos
}
func (d *VarDecl) declNode() {}
func (d *VarDecl) stmtNode() {} // PRIVATE/PUBLIC can appear as statements
// VarScope indicates where a variable lives.
type VarScope int
const (
ScopeLocal VarScope = iota // LOCAL — stack, function-top only
ScopeStatic // STATIC — module-level, function-top only
ScopePrivate // PRIVATE — runtime memvar, anywhere
ScopePublic // PUBLIC — runtime memvar, anywhere
ScopeField // FIELD — database field declaration, function-top only
)
// VarInit represents a single variable with optional initializer.
type VarInit struct {
NamePos token.Position
Name string
Init Expr // nil if no initializer
AsType string // optional type hint
}
// ClassDecl represents CLASS ... ENDCLASS.
type ClassDecl struct {
ClassPos token.Position
Name string
ParentName string // INHERIT FROM parent
Members []Decl // DATA, METHOD, ACCESS, ASSIGN declarations
EndPos token.Position
}
func (d *ClassDecl) Pos() token.Position { return d.ClassPos }
func (d *ClassDecl) End() token.Position { return d.EndPos }
func (d *ClassDecl) declNode() {}
// DataDecl represents DATA member in a class.
type DataDecl struct {
DataPos token.Position
Name string
Init Expr // INIT expression (nil if none)
AsType string // AS type hint
}
func (d *DataDecl) Pos() token.Position { return d.DataPos }
func (d *DataDecl) End() token.Position { return d.DataPos }
func (d *DataDecl) declNode() {}
// MethodDecl represents METHOD declaration in a class or standalone.
type MethodDecl struct {
MethodPos token.Position
Name string
ClassName string // METHOD name CLASS classname (standalone)
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
AccessName string // property name for ACCESS/ASSIGN
Decls []Decl
Body []Stmt
EndPos token.Position
}
func (d *MethodDecl) Pos() token.Position { return d.MethodPos }
func (d *MethodDecl) End() token.Position { return d.EndPos }
func (d *MethodDecl) declNode() {}
// GoDumpDecl represents inline Go code from #pragma BEGINDUMP ... #pragma ENDDUMP.
// Five extension: allows embedding raw Go code directly in PRG files.
type GoDumpDecl struct {
DumpPos token.Position
Code string // raw Go source code
}
func (d *GoDumpDecl) Pos() token.Position { return d.DumpPos }
func (d *GoDumpDecl) End() token.Position { return d.DumpPos }
func (d *GoDumpDecl) declNode() {}
// --- Expressions ---
// LiteralExpr represents a literal value.
// Harbour: HB_ET_NIL, HB_ET_NUMERIC, HB_ET_STRING, HB_ET_LOGICAL, HB_ET_DATE, HB_ET_TIMESTAMP
type LiteralExpr struct {
ValuePos token.Position
Kind token.Kind // INT, LONG, DOUBLE, STRING, TRUE, FALSE, NIL_LIT, DATE_LIT
Value string // raw literal text
}
func (e *LiteralExpr) Pos() token.Position { return e.ValuePos }
func (e *LiteralExpr) End() token.Position { return e.ValuePos }
func (e *LiteralExpr) exprNode() {}
// IdentExpr represents a variable or function name.
// Harbour: HB_ET_VARIABLE, HB_ET_FUNNAME
type IdentExpr struct {
NamePos token.Position
Name string
}
func (e *IdentExpr) Pos() token.Position { return e.NamePos }
func (e *IdentExpr) End() token.Position { return e.NamePos }
func (e *IdentExpr) exprNode() {}
// SelfExpr represents :: (Self access in class method).
// Harbour: HB_ET_SELF
type SelfExpr struct {
ColonPos token.Position
}
func (e *SelfExpr) Pos() token.Position { return e.ColonPos }
func (e *SelfExpr) End() token.Position { return e.ColonPos }
func (e *SelfExpr) exprNode() {}
// BinaryExpr represents a binary operation.
// Harbour: HB_EO_PLUS, HB_EO_MINUS, HB_EO_EQUAL, etc.
type BinaryExpr struct {
Left Expr
OpPos token.Position
Op token.Kind
Right Expr
}
func (e *BinaryExpr) Pos() token.Position { return e.Left.Pos() }
func (e *BinaryExpr) End() token.Position { return e.Right.End() }
func (e *BinaryExpr) exprNode() {}
// UnaryExpr represents a prefix unary operation.
// Harbour: HB_EO_NEGATE, HB_EO_NOT, HB_EO_PREINC, HB_EO_PREDEC
type UnaryExpr struct {
OpPos token.Position
Op token.Kind // MINUS, NOT, INC, DEC
X Expr
}
func (e *UnaryExpr) Pos() token.Position { return e.OpPos }
func (e *UnaryExpr) End() token.Position { return e.X.End() }
func (e *UnaryExpr) exprNode() {}
// PostfixExpr represents postfix ++ or --.
// Harbour: HB_EO_POSTINC, HB_EO_POSTDEC
type PostfixExpr struct {
X Expr
OpPos token.Position
Op token.Kind // INC, DEC
}
func (e *PostfixExpr) Pos() token.Position { return e.X.Pos() }
func (e *PostfixExpr) End() token.Position { return e.OpPos }
func (e *PostfixExpr) exprNode() {}
// AssignExpr represents assignment: x := value, x += value, etc.
// Harbour: HB_EO_ASSIGN, HB_EO_PLUSEQ, etc.
type AssignExpr struct {
Left Expr
OpPos token.Position
Op token.Kind // ASSIGN, PLUSEQ, MINUSEQ, etc.
Right Expr
}
func (e *AssignExpr) Pos() token.Position { return e.Left.Pos() }
func (e *AssignExpr) End() token.Position { return e.Right.End() }
func (e *AssignExpr) exprNode() {}
// CallExpr represents a function call: func(args...)
// Harbour: HB_ET_FUNCALL — pFunName + pParms
type CallExpr struct {
Func Expr // function expression (IdentExpr, or macro)
LParen token.Position
Args []Expr
RParen token.Position
}
func (e *CallExpr) Pos() token.Position { return e.Func.Pos() }
func (e *CallExpr) End() token.Position { return e.RParen }
func (e *CallExpr) exprNode() {}
// DotExpr represents package member access: pkg.Member
// Used for Go package function calls: sql.Open(), fmt.Println()
type DotExpr struct {
X Expr // package (IdentExpr)
DotPos token.Position
Member string // function/field name
}
func (e *DotExpr) Pos() token.Position { return e.X.Pos() }
func (e *DotExpr) End() token.Position { return e.DotPos }
func (e *DotExpr) exprNode() {}
// SendExpr represents method call: obj:method(args...)
// Harbour: HB_ET_SEND — pObject + szMessage/pMessage + pParms
type SendExpr struct {
Object Expr
ColonPos token.Position
Method string // static message name
MacroMethod Expr // if &macro message (nil for static)
HasParens bool // true if () present (method call vs field access)
LParen token.Position
Args []Expr
RParen token.Position
IsAssign bool // obj:prop := value (setter)
}
func (e *SendExpr) Pos() token.Position { return e.Object.Pos() }
func (e *SendExpr) End() token.Position { return e.RParen }
func (e *SendExpr) exprNode() {}
// IndexExpr represents array index: arr[index]
// Harbour: HB_ET_ARRAYAT
type IndexExpr struct {
X Expr
LBracket token.Position
Index Expr
RBracket token.Position
}
func (e *IndexExpr) Pos() token.Position { return e.X.Pos() }
func (e *IndexExpr) End() token.Position { return e.RBracket }
func (e *IndexExpr) exprNode() {}
// AliasExpr represents field access: alias->field or (expr)->field
// Harbour: HB_ET_ALIASVAR, HB_ET_ALIASEXPR
type AliasExpr struct {
Alias Expr // IdentExpr for static alias, any Expr for (dynamic)->field
ArrowPos token.Position
Field Expr // IdentExpr or MacroExpr
}
func (e *AliasExpr) Pos() token.Position { return e.Alias.Pos() }
func (e *AliasExpr) End() token.Position { return e.Field.End() }
func (e *AliasExpr) exprNode() {}
// MacroExpr represents macro expansion: &variable or &(expression)
// Harbour: HB_ET_MACRO with 6 subtypes
type MacroExpr struct {
AmpPos token.Position
Expr Expr // variable or parenthesized expression
}
func (e *MacroExpr) Pos() token.Position { return e.AmpPos }
func (e *MacroExpr) End() token.Position { return e.Expr.End() }
func (e *MacroExpr) exprNode() {}
// BlockExpr represents a code block: {|params| body}
// Harbour: HB_ET_CODEBLOCK — pLocals + pExprList
type BlockExpr struct {
LBrace token.Position
Params []string // parameter names (between | |)
Body Expr // single expression (or comma-separated list)
RBrace token.Position
}
func (e *BlockExpr) Pos() token.Position { return e.LBrace }
func (e *BlockExpr) End() token.Position { return e.RBrace }
func (e *BlockExpr) exprNode() {}
// ArrayLitExpr represents a literal array: {1, 2, 3}
// Harbour: HB_ET_ARRAY
type ArrayLitExpr struct {
LBrace token.Position
Items []Expr
RBrace token.Position
}
func (e *ArrayLitExpr) Pos() token.Position { return e.LBrace }
func (e *ArrayLitExpr) End() token.Position { return e.RBrace }
func (e *ArrayLitExpr) exprNode() {}
// HashLitExpr represents a literal hash: {"a" => 1, "b" => 2}
// Harbour: HB_ET_HASH
type HashLitExpr struct {
LBrace token.Position
Keys []Expr
Values []Expr
RBrace token.Position
}
func (e *HashLitExpr) Pos() token.Position { return e.LBrace }
func (e *HashLitExpr) End() token.Position { return e.RBrace }
func (e *HashLitExpr) exprNode() {}
// IIfExpr represents inline if: IIF(cond, trueVal, falseVal)
// Harbour: HB_ET_IIF
type IIfExpr struct {
IfPos token.Position
Cond Expr
True Expr
False Expr
}
func (e *IIfExpr) Pos() token.Position { return e.IfPos }
func (e *IIfExpr) End() token.Position { return e.False.End() }
func (e *IIfExpr) exprNode() {}
// RefExpr represents pass-by-reference: @variable
// Harbour: HB_ET_REFERENCE, HB_ET_VARREF, HB_ET_FUNREF
type RefExpr struct {
AtPos token.Position
X Expr
}
func (e *RefExpr) Pos() token.Position { return e.AtPos }
func (e *RefExpr) End() token.Position { return e.X.End() }
func (e *RefExpr) exprNode() {}
// --- Statements ---
// ExprStmt wraps an expression as a statement (function calls, assignments).
type ExprStmt struct {
X Expr
}
func (s *ExprStmt) Pos() token.Position { return s.X.Pos() }
func (s *ExprStmt) End() token.Position { return s.X.End() }
func (s *ExprStmt) stmtNode() {}
// ReturnStmt represents RETURN [expr].
type ReturnStmt struct {
ReturnPos token.Position
Value Expr // first/only return value (nil for bare RETURN)
Values []Expr // multi-return: RETURN a, b, c (nil if single)
}
func (s *ReturnStmt) Pos() token.Position { return s.ReturnPos }
func (s *ReturnStmt) End() token.Position {
if s.Value != nil {
return s.Value.End()
}
return s.ReturnPos
}
func (s *ReturnStmt) stmtNode() {}
// QOutStmt represents ? expr, expr, ... (shorthand for QOut).
type QOutStmt struct {
QPos token.Position
IsQQ bool // true for ?? (QQOut)
Exprs []Expr
}
func (s *QOutStmt) Pos() token.Position { return s.QPos }
func (s *QOutStmt) End() token.Position {
if len(s.Exprs) > 0 {
return s.Exprs[len(s.Exprs)-1].End()
}
return s.QPos
}
func (s *QOutStmt) stmtNode() {}
// IfStmt represents IF / ELSEIF / ELSE / ENDIF.
// Harbour: uses PHB_ELSEIF chain for fixups.
type IfStmt struct {
IfPos token.Position
Cond Expr
Body []Stmt
ElseIfs []*ElseIfClause
ElseBody []Stmt // nil if no ELSE
EndPos token.Position
}
type ElseIfClause struct {
ElseIfPos token.Position
Cond Expr
Body []Stmt
}
func (s *IfStmt) Pos() token.Position { return s.IfPos }
func (s *IfStmt) End() token.Position { return s.EndPos }
func (s *IfStmt) stmtNode() {}
// DoWhileStmt represents DO WHILE cond ... ENDDO.
type DoWhileStmt struct {
DoPos token.Position
Cond Expr
Body []Stmt
EndPos token.Position
}
func (s *DoWhileStmt) Pos() token.Position { return s.DoPos }
func (s *DoWhileStmt) End() token.Position { return s.EndPos }
func (s *DoWhileStmt) stmtNode() {}
// ForStmt represents FOR var := start TO end [STEP step] ... NEXT.
type ForStmt struct {
ForPos token.Position
Var string
Start Expr
To Expr
Step Expr // nil for default step 1
Body []Stmt
NextPos token.Position
}
func (s *ForStmt) Pos() token.Position { return s.ForPos }
func (s *ForStmt) End() token.Position { return s.NextPos }
func (s *ForStmt) stmtNode() {}
// ForEachStmt represents FOR EACH var IN collection ... NEXT.
// Harbour: HB_ENUMERATOR structure.
type ForEachStmt struct {
ForPos token.Position
Var string
Collection Expr
Descend bool // FOR EACH DESCEND
Body []Stmt
NextPos token.Position
}
func (s *ForEachStmt) Pos() token.Position { return s.ForPos }
func (s *ForEachStmt) End() token.Position { return s.NextPos }
func (s *ForEachStmt) stmtNode() {}
// SwitchStmt represents SWITCH expr ... CASE ... OTHERWISE ... END.
// Harbour: HB_SWITCHCMD structure.
type SwitchStmt struct {
SwitchPos token.Position
Expr Expr
Cases []*CaseClause
Otherwise []Stmt // nil if no OTHERWISE
EndPos token.Position
}
type CaseClause struct {
CasePos token.Position
Value Expr // case value
Body []Stmt
}
func (s *SwitchStmt) Pos() token.Position { return s.SwitchPos }
func (s *SwitchStmt) End() token.Position { return s.EndPos }
func (s *SwitchStmt) stmtNode() {}
// SeqStmt represents BEGIN SEQUENCE ... RECOVER [USING var] ... END.
type SeqStmt struct {
BeginPos token.Position
Body []Stmt
RecoverVar string // variable name after USING (empty if none)
RecoverBody []Stmt // nil if no RECOVER
EndPos token.Position
}
func (s *SeqStmt) Pos() token.Position { return s.BeginPos }
func (s *SeqStmt) End() token.Position { return s.EndPos }
func (s *SeqStmt) stmtNode() {}
// === Five Go Extensions ===
// MultiAssignStmt: a, b, c := expr or a, b := Func()
// Also handles: a, b := b, a (parallel swap)
// Blank identifier _ discards the value.
type MultiAssignStmt struct {
AssignPos token.Position
Targets []string // variable names ("_" = discard)
Values []Expr // right-hand side expressions
}
func (s *MultiAssignStmt) Pos() token.Position { return s.AssignPos }
func (s *MultiAssignStmt) End() token.Position { return s.AssignPos }
func (s *MultiAssignStmt) stmtNode() {}
// DeferStmt: DEFER expr (execute when function returns)
type DeferStmt struct {
DeferPos token.Position
Call Expr // expression to defer (usually a method/function call)
}
func (s *DeferStmt) Pos() token.Position { return s.DeferPos }
func (s *DeferStmt) End() token.Position { return s.DeferPos }
func (s *DeferStmt) stmtNode() {}
// ConstDecl: CONST block with optional auto-increment
type ConstDecl struct {
ConstPos token.Position
Items []ConstItem
}
type ConstItem struct {
Name string
Value Expr // nil = auto-increment from previous
}
func (d *ConstDecl) Pos() token.Position { return d.ConstPos }
func (d *ConstDecl) End() token.Position { return d.ConstPos }
func (d *ConstDecl) declNode() {}
// SliceExpr: a[low:high] — sub-array or sub-string
type SliceExpr struct {
X Expr
LBracket token.Position
Low Expr // nil = from start
High Expr // nil = to end
RBracket token.Position
}
func (e *SliceExpr) Pos() token.Position { return e.X.Pos() }
func (e *SliceExpr) End() token.Position { return e.RBracket }
func (e *SliceExpr) exprNode() {}
// NilSafeExpr: obj?:Method() — returns NIL if obj is NIL
type NilSafeExpr struct {
X Expr
QPos token.Position
Method string
Args []Expr
HasParens bool
}
func (e *NilSafeExpr) Pos() token.Position { return e.X.Pos() }
func (e *NilSafeExpr) End() token.Position { return e.QPos }
func (e *NilSafeExpr) exprNode() {}
// InterpolatedString: f"Hello {name}, age {age}"
type InterpolatedString struct {
FPos token.Position
Parts []Expr // alternating: LiteralExpr (text), other Expr (interpolated)
}
func (e *InterpolatedString) Pos() token.Position { return e.FPos }
func (e *InterpolatedString) End() token.Position { return e.FPos }
func (e *InterpolatedString) exprNode() {}
// === Five Concurrency Extensions ===
// ChanSendStmt: ch <- value
type ChanSendStmt struct {
ChanPos token.Position
Chan Expr // channel expression
Value Expr // value to send
}
func (s *ChanSendStmt) Pos() token.Position { return s.ChanPos }
func (s *ChanSendStmt) End() token.Position { return s.ChanPos }
func (s *ChanSendStmt) stmtNode() {}
// ChanRecvExpr: <- ch (receive from channel, used as expression)
type ChanRecvExpr struct {
ArrowPos token.Position
Chan Expr
}
func (e *ChanRecvExpr) Pos() token.Position { return e.ArrowPos }
func (e *ChanRecvExpr) End() token.Position { return e.ArrowPos }
func (e *ChanRecvExpr) exprNode() {}
// WatchStmt: WATCH / CASE <- ch / CASE ch <- val / OTHERWISE / ENDWATCH
type WatchStmt struct {
WatchPos token.Position
Cases []*WatchCase
Otherwise []Stmt
EndPos token.Position
}
type WatchCase struct {
CasePos token.Position
RecvChan Expr // CASE val := <- ch (receive)
RecvVar string // variable name for received value ("" if none)
SendChan Expr // CASE ch <- val (send)
SendVal Expr // value to send
Body []Stmt
}
func (s *WatchStmt) Pos() token.Position { return s.WatchPos }
func (s *WatchStmt) End() token.Position { return s.EndPos }
func (s *WatchStmt) stmtNode() {}
// GoBlockStmt: GO { ... } — inline goroutine
type GoBlockStmt struct {
GoPos token.Position
Block *BlockExpr // code block to execute
}
func (s *GoBlockStmt) Pos() token.Position { return s.GoPos }
func (s *GoBlockStmt) End() token.Position { return s.GoPos }
func (s *GoBlockStmt) stmtNode() {}
// ParallelForStmt: PARALLEL FOR i := 1 TO n / body / NEXT
type ParallelForStmt struct {
ForPos token.Position
Var string
Start Expr
To Expr
Step Expr // nil = default 1
Body []Stmt
EndPos token.Position
}
func (s *ParallelForStmt) Pos() token.Position { return s.ForPos }
func (s *ParallelForStmt) End() token.Position { return s.EndPos }
func (s *ParallelForStmt) stmtNode() {}
// AsyncExpr: ASYNC expr — returns a future/channel
type AsyncExpr struct {
AsyncPos token.Position
Call Expr
}
func (e *AsyncExpr) Pos() token.Position { return e.AsyncPos }
func (e *AsyncExpr) End() token.Position { return e.AsyncPos }
func (e *AsyncExpr) exprNode() {}
// AwaitExpr: AWAIT future — blocks until result ready
type AwaitExpr struct {
AwaitPos token.Position
Future Expr
}
func (e *AwaitExpr) Pos() token.Position { return e.AwaitPos }
func (e *AwaitExpr) End() token.Position { return e.AwaitPos }
func (e *AwaitExpr) exprNode() {}
// TimeoutStmt: WITH TIMEOUT n / body / ENDWITH
type TimeoutStmt struct {
WithPos token.Position
Duration Expr // timeout in seconds
Body []Stmt
EndPos token.Position
}
func (s *TimeoutStmt) Pos() token.Position { return s.WithPos }
func (s *TimeoutStmt) End() token.Position { return s.EndPos }
func (s *TimeoutStmt) stmtNode() {}
// === End Five Go Extensions ===
// ExitStmt represents EXIT (break out of loop).
type ExitStmt struct {
ExitPos token.Position
}
func (s *ExitStmt) Pos() token.Position { return s.ExitPos }
func (s *ExitStmt) End() token.Position { return s.ExitPos }
func (s *ExitStmt) stmtNode() {}
// LoopStmt represents LOOP (continue to next iteration).
type LoopStmt struct {
LoopPos token.Position
}
func (s *LoopStmt) Pos() token.Position { return s.LoopPos }
func (s *LoopStmt) End() token.Position { return s.LoopPos }
func (s *LoopStmt) stmtNode() {}
// --- xBase command statements ---
// UseCmd represents USE [file] [VIA driver] [ALIAS name] [EXCLUSIVE|SHARED]
type UseCmd struct {
UsePos token.Position
File Expr // filename expression (nil = close current)
Via string // RDD driver name
Alias string // alias name (static)
AliasExpr Expr // alias expression for ALIAS (expr) — dynamic alias
Shared bool // SHARED flag
ReadOnly bool // READONLY flag
}
func (s *UseCmd) Pos() token.Position { return s.UsePos }
func (s *UseCmd) End() token.Position { return s.UsePos }
func (s *UseCmd) stmtNode() {}
// SelectCmd represents SELECT area
type SelectCmd struct {
SelectPos token.Position
Area Expr // area number or alias name
}
func (s *SelectCmd) Pos() token.Position { return s.SelectPos }
func (s *SelectCmd) End() token.Position { return s.SelectPos }
func (s *SelectCmd) stmtNode() {}
// GoCmd represents GO TOP / GO BOTTOM / GO recno / GOTO recno
type GoCmd struct {
GoPos token.Position
Direction string // "TOP", "BOTTOM", or ""
RecNo Expr // record number expression (nil for TOP/BOTTOM)
}
func (s *GoCmd) Pos() token.Position { return s.GoPos }
func (s *GoCmd) End() token.Position { return s.GoPos }
func (s *GoCmd) stmtNode() {}
// SkipCmd represents SKIP [n]
type SkipCmd struct {
SkipPos token.Position
Count Expr // nil for SKIP 1
}
func (s *SkipCmd) Pos() token.Position { return s.SkipPos }
func (s *SkipCmd) End() token.Position { return s.SkipPos }
func (s *SkipCmd) stmtNode() {}
// SeekCmd represents SEEK expr [SOFTSEEK]
type SeekCmd struct {
SeekPos token.Position
Key Expr
SoftSeek bool
}
func (s *SeekCmd) Pos() token.Position { return s.SeekPos }
func (s *SeekCmd) End() token.Position { return s.SeekPos }
func (s *SeekCmd) stmtNode() {}
// ReplaceCmd represents REPLACE field WITH expr [, field WITH expr ...]
type ReplaceCmd struct {
ReplacePos token.Position
Fields []ReplaceField
}
type ReplaceField struct {
Field Expr // field expression (may include alias)
Value Expr
}
func (s *ReplaceCmd) Pos() token.Position { return s.ReplacePos }
func (s *ReplaceCmd) End() token.Position { return s.ReplacePos }
func (s *ReplaceCmd) stmtNode() {}
// AppendCmd represents APPEND BLANK
type AppendCmd struct {
AppendPos token.Position
}
func (s *AppendCmd) Pos() token.Position { return s.AppendPos }
func (s *AppendCmd) End() token.Position { return s.AppendPos }
func (s *AppendCmd) stmtNode() {}
// DeleteCmd represents DELETE (mark current record for deletion)
type DeleteCmd struct {
DeletePos token.Position
}
func (s *DeleteCmd) Pos() token.Position { return s.DeletePos }
func (s *DeleteCmd) End() token.Position { return s.DeletePos }
func (s *DeleteCmd) stmtNode() {}
// IndexCmd represents INDEX ON expr [TAG tagname] TO file [FOR cond] [UNIQUE] [DESCENDING]
type IndexCmd struct {
IndexPos token.Position
KeyExpr Expr
File Expr
ForCond Expr // nil if no FOR
TagName string // TAG name for CDX compound index (empty = NTX)
Unique bool
Descending bool
}
func (s *IndexCmd) Pos() token.Position { return s.IndexPos }
func (s *IndexCmd) End() token.Position { return s.IndexPos }
func (s *IndexCmd) stmtNode() {}
// SetCmd represents SET commands: SET FILTER TO expr, SET RELATION TO expr INTO alias, etc.
type SetCmd struct {
SetPos token.Position
Setting string // "FILTER", "RELATION", "ORDER", "INDEX", etc.
Expr Expr // the value expression
Extra string // extra info (INTO alias, etc.)
}
func (s *SetCmd) Pos() token.Position { return s.SetPos }
func (s *SetCmd) End() token.Position { return s.SetPos }
func (s *SetCmd) stmtNode() {}
// AtSayCmd represents @ row, col SAY expr [PICTURE pic]
type AtSayCmd struct {
AtPos token.Position
Row Expr
Col Expr
SayExpr Expr
Picture Expr // nil if no PICTURE
}
func (s *AtSayCmd) Pos() token.Position { return s.AtPos }
func (s *AtSayCmd) End() token.Position { return s.AtPos }
func (s *AtSayCmd) stmtNode() {}
// AtGetCmd represents @ row, col GET var [PICTURE pic] [VALID valid] [WHEN when]
type AtGetCmd struct {
AtPos token.Position
Row Expr
Col Expr
Var Expr // the variable expression
VarName string // variable name as string
Picture Expr // nil if no PICTURE
Valid Expr // nil if no VALID (code block)
When Expr // nil if no WHEN (code block)
}
func (s *AtGetCmd) Pos() token.Position { return s.AtPos }
func (s *AtGetCmd) End() token.Position { return s.AtPos }
func (s *AtGetCmd) stmtNode() {}
// AtSayGetCmd represents @ row, col SAY expr GET var [PICTURE pic] [VALID valid] [WHEN when]
type AtSayGetCmd struct {
AtPos token.Position
Row Expr
Col Expr
SayExpr Expr
Var Expr
VarName string
Picture Expr
Valid Expr
When Expr
}
func (s *AtSayGetCmd) Pos() token.Position { return s.AtPos }
func (s *AtSayGetCmd) End() token.Position { return s.AtPos }
func (s *AtSayGetCmd) stmtNode() {}
// ReadCmd represents READ [SAVE]
type ReadCmd struct {
ReadPos token.Position
Save bool
}
func (s *ReadCmd) Pos() token.Position { return s.ReadPos }
func (s *ReadCmd) End() token.Position { return s.ReadPos }
func (s *ReadCmd) stmtNode() {}