package commands import ( "fmt" "strings" "c65gm/internal/compiler" "c65gm/internal/preproc" "c65gm/internal/utils" ) // IfCommand handles IF conditional statements // Syntax: IF // 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 or IF [THEN] if len(params) < 2 { return fmt.Errorf("IF: expected at least 2 parameters, got %d", len(params)) } 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, } // Single param: IF if len(params) == 2 || (len(params) == 3 && strings.ToUpper(params[2]) == "THEN") { c.operator = "!=" c.param2 = nil } else { // Full comparison: IF [THEN] if len(params) == 5 && strings.ToUpper(params[4]) != "THEN" { return fmt.Errorf("IF: expected THEN when 5 parameters, got %s", params[4]) } else if len(params) != 4 && len(params) != 5 { return fmt.Errorf("IF: expected 2, 3, 4, or 5 parameters, got %d", len(params)) } c.operator = params[2] // 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 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 }