Skip to content

Instantly share code, notes, and snippets.

@b4tman
Last active April 10, 2020 12:59
Show Gist options
  • Save b4tman/59f6debb388bf6fe98d3bccbd82b376d to your computer and use it in GitHub Desktop.
Save b4tman/59f6debb388bf6fe98d3bccbd82b376d to your computer and use it in GitHub Desktop.
Калькулятор для вычисления выражений со значениями указанными в форме времени. Например "(12:05 - 11:30) / 2".
# -*- coding: utf-8 -*-
"""
simple calculator for time
based on fourFn.py (pyparsing module examlple)
"""
from pyparsing import Literal,CaselessLiteral,Word,Combine,Optional,\
ZeroOrMore,Forward,nums,alphas
import sys, operator
exprStack = []
def pushFirst( strg, loc, toks ):
exprStack.append( toks[0] )
def pushUMinus( strg, loc, toks ):
if toks and toks[0]=='-':
exprStack.append( 'unary -' )
#~ exprStack.append( '-1' )
#~ exprStack.append( '*' )
bnf = None
def BNF():
"""
expop :: '^'
multop :: '*' | '/'
addop :: '+' | '-'
integer :: ['+' | '-'] '0'..'9'+
atom :: E | real | fn '(' expr ')' | '(' expr ')'
factor :: atom [ expop factor ]*
term :: factor [ multop factor ]*
expr :: term [ addop term ]*
"""
global bnf
if not bnf:
point = Literal( "." )
colon = Literal( ":" )
e = CaselessLiteral( "E" )
fnumber = Combine( Word( "+-"+nums, nums ) +
Optional( point + Optional( Word( nums ) ) ) +
Optional( e + Word( "+-"+nums, nums ) ) )
tnumber = Combine( fnumber + Optional( colon + Optional(fnumber + Optional( colon + Optional(fnumber) ) ) ) )
ident = Word(alphas, alphas+nums+":_$")
plus = Literal( "+" )
minus = Literal( "-" )
mult = Literal( "*" )
div = Literal( "/" )
lpar = Literal( "(" ).suppress()
rpar = Literal( ")" ).suppress()
addop = plus | minus
multop = mult | div
expop = Literal( "^" )
expr = Forward()
atom = (Optional("-") + ( tnumber | ident + lpar + expr + rpar ).setParseAction( pushFirst ) | ( lpar + expr.suppress() + rpar )).setParseAction(pushUMinus)
factor = Forward()
factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) )
term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) )
expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) )
bnf = expr
return bnf
opn = { "+" : operator.add,
"-" : operator.sub,
"*" : operator.mul,
"/" : operator.truediv,
"^" : operator.pow }
fn = { "abs" : abs,
"trunc" : lambda a: int(a),
"round" : round}
def evaluateStack( s ):
if 0 == len(s):
return 0
op = s.pop()
if 0 == len(op):
return 0
if op == 'unary -':
return -evaluateStack( s )
if op in "+-*/^":
op2 = evaluateStack( s )
op1 = evaluateStack( s )
return opn[op]( op1, op2 )
elif op in fn:
return fn[op]( evaluateStack( s ) )
elif op[0].isalpha():
return 0
else:
return float( sec_from_time(op) )
def evaluateString(s):
global exprStack
exprStack = []
results = BNF().parseString( s )
return evaluateStack( exprStack[:] )
def sec_from_time(time):
parts = time.split(':')
parts.reverse()
level = 1
ret = 0
for part in parts:
if len(part):
ret += float(part) * level
level *= 60
return ret
def time_from_sec(sec):
parts = []
sec_remain = float(sec)
while(sec_remain):
if 60 > sec_remain:
part = sec_remain
sec_remain = 0
else:
part = float(sec_remain % 60)
sec_remain = float(int(sec_remain/60))
parts.append(part)
parts.reverse()
return ':'.join(map(lambda p: '{:02.4n}'.format(p), parts))
def test( s, expVal ):
global exprStack
exprStack = []
results = BNF().parseString( s )
val = evaluateStack( exprStack[:] )
if val == expVal:
print ('== "{}" = {} // {} => {}'.format(s, time_from_sec(val), results, exprStack))
else:
print ('!!! "{}" = {} != {} // {} => {}'.format(s, time_from_sec(val), time_from_sec(expVal), results, exprStack))
if __name__ == "__main__":
if 1 == len(sys.argv):
test( "2:1 + 1:2 + 3: +2", 3+3*60 + 3*60+2 )
test( "15:21 - 13:04 + 16:25 - 16:18", 2*60+24 )
test( "0 - (13:04 - 15:21) - (16:18 - 16:25)", 2*60+24 )
test( "12.5:0: + 45:01", 13*60*60+15*60+2 )
else:
arguments = ' '.join(sys.argv[1:])
result = evaluateString(arguments)
print(time_from_sec(result))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment