192 lines
3.5 KiB
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)
|
|
}
|