Exempel: Språket som består av satserna JAG VET, JAG TROR, DU VET
och DU TROR definieras av syntaxen
<Sats> ::= <Subj> <Pred>
<Subj> ::= JAG | DU
<Pred> ::= VET | TROR
I syntaxen ovan har vi tre omskrivningsregler.
Varje regel består av ett vänsterled med en icke-slutsymbol
(t ex <Sats> ovan)
och ett högerled som talar om vad man kan ersätta vänsterledet med.
I högerledet får det förekomma både icke-slutsymboler och
slutsymboler (t ex JAG i exemplet ovan).
Tecknet | betyder eller. Språket består av alla
satser som kan bildas genom omskrivningar av startsymbolen <Sats>.
Meningar av typen
JAG VET ATT DU TROR ATT JAG VET OCH JAG TROR ATT DU VET ATT JAG TROR
definieras nu så här:
<Mening> ::= <Sats> | <Sats><Konj><Mening> <Konj> ::= ATT | OCHPlötsligt har vi fått en syntax som beskriver en oändlig massa meningar!
Syntaxen för programspråk beskrivs ofta i BNF. Så här kan man visa hur Javas tilldelningssatser ser ut:
<Assignment> ::= <Identifier> = <Expression> ; <Identifier> ::= <Letter> | <Letter> <Digit> | <Letter> <Identifier> <Letter> ::= a-z | A-Z | _ | $ <Digit> ::= 0-9Duger denna syntax eller behöver den förbättras?
En kompilator fungerar ungefär så här:
källkod --> lexikal analys --> syntaxanalys --> semantisk analys --> kodgenerering --> målkod
Java.class).
Mio.nextChar().
När något strider mot syntaxen låter vi ett särfall skickas iväg.
Om vi vill analysera grammatiken för meningar av typen "JAG VET ATT DU TROR"
behöver vi alltså metoderna:
läsSats()
läsSubjekt()
läsPredikat()
läsMening()
läsKonj()
class SyntaxAnalys{
private Mio mio = new Mio();
public void kontrolleraInmatning(){
try {
läsMening();
mio.getLine();
System.out.println("Din mening följer syntaxen.");
}
catch(SyntaxErrorException e) {
System.out.println(e + " före " + mio.getLine());
}
} // kontrolleraInmatning()
private void läsMening() throws SyntaxErrorException {
läsSats();
if (!mio.eoln()) {
läsKonj();
läsMening();
}
} // läsMening()
private void läsSats() throws SyntaxErrorException{
läsSubjekt();
läsPredikat();
} // läsSats()
private void läsSubjekt() throws SyntaxErrorException{
String ord = mio.getWord();
if (ord.equals("JAG") || ord.equals("DU"))
return;
else
throw new SyntaxErrorException("Subjektfel");
} // läsSubjekt()
private void läsPredikat() throws SyntaxErrorException{
String ord = mio.getWord();
if (ord.equals("VET") || ord.equals("TROR"))
return;
else
throw new SyntaxErrorException("Predikatfel");
} // läsPedikat()
private void läsKonj() throws SyntaxErrorException{
String ord = mio.getWord();
if (ord.equals("ATT") || ord.equals("OCH"))
return;
else
throw new SyntaxErrorException("Konjunktionsfel");
} // läsKonj()
} // class Syntax
public class SyntaxErrorException extends Exception {
public SyntaxErrorException(String errorMessage){
super (errorMessage);
}
}//class SyntaxErrorException
Denna metod för syntaxanalys ska ni använda er av i extrauppgift 2. Programmet ska läsa in en matematikformel tecken för tecken och med rekursiv medåkning kolla syntaxen och utföra beräkningen. Parenteser, multiplikation, division, addition och subtraktion ska ingå.
'{'), lägg den på
stacken.
'}'), titta på stacken.
Om stacken är tom eller om den symbol som poppar ut inte matchar
högerparentesen har vi ett syntaxfel.
2*(3+4*5).
+ - * /. Den vanliga prioritetsordningen (* och
/ går före + och -) ska gälla mellan
operatorerna. Man ska också kunna använda parenteser i taluttrycken.
Lösning:
|
*
/ \
2 ( )
|
+
/ \
3 *
/ \
4 5
<Uttryck> ::= <Term> | <Term> + <Uttryck> | <Term> - <Uttryck>;
<Term> ::= <Faktor> | <Faktor> * <Term> | <Faktor> / <Term>;
<Faktor> ::= TAL | -<Faktor> | (<Uttryck>);
Koden för uttryck skulle kunna bli ungefär:
double readExpr() {
// expr ::= term | term + expr | term - expr
double x = readTerm(); // expr ::= term ...
if (peek('+')) { // expr ::= term + expr
popToken();
x += readExpr();
} // TODO expr ::= term - expr
return x;
}
Där peek() och popToken() är specialskrivna för att tjuvtitta respektiva plocka ut från inmatningströmmen.
Det finns en ofärdig implemention här som man kan testa
addition och multiplikation.