Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
tcalc - a time calculator
- X-seq: zsh-workers 33210
- From: Florent Carpentier <fcarpentier@xxxxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: tcalc - a time calculator
- Date: Sat, 20 Sep 2014 19:27:08 +0200
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1411234029; bh=6hbeSmOV4LWo0uO7jB3T03NB5Hu2ejmsC3xJvpTU98U=; h=Received:Received:Received:DKIM-Signature:X-Yahoo-Newman-Id:X-Yahoo-Newman-Property:X-YMail-OSG:X-Yahoo-SMTP:Message-ID:Date:From:User-Agent:MIME-Version:To:Subject:Content-Type:From:Subject; b=CE1v/ptE5e16HEqGrGUHSY/ma5b/rm/17cvMMPV/yxUuqRnbmzWj5un0YtMQpWT9/nhb9iwNM5ODrM+yzZyV8zjyX71SIozTWP5AGUIuHqPWZSrBz5UqFYkY+00cZgu6nC+UxBimj9Ix9DUG/PY4XhAa91gINpcFnWVCIH7y1/StQBE870LBfPTVLVlaZSQJWcfISfoDzK6CUblqZKDHSbrVH+x6MYD3YDJARjMCO4QwRH5/dtgM+KmCs7dPnF2QGOUc8vHq49mJqWz5OgcU4l4grx8uAvUv4g323FyRzTIRREZmIHK/xe5PLII3M64L3ychO/3qrjOxxeDyfnKE0Q==
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s1024; t=1411234029; bh=6hbeSmOV4LWo0uO7jB3T03NB5Hu2ejmsC3xJvpTU98U=; h=X-Yahoo-Newman-Id:X-Yahoo-Newman-Property:X-YMail-OSG:X-Yahoo-SMTP:Message-ID:Date:From:User-Agent:MIME-Version:To:Subject:Content-Type; b=SPD4mJ3pLnluYtNsLLzhLv+20Gz4jn4oYzDvbpPVfG3d7XciJvZU6iKPBuoaKUnqVzrqT6JOK9YtuZWBmBmk4EaZsRyw7FAdb0YMeK4T7ABowllygAFatWubmrZsU1Qt2BQ/CHGpJcap0lNTg0rQAusP2hxuZG9UlaYe/D14ZOM=
- Domainkey-signature: a=rsa-sha1; q=dns; c=nofws; s=s2048; d=yahoo.com; b=AzCc6vI1vU3+cM5n57XNric2uxlcY0wozHVHIK0lKMsLEGt1TXw4sKipiKa02QGNxjwm8+SEaXPGWARgKQSuEm3bN8Qp0OFjNbNdEGcsPHmqq4c6X6jZaDzlEkuT7VY91uZHls5gQFp15QfuJkvQXuAsYpUK9OVcR9UqCuQx/knIMR7GjXlXjPJyyRxjo2O69gBgiel2KOrU51JUnIqmdYzL8MNvrdMBACh7qcflSs+EZu5hCz9l7sRbGkpfKv/JKtPCF5820teG4xAFkLh+9cwVJlN6TNbUg0eKbUX3PXtAw4AFHDGcjpGiGsrgahpGE+EvKVBem4E3+qJs6gmYQg==;
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
Hi all,
I have written a time calculator (attached) that works quite like zcalc
but handles times:
% 1 + 2 * 3
7
% 1:0 / 3
0:0:20.000
% 1:0 ** 2 / 2:0
0:0:30.000
If the code is correct and there is interest, someone (possibly me)
could try to integrate its features into zcalc. (tcalc does not have
configurable output formats, previous results reference, etc.)
The full code with tests is here: http://fccode.free.fr/tcalc/tcalc-0.tgz.
Best,
Florent
#! /bin/zsh -i
function help {
cat <<EOF
tcalc is a zsh \`time calculator': it understands and displays times in
hh:mm:ss.ddd format (when the dimension of the expression is a time).
Example session:
% 1 + 2 * 3
7
% 1:0 / 3
0:0:20.000
% 1:0 ** 2 / 2:0
0:0:30.000
EOF
}
function warn {
print $@ >&2
}
function is_time {
[[ $1 == *:* ]]
}
function maybe_to_s {
if is_time $1; then
to_s $1
else
print -- $1
fi
}
# to_s(t) prints the time t converted to seconds.
# t must be of the form [-][h...:][m]m:[s]s[.[d][d][d]].
function to_s {
local s="$1"
[[ "$s" == *.* ]] || s="$s.0" # make sure times are converted to floats
local w
w=( ${(s/:/)s} )
if [ $#w -eq 2 ]; then
print -- $(( $w[1] * 60 + $w[2] ))
elif [ $#w -eq 3 ]; then
print -- $(( $w[1] * 60 * 60 + $w[2] * 60 + $w[3] ))
else
warn "Failed to parse $1"
print 0
fi
}
# Prints the time in [-][h...]h:[m]m:[s]s.ddd format.
function print_time {
local t=$1
if (( t < 0 )); then
print -n -- -
(( t = -t ))
fi
(( dec_part = t - floor(t) ))
integer t # floor
d=$(( t / 3600 ))
print -n "$d:"
t=$(( t % 3600 ))
d=$(( t / 60 ))
print -n "$d:"
t=$(( t % 60 ))
print -- $t.${${dec_part#0.}[1,3]}
}
# print_res(x, dim) prints x (interpreted as a number of seconds) in time
# format if dim equals 1, as is otherwise.
function print_res {
if [[ $2 -eq 1 ]]; then
print_time "$1"
else
print -- "$1"
fi
}
function skip_init_spaces {
setopt re_match_pcre
[[ $1 =~ "^ *" ]]
print -- ${1[MEND+1,$#1]}
}
# matching_paren_pos(s) returns the index of the parenthesis that closes the
# opening one s starts with.
function matching_paren_pos {
local i k=0 # number of open parenthesis so far
for i in {1..$#}; do
if [[ $argv[i] == '(' ]]; then
(( k++ ))
elif [[ $argv[i] == ')' && $k -eq 1 ]]; then
print $i
return
elif [[ $argv[i] == ')' ]]; then
(( k-- ))
fi
done
}
# token(s, pat) prints the first characters of s that match pat, followed by
# the rest of s.
function token {
local tok="" rest="$1" pat="$2"
while [[ "$rest[1]" =~ "$pat" ]]; do
tok="${tok}$rest[1]"
rest="${rest#?}"
done
print -- $tok $rest
}
# lex(s) prints the lexemes of s followed by a space.
function lex {
local s="$1" tok
local digit_re="[0-9.:]"
local op_re="[-+*/]"
while [ "$s" != "" ]; do
s=$( skip_init_spaces "$s" )
local first_char="$s[1]"
if [[ "$first_char" =~ '\(|\)' ]]; then
tok="$first_char"
s="${s#?}"
elif [[ "$first_char" =~ $digit_re ]]; then
token "$s" $digit_re | read tok s
elif [[ "$first_char" =~ $op_re ]]; then
token "$s" $op_re | read tok s
else
tok=""
fi
if [[ "$tok" == "" ]]; then
warn "Illegal character: $first_char"
return 1
fi
print -n -- "$sep$tok"
local sep=" "
done
}
# Computes the dimension of the input expression: returns the power of time (0
# indicates it is dimension-less). Expects a valid expression.
# Roughly transforms expressions this way:
# -1:0 ** 2 * (2 - 3) / 2:0
# becomes
# 1 * 2 + (0, 0) - 1
# To reject operations that do not make sense dimensionwise, we would need an
# infix operator that would check the dimension is the same on both sides of
# additions and subtractions. We use ',' instead, assuming the dimension is the
# same.
function time_dimension {
local i expr
expr=()
# whether we are at the start of the whole expression or after an opening
# parenthesis (to detect unary '-')
local expr_beg=1
i=1
while (( i <= $#argv )); do
case $argv[i] in
(\() expr[$#expr+1]=$argv[i]; expr_beg=1;;
(\)) expr[$#expr+1]=$argv[i];;
(\*\*) expr[$#expr+1]='*'
[[ "$argv[i+1]" == '(' ]] &&
last=$(( i + $( matching_paren_pos $argv[i+1,-1] ) )) ||
last=$(( i + 1 ))
expr[$#expr+1]=$( eval_expr $argv[i+1,last] )
i=$last;;
(\*) expr[$#expr+1]='+';;
(/) expr[$#expr+1]='-';;
(+) expr[$#expr+1]=',';;
(-) (( expr_beg == 1 )) && expr[$#expr+1]='' || expr[$#expr+1]=',';;
(*) expr_beg=0
is_time $argv[i] && expr[$#expr+1]=1 || expr[$#expr+1]=0;;
esac
(( i++ ))
done
print $(( $expr ))
}
function eval_expr {
(( $# > 0 )) || return 1
local i tr_words
tr_words=()
for i in "$@"; do
tr_words[$#tr_words+1]=$( maybe_to_s $i )
done
# eval, otherwise an error in the expression exits the program
eval 'print $(( tr_words ))'
}
function read_line { vared -chep '%% ' $1; }
while getopts "ht" swt; do
case $swt in
(h) help; return 0;;
(t) function read_line { read $1; };;
(*) help; return 2;;
esac
done
if (( $# >= $OPTIND )); then
print "Unrecognised arguments: $@[OPTIND,-1]" >&2
help
return 2
fi
zmodload zsh/mathfunc
history -ap "${ZDOTDIR:-$HOME}/.tcalc_history"
while read_line l; do
print -s -- "$l"
words=$( lex $l ) || continue
res=$( eval_expr $=words ) || continue
dim=$( time_dimension $=words )
print_res $res $dim
l=
done
Messages sorted by:
Reverse Date,
Date,
Thread,
Author