package commands import ( "fmt" "strings" "c65gm/internal/compiler" "c65gm/internal/preproc" "c65gm/internal/utils" ) // WordCommand handles WORD variable declarations // Syntax: // // WORD varname # word with init = 0 // WORD varname = value # word with init value // WORD varname = "string" # word pointer to constant string // WORD varname @ address # word at absolute address // WORD CONST varname = value # constant word type WordCommand struct { varName string value uint16 isConst bool isAbs bool } func (c *WordCommand) WillHandle(line preproc.Line) bool { params, err := utils.ParseParams(line.Text) if err != nil || len(params) == 0 { return false } return strings.ToUpper(params[0]) == "WORD" } func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error { // Clear state c.varName = "" c.value = 0 c.isConst = false c.isAbs = false params, err := utils.ParseParams(line.Text) if err != nil { return err } paramCount := len(params) // Validate parameter count if paramCount != 2 && paramCount != 4 && paramCount != 5 { return fmt.Errorf("WORD: wrong number of parameters (%d)", paramCount) } var varName string var value int64 scope := ctx.FunctionHandler.CurrentFunction() // Create constant lookup function constLookup := func(name string) (int64, bool) { sym := ctx.SymbolTable.Lookup(name, ctx.CurrentScope()) if sym != nil && sym.IsConst() { return int64(sym.Value), true } return 0, false } switch paramCount { case 2: // WORD varname varName = params[1] value = 0 if !utils.ValidateIdentifier(varName) { return fmt.Errorf("WORD: invalid identifier %q", varName) } err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindWord, uint16(value)) case 4: // WORD varname = value OR WORD varname @ address OR WORD varname = "string" varName = params[1] operator := params[2] valueStr := params[3] if !utils.ValidateIdentifier(varName) { return fmt.Errorf("WORD: invalid identifier %q", varName) } // Check for string literal if utils.IsStringLiteral(valueStr) { if operator != "=" { return fmt.Errorf("WORD: expected '=' when assigning string pointer, got %q", operator) } // Generate label for the string label := ctx.GeneralStack.Push() // Get pragma set for this line pragmaSet := ctx.Pragma.GetPragmaSetByIndex(line.PragmaSetIndex) // Add string to constant string handler ctx.ConstStrHandler.AddConstStr(label, valueStr, false, pragmaSet) err = ctx.SymbolTable.AddLabel(varName, scope, label) if err != nil { return fmt.Errorf("WORD: %w", err) } c.varName = varName c.value = 0 // label refs don't have numeric values return nil } // Not a string, evaluate as expression value, err = utils.EvaluateExpression(valueStr, constLookup) if err != nil { return fmt.Errorf("WORD: invalid value %q: %w", valueStr, err) } if operator == "=" { // WORD varname = value if value < 0 || value > 65535 { return fmt.Errorf("WORD: init value %d out of range (0-65535)", value) } err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindWord, uint16(value)) } else if operator == "@" { // WORD varname @ address if value < 0 || value > 0xFFFF { return fmt.Errorf("WORD: absolute address $%X out of range", value) } c.isAbs = true err = ctx.SymbolTable.AddAbsolute(varName, scope, compiler.KindWord, uint16(value)) } else { return fmt.Errorf("WORD: expected '=' or '@', got %q", operator) } case 5: // WORD CONST varname = value constKeyword := strings.ToUpper(params[1]) varName = params[2] operator := params[3] valueStr := params[4] if constKeyword != "CONST" { return fmt.Errorf("WORD: expected CONST keyword, got %q", params[1]) } if operator != "=" { return fmt.Errorf("WORD: expected '=', got %q", operator) } if !utils.ValidateIdentifier(varName) { return fmt.Errorf("WORD: invalid identifier %q", varName) } value, err = utils.EvaluateExpression(valueStr, constLookup) if err != nil { return fmt.Errorf("WORD: invalid value %q: %w", valueStr, err) } if value < 0 || value > 65535 { return fmt.Errorf("WORD: const value %d out of range (0-65535)", value) } c.isConst = true err = ctx.SymbolTable.AddConst(varName, scope, compiler.KindWord, uint16(value)) } if err != nil { return fmt.Errorf("WORD: %w", err) } c.varName = varName c.value = uint16(value) return nil } func (c *WordCommand) Generate(_ *compiler.CompilerContext) ([]string, error) { // Variables are rendered by assembleOutput, not by individual commands return nil, nil }