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