2012-03-31 15 views
11

Sappiamo tutti che l'analisi dell'HTML mediante espressioni regolari non è possibile in generale, dal momento che analizzerebbe una grammatica sensibile al contesto mentre le espressioni regolari possono solo analizzare grammatiche regolari. Lo stesso è certamente vero per altri linguaggi di programmazione.I linguaggi di programmazione con evidenziazione della sintassi sono possibili utilizzando le espressioni regolari?

Ora, recentemente, è stato annunciato l'evidenziatore di sintassi Rainbow.js. La sua premessa è descritta come molto semplice:

Rainbow da solo è molto semplice. Passa attraverso i blocchi di codice, elabora i modelli regex e avvolge i pattern corrispondenti nei tag.

Ho immaginato che l'evidenziazione della sintassi è essenzialmente un compito della stessa complessità dell'analisi del linguaggio, se assumiamo che sia buono e adatto a molte lingue. Tuttavia, mentre c'è un bel po 'di criticism di quella libreria, né quella né la HackerNews discussion (presa come esempio per una discussione di inclini tecnicamente) hanno menzionato che evidenziare la sintassi usando le espressioni regolari è praticamente impossibile in un caso generale, che io Considererei un difetto importante e ostentato.

Ora la domanda è: c'è qualcosa che mi manca? In particolare:

  1. L'evidenziazione della sintassi con le espressioni regolari è possibile in generale?
  2. Si tratta di un'istanza di una regola 80/20 applicata, in cui è sufficiente quanto basta con le espressioni regolari per essere utile?

risposta

3

È possibile eseguire l'evidenziazione della sintassi utilizzando le espressioni regolari come parte della soluzione. Più specificamente, come parte del "lexer" che suddivide il testo di input in simboli. Questo è in realtà il modo in cui lavorano la maggior parte dei compilatori/interpreti.

Per farlo utilizzando regex da solo, tuttavia, è in difficoltà. Considera il caso dell'abbinamento di una stringa in Python. Python consente di delimitare le stringhe tra virgolette singole ' o virgolette doppie ". Inoltre, consente le stringhe a più righe ("sintassi heredoc") utilizzando le virgolette, ''' o """.

Quindi quali parti delle seguenti sono stringhe e quali no? Puoi costruire un'espressione regolare che identifica correttamente le stringhe letterali str1 - str6?

str1 = "hello, world!" 

str2 = 'hello, world!' 

str3 = "The canonical test program is 'Hello World'." 

str4 = '"Why," Peter said, "That\'s ludicrous. Who would do that?"' 

str5 = """The heredoc syntax is handy for cases where you don't want to escape strings. "Very convenient." 
""" 

str6 = """Code sample: 
s1 = "hi!" 
s2 = 'Hi!' 
S3 = ''' 
- apples 
- oranges 
- bananas 
''' 
""" 

L'argomento che "non è possibile (analizzare HTML | programmi di processo) con regex perché (HTML | linguaggi di programmazione) hanno nidificato strutture - non sono regolare" non è del tutto vero - moderno le espressioni regolari (in particolare in Perl) hanno un potere espressivo maggiore delle espressioni regolari nel senso informatico-scientifico. Ma solo perché è can use regular expressions non significa che lo dovrebbe essere.


Edit: il problema della stringa di corrispondenza di cui sopra non è male se il sapore regex supporta backreference nel modello di ricerca. Una regex multilinea come ('|"|'''|""").+?\1 probabilmente farebbe.


Edit 2: Per un esempio dei casi d'angolo nella sintassi hilighting, guardare oltre l'evidenziazione della sintassi di StackOverflow del codice di cui sopra.

+0

Giusto, perché un lexer definisce una grammatica context-free (CFG), ma un'espressione regolare può solo definire una grammatica regolare (RG). – Daniel

+1

L'uso di un "lexer" non implica necessariamente una grammatica context-free - l'analisi lessicale è qualcosa che puoi fare per tutti i tipi di linguaggi. Detto questo, la maggior parte dei linguaggi di programmazione ha grammatiche senza contesto. –

+0

Dubito che abbiate bisogno di qualche caratteristica di fantasia non inclusa nella definizione matematica del 19 ° secolo delle regex. Il back-reference che date può essere facilmente sostituito perché può essere solo una volta su quattro cose: '('...') | (" ... ") | ('' '...' '') | ("" "... """) '. Ciò che precisamente può andare in ogni tipo di stringa è descritto nel [riferimento al linguaggio Python] (http://docs.python.org/reference/lexical_analysis.html#string-literals) e sembra abbastanza semplice da tradurre in espressioni regolari. – delnan

2

Fondamentalmente, no.

È necessario un parser/tokenizer in grado di comprendere la lingua per individuare i bit da evidenziare.

Regex non taglia la senape per tale compito.

+3

-1 Errore: per l'evidenziazione della sintassi, non è necessario * analizzare * la lingua, è sufficiente * tokenize * it.Sai, scopri quale parte è una stringa letterale o identificativa o un intero letterale o una parola chiave. Se ci sono parenti attorno ad esso, e se sono bilanciati, non è rilevante. – delnan

+2

