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 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 -d
Debug Mode
Prints a stepwise stack trace during evaluation.
5.3 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: