From 652e147298e2c31ce080d70674f5ad4b30a35108 Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Mon, 13 Apr 2026 19:57:16 +0200 Subject: [PATCH 1/2] Added functionality to cull unused functions (by pragma _P_REMOVE_UNUSED) --- internal/commands/fend.go | 4 +- internal/commands/func.go | 16 +++++-- internal/compiler/compiler.go | 69 +++++++++++++++++++++++++++ internal/compiler/funchandler.go | 52 ++++++++++++++------ internal/compiler/funchandler_test.go | 8 +++- language.md | 8 ++++ syntax.md | 8 ++++ 7 files changed, 143 insertions(+), 22 deletions(-) diff --git a/internal/commands/fend.go b/internal/commands/fend.go index f8ee2f5..7610978 100644 --- a/internal/commands/fend.go +++ b/internal/commands/fend.go @@ -38,6 +38,8 @@ func (c *FendCommand) Interpret(line preproc.Line, _ *compiler.CompilerContext) } func (c *FendCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) { + funcName := ctx.FunctionHandler.CurrentFunction() ctx.FunctionHandler.EndFunction() - return []string{"\trts"}, nil + // Return RTS followed by end marker + return []string{"\trts", fmt.Sprintf("; @@FUNC_END %s", funcName)}, nil } diff --git a/internal/commands/func.go b/internal/commands/func.go index 77fe4d7..0733efb 100644 --- a/internal/commands/func.go +++ b/internal/commands/func.go @@ -1,6 +1,7 @@ package commands import ( + "fmt" "strings" "c65gm/internal/compiler" @@ -16,7 +17,7 @@ import ( // FUNC name ( in:x out:y io:z ) # with direction modifiers // FUNC name ( {BYTE temp} out:result ) # with implicit declarations type FuncCommand struct { - asmOutput []string + funcName string } func (c *FuncCommand) WillHandle(line preproc.Line) bool { @@ -28,17 +29,22 @@ func (c *FuncCommand) WillHandle(line preproc.Line) bool { } func (c *FuncCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error { - c.asmOutput = nil + c.funcName = "" - asm, err := ctx.FunctionHandler.HandleFuncDecl(line) + funcName, err := ctx.FunctionHandler.HandleFuncDecl(line) if err != nil { return err } - c.asmOutput = asm + c.funcName = funcName return nil } func (c *FuncCommand) Generate(_ *compiler.CompilerContext) ([]string, error) { - return c.asmOutput, nil + if c.funcName == "" { + return nil, nil // No function name parsed + } + // Prepend marker before the function label + marker := fmt.Sprintf("; @@FUNC_BEGIN %s", c.funcName) + return []string{marker, c.funcName}, nil } diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index 9439d08..3a956e2 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -3,6 +3,7 @@ package compiler import ( "fmt" "os" + "path/filepath" "strings" "c65gm/internal/preproc" @@ -234,6 +235,9 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) { _, _ = fmt.Fprintf(os.Stderr, "%s\n", warning) } + // Remove unused functions with _P_REMOVE_UNUSED pragma + codeOutput = c.removeUnusedFunctions(codeOutput) + // Assemble final output with headers and footers return c.assembleOutput(codeOutput), nil } @@ -449,6 +453,71 @@ func findPipeOutsideStrings(line string, startFrom int) int { return -1 } +// removeUnusedFunctions removes functions marked with _P_REMOVE_UNUSED pragma from assembly output +func (c *Compiler) removeUnusedFunctions(codeLines []string) []string { + toRemove := c.ctx.FunctionHandler.GetFunctionsToRemove() + if len(toRemove) == 0 { + return codeLines + } + + var result []string + i := 0 + for i < len(codeLines) { + line := codeLines[i] + + // Check for function start marker + if strings.HasPrefix(line, "; @@FUNC_BEGIN ") { + parts := strings.Fields(line) + if len(parts) >= 3 && toRemove[parts[2]] { + funcName := parts[2] + + // Find the function declaration to get file/line info + var filename string + var lineNo int + for _, funcDecl := range c.ctx.FunctionHandler.functions { + if funcDecl.Name == funcName { + filename = funcDecl.Line.Filename + lineNo = funcDecl.Line.LineNo + break + } + } + + // Print info message to stdout + if filename != "" { + baseFilename := filepath.Base(filename) + fmt.Printf("info:%s:%d:FUNC %s removed.\n", baseFilename, lineNo, funcName) + } else { + fmt.Printf("info:unknown:0:FUNC %s removed.\n", funcName) + } + + // Skip everything until matching @@FUNC_END + foundEnd := false + for i < len(codeLines) { + if strings.HasPrefix(codeLines[i], "; @@FUNC_END ") { + // Check if this is the exact function end marker + parts := strings.Fields(codeLines[i]) + if len(parts) >= 3 && parts[2] == funcName { + foundEnd = true + break + } + } + i++ + } + // Skip the END marker line too if found + if foundEnd && i < len(codeLines) { + i++ + } + // If we didn't find the end marker, we've reached end of file + continue + } + } + + result = append(result, line) + i++ + } + return result +} + // assembleOutput combines all generated sections into final assembly func (c *Compiler) assembleOutput(codeLines []string) []string { var output []string diff --git a/internal/compiler/funchandler.go b/internal/compiler/funchandler.go index c7e68cb..1078c1d 100644 --- a/internal/compiler/funchandler.go +++ b/internal/compiler/funchandler.go @@ -70,28 +70,29 @@ func NewFunctionHandler(st *SymbolTable, ls *LabelStack, csh *ConstantStringHand // HandleFuncDecl parses and processes a FUNC declaration // Syntax: FUNC name ( param1 param2 ... ) // Or: FUNC name (void function) -func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { +// Returns the function name +func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) (string, error) { // Normalize parentheses and commas text := fixIntuitiveFuncs(line.Text) params, err := parseParams(text) if err != nil { - return nil, fmt.Errorf("%s:%d: %w", line.Filename, line.LineNo, err) + return "", fmt.Errorf("%s:%d: %w", line.Filename, line.LineNo, err) } if len(params) < 2 { - return nil, fmt.Errorf("%s:%d: FUNC: expected at least function name", line.Filename, line.LineNo) + return "", fmt.Errorf("%s:%d: FUNC: expected at least function name", line.Filename, line.LineNo) } if strings.ToUpper(params[0]) != "FUNC" { - return nil, fmt.Errorf("%s:%d: not a FUNC declaration", line.Filename, line.LineNo) + return "", fmt.Errorf("%s:%d: not a FUNC declaration", line.Filename, line.LineNo) } funcName := params[1] // Check for redeclaration if fh.FuncExists(funcName) { - return nil, fmt.Errorf("%s:%d: function %q already declared", line.Filename, line.LineNo, funcName) + return "", fmt.Errorf("%s:%d: function %q already declared", line.Filename, line.LineNo, funcName) } // Push function name to current function stack early @@ -108,7 +109,7 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { // FUNC name ( param1 param2 ) if params[2] != "(" || params[len(params)-1] != ")" { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC: expected parentheses around parameters", line.Filename, line.LineNo) + return "", fmt.Errorf("%s:%d: FUNC: expected parentheses around parameters", line.Filename, line.LineNo) } // Extract params between ( and ) - need to handle {BYTE x} specially @@ -116,14 +117,14 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { paramSpecs, err := buildComplexParams(rawParamTokens) if err != nil { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC %s: %w", line.Filename, line.LineNo, funcName, err) + return "", fmt.Errorf("%s:%d: FUNC %s: %w", line.Filename, line.LineNo, funcName, err) } for _, spec := range paramSpecs { direction, varName, isImplicit, implicitDecl, err := parseParamSpec(spec) if err != nil { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC %s: %w", line.Filename, line.LineNo, funcName, err) + return "", fmt.Errorf("%s:%d: FUNC %s: %w", line.Filename, line.LineNo, funcName, err) } if isImplicit { @@ -131,7 +132,7 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { // Format: {BYTE varname} or {WORD varname} if err := fh.parseImplicitDecl(implicitDecl, funcName, line); err != nil { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC %s: implicit declaration: %w", line.Filename, line.LineNo, funcName, err) + return "", fmt.Errorf("%s:%d: FUNC %s: implicit declaration: %w", line.Filename, line.LineNo, funcName, err) } } @@ -139,12 +140,12 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { sym := fh.symTable.LookupWithoutUsage(varName, []string{funcName}) if sym == nil { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC %s: parameter %q not declared", line.Filename, line.LineNo, funcName, varName) + return "", fmt.Errorf("%s:%d: FUNC %s: parameter %q not declared", line.Filename, line.LineNo, funcName, varName) } if sym.IsConst() { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC %s: parameter %q cannot be a constant", line.Filename, line.LineNo, funcName, varName) + return "", fmt.Errorf("%s:%d: FUNC %s: parameter %q cannot be a constant", line.Filename, line.LineNo, funcName, varName) } funcParams = append(funcParams, &FuncParam{ @@ -154,7 +155,7 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { } } else { fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] - return nil, fmt.Errorf("%s:%d: FUNC: invalid syntax", line.Filename, line.LineNo) + return "", fmt.Errorf("%s:%d: FUNC: invalid syntax", line.Filename, line.LineNo) } // Store function declaration @@ -167,8 +168,7 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) { // Record absolute addresses used by this function fh.recordAbsoluteAddresses(funcName, funcParams) - // Generate assembler label - return []string{funcName}, nil + return funcName, nil } // recordAbsoluteAddresses stores which absolute addresses a function uses @@ -1034,3 +1034,27 @@ func (fh *FunctionHandler) CheckUnusedFunctions() []string { return warnings } + +// GetFunctionsToRemove returns a map of function names that should be removed from assembly output +// Functions are removed if they are never called AND have _P_REMOVE_UNUSED pragma enabled +func (fh *FunctionHandler) GetFunctionsToRemove() map[string]bool { + toRemove := make(map[string]bool) + + for _, funcDecl := range fh.functions { + // Skip functions that have been called + if fh.calledFunctions[funcDecl.Name] { + continue + } + + // Check if pragma indicates we should remove this unused function + if fh.pragma != nil { + pragmaSet := fh.pragma.GetPragmaSetByIndex(funcDecl.Line.PragmaSetIndex) + removeValue := pragmaSet.GetPragma("_P_REMOVE_UNUSED") + if removeValue != "" && removeValue != "0" { + toRemove[funcDecl.Name] = true + } + } + } + + return toRemove +} diff --git a/internal/compiler/funchandler_test.go b/internal/compiler/funchandler_test.go index 3c693a0..ff8ecdb 100644 --- a/internal/compiler/funchandler_test.go +++ b/internal/compiler/funchandler_test.go @@ -206,11 +206,15 @@ func TestHandleFuncDecl_VoidFunction(t *testing.T) { pragma := preproc.NewPragma() fh := NewFunctionHandler(st, ls, csh, pragma) - asm, err := fh.HandleFuncDecl(makeLine("FUNC test_void")) + funcName, err := fh.HandleFuncDecl(makeLine("FUNC test_void")) if err != nil { t.Fatalf("HandleFuncDecl failed: %v", err) } + if funcName != "test_void" { + t.Fatalf("expected funcName = \"test_void\", got %q", funcName) + } + if len(asm) != 1 { t.Fatalf("expected 1 asm line, got %d", len(asm)) } @@ -293,7 +297,7 @@ func TestHandleFuncDecl_WithDirections(t *testing.T) { pragma := preproc.NewPragma() fh := NewFunctionHandler(st, ls, csh, pragma) - _, err := fh.HandleFuncDecl(makeLine("FUNC test_dir ( in:{BYTE a} out:{BYTE b} io:{WORD c} )")) + _, _, err := fh.HandleFuncDecl(makeLine("FUNC test_dir ( in:{BYTE a} out:{BYTE b} io:{WORD c} )")) if err != nil { t.Fatalf("HandleFuncDecl failed: %v", err) } diff --git a/language.md b/language.md index 16352a9..2f6075b 100644 --- a/language.md +++ b/language.md @@ -676,12 +676,20 @@ Special characters in defines: Control compiler behavior: +- `_P_USE_LONG_JUMP`: Use JMP instead of branches for large switch statements +- `_P_USE_IMMUTABLE_CODE`: Disable self-modifying code (for ROM) +- `_P_USE_CBM_STRINGS`: Use PETSCII encoding for strings +- `_P_IGNORE_UNUSED`: Suppress warnings for unused variables +- `_P_REMOVE_UNUSED`: Remove unused functions from assembly output (requires explicit pragma on each function) + ```c65 #PRAGMA _P_USE_LONG_JUMP 1 // Use JMP instead of branches #PRAGMA _P_USE_IMMUTABLE_CODE 1 // No self-modifying code (for ROM) #PRAGMA _P_USE_CBM_STRINGS 1 // Use PETSCII encoding #PRAGMA _P_IGNORE_UNUSED 1 // Suppress unused variable warnings #PRAGMA _P_IGNORE_UNUSED 0 // Enable unused variable warnings +#PRAGMA _P_REMOVE_UNUSED 1 // Remove function if unused +#PRAGMA _P_REMOVE_UNUSED 0 // Keep function even if unused (default) ``` ### Debug Directives diff --git a/syntax.md b/syntax.md index b150034..d94d89d 100644 --- a/syntax.md +++ b/syntax.md @@ -254,6 +254,12 @@ Sets compiler pragmas (options). - Value: "0" enables warnings, any non-"0" value disables warnings - Note: Do not use `=` sign, use space: `#PRAGMA _P_IGNORE_UNUSED 1` +**_P_REMOVE_UNUSED** +- When enabled (value ≠ "" and ≠ "0"), unused functions with this pragma will be removed from the final assembly output +- Requires function to be marked with `#PRAGMA _P_REMOVE_UNUSED 1` at function scope +- Functions are only removed if they are never called in the program +- The compiler will output an info message to stdout when a function is removed + **Examples:** ``` #PRAGMA _P_USE_LONG_JUMP 1 @@ -262,6 +268,8 @@ Sets compiler pragmas (options). #PRAGMA _P_USE_CBM_STRINGS 1 #PRAGMA _P_IGNORE_UNUSED 1 //suppress unused variable warnings #PRAGMA _P_IGNORE_UNUSED 0 //enable unused variable warnings +#PRAGMA _P_REMOVE_UNUSED 1 //remove function if unused +#PRAGMA _P_REMOVE_UNUSED 0 //keep function even if unused (default) ``` --- From bd4dc5d8856c85cc5aa3ea9c46b73407f4430da3 Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Mon, 13 Apr 2026 20:29:45 +0200 Subject: [PATCH 2/2] Updated tests so they work with new FuncHandler signature --- AGENTS.md | 48 ++++++++++++++++++++++-- internal/compiler/funchandler.go | 6 +++ internal/compiler/funchandler_test.go | 53 ++++++++++++--------------- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 35a5aa2..b1fad8d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -116,12 +116,33 @@ All file operations must be restricted to the project's source code and document 3. Update documentation in appropriate .md files 4. Consider adding examples in `examples/` directory -### Testing -**IMPORTANT**: The agent cannot run tests. Provide these instructions to the user: +### Testing and Rebuilding +**IMPORTANT**: The agent cannot run tests or compile code. Provide these instructions to the user: + +#### Testing: - Run all tests: `go test ./...` - Run specific package tests: `go test ./internal/compiler` - Test with verbose output: `go test -v ./...` +#### Rebuilding c65gm: +When making changes to the compiler, ask the user to rebuild c65gm: +```bash +go build -o c65gm +``` +This creates a fresh binary with your changes. The agent should: +1. Make code changes +2. Ask the user to rebuild c65gm: `go build -o c65gm` +3. **Check file datetime**: Verify the c65gm binary was recently updated (use `ls -la c65gm` or similar) +4. Ask the user to run tests: `go test ./...` +5. Test the changes with example .c65 files + +**IMPORTANT**: Always check the file datetime of the c65gm binary after asking for a rebuild. If the timestamp hasn't changed, the user may have forgotten to rebuild, or the build may have failed. Testing with an old version is wasteful and can lead to incorrect conclusions. + +#### Compiling .c65 files: +```bash +C65LIBPATH=/app/lib ./c65gm -in input.c65 -out output.asm +``` + ## Common Pitfalls 1. **Expression evaluation**: Remember no operator precedence @@ -164,4 +185,25 @@ acme -f cbm -o output.prg output.asm ### Testing - `*_test.go` files throughout the codebase -- `examples/`: Functional test programs \ No newline at end of file +- `examples/`: Functional test programs + +## Tool Notes + +### Shell Environment +- **Shell**: Only `/bin/sh` (BusyBox) is available, not bash +- **The `bash` tool** in the interface runs commands through `/bin/sh` (BusyBox) +- **BusyBox limitations**: Many GNU extensions are not available + +### grep Limitations +The environment uses BusyBox grep which doesn't support GNU grep extensions: +- **NOT supported**: `--include="*.go"`, `--exclude="*.md"`, etc. +- **Use instead**: `find /app -name "*.go" -type f -exec grep -l "pattern" {} \;` +- **Example**: Instead of `grep -r "CheckUnusedFunctions" /app --include="*.go"`, use: + ```bash + find /app -name "*.go" -type f -exec grep -l "CheckUnusedFunctions" {} \; + ``` + +### File Operations +- Use `find` with `-exec` for pattern-based file searches +- Use `grep` without GNU extensions for simple pattern matching +- Prefer the `bash` tool with proper BusyBox-compatible commands \ No newline at end of file diff --git a/internal/compiler/funchandler.go b/internal/compiler/funchandler.go index 1078c1d..644540d 100644 --- a/internal/compiler/funchandler.go +++ b/internal/compiler/funchandler.go @@ -1020,10 +1020,16 @@ func (fh *FunctionHandler) CheckUnusedFunctions() []string { // Check if pragma indicates we should ignore unused warnings for this function if fh.pragma != nil { pragmaSet := fh.pragma.GetPragmaSetByIndex(funcDecl.Line.PragmaSetIndex) + // Skip warning if _P_IGNORE_UNUSED is enabled value := pragmaSet.GetPragma("_P_IGNORE_UNUSED") if value != "" && value != "0" { continue // Skip warning for this function } + // Also skip warning if _P_REMOVE_UNUSED is enabled (function will be removed) + removeValue := pragmaSet.GetPragma("_P_REMOVE_UNUSED") + if removeValue != "" && removeValue != "0" { + continue // Skip warning for this function (it will be removed) + } } // Format warning message with file and line info diff --git a/internal/compiler/funchandler_test.go b/internal/compiler/funchandler_test.go index ff8ecdb..6cb58f2 100644 --- a/internal/compiler/funchandler_test.go +++ b/internal/compiler/funchandler_test.go @@ -215,13 +215,6 @@ func TestHandleFuncDecl_VoidFunction(t *testing.T) { t.Fatalf("expected funcName = \"test_void\", got %q", funcName) } - if len(asm) != 1 { - t.Fatalf("expected 1 asm line, got %d", len(asm)) - } - if asm[0] != "test_void" { - t.Errorf("expected label 'test_void', got %q", asm[0]) - } - if !fh.FuncExists("test_void") { t.Error("function should exist") } @@ -238,13 +231,13 @@ func TestHandleFuncDecl_WithExistingParams(t *testing.T) { st.AddVar("x", "test_func", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) st.AddVar("y", "test_func", KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - asm, err := fh.HandleFuncDecl(makeLine("FUNC test_func ( x y )")) + funcName, err := fh.HandleFuncDecl(makeLine("FUNC test_func ( x y )")) if err != nil { t.Fatalf("HandleFuncDecl failed: %v", err) } - if len(asm) != 1 { - t.Fatalf("expected 1 asm line, got %d", len(asm)) + if funcName != "test_func" { + t.Fatalf("expected funcName = \"test_func\", got %q", funcName) } funcDecl := fh.findFunc("test_func") @@ -263,13 +256,13 @@ func TestHandleFuncDecl_ImplicitDeclarations(t *testing.T) { pragma := preproc.NewPragma() fh := NewFunctionHandler(st, ls, csh, pragma) - asm, err := fh.HandleFuncDecl(makeLine("FUNC test_impl ( {BYTE a} {WORD b} )")) + funcName, err := fh.HandleFuncDecl(makeLine("FUNC test_impl ( {BYTE a} {WORD b} )")) if err != nil { t.Fatalf("HandleFuncDecl failed: %v", err) } - if len(asm) != 1 { - t.Fatalf("expected 1 asm line, got %d", len(asm)) + if funcName != "test_impl" { + t.Fatalf("expected funcName = \"test_impl\", got %q", funcName) } // Check that variables were declared @@ -297,7 +290,7 @@ func TestHandleFuncDecl_WithDirections(t *testing.T) { pragma := preproc.NewPragma() fh := NewFunctionHandler(st, ls, csh, pragma) - _, _, err := fh.HandleFuncDecl(makeLine("FUNC test_dir ( in:{BYTE a} out:{BYTE b} io:{WORD c} )")) + _, err := fh.HandleFuncDecl(makeLine("FUNC test_dir ( in:{BYTE a} out:{BYTE b} io:{WORD c} )")) if err != nil { t.Fatalf("HandleFuncDecl failed: %v", err) } @@ -369,7 +362,7 @@ func TestHandleFuncDecl_Errors(t *testing.T) { // Special case for redeclaration test if tt.name == "redeclaration" { - fh.HandleFuncDecl(makeLine("FUNC duplicate ( {BYTE x} )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC duplicate ( {BYTE x} )")) } _, err := fh.HandleFuncDecl(makeLine(tt.line)) @@ -393,7 +386,7 @@ func TestHandleFuncCall_VarArgs(t *testing.T) { // Declare function with params st.AddVar("param_a", "test_func", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) st.AddVar("param_b", "test_func", KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test_func ( param_a param_b )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test_func ( param_a param_b )")) // Declare caller variables st.AddVar("var_a", "", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) @@ -435,7 +428,7 @@ func TestHandleFuncCall_OutParams(t *testing.T) { // Declare function with out param st.AddVar("result", "get_result", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC get_result ( out:result )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC get_result ( out:result )")) // Declare caller variable st.AddVar("output", "", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) @@ -475,7 +468,7 @@ func TestHandleFuncCall_ConstArgs(t *testing.T) { // Declare function st.AddVar("x", "test_const", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) st.AddVar("y", "test_const", KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test_const ( x y )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test_const ( x y )")) asm, err := fh.HandleFuncCall(makeLine("CALL test_const ( 42 $1234 )")) if err != nil { @@ -511,7 +504,7 @@ func TestHandleFuncCall_LabelArg(t *testing.T) { // Declare function st.AddVar("ptr", "test_label", KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test_label ( ptr )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test_label ( ptr )")) asm, err := fh.HandleFuncCall(makeLine("CALL test_label ( @my_label )")) if err != nil { @@ -544,7 +537,7 @@ func TestHandleFuncCall_StringArg(t *testing.T) { // Declare function st.AddVar("str_ptr", "print", KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC print ( str_ptr )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC print ( str_ptr )")) asm, err := fh.HandleFuncCall(makeLine(`CALL print ( "hello" )`)) if err != nil { @@ -587,7 +580,7 @@ func TestHandleFuncCall_Errors(t *testing.T) { name: "wrong arg count", setup: func(fh *FunctionHandler, st *SymbolTable) { st.AddVar("x", "test", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test ( x )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test ( x )")) }, line: "CALL test ( 1 2 )", wantErr: "expected 1 arguments, got 2", @@ -598,7 +591,7 @@ func TestHandleFuncCall_Errors(t *testing.T) { name: "type mismatch", setup: func(fh *FunctionHandler, st *SymbolTable) { st.AddVar("param", "test", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test ( param )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test ( param )")) st.AddVar("wvar", "", KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) }, line: "CALL test ( wvar )", @@ -608,7 +601,7 @@ func TestHandleFuncCall_Errors(t *testing.T) { name: "const to out param", setup: func(fh *FunctionHandler, st *SymbolTable) { st.AddVar("result", "test", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test ( out:result )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test ( out:result )")) }, line: "CALL test ( 42 )", wantErr: "out/io parameter", @@ -617,7 +610,7 @@ func TestHandleFuncCall_Errors(t *testing.T) { name: "label to byte param", setup: func(fh *FunctionHandler, st *SymbolTable) { st.AddVar("x", "test", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) - fh.HandleFuncDecl(makeLine("FUNC test ( x )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test ( x )")) }, line: "CALL test ( @label )", wantErr: "byte parameter", @@ -653,7 +646,7 @@ func TestHandleFuncCall_EmptyParens(t *testing.T) { fh := NewFunctionHandler(st, ls, csh, pragma) // Declare void function - fh.HandleFuncDecl(makeLine("FUNC init")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC init")) tests := []struct { name string @@ -696,7 +689,7 @@ func TestEndFunction(t *testing.T) { fh := NewFunctionHandler(st, ls, csh, pragma) // Declare function (pushes to stack) - fh.HandleFuncDecl(makeLine("FUNC test ( {BYTE x} )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test ( {BYTE x} )")) if fh.CurrentFunction() != "test" { t.Errorf("current function = %q, want 'test'", fh.CurrentFunction()) @@ -721,12 +714,12 @@ func TestCurrentFunction(t *testing.T) { t.Error("expected empty current function initially") } - fh.HandleFuncDecl(makeLine("FUNC func1 ( {BYTE x} )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC func1 ( {BYTE x} )")) if fh.CurrentFunction() != "func1" { t.Errorf("expected 'func1', got %q", fh.CurrentFunction()) } - fh.HandleFuncDecl(makeLine("FUNC func2 ( {BYTE y} )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC func2 ( {BYTE y} )")) if fh.CurrentFunction() != "func2" { t.Errorf("expected 'func2', got %q", fh.CurrentFunction()) } @@ -922,7 +915,7 @@ func TestHandleFuncCall_AbsoluteParams(t *testing.T) { fh := NewFunctionHandler(st, ls, csh, pragma) // Declare function with absolute params - fh.HandleFuncDecl(makeLine("FUNC test_abs ( {BYTE param_a @ $fa} {WORD param_b @ $fb} )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC test_abs ( {BYTE param_a @ $fa} {WORD param_b @ $fb} )")) // Declare caller variables st.AddVar("var_a", "", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) @@ -963,7 +956,7 @@ func TestHandleFuncCall_AbsoluteOutParams(t *testing.T) { fh := NewFunctionHandler(st, ls, csh, pragma) // Declare function with absolute out param - fh.HandleFuncDecl(makeLine("FUNC get_result ( out:{BYTE result @ $fa} )")) + _, _ = fh.HandleFuncDecl(makeLine("FUNC get_result ( out:{BYTE result @ $fa} )")) // Declare caller variable st.AddVar("output", "", KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})