package compiler import ( "fmt" "strings" "testing" "c65gm/internal/preproc" "c65gm/internal/utils" ) // TestBreakCommand is a simple command implementation for testing type TestBreakCommand struct { line preproc.Line } func (c *TestBreakCommand) WillHandle(line preproc.Line) bool { params, err := utils.ParseParams(line.Text) if err != nil || len(params) == 0 { return false } return strings.ToUpper(params[0]) == "BREAK" } func (c *TestBreakCommand) Interpret(line preproc.Line, ctx *CompilerContext) error { c.line = line params, err := utils.ParseParams(line.Text) if err != nil { return err } if len(params) != 1 { return fmt.Errorf("BREAK does not expect parameters") } return nil } func (c *TestBreakCommand) Generate(ctx *CompilerContext) ([]string, error) { // BREAK jumps to end of WHILE loop label, err := ctx.LoopEndStack.Peek() if err != nil { return nil, fmt.Errorf("BREAK outside of WHILE loop") } return []string{ fmt.Sprintf(" jmp %s_end", label), }, nil } func TestCompilerArchitecture(t *testing.T) { // Create pragma pragma := preproc.NewPragma() // Create compiler comp := NewCompiler(pragma) // Register BREAK command comp.Registry().Register(&TestBreakCommand{}) // Create test input - BREAK inside a simulated WHILE lines := []preproc.Line{ { Text: "BREAK", Filename: "test.c65", LineNo: 1, Kind: preproc.Source, PragmaSetIndex: 0, }, } // Manually push a WHILE label so BREAK has something to reference comp.Context().LoopEndStack.Push() // Compile output, err := comp.Compile(lines) // Should fail because BREAK needs proper WHILE context // But this tests the basic flow: WillHandle -> Interpret -> Generate if err != nil { t.Logf("Expected controlled error: %v", err) } // Check we got some output structure if len(output) == 0 { t.Logf("Got output lines: %d", len(output)) } t.Logf("Output:\n%s", strings.Join(output, "\n")) } func TestCommandRegistry(t *testing.T) { registry := NewCommandRegistry() breakCmd := &TestBreakCommand{} registry.Register(breakCmd) line := preproc.Line{ Text: "BREAK", Filename: "test.c65", LineNo: 1, Kind: preproc.Source, } cmd, found := registry.FindHandler(line) if !found { t.Fatal("Expected to find BREAK handler") } if cmd != breakCmd { t.Fatal("Expected to get same command instance") } } func TestCompilerContext(t *testing.T) { pragma := preproc.NewPragma() ctx := NewCompilerContext(pragma) // Test that all resources are initialized if ctx.SymbolTable == nil { t.Error("SymbolTable not initialized") } if ctx.FunctionHandler == nil { t.Error("FunctionHandler not initialized") } if ctx.ConstStrHandler == nil { t.Error("ConstStrHandler not initialized") } if ctx.LoopEndStack == nil { t.Error("LoopEndStack not initialized") } if ctx.IfStack == nil { t.Error("IfStack not initialized") } if ctx.GeneralStack == nil { t.Error("GeneralStack not initialized") } if ctx.Pragma == nil { t.Error("Pragma not initialized") } // Test CurrentScope scope := ctx.CurrentScope() if scope != nil { t.Errorf("Expected nil scope in global context, got %v", scope) } }