tally/main.go

183 lines
3.5 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"tildegit.org/sloum/tally/termios"
)
const (
Empty int = iota
Text
Number
Expr
)
var wb workbook
var reAddr *regexp.Regexp = regexp.MustCompile(`^[A-Z][0-9]+$`)
var reAddrRange *regexp.Regexp = regexp.MustCompile(`^[A-Z][0-9]+:[A-Z][0-9]+$`)
var modified bool = false
type point struct {
row int
col int
}
func runCommand(elems []string) {
if len(elems) == 0 {
return
}
switch elems[0] {
case "write", "w":
if len(elems) >= 2 {
path := ExpandedAbsFilepath(strings.Join(elems[1:], " "))
WriteFile(path)
} else {
path := wb.path
ext := filepath.Ext(path)
if ext != ".tss" {
path = path[:len(path)-len(ext)] + ".tss"
}
WriteFile(path)
}
modified = false
case "write-csv", "wc":
if len(elems) >= 2 {
path := ExpandedAbsFilepath(strings.Join(elems[1:], " "))
WriteCsv(path, false)
} else {
path := wb.path
ext := filepath.Ext(path)
if ext != ".csv" {
path = path[:len(path)-len(ext)] + ".csv"
}
WriteCsv(path, false)
}
case "write-tsv", "wt":
if len(elems) >= 2 {
path := ExpandedAbsFilepath(strings.Join(elems[1:], " "))
WriteCsv(path, true)
} else {
path := wb.path
ext := filepath.Ext(path)
if ext != ".tsv" {
path = path[:len(path)-len(ext)] + ".tsv"
}
WriteCsv(path, true)
}
case "trim", "t":
wb.sheets[wb.sheet].TrimSheet()
fmt.Print("\033[2J") // Clear the screen
case "recalculate", "r":
wb.sheets[wb.sheet].Recalculate()
default:
// TODO report error
panic("Invalid command")
}
}
func Getch() rune {
reader := bufio.NewReader(os.Stdin)
char, _, err := reader.ReadRune()
if err != nil {
return 0
}
return char
}
func GetLine(prefix string) (string, error) {
defer termios.SetCharMode()
termios.SetLineMode()
reader := bufio.NewReader(os.Stdin)
fmt.Print(prefix)
fmt.Print("\033[?25h")
text, err := reader.ReadString('\n')
if err != nil {
return "", err
}
fmt.Print("\033[?25l")
return text[:len(text)-1], nil
}
func DigitCount(num int) int {
counter := 0
for ; num > 0; num /= 10 {
counter++
}
return counter
}
func GetCell(p point) (cell, error) {
if p.row > wb.sheets[wb.sheet].rows || p.col > wb.sheets[wb.sheet].cols {
return cell{}, fmt.Errorf("Invalid reference")
}
return wb.sheets[wb.sheet].cells[p.row][p.col], nil
}
func Addr2Point(addr string) point {
p := point{}
p.col = int(addr[0]) - 65
p.row, _ = strconv.Atoi(addr[1:])
p.row--
return p
}
func Point2Addr(p point) string {
var s strings.Builder
s.WriteRune(rune(p.col) + 65)
s.WriteString(strconv.Itoa(p.row+1))
return s.String()
}
func ApplyPointDiff(p1, p2 point) point {
return point{row: p2.row - p1.row, col: p2.col - p1.col}
}
func IsAddr(addr string) bool {
return reAddr.MatchString(addr)
}
func IsRange(addr string) bool {
return reAddrRange.MatchString(addr)
}
func IsFunc(val string) bool {
switch strings.ToUpper(val) {
case "+", "-", "/", "*", "^", "MIN", "MAX", "SQRT",
"ROUND", "SUBSTR", "UPPER", "LOWER", "SUM":
return true
default:
return false
}
}
func main() {
args := os.Args
if len(args) > 1 {
path := ExpandedAbsFilepath(args[1])
ext := strings.ToLower(filepath.Ext(path))
switch ext {
case ".tss":
LoadFile(path)
case ".csv":
LoadCsv(path, false)
case ".tsv":
LoadCsv(path, true)
default:
fmt.Fprintf(os.Stderr, "Unknown file type %q", ext)
os.Exit(1)
}
wb.sheets[wb.sheet].Recalculate()
} else {
wb = makeWorkbook("blank.tss")
}
wb.Run()
}