tally/cell.go

192 lines
3.5 KiB
Go

package main
import (
"fmt"
"strconv"
"strings"
)
type cell struct {
kind int // Enum for the type of cell
rawVal string // A raw string of the value
num float64 // A quick reference field for the current value
expr []string // Fields representing an expression
mask string // What the user sees
mods []int // Enums of modifiers on the cell
}
func (c cell) String(width int, selected bool) string {
mods := ""
if selected {
mods += "\033[7m"
}
return fmt.Sprintf("%s%*.*s\033[0m", mods, width, width, c.mask)
}
func (c *cell) Edit(row, col int) {
line, err := GetLine(fmt.Sprintf("\033[2KUpdate %c%d: \033[?25h", col+64, row))
if err != nil {
panic(err)
}
fmt.Print("\033[?25l")
line = strings.TrimSpace(line)
c.Update(line)
}
func (c *cell) Update(val string) bool {
if val == c.rawVal || len(val) == 0 {
// If nothing was changed or the change is empty just return
return false
}
c.rawVal = val
num, err := strconv.ParseFloat(val, 64)
if err == nil {
c.kind = Number
c.num = num
c.mask = strconv.FormatFloat(num, 'f', -1, 64)
} else if strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"") {
c.kind = Text
c.mask = strings.Replace(val[1:len(val)-1], "\\_", " ", -1)
} else {
c.kind = Expr
c.expr = strings.Fields(val)
}
return true
}
func (c *cell) Calculate() {
if c.kind != Expr {
return
}
if len(c.expr) < 2 {
c.mask = "#Err"
return
}
insideString := false
s := makeStack()
for _, v := range c.expr {
if IsAddr(v) {
c, err := GetCell(Addr2Point(v))
if err != nil {
c.mask = "#Err"
return
}
if c.kind == Text {
v = c.rawVal
} else {
v = c.mask
}
} else if IsRange(v) {
// TODO Apply range
}
if s.ptr >= 0 && s.text {
c.mask = "#Err"
return
}
switch true {
case strings.HasPrefix(v, "\""):
if s.ptr >= 0 {
c.mask = "#Err"
return
}
if !strings.HasSuffix(v, "\"") {
insideString = true
}
s.str.WriteString(strings.Replace(v, "\"", "", -1))
s.text = true
case insideString:
if s.ptr >= 0 {
c.mask = "#Err"
return
}
if strings.HasSuffix(v, "\"") {
insideString = false
v = strings.Replace(v, "\"", "", -1)
}
s.str.WriteRune(' ')
s.str.WriteString(v)
case IsFunc(v):
if s.text {
c.mask = "#Err"
return
} else if v == "+" {
s.Add()
} else if v == "-" {
s.Subtract()
} else if v == "/" {
s.Divide()
} else if v == "*" {
s.Multiply()
}
default:
v, err := strconv.ParseFloat(v, 64)
if err != nil {
c.mask = "#Err"
return
}
s.Push(v)
}
}
if s.text {
c.mask = strings.Replace(s.str.String(), "\\_", " ", -1)
} else if s.ptr == 0 {
c.mask = strconv.FormatFloat(s.data[0], 'f', -1, 64)
} else {
c.mask = "Err"
}
}
type stack struct {
data [100]float64
ptr int
text bool
str strings.Builder
}
func makeStack() stack {
return stack{data: [100]float64{}, ptr: -1, text: false}
}
func (s *stack) Push(v float64) {
if s.ptr >= 99 {
panic("Stack overflow")
}
s.ptr++
s.data[s.ptr] = v
}
func (s *stack) Pop() float64 {
if s.ptr < 0 {
panic("Stack underflow")
}
s.ptr--
return s.data[s.ptr+1]
}
func (s *stack) Add() {
if s.text {
val := strconv.FormatFloat(s.Pop(), 'f', -1, 64)
s.str.WriteString(val)
} else {
s.Push(s.Pop() + s.Pop())
}
}
func (s *stack) Subtract() {
second := s.Pop()
s.Push(s.Pop() - second)
}
func (s *stack) Multiply() {
s.Push(s.Pop() * s.Pop())
}
func (s *stack) Divide() {
second := s.Pop()
s.Push(s.Pop() / second)
}