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) }