Mi dispiace, questo è falso. L'evidenziazione della sintassi ha bisogno di riconoscere i costrutti di nidificazione. Dipende dalla lingua Per alcune lingue, l'identificazione di token non è sufficiente. Inoltre, l'evidenziazione della sintassi riconosce le parentesi sbilanciate: ad es. Vim. – Kaz

+2

@delnan Non sono d'accordo, perché dipende dal tipo di evidenziazione che si desidera. Se sono solo le parole chiave OK tokenize, ma se si desidera evidenziare una parentesi graffa corrispondente o comprimere un metodo (come fa Eclipse), è necessario conoscere la lingua. Inoltre, "parse" e "tokenize" hanno un significato abbastanza vicino, specialmente quando si confronta con l'espressione regolare. Ho modificato la mia risposta per includere "tokenize" e così facendo non credo cambi il significato fondamentale della mia risposta. Inoltre, leggi la risposta più votata, in pratica dice quello che sto dicendo. – Bohemian

11

L'evidenziazione della sintassi mediante regexp è un'arte vecchia. Penso che persino Emacs e Vi siano partiti in questo modo.

ho pensato che l'evidenziazione della sintassi è essenzialmente un compito della stessa complessità di analisi lingua, [...]

No. La differenza è: Il compilatore ha bisogno di vera analisi perché ha bisogno di capire il programma completo e ha anche bisogno di generare cose da quella comprensione. L'evidenziazione della sintassi sulle altre mani non ha bisogno di capire il codice. Ha solo bisogno di capire la struttura generale della lingua - cosa sono i letterali stringa - quali sono le parole chiave ... e così via. Un effetto collaterale di questa differenza è: è possibile evidenziare il codice che è sintatticamente errato, ma non è possibile analizzarlo.

un approccio leggermente diverso a questo: l'analisi di una lingua è spesso un processo in due fasi: lexing (frazionamento del flusso di byte in un flusso di "token") e reale analisi (portare il flusso di token in qualche struttura complessa - spesso un Abstract Syntax Tree). Lexing viene solitamente fatto usando ---- espressioni regolari. Vedi i documenti flessibili per questo. E questo è fondamentalmente tutto ciò che un evidenziatore di sintassi di base deve capire.

Ovviamente ci sono casi d'angolo che regexp da soli non possono catturare. Un esempio tipico è:

foo(bla, bar); 

Qui foo potrebbe essere una chiamata ad un metodo statico o di un metodo di istanza o di una macro o qualcos'altro. Ma il tuo Evidenziatore di espressioni regolari non può dedurlo. Può solo aggiungere colori per una "chiamata generale".

Quindi: questa è una regola del 100/0 percento se i requisiti sono di basso livello (vale a dire senza l'esempio precedente) e in genere una regola 90/10 per le cose del mondo reale.

+0

+1. Tutti punti eccellenti. –

1

Un buon esempio da osservare è l'implementazione dell'evidenziazione della sintassi in Vim. Usa schemi che sono basati su espressioni regolari. Tuttavia, i pattern vengono utilizzati per riconoscere strutture di contenimento gerarchiche nel documento e non semplicemente per tokenizzare l'input.

È possibile dichiarare le regioni che iniziano e terminano con una corrispondenza di modello di espressioni regolari (più un altro motivo che consente di saltare il materiale intermedio). Queste regioni possono dichiarare di contenere altre regioni o modelli semplici. Il contenimento può essere ricorsivo. Vim funziona tutto questo. Quindi è essenzialmente una forma di analisi senza contesto.

Questo approccio può gestire lingue con vari livelli di incorporamento, con diverse proprietà lessicali.

Ad esempio, ho una lingua in cui ci sono essenzialmente due gruppi di parole chiave (a causa di un incorporamento della lingua del dominio in corso). Le regole di evidenziazione della sintassi Vim che ho scritto riconoscono correttamente il contesto e colorano le parole chiave in modo diverso.Si noti che c'è una certa sovrapposizione tra questi insiemi di parole chiave: stessa parola, significato diverso in un contesto diverso.

Per un esempio di questo vedere: http://www.kylheku.com/cgit/txr/tree/genman.txr. Se si cerca la sintassi (do, si scoprirà che un'istanza è colorata in viola e un'altra in verde. Sono diversi: uno è in un linguaggio di estrazione del testo e un altro in un dialetto Lisp incorporato. L'evidenziazione della sintassi di Vim è abbastanza potente da gestire una combinazione di lingue con diversi gruppi di parole chiave. (Sì, sebbene questo sia pubblicato sul web, è in realtà un processo Vim che esegue l'evidenziazione della sintassi.)

Oppure considerare qualcosa come la shell, dove è possibile avere una sintassi del tipo letterale stringa, come "foo bar", ma al suo interno, puoi avere una sostituzione di comando, all'interno della quale devi ricorsivamente riconoscere e colorare la sintassi della shell: "foo $(for x in *; do ...; done) bar".

Quindi no, non è possibile fare un higlight di sintassi utile e accurato solo con la tokenizzazione delle espressioni regolari, ma l'espressione regolare con l'analisi gerarchica può fare un buon lavoro.

Problemi correlati