Python-Syntax-Highlighter

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich habe aus Spaß an der Aufgabe einen einfachen Syntax-Highlighter für Python in Python prototypisiert. Ich brauche den eigentlich in JavaScript, daher auch keine Python-spezifischen Imports für Scanner, Parser oder so. Dennoch frage ich mich (und euch), geht es noch kürzer? Mich stört die elif-Kaskade, doch mir fällt nichts besseres ein.

Code: Alles auswählen

import re

TOKENS = re.compile('''
    (\\#[^\r\n]*) |
    (and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|
     for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|
     try|while|with|yield)\\b |
    ([rR]?[uU]?
     (?:""".*?"""|"(?:\\\\"|[^"])*?"|\'\'\'.*?\'\'\'|'(?:\\\\'|[^'])*?')) |
    (0[xX][0-9a-fA-F]+|\\d+(?:\\.\\d+)?(?:[eE][-+]?\\d+)?[lLjJ]?) |
    ((?:(?<=def)|(?<=class))(\\s+)(\\w+)) |
    (\\s+|\\w+|.)''', re.DOTALL + re.VERBOSE)

def colorize(s):
    tokens = []
    for m in TOKENS.finditer(s):
        if m.group(1): # comments
            tokens.append("<i>%s</i>" % m.group())
        elif m.group(2): # keywords
            tokens.append("<b>%s</b>" % m.group())
        elif m.group(3): # strings
            tokens.append("<span class='str'>%s</span>" % m.group())
        elif m.group(4): # numbers
            tokens.append("<span class='num'>%s</span>" % m.group())
        elif m.group(5): # defined names
            tokens.append("%s<u>%s</u>" % (m.group(6), m.group(7)))
        else:
            tokens.append(m.group())
    return "".join(tokens)
Stefan
Zuletzt geändert von sma am Dienstag 25. März 2008, 10:25, insgesamt 2-mal geändert.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Jetzt, wo ich's aufgeschrieben habe, biete ich noch diese Variante, die kompakter und einfacher erweiterbar ist, jedoch vielleicht nicht ganz so schnell. Leider gibt es keine direkte Entsprechung von `expand` in JavaScript.

Code: Alles auswählen

import re

def _(p, r): return (re.compile(p), r)

TOKENS = [
    _('(\\#[^\r\n]*)', '<i>\\1</i>'),
    _('''(?x)(and|as|assert|break|class|continue|def|del|elif|else|except|exec|
      finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|
      return|try|while|with|yield)\\b''', '<b>\\1</b>'),
    _('''(?xs)([rR]?[uU]?(?:""".*?"""|"(?:\\\\"|[^"])*?"|\'\'\'.*?\'\'\'|
      '(?:\\\\'|[^'])*?'))''', '<span class="str">\\1</span>'),
    _('(0[xX][0-9a-fA-F]+|\\d+(?:\\.\\d+)?(?:[eE][-+]?\\d+)?[lLjJ]?)',
      '<span class="num">\\1</span>'),
    _('(?:(?<=def)|(?<=class))(\\s+)(\\w+)', '\\1<u>\\2</u>'),
    _('(\\#[^\r\n]*)', '<i>\\1</i>'),
    _('(\\s+|\\w+|.)', '\\1'),
]

def colorize(s):
    chunks = []
    while s:
        for pattern, replacement in TOKENS:
            m = pattern.match(s)
            if m:
                chunks.append(m.expand(replacement))
                s = s[m.end():]
                break
    return "".join(chunks)
Stefan
Zuletzt geändert von sma am Dienstag 25. März 2008, 10:26, insgesamt 2-mal geändert.
lunar

Darf man fragen, warum der Highligther in JavaScript implementiert werden soll?

Wenn du Highlighting Serverseitig implementierst, kannst du auf ausgereiftere Syntaxhighligther zurückgreifen.

Btw, bist du wirklich sicher, dass da nichts für dich dabei ist? Nicht, dass ich deinen Programmiereifer bremsen will, aber du läuft Gefahr, dass Rad schon wieder zu erfinden ;)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

lunar hat geschrieben:Darf man fragen, warum der Highligther in JavaScript implementiert werden soll?
Weil's clientseitig laufen sollte. Und nein, ich habe nicht gesucht, wenn ich so was mache, geht's mir um's selbstmachen. Andere lösen Kreuzworträtsel. Das ist ein Ausgleich zum Pflegen von Projektplänen und Hüten von widerspenstigen Entwicklern ;)[/quote]
lunar hat geschrieben:Nicht, dass ich deinen Programmiereifer bremsen will, aber du läuft Gefahr, dass Rad schon wieder zu erfinden ;)
Ich weiß, aber das ist mir egal. Ich hatte das neulich zum Zug geschrieben und dachte mir, teile ich's der Welt mit. Mein Bestreben ist, minimale Lösungen zu finden. Der erste Google-Hit etwa braucht über 20k Quelltext, um Python einzufärben.

Stefan
lunar

sma hat geschrieben:
lunar hat geschrieben:Darf man fragen, warum der Highligther in JavaScript implementiert werden soll?
Weil's clientseitig laufen sollte.
Ach nein ;)

Die Frage ist doch, ob clientseitig sinnvoll ist. Ich würde eher AJAX nehmen, und das Highligthing auf den Server verschieben. Immerhin ist Syntax-Highlighting recht komplex, was dementsprechend große Bibliotheken erfordern würde.

Wobei das natürlich völlig egal ist, wenn du einen JavaScript-Highlighter aus akademischem Interesse implementieren willst. Nur für Real-World-Programme würde ich ihn nicht unbedingt einsetzen ;)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

lunar hat geschrieben:Immerhin ist Syntax-Highlighting recht komplex, was dementsprechend große Bibliotheken erfordern würde.
Ahem, den vollständigen Code habe ich doch gepostet. Die JavaScript-Version wird sich davon nicht wesentlich unterscheiden. Wieso ist das groß? Missverstehen wir uns irgendwie?

Stefan
lunar

sma hat geschrieben:
lunar hat geschrieben:Immerhin ist Syntax-Highlighting recht komplex, was dementsprechend große Bibliotheken erfordern würde.
Ahem, den vollständigen Code habe ich doch gepostet. Die JavaScript-Version wird sich davon nicht wesentlich unterscheiden. Wieso ist das groß? Missverstehen wir uns irgendwie?
Offenbar besteht schon ein kleines Missverständnis hinsichtlich des Anspruchs an einen Formatter. Ich persönlich hätte gerne einen, der korrekte Tokens zurückgibt und infolge dessen korrekt hervorhebt. Dir scheint es offenbar lediglich um geringe Codegröße zu gehen, was sich in der Qualität entsprechend niederschlägt. Dein Code funktioniert nicht. Nichts für ungut, aber die Resultate können nicht überzeugen:

Der Test-Code
Dein Formatter mit zusätzlichem HTML-Code und entsprechenden Definitionen für die CSS-Klassen
Das Resultat von Pygments (full-Option für den HTML-Formatter angegeben, um CSS-Definitionen einzubetten)

Sorry, dass es gepastet ist, aber mir viel kein besseres Medium ein. Kennst du ein Tool, um einen komplette Website mit der Gecko-Engine in ein Bild rendern zu lassen? Das wäre für diesen Zweck besser gewesen.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

lunar, erst mal vielen Dank für deinen Test und Bug-Report. Das "Dein Code funktioniert nicht" bedeutet, dass ich vergessen habe, Schlüsselworte auf Worte zu beschränken, ja? Weitere Probleme außer dem nakten "<" sehe ich nicht.

Schaue ich mir das Ergebnis von Pygments an, wird da nicht nur nach einem def und class das nächste Wort anders hervorgehoben, sondern auch nach from und import. Das könnte ich auch. Dann werden """-Strings an Doc-String-Position anders dargestellt. Damit täte ich mich schwer. Schließlich werden offenbar eingebaute Funktionen und vordefinierte Typen hervorgehoben. Das wäre wieder kein Problem - zwei weitere Listen von Schlüsselworten - gefällt mir aber nicht so, weil ich nicht einsehe, warum ValueError bzw. irgendwie anders ist als ArgumentError.

Stefan
lunar

sma hat geschrieben:lunar, erst mal vielen Dank für deinen Test und Bug-Report. Das "Dein Code funktioniert nicht" bedeutet, dass ich vergessen habe, Schlüsselworte auf Worte zu beschränken, ja? Weitere Probleme außer dem nakten "<" sehe ich nicht.
Ich habe keine Ahnung, was nicht stimmt, ich sehe nur, dass das Ergebnis im Firefox nicht so ganz überzeugend war ;)

Bei genauerer Betrachtung scheint das allerdings tatsächlich nur daran zu liegen, dass du HTML-Sonderzeichen nicht gequotet hast. Allerdings ist das nicht der einzige Bug:

Code: Alles auswählen

[lunar@nargond]-[12:30:04] >> /home/lunar/test
[4]--> print colorize(r"'foo\''")
<span class="str">'foo\'</span>'
Maskierte Anführungszeichen in einem Literal werden ebenfalls nicht erkannt, was mich bei einem Parser, der auf regulären Ausdrücken aufbaut, auch wundern würde.

Die Ausgabe von Pygments war mehr als Vergleich (weil Pygments korrekt parst) gedacht denn als Anregung.
BlackJack

@sma: Vordefinierte Funktionen und Typen gesondert hervor zu heben, finde ich ganz gut, weil man dann eher sieht, wenn man etwas "eingebautes" überschreibt.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

lunar hat geschrieben:Maskierte Anführungszeichen in einem Literal werden ebenfalls nicht erkannt, was mich bei einem Parser, der auf regulären Ausdrücken aufbaut, auch wundern würde.

Die Ausgabe von Pygments war mehr als Vergleich (weil Pygments korrekt parst) gedacht denn als Anregung.
Wobei ja zu sagen ist, dass Pygments durchaus auch Reguläre Ausdrücke verwendet.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also ich finde diese minimalistische Lösung ganz nett. Könnte ich in PyLucid einbauen, als alternative, wenn Pygments nicht verfügbar ist ;)
Allerdings sollten dann die Klassennamen am besten gleich sein, damit die Stylesheets die selben sein können...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Du vergisst, das nicht jeder nen Python-Highlighter braucht. Pygments kann da durchaus etwas mehr :)


MfG EnTeQuAk
lunar

Leonidas hat geschrieben:
lunar hat geschrieben:Maskierte Anführungszeichen in einem Literal werden ebenfalls nicht erkannt, was mich bei einem Parser, der auf regulären Ausdrücken aufbaut, auch wundern würde.[...]
Wobei ja zu sagen ist, dass Pygments durchaus auch Reguläre Ausdrücke verwendet.
Allerdings innerhalb eines Zustandsautomaten, denn gerade Dinge wie Strings mit maskierten Anführungszeichen darin lassen sich mit regulären Ausdrücken nur schwerlich abbilden.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

lunar hat geschrieben:Allerdings innerhalb eines Zustandsautomaten, denn gerade Dinge wie Strings mit maskierten Anführungszeichen darin lassen sich mit regulären Ausdrücken nur schwerlich abbilden.
Python's Reguläre Ausdrücke sind aber auch keine.
TUFKAB – the user formerly known as blackbird
lunar

mitsuhiko hat geschrieben:
lunar hat geschrieben:Allerdings innerhalb eines Zustandsautomaten, denn gerade Dinge wie Strings mit maskierten Anführungszeichen darin lassen sich mit regulären Ausdrücken nur schwerlich abbilden.
Python's Reguläre Ausdrücke sind aber auch keine.
Inwiefern?
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

lunar hat geschrieben:
mitsuhiko hat geschrieben:Python's Reguläre Ausdrücke sind aber auch keine.
Inwiefern?
Die können Backrefs, Lookaheads und alles mögliche was mit einem NFA nicht möglich ist.

Unabhängig davon spricht nichts dagegen Strings mit Regulär Expressions inkl. Escapes zu matchen:

Code: Alles auswählen

string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?s)')
TUFKAB – the user formerly known as blackbird
lunar

mitsuhiko hat geschrieben:
lunar hat geschrieben:
mitsuhiko hat geschrieben:Python's Reguläre Ausdrücke sind aber auch keine.
Inwiefern?
Die können Backrefs, Lookaheads und alles mögliche was mit einem NFA nicht möglich ist.
Ich hatte dein vorletztes Posting so verstanden, dass du behaupten wolltest, Python's reguläre Ausdrücke wäre in Wirklichkeit keine regulären Ausdrücke. Insofern erscheint es mir gerade etwas irreführend, reguläre Ausdrücke mit Zustandsautomaten zu vergleichen...
Unabhängig davon spricht nichts dagegen Strings mit Regulär Expressions inkl. Escapes zu matchen:

Code: Alles auswählen

string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?s)')
Dagegen spricht die Lesbarkeit.
BlackJack

Reguläre Ausdrücke und endliche Automaten sind äquivalent. Irreführend ist, die Bezeichnung reguläre Ausdrücke für dass was Programmiersprachen bzw. regex-Engines alles können. Jedenfalls für theoretische Informatiker. :-)
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

lunar hat geschrieben:

Code: Alles auswählen

string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?s)')
Dagegen spricht die Lesbarkeit.
Die spricht immer gegen Regex ;)
Antworten