Term-Compiler

Luccia

Mitglied
Hallo, wie gehts so :)
Für die Schule muss ich bis nächste Woche einen einfachen Term-Compiler programmieren. Dafür sollen wir Blue-J verwenden und Objekt Orientiert programmieren.
Dieser Term-Compiler soll einfache Mathematische Gleichungen berechnen können, und daher auch die Rechenzeichen +,-,/,* erkennen, sowie Klammern (also z.B. (2+3)*5).
Wir haben als Anfang schon die Klassen Stackmaschine und Befehlsleser bekommen (die Codesposte ich unten dran)
Ich muss jetzt noch eine Klasse Lexer und eine Klasse Parser erstellen und falls es klappen sollte einen Userinterface.
Da es mein erstes größeres Javaprojekt ist (vorher sollten wir nur einmal eine Kreuzung mit Autos und Ampeln machen) bin ich etwas nervös und kenne mich auch ehrlich gesagt nicht ganz so mit Java aus (aber das wird sich hoffentlich über die Woche noch ändern :D). Ich hatte gehofft, hier auf ein paar freundliche User zu treffen, die mir mal hier und mal da, wenn das Projekt nicht ganz so klappt mal unter die Arme greifen und auf den Fehler deuten. :D.

Aber zuerst einmal die Codes und was ich schon über meinen Lexer und meinen Parser weiß.

Also zuerst Stackmaschine:

Java:
import java.util.Stack;
import java.io.*;
/**
 * Write a description of class Stackmaschine here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Stackmaschine
{
    // instance variables - replace the example below with your own
    private Stack<Double> mystack; // der Stack der Maschine
    private Befehlsleser mybefle; // verantwortlich für das Einlesen aus der Datei Readme.txt (das ist unsauvber aber praktisch...
    private double [] befehlswort; // das aktuelle Befehlswort der Maschine (mit Argument)

    /**
     * Constructor for objects of class Stackmaschine
     * 
     * Das Exceptionhandling ist noch ziemlich mistig (nicht existent).
     * Eigentlich sollte man die IO-Exception nicht bis an die Maschine durchreichen...
     */
    public Stackmaschine() throws IOException
    {
        mystack = new Stack<Double>();
        mybefle = new Befehlsleser();
    }

    /**
     * Diese Methode lässt die Stackmaschine einen Rechenschritt machen
     * (d.h. ein Befehlswort von der Eingabe holen und den Befehl ausführen
     * 
     * @param 
     * @return
     */
    public void Schritt() throws IOException
    {
        befehlswort = mybefle.naechsterBefehl();
        if (befehlswort[0]==1)
        {
            mystack.push(befehlswort[1]);
        }
        if (befehlswort[0]==2)
        {
            this.add();
        }
        if (befehlswort[0]==3)
        {
            this.sub();
        }
        if (befehlswort[0]==4)
        {
            this.mult();
        }
        if (befehlswort[0]==5)
        {
            this.div();
        }        
    }
    
    
    public void add()
    {
        double x;
        double y;
        double temp;

        x=mystack.pop();
        y=mystack.pop();
        temp=x+y;
        mystack.push(temp);
    }
    
        public void mult()
    {
        double x;
        double y;
        double temp;

        x=mystack.pop();
        y=mystack.pop();
        temp=x*y;
        mystack.push(temp);
    }
    
    public void div()
    {
        double x;
        double y;
        double temp;

        x=mystack.pop();
        y=mystack.pop();
        temp=y/x;
        mystack.push(temp);
    }
    
    public void sub()
    {
        double x;
        double y;
        double temp;

        x=mystack.pop();
        y=mystack.pop();
        temp=y-x;
        mystack.push(temp);
    }
    
    
}

Befehlsleser:

Java:
import java.io.*;

