forked from IF-LK-2020/rechenmaschine
537 lines
22 KiB
Java
537 lines
22 KiB
Java
import java.util.Scanner;
|
|
import java.text.DecimalFormat;
|
|
/**
|
|
* Hauptklasse des Projekts
|
|
*/
|
|
public class Rechenmaschine {
|
|
|
|
private List<Token> tokenlist;
|
|
|
|
private String fehler;
|
|
|
|
private String result;
|
|
|
|
private int klammern;
|
|
|
|
public Rechenmaschine(String eingabe) {
|
|
tokenlist = new List<>();
|
|
|
|
if (!scanne(eingabe)) {
|
|
System.out.println("Fehler bei der lexikalischen Analyse:");
|
|
System.out.println(fehler);
|
|
return;
|
|
}
|
|
|
|
if (!parse()) {
|
|
System.out.println("Fehler bei der syntaktischen Analyse:");
|
|
System.out.println(fehler);
|
|
return;
|
|
}
|
|
|
|
if (!analyse()) {
|
|
System.out.println("Fehler bei der semantischen Analyse:");
|
|
System.out.println(fehler);
|
|
return;
|
|
}
|
|
|
|
run();
|
|
|
|
}
|
|
|
|
|
|
public boolean scanne(String pEingabe) {
|
|
char[] eingabe = pEingabe.toCharArray();
|
|
klammern = 0;
|
|
int state = 0;
|
|
StringBuilder currentToken = new StringBuilder();
|
|
for (char buchstabe : eingabe) {
|
|
switch (state) {
|
|
case 0:
|
|
switch (buchstabe) {
|
|
case '0' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 1;
|
|
}
|
|
case '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 2;
|
|
}
|
|
case '.' -> {
|
|
System.out.println("Am anfang des Termes kann kein . stehen.");
|
|
return false;
|
|
}
|
|
case '+', '-', '*', '/' -> {
|
|
System.out.println("Der Term darf nicht mir mit dem Operator " + buchstabe + " beginnen.");
|
|
return false;
|
|
}
|
|
case '(' -> {
|
|
tokenlist.append(new Token("PARANTHESES", Character.toString(buchstabe)));
|
|
state = 6;
|
|
klammern++;
|
|
}
|
|
case ')' -> {
|
|
System.out.println("Der Term darf nicht mit ) beginnen");
|
|
return false;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (buchstabe) {
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
System.out.println("Wenn eine 0 am anfang eines Operands ist, muss ein . folgen.");
|
|
return false;
|
|
}
|
|
case '.' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 3;
|
|
}
|
|
case '+', '-', '*', '/' -> {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
currentToken = new StringBuilder();
|
|
tokenlist.append(new Token("OPERATOR", Character.toString(buchstabe)));
|
|
state = 5;
|
|
}
|
|
case ')' -> {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
currentToken = new StringBuilder();
|
|
tokenlist.append(new Token("PARANTHESES", ")"));
|
|
state = 7;
|
|
klammern--;
|
|
}
|
|
case '(' -> {
|
|
System.out.println("Mach einem Operand darf nicht unmittelbar ein ( folgen");
|
|
return false;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (buchstabe) {
|
|
case '.' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 3;
|
|
}
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 2;
|
|
}
|
|
case '+', '-', '*', '/' -> {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
currentToken = new StringBuilder();
|
|
tokenlist.append(new Token("OPERATOR", Character.toString(buchstabe)));
|
|
state = 5;
|
|
}
|
|
case ')' -> {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
currentToken = new StringBuilder();
|
|
tokenlist.append(new Token("PARANTHESES", ")"));
|
|
state = 7;
|
|
klammern--;
|
|
}
|
|
case '(' -> {
|
|
System.out.println("Nach einem Operand darf nicht unmittelbar ein ( folgen.");
|
|
return false;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (buchstabe) {
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 4;
|
|
}
|
|
case '+', '-', '*', '/', '(', ')' -> {
|
|
System.out.println("Nach einem . darf kein Rechenoperator, oder eine Klammer folgen");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (buchstabe) {
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 4;
|
|
}
|
|
case '+', '-', '*', '/' -> {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
currentToken = new StringBuilder();
|
|
tokenlist.append(new Token("OPERATOR", Character.toString(buchstabe)));
|
|
state = 5;
|
|
}
|
|
case '.' -> {
|
|
System.out.println("Eine Dezimalzahl kann nur ein . haben.");
|
|
return false;
|
|
}
|
|
case ')' -> {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
currentToken = new StringBuilder();
|
|
tokenlist.append(new Token("PARANTHESES", Character.toString(buchstabe)));
|
|
state = 7;
|
|
klammern--;
|
|
}
|
|
case '(' -> {
|
|
System.out.println("Nach einem Operand darf nicht unmittelbar ein ( folgen.");
|
|
return false;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 5:
|
|
switch (buchstabe) {
|
|
case '0' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 1;
|
|
}
|
|
case '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 2;
|
|
}
|
|
case '.' -> {
|
|
System.out.println("Nach einem Operator kann kein . stehen.");
|
|
return false;
|
|
}
|
|
case '+', '-', '*', '/' -> {
|
|
System.out.println("Nach einem Operator darf nicht direkt ein weiterer Operator folgen.");
|
|
return false;
|
|
}
|
|
case '(' -> {
|
|
tokenlist.append(new Token("PARANTHESES", "("));
|
|
state = 6;
|
|
klammern++;
|
|
}
|
|
case ')' -> {
|
|
System.out.println("Nach einem Operator darf kein ) folgen.");
|
|
return false;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 6:
|
|
switch (buchstabe) {
|
|
case '0' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 1;
|
|
}
|
|
case '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
|
|
currentToken.append(buchstabe);
|
|
state = 2;
|
|
}
|
|
case '+', '-' -> {
|
|
tokenlist.append(new Token("OPERATOR", Character.toString(buchstabe)));
|
|
state = 5;
|
|
}
|
|
case '*', '/' -> {
|
|
System.out.println("Unmittelbar nach ( darf kein Operator oder ) folgen.");
|
|
return false;
|
|
}
|
|
case '(' -> {
|
|
tokenlist.append(new Token("PARANTHESES", Character.toString(buchstabe)));
|
|
klammern++;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case 7:
|
|
switch (buchstabe) {
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(' -> {
|
|
System.out.println("Nach ) muss ein Operator folgen");
|
|
return false;
|
|
}
|
|
case '+', '-', '*', '/' -> {
|
|
tokenlist.append(new Token("OPERATOR", Character.toString(buchstabe)));
|
|
state = 5;
|
|
}
|
|
case ')' -> {
|
|
tokenlist.append(new Token("PARANTHESES", Character.toString(buchstabe)));
|
|
klammern--;
|
|
}
|
|
default -> {
|
|
System.out.println("Der Buchstabe " + buchstabe + " ist nicht in der Sprache vorhanden.");
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (state == 5) {
|
|
fehler = "Fehler im Wort " + pEingabe + ":\nDas Wort darf nicht mit einem Operator enden!";
|
|
return false;
|
|
} else if (state == 3) {
|
|
fehler = "Fehler im Wort " + pEingabe + ":\nDas Wort darf nicht mit einem . enden!";
|
|
return false;
|
|
} else if (state == 6) {
|
|
fehler = "Fehler im Wort " + pEingabe + ":\nDas Wort darf nicht mit ( enden!";
|
|
return false;
|
|
} else if (klammern != 0) {
|
|
fehler = "Fehler im Wort " + pEingabe + ":\nIm wort müssen alle Klammern geschlossen sein!";
|
|
return false;
|
|
} else {
|
|
tokenlist.toLast();
|
|
|
|
if (tokenlist.hasAccess()) {
|
|
if (!tokenlist.getContent().getToken().equals(")")) {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
}
|
|
} else {
|
|
tokenlist.append(new Token("OPERAND", currentToken.toString()));
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public boolean parse() {
|
|
|
|
int state = 0;
|
|
|
|
tokenlist.toFirst();
|
|
while (tokenlist.hasAccess()) {
|
|
Token currentToken = tokenlist.getContent();
|
|
|
|
switch (state) {
|
|
case 0:
|
|
if (currentToken.getType().equals("OPERATOR")) {
|
|
fehler = "Rechenterm darf nicht mit einem Operator starten!";
|
|
return false;
|
|
} else if (currentToken.getType().equals("OPERAND")) {
|
|
state = 1;
|
|
break;
|
|
} else if (currentToken.getToken().equals("(")) {
|
|
state = 2;
|
|
break;
|
|
} else if (currentToken.getToken().equals(")")) {
|
|
state = 1;
|
|
break;
|
|
} else {
|
|
fehler = "Unbekanntes Token: " + currentToken.getType();
|
|
return false;
|
|
}
|
|
|
|
|
|
case 1:
|
|
if (currentToken.getType().equals("OPERATOR")) {
|
|
state = 0;
|
|
break;
|
|
} else if (currentToken.getType().equals("OPERAND")) {
|
|
fehler = "Auf einen Operanden darf kein Operand folgen!";
|
|
return false;
|
|
} else if (currentToken.getToken().equals("(")) {
|
|
fehler = "Auf einen Operanden kann kein ( folgen";
|
|
return false;
|
|
} else if (currentToken.getToken().equals(")")) {
|
|
state = 1;
|
|
break;
|
|
} else {
|
|
fehler = "Unbekanntes Token: " + currentToken.getType();
|
|
return false;
|
|
}
|
|
case 2:
|
|
if (currentToken.getType().equals("OPERATOR")) {
|
|
state = 0;
|
|
break;
|
|
} else if (currentToken.getType().equals("OPERAND")) {
|
|
state = 1;
|
|
break;
|
|
} else if (currentToken.getToken().equals("(")) {
|
|
state = 2;
|
|
break;
|
|
} else if (currentToken.getToken().equals(")")) {
|
|
state = 1;
|
|
break;
|
|
} else {
|
|
fehler = "Unbekanntes Token: " + currentToken.getType();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
tokenlist.next();
|
|
}
|
|
|
|
if (state == 0) {
|
|
fehler = "Ein Rechenterm darf nicht auf einen Operator enden!";
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public boolean analyse() {
|
|
tokenlist.toFirst();
|
|
while (tokenlist.hasAccess()) {
|
|
if (tokenlist.getContent().getToken().equals("/")) {
|
|
tokenlist.next();
|
|
if (tokenlist.getContent().getToken().equals("0")) {
|
|
fehler = "Es kann nicht durch 0 geteilt werden.";
|
|
return false;
|
|
}
|
|
}
|
|
tokenlist.next();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void punktVorStrich(List<Token> pList) {
|
|
pList.toFirst();
|
|
while (pList.hasAccess()) {
|
|
if (pList.getContent().getToken().equals("*")) {
|
|
pList.remove();
|
|
Double faktor1 = Double.parseDouble(pList.getContent().getToken());
|
|
|
|
pList.current = pList.getPrevious(pList.current);
|
|
Double faktor2 = Double.parseDouble(pList.getContent().getToken());
|
|
|
|
pList.remove();
|
|
|
|
pList.getContent().setToken(String.valueOf(faktor2 * faktor1));
|
|
} else if (pList.getContent().getToken().equals("/")) {
|
|
pList.remove();
|
|
|
|
Double divisor = Double.parseDouble(pList.getContent().getToken());
|
|
|
|
//Previous Token muss gelöscht werden, tokenlist.getContent != null ist,
|
|
// wenn der 2. Token am ende des Termes ist.
|
|
pList.current = pList.getPrevious(pList.current);
|
|
Double dividend = Double.parseDouble(pList.getContent().getToken());
|
|
|
|
pList.remove();
|
|
|
|
pList.getContent().setToken(String.valueOf(dividend / divisor));
|
|
}
|
|
pList.next();
|
|
}
|
|
}
|
|
|
|
private void klammerRegel(List<Token> pList) {
|
|
List<Token> newTokenlist = new List<Token>();
|
|
pList.toFirst();
|
|
double berechnet;
|
|
//Durchlaufe die Tokenliste
|
|
while (pList.hasAccess()) {
|
|
// Token gleich ( dann entferne ihn
|
|
if (pList.getContent().getToken().equals("(")) {
|
|
pList.remove();
|
|
//anschließend durchlaufe solange, bis auf ) gestoßen wird
|
|
while (!pList.getContent().getToken().equals(")")) {
|
|
|
|
|
|
//wenn noch nicht am ende der tokenliste
|
|
if (pList.hasAccess()) {
|
|
//wenn ( erneutvorkommt
|
|
if (pList.getContent().getToken().equals("(")) {
|
|
// rufe die methode mehrereKlammern auf und speicher das ergebnis als neues Token in newTokenlist
|
|
berechnet = mehrereKlammern(pList);
|
|
newTokenlist.append(new Token("OPERAND", String.valueOf(berechnet)));
|
|
}
|
|
//wenn der Token nicht ) ist dann soll dieser in newTokenlist eingefügt werden und anschließend entfernt werden
|
|
if (!pList.getContent().getToken().equals(")")) {
|
|
newTokenlist.append(pList.getContent());
|
|
pList.remove();
|
|
}
|
|
}
|
|
}
|
|
//wenn auf ) gestoßen wurde, soll ) entfernt werden
|
|
pList.remove();
|
|
//newTokenlist wird ausgerechnet
|
|
berechnet = berechne(newTokenlist);
|
|
//und in die Tokenliste eingefügt
|
|
if (pList.hasAccess()) {
|
|
pList.insert(new Token("OPERAND", String.valueOf(berechnet)));
|
|
} else {
|
|
pList.append(new Token("OPERAND", String.valueOf(berechnet)));
|
|
}
|
|
newTokenlist = new List<Token>();
|
|
//hier wurde nun 1 klammer gelöst
|
|
}
|
|
//wenn die liste noch nicht zu ende ist, soll das nächste Element geprüft werden
|
|
pList.next();
|
|
}
|
|
}
|
|
|
|
private double mehrereKlammern(List<Token> pList) {
|
|
List<Token> newTokenlist = new List<Token>();
|
|
double berechnet = 0;
|
|
//wenn current ( ist
|
|
if (pList.getContent().getToken().equals("(")) {
|
|
//wird es entfernt
|
|
pList.remove();
|
|
//anschließend wird die übergebene tokenliste solange durchlaufen, bis ) auftritt
|
|
while (!pList.getContent().getToken().equals(")")) {
|
|
|
|
|
|
if (pList.hasAccess()) {
|
|
//wenn current ( ist dann wird diese methode nochmal ab current ausgeführt und anschließend
|
|
// das ergebnis in newTokenlist eingefügt
|
|
if (pList.getContent().getToken().equals("(")) {
|
|
berechnet = mehrereKlammern(pList);
|
|
newTokenlist.append(new Token("OPERAND", String.valueOf(berechnet)));
|
|
}
|
|
// wenn current nich ) ist, dann wird es in newTokenlist gespeichert und aus der tokenliste entfernt
|
|
if (!pList.getContent().getToken().equals(")")) {
|
|
newTokenlist.append(pList.getContent());
|
|
pList.remove();
|
|
}
|
|
}
|
|
}
|
|
//wenn ) auftritt dann wird es entfernt und anschließend wird die berechnung von newTokenlist rückgegeben.
|
|
pList.remove();
|
|
berechnet = berechne(newTokenlist);
|
|
}
|
|
return berechnet;
|
|
|
|
|
|
}
|
|
|
|
private double berechne(List<Token> pList) {
|
|
double result1 = 0;
|
|
punktVorStrich(pList);
|
|
pList.toFirst();
|
|
String previous = "";
|
|
while (pList.hasAccess()) {
|
|
Token currentToken = pList.getContent();
|
|
if (currentToken.getType().equals("OPERATOR")) {
|
|
previous = currentToken.getToken();
|
|
}
|
|
if (currentToken.getType().equals("OPERAND")) {
|
|
switch (previous) {
|
|
case "+", "" -> result1 += Double.parseDouble(currentToken.getToken());
|
|
case "-" -> result1 -= Double.parseDouble(currentToken.getToken());
|
|
}
|
|
}
|
|
pList.next();
|
|
}
|
|
return result1;
|
|
}
|
|
|
|
public void run() {
|
|
klammerRegel(tokenlist);
|
|
punktVorStrich(tokenlist);
|
|
double result = berechne(tokenlist);
|
|
DecimalFormat format = new DecimalFormat("#,##0.####");
|
|
String resultString = format.format(result);
|
|
this.result= resultString;
|
|
}
|
|
|
|
public String getResult() {
|
|
return result;
|
|
}
|
|
} |