Il progetto finale consiste nell'implementazione di un'estensione del linguaggio sviluppato durante gli ultimi laboratori Java; può quindi essere usata come base di partenza la soluzione proposta per l'ultimo laboratorio. È comunque richiesto che le implementazioni della semantica statica e dinamica siano basate sul visitor pattern.
L'interfaccia da linea di comando per interagire con l'interprete è la stessa utilizzata nei laboratori finali:
- il programma da eseguire può essere letto da un file di testo
<filename>con l’opzione-i <filename>, altrimenti viene letto dallo standard input - l'output del programma in esecuzione può essere salvato su un file di testo
<filename>con l’opzione-o <filename>, altrimenti viene usato lo standard output - l’opzione
-ntc(abbreviazione di no-type-checking) permette di eseguire il programma senza effettuare prima il controllo di semantica statica del typechecker
Il linguaggio contiene le nuove parole chiave for e of e i nuovi simboli [, ] e :.
La sintassi del linguaggio è definita da questa grammatica non ambigua e in forma EBNF:
Prog ::= StmtSeq EOF
StmtSeq ::= Stmt (';' StmtSeq)?
Stmt ::= 'var'? IDENT '=' Exp | 'print' Exp | 'if' '(' Exp ')' Block ('else' Block)? | 'for' '(' 'var' IDENT 'of Exp ')' Block
Block ::= '{' StmtSeq '}'
Exp ::= And (',' And)*
And ::= Eq ('&&' Eq)*
Eq ::= Add ('==' Add)*
Add ::= Mul ('+' Mul)*
Mul::= Unary ('*' Unary)*
Unary ::= 'fst' Unary | 'snd' Unary | '-' Unary | '!' Unary | Dict
Dict ::= Atom ('[' Exp (':' Exp?)? ']')*
Atom :: = '[' Exp ':' Exp ']' | BOOL | NUM | IDENT | '(' Exp ')'
La grammatica non richiede trasformazioni e può essere utilizzata così com'è per sviluppare un parser con un solo token di lookahead.
Rispetto al linguaggio del laboratorio, sono stati aggiunti
- il literal di tipo
dict(dizionario)'[' Exp ':' Exp ']' - due operatori binari sui dizionari
- accesso (get)
Exp '[' Exp ']' - cancellazione (delete)
Exp '[' Exp ':' ']'
- accesso (get)
- l'operatore ternario di aggiornamento (update) di un dizionario
Exp '[' Exp ':' Exp ']' - lo statement
'for' '(' 'var' IDENT 'of' Exp ')' Block
Direttamente dalla definizione della grammatica si deduce che tutti gli operatori sui dizionari (get, delete e update) hanno lo stesso livello di priorità,
associano da sinistra e hanno la precedenza sugli operatori unari fst, snd, - e !.
Per esempio, l'espressione [0:1][1:2][0:][1] è sintatticamente corretta e corrisponde alla sequenza di operazioni di update, delete e get (nell'ordine da sinistra a destra) sul literal [0:1].
Per ulteriori chiarimenti potete fare riferimento ai programmi in tests/success e tests/failure/syntax.
La semantica statica e dinamica del linguaggio sono definite in F# nel file semantics/Semantics.fs.
Il file semantics/Program.fs contiene un semplice esempio di programma corrispondente a tests/success/prog01.txt.
Esso può essere eseguito con il comando dotnet run.
Il costruttore DictType of staticType corrisponde al nuovo tipo dict dei dizionari costruiti a partire dal literal '[' Exp ':' Exp ']'.
Tutti i dizionari devono avere chiavi di tipo IntType, ma i valori associati alle chiavi possono essere di qualsiasi tipo, purché omogeneo
all'interno di uno stesso dizionario.
Per esempio, DictType BoolType è il tipo dei dizionari che associano alle chiavi valori booleani;
DictType(DictType(PairType(IntType,IntType))) è il tipo dei dizionari che associano alle chiavi valori che, a loro volta,
sono dizionari e associano alle chiavi coppie di numeri interi.
- se
Exp1ha tipoIntTypeeExp2ha tipoty, allora il literal'[' Exp1 ':' Exp2 ']'ha tipoDictType ty - se
Exp1ha tipoDictType tyeExp2ha tipoIntType, allora l'espressioneExp1 '[' Exp2 ':' ']'ha tipoDictType ty - se
Exp1ha tipoDictType tyeExp2ha tipoIntType, allora l'espressioneExp1 '[' Exp2 ']'ha tipoty - se
Exp1ha tipoDictType ty,Exp2ha tipoIntTypeeExp3ha tipotyallora l'espressioneExp1 '[' Exp2 ':' Exp3 ']'ha tipoDictType ty - se
Expha tipoDictType tyrispetto all'ambiente statico correnteenveBlockè corretto rispetto all'ambiente ottenuto aggiungendo aenvun nuovo scope annidato dove l'unica variabile dichiarata èIDENTdi tipoPairType(IntType,ty), allora lo statement'for' '(' 'var' IDENT 'of' Exp ')' Blockè corretto e l'ambiente correnteenvnon viene modificato
Per ulteriori chiarimenti potete fare riferimento ai programmi in tests/success, tests/failure/static-semantics e tests/failure/static-semantics-only.
Il costruttore DictValue of Map<int, value> rappresenta i valori di tipo dizionario attraverso il tipo predefinito in F# Map<int, value> delle mappe
con chiavi di tipo int e valori di tipo value.
Note importanti sui valori di tipo dizionario
-
Le chiavi risultano ordinate secondo l'ordine naturale dei numeri interi.
Nell'implementazione in Java si consiglia di usare la classe predefinita
TreeMap<K,V>del packagejava.util. -
Diversamente dai requisiti della semantica statica, la semantica dinamica non impone che un dizionario debba contenere valori di tipo omogeneo.
Per esempio, il programma
print [0:1][1:true][1]stampatruese eseguito con l'opzione-ntc(no-type-checking).
-
se
Exp1si valuta in un valore di tipo interokeyeExp2in un valoreval, allora l'espressione'[' Exp1 ':' Exp2 ']'si valuta in un nuovo dizionario con l'unica chiavekeye il valorevalassociato a essa. Viene sollevata un'eccezione sekeynon è un valore di tipo intero -
se
Exp1si valuta in un dizionariodicteExp2in un valore di tipo interokey, allora l'espressioneExp1 '[' Exp2 ':' ']'si valuta in un nuovo dizionariodict'che contiene le stesse chiavi e valori associati indict, eccetto la chiavekeyche non è presente indict'. Viene sollevata un'eccezione sedictnon è un valore di tipo dizionario, oppurekeynon è un valore di tipo intero, oppurekeynon è una chiave presente nel dizionariodict -
se
Exp1si valuta in un dizionariodicteExp2in un valore di tipo interokey, allora l'espressioneExp1 '[' Exp2 ']'si valuta nel valore associato alla chiavekeynel dizionariodict. Viene sollevata un'eccezione sedictnon è un valore di tipo dizionario, oppurekeynon è un valore di tipo intero, oppurekeynon è una chiave presente nel dizionariodict -
se
Exp1si valuta in un dizionariodict,Exp2in un valore di tipo interokeyeExp3in un valoreval, allora l'espressioneExp1 '[' Exp2 ':' Exp3 ']'si valuta in un nuovo dizionariodict'che contiene le stesse chiavi e valori associati indict, eccetto la chiavekeyche è associata avalindict'. Viene sollevata un'eccezione sedictnon è un valore di tipo dizionario oppurekeynon è un valore di tipo intero -
se
Expsi valuta in un dizionariodict, allora l'esecuzione dello statement'print' Expnon modifica l'ambiente dinamico e stampa tra parantesi quadre tutte le associazionikey:valueindictnell'ordine crescente delle chiavi e separate da una virgola senza alcun spazio. Per esempioprint [3:5,0][2:4,1][1:3,2]stampa la stringa[1:(3,2),2:(4,1),3:(5,0)] -
se
Expsi valuta in un dizionariodict, allora lo statement'for' '(' 'var' IDENT 'of' Exp ')' Blockesegue iterativamente per ogni coppia(key,value)indictil bloccoBlockassegnando prima tale coppia alla variabileIDENT. Quindi il numero totale di cicli eseguiti coincide con il numero di chiavi contenute indict.L'ambiente dinamico rispetto al quale viene eseguito
blockper la prima volta (assumendo chedictnon sia vuoto) è ottenuto aggiungendo all'ambiente corrente un nuovo scope annidato dove l'unica variabile dichiarata èIDENT, inizializzata con un qualsiasi valore (per esempio 0).Per le iterazioni successive alla prima (se
dictha più di una chiave), l'ambiente dinamico rispetto al quale viene eseguitoblockè ottenuto aggiornando nell'ambiente risultante dopo l'esecuzione diblockal ciclo precedente il valore della variabileIDENTcon la coppia(key,value)didictconsiderata nel ciclo corrente.L'ambiente dopo l'esecuzione del
'for'corrisponde a quello ottenuto dall'esecuzione diblocknell'ultima iterazione dove lo scope più annidato che contiene la variabileIDENTdel'for'è stato eliminato.Se
dictè vuoto, allora l'esecuzione del'for'non ha alcun effetto sull'ambiente.Note importanti sull'esecuzione dello statement
'for'-
l'espressione
expviene valutata una volta sola, all'inizio dell'esecuzione del'for' -
l'esecuzione del
'for'considera le coppie(key,value)del dizionario rispetto all'ordine crescente delle chiaviEsempio: l'esecuzione del programma
for(var p of [3:5][2:4][1:3]){print p}stampa
(1,3) (2,4) (3,5)Il file
tests/success/prog08.txtcontiene un esempio simile.
Nota importante sulla semantica dinamica degli operatori su dizionari
Gli operatori su dizionari sono funzionali, quindi la loro valutazione non può modificare valori di tipo dizionario già presenti nell'ambiente.
Per esempio il programma nel file
tests/success/prog05.txtstampatrue:var d1=[1:3]; var d2=d1[1:4]; print d1[1]==3&&d2[1]==4 // prints true -
semantics/Semantica.fs: semantica statica e dinamica del linguaggio definita in F#semantics/Program.fs: contiene un semplice esempio di programma tradotto in F#, corrispondente atests/success/prog01.txt, eseguibile rispetto alla semantica definita insemantics/Semantics.fstests/success: test corretti anche senza l'opzione-ntctests/failure/syntax: test con errori di sintassitests/failure/static-semantics: test con errori statici senza l'opzione-ntced errori dinamici con l'opzione-ntctests/failure/static-semantics-only: test con errori statici senza l'opzione-ntce corretti con l'opzione-ntctests/failure/dynamic-semantics: test che generano errori dinamici con o senza l'opzione-ntc
-
La consegna è valida solo se il progetto passa tutti i test contenuti nel folder
tests; la valutazione del progetto tiene conto dell'esecuzione di test aggiuntivi e della qualità del codice -
È possibile consegnare il progetto alcuni giorni prima delle date delle prove scritte. Il calendario delle scadenze per le consegne è: 17 giugno, 12 luglio, 6 settembre, 13 gennaio e 31 gennaio.
Dopo ogni scadenza, vengono corretti tutti i progetti consegnati e pubblicati i relativi risultati prima che le consegne siano riaperte.
Dopo l'ultima scadenza del 31 gennaio non sarà più possibile consegnare progetti validi per l'anno accademico in corso
-
Il progetto può essere consegnato anche se l'esame scritto non è stato ancora superato
-
Dopo il commit (e push) finale del progetto su GitHub, la consegna va segnalata da un singolo componente del gruppo utilizzando AulaWeb e indicando il numero del gruppo definito nell'elenco su AulaWeb
-
Per ricevere supporto durante lo sviluppo del progetto è consigliabile tenere sempre aggiornato il codice del progetto sul repository GitHub
-
Dopo che il progetto è stato valutato positivamente, il relativo colloquio individuale può essere sostenuto anche se l'esame scritto non è stato ancora superato; esso ha lo scopo di verificare che ogni componente del gruppo abbia compreso il funzionamento del codice e abbia contribuito attivamente al suo sviluppo
-
L'OpenBadge Soft skills - Sociale base 1 - A verrà assegnato ai componenti del gruppo solo se tutti avranno superato positivamente (ossia senza decremento del punteggio) il colloquio individuale
-
Per ulteriori informazioni consultare la pagina AulaWeb sulle modalità di esame