/**
 * Write a description of class Befehlsleser here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class Befehlsleser
{
    private FileReader reader; // dient dem Einlesen aus Datei
    private BufferedReader inBuffer; // dient dem Einlesen aus Datei
    private double [] befehlswort; // das an die Stackmaschine durchzureichende Befehlswort

    /**
     * Constructor for objects of class Befehlsleser
     */
    public Befehlsleser() throws IOException
    {
        reader = new FileReader("Readme.txt"); // etwas getrickst: Die Eingabe ist die Doku-Datei von BlueJ...
        inBuffer = new BufferedReader(reader);
        befehlswort = new double [2];
    }

    /**
     * Die Methode liest die nächste Befehlszeile aus der Eingabe und macht daraus ein
     * Befehlswort für die Stackmaschine
     * Wenn man so will, ist das hier das I von IO eines Computers
     * 
     * @param
     * @return     Das Befehlswort, ein Array mit zwei Feldern. Das erste enthaelt den Befehl,
     * das zweite ggf. das Argument
     */
   public double [] naechsterBefehl() throws IOException
   {
       String befehl;
       double befehlscode;
       double argument;
       
       befehl = this.zeileLesen();
       
       if (befehl.startsWith("push"))
       {
           befehlscode=1; //
           argument = Double.parseDouble(befehl.substring(5));
           befehlswort[0]=befehlscode;
           befehlswort[1]=argument;
       }
       
       if (befehl.startsWith("add"))
       {
           befehlscode=2;
           befehlswort[0]=befehlscode;
           befehlswort[1]=0.0;
       }
       if (befehl.startsWith("sub"))
       {
           befehlscode=3;
           befehlswort[0]=befehlscode;
           befehlswort[1]=0.0;
       }
       if (befehl.startsWith("mult"))
       {
           befehlscode=4;
           befehlswort[0]=befehlscode;
           befehlswort[1]=0.0;
       }
       if (befehl.startsWith("div"))
       {
           befehlscode=5;
           befehlswort[0]=befehlscode;
           befehlswort[1]=0.0;
       }
       
       return befehlswort;
   }
   
   private String zeileLesen() throws IOException
  /* Die Methode gibts, weil das Exceptionhandling für das Dateilesen später hier erfolgen soll
   * sonst könnte man das auch einfach oben beim Einlesen hinschreiben
   */
   {
       return inBuffer.readLine();
   } 

}


Jetzt mein Verständnis vom Lexer: (soll ein DEA sein)

Der Lexer ist der Teil des Term-Compilers, der überprüft, ob der Term nur aus Zahlen (0-9 usw), Klammer () und Rechenzeichen (+,*,-,/) besteht. Sollte ein anderes Zeichen im Term eingegeben worden sein zum Beispiel ein Buchstabe, dann sollte der Lexer dem Benutzer ein Fehlermeldung zeigen.
Aus den einzelen Komponenten erstellt der Lexer dann Tokens und sendet diese an den Parser zur Weiterverarbeitung.
Mein Lexer braucht daher folgende Teile:
- Erkennungsteil für die Komponeten (was passiert, wenn er ein Teil akzeptiert)
- Erkennungsteil im Falle, dass ein Komponent nicht akzeptiert wird.
- Lexer braucht 4 erkennbare Tokens Zahl, Rechenzeichen, offene Klammer und geschlossene Klammer.

Und dem Parser (soll ein Kellerautomat sein)

Der Parser ist das Program, dass die vom Lexer erstellten Tokens erhält. (z.B. 5 * 3 * dabei ist jedes
Zeichen sein eigener Token). Dies würde beim Lexer durch gehn, da alle Lexer Regeln eingehalten sind (d.h. Keine Unerlaubten Zeichen) Der Parser untersucht diese Token aber und stellt fest, dass das eine Zeichen zuviel ist. Dies soll in einem Kellerautomaten realisiert werden.
Was muss mein Praser also können?
-Er untersucht die ankommenden Token.
-Wenn ein Token Zahl kommt, muss er erkennen können, dass danach keine weiterer Token Zahl kommen kann. Danach kommen darf ein Token Rechenzeichen und Token geschlossene Klammer.
-Ein Token Rechenzeichen darf nicht am Anfang stehen (ich geh jetzt mal vom ganz einfachen fall aus, dass mein Term-Compiler weder negative Zahlen noch Kommazahlen lesen kann. Sollte ichs hinbekommen das ganze zu machen, dann kann ich ja noch immer ausweiterungen hinzufügen). Nach einem Token Rechenzeichen darf der Token offene Klammer und Token Zahl kommen.
-Der Token offene Klammer darf am anfang stehen. Nach ihm darf nur der Token Zahl stehen. (Zudem muss das Programm sich merken, dass eine Klammer offene ist, damit diese später auf jeden Fall geschlossen wird)
-Nach dem Token geschlossene Klammer darf nur ein Token Rechenzeichen stehen.
- Parser sollte der sein, der zum Schluss alles berechnet. Dabei muss unbedingt beachtet werden: Was in Klammern steht zuerst und ansonsten Punkt vor Strich.

