aboutsummaryrefslogtreecommitdiffstats
path: root/calc.go
blob: 67794aa7659583c9edd9d2eb7f4a42bfa027ef3d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package main

import "strconv"

type AngleMode bool

const (
	degrees AngleMode = false
	radians AngleMode = true
)

func (a AngleMode) String() string {
	if a == degrees {
		return "deg"
	}
	return "rad"
}

type Calculator struct {
	stack     Stack
	buf       string
	angleMode AngleMode
}

// swap swaps the values of the buffer and the bottom element of the stack. If
// the buffer is empty this simply pops from the stack. If the stack is empty,
// this simply pushes to the stack.
func (c *Calculator) swap() {
	stackVal, err := c.stack.pop()
	stackIsEmpty := err != nil

	if v, err := c.parseBuffer(); err == nil {
		c.stack.push(v)
	}

	if stackIsEmpty {
		c.buf = ""
	} else {
		c.buf = printNum(stackVal)
	}
}

// dup duplicates the value at the bottom of the stack.
func (c *Calculator) dup() {
	v, err := c.stack.pop()
	if err != nil {
		// empty
		return
	}
	c.stack.push(v)
	c.stack.push(v)
}

// negate negates the number in the buffer, if any; or the bottom number on the
// stack, if any.
func (c *Calculator) negate() {
	if v, err := c.parseBuffer(); err == nil {
		c.buf = printNum(-v)
	} else if len(c.buf) == 0 && len(c.stack) > 0 {
		c.stack[len(c.stack)-1] = -c.stack[len(c.stack)-1]
	}
}

// performOp performs the specified arithmetic operation.
func (c *Calculator) performOperation(operator byte) {
	fn, err := parseOperator(operator)
	if err != nil {
		return
	}
	lhs, rhs, err := c.operands()
	if err != nil {
		return
	}
	c.stack.push(fn(lhs, rhs))
	c.buf = ""
}

// operands returns the left and right operands, or error if there are not enough.
func (c *Calculator) operands() (lhs, rhs float64, err error) {
	if rhs, err = c.parseBuffer(); err == nil {
		lhs, err = c.stack.pop()
		return
	} else if rhs, err = c.stack.pop(); err == nil {
		lhs, err = c.stack.pop()
		if err != nil { // missing lhs
			c.stack.push(rhs)
		}
		return
	}
	return 0, 0, OperandErr{}
}

// parseBuffer returns the numerical value of the contents of the buffer.
func (c Calculator) parseBuffer() (float64, error) {
	if con, err := parseConstant(c.buf); err == nil {
		return con, nil
	} else if fl, err := strconv.ParseFloat(c.buf, 64); err == nil {
		return fl, nil
	}
	return 0, InvalidBufferContentErr{c.buf}
}

type InvalidBufferContentErr struct {
	buf string
}

func (e InvalidBufferContentErr) Error() string {
	return "invalid buffer contents: " + e.buf
}

type OperandErr struct{}

func (e OperandErr) Error() string {
	return "not enough operands"
}