Op het moment ben ik bezig met het schrijven met een parser voor wikitext (in dit geval de mediawikiversie. Ja, daar bestaat al een parser voor, maar die is ontworpen door een stel techies die wel regexps konden maar verder niets van formele talen wisten. Do the maths.). Dit is dan ook geen simpele taal om systematischer te parsen - ik heb er dan ook voor gekozen een parser van de grond af te bouwen. Wellicht niet de meest efficiente methode (maar dat geldt ook voor het in python bouwen), maar ik ben niet veel verder gekomen dan de eerste hoofdstukken van the dragon book 
De structuur is op het moment als volgt:
* Een lexer die een stroom van tokens uitstuurt die voor de parser logisch zijn
* een buffered reader die deze stroom buffert, zodat het mogelijk is te backtracken en te peek()en
* een recursive backtracking parser die uit deze stroom leest
Het recursieve van deze parser levert echter een probleem op: hoe kan ik de reader laten backtracken in een recursieve aanroep. Stel ik heb de volgende tekst:
[[link|met {{net-geen-template} in de beschrijving]]
wanneer openende {'s worden gevonden dan probeert de parser 'm eerst als parameter ( {{{1}}} ) te parsen, vervolgens als template( {{template}} ), en als laatste als tekst. Voordat de tekst hier gekozen wordt wordt dus twee keer een backtrackactie uitgevoerd - waarbij de input stream natuurlijk ook een eind terug moet.
De reader heeft echter nog een functie die nodig is: het toevoegen van tokens aan de stroom. Neem als voorbeeld:
[[plaatje|met in de beschrijving een [[link]]]]
De vier ]]]]'s worden in eerste instantie als één token gezien (met lengte 4), geparsed als een wikilink, waarna een token met lengte 2 weer de stroom in wordt gestuurd. Het toevoegen van tokens geeft een extra 'dimensie' aan het probleem, omdat deze bij een eventuele backtrack weer weggegooid moeten worden.
Om zo'n de reader recursive-compatible te maken heb ik een paar manieren bedacht, maar die hebben allemaal een aantal nadelen:
* een nieuwe reader maken die de oude inleest heeft twee nadelen. De peek() leest een extra token in (maar dat is vrij makkelijk te fixxen door van de parent ook de peek() te gebruiken, mits die bestaat) - maar belangrijker: tokens die zijn toegevoegd worden niet ook aan de parent toegevoegd, waardoor de hierboven beschreven ]]'s nooit aankomen bij de parser van de buitenste wikilink
* bijhouden waar een bepaalde parser begonnen is met lezen. Dit lost het peek() en het probleem van het toevoegen van ]]'s op - maar hoe gooi je die ooit weer uit de stream? Op het onderste niveau is dat simpel: een backup houden van de input stream en die uitlezen in plaats van je buffer met toegevoegde tokens, maar hoe kom je te weten op welk punt in je buffer die toegevoegde tekens zijn gebleven, of hoever je buffer is opgeschoven?
* voor iedere parser een eigen buffer bijhouden. In feite is dit optie 1, maar dan zonder de peek()-problemen. Wel heb je hier óf problemen met het toevoegen van tokens, óf met het weghalen van tokens.
Doe ik te moeilijk, moet ik 'gewoon' een parser generator gebruiken (welke dan?) of heeft iemand gewoon een goed idee?
Source > http://svn.wikimedia.org/...pedia/trunk/pywikiparser/ (Lexer.py, BufferedReader.py en Parser.py; in ObjectTree/ staat een simpele object tree omdat ik niet tevreden was met de bestaande
)
De structuur is op het moment als volgt:
* Een lexer die een stroom van tokens uitstuurt die voor de parser logisch zijn
* een buffered reader die deze stroom buffert, zodat het mogelijk is te backtracken en te peek()en
* een recursive backtracking parser die uit deze stroom leest
Het recursieve van deze parser levert echter een probleem op: hoe kan ik de reader laten backtracken in een recursieve aanroep. Stel ik heb de volgende tekst:
[[link|met {{net-geen-template} in de beschrijving]]
wanneer openende {'s worden gevonden dan probeert de parser 'm eerst als parameter ( {{{1}}} ) te parsen, vervolgens als template( {{template}} ), en als laatste als tekst. Voordat de tekst hier gekozen wordt wordt dus twee keer een backtrackactie uitgevoerd - waarbij de input stream natuurlijk ook een eind terug moet.
De reader heeft echter nog een functie die nodig is: het toevoegen van tokens aan de stroom. Neem als voorbeeld:
[[plaatje|met in de beschrijving een [[link]]]]
De vier ]]]]'s worden in eerste instantie als één token gezien (met lengte 4), geparsed als een wikilink, waarna een token met lengte 2 weer de stroom in wordt gestuurd. Het toevoegen van tokens geeft een extra 'dimensie' aan het probleem, omdat deze bij een eventuele backtrack weer weggegooid moeten worden.
Om zo'n de reader recursive-compatible te maken heb ik een paar manieren bedacht, maar die hebben allemaal een aantal nadelen:
* een nieuwe reader maken die de oude inleest heeft twee nadelen. De peek() leest een extra token in (maar dat is vrij makkelijk te fixxen door van de parent ook de peek() te gebruiken, mits die bestaat) - maar belangrijker: tokens die zijn toegevoegd worden niet ook aan de parent toegevoegd, waardoor de hierboven beschreven ]]'s nooit aankomen bij de parser van de buitenste wikilink
* bijhouden waar een bepaalde parser begonnen is met lezen. Dit lost het peek() en het probleem van het toevoegen van ]]'s op - maar hoe gooi je die ooit weer uit de stream? Op het onderste niveau is dat simpel: een backup houden van de input stream en die uitlezen in plaats van je buffer met toegevoegde tokens, maar hoe kom je te weten op welk punt in je buffer die toegevoegde tekens zijn gebleven, of hoever je buffer is opgeschoven?
* voor iedere parser een eigen buffer bijhouden. In feite is dit optie 1, maar dan zonder de peek()-problemen. Wel heb je hier óf problemen met het toevoegen van tokens, óf met het weghalen van tokens.
Doe ik te moeilijk, moet ik 'gewoon' een parser generator gebruiken (welke dan?) of heeft iemand gewoon een goed idee?
Source > http://svn.wikimedia.org/...pedia/trunk/pywikiparser/ (Lexer.py, BufferedReader.py en Parser.py; in ObjectTree/ staat een simpele object tree omdat ik niet tevreden was met de bestaande