454 lines
8.3 KiB
Go
454 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
Bold int = 1
|
|
Faint int = 2
|
|
Italic int = 3
|
|
Underline int = 4
|
|
)
|
|
|
|
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 := c.Mods()
|
|
if selected {
|
|
mods += "\033[7m"
|
|
}
|
|
format := "%s %*.*s \033[0m"
|
|
if c.kind == Text {
|
|
format = "%s %-*.*s \033[0m"
|
|
}
|
|
return fmt.Sprintf(format, mods, width-2, width-2, c.mask)
|
|
}
|
|
|
|
func (c cell) Mods() string {
|
|
var o strings.Builder
|
|
for _, mod := range c.mods {
|
|
o.WriteString(fmt.Sprintf("\033[%dm", mod))
|
|
}
|
|
return o.String()
|
|
}
|
|
|
|
func (c *cell) ToggleMod(mod int) {
|
|
if mod > 4 || mod < 1 {
|
|
return
|
|
}
|
|
present := false
|
|
for i, m := range c.mods {
|
|
if m == mod {
|
|
if i != len(c.mods)-1 {
|
|
c.mods[i] = c.mods[len(c.mods)-1]
|
|
}
|
|
c.mods = c.mods[:len(c.mods)-1]
|
|
present = true
|
|
break
|
|
}
|
|
}
|
|
if !present {
|
|
c.mods = append(c.mods, mod)
|
|
}
|
|
}
|
|
|
|
func (c *cell) Edit(row, col, width int, text bool) {
|
|
line := GetEditLine(fmt.Sprintf("%c%d > ", col+64, row), c.rawVal, width)
|
|
if len(line) == 0 {
|
|
return
|
|
}
|
|
fmt.Print("\033[?25l")
|
|
line = strings.TrimSpace(line)
|
|
if text && (!strings.HasPrefix(line, "\"") && !strings.HasSuffix(line, "\"")){
|
|
line = fmt.Sprintf("\"%s\"", line)
|
|
}
|
|
updated := c.Update(line)
|
|
if updated {
|
|
modified = true
|
|
}
|
|
}
|
|
|
|
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.rawVal = strings.Replace(c.rawVal, " ", "\\_", -1)
|
|
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
|
|
}
|
|
insideString := false
|
|
s := makeStack()
|
|
|
|
for _, v := range c.expr {
|
|
var stackError error = nil
|
|
if IsAddr(v) {
|
|
pt, _, _ := Addr2Point(v)
|
|
c, err := GetCell(pt)
|
|
if err != nil {
|
|
c.mask = "#Err: Invalid cell reference " + v
|
|
return
|
|
}
|
|
if c.kind == Text {
|
|
v = c.rawVal
|
|
} else if c.kind == Empty {
|
|
break
|
|
} else {
|
|
v = c.mask
|
|
}
|
|
} else if IsRange(v) {
|
|
stackError = s.Sum(v)
|
|
goto ERRCHECK
|
|
}
|
|
|
|
switch true {
|
|
case strings.HasPrefix(v, "\""):
|
|
if !strings.HasSuffix(v, "\"") {
|
|
insideString = true
|
|
}
|
|
s.str.WriteString(strings.Replace(v, "\"", "", -1))
|
|
s.text = true
|
|
case insideString:
|
|
if strings.HasSuffix(v, "\"") {
|
|
insideString = false
|
|
v = strings.Replace(v, "\"", "", -1)
|
|
}
|
|
s.str.WriteRune(' ')
|
|
s.str.WriteString(v)
|
|
case IsFunc(v):
|
|
v = strings.ToUpper(v)
|
|
if v == "SPC" {
|
|
s.str.WriteRune(' ')
|
|
s.text = true
|
|
}
|
|
if v == "+" {
|
|
stackError = s.Add()
|
|
}
|
|
if v == "-" {
|
|
stackError = s.Subtract()
|
|
}
|
|
if v == "/" {
|
|
stackError = s.Divide()
|
|
}
|
|
if v == "*" {
|
|
stackError = s.Multiply()
|
|
}
|
|
if v == "." {
|
|
if s.Len() < 1 {
|
|
stackError = fmt.Errorf("Stack underflow")
|
|
}
|
|
num := s.Pop()
|
|
s.str.WriteString(strconv.FormatFloat(num, 'f', -1, 64))
|
|
s.text = true
|
|
}
|
|
if v == "MIN" {
|
|
stackError = s.Min()
|
|
}
|
|
if v == "MAX" {
|
|
stackError = s.Max()
|
|
}
|
|
if v == "ROUND" {
|
|
stackError = s.Round()
|
|
}
|
|
if v == "SWAP" {
|
|
stackError = s.Swap()
|
|
}
|
|
if v == "DUP" {
|
|
stackError = s.Dup()
|
|
}
|
|
if v == "CLEAR" {
|
|
s.ClearStack()
|
|
}
|
|
if v == "DROP" {
|
|
stackError = s.Drop()
|
|
}
|
|
if v == "OVER" {
|
|
stackError = s.Over()
|
|
}
|
|
if v == "POW" {
|
|
stackError = s.Pow()
|
|
}
|
|
if v == "FLOOR" {
|
|
stackError = s.Floor()
|
|
}
|
|
if v == "CEIL" {
|
|
stackError = s.Ceil()
|
|
}
|
|
default:
|
|
v, err := strconv.ParseFloat(v, 64)
|
|
if err != nil {
|
|
c.mask = "#Err: Cannot parse float"
|
|
return
|
|
}
|
|
s.Push(v)
|
|
}
|
|
|
|
ERRCHECK: if stackError != nil {
|
|
c.mask = fmt.Sprintf("#ERR: %s", stackError.Error())
|
|
return
|
|
}
|
|
}
|
|
|
|
if s.text {
|
|
if s.ptr != -1 {
|
|
for s.Len() > 0 {
|
|
num := s.Pop()
|
|
s.str.WriteString(strconv.FormatFloat(num, 'f', -1, 64))
|
|
}
|
|
}
|
|
c.mask = strings.Replace(s.str.String(), "\\_", " ", -1)
|
|
} else if s.ptr == 0 {
|
|
c.mask = strconv.FormatFloat(s.data[0], 'f', -1, 64)
|
|
} else if s.ptr == -1 {
|
|
c.mask = ""
|
|
} else {
|
|
c.mask = "#Err: Invalid Formula"
|
|
}
|
|
}
|
|
|
|
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) Len() int {
|
|
return s.ptr + 1
|
|
}
|
|
|
|
func (s *stack) Dup() error {
|
|
if s.Len() < 1 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
val := s.Pop()
|
|
s.Push(val)
|
|
s.Push(val)
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Drop() error {
|
|
if s.Len() < 1 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
s.Pop()
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) ClearStack() {
|
|
s.ptr = -1
|
|
}
|
|
|
|
func (s *stack) Add() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
if s.text {
|
|
val := strconv.FormatFloat(s.Pop(), 'f', -1, 64)
|
|
s.str.WriteString(val)
|
|
} else {
|
|
s.Push(s.Pop() + s.Pop())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Subtract() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
second := s.Pop()
|
|
s.Push(s.Pop() - second)
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Multiply() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
s.Push(s.Pop() * s.Pop())
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Divide() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
second := s.Pop()
|
|
s.Push(s.Pop() / second)
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Min() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
one := s.Pop()
|
|
two := s.Pop()
|
|
if one < two {
|
|
s.Push(one)
|
|
} else {
|
|
s.Push(two)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Max() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
one := s.Pop()
|
|
two := s.Pop()
|
|
if one > two {
|
|
s.Push(one)
|
|
} else {
|
|
s.Push(two)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Round() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
decimals := s.Pop()
|
|
num := s.Pop()
|
|
s.Push(math.Round(num*math.Pow(10, decimals)) / math.Pow(10, decimals))
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Over() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
tos := s.Pop()
|
|
cpy := s.Pop()
|
|
s.Push(cpy)
|
|
s.Push(tos)
|
|
s.Push(cpy)
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Swap() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
tos := s.Pop()
|
|
newTos := s.Pop()
|
|
s.Push(tos)
|
|
s.Push(newTos)
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Pow() error {
|
|
if s.Len() < 2 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
exp := s.Pop()
|
|
base := s.Pop()
|
|
s.Push(math.Pow(base, exp))
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Floor() error {
|
|
if s.Len() < 1 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
s.Push(math.Floor(s.Pop()))
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Ceil() error {
|
|
if s.Len() < 1 {
|
|
return fmt.Errorf("Stack underflow")
|
|
}
|
|
s.Push(math.Ceil(s.Pop()))
|
|
return nil
|
|
}
|
|
|
|
func (s *stack) Sum(rng string) error {
|
|
points := strings.Split(rng[:len(rng)-1], ":")
|
|
pointA, _, _ := Addr2Point(points[0])
|
|
pointB, _, _ := Addr2Point(points[1])
|
|
rowStart := pointA.row
|
|
rowEnd := pointB.row
|
|
colStart := pointA.col
|
|
colEnd := pointB.col
|
|
if rowEnd < rowStart || colEnd < colStart {
|
|
return fmt.Errorf("Range starts after range end")
|
|
}
|
|
if colEnd > wb.sheets[wb.sheet].cols-1 || rowEnd > wb.sheets[wb.sheet].rows-1 || colStart < 0 || rowStart < 0 {
|
|
return fmt.Errorf("Range out of bounds")
|
|
}
|
|
|
|
var sum float64 = 0.0
|
|
var val float64
|
|
var c cell
|
|
var err error
|
|
cells := wb.sheets[wb.sheet].cells
|
|
|
|
for row := rowStart; row <= rowEnd; row++ {
|
|
for col := colStart; col <= colEnd; col++ {
|
|
c = cells[row][col]
|
|
val = c.num
|
|
if c.kind == Expr {
|
|
val, err = strconv.ParseFloat(c.mask, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
}
|
|
sum += val
|
|
}
|
|
}
|
|
s.Push(sum)
|
|
return nil
|
|
}
|
|
|