c65gm/internal/commands/else_test.go

264 lines
5.4 KiB
Go

package commands
import (
"fmt"
"strings"
"testing"
"c65gm/internal/compiler"
"c65gm/internal/preproc"
)
func TestElseCommand_WillHandle(t *testing.T) {
cmd := &ElseCommand{}
tests := []struct {
name string
line string
want bool
}{
{"ELSE", "ELSE", true},
{"not ELSE", "IF a = b", false},
{"empty", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
line := preproc.Line{Text: tt.line, Kind: preproc.Source}
got := cmd.WillHandle(line)
if got != tt.want {
t.Errorf("WillHandle() = %v, want %v", got, tt.want)
}
})
}
}
func TestEndIfCommand_WillHandle(t *testing.T) {
cmd := &EndIfCommand{}
tests := []struct {
name string
line string
want bool
}{
{"ENDIF", "ENDIF", true},
{"not ENDIF", "IF a = b", false},
{"empty", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
line := preproc.Line{Text: tt.line, Kind: preproc.Source}
got := cmd.WillHandle(line)
if got != tt.want {
t.Errorf("WillHandle() = %v, want %v", got, tt.want)
}
})
}
}
func TestIfElseEndif_Integration(t *testing.T) {
tests := []struct {
name string
lines []string
setupVars func(*compiler.SymbolTable)
wantAsm []string
}{
{
name: "IF...ENDIF (no ELSE)",
lines: []string{
"IF a = b",
"ENDIF",
},
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
},
wantAsm: []string{
"; IF a = b",
"\tlda a",
"\tcmp b",
"\tbne _I1",
"; ENDIF",
"_I1",
},
},
{
name: "IF...ELSE...ENDIF",
lines: []string{
"IF a = b",
"ELSE",
"ENDIF",
},
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
},
wantAsm: []string{
"; IF a = b",
"\tlda a",
"\tcmp b",
"\tbne _I1",
"; ELSE",
"\tjmp _I2",
"_I1",
"; ENDIF",
"_I2",
},
},
{
name: "nested IF statements",
lines: []string{
"IF a = 10",
"IF b = 20",
"ENDIF",
"ENDIF",
},
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
},
wantAsm: []string{
"; IF a = 10",
"\tlda a",
"\tcmp #$0a",
"\tbne _I1",
"; IF b = 20",
"\tlda b",
"\tcmp #$14",
"\tbne _I2",
"; ENDIF",
"_I2",
"; ENDIF",
"_I1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := compiler.NewCompilerContext(preproc.NewPragma())
tt.setupVars(ctx.SymbolTable)
var allAsm []string
for _, lineText := range tt.lines {
line := preproc.Line{Text: lineText, Kind: preproc.Source, PragmaSetIndex: 0}
// Determine which command to use
var cmd compiler.Command
if strings.HasPrefix(strings.ToUpper(lineText), "IF") {
cmd = &IfCommand{}
} else if strings.ToUpper(lineText) == "ELSE" {
cmd = &ElseCommand{}
} else if strings.ToUpper(lineText) == "ENDIF" {
cmd = &EndIfCommand{}
} else {
t.Fatalf("unknown command: %s", lineText)
}
err := cmd.Interpret(line, ctx)
if err != nil {
t.Fatalf("Interpret(%q) error = %v", lineText, err)
}
asm, err := cmd.Generate(ctx)
if err != nil {
t.Fatalf("Generate(%q) error = %v", lineText, err)
}
allAsm = append(allAsm, fmt.Sprintf("; %s", lineText))
allAsm = append(allAsm, asm...)
}
if !equalAsmElse(allAsm, tt.wantAsm) {
t.Errorf("Assembly mismatch\ngot:\n%s\nwant:\n%s",
strings.Join(allAsm, "\n"),
strings.Join(tt.wantAsm, "\n"))
}
})
}
}
func TestElseCommand_Errors(t *testing.T) {
tests := []struct {
name string
line string
wantErr string
}{
{
name: "ELSE without IF",
line: "ELSE",
wantErr: "stack underflow",
},
{
name: "wrong param count",
line: "ELSE extra",
wantErr: "wrong number of parameters",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := compiler.NewCompilerContext(preproc.NewPragma())
cmd := &ElseCommand{}
line := preproc.Line{Text: tt.line, Kind: preproc.Source}
err := cmd.Interpret(line, ctx)
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("error = %q, want substring %q", err.Error(), tt.wantErr)
}
})
}
}
func TestEndIfCommand_Errors(t *testing.T) {
tests := []struct {
name string
line string
wantErr string
}{
{
name: "ENDIF without IF",
line: "ENDIF",
wantErr: "stack underflow",
},
{
name: "wrong param count",
line: "ENDIF extra",
wantErr: "wrong number of parameters",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := compiler.NewCompilerContext(preproc.NewPragma())
cmd := &EndIfCommand{}
line := preproc.Line{Text: tt.line, Kind: preproc.Source}
err := cmd.Interpret(line, ctx)
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("error = %q, want substring %q", err.Error(), tt.wantErr)
}
})
}
}
// equalAsmElse compares two assembly slices for equality
func equalAsmElse(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}