c65gm/internal/compiler/compiler_test.go

148 lines
3.2 KiB
Go

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)
}
}