In poche parole
I linguaggi di programmazione sono composti da una sintassi che rappresenta il programma come stringhe di caratteri e una semantica che è il significato previsto del programma.
I linguaggi formali sono sintassi senza significato. Ha lo scopo di studiare la struttura di insiemi di stringhe definiti formalmente, senza di solito attribuire significato a tali stringhe.
L'espressione regolare e altri formalismi (come le grammatiche senza contesto) sono usati per definire linguaggi formali, usati come componente sintattica della programmazione e linguaggi naturali, cioè per rappresentare le frasi in modo strutturato. Altri meccanismi sono usati per mettere in relazione quella struttura con la semantica dei linguaggi di programmazione.
Molto qui è notevolmente semplificato, in particolare per quanto riguarda il linguaggio naturale.
Con molti più dettagli
Per rispondere alla tua domanda dovremmo iniziare dall'inizio. Una lingua nel solito senso è, in modo informale, un mezzo per trasmettere informazioni o idee. In una lingua, di solito si distingue tra sintassi e semantica. La semantica è ciò di cui vuoi parlare / scrivere. le informazioni che si desidera trasmettere. La sintassi è il mezzo che usi per trasmetterla, cioè una rappresentazione convenzionale che può essere scambiata tra persone, e ora anche tra persone e dispositivi, o tra dispositivi (computer).
In genere, userai la parola dog
per trasmettere l'idea di un cane. La parola dog
è composta da tre lettere, o un suono equivalente, e vuole essere la rappresentazione di un tipo di animale. L'idea chiave è che la comunicazione avviene attraverso la rappresentazione di ciò che deve essere comunicato. Le strutture di rappresentazione sono generalmente chiamate sintassi, mentre ciò che viene rappresentato è chiamato semantica. Questo vale più o meno per il linguaggio naturale e per i linguaggi di programmazione.
Le parole sono entità sintattiche per rappresentare concetti semantici più o meno elementari. Ma questi concetti elementari devono essere messi insieme in una varietà di modi per dare un significato più complesso. Scriviamo
the dog
per comunicare che intendiamo un cane specifico e the dog bites the cat
per trasmettere un'idea più complessa. Ma il modo in cui le parole sono organizzate deve essere fissato da regole, in modo da poter dire quale cane e gatto sta effettivamente mordendo l'altro.
Quindi abbiamo regole del genere sentence -> subject verb complement
che dovrebbero corrispondere a frasi e ci dicono come sono articolate le idee associate a ciascuna parte. Queste regole sono regole sintattiche, poiché ci dicono come deve essere organizzata la rappresentazione del nostro messaggio. La subject
stessa può essere definita da una regola subject -> article noun
e così via.
2 x + 1 = 23X123
equation -> expression "=" expression
expression -> expression "+" expression
expression -> number
La struttura dei linguaggi di programmazione è la stessa. I linguaggi di programmazione sono semanticamente specializzati nell'esprimere calcoli da eseguire, piuttosto che esprimere problemi da risolvere, prove di teoremi o relazioni amichevoli tra animali. Ma questa è la differenza principale.
Le rappresentazioni utilizzate nella sintassi sono generalmente stringhe di caratteri o di suoni per le lingue parlate. La semantica di solito appartiene al dominio astratto, o forse alla realtà, ma è ancora astratta nei nostri processi di pensiero, o al dominio comportamentale dei dispositivi. La comunicazione implica la codifica dell'informazione / idea nella sintassi, che viene trasmessa e decodificata dal ricevitore. Il risultato viene quindi interpretato in qualsiasi modo dal destinatario.
Quindi ciò che vediamo del linguaggio è principalmente la sintassi e la sua struttura. L'esempio sopra è solo uno dei modi più comuni per definire le stringhe sintattiche e la loro organizzazione strutturale. Ce ne sono altri Per una determinata lingua, ad alcune stringhe può essere assegnata una struttura e si dice che appartenga alla lingua, mentre altre no.
Lo stesso vale per le parole. Alcune sequenze di lettere (o suoni) sono parole legittime, mentre altre no.
I linguaggi formali sono solo sintassi senza semantica. Definiscono con una serie di regole quali sequenze possono essere costruite, usando gli elementi base di un alfabeto. Quali sono le regole possono essere molto variabili, a volte complesse. Ma i linguaggi formali sono usati per molti scopi matematici oltre la comunicazione linguistica, sia per uso naturale che per linguaggi di programmazione. L'insieme di regole che definiscono le stringhe in una lingua è chiamato grammatica. Ma ci sono molti altri modi per definire le lingue.
In pratica, una lingua è strutturata su due livelli. Il livello lessicale definisce le parole costruite da un alfabeto di caratteri. Il livello sintattico definisce frasi o programmi costruiti da un alfabeto di parole (o più precisamente da famiglie di parole, in modo che rimanga un alfabeto finito). Questo è necessariamente un po 'semplificato.
La struttura delle parole è abbastanza semplice nella maggior parte delle lingue (programmazione o naturale) in modo che siano generalmente definite con quello che è generalmente considerato il tipo più semplice di linguaggio formale: le lingue normali. Possono essere definiti con espressioni regolari (regexp) e sono facilmente identificabili con dispositivi programmati chiamati automi a stati finiti. Nel caso dei linguaggi di programmazione, esempi di una parola sono un identificatore, un numero intero, una stringa, un numero reale, una parola riservata come if
o repeat
, un simbolo di punteggiatura o una parentesi aperta. Esempi di famiglie di parole sono identificatore, stringa, numero intero.
Il livello sintattico è generalmente definito da un tipo leggermente più complesso di linguaggio formale: i linguaggi senza contesto, usando le parole come alfabeto. Le regole che abbiamo visto sopra sono regole senza contesto per il linguaggio naturale. Nel caso dei linguaggi di programmazione le regole possono essere:
statement -> assignment
statement -> loop
loop -> "while" expression "do" statement
assignment -> "identifier" "=" expression
expression -> "identifier"
expression -> "integer"
expression -> expression "operator" expression
Con tali regole puoi scrivere:
while aaa /= bbb do aaa = aaa + bbb / 6
che è una dichiarazione.
E il modo in cui è stato prodotto può essere rappresentato da una struttura ad albero chiamata albero di analisi o albero di sintassi (non completo qui):
statement
|
_______________ loop _______________
/ / \ \
"while" expression "do" statement
__________|_________ |
/ | \ assignment
expression "operator" expression _______|_______
| | | / | \
"identifier" "/=" "identifier" "identifier" "=" expression
| | | |
aaa bbb aaa ... ...
I nomi che appaiono a sinistra di una regola sono chiamati non terminali, mentre le parole sono chiamate anche terminali, in quanto sono nell'alfabeto per la lingua (sopra il livello lessicale). Non terminali rappresentano le diverse strutture sintattiche, che possono essere utilizzate per comporre un programma.
Tali regole sono chiamate senza contesto, perché un non terminale può essere sostituito arbitrariamente usando una delle regole corrispondenti, indipendentemente dal contesto in cui appare. L'insieme di regole che definiscono la lingua è chiamato grammatica senza contesto.
In realtà ci sono delle restrizioni su ciò, quando gli identificatori devono essere dichiarati per la prima volta o quando un'espressione deve soddisfare le restrizioni di tipo. Ma tale restrizione può essere considerata semantica, piuttosto che sintattica. In realtà alcuni professionisti li collocano in quella che chiamano
semantica statica .
Dato qualsiasi frase, qualsiasi programma, il significato di quella frase viene estratto analizzando la struttura fornita dall'albero di analisi per questa frase. Quindi è molto importante sviluppare algoritmi, chiamati parser, in grado di recuperare la struttura ad albero corrispondente a un programma, quando viene dato il programma.
Il parser è preceduto dall'analizzatore lessicale che riconosce le parole e determina la famiglia di appartenenza. Quindi la sequenza di parole, o elementi lessicali, viene data al parser che recupera la struttura ad albero sottostante. Da questa struttura il compilatore può quindi determinare come generare il codice, che è la parte semantica dell'elaborazione del programma sul lato del compilatore.
Il parser di un compilatore può effettivamente costruire una struttura di dati corrispondente all'albero di analisi e passarlo alle fasi successive del processo di compilazione, ma non è necessario. L'esecuzione dell'algoritmo di analisi equivale allo sviluppo di una strategia computazionale per esplorare l'albero di sintassi implicito nel testo del programma. Questo albero di sintassi / analisi può o meno essere esplicitato nel processo, a seconda della strategia di compilazione (numero di fasi). Ciò che è necessario, tuttavia, è che alla fine vi sia almeno un'esplorazione dal basso verso l'alto dell'albero di analisi, esplicitato o lasciato implicito nella struttura di calcolo.
La ragione di ciò, intuitivamente, è che un modo formale standard per definire la semantica associata a una struttura ad albero sintattico è per mezzo di quello che viene chiamato omomorfismo. Non temere la parola grossa. L'idea è solo quella di considerare il significato del tutto costruito dal significato delle parti, sulla base dell'operatore che le collega
Ad esempio, la frase the dog bites the cat
può essere analizzata con la regola sentence -> subject verb complement
. Conoscere il significato dei 3 sottoalberi subject
, verb
e complement
, la regola che li compone ci dice che il soggetto sta facendo l'azione e che il gatto è colui che viene morso.
Questa è solo una spiegazione intuitiva, ma può essere formalizzata. La semantica è costruita verso l'alto dai costituenti. Ma questo nasconde molta complessità.
Il funzionamento interno di un compilatore può essere scomposto in più fasi. Il compilatore effettivo può funzionare fase per fase, utilizzando rappresentazioni intermedie. Potrebbe anche unire alcune fasi. Ciò dipende dalla tecnologia utilizzata e dalla complessità della compilazione del linguaggio a portata di mano.