Improved funchandler.go HandleFuncCall to handle consts and expressions better

This commit is contained in:
Mattias Hansson 2025-11-08 13:46:41 +01:00
parent 2dc591f40d
commit d57a40a7cf

View file

@ -272,25 +272,57 @@ func (fh *FunctionHandler) HandleFuncCall(line preproc.Line) ([]string, error) {
for i, arg := range callArgs {
param := funcDecl.Params[i]
// Handle different argument types
// Handle special cases first
if strings.HasPrefix(arg, "@") {
// Label reference: @labelname
if err := fh.processLabelArg(arg, param, funcName, line, &inAssigns); err != nil {
return nil, err
}
} else if strings.HasPrefix(arg, "\"") && strings.HasSuffix(arg, "\"") {
continue
}
if strings.HasPrefix(arg, "\"") && strings.HasSuffix(arg, "\"") {
// String constant
if err := fh.processStringArg(arg, param, funcName, line, pragmaSet, &inAssigns); err != nil {
return nil, err
}
} else if sym := fh.symTable.Lookup(arg, fh.currentFuncs); sym != nil {
// Variable reference
continue
}
// Use ParseOperandParam for variables, constants, and expressions
constLookup := func(name string) (int64, bool) {
if sym := fh.symTable.Lookup(name, fh.currentFuncs); sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
_, _, value, isVar, err := ParseOperandParam(
arg, fh.symTable, fh.currentFuncs, constLookup)
if err != nil {
return nil, fmt.Errorf("%s:%d: CALL %s arg %d (%q): %w",
line.Filename, line.LineNo, funcName, i+1, arg, err)
}
// Out/IO parameters must be writable variables
if param.Direction.Has(DirOut) && !isVar {
return nil, fmt.Errorf("%s:%d: CALL %s: cannot pass constant/expression to out/io parameter %q",
line.Filename, line.LineNo, funcName, param.Symbol.Name)
}
if isVar {
// Variable - get full symbol for type checking
sym := fh.symTable.Lookup(arg, fh.currentFuncs)
if sym == nil {
return nil, fmt.Errorf("%s:%d: CALL %s: internal error - variable %q not found",
line.Filename, line.LineNo, funcName, arg)
}
if err := fh.processVarArg(sym, param, funcName, line, &inAssigns, &outAssigns); err != nil {
return nil, err
}
} else {
// Numeric constant
if err := fh.processConstArg(arg, param, funcName, line, &inAssigns); err != nil {
// Constant or expression result
if err := fh.processConstValue(value, param, funcName, line, &inAssigns); err != nil {
return nil, err
}
}
@ -439,6 +471,34 @@ func (fh *FunctionHandler) processConstArg(arg string, param *FuncParam, funcNam
return nil
}
// processConstValue handles pre-computed constant/expression values
// Used after ParseOperandParam has already validated and computed the value
func (fh *FunctionHandler) processConstValue(value uint16, param *FuncParam, funcName string, line preproc.Line, inAssigns *[]string) error {
// Verify value fits in parameter type
if param.Symbol.IsByte() && value > 255 {
return fmt.Errorf("%s:%d: CALL %s: value %d out of byte range for parameter %q",
line.Filename, line.LineNo, funcName, value, param.Symbol.Name)
}
lowByte := uint8(value & 0xFF)
highByte := uint8((value >> 8) & 0xFF)
*inAssigns = append(*inAssigns,
fmt.Sprintf(" lda #%d", lowByte),
fmt.Sprintf(" sta %s", param.Symbol.FullName()),
)
if param.Symbol.IsWord() {
// Optimize: only reload A if high byte differs
if highByte != lowByte {
*inAssigns = append(*inAssigns, fmt.Sprintf(" lda #%d", highByte))
}
*inAssigns = append(*inAssigns, fmt.Sprintf(" sta %s+1", param.Symbol.FullName()))
}
return nil
}
// parseImplicitDecl parses {BYTE varname} or {WORD varname} and adds to symbol table
func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error {
parts := strings.Fields(decl)