package parser

import (
	"github.com/marzeq/qk/attributes"
	"github.com/marzeq/qk/shared"
	"github.com/marzeq/qk/symbols"
	"github.com/marzeq/qk/tokeniser"
	"github.com/marzeq/qk/types"
)

type (
	// Node locations are half-open spans covering the represented source syntax.
	Node           interface{ GetLoc() shared.Location }
	ExpressionNode interface {
		Node
		SetType(types.Type)
		GetType() types.Type
	}
)

type RootNode struct {
	Body []Node
	Loc  shared.Location
}

type WhenContext uint8

const (
	WhenDeclarations WhenContext = iota
	WhenStatements
	WhenExpression
	WhenType
)

type WhenBranchNode struct {
	Condition ExpressionNode
	Body      []Node
	Value     Node
	Loc       shared.Location
}

type WhenNode struct {
	Branches  []WhenBranchNode
	ElseBody  []Node
	ElseValue Node
	Context   WhenContext
	Loc       shared.Location
	Type      types.Type
}

func (n WhenNode) GetLoc() shared.Location { return n.Loc }
func (n *WhenNode) SetType(t types.Type)   { n.Type = t }
func (n *WhenNode) GetType() types.Type    { return n.Type }
func (n WhenNode) _type()                  {}

type CompilerDirectiveNode struct {
	Name      string
	Condition ExpressionNode
	Message   string
	NameLoc   shared.Location
	Loc       shared.Location
}

func (n CompilerDirectiveNode) GetLoc() shared.Location { return n.Loc }

type LinkItemNode struct {
	Link      *attributes.Link
	When      *LinkWhenNode
	Directive *CompilerDirectiveNode
}

type LinkWhenBranchNode struct {
	Condition ExpressionNode
	Items     []LinkItemNode
}

type LinkWhenNode struct {
	Branches  []LinkWhenBranchNode
	ElseItems []LinkItemNode
	Loc       shared.Location
}

type ParsedLinkAttribute struct {
	Items []LinkItemNode
}

func (a ParsedLinkAttribute) GetType() attributes.AttributeType { return attributes.AttributeTypeLink }

func (n RootNode) GetLoc() shared.Location { return n.Loc }

type IdentifierNode struct {
	Name               string
	Module             string
	ResolvedModuleName string
	TypeArguments      []TypeNode
	ResolvedTypeArgs   []types.Type
	Loc                shared.Location
	Symbol             *symbols.Symbol
	Type               types.Type
}

func (n IdentifierNode) GetLoc() shared.Location { return n.Loc }
func (n IdentifierNode) String() string {
	if n.Module == "" {
		return n.Name
	}
	return n.Module + "." + n.Name
}
func (n *IdentifierNode) SetType(t types.Type)       { n.Type = t }
func (n *IdentifierNode) GetType() types.Type        { return n.Type }
func (n *IdentifierNode) GetSymbol() *symbols.Symbol { return n.Symbol }

type FieldAccessNode struct {
	Subject ExpressionNode
	Field   *IdentifierNode

	Loc         shared.Location
	Type        types.Type
	EnumValue   string
	IsEnumValue bool
	IsFlagValue bool
	IsFlagTest  bool
	FlagValue   string
	FlagType    types.Type
	// TaggedUnionType is set when this field access constructs an empty
	// tagged-union variant without parentheses.
	TaggedUnionType    types.Type
	TaggedUnionVariant int
	MethodSymbol       *symbols.Symbol
	MethodModule       string
	// ResolvedIdentifier is set when this dotted access names a declaration in
	// an imported module rather than a value field.
	ResolvedIdentifier *IdentifierNode
	// ModulePath is the canonical path of an intermediate module namespace.
	ModulePath string
}

func (n FieldAccessNode) GetLoc() shared.Location { return n.Loc }
func (n *FieldAccessNode) SetType(t types.Type)   { n.Type = t }
func (n *FieldAccessNode) GetType() types.Type    { return n.Type }

type EnumLiteralNode struct {
	Variant string
	Value   string
	Loc     shared.Location
	Type    types.Type
}

func (n EnumLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *EnumLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *EnumLiteralNode) GetType() types.Type    { return n.Type }

type NamedTypeNode struct {
	ModName       string
	Name          string
	TypeArguments []TypeNode
	// Capture marks a type-pattern binding such as $T in a method owner.
	// Captures are converted to declaration-scoped generic parameters before
	// semantic type resolution.
	Capture           bool
	CaptureConstraint TypeNode

	Loc shared.Location
}

func (n NamedTypeNode) GetLoc() shared.Location { return n.Loc }
func (n NamedTypeNode) _type()                  {}

type StructField struct {
	Name string
	Type TypeNode
}
type StructTypeNode struct {
	Fields     []StructField
	Loc        shared.Location
	Attributes attributes.Attributes
}

func (n StructTypeNode) GetLoc() shared.Location { return n.Loc }
func (n StructTypeNode) _type()                  {}

type ReprTypeNode struct {
	Operand TypeNode
	Loc     shared.Location
}

func (n ReprTypeNode) GetLoc() shared.Location { return n.Loc }
func (n ReprTypeNode) _type()                  {}

type EnumTypeNode struct {
	Name     string
	Module   string
	Variants []string
	Values   []string
	Loc      shared.Location
}

func (n EnumTypeNode) GetLoc() shared.Location { return n.Loc }
func (n EnumTypeNode) _type()                  {}

type FlagsTypeNode struct {
	Name       string
	Module     string
	Underlying TypeNode
	Variants   []string
	Values     []string
	Loc        shared.Location
}

func (n FlagsTypeNode) GetLoc() shared.Location { return n.Loc }
func (n FlagsTypeNode) _type()                  {}

type UnionTypeNode struct {
	Name     string
	Module   string
	TagType  TypeNode
	AutoTag  bool
	Fields   []StructField
	Variants []TaggedUnionVariantNode
	Loc      shared.Location
}

func (n UnionTypeNode) GetLoc() shared.Location { return n.Loc }
func (n UnionTypeNode) _type()                  {}

type TaggedUnionVariantNode struct {
	Name   string
	Fields []StructField
	Loc    shared.Location
}

func (n TaggedUnionVariantNode) GetLoc() shared.Location { return n.Loc }

type OpaqueTypeNode struct {
	Loc shared.Location
}

func (n OpaqueTypeNode) GetLoc() shared.Location { return n.Loc }
func (n OpaqueTypeNode) _type()                  {}

type TraitMethodNode struct {
	Name              string
	GenericParameters []GenericParameterNode
	Receiver          MethodReceiverKind
	Args              []*FunctionNodeArg
	ReturnType        TypeNode
	Body              Node
	ExpressionBody    bool
	Loc               shared.Location
}

type TraitTypeNode struct {
	Methods []TraitMethodNode
	Loc     shared.Location
}

func (n TraitTypeNode) GetLoc() shared.Location { return n.Loc }
func (n TraitTypeNode) _type()                  {}

type SliceTypeNode struct {
	ElementType TypeNode
	Mutable     bool
	Loc         shared.Location
}

func (n SliceTypeNode) GetLoc() shared.Location { return n.Loc }
func (n SliceTypeNode) _type()                  {}

type ArrayTypeNode struct {
	ElementType TypeNode
	Length      ExpressionNode
	Loc         shared.Location
}

func (n ArrayTypeNode) GetLoc() shared.Location { return n.Loc }
func (n ArrayTypeNode) _type()                  {}

type PointerTypeNode struct {
	BaseType TypeNode
	Mutable  bool
	Loc      shared.Location
}

func (n PointerTypeNode) GetLoc() shared.Location { return n.Loc }
func (n PointerTypeNode) _type()                  {}

type DynTypeNode struct {
	TraitType TypeNode
	Mutable   bool
	Loc       shared.Location
}

func (n DynTypeNode) GetLoc() shared.Location { return n.Loc }
func (n DynTypeNode) _type()                  {}

type FunctionTypeNode struct {
	Parameters    []TypeNode
	ReturnType    TypeNode
	TypedVariadic bool
	Loc           shared.Location
}

type MultipleReturnTypeNode struct {
	Types []TypeNode
	Loc   shared.Location
}

func (n MultipleReturnTypeNode) GetLoc() shared.Location { return n.Loc }
func (n MultipleReturnTypeNode) _type()                  {}

func (n FunctionTypeNode) GetLoc() shared.Location { return n.Loc }
func (n FunctionTypeNode) _type()                  {}

type TypeNode interface {
	Node

	_type() // marker method
}

type BoolLiteralNode struct {
	Value string
	Loc   shared.Location
	Type  types.Type
}

func (n BoolLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *BoolLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *BoolLiteralNode) GetType() types.Type    { return n.Type }

type IntegerLiteralNode struct {
	Value string
	Loc   shared.Location
	Type  types.Type
}

func (n IntegerLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *IntegerLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *IntegerLiteralNode) GetType() types.Type    { return n.Type }

type ReprNode struct {
	Operand ExpressionNode
	Loc     shared.Location
	Type    types.Type
}

func (n ReprNode) GetLoc() shared.Location { return n.Loc }
func (n *ReprNode) SetType(t types.Type)   { n.Type = t }
func (n *ReprNode) GetType() types.Type    { return n.Type }

type FloatLiteralNode struct {
	Value string
	Loc   shared.Location
	Type  types.Type
}

func (n FloatLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *FloatLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *FloatLiteralNode) GetType() types.Type    { return n.Type }

type StringLiteralNode struct {
	Value string
	Loc   shared.Location
	Type  types.Type
}

func (n StringLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *StringLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *StringLiteralNode) GetType() types.Type    { return n.Type }

type EmbedNode struct {
	Path     string
	Contents string
	Loc      shared.Location
	Type     types.Type
}

func (n EmbedNode) GetLoc() shared.Location { return n.Loc }
func (n *EmbedNode) SetType(t types.Type)   { n.Type = t }
func (n *EmbedNode) GetType() types.Type    { return n.Type }

type CStringLiteralNode struct {
	Value string
	Loc   shared.Location
	Type  types.Type
}

func (n CStringLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *CStringLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *CStringLiteralNode) GetType() types.Type    { return n.Type }

type CharLiteralNode struct {
	Value byte
	Loc   shared.Location
	Type  types.Type
}

func (n CharLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *CharLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *CharLiteralNode) GetType() types.Type    { return n.Type }

type NilLiteralNode struct {
	Loc  shared.Location
	Type types.Type
}

func (n NilLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *NilLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *NilLiteralNode) GetType() types.Type    { return n.Type }

type NoInitializerNode struct {
	Loc  shared.Location
	Type types.Type
}

func (n NoInitializerNode) GetLoc() shared.Location { return n.Loc }
func (n *NoInitializerNode) SetType(t types.Type)   { n.Type = t }
func (n *NoInitializerNode) GetType() types.Type    { return n.Type }

type StructLiteralNode struct {
	Name            *IdentifierNode
	Fields          []shared.Pair[string, ExpressionNode] // field name, value
	FlagMembers     []string
	NoInitRemaining bool
	Loc             shared.Location
	Symbol          *symbols.Symbol
	Type            types.Type
}

func (n StructLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *StructLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *StructLiteralNode) GetType() types.Type    { return n.Type }

type SliceLiteralNode struct {
	Elements     []ExpressionNode
	RepeatValue  ExpressionNode
	RepeatAmount ExpressionNode
	Loc          shared.Location
	Type         types.Type
}

func (n SliceLiteralNode) GetLoc() shared.Location { return n.Loc }
func (n *SliceLiteralNode) SetType(t types.Type)   { n.Type = t }
func (n *SliceLiteralNode) GetType() types.Type    { return n.Type }

type FunctionCallNode struct {
	Callee ExpressionNode
	// Name and Symbol are populated for direct calls, preserving linkage and
	// foreign-ABI information that is not part of a callable value's type.
	Name   *IdentifierNode
	Args   []ExpressionNode
	Loc    shared.Location
	Symbol *symbols.Symbol
	Method bool
	Type   types.Type
	// CompileTimeApplication is populated when call syntax invokes a
	// type-producing or compile-time value binding, such as DynamicArray(T).
	CompileTimeApplication *IdentifierNode

	TraitCall bool
	TraitSlot int

	TypedVariadic      bool
	TypedVariadicStart int
	TypedVariadicSlice types.Type
	VariadicExpansion  bool

	TaggedUnionType    types.Type
	TaggedUnionVariant int
	// TaggedUnionTemplate is set when a generic union constructor omits its
	// owner type arguments. Attribution infers them from the payload arguments.
	TaggedUnionTemplate *symbols.Symbol
}

// LambdaNode is a non-capturing anonymous function expression. Function is
// populated during semantic analysis and reuses the ordinary function
// pipeline once the optional contextual parameter types have been resolved.
type LambdaNode struct {
	Args          []*FunctionNodeArg
	Body          ExpressionNode
	TypedVariadic bool
	Loc           shared.Location
	Type          types.Type
	Function      *FunctionDefNode
	Attributed    bool
	Validated     bool
}

func (n LambdaNode) GetLoc() shared.Location { return n.Loc }
func (n *LambdaNode) SetType(t types.Type)   { n.Type = t }
func (n *LambdaNode) GetType() types.Type    { return n.Type }

// InlineAsmNode describes one LLVM-style inline assembly expression. Output
// constraints precede input constraints in the constraint string assembled by
// the backend, matching LLVM's operand numbering rules.
type InlineAsmNode struct {
	Template string
	Outputs  []InlineAsmOutput
	Inputs   []InlineAsmInput
	Clobbers []string
	Volatile bool
	Loc      shared.Location
	Type     types.Type
}

type InlineAsmOutput struct {
	TypeNode   TypeNode
	Type       types.Type
	Constraint string
	Loc        shared.Location
}

type InlineAsmInput struct {
	Value      ExpressionNode
	Constraint string
	Loc        shared.Location
}

func (n InlineAsmNode) GetLoc() shared.Location { return n.Loc }
func (n *InlineAsmNode) SetType(t types.Type)   { n.Type = t }
func (n *InlineAsmNode) GetType() types.Type    { return n.Type }

func (n FunctionCallNode) GetLoc() shared.Location { return n.Loc }
func (n *FunctionCallNode) SetType(t types.Type)   { n.Type = t }
func (n *FunctionCallNode) GetType() types.Type {
	if n.Type != nil {
		return n.Type
	}
	if n.CompileTimeApplication != nil && n.CompileTimeApplication.Symbol != nil {
		if n.CompileTimeApplication.Symbol.Kind == symbols.SymbolKindType {
			return n.CompileTimeApplication.Symbol.TypeInfo
		}
		return n.CompileTimeApplication.Symbol.Type
	}
	if n.TaggedUnionType != nil {
		return n.TaggedUnionType
	}
	if n.Symbol != nil && n.Symbol.Kind == symbols.SymbolKindFunction && n.Symbol.Signature != nil {
		if n.Symbol.Signature.ReturnType == nil {
			return types.PrimitiveVoid
		}
		return n.Symbol.Signature.ReturnType
	}
	if n.Callee != nil {
		if ptr, ok := types.Underlying(n.Callee.GetType()).(types.PointerType); ok {
			if fn, ok := types.Underlying(ptr.Base).(types.FunctionType); ok {
				return fn.ReturnType
			}
		}
	}
	return types.ErrorType{}
}

type UnaryOpKind uint

const (
	UnaryOpLogicalNot UnaryOpKind = iota
	UnaryOpNegate
	UnaryOpReference
	UnaryOpMutableReference
	UnaryOpDereference
	UnaryOpSliceLen
	UnaryOpBitwiseNot
)

func (u UnaryOpKind) String() string {
	switch u {
	case UnaryOpLogicalNot:
		return "!"
	case UnaryOpNegate:
		return "-"
	case UnaryOpReference:
		return "&"
	case UnaryOpDereference:
		return "*"
	case UnaryOpSliceLen:
		return "[]"
	case UnaryOpBitwiseNot:
		return "~"
	default:
		return "unknown"
	}
}

type UnaryOpNode struct {
	Op      UnaryOpKind
	Operand ExpressionNode
	Loc     shared.Location
	Type    types.Type
}

func (n UnaryOpNode) GetLoc() shared.Location { return n.Loc }
func (n *UnaryOpNode) SetType(t types.Type)   { n.Type = t }
func (n *UnaryOpNode) GetType() types.Type    { return n.Type }

type IndexExprNode struct {
	Subject ExpressionNode
	Index   ExpressionNode
	Loc     shared.Location
	Type    types.Type
}

func (n IndexExprNode) GetLoc() shared.Location { return n.Loc }
func (n *IndexExprNode) SetType(t types.Type)   { n.Type = t }
func (n *IndexExprNode) GetType() types.Type    { return n.Type }

type SliceExprNode struct {
	Subject ExpressionNode
	Start   ExpressionNode
	End     ExpressionNode
	Loc     shared.Location
	Type    types.Type
}

func (n SliceExprNode) GetLoc() shared.Location { return n.Loc }
func (n *SliceExprNode) SetType(t types.Type)   { n.Type = t }
func (n *SliceExprNode) GetType() types.Type    { return n.Type }

type BinaryOpKind uint

const (
	BinaryOpLogicalOr BinaryOpKind = iota
	BinaryOpLogicalAnd
	BinaryOpEqual
	BinaryOpNotEqual
	BinaryOpLess
	BinaryOpLessEqual
	BinaryOpGreater
	BinaryOpGreaterEqual
	BinaryOpAdd
	BinaryOpSubtract
	BinaryOpMultiply
	BinaryOpDivide
	BinaryOpModulo
	BinaryOpBitwiseAnd
	BinaryOpBitwiseXor
	BinaryOpBitwiseOr
	BinaryOpShiftLeft
	BinaryOpShiftRight
)

func (b BinaryOpKind) String() string {
	switch b {
	case BinaryOpLogicalOr:
		return "||"
	case BinaryOpLogicalAnd:
		return "&&"
	case BinaryOpEqual:
		return "=="
	case BinaryOpNotEqual:
		return "!="
	case BinaryOpLess:
		return "<"
	case BinaryOpLessEqual:
		return "<="
	case BinaryOpGreater:
		return ">"
	case BinaryOpGreaterEqual:
		return ">="
	case BinaryOpAdd:
		return "+"
	case BinaryOpSubtract:
		return "-"
	case BinaryOpMultiply:
		return "*"
	case BinaryOpDivide:
		return "/"
	case BinaryOpModulo:
		return "%"
	case BinaryOpBitwiseAnd:
		return "&"
	case BinaryOpBitwiseXor:
		return "^"
	case BinaryOpBitwiseOr:
		return "|"
	case BinaryOpShiftLeft:
		return "<<"
	case BinaryOpShiftRight:
		return ">>"
	default:
		return "unknown"
	}
}

type BinaryOpNode struct {
	Op       BinaryOpKind
	Operand1 ExpressionNode
	Operand2 ExpressionNode
	Loc      shared.Location
	Type     types.Type
}

func (n BinaryOpNode) GetLoc() shared.Location { return n.Loc }
func (n *BinaryOpNode) SetType(t types.Type)   { n.Type = t }
func (n *BinaryOpNode) GetType() types.Type    { return n.Type }

type CastNode struct {
	ToType  TypeNode
	Operand ExpressionNode
	Loc     shared.Location
	Type    types.Type

	TraitConversion  bool
	TraitRecast      bool
	TraitUnwrap      bool
	ConcreteType     types.Type
	TraitMethods     []*symbols.Symbol
	TraitCandidates  []TraitCastCandidate
	Checked          bool
	CheckedType      types.Type
	GenericAssertion bool
	StaticAssertion  bool
	AssertionMatches bool
	StaticTraitView  *types.StaticTraitView
}

type TraitCastCandidate struct {
	ConcreteType types.Type
	Methods      []*symbols.Symbol
}

func (n CastNode) GetLoc() shared.Location { return n.Loc }
func (n *CastNode) SetType(t types.Type)   { n.Type = t }
func (n *CastNode) GetType() types.Type    { return n.Type }

type SizeOfNode struct {
	Operand     TypeNode
	Expression  ExpressionNode
	OperandType types.Type
	Loc         shared.Location
	Type        types.Type
}

func (n SizeOfNode) GetLoc() shared.Location { return n.Loc }
func (n *SizeOfNode) SetType(t types.Type)   { n.Type = t }
func (n *SizeOfNode) GetType() types.Type    { return n.Type }

type SizeOfExprNode struct {
	Operand     ExpressionNode
	OperandType types.Type
	Loc         shared.Location
	Type        types.Type
}

func (n SizeOfExprNode) GetLoc() shared.Location { return n.Loc }
func (n *SizeOfExprNode) SetType(t types.Type)   { n.Type = t }
func (n *SizeOfExprNode) GetType() types.Type    { return n.Type }

