Svar:
heltal: [0-9]+
personnummer: [0-9][0-9][0-1][0-9][0-3][0-9]-[0-9][0-9][0-9][0-9]
b) Vilka andra terminaler, förutom heltal och personnummer behövs för att man ska kunna skriva en grammatik för språket?
Svar:
Nyckelorden person, prioritet, vaccinerat, lista, alla, vaccinerade, ovaccinerade och avsluta, samt radslut
Svar (filen vaccinering.l):
%{ #include "vaccinering.tab.h" %} %% [\t ] { /* Ignorera whitespace */ } \n { return RADSLUT; } "person" { return PERSON; } "prioritet" { return PRIORITET; } "vaccinerat" { return VACCINERAT; } "lista" { return LISTA; } "alla" { return ALLA; } "vaccinerade" { return VACCINERADE; } "ovaccinerade" { return OVACCINERADE; } "avsluta" { return AVSLUTA; } [0-9][0-9][0-1][0-9][0-3][0-9]-[0-9][0-9][0-9][0-9] { return PERSONNUMMER; } [0-9]+ { /* printf("[HELTAL: '%s']", yytext); */ return HELTAL; } . { fprintf(stderr, "[Ignorerar felaktigt tecken: '%c']\n", *yytext); } %%
Svar:
inmatning -> kommandolista avslutakommando
kommandolista -> kommando kommandolista | tomt
kommando -> personkommando | prioritetskommando | vaccineratkommando | listakommando
personkommando -> person personlista radslut
prioritetskommando -> prioritet heltal personlista radslut
vaccineratkommando -> vaccinerat personlista radslut
listakommando -> lista alla radslut | lista vaccinerade radslut | lista ovaccinerade radslut
avslutakommando -> avsluta radslut
personlista -> personnummer personlista | personnummer
tomt markerar en tom produktion.
Grammatiken finns också som en fil för Yacc/Bison: vaccinering.y
person 650402-7266 720515-4524 vaccinerat 871031-1922 avsluta
Svar:
Svar:
En semantisk fas är inte särskilt användbar här. Semantik handlar om betydelse, vad olika konstruktioner i språket betyder, och därför arbetar den med datatyper, för att till exempel (i många språk) skilja på vad + betyder i en konstruktion som 3 + 3 respektive "3" + "3". (I det ena fallet är det heltalsaddition, i det andra strängkonkatenering.) I inmatningen har vi något som skulle kunna kallas datatyper, nämligen heltal och personnummer, men dels gör man inga beräkningar eller andra operationer med dem, och dels står de på olika syntaktiska platser, så det blir aldrig någon tvekan om vad som är vad.
Å andra sidan kan det finnas problem kvar i inmatningen efter att den syntaktiska analysen är gjord. Enligt grammatiken går det utmärkt att mata in samma personnummer flera gånger med person-kommandon, man kan både vaccinera och ge prioriteter till personer som inte finns inmatade, Man kan mata in personnummer som ingen person har, och (beroende på hur duktig man var när man skrev scannern) helt felaktiga personnummer. Kontrollen av sådant kan man se som en semantisk fas.
Svar:
Symboltabellen i en riktig kompilator innehåller också information om datatyper och variabelns "scope". Variabeln x kanske är en heltalsvariabel, som är lokal i funktionen f. Någon sådan information finns inte om personnumren. I språk med typade variabler använder den semantiska fasen symboltabellen för att hitta datatypen på variabler, så kompilatorn kan avgöra vad + i ett uttryck som x + y innebär. I vaccineringsspråket har vi inga variabler alls.
Däremot behöver man, precis som med en riktig symboltabell, kunna lagra nya personnummer, hitta existerande personnummer, och även associera information med dem (personernas prioritet, ifall de blivit vaccinerade, och i så fall när).
Om grammatiken från uppgift 3 ovan inte lämpar sig för en prediktiv recursive-descent-parser, måste den först göras om.
Svar:
Grammatiken lämpar sig inte för att implementeras i en prediktiv recursive-descent-parser eftersom den innehåller FIRST()-konflikter. För att få bort dem transformerar vi grammatiken genom att vänsterfaktorisera listakommando och personlista. De här produktionerna:
listakommando -> lista alla radslut | lista vaccinerade radslut | lista ovaccinerade radslut
görs om till:
listakommando -> lista lista-argument
lista-argument -> alla radslut | vaccinerade radslut | ovaccinerade radslut
De här produktionerna:
personlista -> personnummer personlista | personnummer
görs om till:
personlista -> personnummer flerpersoner
flerpersoner -> personlista | tomt
Resultat:
inmatning -> kommandolista avslutakommando
kommandolista -> kommando kommandolista | tomt
kommando -> personkommando | prioritetskommando | vaccineratkommando | listakommando
personkommando -> person personlista radslut
prioritetskommando -> prioritet heltal personlista radslut
vaccineratkommando -> vaccinerat personlista radslut
listakommando -> lista lista-argument
lista-argument -> alla radslut | vaccinerade radslut | ovaccinerade radslut
avslutakommando -> avsluta radslut
personlista -> personnummer flerpersoner
flerpersoner -> personlista | tomt
Därefter skriver vi parsern. En nedladdningsbar komplett fil finns här: vaccineringsparser.c
void match(int expected) { if (lookahead != expected) error(); else lookahead = scan(); } void inmatning(void); void kommandolista(void); void kommando(void); void personkommando(void); void prioritetskommando(void); void vaccineratkommando(void); void listakommando(void); void lista_argument(void); void avslutakommando(void); void personlista(void); void flerpersoner(void); void inmatning(void) { kommandolista(); avslutakommando(); } void kommandolista(void) { if (lookahead == PERSON || lookahead == PRIORITET || lookahead == VACCINERAT || lookahead == LISTA) { kommando(); kommandolista(); } else { /* Ingenting */ } } void kommando(void) { if (lookahead == PERSON) { personkommando(); } else if (lookahead == PRIORITET) { prioritetskommando(); } else if (lookahead == VACCINERAT) { vaccineratkommando(); } else if (lookahead == LISTA) { listakommando(); } else { error(); } } void personkommando(void) { match(PERSON); personlista(); match(RADSLUT); } void prioritetskommando(void) { match(PRIORITET); match(HELTAL); personlista(); match(RADSLUT); } void vaccineratkommando(void) { match(VACCINERAT); personlista(); match(RADSLUT); } void listakommando(void) { match(LISTA); lista_argument(); } void lista_argument(void) { if (lookahead == ALLA) { match(ALLA); match(RADSLUT); } else if (lookahead == VACCINERADE) { match(VACCINERADE); match(RADSLUT); } else if (lookahead == OVACCINERADE) { match(OVACCINERADE); match(RADSLUT); } else { error(); } } void avslutakommando(void) { match(AVSLUTA); match(RADSLUT); } void personlista(void) { match(PERSONNUMMER); flerpersoner(); } void flerpersoner(void) { if (lookahead == PERSONNUMMER) { personlista(); } else { /* Ingenting */ } } void parse() { lookahead = scan(); inmatning(); printf("Ok.\n"); }