package commands import ( "fmt" "strings" "c65gm/internal/compiler" "c65gm/internal/preproc" "c65gm/internal/utils" ) // WhileCommand handles WHILE loop statements // Syntax: WHILE // Operators: =, ==, <>, !=, >, <, >=, <= type WhileCommand struct { operator string param1 *operandInfo param2 *operandInfo useLongJump bool loopLabel string skipLabel string } func (c *WhileCommand) WillHandle(line preproc.Line) bool { params, err := utils.ParseParams(line.Text) if err != nil || len(params) == 0 { return false } return strings.ToUpper(params[0]) == "WHILE" } func (c *WhileCommand) 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("WHILE: 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("WHILE: 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("WHILE: 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 labels c.loopLabel = ctx.LoopStack.Push() c.skipLabel = ctx.WhileStack.Push() return nil } func (c *WhileCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) { op, err := parseOperator(c.operator) if err != nil { return nil, fmt.Errorf("WHILE: %w", err) } // Emit loop label asm := []string{c.loopLabel} // Generate comparison (jumps to skipLabel on false) gen, err := newComparisonGenerator( op, c.param1, c.param2, c.useLongJump, ctx.WhileStack, ctx.GeneralStack, ) if err != nil { return nil, fmt.Errorf("WHILE: %w", err) } cmpAsm, err := gen.generate() if err != nil { return nil, fmt.Errorf("WHILE: %w", err) } asm = append(asm, cmpAsm...) return asm, nil } // normalizeOperator converts operator variants to canonical form func normalizeOperator(op string) string { switch op { case "==": return "=" case "!=": return "<>" default: return op } } // parseOperator converts string operator to comparisonOp func parseOperator(op string) (comparisonOp, error) { switch op { case "=": return opEqual, nil case "<>": return opNotEqual, nil case ">": return opGreater, nil case "<": return opLess, nil case ">=": return opGreaterEqual, nil case "<=": return opLessEqual, nil default: return 0, fmt.Errorf("unsupported operator %q", op) } }