c65gm/internal/commands/while.go

133 lines
3.2 KiB
Go

package commands
import (
"fmt"
"strings"
"c65gm/internal/compiler"
"c65gm/internal/preproc"
"c65gm/internal/utils"
)
// WhileCommand handles WHILE loop statements
// Syntax: WHILE <param1> <op> <param2>
// 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
}
// WHILE <param1> or WHILE <param1> <op> <param2> [DO]
if len(params) < 2 {
return fmt.Errorf("WHILE: 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("WHILE: param1: %w", err)
}
c.param1 = &operandInfo{
varName: varName,
varKind: varKind,
value: value,
isVar: isVar,
}
// Single param: WHILE <param1>
if len(params) == 2 || (len(params) == 3 && strings.ToUpper(params[2]) == "DO") {
c.operator = "!="
c.param2 = nil
} else {
// Full comparison: WHILE <param1> <op> <param2> [DO]
if len(params) == 5 && strings.ToUpper(params[4]) != "DO" {
return fmt.Errorf("WHILE: expected DO when 5 parameters, got %s", params[4])
} else if len(params) != 4 && len(params) != 5 {
return fmt.Errorf("WHILE: 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("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
}