/media/sda-magnetic/david/Dok-15-2023-11-27/fernuni-hagen/cs-i-ii/old-cs-2-03/java-src/java-2020-10-14/Arithmetic2/Arithmetic.java


/*
Subklassen:

Expression
Term 
Factor

Terminals:
+ Plus 
* Mal
( OpenRoundBracket
) ClouseRoundBracket

Terminals:
0000 to 9999
aaaa to zzzz

Jetzt machen wir das so:

Wir rufen jedes Mal, wenn Expression aufgerufen wird, ein neues Objekt, zum Beispiel. Term wiederum kann ein neues Objekt erzeugen Faktor. 
Das heißt, wir haben drei Objekte. Die nicht voneinander vererbt sind

expression()
term()
factor()

Und jedes Mal, wenn wir einen Expression() erzeugen, erzeugen wir vielleicht einen Term()

Jeder diese Teile, zum Beispiel in

(5+6)*4

erzeugt ein eigenes Objekt Expression, Term, .... Es ist gleich gesagt, dass das viel Speicher in Anspruch nimmt, aber das ist uns jetzt egal.

Jetzt müssen wir unsere Objekte anschauen, wir haben 

Terminale: +, *, (, ), 99, aa
Nichtterminale: Expression, Term, Factor

Es bietet sich, Expression, Term, Factor zwar nicht voneinander zu vererben, aber eine Superklass

NichtTermal zu erstellen.
Genauso wie wir eine Superklasse Terminal haben

Wir müssen bedenken, dass auf Grund unserer Linksrekursion, die Regel 

expr ::= expr + term
term ::= term * factor
factor ::= const | id | (expr)

nicht gilt. Sondern wir haben expr2 und term2

Also wir haben 5

expr
expr2
term
term2
factor

Das interessante ist, dass sich unsere Vererbung schon augenscheinlich lohnt. Weil, wir haben eine lexikalische Analyse. Und die lautet gettoken()

Was wir bisher machen können, ist einen Lexer schreiben und wir ein kleines Programm bringen wir den Lexer, beim Parser unter.
Wir haben in C eine alleine stehe Funktion gettoken()

Wenn wir eine Superklasse terminal haben, dann bringen wir darin unter gettoken().

Und die gilt für alle Subklassen. 

Jetzt erzeugen wir ein Objekt, von der Klasse Expression. Dann kommt bei

(5+6)*4

Ein Expression. Wir erzeugen als ein Objekt von der Klasse Expression()

Der Term

Wir haben zwei Teile vom Term 

(5+6)
und 
4

Wir erzeugen zwei Objekte. Dann kommt der Faktor ("").
Und darin der Expresion 5+6.
Das heißt, wir erzeugen so viele Objekte, wie Bestandteile in unserem Ausdruck. Das kann nachher nützlich sein, wenn wir zum Beispiel einen Baum machen.

Jetzt fangen wir an:

Wir verzichten auf id, das heißt, aaa
*/
public class Arithmetic {
    static String SourceCode;
    static int count = 0;
    
    public static void main (String [] args) {
        SourceCode = "((10+10)*10)+2";
        count = 0;
        NumExpr ne = new NumExpr();
        System.out.println(ne.numexpr());
    }
    
    static public class Terminal {}

    static public class Plus extends Terminal {
        Plus () {}

        boolean isThatTerminal() {
            if (SourceCode.length() <= count)
                return false;
            if(SourceCode.charAt(count) == '+') {
                count++;
                return true;
            }   
            return false;
        }
    }
    static public class Times extends Terminal {
        Times () {}

        boolean isThatTerminal() {
            if (SourceCode.length() <= count)
                return false;
            if(SourceCode.charAt(count) == '*') {
                count++;
                return true;
            }
            return false;
        }
    }

    static public class OpenRoundBracket extends Terminal {
        OpenRoundBracket () {}
        boolean isThatTerminal() {
            if (SourceCode.length() <= count)
                return false;
            if(SourceCode.charAt(count) == '(') {
                count++;
                return true;
            }
            return false;
        }
    }

    static public class CloseRoundBracket extends Terminal {
        CloseRoundBracket () {}
        boolean isThatTerminal() {
            if (SourceCode.length() <= count)
                return false;
            if(SourceCode.charAt(count) == ')') {
                count++;
                return true;
            }
            return false;
        }
    }

    static public class Const extends Terminal {
        String tokenStr;
        int val;
    
        Const () {}
    
        int ConstToInt () {
            val = 0;

            if (SourceCode.length() <= count) 
                return -1;
            if (((SourceCode.charAt (count) >= '0') && (SourceCode.charAt (count) <= '9'))) {
                val = SourceCode.charAt (count) - '0';
                count++;
            }
            else
                return -1;
            if (SourceCode.length() <= count) 
                return val;                
            while ((SourceCode.charAt (count) >= '0') && (SourceCode.charAt (count) <= '9')) {
                if (SourceCode.length() <= count) 
                    return val;                
                val *= 10;
                val += (SourceCode.charAt (count)  - '0');
                count++;
            }
            return val;
        }
        
        
        boolean isThatTerminal () {
            val = 0;
            int count2 = count;
        
            if (SourceCode.length() <= count) {
                return false;
            }
            if (((SourceCode.charAt (count2) >= '0') && (SourceCode.charAt (count2) <= '9'))) {
                val = SourceCode.charAt (count2)  - '0';
                count2 ++;
            }
            else {
                return false;
            }
            if (SourceCode.length() <= count2) {
                return true;
            }
            while ((SourceCode.charAt (count2) >= '0') && (SourceCode.charAt (count2) <= '9')) {
                if (SourceCode.length() <= count2) 
                    return true;                
                val *= 10;
                val += SourceCode.charAt (count2)  - '0';
                count2++;
            }
            return true;
        }

    }

    static public class NotTerminal {
        NotTerminal() {}
    }
    

    static public class NumExpr extends NotTerminal {
        int op1;
        int op2;
        
        NumExpr () {}
        
        int numexpr () {
            Term t = new Term();
            op1 = t.term();
            Plus p = new Plus();
            if (p.isThatTerminal()) {
                NumExpr e = new NumExpr();
                op2 = e.numexpr();
                return op1 + op2;
            }
            return op1;
        }
    }

    static public class Term extends NotTerminal {
        int op1;
        int op2;
        
        Term () {}
        
        int term () {
            Factor f = new Factor();
            op1 = f.factor();
            Times tm = new Times();
            if (tm.isThatTerminal()) {
                Term t = new Term(); 
                op2 = t.term();
                return op1 * op2;
            }
            return op1;
        }
    }

    static public class Factor extends NotTerminal {
        OpenRoundBracket o = new OpenRoundBracket();
        Const c = new Const();
        int op1, op2;
        
        Factor () {}
        
        int factor () {
            if (o.isThatTerminal()) {
                NumExpr e = new NumExpr();
                op1 = e.numexpr();
                CloseRoundBracket cl = new CloseRoundBracket();
                if (cl.isThatTerminal()) {
                    return op1;
                }
                else
                    System.out.println ("Error");
            }
            else if (c.isThatTerminal()) {
                op1 = c.ConstToInt();
                return op1;
            }
            else {
                System.out.println ("Error");
                op1 = -1;
            }
            return op1;
        }
    }
}