diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..99749f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +GOCMD := go +BINARY := tally +PREFIX := /usr/local +EXEC_PREFIX := ${PREFIX} +BINDIR := ${EXEC_PREFIX}/bin +DATAROOTDIR := ${PREFIX}/share +MANDIR := ${DATAROOTDIR}/man +MAN1DIR := ${MANDIR}/man1 + +.PHONY: build +build: + ${GOCMD} build -o ${BINARY} + +.PHONY: install +install: install-bin install-man clean + +.PHONY: install-man +install-man: tally.1 + gzip -k ./tally.1 + install -d ${DESTDIR}${MAN1DIR} + install -m 0644 ./tally.1.gz ${DESTDIR}${MAN1DIR} + +.PHONY: install-bin +install-bin: build + install -d ${DESTDIR}${BINDIR} + install -m 0755 ./${BINARY} ${DESTDIR}${BINDIR} + +.PHONY: clean +clean: + ${GOCMD} clean + rm -f ./tally.1.gz 2> /dev/null + rm -f ./${BINARY}_* 2> /dev/null + +.PHONY: uninstall +uninstall: clean + rm -f ${DESTDIR}${MAN1DIR}/tally.1.gz + rm -f ${DESTDIR}${BINDIR}/${BINARY} + diff --git a/README.md b/README.md index a6fc297..f6e1b8e 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ A1 A2 + In the above example the values of `A1` and `A2` get added together and the result is the value of the cell. Note that if either `A1` or `A2` is a text value, rather than a number, this will result in `#Err: Stack underflow. We will cover mixing text and numbers further into the examples. -Opperators exist for `+`, `-`, `\*`, and `/`. +Basic math operators exist for `+`, `-`, `\*`, and `/`. Additional operators that work with numbers are: `ROUND`, `MAX`, `MIN`, `FLOOR`, `CEIL`, and `POW`. There are also functions to do a few basic things: @@ -188,6 +188,8 @@ The number stack can be manipulated in a number of ways. You have seem mathemati `SWAP` will swap the position of the top two values on the stack. +`OVER` will copy the number underneath the top value on the stack and place the copy on the top of the stack without removing any numbers from the stack.. + #### Errors If there is a problem in your expression, the cell will report `#Err`, likely followed by a message about the nature of the problem. If you try to reference a stack value that doesn't exist, for example... or even a cell that does not exist. diff --git a/cell.go b/cell.go index 2bca1ac..de57034 100644 --- a/cell.go +++ b/cell.go @@ -191,6 +191,18 @@ func (c *cell) Calculate() { 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 { @@ -352,6 +364,18 @@ func (s *stack) Round() error { 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") @@ -363,3 +387,29 @@ func (s *stack) Swap() error { 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 +} + diff --git a/main.go b/main.go index d7f081d..b43812e 100644 --- a/main.go +++ b/main.go @@ -153,8 +153,10 @@ func IsRange(addr string) bool { func IsFunc(val string) bool { switch strings.ToUpper(val) { - case "+", "-", "/", "*", "^", "MIN", "MAX", "SQRT", - "ROUND", "SUM", "SPC", ".", "SWAP": + case "+", "-", "/", "*", "MIN", "MAX", + "ROUND", "SUM", "SPC", ".", "SWAP", + "DUP", "DROP", "CLEAR", "OVER", "POW", + "FLOOR", "CEIL": return true default: return false diff --git a/tally.1 b/tally.1 new file mode 100644 index 0000000..42d44c0 --- /dev/null +++ b/tally.1 @@ -0,0 +1,212 @@ +.TH "tally" 1 "18 MAR 2021" "" "General Operation Manual" +.SH NAME +\fBtally\fP- concatenative spreadsheet +.SH SYNOPSIS +.nf +.fam C +\fBtally\fP [\fIoptions\fP] [\fIfilepath\fP] +.fam T +.fi +.SH DESCRIPTION +\fBtally\fP is a spreadsheet application for the terminal that uses vi-like keybindings and concatenative expressions. +.TP +\fBtally\fP supports opening and writing its own format (\fI.tss\fP) as well as \fI.csv\fP and \fI.tsv\fP files. +.SH OPTIONS +.TP +.B +\fB-h\fP +Display usage help and exit. +.SH COMMANDS +.SS KEY COMMANDS +These commands work as a single keypress anytime \fBtally\fP is not taking in a line based command or when the user is being prompted for action. This is the default command mode of \fBtally\fP. +.TP +.B +h, j, k, l +Move the selection one cell left (h), down (j), up (k), or right (l). +.TP +.B +^ +Move the selection to the first column of the current row. +.TP +.B +$ +Move the selection to the last column of the current row. +.TP +.B +g +Move the selection to the frist row of the current column. +.TP +.B +G +Move the selection to the last row of the current column. +.TP +.B +Q +Quit \fBtally\fP. If there is unsaved work, will query as to whether quiting should still occur. +.TP +.B +, +Update/edit the value of the currently selected cell. +.TP +.B +" +Update/edit the value of the currently selected cell and wrap the input in quotes to produce a text value without having to type quotes every time. +.TP +.B +y +Yank/copy the currently selected cell to the copy buffer. +.TP +.B +p +Paste the value of the copy buffer to the currently selected cell. This will paste all cell references in expressions verbatim, with no relational adjusments. +.TP +.B +P +Paste the value of the copy buffer to the currently selected cell. This will paste all cell references in expressions as relative references and offset them based on where they were yanked from and where they are being pasted to. +.TP +.B ++ +Zoom in. This has the effect of displaying fewer columns on the screen, but making the one's that are shown wider. +.TP +.B +- +Zoom out. This has the effect of displaying more columns on the screen, but making the one's that are shown narrower. +.TP +.B +b +Make the value of the currently selected cell appear bold. +.TP +.B +f +Make the value of the currently selected cell appear faint/dim. If the cell has bold turned on, faint will be over-ridden by bold and no difference will be seen. +.TP +.B +i +Italicize the value of the currently selected cell. +.TP +.B +u +Underline the value of the currently selected cell. +.TP +.B +: +Enter command mode (will prompt for command). +.SS LINE COMMANDS +These commands are typed in by the user to perform an action of some sort. As listed in KEY COMMANDS, this mode is initiated by pressing \fI:\fP. +.TP +.B +write, w +Accepts an optional filepath, which can be relative. Will write the current spreadsheet to a tally file (.tss). If no filepath is provided, the current spreadsheet will be saved. If there is no current path a default filename will be used and the sheet will be saved to the current working directory. +.TP +.B +write-csv, wc +Accepts an optional filepath, which can be relative. Will write/export the current spreadsheet to a csv file. If no filepath is provided, the current spreadsheet will be saved. If there is no current path a default filename will be used and the sheet will be saved to the current working directory. When saving as csv do note that all expressions will be lost and only their values will be saved to the resulting csv file. +.TP +.B +write-tsv, wt +Accepts an optional filepath, which can be relative. Will write/export the current spreadsheet to a tsv file. If no filepath is provided, the current spreadsheet will be saved. If there is no current path a default filename will be used and the sheet will be saved to the current working directory. When saving as tsv do note that all expressions will be lost and only their values will be saved to the resulting tsv file. +.TP +.B +trim +Removes empty rows/columns at the end of the file. It does this recursively starting with the bottom-most row and right-most column until it encounters a row or column (each working independently) with data in it. This is a quick way to remove unneeded space from a spreadsheet, particularly if it is being exported as a csv or tsv. +.TP +.B +recalculate +Forces a recalculation of the entire sheet. This should generally not be needed, but is avialable should you encounter the need. +.SH Data Types +\fBtally\fP recognizes three types of cell data, discussed in this section. If \fBtally\fP understood what you entered into a given cell you will see a numerical or textual value, otherwise you will see \fI#Err\fP, likely followed by information about the error. +.TP +.B +numbers +Positive, negative, integer, or decimal. Internally tally thinks of all numbers as 64 bit floating point values. +.TP +.B +text +A string of characters. All strings must be surrounded by double quotes, \fI"like so"\fP. +.TP +.B +expressions +Any combination of numbers, text, operators, or references to cells in the form of column then row: \fIA2\fP, for example. Expressions are concatenative and operate on a stack. As a result you would not write \fI5 + 2\fP; you would write \fI5 2 +\fP and get the result \fI7\fP. Text does not get added to the stack like a number would and instead turns the expression into a textual expression. Textual expressions require no operator to concatenate text references (no \fI+\fP or the like is needed). Numbers still get calculated as normal in a textual expression, but must be flushed to the output buffer by using the \fI.\fP operator. Any numbers left on the stack at the end of a textual expression will get popped off the stack and concatenated to the end of the output text. Avoiding using cell references, this combination of numbers and text can be shown thus: \fI5 4 + "Hello" 7 5\fP would result in the output \fI"Hello579"\fP, while \fI5 4 + . "Hello" 7 . 5\fP would result in the output \fI"9Hello75"\fP. Hopefully that was illustrative enough to get the basics for how to combine text and numbers. +.SH Operators +The following is a list of the operators that \fBtally\fP recognizes, as well as a description of what they do. Operators, when used in expressions, are not case sensitive. +.TP +.B ++ +Add the top two numbers on the stack and replace them with the result. An error will result if there are not at least two numbers on the stack. +.TP +.B +- +Subtract the top number on the stack from the number beneath it on the stack and replace them with the result. An error will result if there are not at least two numbers on the stack. +.TP +.B +* +Multiply the top two numbers on the stack and replace them with the result. An error will result if there are not at least two numbers on the stack. +.TP +.B +/ +Divide the top two numbers on the stack, with top of stack being the divisor/denominator, and replace them with the result. An error will result if there are not at least two numbers on the stack. +.TP +.B +MIN +Compare the top two numbers on the stack and replace them with the smaller/lower of the two. An error will result if there are not at least two numbers on the stack. +.TP +.B +MAX +Compare the top two numbers on the stack and replace them with the larger/higher of the two. An error will result if there are not at least two numbers on the stack. +.TP +.B +ROUND +Rounds a number to a certain number of decimal places. The number on top of the stack is the number of decimal places to round to and the number below it is the number to round. Replaces both numbers with the rounded number. An error will result if there are not at least two numbers on the stack. +.TP +.B +POW +Raises the number underneath the top of the stack to the power of the number on the top of the stack. An error will result if there are not at least two numbers on the stack. +.TP +.B +FLOOR +Replaces the top value on the numerical stack with its floored value (ie. rounds down to the nearest whole number). An error will result if there is not at least one number on the stack. +.TP +.B +CEIL +Replaces the top value on the numerical stack with its ceilinged value (ie. rounds up to the nearest whole number). An error will result if there is not at least one number on the stack. +.TP +.B +SPC +Add a space to the text output buffer of an expression. If the expression is not a text expression (ie. it has only involved numbers to that point in the expression) it will be turned into one. +.TP +.B + . +Remove the number on top of the numerical stack and add its textual representation to the text output buffer. If the expression is not a text expression (ie. it has only involved numbers to that point in the expression) it will be turned into one. An error will result if there is not at least one number on the stack. +.TP +.B +SWAP +Swap the top value on the numerical stack with the number beneath it such that the number beneath it becomes top of stack and the previous top of stack is now below top of stack. An error will result if there are not at least two numbers on the stack. +.TP +.B +DUP +Make a copy of the top value on the numerical stack and add that copy to the top of the numerical stack. An error will result if there is not at least one number on the stack. +.TP +.B +OVER +Make a copy of the value below the top value on the numerical stack (the value underneath the top value) and add it to the top of the stack. An error will result if there are not at least two numbers on the stack. +.TP +.B +DROP +Remove the top value on the numerical stack and do nothing with it (throw it away). An error will result if there is not at least one number on the stack. +.TP +.B +CLEAR +Drop all values from the numerical stack. This would most often be used to ensure that no values will get flushed to the textual output buffer at the end of an expression. However, be wary of doing so as it likely indicates that some refactoring could prevent the need to use it in that way. +.SH BUGS +There are very likely bugs. This software was largely made for personal use and is being made available so that others can modify it to their needs if they would like. Feel free to fork and patch as you see fit (within the terms of the Floodgap Free Software License). +.SH LINKS +\fBtally\fP maintains a presence in the following locations: +.TP +.B +Source Code Repository +\fIhttps://tildegit.org/sloum/tally\fP +.SH AUTHORS +\fBtally\fP was developed by sloum +.SH LICENSE +\fBtally\fP is available under the terms of the Floodgap Free Software License: \fIhttps://www.floodgap.com/software/ffsl/license.html\fP + diff --git a/test.qsh b/test.qsh deleted file mode 100644 index 1766624..0000000 --- a/test.qsh +++ /dev/null @@ -1 +0,0 @@ -tssSheet12362325A1 A2 +"Total" \ No newline at end of file