Oblig-faq

Denne siden inneholder svar p� ofte stilte sp�rsm�l om obligene og verkt�y i INF5110 v�ren 2006.

Innhold

  1. Oppgaven
    1. Omskriving av grammatikken
      1. Rekkef�lge
      2. Korrigere assosiativitet
    2. Utskrift
    3. Hva betyr "!"? Er det en logisk NOT-operator?
  2. Antlr
    1. Generelt
    2. Vokabularer
      1. Mystiske parserfeil
    3. Skanning
      1. Begrense lengden av terminaler
    4. Lookahead
      1. Syntaktisk lookahead er f�lsomt for rekkef�lge

1. Oppgaven

1.1 Omskriving av grammatikken

1.1.1 Hvilken rekkef�lge b�r man foreta omskrivingene i?

Det f�lgende gjelder uttrykksgrammatikken, f.o.m. produksjonen exp:
  1. Presedenskaskade (se 3.4 i Louden)
  2. Skriv om til riktig assosiativitet (se. 3.4.2 i Louden)
  3. Fjerne venstrerekursjon (se 4.2.3 i Louden)
  4. [Skriv om til EBNF] (vanligvis er EBNF � foretrekke, se ogs� spm. 2.2)
Det viktigste her er skillet mellom det f�rste steget og de tre andre. De siste kan man i st�rre grad blande sammen. Merk at disse omskrivingene m� gj�res i begge grammatikkene, derfor kan det l�nne seg � starte med den konsise, flertydige grammatikken, og bruke den som input (med LL(k)-opsjoner og -predikater fjernet) til venstrefaktorisering i den andre LL(1)-grammatikken.

1.1.2 Hvordan rette opp syntakstr�r som har blitt h�yreassosiative etter fjerning av venstrerekursjon?

Generelt, s� er det to l�sninger:
  1. Bruk EBNF. Antlr genererer kode omtrent som beskrevet i 4.2 av Louden.
  2. Bruk en parameter for � sende venstresida til en syntakstrenode med i en h�yrerekursiv BNF-regel.
For � gj�re en lang historie kort: Det f�rste alternativet er � foretrekke, det er kortere og klarere, og unng�r komplikasjonene i aksjonskoden som det andre tilf�rer. Eks. flertydig, venstreassosiativ grammatikk:
      exp : exp "+" exp | NUMBER;
    
til venstreassosiativ form:
      exp : exp "+" NUMBER | NUMBER;
    
etter fjerning av venstrerekursjon:
      exp : NUMBER exprr;
      exprr : "+" NUMBER exprr | // Empty ;
    
L�sning 1: � skrive om til EBNF
      exp : NUMBER ("+" NUMBER)* ;
    
Med skisse av aksjonskode (se AmbigiousExp.g for ett mer fullstendig eksempel):
    exp returns[Exp e]  
    : lhs:NUMBER {e = new Exp(Integer.valueOf(lhs.getText())); } 
      (("+" rhs:NUMBER) 
        { e=new Exp(e, new Exp(Integer.valueOf(rhs.getText()))); }
      )*
    ;
    
L�sning 2:
    exp returns[Exp e]  
    : lhs:NUMBER e = exprr[lhs] ;

    exprr[Exp lhs] returns [Exp e]
    {
        Exp rhs;
	e = lhs;
    }
    : "+" n:NUMBER {rhs = new Exp(Integer.valueOf(n.getText())); } 
       e=exprr[ new Exp(lhs, rhs)]
    | // Empty  {e = lhs; }
    

1.2 Utskrift

1.2.1 Hvordan skrive ut noder som ikke er vist i Canonical.ast?

Den generelle syntaksen for utskrift av en node er som f�lger:

      node       : "(" NODE_NAME (attributes)* (node)* ")"
      attributes : "ref"
    

hvor NODE_NAME er venstresiden til produksjonen noden er utledet fra i den opprinnelige Diss-grammatikken. N�r det gjelder uttrykk, bruker eksempelet en litt innviklet syntaks:

(ARITH_OP + ... )

Dette kan godt forenkles til bare:

(+ ... )
siden n�r man leser terminalen +, s� vet man at den er operator for ett aritmetisk uttrykk, som er ett uttrykk.
Konkrete sp�rsm�l:
Utskrift av true:
(BOOL_LITERAL "true")
Utskrift av LOG_OP, input
b =  false && true;
gir:
(ASSIGN_STMT (NAME b) (LOG_OP && (BOOL_LITERAL "false") (BOOL_LITERAL "true")))
    
Input
b =  true || b;
gir:
(ASSIGN_STMT (NAME b) (LOG_OP || (BOOL_LITERAL "true") (NAME b)))
    

Utskrift av WHILE_STMT: input:

while (condition) { }

b�r bli:

      (WHILE_STMT (NAME condition)
          ()
      )
    

1.3 Hva betyr "!"? Er det en logisk NOT-operator?

