package calc import ( "chain" "errors" "strconv" "strings" ) // stack holds the shared RPN operand stack, bottom (index 0) to top. var stack []int // ErrUnderflow is raised when an operator needs more operands than available. var ErrUnderflow = errors.New("stack underflow: operator needs two operands") // ErrDivByZero is raised on division by zero. var ErrDivByZero = errors.New("division by zero") // Push consumes a single token: an integer is pushed onto the stack; an // operator (+, -, *, /) pops two operands and pushes the result. func Push(cur realm, token string) { token = strings.TrimSpace(token) if token == "" { panic("empty token") } switch token { case "+", "-", "*", "/": if len(stack) < 2 { panic(ErrUnderflow.Error()) } b := stack[len(stack)-1] a := stack[len(stack)-2] stack = stack[:len(stack)-2] var r int switch token { case "+": r = a + b case "-": r = a - b case "*": r = a * b case "/": if b == 0 { panic(ErrDivByZero.Error()) } r = a / b } stack = append(stack, r) chain.Emit("Op", "op", token, "result", strconv.Itoa(r)) default: n, err := strconv.Atoi(token) if err != nil { panic("invalid token: expected integer or one of + - * /, got " + token) } stack = append(stack, n) chain.Emit("Push", "value", strconv.Itoa(n)) } } // Clear empties the shared stack. func Clear(cur realm) { stack = nil chain.Emit("Clear") } // Depth returns the number of values currently on the stack. func Depth() int { return len(stack) } // Top returns the top value and whether the stack is non-empty. func Top() (int, bool) { if len(stack) == 0 { return 0, false } return stack[len(stack)-1], true } // Render renders the current stack, the top value as the result, and a // short usage guide. func Render(path string) string { var sb strings.Builder sb.WriteString("# RPN Calculator\n\n") sb.WriteString("## Result\n\n") if top, ok := Top(); ok { sb.WriteString("**") sb.WriteString(strconv.Itoa(top)) sb.WriteString("**\n\n") } else { sb.WriteString("_(empty stack)_\n\n") } sb.WriteString("## Stack (bottom → top)\n\n") if len(stack) == 0 { sb.WriteString("_empty_\n\n") } else { for i, v := range stack { sb.WriteString(strconv.Itoa(i)) sb.WriteString(". ") sb.WriteString(strconv.Itoa(v)) sb.WriteString("\n") } sb.WriteString("\n") } sb.WriteString("## Usage\n\n") sb.WriteString("Reverse Polish Notation: operands first, operator last.\n\n") sb.WriteString("- `Push(\"5\")` then `Push(\"3\")` then `Push(\"+\")` → `8`\n") sb.WriteString("- Supported operators: `+`, `-`, `*`, `/` (integer division)\n") sb.WriteString("- An operator pops the top two values (a, b) and pushes `a op b`.\n") sb.WriteString("- `Clear()` empties the stack.\n") sb.WriteString("- Divide-by-zero and underflow abort the transaction.\n") return sb.String() }