2012-06-29 11 views
5

Ich habe einen BBCode -> HTML-Konverter, der auf das Änderungsereignis in einem Textfeld reagiert. Gegenwärtig geschieht dies mit einer Reihe von regulären Ausdrücken, und es gibt eine Reihe von pathologischen Fällen. Ich wollte schon immer den Stift auf dieser Grammatik schärfen, wollte mich aber nicht in Yak rasieren. Aber ... kürzlich wurde ich auf pegjs aufmerksam, was eine ziemlich vollständige Implementierung der PEG-Parser-Generation zu sein scheint. Ich habe den größten Teil der Grammatik angegeben, aber ich frage mich jetzt, ob das ein richtiger Parser ist.Verwenden von PEG Parser für BBCode Parsing: pegjs oder ... was?

Meine spezifische Fragen sind:

  1. Da meine Anwendung beruht auf übersetzen, was ich kann den Rest als Rohtext in HTML und verlassen, wird die Umsetzung bbcode einen Parser verwenden, die auf einem Syntaxfehler Sinn machen kann fehlschlagen ? Zum Beispiel: [url=/foo/bar]click me![/url] würde sicherlich erwartet, um erfolgreich zu sein, sobald die schließende Klammer auf dem Schließen-Tag eingegeben wird. Was aber sieht der Nutzer in der Zwischenzeit? Mit Regex kann ich nicht übereinstimmende Dinge einfach ignorieren und sie als normalen Text für Vorschauzwecke behandeln. Mit einer formalen Grammatik weiß ich nicht, ob dies möglich ist, weil ich mich darauf verlasse, den HTML-Code aus einem Parse-Baum zu erstellen, und was bei einer Syntaxanalyse fehlschlägt, ist ... was?

  2. Ich bin unklar, wo die Transformationen durchgeführt werden sollten. In einem formalen Lex/Yacc-basierten Parser hätte ich Header-Dateien und Symbole, die den Knotentyp bezeichnen. In pegjs bekomme ich geschachtelte Arrays mit dem Knoten Text. Ich kann den übersetzten Code als eine Aktion des von pegjs erzeugten Parsers ausgeben, aber es scheint wie ein Code-Geruch, einen Parser und einen Emitter zu kombinieren. Allerdings, wenn ich rufe PEG.parse.parse(), bekomme ich so etwas wie dies zurück:

[ 
     [ 
      "[", 
      "img", 
      "", 
      [ 
      "/", 
      "f", 
      "o", 
      "o", 
      "/", 
      "b", 
      "a", 
      "r" 
      ], 
      "", 
      "]" 
     ], 
     [ 
      "[/", 
      "img", 
      "]" 
     ] 
    ]

gegeben eine Grammatik wie:

document 
    = (open_tag/close_tag/new_line/text)* 

open_tag 
    = ("[" tag_name "="? tag_data? tag_attributes? "]") 


close_tag 
    = ("[/" tag_name "]") 

text 
    = non_tag+ 

non_tag 
    = [\n\[\]] 

new_line 
    = ("\r\n"/"\n") 

ich die Grammatik Abkürzen, natürlich, aber Sie bekomme eine Vorstellung. Also, wenn Sie bemerken, gibt es keine kontextuellen Informationen in dem Array von Arrays, die mir sagt, was für ein Knoten ich habe und ich bin noch die String-Vergleiche wieder selbst wenn der Parser dies bereits getan hat. Ich erwarte, dass es möglich ist, Callbacks zu definieren und Aktionen zu verwenden, um sie während einer Analyse auszuführen, aber im Web gibt es kaum Informationen darüber, wie man das machen könnte.

Banne ich den falschen Baum an? Sollte ich zum Regex-Scan zurückkehren und das Parsing vergessen?

Dank

+0

Steve, Ihre Frage ist sehr interessant (+1), ich möchte nur das Gleiche in einer Erweiterung tun: BBCode in einem Textarea analysieren (leider ist das das Format, das ein Forum immer noch benutzt), und ein "live "Vorschau vom eingegebenen Text mit PEG.js oder etwas anderem als regulären Ausdrücken. Hast du es geschafft, die Grammatik für den BBCode-Parser zu erstellen? Kannst du deine Lösung nicht über GitHub oder etwas anderes teilen? Das würde mir sehr helfen. Vielen Dank im Voraus! – Sk8erPeter

+0

Ich habe [BBCode-Parser von Patorjk verwendet] (https://github.com/patorjk/Extendible-BBCode-Parser). Funktioniert gut und kann an Ihre eigenen Bedürfnisse angepasst werden, wenn Sie spezielle Tags haben. –

+0

Danke, ich habe diese Bibliothek schon gesehen, aber sie verwendet reguläre Ausdrücke, die ich vermeiden wollte, weil das Parsen von BBCode mit regulären Ausdrücken theoretisch nicht ohne Fehler gemacht werden kann ([»» link] (http: // nordmann.de/blog/do_NOT_parse_using_regexp.html)) in einigen Fällen, z wenn sie ineinander verschachtelt werden, usw. Deshalb wollte ich es mit Parsing-Ausdruck-Grammatik-Formalismus machen. Hast du nicht versucht, die Grammatik, die du angefangen hast, zu verbessern? :) Könnten Sie nicht die Grundlage dafür teilen? :) – Sk8erPeter

Antwort

2

In Bezug auf Ihre erste Frage, die ich tosay haben, dass eine Live-Vorschau schwierig sein wird. Die Probleme, auf die Sie hingewiesen haben, dass der Parser nicht versteht, dass die Eingabe "in Bearbeitung" ist, sind korrekt. Peg.js sagt Ihnen, an welchem ​​Punkt der Fehler ist, also könnten Sie vielleicht diese Information nehmen und ein paar Wörter zurückgehen und erneut analysieren oder wenn ein End-Tag fehlt, versuchen Sie es am Ende hinzuzufügen.

Der zweite Teil Ihrer Frage ist einfacher, aber Ihre Grammatik wird danach nicht so schön aussehen. Im Grunde, was Sie tun, ist Put-Rückrufe bei jeder Regel, so zum Beispiel

text 
    = text:non_tag+ { 
    // we captured the text in an array and can manipulate it now 
    return text.join(""); 
    } 

Im Moment Sie diese Rückrufe inline in Ihrer Grammatik zu schreiben. Ich mache gerade eine Menge von diesen Sachen bei der Arbeit, also könnte ich einen pullrequest an peg.js machen, um das zu beheben. Aber ich bin mir nicht sicher, wann ich die Zeit dafür finde.

1

Versuchen Sie etwas wie diese Ersetzungsregel. Du bist auf dem richtigen Weg; Sie müssen es nur sagen, um die Ergebnisse zusammenzustellen.

Text = Ergebnis: non_tag + {return result.join (''); }

3

Erste Frage (Grammatik für unvollständige Texte):

Sie

incomplete_tag = ("[" tag_name "="? tag_data? tag_attributes?) 
//       the closing bracket is omitted ---^ 

nachopen_tag und document einen unvollständigen-Tag am Ende einschließen ändern hinzufügen können. Der Trick ist, dass Sie den Parser mit allen benötigten Produktionen an immer parse, aber die gültigen kommen zuerst. Sie können dann während der Live-Vorschau incomplete_tag ignorieren.

Zweite Frage (wie Aktionen enthalten):

Sie sogenannte Schreib Aktionen nach Ausdrücken. Eine Aktion ist Javascript-Code eingeschlossen durch geschweifte Klammern und erlaubt nach einem Ausdruck pegjs, d. e. auch mitten in einer Produktion!

In der Praxis sind Aktionen wie { return result.join("") } fast immer notwendig, da sich pegjs in einzelne Zeichen aufteilt. Auch komplizierte verschachtelte Arrays können zurückgegeben werden. Daher schreibe ich normalerweise Hilfsfunktionen in den Pegjs-Initialisierer am Kopf der Grammatik, um die Aktionen klein zu halten. Wenn Sie die Funktionsnamen sorgfältig auswählen, ist die Aktion selbstdokumentierend.

Für ein Beispiel siehe PEG for Python style indentation. Haftungsausschluss: Dies ist eine Antwort von mir.