print logo

Beskrivelse av programmeringsspråket Diss.

Her beskrives syntaksen og den statiske semantikken (hva som skal sjekkes av kompilatoren) til språket Diss. Den dynamiske semantikken (altså hva som skal gjøres under utførelsen) skulle være rimelig opplagt, men slik oppgaven er formulert blir dette ikke viktig.

Diss 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

Diss støtter brukerdefinerte typer kalt "strukter". De tilsvarer struct -konstruksjonen i C og lignende språk. I Diss 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

Diss 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 Diss 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

Diss 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.

read_int()

Leser en int fra standard inn.

read_float()

Leser en float fra standard inn.

read_char()

Leser en karakter 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 Diss 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. Det er ingen implisitte konversjoner mellom int og bool - man kan ikke gjøre f.eks if ( 1 ){}.

Grammatikk

Grammatikken som egen fil.

Presedens

Presedens-rekkefølge (fra lavest til høyest):

  1. || 
  2. &&
  3. ! (altså "!a && b" betyr "(!a) && b")
  4. Alle relasjonsoperatorene
  5. + og -
  6. * og /
  7. ** (eksponensiering)
  8. . (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 det det må bety: "!( !( !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.

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.
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 Diss-program kjøres. 
  • Det må sjekkes at nøkkelordet "ref" brukes både ved kall og i funksjonssignaturen.