package compiler import ( "c65gm/internal/utils" "fmt" "c65gm/internal/preproc" ) // Command represents a script command that can interpret source lines and generate assembly type Command interface { // WillHandle checks if this command can handle the given line // Should clear any internal state and return true if it handles this line WillHandle(line preproc.Line) bool // Interpret parses and validates the line, storing state in the command // Returns error if line is malformed or invalid Interpret(line preproc.Line, ctx *CompilerContext) error // Generate produces assembly output based on previously interpreted line // Returns assembly lines and error if generation fails Generate(ctx *CompilerContext) ([]string, error) } // CommandRegistry manages registered commands and dispatches lines to appropriate handlers type CommandRegistry struct { commands []Command } // NewCommandRegistry creates a new command registry func NewCommandRegistry() *CommandRegistry { return &CommandRegistry{ commands: make([]Command, 0), } } // Register adds a command to the registry func (r *CommandRegistry) Register(cmd Command) { r.commands = append(r.commands, cmd) } // FindHandler finds the first command that will handle the given line // Returns the command and true if found, nil and false otherwise func (r *CommandRegistry) FindHandler(line preproc.Line) (Command, bool) { for _, cmd := range r.commands { if cmd.WillHandle(line) { return cmd, true } } return nil, false } // UnhandledLineError represents an error when no command handles a source line type UnhandledLineError struct { Line preproc.Line } func (e *UnhandledLineError) Error() string { return fmt.Sprintf("%s:%d: unhandled line: %s", e.Line.Filename, e.Line.LineNo, e.Line.Text) } // ParseOperandParam parses a command parameter that can be a variable or expression // Returns: varName, varKind, value, isVar, error func ParseOperandParam( param string, symTable *SymbolTable, scope []string, constLookup utils.ConstantLookup, ) (varName string, varKind VarKind, value uint16, isVar bool, err error) { // Try variable lookup first sym := symTable.Lookup(param, scope) if sym != nil { varKind = sym.GetVarKind() value = sym.Value // Constants are treated as literals (inlined), not variables if sym.IsConst() { varName = sym.FullName() // Preserve name for documentation isVar = false return } // It's a variable varName = sym.FullName() isVar = true return } // Not a variable, must be an expression val, evalErr := utils.EvaluateExpression(param, constLookup) if evalErr != nil { err = fmt.Errorf("not a valid variable or expression: %w", evalErr) return } if val < 0 || val > 65535 { err = fmt.Errorf("value %d out of range (0-65535)", val) return } value = uint16(val) isVar = false return }