Compilerbauhome Compilerbau: Recursive Descent Parser für arithmetische Ausdrücke Prof. Dr. Uwe Schmidt FH Wedel

Recursive Descent Parser für arithmetische Ausdrücke

weiter

weiter

Die Symbol-Klasse: Token.java

public class Token {
    public static final int
        Eof     = 1,
        Plus    = 2,
        Minus   = 3,
        Times   = 4,
        Div     = 5,
        Id      = 6,
        Num     = 7,
        Lparent = 8,
        Rparent = 9,
        Err     = 10;
        
    public int token;
    public String text;
 
    Token(int token,
          String text) {
        this.token = token;
        this.text  = new String(text);
    }
 
    public String toString() {
        return
            "token: " + token +
            "\ttext: \"" + text + "\"";
    }
}
 

weiter

Die Scanner-Spezifikation mit JLex

%%
 
%class ExprScanner
%type Token
%function nextSymbol
%full
%line
%char
 
%eofval{
  yy_reader.close();
  return
    new Token(Token.Eof"");
%eofval}
 
letter=[a-zA-Z]
letterdigit=[a-zA-Z0-9]
digit=[0-9]
digits={digit}+
whitespace=[ \t\n]+
 
%%
 
"+" { return
        new Token(Token.Plusyytext());
    }
 
"-" { return
        new Token(Token.Minusyytext());
    }
 
"*" { return
        new Token(Token.Timesyytext());
    }
 
"/" { return
        new Token(Token.Divyytext());
    }
 
"(" { return
        new Token(Token.Lparentyytext());
    }
 
")" { return
        new Token(Token.Rparentyytext());
    }
 
{letter}{letterdigit}*
    { return
        new Token(Token.Idyytext());
    }
 
{digits}
    { return
        new Token(Token.Numyytext());
    }
 
("--".*[\n])|{whitespace}
    { }
 
.   { return
        new Token(Token.Erryytext());
    }

weiter

Die LL(1)-Grammatik

S  ::= E $
 
E  ::= T E'
 
E::= + T E'
   |   - T E'
   |
 
T  ::= F T'
 
T::= * F T'
   |   / F T'
   |
 
F  ::= id
   |   num
   |   ( E )

weiter

Der Recursive Descent Parser

import java.io.IOException;
 
public class RecDescentParser {
 
    private ExprScanner s;
    private Token t;
 
    public RecDescentParser() {
        s = new ExprScanner(System.in);
    }
 
    void advance() {
        try {
            t = s.nextSymbol();
        }
        catch (IOException e) {
            t = new Token(Token.Eof"");
        }
    }
 
    void eat(int token) {
        if ( token == t.token ) {
            advance();
        } else {
            error();
        }
    }
 
    void S() {
        E();
        if (t.token != Token.Eof)
            error();
    }
 
    void E() {
        switch (t.token) {
        case Token.Id:
        case Token.Num:
        case Token.Lparent:
            T();
            Eprime();
            break;
        default:
            error();
        }
    }
 
    void Eprime() {
        switch (t.token) {
        case Token.Plus:
            advance();
            T();
            Eprime();
            break;
        case Token.Minus:
            advance();
            T();
            Eprime();
            break;
        case Token.Eof:
            break;
        case Token.Rparent:
            break;
        default:
            error();
        }
    }
 
    void T() {
        switch (t.token) {
        case Token.Id:
        case Token.Num:
        case Token.Lparent:
            F();
            Tprime();
            break;
        default:
            error();
        }
    }
 
    void Tprime() {
        switch (t.token) {
        case Token.Plus:
            break;
        case Token.Minus:
            break;
        case Token.Times:
            advance();
            F();
            Tprime();
            break;
        case Token.Div:
            advance();
            F();
            Tprime();
            break;
        case Token.Eof:
            break;
        case Token.Rparent:
            break;
        default:
            error();
        }
    }
 
    void F() {
        switch (t.token) {
        case Token.Id:
            advance();
            break;
        case Token.Num:
            advance();
            break;
        case Token.Lparent:
            advance();
            E();
            eat(Token.Rparent);
            break;
        default:
            error();
        }
    }
 
    private int errcnt = 0;
 
    void error() {
        ++errcnt;
        System.out.println("parse error, illegal symbol \""
                           + t.text + "\"");
        if ( t.token != Token.Eof ) {
            advance();
        }
    }
 
    public void parse()
        throws java.io.IOException {
 
        advance();
        S();
 
        System.out.println("Parsing done, "
                           + errcnt
                           + " error(s) found");
    }
 
    public static void main(String argv[])
        throws java.io.IOException {
 
        (new RecDescentParser())
            .parse();
    }
}

weiter

Der Makefile

JCP     := $(PWD)/../../../software:.:$(CLASSPATH)
 
JAVA    := java -classpath $(JCP)
JAVAC   := javac -classpath $(JCP)
JLEX    := java  -classpath $(JCP) JLex.Main
 
lexsrc  := $(shell echo *.lex)
javagen := $(lexsrc:.lex=.java)
javasrc := $(shell echo *.java)
classes := $(lexsrc:.lex=.class) $(javasrc:.java=.class)
output  := Test.expr.tokens Test.expr.parse
 
all             : examples
 
examples        : $(javagen) $(classes) $(output)
 
Test.expr.tokens        : Test.expr $(classes)
        $(JAVA) TestExprScanner < Test.expr > Test.expr.tokens
 
Test.expr.parse : Test.expr $(classes)
        $(JAVA) RecDescentParser < Test.expr > Test.expr.parse
 
%.java  : %.lex
        $(JLEX) $<
        mv -f $<.java $*.java
 
%.class : %.java
        $(JAVAC) $<
 
.SUFFIXES       : .class .java .lex
 
clean   :
        rm -f $(javagen) $(classes) $(output) *~ .*~

Letzte Änderung: 14.02.2012
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel