2012-06-29 23 views
5

Ho un convertitore bbcode -> html che risponde all'evento change in una textarea. Attualmente, questo viene fatto usando una serie di espressioni regolari, e ci sono un certo numero di casi patologici. Ho sempre voluto affinare la matita su questa grammatica, ma non volevo entrare nella rasatura degli yak. Ma ... di recente sono venuto a conoscenza di pegjs, che sembra un'implementazione abbastanza completa della generazione di parser PEG. Ho specificato la maggior parte della grammatica, ma ora mi chiedo se questo sia un uso appropriato di un parser completo.Utilizzo di PEG Parser per l'analisi di BBCode: pegjs o ... cosa?

Le mie domande specifiche sono:

  1. Come la mia applicazione si basa sulla traduzione di quello che posso per HTML e lasciando il resto come testo grezzo, non attuare bbcode utilizzando un parser in grado di fallire su un errore di sintassi ha senso ? Ad esempio: ci si aspetta che il numero [url=/foo/bar]click me![/url] abbia successo una volta inserita la parentesi di chiusura sul tag di chiusura. Ma cosa vedrebbe l'utente nel frattempo? Con regex, posso semplicemente ignorare le cose non corrispondenti e trattarle come testo normale per scopi di anteprima. Con una grammatica formale, non so se questo sia possibile perché mi sto affidando alla creazione dell'HTML da un albero di analisi e quello che fallisce nel parse è ... cosa?

  2. Non sono chiaro dove devono essere eseguite le trasformazioni. In un parser formale basato su lex/yacc, avrei file di intestazione e simboli che denotavano il tipo di nodo. In pegjs, ottengo array annidati con il testo del nodo. Posso emettere il codice tradotto come azione del parser generato da pegjs, ma sembra un odore di codice per combinare un parser e un emettitore. Tuttavia, se chiamo PEG.parse.parse(), torno qualcosa di simile:

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

dato una grammatica come:

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") 

che sto abbreviare la grammatica, ovviamente, ma si avere l'idea Quindi, se si nota, non ci sono informazioni contestuali nella matrice di array che mi dice che tipo di nodo ho e sono lasciato a fare il confronto di stringhe ancora anche se il parser lo ha già fatto. Mi aspetto che sia possibile definire i callback e utilizzare le azioni per eseguirli durante un'analisi, ma sul Web sono disponibili scarse informazioni su come si potrebbe farlo.

Sto abbaiando dall'albero sbagliato? Devo tornare alla scansione regolare e dimenticare l'analisi?

Grazie

+0

Steve, la tua domanda è molto interessante (+1), voglio solo fare la stessa cosa in un'estensione: analizzare il BBCode in una textarea (purtroppo questo è il formato utilizzato da un forum) e creare un "live" "anteprima dal testo digitato utilizzando PEG.js o qualsiasi altra cosa tranne le espressioni regolari. Sei riuscito a creare la grammatica per il parser di BBCode? Non potresti per favore condividere la tua soluzione tramite GitHub o altro? Questo mi aiuterebbe molto. Grazie mille in anticipo! – Sk8erPeter

+0

Ho usato [parser bbcode di patorjk] (https://github.com/patorjk/Extendible-BBCode-Parser). Funziona alla grande e può essere ottimizzato per le tue esigenze se hai tag speciali. –

+0

Grazie, ho già visto questa libreria, ma usa espressioni regolari, che volevo evitare, perché teoricamente, l'analisi del BBCode usando le espressioni regolari non può essere eseguita senza errori ([»» link] (http: // kore- nordmann.de/blog/do_NOT_parse_using_regexp.html)) in alcuni casi, ad es. quando li annidiamo l'uno nell'altro, ecc. Ecco perché volevo farlo usando il formalismo grammaticale di espressione parsing. Quindi non hai provato a migliorare la grammatica che hai iniziato? :) Non potresti condividere la base di esso? :) – Sk8erPeter

risposta

2

Per quanto riguarda la prima domanda che ho tosay che un'anteprima in tempo reale sta per essere difficile. I problemi che hai evidenziato riguardo al fatto che il parser non capirà che l'input è "work in progress" sono corretti. Peg.js ti dice a che punto l'errore è, quindi forse potresti prendere quelle informazioni e tornare indietro di qualche parola e analizzare di nuovo o se manca un tag di fine prova ad aggiungerlo alla fine.

La seconda parte della tua domanda è più semplice ma la tua grammatica non sarà poi così piacevole. In sostanza ciò che si fa è mettere callback su ogni regola, così per esempio

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

Al momento si deve scrivere queste callback linea nella tua grammatica. Al momento sto lavorando molto su questo materiale, quindi potrei fare un richiamo a peg.js per risolverlo. Ma non sono sicuro quando trovo il tempo per farlo.

1

Prova qualcosa come questa regola di sostituzione. Sei sulla strada giusta; devi solo dirlo per assemblare i risultati.

testo = risultato: non_tag + {return result.join (''); }

3

Prima domanda (grammatica per i testi incompleti):

È possibile aggiungere

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

dopoopen_tag e cambiare document includere un tag incompleta alla fine. Il trucco è che tu fornisci il parser con tutte le produzioni necessarie a sempre parse, ma quelle valide vengono prima. Puoi quindi ignorare incomplete_tag durante l'anteprima dal vivo.

Seconda domanda (come includere azioni):

si scrive cosiddetta azioni dopo espressioni. Un'azione è un codice Javascript racchiuso tra parentesi graffe e consentito dopo un'espressione pegjs, i. e. anche nel bel mezzo di una produzione!

In pratica azioni come { return result.join("") } sono quasi sempre necessarie perché pegjs si divide in singoli caratteri. Anche gli array annidati complicati possono essere restituiti. Perciò di solito scrivo le funzioni di aiuto nell'inizializzatore pegjs all'inizio della grammatica per mantenere le azioni piccole. Se si scelgono attentamente i nomi delle funzioni, l'azione è auto-documentante.

Per un esame vedere PEG for Python style indentation. Disclaimer: questa è una mia risposta.