re-basierte Scanner-Klasse

Code-Stücke können hier veröffentlicht werden.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Offenbar versteckt sich seit einigen Jahren eine Scanner-Klasse im re-Modul. Der Autor des zitierten Blog-Beitrags hat dennoch eine eigene Implementierung gebaut, das kann ich auch, dachte ich mir:

Code: Alles auswählen

class Scanner(object):
    def __init__(self, *rules):
        self.rules = rules
        self.re = re.compile("|".join("(" + r[0] + ")" for r in rules))

    def scan(self, s):
        for m in self.re.finditer(s):
            try:
                rule = self.rules[m.lastindex - 1]
            except IndexError:
                yield 'ERROR', s[m.start():]
                break
            value = m.group()
            token = rule[1] if len(rule) > 1 else value
            if len(rule) > 2:
                value = rule[2](value)
            if token != 'IGNORE':
                yield token, value
        yield 'END', ''
Mein Code ist ebenfalls nicht kompatibel, aber IMHO einfacher zu benutzen. Ich übergebe `Scanner` eine Liste von Tupeln. Die Methode `scan` liefert immer ein 2-Tupel mit dem Tokentyp und dem Wert zurück. Das Eingabetupel beginnt immer mit einem regulären Ausdruck. Folgt nichts weiter, sind Tokentyp und Wert gleich. Ansonsten ist der zweite Wert des Eingabetupels der Tokentyp. Ist dieser IGNORE, wird das Token ignoriert. Ein optionaler dritter Wert ist eine Funktion, die den Wert nochmal verarbeiten kann. Hier ein Beispiel für S-Ausdrücke:

Code: Alles auswählen

for t in Scanner(
    (r'[()]',),
    (r'\d+(?![^()\s])', 'int', int),
    (r'[^()\s]+', 'sym',),
    (r'\s+', 'IGNORE')
).scan("(a 1+ (c? 3))"):
    print t
Stefan
Antworten