Fichiers
typo-cpge/latex/devuldertex2latex.py
2021-02-22 20:27:30 +01:00

210 lignes
7.6 KiB
Python

import pyparsing as pyp
import io
def automate(s) :
conversion = lambda v : str(v*4) + "pt" ## valeur de l'unité de letstate
pyp.ParserElement.inlineLiteralsUsing(pyp.Suppress)
ident = (pyp.Combine(pyp.Word(pyp.alphas)+pyp.Optional(pyp.Word(pyp.nums)))).setName("ident")
integer = (pyp.Optional(pyp.oneOf(["+", "-"]), default="+")+pyp.Word(pyp.nums)).setParseAction(lambda toks: int(toks[0]+toks[1])).setName("integer")
real = pyp.Combine(pyp.Optional(pyp.oneOf("+ -")) + pyp.Word(pyp.nums) + "." +
pyp.Optional(pyp.Word(pyp.nums))).setName("real").setParseAction(lambda toks: float(toks[0]))
number = real | integer
maybelabel = "{" + pyp.Optional(pyp.QuotedString(quoteChar="$", escChar="\\", unquoteResults=False) | ident, default=None) + "}"
# intro
intro = r"\begin{picture}("+ integer + "," + integer + ")"
bounding_box = None
def assign_bb(s, l, toks) :
x, y = toks
nonlocal bounding_box
bounding_box = (x, y)
intro.setParseAction(assign_bb)
# parfois, le double cercle des états acceptants est réalisé en superposant deux états de diamètres différents
curstatediam = None
def do_setstatediam(toks) :
nonlocal curstatediam
curstatediam = toks[0]
statediam = ("\\setstatediam"+"{"+integer+"}").setParseAction(do_setstatediam)
# letstate
etats = {}
etats_alias = {}
def do_letstate(s, l, toks) :
id, x, y = toks
nonlocal etats, curstatediam, etats_alias
for ancien in [e for e in etats.values() if e["posx"] == x and e["posy"] == y and e["statediam"] != curstatediam] :
ancien["accepting"] = True
etats_alias[id] = ancien["id"]
return
etats[id] = {"id": id, "posx": x, "posy": y, "initial": False, "accepting": False, "label": "", "statediam" : curstatediam}
letstate = (r"\letstate " + ident + "=(" + integer + "," + integer + ")").setParseAction(do_letstate)
# catégories d'états
def do_catetat(s, l, toks) :
statut, id, label = toks
nonlocal etats
if "initial" in statut :
etats[id]["initial"] = True
elif "final" in statut :
etats[id]["accepting"] = True
if label is not None :
etats[id]["label"] = label
drawstate = (pyp.oneOf(["\\drawstate","\\drawinitialstate", "\\drawfinalstate"]) + pyp.Optional("["+ident+"]").suppress() + "(" + ident + ")" + maybelabel ).setParseAction(do_catetat)
# transitions
transitions = []
def do_trans(toks) :
nature, indication, orig, dest, label = toks
nonlocal transitions, etats_alias
if "curved" in nature :
bend = True
else :
bend = False
orig = etats_alias.get(orig, orig)
dest = etats_alias.get(dest, dest)
transitions.append({"orig": orig, "dest": dest, "label": label, "bend": bend, "indication":indication})
trans = (pyp.oneOf(["\\drawcurvedtrans", "\\drawtrans", "\\drawedge"]) + pyp.Optional("["+ident+"]", default=None) + "(" + ident + "," + ident + ")" + maybelabel).setParseAction(do_trans)
boucles = []
def do_loop(toks) :
indication, etat, label = toks
nonlocal boucles, etats_alias
etat = etats_alias.get(etat, etat)
boucles.append({"indication":indication, "orig":etat, "label":label})
loop = ("\\drawloop" + pyp.Optional("[" + ident + "]", default=None) + "(" + ident + ")" + maybelabel).setParseAction(do_loop)
ignorer = ("\\setprofcurve" + "{" + integer + "}") | ("\\setloopdiam" + "{" + integer + "}")
statement = letstate | drawstate | trans | loop | statediam | ignorer.suppress()
outro = pyp.Suppress(r"\end{picture}")
corps = pyp.Forward()
corps << pyp.Optional(statement + corps)
parser = intro + corps + outro
try :
parser.parseString(s)
except pyp.ParseException as e :
print(e)
print(s)
print(e.line)
raise e
os = io.StringIO()
print(r"\begin{tikzpicture}[shorten >=1pt,node distance=2cm,auto, ->, initial text={}]", file=os)
for id in etats :
state = etats[id]
print(r"\node[state", end="", file=os)
if state["initial"] :
print(", initial", end="", file=os)
if state["accepting"] :
print(", accepting", end="", file=os)
print("] (", state["id"], ") at (", conversion(state["posx"]), ",", conversion(state["posy"]), ") {", state["label"], "};", sep="", file=os)
for t in transitions :
if t["indication"] == "r" :
benddir = " right"
elif t["indication"] == "l" :
benddir = " left"
else :
benddir = " left"
if t["bend"] :
bend = "[bend" + benddir + "]"
else :
bend = ""
print(r"\path (", t["orig"], ") edge", bend, " node {", t["label"], "} (", t["dest"], ");", sep="", file=os)
for t in boucles :
if t["indication"] == "b" :
dir = "below"
else :
dir = "above"
print(r"\path (", t["orig"], ") edge[loop ", dir, "] node {", t["label"], "} (", t["orig"], ");", sep="", file=os)
print(r"\end{tikzpicture}", file=os)
return os.getvalue()
def traiter_automates(nf_i, nf_o) :
inst = open(nf_i)
outst = open(nf_o, "w")
in_auto = False
while True :
line = inst.readline()
if line is None or line == "" :
break
if line.startswith(r"\begin{picture}") :
auto = line
in_auto = True
elif line.startswith(r"\end{picture}") :
auto += line
outst.write(automate(auto))
auto = ""
in_auto = False
elif in_auto :
auto += line
else :
outst.write(line)
inst.close()
outst.close()
def recup_comment(s) :
spl = s.split("%", 1)
if len(spl) == 1 :
return ""
else :
return "%" + spl[1]
def exsol(nf_i, nf_o) :
inst = open(nf_i)
outst = open(nf_o, "w")
while True :
line = inst.readline()
if line is None or line == "" :
break
elif line.startswith(r"\begin{ex}") :
print(r"\begin{question}" + recup_comment(line), file=outst)
elif line.startswith(r"\begin{sol}") :
print(r"\end{question}", file=outst)
print(r"\begin{corrige}" + recup_comment(line), file=outst)
elif line.startswith(r"\end{sol}") :
print(r"\end{corrige}", file=outst)
inst.readline() # ignorer la ligne suivante
else :
print(line, end="", file=outst)
inst.close()
outst.close()
def resume(nf_i, nf_o) :
inst = open(nf_i)
outst = open(nf_o, "w")
texte = inst.readlines()
inst.close()
imbrication = 0
for k in range(len(texte)) :
if texte[k].startswith(r"\begin{enumerate}") :
if imbrication == 0 :
texte[k] = texte[k].replace("enumerate", "enumq")
imbrication += 1
elif texte[k].startswith(r"\end{enumerate}") :
imbrication -= 1
if imbrication == 0 :
texte[k] = texte[k].replace("enumerate", "enumq")
k = len(texte) - 1
while k >= 0 :
if texte[k].startswith(r"\begin{enumq}[resume]") :
texte[k] = texte[k].replace(r"\begin{enumq}[resume]", r"\resume{enumq}")
k = k - 1
while not texte[k].startswith(r"\end{enumq}") :
k = k - 1
texte[k] = texte[k].replace(r"\end{enumq}", r"\suspend{enumq}")
k = k - 1
for l in texte :
print(l, end="", file=outst)
outst.close()