type AlignOfNode struct {
	Operand     TypeNode
	Expression  ExpressionNode
	OperandType types.Type
	Loc         shared.Location
	Type        types.Type
}

func (n AlignOfNode) GetLoc() shared.Location { return n.Loc }
func (n *AlignOfNode) SetType(t types.Type)   { n.Type = t }
func (n *AlignOfNode) GetType() types.Type    { return n.Type }

type OffsetOfNode struct {
	Operand     TypeNode
	OperandType types.Type
	Field       string
	Loc         shared.Location
	Type        types.Type
}

func (n OffsetOfNode) GetLoc() shared.Location { return n.Loc }
func (n *OffsetOfNode) SetType(t types.Type)   { n.Type = t }
func (n *OffsetOfNode) GetType() types.Type    { return n.Type }

type ImportNode struct {
	Modules         []string
	ResolvedModules []string
	Aliases         []string
	Loc             shared.Location
}

func (n ImportNode) GetLoc() shared.Location { return n.Loc }

type ModuleNode struct {
	Name       string
	Attributes attributes.Attributes
	Loc        shared.Location
}

func (n ModuleNode) GetLoc() shared.Location { return n.Loc }

type FunctionNodeArg struct {
	Name    string
	Type    TypeNode
	Default ExpressionNode
	Mutable bool
	Symbol  *symbols.Symbol
	Loc     shared.Location
}

func (a FunctionNodeArg) GetLoc() shared.Location {
	if a.Loc.LC.Line != 0 {
		return a.Loc
	}
	return a.Type.GetLoc()
}

type GenericParameterNode struct {
	Name       string
	Constraint TypeNode
	Loc        shared.Location
}

func (n GenericParameterNode) GetLoc() shared.Location { return n.Loc }

type FunctionDefNode struct {
	Name                         string
	MethodOwner                  string
	MethodOwnerType              TypeNode
	MethodOwnerGenericParameters []GenericParameterNode
	Receiver                     MethodReceiverKind
	GenericParameters            []GenericParameterNode
	Args                         []*FunctionNodeArg
	RetTypeNode                  TypeNode
	Body                         Node
	ExpressionBody               bool
	HasVariadic                  bool
	TypedVariadic                bool
	Pub                          bool
	Attributes                   attributes.Attributes
	Loc                          shared.Location
	Symbol                       *symbols.Symbol
	GenericInstance              bool
}

type MethodReceiverKind uint8

const (
	MethodReceiverNone MethodReceiverKind = iota
	MethodReceiverValue
	MethodReceiverPointer
	MethodReceiverMutablePointer
)

func (n FunctionDefNode) GetLoc() shared.Location { return n.Loc }

func (n FunctionDefNode) HasMethodOwner() bool {
	return n.MethodOwner != "" || n.MethodOwnerType != nil
}

func (n FunctionDefNode) IsGeneric() bool {
	return len(n.MethodOwnerGenericParameters) != 0 || len(n.GenericParameters) != 0
}

type TypeAliasNode struct {
	Name              string
	GenericParameters []GenericParameterNode
	Type              TypeNode
	Transparent       bool
	Pub               bool
	Loc               shared.Location
	Symbol            *symbols.Symbol
}

func (n TypeAliasNode) GetLoc() shared.Location { return n.Loc }

type IfBranch struct {
	Condition ExpressionNode
	Node      *BlockNode
}
type IfNode struct {
	IfBranch       IfBranch
	ElseIfBranches []IfBranch
	ElseBranch     *BlockNode
	Expression     bool
	Loc            shared.Location
	Type           types.Type
}

func (n IfNode) GetLoc() shared.Location { return n.Loc }
func (n *IfNode) SetType(t types.Type)   { n.Type = t }
func (n *IfNode) GetType() types.Type    { return n.Type }

type MatchPatternKind uint8

const (
	MatchPatternWildcard MatchPatternKind = iota
	MatchPatternLiteral
	MatchPatternVariant
	MatchPatternRange
	MatchPatternAlternative
)

type MatchBinding struct {
	Name   string
	Field  string
	Loc    shared.Location
	Symbol *symbols.Symbol
}

