Ci sono così tanti linguaggi di programmazione che supportano l'inserimento di mini-lingue. PHP è incorporato in HTML. XML può essere incorporato in JavaScript. Linq può essere incorporato in C#. Le espressioni regolari possono essere incorporate in Perl.componibili Grammatiche
// JavaScript example
var a = <node><child/></node>
Vieni a pensarci, la maggior parte dei linguaggi di programmazione può essere modellata come mini-lingue diverse. Java, per esempio, potrebbe essere suddiviso in un almeno quattro mini-lingue distinte:
- A langauge di dichiarazione del tipo (direttiva pacchetto, le direttive di importazione, dichiarazione della classe)
- Un linguaggio membro-dichiarazione (modificatori di accesso, dichiarazioni di metodo, membro vars)
- Un linguaggio economico (flusso di controllo, l'esecuzione sequenziale)
- Un linguaggio espressione (letterali, assegnazioni, confronti, aritmetica)
Potendo implementare quei quattro linguaggi concettuali come quattro grammatiche distinte certamente ridurrebbe il molto dello spaghettiism che vedo solitamente nelle implementazioni di parser e compilatori complessi.
Ho implementato parser per vari tipi di linguaggi (utilizzando ANTLR, JavaCC e parser di discesa ricorsivi personalizzati), e quando la lingua diventa davvero grande e complessa, di solito si finisce con una grammatica huuuuuuuge, e l'implementazione del parser diventa davvero brutta davvero veloce.
Idealmente, quando si scrive un parser per una di queste lingue, sarebbe bello per la sua attuazione come una raccolta di parser componibili, passando il controllo avanti e indietro tra di loro.
La cosa complicata è che spesso il langeuge di contenimento (ad es. Perl) definisce il proprio terminus sentinel per la lingua contenuta (ad es. Espressioni regolari). Ecco un buon esempio:
my $result ~= m|abc.*xyz|i;
In questo codice, il codice Perl principale definisce un terminale non standard "|" per l'espressione regolare. Implementare il parser di espressioni regolari come completamente distinto dal parser perl sarebbe davvero difficile, perché il parser di espressioni regolari non saprebbe come trovare il terminale di espressione senza consultare il parser genitore.
Oppure, permette di dire che ho avuto una lingua che ha permesso l'inserimento di espressioni Linq, ma invece di terminare con un punto e virgola (come C# fa), ho voluto affidare le espressioni Linq appaiono tra parentesi quadre:
var linq_expression = [from n in numbers where n < 5 select n]
Se ho definito la grammatica Linq all'interno della grammatica di lingua madre, ho potuto facilmente scrivere una produzione univoca per un "LinqExpression" utilizzando lookahead sintattica per trovare le recinzioni della staffa. Ma poi la mia grammatica genitoriale dovrebbe assorbire l'intera specifica Linq. E questa è una resistenza. D'altra parte, un parser Linq figlio separato avrebbe un tempo molto difficile capire dove fermarsi, perché avrebbe bisogno di implementare lookahead per i tipi di token stranieri.
E questo praticamente escluderebbe l'utilizzo di fasi separate di lexing/parsing, poiché il parser Linq definirà un intero insieme di regole di tokenizzazione diverso dal parser padre. Se stai effettuando la scansione di un token alla volta, come fai a sapere quando passare il controllo all'analizzatore lessicale della lingua madre?
Cosa ne pensate?Quali sono le migliori tecniche oggi disponibili per l'implementazione di grammatiche linguistiche distinte, disaccoppiate e componibili per l'inclusione di mini-lingue all'interno di lingue madri più grandi?
OMeta ha questo! Puoi comporre più grammatiche insieme o persino ereditare grammatiche esistenti in stile OOP. – CMCDragonkai