Es: Fortran (applicazioni scientifiche, ingegneristiche), Cobol

Transcript

Es: Fortran (applicazioni scientifiche, ingegneristiche), Cobol
LINGUAGGI DI PROGRAMMAZIONE
LINGUAGGI DI BASSO LIVELLO
Linguaggio macchina
Uno per ogni processore o famiglia di processori
Linguaggio assembler
Versione simbolica di quello macchina.
LINGUAGGI DI ALTO LIVELLO
Linguaggi imperativi
Orientati all’assegnamento di locazioni di memoria o di registri.
Es: Fortran (applicazioni scientifiche, ingegneristiche), Cobol
(applicazioni gestionali) , Pascal, C , Basic.
Linguaggi imperativi orientati agli oggetti
Basati sulle “classi”, che sono una evoluzione delle “strutture”; le
classi incorporano sia i dati che le funzioni che operano su di essi.
Es: Smalltalk, C++, Java.
Linguaggi funzionali
Basati sulla combinazione di funzioni per ottenerne di più potenti.
Es: Lisp, CommonLisp, Scheme.
Linguaggi logici
Es: Prolog
SQL
usato in Intelligenza Artificiale
orientato al reperimento di informazione nei databases;
specifica le proprietà logiche dell’operazione da
eseguire e dell’informazione su cui agire.
Programmazione imperativa
(procedurale, strutturata, orientata agli oggetti)
Programmazione dichiarativa
(funzionale, logica)
PROGRAMMAZIONE VISUALE
Orientata alla possibilità di scrivere programmi partendo da oggetti
posizionati sullo schermo ai quali sono associate azioni da compiere.
Permette di scrivere in modo relativamente semplice programmi orientati
agli oggetti; si scelgono alcune scatole, dei collegamenti con I/O
appropriato, ecc.
Ci sono ambienti di programmazione che consentono l’uso più o meno
esteso della programmazione visuale: Visual Basic, Hypercard,
SQLWindows, J-Builder.
1
SVILUPPO DI UN PROGRAMMA TRADIZIONALE
Redazione
Con un editor che visualizza i caratteri stampabili ed interpreta quelli
non stampabili; gli editor possono essere sensibili al linguggio di
programmazione.
Traduzione
- Compilatori
ma può avvenire anche con:
- Interpreti
- Compilatori just in time (CJT)
- Soluzioni ibride
Sorgente pascal
Sorgente C
Compilatore pascal
Sorgente assembler
compilatore C
assemblatore
Files di testo
sorgenti
non eseguibili
COMP/ASSEMB
Files binari
oggetto
non eseguibili
LINKING
File binario
eseguibile
LOADING
in memoria principale
2
COMPILATORI
•
Un compilatore è un programma che legge un programma scritto in
un linguaggio, detto linguaggio sorgente, e lo traduce in un
programma equivalente scritto in un altro linguaggio, detto
linguaggio target. Come parte importante del processo di traduzione
il compilatore comunica all'utente la presenza di errori nel
programma sorgente.
•
Ci sono migliaia di linguaggi sorgenti e migliaia di linguaggi target,
ma i compiti di base che un compilatore deve compiere sono
essenzialmente gli stessi.
•
Concettualmente un compilatore opera in fasi, ciascuna delle quali
trasforma il programma sorgente da una rappresentazione in un'altra.
•
Oltre al compilatore, altri programmi possono essere necessari per
creare un programma eseguibile.
•
Preprocessori. Producono input per i compilatori. Essi possono
completare il programma sorgente, ad esempio includendo il
contenuto di alcuni files (come in C con #include <stdio.h>),
espandere delle abbreviazioni, dette macro, in statements del
linguaggio sorgente, permettere costrutti tipo if-statements per
specificare condizioni di compilazione, ecc.
•
Assemblatori. Un compilatore può creare un programma in
linguaggio assembler, che successivamente deve essere tradotto da un
assemblatore in codice macchina. Altri compilatori possono invece
eseguire il compito dell'assemblatore, creando loro stessi codice
macchina.
•
Caricatori. Il processo di linking consiste nel creare un unico
programma a partire da diversi files di codice macchina rilocabile;
questi files possono essere il risultato di diverse compilazioni ed uno
o più possono essere files di librerie di routines fornite dal sistema. Il
processo di loading consiste nel prendere codice macchina rilocabile,
e caricare istruzioni e dati in memoria all'indirizzo di caricamento
desiderato. Spesso un unico programma, chiamato caricatore, esegue
la duplice funzione di linking/loading.
•
Il comando UNIX gcc serve sia per compilare che per legare e
caricare i programmi C. Il compilatore genera un modulo oggetto,
chiamato object file (nomefile.o), che può essere processato dal
linker insieme ad altri object files. Il linker crea un modulo eseguibile
il cui nome, se non specificato diversamente, è a.out.
3
FASI DI UN COMPILATORE
Tre sono di analisi, tre di sintesi e due di interazione con le altre.
source program
analizzatore lessicale
analizzatore sintattico
analyzer
gestore della
tabella dei simboli
analizzatore semantico
analyzer
generatore del codice intermedio
gestore degli
errori
ottimizzatore del codice
generatore del codice
generator
target program
ANALISI LESSICALE
Durante questa fase la sequenza di caratteri che costituisce il programma
viene letta da sinistra verso destra e raggruppata in sequenze di caratteri
(chiamate "token ") che hanno un senso logico globale, come un
identificatore, una parola chiave, un simbolo di interpunzione.
Es. la sequenza di caratteri nell'istruzione:
position = initial + rate * 60
sarebbe raggruppata nei seguenti "tokens" :
1.
2.
3.
4.
5.
6.
7.
Identificatore
Simbolo di assegnamento
Identificatore
Segno
Identificatore
Segno
Numero intero
position
=
initial
+
rate
*
60
I caratteri “white” che separano i "token" vengono di solito eliminati
durante questa fase.
La parte di compilatore che fa questa analisi si chiama scanner e lavora
usando tecniche di teoria degli automi finiti.
4
ANALISI SINTATTICA
È chiamata anche parsing (e parser si chiame la parte di compilatore
che compie questa analisi) e consiste nel raggruppare i tokens in costrutti
grammaticali che sono usati dal compilatore per sintetizzare l'output.
Di solito i costrutti grammaticali sono rappresentati da un albero
dell'analisi, così lo scopo del parser per lo più è quello di costruire il
parse tree.
Es. Un possibile albero dell'analisi (parse tree) per l'istruzione in esame è:
assignment statement
=
identifier
position
expression
expression
+
|
identifier
|
initial
expression
expression
|
expression
*
|
identifier
number
|
|
rate
60
Un'altra comune rappresentazione della struttura sintattica dell'input è
data dall'albero della sintassi (sintax tree), che è una forma compressa del
parse tree nel quale gli operatori appaiono come nodi interni e gli
operandi di un operatore sono i figli del nodo di quell'operatore. Un
esempio di sintax tree per l'istruzione precedente è:
=
+
position
initial
5
*
rate
60
ANALISI SEMANTICA
Questa fase usa la struttura gerarchica determinata dalla fase precedente
per identificare e controllare gli operatori e gli operandi delle espressioni
e delle istruzioni ed identificare eventuali errori semantici nel programma
sorgente. Compito principale di questa fase è il controllo dei tipi, cioè il
controllo che ogni operatore abbia operandi permessi dalla specifica del
linguaggio.
Es. Nell'istruzione precedente, se tutti gli identificatori fossero stati
dichiarati float, l'approccio sarebbe quello di convertire l'intero 60 in un
float ed il compilatore rimpiazzerebbe la costante intera con una float
equivalente.
=
+
position
*
initial
rate
intofloat
60
GESTIONE DELLA TABELLA DEI SIMBOLI
Scopo della gestione della tabella dei simboli è quello di memorizzare
tutti gli identificatori usati nel programma sorgente e di collezionare gli
attributi di ogni identificatore (la memoria allocata, il tipo, dove è valido
nel programma, se è il nome di una procedura ed in tal caso il numero ed i
tipi degli argomenti oltre al metodo di passaggio dei parametri ed al tipo
del valore ritornato). Quando un identificatore viene individuato nel
programma sorgente dall' analizzatore lessicale esso viene messo nella
tabella dei simboli; i vari attributi vengono aggiunti via via che vengono
collezionati dalle varie fasi.
RILEVAMENTO E COMUNICAZIONE DEGLI ERRORI
Ogni fase può trovare errori e comunicarli; il processo di compilazione
prosegue alla ricerca del maggior numero di errori, senza interrompersi
dopo ogni errore.
6
GENERAZIONE DEL CODICE INTERMEDIO
Si può pensare alla rappresentazione intermedia come ad un programma
per una macchina astratta. La forma intermedia deve essere facile da
produrre a facile da tradurre nel linguaggio target. Di forme intermedie
ne esistono una grande varietà; quella chiamata "codice a tre indirizzi" è
simile ad un linguaggio macchina dove ogni locazione di memoria può
agire come un registro.
OTTIMIZZAZIONE DEL CODICE
Cerca di migliorare il codice intermedio per poter ottenere codice finale
più veloce. Nei cosiddetti "compilatori ottimizzanti" una parte
significativa del tempo di compilazione è spesa in questa fase. Tuttavia ci
sono semplici ottimizzazioni che non rallentano troppo la compilazione e
che migliorano sensibilmente il tempo di esecuzione del programma
target.
GENERAZIONE DEL CODICE
È la fase finale che genera il codice target, che di solito è codice
macchina rilocabile o codice assembler. Locazioni di memoria sono
scelte per ciascuna variabile del programma. Poi le istruzioni intermedie
sono tradotte in una sequenza di istruzioni macchina equivalenti. Aspetto
cruciale di questa fase è l'assegnazione dei registri.
7
ESEMPIO DI TRADUZIONE DI UNA ISTRUZIONE
position = initial + rate * 60
lexical analyzer
id1 = id2 + id3 * 60
syntax analyzer
=
+
id1
*
id2
id3
60
semantic analyzer
SYMBOL TABLE
position
initial
rate
....
....
....
=
+
id1
*
id2
id3
intofloat
60
intermediate code generator
temp1=intoreal(60)
temp2=id3 * temp1
temp3=id2 + temp2
id1=temp3
code optimizer
temp=id3 * 60.0
id1=id2+temp
code generator
MOVF
MULF
MOVF
ADDF
MOVF
8
id3, R2
#60.0, R2
id2,R1
R2, R1
R1,id1
COMPILER-COMPILER
Regole
lessicali
SCANNER
Grammatica
PARSER
COMPILER-COMPILER
GENERATORE
DEL CODICE
Routines
semantiche
I compilatori generati con questa tecnica sono più grossi e compilano più
lentamente di quelli scritti a mano, ma il codice generato è più veloce.
INTERPRETI
Un interprete processa un programma sorgente come un compilatore
(esegue analisi lessicale e sintattica ed eventualmente traduce in una
forma interna, tipo quadruple od una estensione della forma polacca
postfissa) ma lo esegue direttamente, cioè senza tradurre in codice
macchina.
L’interprete può essere visto come un insieme di funzioni la cui
esecuzione è giudata dalla forma interna del programma.
La fase di traduzione è più veloce che nei compilatori, ma l’esecuzione è
più lenta.
È vantaggioso in fase di debugging, poichè si hanno in linea tutte le
tabelle.
La maggior parte dei linguaggi può essere sia interpretata che compilata.
COMPILATORI JUST ON TIME (CJT)
Sono un compromesso tra compilatori e interpreti.
SOLUZIONI IBRIDE
Le applet java, copiate via rete, contengono una compilazione del codice
sorgente, chiamato byte-code; esso costituisce codice macchina
“generale”, cioè indipendente da specifici processori e/o architetture. Al
momento dell’ esecuzione il byte-code viene interpretato o compilato da
parte di una JVM (Java Virtual Machine) e poi eseguito. Alcuni
calcolatori hanno un processore in grado di eseguire il byte-code
direttamente.
9