Search Apps Documentation Source Content File Folder Download Copy Actions Download

calc.gno

2.84 Kb · 118 lines
  1package calc
  2
  3import (
  4	"chain"
  5	"errors"
  6	"strconv"
  7	"strings"
  8)
  9
 10// stack holds the shared RPN operand stack, bottom (index 0) to top.
 11var stack []int
 12
 13// ErrUnderflow is raised when an operator needs more operands than available.
 14var ErrUnderflow = errors.New("stack underflow: operator needs two operands")
 15
 16// ErrDivByZero is raised on division by zero.
 17var ErrDivByZero = errors.New("division by zero")
 18
 19// Push consumes a single token: an integer is pushed onto the stack; an
 20// operator (+, -, *, /) pops two operands and pushes the result.
 21func Push(cur realm, token string) {
 22	token = strings.TrimSpace(token)
 23	if token == "" {
 24		panic("empty token")
 25	}
 26
 27	switch token {
 28	case "+", "-", "*", "/":
 29		if len(stack) < 2 {
 30			panic(ErrUnderflow.Error())
 31		}
 32		b := stack[len(stack)-1]
 33		a := stack[len(stack)-2]
 34		stack = stack[:len(stack)-2]
 35
 36		var r int
 37		switch token {
 38		case "+":
 39			r = a + b
 40		case "-":
 41			r = a - b
 42		case "*":
 43			r = a * b
 44		case "/":
 45			if b == 0 {
 46				panic(ErrDivByZero.Error())
 47			}
 48			r = a / b
 49		}
 50		stack = append(stack, r)
 51		chain.Emit("Op", "op", token, "result", strconv.Itoa(r))
 52	default:
 53		n, err := strconv.Atoi(token)
 54		if err != nil {
 55			panic("invalid token: expected integer or one of + - * /, got " + token)
 56		}
 57		stack = append(stack, n)
 58		chain.Emit("Push", "value", strconv.Itoa(n))
 59	}
 60}
 61
 62// Clear empties the shared stack.
 63func Clear(cur realm) {
 64	stack = nil
 65	chain.Emit("Clear")
 66}
 67
 68// Depth returns the number of values currently on the stack.
 69func Depth() int {
 70	return len(stack)
 71}
 72
 73// Top returns the top value and whether the stack is non-empty.
 74func Top() (int, bool) {
 75	if len(stack) == 0 {
 76		return 0, false
 77	}
 78	return stack[len(stack)-1], true
 79}
 80
 81// Render renders the current stack, the top value as the result, and a
 82// short usage guide.
 83func Render(path string) string {
 84	var sb strings.Builder
 85	sb.WriteString("# RPN Calculator\n\n")
 86
 87	sb.WriteString("## Result\n\n")
 88	if top, ok := Top(); ok {
 89		sb.WriteString("**")
 90		sb.WriteString(strconv.Itoa(top))
 91		sb.WriteString("**\n\n")
 92	} else {
 93		sb.WriteString("_(empty stack)_\n\n")
 94	}
 95
 96	sb.WriteString("## Stack (bottom → top)\n\n")
 97	if len(stack) == 0 {
 98		sb.WriteString("_empty_\n\n")
 99	} else {
100		for i, v := range stack {
101			sb.WriteString(strconv.Itoa(i))
102			sb.WriteString(". ")
103			sb.WriteString(strconv.Itoa(v))
104			sb.WriteString("\n")
105		}
106		sb.WriteString("\n")
107	}
108
109	sb.WriteString("## Usage\n\n")
110	sb.WriteString("Reverse Polish Notation: operands first, operator last.\n\n")
111	sb.WriteString("- `Push(\"5\")` then `Push(\"3\")` then `Push(\"+\")` → `8`\n")
112	sb.WriteString("- Supported operators: `+`, `-`, `*`, `/` (integer division)\n")
113	sb.WriteString("- An operator pops the top two values (a, b) and pushes `a op b`.\n")
114	sb.WriteString("- `Clear()` empties the stack.\n")
115	sb.WriteString("- Divide-by-zero and underflow abort the transaction.\n")
116
117	return sb.String()
118}