Ja. Det er logisk negasjon, som i C* og Java. Dette er den eneste un�re operatoren i spr�ket. Man trenger ikke bry seg om betydningen av denne i oblig 1, annet enn evt. til klassifisering av syntakstretyper, men i oblig 2 skal dens operand typesjekkes som bool.

2. Antlr

2.1 Generelt

2.1.1 Store og sm� bokstaver

Symptom:

    program : (Decl)* EOF ; 
i stedet for
    program : (decl)* EOF ; 
genererer og kompilerer, men gir parsefeil run-time.

Pass p� bruken av store og sm� bokstaver i Antlr-identifikatorer. Hovedreglen er at terminaler starter med stor forbokstav (og b�r ellers bare inneholde store bokstaver, for � f�lge konvensjon fra eksemplene), og non-terminaler starter med liten forbokstav.

Er det noen gode eksempler i Antlr-distribusjonen?

Ja, men eksemplene er ofte enten for enkle og banale, eller for vanskelige og omfattende. De jeg hadde mest bruk for var TinyC- og Pascal- og Java-eksemplene, listet opp noenlunde p� stigende brukbarhet og vanskelighetsgrad.

Pascal- og Java-eksemplene (1273 LOC!) er store og omfattende, men demonstrerer sv�rt mange av Antlrs avanserte egenskaper, fiklete skanning, syntaktisk lookahead, og AST-bygging med imagin�re tokens.

2.2 Vokabularer

2.2.1 Mystiske parserfeil p� terminaler

Symptom:

line 19:13: expecting '.', found '.'

Dette, og flere andre ganske merkelige feil p� terminaler, kan skyldes at parseren og skanneren har forskjellig oppfatning av kodeverdier for den samme terminalen i konstant-tabellen. L�sningen er � s�rge for at parsere og skannere bruker det samme vokabularet med opsjonene importVocab og exportVocab. Sjekk kapitlet om "Token Vocabularies" i referansemanualen.

Antlr byr p� ganske mange m�ter (rekkef�lge mellom skanner og parser i samme fil, byggerekkef�lge, bruk av import og eksport) � rote med terminaldefinisjonene, s� for � spare dere for un�dvendig bry skal jeg her gjengi mitt oppsett av grammatikkfiler og vokabular- innstillinger. Mitt l�sningsforslag best�r av tre filer:

I parsergrammatikkene Diss(1|2).g, eksporteres terminaldefinisjonene til to filer med prefikset Diss:
      class Diss2Parser extends Parser;

      options {
          exportVocab = Diss;
      }
    
I skannergrammatikken DissLexer.g, importeres definisjone eksportert over:
      /** Scanner. */
      class DissLexer extends Lexer;
      options {
          importVocab=Diss;
	  ...
      }
    
Man kan droppe eksport-opsjonene p� parserne, og i stedet importere ett av de defaulte parser-vokabularene ("Diss(1|2)Parser").

2.3 Skanning

2.3.1 Hvordan begrense lengden til terminaler?

Kort svar: Dessverre st�tter ikke Antlr lengdebegrensninger, som de fleste andre regexp-baserte skannerverkt�y, av typen (eksempel fra en skanner i GNU flex):

      identifier		{letter}{digit_letter_or_underscore}{0,15} 
    

p� en grei m�te. Brute-force-l�sningen med � repetere en stadig lengre tegnsekvens 1, 2 ... N ganger, er b�de inelegant og produserer advarsler om non-determinisme (kan dog l�ses opsjonen greedy).

Dette er ikke noen viktig del av obligen, s� l�sninger som ikke tilfredsstiller kravet om h�yst 16 tegns lengde p� identifikatorer, er akseptable.

Lengre svar: De som er interessert, kan pr�ve ett s�kalt semantisk validerende predikat p� regelen som kjenner igjen navn i skanneren. Noe � la:

	NAME : LETTER (DIGIT | LETTER | '_')* { text.length() <= 16 }? ;
      

Variabelen text er en instans av java.lang.StringBuffer i den genererte skannerklassen. Predikatet over kaster ett unntak hvis lengden til identifikatoren overstiger 16 tegn. Imidlertid skaper dette nye problemer med at unntaket ikke fanges opp av Antlrs defaulte feilh�ndtering.

2.4 Lookahead

2.4.1 Syntaktisk lookahead er rekkef�lgesensitivt!

Eks:
	exp : (NAME | simpleExp DOT) => var
	    | simpleExp
            ; 
      
parser forskjellig fra
        exp : simpleExp
            | (NAME | simpleExp DOT) => var
            ; 
      

Det er viktig � plassere regler med syntaktiske predikater f�r regler de st�r i konflikt med. For detaljer om LL(k)-predikater og rekkef�lge, se avsnittet om "Predicated-LL(k) Lexing" i ref.manualen.

2006-03-16 14:50
Sven-J�rgen Karlsen