Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}