tot — command-line postfix calculator

1 Description and Rationale

tot, v. trans. To sum up; to calculate.

tot is a command-line calculator. Unlike bc, it operates on an expression given on the command line: you don't have to enter and exit.

$ tot 3 4 x 3 +
15
$

Unlike expr, it supports infinite-precision integers and floating-point numbers.

$  tot 2 256 pow dup float
1.1579209e+77
115792089237316195423570985008687907853269984665640564039457584007913129639936

Unlike dc, it makes it possible to express the most common operations without any need for shell quoting or output editing.

$ dc --expression='2 256 ^ p'
115792089237316195423570985008687907853269984665640564039457584007913\
129639936

tot uses postfix, which makes it very easy to concatenate expressions together. Unlike most Forths, it supports bignums and has an unlimited stack.

tot can be used in the middle of a pipeline and can read data from standard input.

$ echo 256 | tot in num 16 /
16

tot has vectors, and provides higher-order functionals that can operate on them.

$ seq 1 6 | tot in nums product
720
$ seq 1 6 | tot in nums 1 quote x fold
720

tot's stack can hold files and strings mixed with numbers.

$ seq 1 25 | tot in nums product string "25! =" 2 pack sp join
25! = 15511210043330985984000000
$ tot file /usr/share/dict/words len
1145922

tot is not a complete programming language, but you can define functions in a Forth-like syntax.

$ tot : double 2 x end 4 double
8

2 Philosophy

2.1 Non-Interactive

2.2 Minimal Shell Quoting

I want tot expressions to require minimal shell quoting. So, most words are spelled without any shell metacharacters. When a metacharacter makes an obviously good word-name, I usually define an "uglier" synonym so that quoting can be avoided. Examples:

  Obviously Good Ugly Synonym
  ** pow
  ^ pow
  ' quote
  " string
  * x
  < lt
  <= lte
  > gt
  >= gte
  <> not
  [ vec

2.3 Postfix Notation

I believe that, for a calculator, postfix is inherently superior to infix, but additionally, parentheses are shell metacharacters, so with infix notation most expressions require shell quoting; see above.

2.4 Clean Output

Many calculators print results in a way that sometimes requires neatening-up. dc prints big numbers with backslashes, some calculators print prompts, insert leading whitespace, etc.

tot just prints the stack, one element per line (the -1 option only prints the top element). tot has multiple data types, and my approach is to print them cleanly i.e. ambiguously, with no syntax or delimiters to disambiguate them.

3 Download

Source code: tot-latest.tar.gz

tot is also available from my Arch Linux package repository; add this to /etc/pacman.conf:

[kw]
SigLevel = Optional TrustAll
Server = http://www.lib.uchicago.edu/keith/pkgrepo/kw/$arch

and then do:

pacman -Sy tot

4 Functions

tot currently has 129 built-in functions.

" WORD ( – x ) string literal PRIMITIVE
% ( x – x ) percentage 100 / *
( WORD ( – ) comment PRIMITIVE
* ( x x – x ) multiplication PRIMITIVE
** ( x x – x ) power PRIMITIVE
+ ( x x – x ) addition PRIMITIVE
- ( x x – x ) subtraction PRIMITIVE
/mod ( x x – x x ) remainder and quotient over over f/ rot rot mod
0= ( x – x ) equal to 0 0 =
1+ ( x – x ) add 1 to top of stack PRIMITIVE
1- ( x – x ) subtract 1 from top of stack PRIMITIVE
\: WORD ( – ) define a function PRIMITIVE
:: WORD ( – x ) lambda expression PRIMITIVE
< ( x x – x ) less than PRIMITIVE
<= ( x x – x ) less than or equal PRIMITIVE
<> ( x – x ) not equal = 0 =
= ( x x – x ) equal PRIMITIVE
> ( x x – x ) greater than PRIMITIVE
>= ( x x – x ) greater than or equal PRIMITIVE
@ WORD ( – x ) filename PRIMITIVE
EiB ( x – x ) exbibyte 2 60 pow *
GiB ( x – x ) gibibyte 2 30 pow *
KiB ( x – x ) kibibyte 2 10 pow *
MiB ( x – x ) mebibyte 2 20 pow *
PiB ( x – x ) pebibyte 2 50 pow *
TiB ( x – x ) tebibyte 2 40 pow *
YiB ( x – x ) yobibyte 2 80 pow *
ZiB ( x – x ) zebibyte 2 70 pow *
[ WORD ( – x ) numeric vector literal PRIMITIVE
^ ( x x – x ) power PRIMITIVE
abs ( x – x ) absolute value PRIMITIVE
acos ( x – x ) arccosine of x1 radians PRIMITIVE
apply ( x – ) apply function PRIMITIVE
asin ( x – x ) arcsine of x1 radians PRIMITIVE
atan ( x – x ) arctangent of x1 radians PRIMITIVE
c/ ( x x – x ) ceiling division PRIMITIVE
ceiling ( x – x ) ceiling PRIMITIVE
commas ( x – x ) add commas to x1, converting to string PRIMITIVE
constant WORD ( x – ) define a constant PRIMITIVE
cos ( x – x ) cosine of x1 radians PRIMITIVE
days ( x – x ) convert seconds to days 60 60 * 24 * /mod
degrees ( x – x ) convert x1 degrees to radians PRIMITIVE
depth ( – x ) push depth of stack on top of stack PRIMITIVE
drop ( x – ) drop top of stack PRIMITIVE
dup ( x – x x ) duplicate top of stack PRIMITIVE
f/ ( x x – x ) floored division PRIMITIVE
file WORD ( – x ) filename PRIMITIVE
filename ( x – x ) convert string to filename PRIMITIVE
filter ( x x – x ) filter functional PRIMITIVE
float ( x – x ) convert x1 to floating point PRIMITIVE
floor ( x – x ) floor PRIMITIVE
fold ( x x x – x ) fold functional PRIMITIVE
fold1 ( x – x ) fold with init value from vec over hd swap fold
gt ( x x – x ) greater than PRIMITIVE
gte ( x x – x ) greater than or equal PRIMITIVE
hd ( x – x ) replace vec with its first element (car) PRIMITIVE
hms ( x – x ) format hours minutes seconds 3 pack string : join
hours ( x – x ) convert seconds to hours 60 60 * /mod
id ( – ) identity function PRIMITIVE
if ( x x x – … ) conditional PRIMITIVE
in ( – … ) push stdin lines, number of lines on top PRIMITIVE
include WORD ( – ) include a source file PRIMITIVE
join ( x x – x ) join vec elements into a STRING, elements separated by x1 PRIMITIVE
lambda WORD ( – x ) lambda expression PRIMITIVE
len ( x – x ) length of string, vec or file PRIMITIVE
lines ( x – … ) push file lines PRIMITIVE
log ( x – x ) natural logarithm of x1 PRIMITIVE
log10 ( x – x ) logarithm base 10 of x1 PRIMITIVE
logand ( x x – x ) bitwise logical and PRIMITIVE
lognot ( x – x ) bitwise logical negation PRIMITIVE
logor ( x x – x ) bitwise logical or PRIMITIVE
logxor ( x x – x ) bitwise logical xor PRIMITIVE
lsh ( x x – x ) left shift PRIMITIVE
lt ( x x – x ) less than PRIMITIVE
lte ( x x – x ) less than or equal PRIMITIVE
map ( x x – x ) map functional PRIMITIVE
max ( x – x ) maximum PRIMITIVE
min ( x – x ) minimum PRIMITIVE
minutes ( x – x ) convert seconds to minutes 60 /mod
mod ( x x – x ) remainder (modulus) PRIMITIVE
ne ( x – x ) not equal = 0 =
neg ( x – x ) unary negation PRIMITIVE
nl ( – x ) newline PRIMITIVE
not ( x – x ) not 0 =
nth ( x x – x ) replace vec with its nth (0-based) element PRIMITIVE
num ( x – x ) convert string to number PRIMITIVE
nums ( x – x ) convert vec of strings to numbers quote num map
over ( x x – x x x ) place a copy of x1 on top of the stack PRIMITIVE
pack ( x – x ) pack the top x1 items on the stack into a vec PRIMITIVE
pi ( – x ) pi PRIMITIVE
pow ( x x – x ) power PRIMITIVE
product ( x – x ) product of top x1 stack elements 1 quote * fold
quote WORD ( – x ) quote a function PRIMITIVE
read ( x – x ) read file PRIMITIVE
rem ( x x – x ) remainder (modulus) PRIMITIVE
rev ( x – x ) reverse a vec PRIMITIVE
root ( x – x ) base x2 root of x1 PRIMITIVE
rot ( x x x – x x x ) rotate top three stack elements PRIMITIVE
rsh ( x x – x ) right shift PRIMITIVE
sin ( x – x ) sine of x1 radians PRIMITIVE
sp ( – x ) space PRIMITIVE
split ( x x – x ) split string PRIMITIVE
sqrt ( x – x ) square root PRIMITIVE
stdin ( – x ) standard input PRIMITIVE
string WORD ( – x ) string literal PRIMITIVE
sum ( x – x ) sum of top x1 stack elements 0 quote + fold
swap ( x x – x x ) swap top two stack elements PRIMITIVE
tab ( – x ) tab PRIMITIVE
tan ( x – x ) tangent of x1 radians PRIMITIVE
tl ( x – x ) replace vec with its tail (cdr) PRIMITIVE
unpack ( x – … ) unpack a vec onto the stack PRIMITIVE
vec WORD ( – x ) numeric vector literal PRIMITIVE
weeks ( x – x ) convert seconds to weeks 60 60 * 24 * 7 * /mod
x ( x x – x ) multiplication PRIMITIVE
       

5 Blog

5.1 <2014-10-07 Tue> Regular Expressions, Fields, More Relational Polymorphism, Unambiguous Debugging

tot now has simple Posix regular expression matching via four new words:

imatch ( s r – b ) does regex r match string s (case-insensitively)? PRIMITIVE
imatches ( s r – v ) extract substrings for regex r from string s (case-insensitively) PRIMITIVE
match ( s r – b ) does regex r match string s? PRIMITIVE
matches ( s r – v ) extract substrings for regex r from string s PRIMITIVE

Matching example:

$ (seq 4; echo foo; echo bar)
1
2
3
4
foo
bar
$ (seq 4; echo foo; echo bar) | tot in :: string '^[0-9]+$' match end filter
1       2       3       4
$ (seq 4; echo foo; echo bar) | tot in :: string '^[0-9]+$' match not end filter
foo     bar
: jfcl;

Parenthesized substrings are extracted by matches (and imatches) and result in a vector of strings. If the match succeeds, the vector is of length m+1 where m is the number of parenthesized substrings; the first element of the vector is the entire matching text. If the match fails, the vector is empty (which can be matched with the new word null:

null ( v – b ) is vector v empty? PRIMITIVE

Example:

$ echo foo: 56 bar: 72 | tot in hd string 'foo: ([0-9]+) bar: ([0-9]+)' matches unpack
foo: 56 bar: 72
56
72
$

The new word field provides simple subfield extraction from lines:

field ( x i s – n ) extract field i from s-separated string x rot swap split swap nth
$ tot @ /etc/passwd lines :: 0 string : field end map :: len 4 lte end filter unpack
root
bin
mail
ftp
http
dbus
ntp
exim
ldap
mpd
git
rpc
: jfcl;

The relational operators lt, lte, gt, gte, ne (and their synonyms) now work on strings as well as numbers.

In debug mode (-d), stack values are now displayed unambiguously.

$ tot 12345 string 12345 @ 12345 vec 12345 end
12345
12345
12345
12345
$ tot -v 12345 string 12345 @ 12345 vec 12345 end
VEC     12345
FILE    12345
STRING  12345
NUM     12345
$ tot -d 12345 string 12345 @ 12345 vec 12345 end
12345
12345
12345
12345
[0 EMPTY]  | 12345 string 12345 @ 12345 vec 12345 end
[1 NUM] 12345 | string 12345 @ 12345 vec 12345 end
[2 STRING] 12345 "12345" | @ 12345 vec 12345 end
[3 FILE] 12345 "12345" <12345> | vec 12345 end
[4 VEC] 12345 "12345" <12345> [12345] |
: jfcl;

Strings are displayed within double quotes, with "non-printable" values escaped (as for OCaml):

$ tot -d string "1 2\"3

4 ^E" > /dev/null
[0 EMPTY]  | string 1 2"3

4
[1 STRING] "1 2\"3\n\n4 \005" |
: jfcl;

Filenames are displayed within angle brackets and vectors within square brackets.

In addition, the debugger now displays the type of the top item on the stack after the stack depth.

5.2 <2014-07-14 Mon> -d Debug Mode

Prints a stepwise stack trace during evaluation.

5.3 <2013-07-12 Fri> Lambda Expressions

tot now has lambda expressions for use with the functionals. The immediate word lambda (synonym: ::) is just like : except it doesn't take a name.

$ tot file /etc/passwd lines :: string : split hd end map unpack
root
bin
daemon
mail
ftp
http
uuidd
dbus
nobody
keith
ntp
avahi
exim
ldap
mpd
git
polkitd
$

6 Publishing Options   skip

Local Variables: mode: org org-confirm-babel-evaluate: nil End:

Author: Keith Waclena

Email: keith

Created: 2016-11-03 Thu 15:20

Emacs 25.1.2 (Org mode 8.2.10)

Validate