c65gm/internal/compiler/labelstack_test.go

232 lines
4.8 KiB
Go

package compiler
import "testing"
func TestLabelStack_PushCreatesUniqueLabels(t *testing.T) {
stack := NewLabelStack("test")
lbl1 := stack.Push()
lbl2 := stack.Push()
lbl3 := stack.Push()
if lbl1 != "test1" {
t.Errorf("expected test1, got %s", lbl1)
}
if lbl2 != "test2" {
t.Errorf("expected test2, got %s", lbl2)
}
if lbl3 != "test3" {
t.Errorf("expected test3, got %s", lbl3)
}
}
func TestLabelStack_PeekDoesNotRemove(t *testing.T) {
stack := NewLabelStack("peek")
stack.Push()
lbl := stack.Push()
peeked, err := stack.Peek()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if peeked != lbl {
t.Errorf("expected %s, got %s", lbl, peeked)
}
if stack.Size() != 2 {
t.Errorf("expected size 2 after peek, got %d", stack.Size())
}
}
func TestLabelStack_PopRemoves(t *testing.T) {
stack := NewLabelStack("pop")
stack.Push()
lbl := stack.Push()
popped, err := stack.Pop()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if popped != lbl {
t.Errorf("expected %s, got %s", lbl, popped)
}
if stack.Size() != 1 {
t.Errorf("expected size 1 after pop, got %d", stack.Size())
}
}
func TestLabelStack_PeekEmptyReturnsError(t *testing.T) {
stack := NewLabelStack("empty")
_, err := stack.Peek()
if err == nil {
t.Error("expected error on peek of empty stack")
}
}
func TestLabelStack_PopEmptyReturnsError(t *testing.T) {
stack := NewLabelStack("empty")
_, err := stack.Pop()
if err == nil {
t.Error("expected error on pop of empty stack")
}
}
func TestLabelStack_IsEmpty(t *testing.T) {
stack := NewLabelStack("check")
if !stack.IsEmpty() {
t.Error("new stack should be empty")
}
stack.Push()
if stack.IsEmpty() {
t.Error("stack with item should not be empty")
}
stack.Pop()
if !stack.IsEmpty() {
t.Error("stack after pop should be empty")
}
}
func TestLabelStack_CounterNeverResets(t *testing.T) {
stack := NewLabelStack("counter")
lbl1 := stack.Push()
lbl2 := stack.Push()
stack.Pop()
stack.Pop()
lbl3 := stack.Push()
if lbl1 != "counter1" || lbl2 != "counter2" || lbl3 != "counter3" {
t.Errorf("counter reset detected: %s, %s, %s", lbl1, lbl2, lbl3)
}
}
func TestLabelStack_WhileWendPattern(t *testing.T) {
whileStack := NewLabelStack("whilelbl")
wendStack := NewLabelStack("wendlbl")
// WHILE
whileLbl := whileStack.Push()
wendLbl := wendStack.Push()
if whileLbl != "whilelbl1" {
t.Errorf("expected whilelbl1, got %s", whileLbl)
}
if wendLbl != "wendlbl1" {
t.Errorf("expected wendlbl1, got %s", wendLbl)
}
// WEND
w, err := whileStack.Pop()
if err != nil || w != whileLbl {
t.Errorf("failed to pop while label")
}
wd, err := wendStack.Pop()
if err != nil || wd != wendLbl {
t.Errorf("failed to pop wend label")
}
}
func TestLabelStack_IfElseEndifPattern(t *testing.T) {
ifStack := NewLabelStack("iflbl")
// IF
ifLbl1 := ifStack.Push()
// ELSE - peek the if label, then push new one
peeked, err := ifStack.Peek()
if err != nil || peeked != ifLbl1 {
t.Errorf("failed to peek if label")
}
// Commit (pop) the first label
popped, err := ifStack.Pop()
if err != nil || popped != ifLbl1 {
t.Errorf("failed to pop first if label")
}
// Push new label for ENDIF
ifLbl2 := ifStack.Push()
// ENDIF
endifLbl, err := ifStack.Pop()
if err != nil || endifLbl != ifLbl2 {
t.Errorf("failed to pop endif label")
}
if ifLbl1 != "iflbl1" || ifLbl2 != "iflbl2" {
t.Errorf("unexpected label names: %s, %s", ifLbl1, ifLbl2)
}
}
func TestLabelStack_NestedIfPattern(t *testing.T) {
ifStack := NewLabelStack("iflbl")
// Outer IF
outerIf := ifStack.Push()
// Inner IF
innerIf := ifStack.Push()
// Inner ENDIF
innerEnd, _ := ifStack.Pop()
if innerEnd != innerIf {
t.Errorf("inner endif mismatch")
}
// Outer ENDIF
outerEnd, _ := ifStack.Pop()
if outerEnd != outerIf {
t.Errorf("outer endif mismatch")
}
if outerIf != "iflbl1" || innerIf != "iflbl2" {
t.Errorf("nested labels incorrect: %s, %s", outerIf, innerIf)
}
}
func TestLabelStack_BreakPattern(t *testing.T) {
wendStack := NewLabelStack("wendlbl")
// WHILE
wendLbl := wendStack.Push()
// BREAK - needs to peek wend label without popping
breakTarget, err := wendStack.Peek()
if err != nil || breakTarget != wendLbl {
t.Errorf("failed to peek for break")
}
// Stack should still have the label
if wendStack.Size() != 1 {
t.Error("peek should not modify stack")
}
// WEND - now pop it
wend, _ := wendStack.Pop()
if wend != wendLbl {
t.Errorf("wend label mismatch")
}
}
func TestLabelStack_MultipleSeparateStacks(t *testing.T) {
whileStack := NewLabelStack("whilelbl")
ifStack := NewLabelStack("iflbl")
generalStack := NewLabelStack("general")
w := whileStack.Push()
i := ifStack.Push()
g := generalStack.Push()
if w != "whilelbl1" || i != "iflbl1" || g != "general1" {
t.Errorf("separate stacks interfering: %s, %s, %s", w, i, g)
}
}