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 }