210 lignes
7.6 KiB
Python
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() |