So jetzt :D, dass wars erst mal mit den Angaben, die ich soweit über das Projekt machen kann.
Ich wollt mit dem Reinstellen davon wissen, ob meine Logik so passt (also vorallem die vom Parser) oder ob ich Fehler bei meinen Überlegungen gemacht habe.
Und ob es für mich Sinn macht, anstelle von einem Token Rechenzeichen, 4 Token mit den einzelnen Zeichen zu erstellen.
Und die letzte Frage:Sollte ich wirklich den Parser alles berechnen lassen? oder wäre es sinnvoller eine Extra Klasse zu erstellen, also den "Rechner".

LG Luccia :)
 

Luccia

Mitglied
Viel Spass - mach mal ein grosszügiges Angebot unter "Jobs"

Ist das etwa soviel Arbeit :(? Naja, prinzipiell habe ich ja auch noch 3 Wochen Zeit, (also bis zum Dienstag vor Weihnachten) aber ich hätte gerne das Meiste davon bis zum Ende dieses Wochenendes fertig, da ich in der Schule leider auch noch andere Projekte bearbeiten muss. Dafür gebe ich mir auch viel Mühe und werd außer dem Projekt und Schule gehn nichts anderes machen :rtfm: . Ich schaff das schon :D!
 

Andi_CH

Top Contributor
Ich dachte das sei hier schon einmal behandelt worden aber auf die Schnelle finde ich nichts.

Ich würde nicht mit programmieren beginnen, sondern mit Analyse und design, sonst verrenst du dich.
Papier nicht Computer, UML nicht Java ist im Momet gefragt.

Also ich möchte das nicht nebenei in drei Wochen erledigen müssen - so eine zweistellige Stundenzahl geht dafür schon drauf.
 

Luccia

Mitglied
Gut das ich zu den Personen gehöre, denen Noten wichtiger sind als Schlaf. (und noch dazu würds mich schon aus persönlichem ehrgeiz arg stören, wenn ichs nicht fertig bringen soll.)
Also ich hab damit begonnen Java zu lernen (bzw über eine Tutorial zu wissen wo was steht, damit ich später darauf zurück greifen kann).
Okay, werde ich jetzt gleich damit beginnen :D (also mit dem UML und der schriftlichen ausarbeit.)

Es gibt einen ähnlichen Beitrag zu dem Thema. So hab ich über google das Forum hier ja auch gefunden, aber derjenige hat das Problem nur mit einem Parser gelöst. Das hilft mir nicht viel, da ich ja sowohl Parser als auch Lexer und Benutzer oberfläche haben muss :/, ansonsten hätte ich meine Fragen natürlich darein gepostet.
Zweistellige Stundenzahl ist mehr als nur vertrettbar (3 stellig wäre ein größeres Problem). Hab ja ein ganzes Wochenende nur für Informatik eingeplant.

Aber nochmal zu meinen Fragen zurück. Muss ich für jedes einzelene Rechenzeichen einen Token machen oder reicht einer alleine?
Und ist es Sinnvoll den Parser alles berechnen zu lassen, oder wird es für mich später einfacher sein eine eigene Klasse dafür zu erstellen?

Achja und danke für den Tipp mit der Ausarbeitung :)
 

Neue Themen


Oben