Last active
September 22, 2019 18:46
-
-
Save jul/619bb89fc984d7ea8cf4af59df353c79 to your computer and use it in GitHub Desktop.
une calculatrice qui parle (si espeak est installé)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#/!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
Perversion | |
""" | |
from collections import MutableMapping, Counter, MutableSequence | |
from json import * | |
from functools import reduce | |
#sum = lambda *alot: reduce(lambda x, y: x+y, alot) | |
MARKER=object() | |
RECURSOR=object() | |
STOPER=object() | |
class Path(tuple): | |
def endswith( self, *a_tuple ): | |
""" | |
>>> p = Path( [ 'a', 'b', 'c' ] ) | |
>>> p.endswith( 'b', 'c' ) | |
>>> True | |
""" | |
return self[len(self) - len(a_tuple) : ] == a_tuple | |
def startswith( self, *a_tuple ): | |
""" | |
>>> p = Path( [ 'a', 'b', 'c', 'd' ] ) | |
>>> p.startswith( 'a', 'b' ) | |
>>> True | |
""" | |
return self[: len( a_tuple ) ] == a_tuple | |
def _contains( self, a_tuple, _from = 0, follow = 0): | |
if len( a_tuple) == follow: | |
return True | |
index = False | |
here = self[ _from:] | |
try: | |
index = here.index(a_tuple[follow] ) | |
return self._contains( | |
a_tuple, | |
index + 1 , | |
follow + 1 | |
) | |
except ValueError: | |
return False | |
return False | |
def contains(self, *a_tuple ): | |
""" | |
>>> p = Path( [ 'a', 'b', 'c', 'd' ] ) | |
>>> p.contains( 'b', 'c' ) | |
>>> True | |
""" | |
return self._contains(a_tuple) | |
def value(self): | |
""" returns the left most value | |
""" | |
return self[-1] | |
def key(self): | |
""" returns all the keys in the Path | |
""" | |
return Path(self[:-1]) | |
def _any(p): | |
return True | |
def identity(p, acc=MARKER): | |
yield p | |
dispatch = pandoc_disp = dict() | |
pandoc_disp[_any]=identity | |
def mapping_row_iter(tree, path=MARKER, predicate=pandoc_disp,accumulator=MARKER): | |
""" | |
iterator on a tree that yield an iterator on a mapping in the form of | |
a list of ordered key that leads to the element and the value | |
""" | |
if path is MARKER: | |
path = () | |
p = Path(path+(tree,)) | |
for pred, effector in predicate.items(): | |
switch = pred(p) | |
if isinstance(switch, tuple): | |
switch, predicate = switch | |
def recursor(p, predicate=predicate): | |
return mapping_row_iter(p.value(),path=p.key(), predicate=predicate, accumulator=accumulator) | |
_effector = accumulator is MARKER and (lambda e:effector(e)) or (lambda e:effector(e, accumulator)) | |
if switch in {RECURSOR,}: | |
for value in _effector(p): | |
yield from recursor(value) | |
break | |
if switch: | |
yield from _effector(p) | |
break | |
compute_me="""/A -e-e-10 0 + 1 1.+ A 12 2 x 1. 1+ 2 1 _XZZT 1.5 e2-2 | |
-e-e1 *2/4/ .25// 23000e-BUG3 M 200 m 2 + 10 2.1e1 A N e2-2 +""" | |
def while_digit(e, acc): | |
acc["index"]+=1 | |
acc["current"] += e.value() | |
yield MARKER | |
def _move_to_stack(e, acc, ind=1): | |
print(acc) | |
if acc["current"]: | |
try: | |
acc["stack"] += [float(acc["current"],)] | |
yield ("=", acc["current"]) | |
acc["load"] += 1 | |
except Exception as ex: | |
acc["err"] += [ (acc["index"], str(ex),), ] | |
acc["current"] = "" | |
acc["index"]+=ind | |
print(acc) | |
def move_to_stack(e, acc): | |
yield from _move_to_stack(e,acc) | |
def store(e, acc): | |
acc["mem"] = acc["stack"] | |
acc["pc"]+=1 | |
yield ("S", acc["stack"]) | |
def recall(e, acc): | |
acc["stack"] += acc.get("mem",list()) | |
acc["pc"]+=1 | |
yield ("R",acc.get("mem", list())) | |
rop = lambda op: lambda s: reduce(op,s) | |
def flatten(a, acc=MARKER): | |
for x in a.value(): | |
yield Path(x,) | |
dmux = dict({ | |
"+": rop(float.__add__), "_": rop(float.__sub__), "M": rop(max), | |
"/": rop(float.__truediv__), "x": rop(float.__mul__), "D": lambda s: sorted(s)[int(len(s)/2)], | |
"*": rop(float.__mul__), "m": rop(min), "A": lambda s: sum(s)/len(s), | |
}) | |
def muxer(e,acc): | |
yield from _move_to_stack(e,acc,0) | |
acc["index"]+=1 | |
try: | |
acc["stack"]= [ dmux[e.value()](acc["stack"]), ] | |
acc["pc"] += 1 | |
yield (e.value(), acc["stack"]) | |
except Exception as e: | |
acc["err"] += [ (acc["index"], str(e),),] | |
def is_this(needles): | |
return lambda hay: hay.value() in set(needles) | |
def clear(e,acc): | |
acc["stack"]=list() | |
acc["index"]=0 | |
acc["pc"]=0 | |
acc["load"]=0 | |
acc["ctrl"]=1 | |
acc["err"]=acc["ignored"]=list() | |
yield ("C", acc) | |
def debug(e,acc): | |
print(dumps(acc, indent=4)) | |
acc["ctrl"]+=1 | |
yield ("d",acc) | |
def indexor(e,acc): | |
acc['index']+=1 | |
acc["ignored"] += [(acc['index'], e.value(),),] | |
yield MARKER | |
is_array = lambda p: isinstance(p.value(), MutableSequence) and RECURSOR | |
def quit(e,acc): | |
raise ValueError("quit)") | |
dispatch=dict(( | |
(is_array, flatten), | |
(is_this(".e0123456789-"), while_digit), | |
(is_this(" "), move_to_stack), | |
(is_this(dmux.keys()), muxer), | |
(is_this("C"), clear), | |
(is_this("d"), debug), | |
(is_this("Q"), quit), | |
(is_this("S"), store), | |
(is_this("R"), recall), | |
(any, indexor), | |
)) | |
new_pile=lambda :dict(current="",ctrl=0, pc=0, load=0, stack=list(), err=list(), index=0, ignored=list()) | |
pile=new_pile() | |
res = mapping_row_iter(list(compute_me), predicate=dispatch, accumulator=pile) | |
print(";".join(map(lambda e:"%s%s" %e,(filter(lambda e : e is not MARKER,list(res)))))) | |
# >>> =12;=2;x(24.0,);=1.;=1;+(26.0,);=2;=1;_(23.0,);=1.5;*(34.5,);=2;/(17.25,);=23000e-3;M(23.0,);=200;m(23.0,);=2;+(25.0,);=10;=2.1e1;A(18.666666666666668,);N([-18.666666666666668],) | |
print(pile) | |
# >>> {'current': '', 'pc': 10, 'load': 13, 'stack': ([-18.666666666666668],)} | |
for i,v in pile["err"]: | |
print(compute_me) | |
print((" "*(i - 1)) + '^ ' + str(v)) | |
print() | |
# >>> {'current': '', 'pc': 10, 'load': 13, 'stack': ([-18.666666666666668],)} | |
for i,v in pile["ignored"]: | |
print(compute_me) | |
print((" "*(i - 1)) + "^ was ignored") | |
print() | |
pile=new_pile() | |
lasterr=0 | |
from tkinter import * | |
gui = Tk() | |
gui.title("Simple Calculator") | |
# set the configuration of GUI window | |
gui.geometry() | |
equation = StringVar() | |
result=StringVar() | |
error=StringVar() | |
deb=StringVar() | |
current_proc="" | |
DEF_TOSY=dict({ | |
"-s" : "145", | |
"-v" : "fr" | |
}) | |
def say(tosay,**_kw): | |
kw = DEF_TOSY.copy() | |
kw.update(_kw) | |
call([ "espeak",] + sum([[k,v] for k,v in kw.items()],[]) + [ tosay,] ) | |
translate = dict({ | |
"/" : "divisé", "+" : "pluce ", "*" : "fois", | |
"e" : "dix puissance ", "-" : "moins", "." : "point", | |
"A" : "moyenne", "D" : "médiane ", | |
"M" : "maximum", "_" : "soustraction ", | |
"m" : "minimum", " " : "puis", | |
'\x08': "effacer ", | |
"S" : "enregistre pile ", "R" : "rappel la pile ", | |
"C" : "remise à zéro", "=" : "", | |
}) | |
translate.update({ str(x):str(x) for x in range(10) }) | |
def current_str(string): | |
if string.isdecimal(): | |
return str(int(string)) | |
# in face of ambiguity refuse to guess | |
return " ".join(map(lambda ac:translate.get(ac,ac), list(string))) | |
def key(a,**kw): | |
global current_proc,equation, result, error | |
try: | |
tosay = translate[a.char] | |
except KeyError: | |
return "break" | |
if a.char=="C": | |
equation.set("") | |
if tosay=="effacer ": | |
equation.set(equation.get()[:-1]) | |
if not(pile["current"]): | |
try: | |
pile["current"]=str(pile["stack"].pop()) | |
except IndexError: | |
pass | |
try: | |
pile["current"] = pile["current"][:-1] | |
tosay = "en cours " + current_str(pile["current"]) | |
except IndexError: | |
pass | |
run=mapping_row_iter(list(a.char), predicate=dispatch, accumulator=pile) | |
print(";".join(map(lambda e:"%s%s" %e,(filter(lambda e : e is not | |
MARKER, run))))) | |
if tosay == "puis": | |
tosay=" " if bool(pile["current"]) else "et" | |
if a.char == "=" : | |
tosay+=pile["current"] and "en cours " + current_str(pile["current"]) or (pile["stack"] and " et ".join(map(lambda f:("%g" % f).replace(".",","),pile["stack"])) or "vide" ) | |
print(tosay) | |
say(tosay) | |
if a.char == "C": | |
equation.set("") | |
return "break" | |
deb.set(dumps(pile, indent=4)) | |
if not pile["err"] and not pile["ignored"]: | |
error.set("") | |
if pile["err"]: | |
tosay="pile vide" | |
error.set(tosay) | |
say(tosay) | |
pile["err"]=list() | |
if pile["ignored"]: | |
error.set("<" + str(pile["ignored"][-1][1]) + "> is not a valid character") | |
result.set("\n".join([ "%2d: %.2f" % t for t in enumerate(pile["stack"][::-1])][::-1])) | |
import sys | |
from subprocess import call | |
q=Button(gui, text="Quit", command=lambda *a:sys.exit()) | |
q.grid() | |
pref=dict(font=("Courier", 44), background="blue", foreground="white") | |
pref2=dict(font=("Courier", 30), relief="sunken", anchor="w", justify="left" ,background="white", foreground="midnightblue") | |
expression_field = Entry(gui, textvariable=equation, width=30, **pref) | |
expression_field.grid(row=0,columnspan=4,) | |
expression_field.bind("<Key>", key) | |
error_field = Label(gui, textvariable=error,**pref2) | |
error_field.grid(columnspan=4, row=1, column=0,) | |
result_field = Label(gui, textvariable=result, **pref2) | |
result_field.grid(columnspan=2,sticky ="W", row=2,column=0,rowspan=6) | |
pile_field = Label(gui, textvariable=deb, **pref2) | |
pile_field.grid(columnspan=2,sticky ="W", row=2,column=1, rowspan=6) | |
equation.set('>') | |
gui.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment