A spreadsheet program for the terminal with concatenative expressions https://sloum.colorfield.space/docs/tally/
Go to file
sloum cfb76d0b9d Makes binary smaller when using makefile 2021-06-25 15:56:37 -07:00
qline Adds qline for editable input 2021-04-05 22:30:00 -07:00
termios Initial commit of mostly working spreadsheet program 2021-03-14 19:45:56 -07:00
.gitignore Initial commit of mostly working spreadsheet program 2021-03-14 19:45:56 -07:00
LICENSE Adds license 2021-03-14 19:59:47 -07:00
Makefile Makes binary smaller when using makefile 2021-06-25 15:56:37 -07:00
README.md Adds qline for editable input 2021-04-05 22:30:00 -07:00
TODO.md Adds todo list 2021-03-20 15:18:02 -07:00
cell.go Offsets cell data with a space char to prevent 'runtogether' 2021-04-06 13:59:20 -07:00
csvLoader.go Adds csv and tsv io as well as quoted string entry 2021-03-16 22:17:52 -07:00
files.go Adds csv and tsv io as well as quoted string entry 2021-03-16 22:17:52 -07:00
go.mod Adds go.mod 2021-03-18 14:10:15 -07:00
main.go Adds url opening 2021-05-08 14:53:33 -07:00
open_browser_darwin.go Fixes package naming issue from copied packages 2021-05-08 14:58:45 -07:00
open_browser_other.go Fixes package naming issue from copied packages 2021-05-08 14:58:45 -07:00
open_browser_windows.go Fixes package naming issue from copied packages 2021-05-08 14:58:45 -07:00
sheet.go Adds qline for editable input 2021-04-05 22:30:00 -07:00
tally.1 Adds url opening to the man page 2021-05-08 15:01:55 -07:00
tally.png Adds terminal resizing and a screen shot 2021-03-29 20:34:13 -07:00
workbook.go Adds url opening 2021-05-08 14:53:33 -07:00



This is a work in progress spreadsheet program with a semi-limited feature set. For fun, all expressions use postfix notation similar to Forth in style. It is made available under the terms of the Floodgap Free Software License (see the file 'LICENSE').

In general the use case for tally is small spreadsheets, notes, shopping lists, bills, little bits of relational data. It is not meant to do what Excel, or Calc, or Lotus123 do. It is meant to be a small and simple alternative for small and simple use cases.

screen shot of tally showing monthly expenses vs income

Building / Installing

You will need a Go compiler. Testing on eary versions has not been done, but suffice to say you should likely not need the latest cutting edge Go version to compile tally.

A makefile is forthcoming. Until then a simple go build or go install from within the repo directory should work fine.


tally uses its own file format, but can import most csv and tsv files (any that are in the format described by RFC 4180). At present most basic features are available with the exception of actions upon ranges (A1:C3 SUM, for example). These are hopefully coming soon.


To create a new spreadsheet, invoke tally without any arguments:


To open an existing file, pass a filepath. Here are some examples:

tally ~/my-tally-spreadsheet.tss
tally ~/my-cool-csv-file.csv
tally ~/my-awesome-tsv-file.tsv

To quit tally at any time press Q. If you have unsaved changes you will be asked if you are sure you want to quit without saving.


When starting a new sheet from scratch the workbook will have one sheet with one cell (A1). If you load an existing tss, csv, or tsv file then thenumber of rows and columns, along with their values, will be present in the sheet shown by tally.


To move around the sheet, use vi-like keys:

  • h, j, k, l, or arrow keys: Left, Down, Up, Right
  • ^, $, home, end: Move to First or Last column in current row
  • g, G, PageUp, PageDown: Move to First or Last row in current column


You can enter data into cells in a few different ways. Cells accept the following types of values:

  • number: Positive, negative, integer, or decimal. Internally tally thinks of all numbers as decimals
  • text: A string of characters. All strings must be surrounded by double quotes, "like so"
  • expression: Any combination of the above two types, as well as references to cell in the form of column then row: A2, for example.

To enter data into a cell you can press the enter or space key. If you are going to be entering text into the cell, there is a shortcut that allows you to enter the text without needing to quote it (the quoting will be done for you): press the " key.

If tally understood what you entered you will see a value. If it did not, or there is a problem with your expression, you will see #Err.

When referencing a cell in an expressions, such as A2, you may optionally "lock" the row or column while leaving the other to be used as a relative reference. To lock a column use the following syntax: $A2. To lock a row use the following syntax: A$2. You can lock both, $A$2, but it is likely easier to just do a non-relative paste using the p key (rather than P).

Deleting Data

To delete the contents of a cell, that is - to revert it to an empty state, press d, Backspace, or Delete

Yank / Paste

You can copy a cell with y. To paste it you have two options. The difference between these options is how they deal with cell references in expressions. Pressing p will paste the cell and will not change any references from when it was copied. Pressing P will update the cell references to be relative. In this case that means they will change based on the distance (in rows and columns) between where they were copied from and where they are being pasted. This allows for dynamic expressions that can be moved around a sheet conveniently. Cells can have their column or row reference locked. If locking is performed then a relative paste will respect that lock and keep the row or column that was locked as it was and the other will be handled relatively.

Cell Modifiers

You can change the appearance of a cell through the use of modifiers. All modifiers work as toggles, so turning on and off is done with the same key. The available modifiers are:

  • b: Bold
  • f: Faint/dim; if bold has been added to the cell then faint will have no effect
  • u: Underline
  • i: Italics


You can zoom in or out with the + and - keys. This has the effect of controlling how many columns you see on the screen at a given time. At present all columns have the same width.


In addition to the editing keys, there are a few commands that can be issued. To enter command mode press : and then enter your command.

The following commands are available:

  • write or w: Will write the current spreadsheet to a file. If no filepath is passed (write ~/my-file.tss) tally will save the current file, using a default name and the execution directory if necessary (ie. if tally was run without a file and no path has yet been passed to write)
  • write-csv or wc: Will write the current spreadsheet to a csv file. If no filepath is passed tally will save to the current file path, but with the file type csv
  • write-tsv or wt: Will write the current spreadsheet to a csv file. If no filepath is passed tally will save to the current file path, but with the file type tsv
  • 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
  • recalculate: Forces a recalculation of the entire sheet. This should generally not be needed, but is avialable should you encounter the need


Expressions in tally do not follow the Excel style expression methodology. Instead, expressions use a stack for execution and use postfix notation. This is similar to nimf, the concatenative language written by the author of tally.

Working with numbers and cell references

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.

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:


The above would round the numerical value at A1 to two decimal places.


The above would find the greater of the two values that come before MAX. MIN is also available.

You can combine cell references and hard coded numbers as well:

A1 2 * 10.5 MIN 1 ROUND

In the above, lets assume A1 is currently 3.125. If so, then A1 gets multiplied by 2, resulting in 6.25. Then the minimum value between 6.25 and 10.5 is found, which is 6.25. This then gets rounded to one decimal place for the final value of 6.3.

Working with text and cell references

Text is pretty easy to work with. No operators are required in most cases. Since tally splits expressions on whitespace, consecutive whitespace in strings should be entered as \\\_. So, a cell with the value "Hello" that is intended to be combined with another cell and result in a space in between may need to be written as "Hello\\\_".

To then combine them, you could use the following expression (assuming A2 to be something like "world"):

A1 A2

or since text can be entered into expression manually, the following would also work:

A1 "world"

Let's say you did not want to use \\\_ to create a space. You could use the SPC function, like so:


Assuming A1 to be "Hello" and A2 to be "world", this would create the cell value Hello world. Without SPC the words would be shoved together and read as Helloworld.

Combining text and numbers

To concatenate a numerical and text value, you can enter the values as if they were both strings, but it may not work out like you would think:

21.75 "Hello"

The above results in Hello21.75. If you want the 21.75 to appear before "Hello", you need to use a special function: .. This lets tally know that you would like the number on the top of the stack to be appended to the current string value and dropped from the stack . So the above could be rewritten as:

21.72 . SPC "Hello"

With the result being 21.72 Hello. Of course, either the number value or the text value could have been a cell reference, such as B25 of the like.

You may be asking yourself, why do we need .? The answer is that it allows you to have numbers before and after text and choose when to put numbers into the text value. This gives a fine grained control of both the number stack as well as the string buffer.

For a more complicated example, we could do something like the following:

23 5 + . "hello" 3 1 -

This produces the result: 28hello2. If you remove the . that comes after the plus, you would get the output: hello228. Now, that may seem strange. Why is that happening? It is happening for the same reason you saw a 2 at the end in the first result: at the end of the expression, the stack is flushed and added to the string from top of stack down. So since 2 was the last number on the stack it is the first off. Then 28 gets added to the end.

There are a few words that affect the stack, but do not necessarily modify the output in a transformative way like + or MIN do, for example. The next section will cover these. The important thing to take away from the above paragraph is that . can help you control where numbers go, and any numbers left on the stack at the end of an expression that involves text will be flushed to the text buffer (this is not true of purely numerical expressions, where the cell value is always just represented by the top of the stack).

Stack Manipulation

The number stack can be manipulated in a number of ways. You have seem mathematical operators and a few functions (MIN, ROUND, MAX, .). There are a few more options for dealing with numbers on the stack:

DUP will copy the number on the top of the stack and place the copy on top. So if the stack starts empty and you enter 2 DUP, the stack will contain 2 and then another 2.

DROP will remove the top value on the stack and throw it away.

CLEAR will wipe out all values from the stack. This, in theory, could be useful to prevent a stack flush at the end of a field containing numbers and text. However, be wary of doing so as it likely indicates that some refactoring could prevent the need to use it.

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..


tally can accept ranges of cells for operation. However, tally currently only supports summing the numerical values of those cells. The syntax is A1:B3+. The + at the end designates summing the range. Eventually there may be other range operations available. Locking rows and columns can be used in ranges.


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.

Some functions require certain things to be in place on the stack. All of the math operators, for example, require at least two numbers to be on the stack (the two will be replaced by the result of the operation). ., DROP, and DUP only require one item on the number stack. If the minimum number of items on the stack is not met, an error will be the result.

tally is intended to be a part of a suite of office/productivity applications written by tally's developer for personal use. Building your own tools, even when they lack some features of program x, is a rewarding experience and can be quite educational. It also can produce highly hackable and editable software for personal use. That having been said, if you get utility from any of these programs, tally included: awesome! Here are some of the other applications related to tally:

  • chalk - A line based text editor, a kind of less powerful but more friendly ed
  • hermes - A visual editor more akin to vi, based on kilo by antirez
  • bombadillo - An internet client supporting gopher, gemini, finger, file, telnet, and http(s)
  • nimf - A concatenative programming language/interpreter
  • lid - A lo-fi image ditherer

Forthcoming are drawing, calendar, slides, mail, and notes programs.