c65gm/internal/commands/if.go

114 lines
2.5 KiB
Go

package commands
import (
"fmt"
"strings"
"c65gm/internal/compiler"
"c65gm/internal/preproc"
"c65gm/internal/utils"
)
// IfCommand handles IF conditional statements
// Syntax: IF <param1> <op> <param2>
// Operators: =, ==, <>, !=, >, <, >=, <=
type IfCommand struct {
operator string
param1 *operandInfo
param2 *operandInfo
useLongJump bool
skipLabel string
}
func (c *IfCommand) WillHandle(line preproc.Line) bool {
params, err := utils.ParseParams(line.Text)
if err != nil || len(params) == 0 {
return false
}
return strings.ToUpper(params[0]) == "IF"
}
func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error {
params, err := utils.ParseParams(line.Text)
if err != nil {
return err
}
if len(params) != 4 {
return fmt.Errorf("IF: expected 4 parameters, got %d", len(params))
}
c.operator = normalizeOperator(params[2])
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
// Parse param1
varName, varKind, value, isVar, err := compiler.ParseOperandParam(
params[1], ctx.SymbolTable, scope, constLookup)
if err != nil {
return fmt.Errorf("IF: param1: %w", err)
}
c.param1 = &operandInfo{
varName: varName,
varKind: varKind,
value: value,
isVar: isVar,
}
// Parse param2
varName, varKind, value, isVar, err = compiler.ParseOperandParam(
params[3], ctx.SymbolTable, scope, constLookup)
if err != nil {
return fmt.Errorf("IF: param2: %w", err)
}
c.param2 = &operandInfo{
varName: varName,
varKind: varKind,
value: value,
isVar: isVar,
}
// Check pragma
ps := ctx.Pragma.GetPragmaSetByIndex(line.PragmaSetIndex)
longJumpPragma := ps.GetPragma("_P_USE_LONG_JUMP")
c.useLongJump = longJumpPragma != "" && longJumpPragma != "0"
// Create skip label (jumps here on FALSE, or to ELSE if present)
c.skipLabel = ctx.IfStack.Push()
return nil
}
func (c *IfCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) {
op, err := parseOperator(c.operator)
if err != nil {
return nil, fmt.Errorf("IF: %w", err)
}
// Generate comparison (jumps to skipLabel on FALSE)
gen, err := newComparisonGenerator(
op,
c.param1,
c.param2,
c.useLongJump,
ctx.IfStack,
ctx.GeneralStack,
)
if err != nil {
return nil, fmt.Errorf("IF: %w", err)
}
cmpAsm, err := gen.generate()
if err != nil {
return nil, fmt.Errorf("IF: %w", err)
}
return cmpAsm, nil
}