Eigentlich sind Parser einfach. Ich verstehe nicht, warum so viele Leute das Verarbeiten von Programmen im Gegensatz zu anderen Daten zu fürchten. Eine Parsing-Expression-Grammar (PEG) kann man sich auch recht einfach ad-hoc bauen. Ich habe das in Postings hier schon 2x gemacht. PEGs sind eine Verallgemeinerung von regulären Ausdrücken und nicht ganz so mächtig wie kontextfreie Grammatiken (was man so landläufig in der Uni lernt) aber einfacher zu konstruieren, weil man keine komplexen Parsergeneratoren braucht.
Sobald du "zählen" willst - was z.B. schachteln heißt - reichen reguläre Ausdrücke nicht. Das zu beweisen lernt man an der Uni dann in Automatentheorie. Man lernt dann auch, dass wenn man einen Stapel (wir haben das damals noch Kellerspeicher genannt ;) hinzufügt, man doch zählen kann und entsprechende Automaten kontextfreie Grammatiken (nicht anderes bauen nämlich Parsergeneratoren wie Yacc oder Bison). Alternativ kann man Rekursion statt eines Stapels einsetzen und landet dann bei rekursiv absteigenden Parsern, wie man sie von Hand schreiben kann, oder wie sie Tools wie Coco/R bauen. Ich schweife aber total ab.
Mein Vorschlag wäre, Einrückungen gesondert über einen Stapel zu behandeln. Prüfe bei jeder Zeile die Einrückung. Zähle die Leerzeichen. Sind es mehr als die aktuelle Einrückung, erzeuge ein <div class="indent"> o.ä. und mache weiter. Ist es weniger, entferne solange Einrückungen vom Stapel, bis es wieder passt. Dann mache weiter.
Code: Alles auswählen
output = []
indents = []
def set_indent(i, begin, end):
indents.append((i, end))
output.append(begin)
def need_indent(indent):
while indent < indents[-1][0]:
output.append(indents.pop()[1])
return indent > indents[-1][0]
def parse(lines):
set_indent(0, "<body>", "</body>")
for line in lines.splitlines():
if line.strip():
indent = len(line) - len(line.lstrip()); line = line[indent:]
if need_indent(indent):
if line.startswith("* "):
set_indent(indent, "<ul>", "</ul>")
else:
set_indent(indent, "<div class='indent'>", "</div>")
if line.startswith("* "):
set_indent(indent + 2, "<li>", "</li>")
line = line[2:]
output.append(line)
while indents:
output.append(indents.pop()[1])
return "\n".join(output)
print parse("""
Line.
Indented Line.
* List1
* Sublist 1
* Sublist 2
* List2
Line in List
Indented Line.
""")
Stefan
PS: Ich überlasse es dem Leser, die häßliche globale Variable output zu entfernen.