type MatchPatternNode struct {
	Kind         MatchPatternKind
	Literal      ExpressionNode
	Start        ExpressionNode
	End          ExpressionNode
	Variant      string
	Bindings     []MatchBinding
	Payload      bool
	Alternatives []*MatchPatternNode
	TagValue     string
	PayloadType  types.Type
	Loc          shared.Location
}

func (n MatchPatternNode) GetLoc() shared.Location { return n.Loc }

type MatchArmNode struct {
	Patterns []*MatchPatternNode
	Guard    ExpressionNode
	Body     ExpressionNode
	Loc      shared.Location
}

func (n MatchArmNode) GetLoc() shared.Location { return n.Loc }

type MatchNode struct {
	Subjects    []ExpressionNode
	BindingName string
	BindingLoc  shared.Location
	Binding     *symbols.Symbol
	Arms        []MatchArmNode
	Expression  bool
	Loc         shared.Location
	Type        types.Type
}

func (n MatchNode) GetLoc() shared.Location { return n.Loc }
func (n *MatchNode) SetType(t types.Type)   { n.Type = t }
func (n *MatchNode) GetType() types.Type    { return n.Type }

type ForNode struct {
	ExprsOrStmts []Node
	Body         *BlockNode
	Loc          shared.Location
}

func (n ForNode) GetLoc() shared.Location { return n.Loc }

type RangeForNode struct {
	Name      string
	NameLoc   shared.Location
	Start     ExpressionNode
	End       ExpressionNode
	Inclusive bool
	Reversed  bool
	Body      *BlockNode
	Loc       shared.Location
	Symbol    *symbols.Symbol
}

func (n RangeForNode) GetLoc() shared.Location { return n.Loc }

type ForEachNode struct {
	Name         string
	NameLoc      shared.Location
	Destructure  []ForEachBinding
	ElementKind  ForEachElementKind
	IndexName    string
	IndexNameLoc shared.Location
	Iterable     ExpressionNode
	Reversed     bool
	Body         *BlockNode
	Loc          shared.Location
	Symbol       *symbols.Symbol
	IndexSymbol  *symbols.Symbol
}

type ForEachBinding struct {
	Name   string
	Loc    shared.Location
	Symbol *symbols.Symbol
}

func (n ForEachNode) GetLoc() shared.Location { return n.Loc }

type ForEachElementKind uint8

const (
	ForEachElementValue ForEachElementKind = iota
	ForEachElementPointer
	ForEachElementMutablePointer
)

type ControlKeywordNode struct {
	Keyword      tokeniser.KeywordKind
	ReturnValue  ExpressionNode // only for "return"
	ReturnValues []ExpressionNode
	Loc          shared.Location
}

func (n ControlKeywordNode) GetLoc() shared.Location { return n.Loc }

type DeferNode struct {
	Action Node
	Loc    shared.Location
}

func (n DeferNode) GetLoc() shared.Location { return n.Loc }

type DeclarationNode struct {
	Name              string
	NameLoc           shared.Location
	GenericParameters []GenericParameterNode
	Mutable           bool
	Pub               bool
	TypeNode          TypeNode
	Value             ExpressionNode
	Comptime          bool
	Attributes        attributes.Attributes
	Loc               shared.Location
	Symbol            *symbols.Symbol
}

type MultiDeclarationNode struct {
	Names    []string
	NameLocs []shared.Location
	Mutable  bool
	Value    ExpressionNode
	Loc      shared.Location
	Symbols  []*symbols.Symbol
}

func (n MultiDeclarationNode) GetLoc() shared.Location { return n.Loc }

func (n DeclarationNode) GetLoc() shared.Location { return n.Loc }

type AssignmentNode struct {
	Assignee  ExpressionNode
	Assignees []ExpressionNode
	Value     ExpressionNode
	Compound  bool
	Loc       shared.Location
}

func (n AssignmentNode) GetLoc() shared.Location { return n.Loc }

type BlockNode struct {
	Body       []Node
	Expression bool
	Loc        shared.Location
	Type       types.Type
}

func (n BlockNode) GetLoc() shared.Location { return n.Loc }
func (n *BlockNode) SetType(t types.Type)   { n.Type = t }
func (n *BlockNode) GetType() types.Type    { return n.Type }