Beskrivelse av programmeringsspr?ket D# (D-sharp).
Her beskrives syntaksen og den statiske semantikken (hva som skal sjekkes av kompilatoren) til spr?ket D#. Den dynamiske semantikken (alts? hva som skal gj?res under utf?relsen) skulle v?re rimelig opplagt, men eventuelle detaljer som er n?dvendige vil vi komme tilbake til i forbindelse med oblig 2.
D# er med vilje lagt opp til ? ha en del likhetstrekk med C (og andre spr?k i samme familien...).
Syntaks
I beskrivelsen under er ikke-terminaler skrevet med store bokstaver, og metasymbolene (i bokas betydning, alts? de som brukes til ? beskrive grammatikken) er : ->, |, (, ), {, }, [, ]. Her betyr {...} gjentakelse null eller flere ganger, og [...] betyr at det kan v?re med eller ikke.
Alt annet, som er skrevet som tette sekvenser er terminalsymboler, og de med sm? bokstaver er reserverte (!) n?kkelord. Merk dog at at alle terminalsymbolene er skrevet i anf?rselstegn for ? skille dem fra de tilsvarende metasymbolene.
Det er noen terminaler som ikke g?r greit fram av syntaksen under, og disse er betegnet NAME, INT_LITERAL, FLOAT_LITERAL og STRING_LITERAL.
NAME
NAME skal starte med bokstav, og deretter v?re en sekvens av siffer, bokstaver og underscore. De kan ikke v?re p? mer enn 16 tegn, og alle er signifikante. Store og sm? bokstaver regnes som forskjellige tegn. Merk alts? at alle n?kkelord skrives med sm? bokstaver, og at de ikke kan brukes som vanlige navn.
INT_LITERAL
INT_LITERAL skal inneholde ett eller flere siffer.
FLOAT_LITERAL
FLOAT_LITERAL skal inneholde ett eller flere siffer, fulgt av et punktum, fulgt av ett eller flere siffer.
STRING_LITERAL
STRING_LITERAL skal best? av en tekststreng innesluttet i anf?rselstegn ("). Strengen kan ikke inneholde linjeskift. Den semantiske "verdien" av en STRING_LITERAL er kun det som er inni anf?rselstegn; selve anf?rselstegnene skal ikke inkluderes.
structs
D# st?tter brukerdefinerte typer kalt "strukter". De tilsvarer struct -konstruksjonen i C og lignende spr?k. I D# defineres de med n?kkelordet "struct" fulgt av en serie variabeldefinisjoner. Variable av strukt-typer kan antas ? ha referanse-semantikk, m.a.o, de oppf?rer seg som pekere. Instanser av strukter kan opprettes med n?kkelordet new. Dette gj?r det mulig ? ha referanser til strukt-typer som attributter til strukter. Den spesielle verdien "null" betegner "ingen struct", som i C# eller Java. Merk at, i motsetning til C/C++, skal en struct-deklarasjon ikke avsluttes med semikolon. Operatoren . (punktum) brukes for ? aksessere attributter i en struct. Denne operatoren har h?yere presedens enn alle andre operatorene, med unntak av parenteser.
pass-by-reference
D# st?tter s?kalt "pass-by-reference" (selvsagt i tillegg til "vanlig" pass-by-value). Endringer som gj?res i en funksjon med en pass-by-reference parameter vil reflekteres i skopet til den kallende funksjonen (jfr operatoren & i C++ eller "ref" i C#). pass-by-reference markeres i D# med n?kkelordet "ref", som m? brukes b?de i funksjonssignaturen og i funksjonskallet. Et eksempel:
void swap( ref int a, ref int b ) { int tmp = a; a = b; b = tmp; } void Main() { int x = 42; int y = 84; swap( ref x, ref y ); }
Det er tillatt ? sende struct-attributter by ref (m.a.o., kallet func( ref a.b ) er tillatt.
(Syntaksen for ref parametre er i stor grad hentet fra C#.)
Eksponensiering
D# st?tter en eksponensieringsoperator ** (som i Fortran). Uttrykket 5**2 betyr 5 opph?yd i andre potens. Merk at **, i motsetning til de fleste andre operatorene, er h?yre-assosiativ. Det inneb?rer at 5 ** 2 ** 3 betyr 5 ** (2 ** 3). Uttrykk med ** evaluerer alltid til en float.
Indre deklarasjoner
I en funksjon kommer alle deklarasjonene f?r den f?rste eksekverbare setningene. Disse deklarasjonene kan inkludere alt som kan deklareres p? ytterste niv?, alts? variable, funksjoner og structer. Det sier seg selv at navnene p? disse funksjonene og structene ikke er tilgjengelige utenfor den funksjonen de er deklarert i (men navnene p? medlemmene i structene er naturligvis tilgjengelig fra det omliggende skop).
Short-circuit evaluation
De logiske operatorene && og || benytter s?kalt short-circuit evaluation. Det betyr at dersom det f?rste uttrykket evaluerer til sann, skal ikke det andre uttrykket evalueres i det hele tatt.
Typer og implisitt typekonvertering
Det er tillatt ? tilegne uttrykk av typen int til variable av typen float. Det motsatte er ikke tilfelle. Det finnes ingen cast-operator. Dersom et aritmetisk uttrykk inneholder en eller flere subuttrykk av typen float, skal hele uttrykket evalueres med flyttallsaritmetikk.
Merk at dette ogs? gjelder i andre sammenhenger der typekonverteringer kan v?re naturlig - dersom en funksjon er definert til ? returnere en float, m? man tillate at uttrykk av typen int brukes.
Det er ingen implisitte konverteringer mellom int og bool - man kan ikke gj?re f.eks if ( 1 ){}.
Standardbibliotek
Programmet har et standardbibliotek med et sett av IO-funksjoner. Dersom ikke annet er angitt har de returtypen void.
read_int()
Leser en int fra standard inn.
read_float()
Leser en float fra standard inn.
read_char()
Leser ett tegn fra standard inn og returnerer ASCII-verdien som en int. Returnerer -1 ved EOF.
read_string()
Leser en string fra standard inn opp til f?rste whitespace.
read_line()
Leser en tekstlinje fra standard inn.
print_int( int i )
Skriver en int til standard ut.
print_float( float f )
Skriver en float til standard ut.
print_str( string s )
Skriver en streng til standard ut.
print_line( string s )
Skriver en streng til standard ut fulgt av et linjeskift.
Kommentarer
Kommentarer i D# starter med // og fortsetter linjen ut(som i C++/Java/C#). Man trenger ikke ? st?tte C's /* */-kommentarer. Merk at det ikke er noen grunn til ? gi kommentarer og whitespace videre til parseren - disse kan trygt fjernes i scanneren.
Grammatikk
Grammatikken som egen fil.Presedens
Presedens-rekkef?lge (fra lavest til h?yest):
- ||
- &&
- ! (alts? "!a && b" betyr "(!a) && b")
- Alle relasjonsoperatorene
- + og -
- * og /
- ** (eksponensiering)
- . (punktum, for struct-aksess)
Assosiativitet
- ||, &&, +, -, *, / og . er venstre-assosiative
- ** er h?yre-assosiativ
- Relasjonoperatorene er ikke-assosiative (alts? er f.eks. "a < b+c < d" ulovlig).
- Det er lov ? skrive "!!! b", og det betyr: "!( !( !b))).
Merk at spr?ket ikke st?tter un?r minus (og heller ingen un?r pluss, men det er det vel f? som gr?ter over).
Semantikk (ikke viktig i oblig 1)
Bruksforskomster av navn uten punktum foran bindes p? vanlig m?te til en deklarasjon: Let ut gjennom "blokkene" (alts? funksjoner eller program) som omslutter bruksstedet. Se p? deklarasjonene i hver blokk, og velg den deklarasjonen der du f?rst f?r treff. Om man ikke f?r treff er det en feil i programmet. Her regnes en formell parameter med til de lokale variable i prosedyren.
Spr?ket har fire innebygde typer: "float", "int", "string" og "bool". I tillegg utgj?r hver struct-deklarasjon en ny type. N?r man leter etter en strukt leter man ogs? utover, blokk for blokk, som for andre navn. Se ovenfor.
Alle navn m? v?re deklarert (tekstlig) f?r de brukes (og sjekkingen kan derved naturlig gj?res i ett gjennoml?p).
Semantiske regler som m? sjekkes:
Uttrykk
- Det m? sjekkes at uttrykk er typeriktig formet, p? den opplagte m?ten. Hele uttrykket blir derved ogs? tilordnet en type.
- Det m? sjekkes at typen p? begge sider av en tilordning er den samme. MERK: Det er lov b?de ? lese og tilordne verdi til en formell parameter inne i funksjonen.
- Det m? sjekkes at typen av uttrykket etter "if" og etter "while" er "bool".
- Det m? sjekkes at det uttrykket som kommer foran et punktum evaluerer til en struct-type.
- Likeledes m? det sjekkes at navnet som kommer etter et punktum er navnet p? et attributt av struct-typen.
Funksjoner
- Funksjoner som kalles inne i et uttrykk m? v?re deklarert med "float", "int", "string" eller "bool" eller med navnet p? en struct-type.
- Navn som brukes som funksjon (i kall) m? v?re deklarert som funksjon, og tilsvarende for variable (heri inkludert parametere).
- Det m? sjekkes at antall og typer p? parametere stemmer overens mellom kallsted og deklarasjon.
- Return-setninger kan bare forekomme inne i funksjons-deklarasjoner, og de angir at funksjonen skal terminere.
- Dersom funksjonen er deklarert med "void" skal return-setningen ikke ha noe uttrykk, ellers skal den ha et uttrykk av funksjonens type.
- Dersom funksjonen ikke er deklarert med "void" m? det sjekkes at funksjonen inneholder minst ett return-statement som vil n?s uansett.
Generelt
- Det m? ikke v?re dobbeltdeklarasjoner innen en "blokk" (og her regnes samme navn p? funksjon og variabel som en dobbelt-deklarasjon). Dette gjelder naturligvis ogs? innenfor structer.
- Navnet p? en formell parameter m? (alts?) ikke kollidere med navnet p? noen lokal deklarasjon i funksjonen.
- Det m? sjekkes at alle navn som brukes er deklarerte (se bindingsreglen over).
- Det m? sjekkes at det p? ytterste niv? deklareres en funksjon med signaturen void Main(). Denne blir kalt av kj?retidsmilj?et n?r et D#-program kj?res.
- Det m? sjekkes at n?kkelordet "ref" brukes b?de ved kall og i funksjonssignaturen.