184 lines
4.6 KiB
Go
184 lines
4.6 KiB
Go
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
|
|
}
|