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>
This commit is contained in:
930
compiler/ast/ast.go
Normal file
930
compiler/ast/ast.go
Normal file
@@ -0,0 +1,930 @@
|
||||
// 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 { 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
|
||||
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 ¯o 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
|
||||
}
|
||||
|
||||
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 TO file [FOR cond] [UNIQUE] [DESCENDING]
|
||||
type IndexCmd struct {
|
||||
IndexPos token.Position
|
||||
KeyExpr Expr
|
||||
File Expr
|
||||
ForCond Expr // nil if no FOR
|
||||
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() {}
|
||||
Reference in New Issue
Block a user