RegEx- Umwandlung von String in ausführbares Objekt/ Befehl

Fodoboo131

Mitglied
Guten Abend,

Grober Kontext: Ich sollte objektorientiert ein Turtle Programm schreiben, welches die Turtle über ein Feld laufen lässt.

Problem: Wir bekommen Strings mit ganz vielen Objektaufrufen, z.B.:
Java:
public static final String PROG_2 = "Subroutine(arc Repeat(10 Go(5) Turn(-5)))" + LS +
                                        "Subroutine(petal Call(arc) Turn(-130) Call(arc))" + LS +
                                        "Subroutine(flower Turn(90) PenDown() Repeat(10 Go(30) Turn(-5)) Repeat(9 Call(petal) Turn(30)) PenUp() Turn(208) Go(290) Turn(112))" + LS +
                                        "Repeat(5 Call(flower) Go(100))";
Diesen sollen wir in eine ausführbare Sequence umwandeln. Eine Sequence ist eine Abfolge von beliebig vielen Befehlen(realisiert durch unterschiedliche Klassen)

Repeat(int n, Sequence seq) <--- seq wird n mal ausgeführt.
Subroutine(String name, Sequence seq) <--- seq wird ausgeführt, wenn ihr name aufgerufen wird.
Call(String name) <--- ruft eine Subroutine mit Namen auf / führt diese aus

So soll meine Zielvariable am Ende aussehen:
Java:
final Stmt meadow = new Sequence(new Subroutine("arc",
            new Repeat(10, new Go(5), new Turn(-5))),
            new Subroutine("petal",
                    new Call("arc"),
                    new Turn(-130),
                    new Call("arc")),
            new Subroutine("flower",
                    new Turn(90),
                    new PenDown(),
                    new Repeat(10, new Go(30), new Turn(-5)),
                    new Repeat(9, new Call("petal"), new Turn(30)),
                    new PenUp(),
                    new Turn(208),
                    new Go(290),
                    new Turn(112)),
            new Repeat(5, new Call("flower"), new Go(100)));

Ich hab das ganze jetzt mit RegEx probiert, aber ich struggle, die Repeat und Subroutine Klassen gescheit zu implementieren:
Java:
package turtle.parser;

import turtle.stmt.Go;
import turtle.stmt.PenDown;
import turtle.stmt.PenUp;
import turtle.stmt.Repeat;
import turtle.stmt.Sequence;
import turtle.stmt.Turn;
import turtle.stmt.Subroutine;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.*;

public class LogoParser{
    String program;
    List<Object> seq= new ArrayList<>();
    String[] l1;

    public LogoParser(String program){
        this.program = program;
        l1 = program == null ? null : program.split("\s");
    }

    public Sequence run() throws ParseException {

        checkProgram();

        return seq==null ? null:  new Sequence(seq.toArray());
    }

    private void checkProgram() throws ParseException{
        Pattern turnPattern = Pattern.compile("Turn\\( \\d+\\)");
        String turn = "Turn\\( \\d+\\)";
        Pattern goPattern = Pattern.compile("Go\\(\\d+\\)"); //unlimited amount of digits
        Pattern repeatPattern = Pattern.compile("Repeat\\(\\d+\s([A-Za-z]+|\s| \\( | \\d+ \\))+ \\)\\) ");
        Pattern subroutinePattern = Pattern.compile("Subroutine\\([A-Za-z]+\s.+\\)");
        Pattern callPattern = Pattern.compile("Call\\(.+\\)");
        Pattern penUpPattern = Pattern.compile("PenUp\\(\\)");
        Pattern penDownPattern = Pattern.compile("PenDown\\(\\)");
        Pattern repeatEnd = Pattern.compile(".+\\)\\)");

        for (int i = 0; i < l1.length; i++) {
            Matcher goMatcher = goPattern.matcher(l1[i]);
            Matcher turnMatcher = turnPattern.matcher(l1[i]);
            Matcher repeatMatcher = repeatPattern.matcher(l1[i]);
            Matcher subroutineMatcher = subroutinePattern.matcher(l1[i]);
            Matcher callMatcher = callPattern.matcher(l1[i]);
            Matcher penUpMatcher = penUpPattern.matcher(l1[i]);
            Matcher penDownMatcher = penDownPattern.matcher(l1[i]);
            Matcher repeatEndMatcher = repeatEnd.matcher(l1[i]);

            if (goMatcher.find()){
                seq.add(new Go(Integer.parseInt(l1[i].replaceAll("[^0-9]", ""))));
                //deletes programm which is already in Sequence
                l1[i].replaceAll(goPattern.toString(), "");
            }
            else if (turnMatcher.find()){
                seq.add(new Turn(Integer.parseInt(l1[i].replaceAll("^\\d", ""))));
            }
            else if (penUpMatcher.find()){
                seq.add(new PenUp());
            }
            else if (penDownMatcher.find()){
                seq.add(new PenDown());
            }
            else if (subroutineMatcher.find()){
                //leider noch kein Ansatz
            }
            else if (repeatMatcher.find()){
                List<Object> subList = new ArrayList<>();
                int index = Integer.parseInt(l1[i].replaceAll("^\\d", ""));
                //wie füge ich alle elemente in meine subList ?
                seq.add(new Repeat(index, subList.toArray()));
            }
            else throw new ParseException("wrong sequence, try again");
        }
    }

}

Ich wäre über jeden Hinweis dankbar

MfG
 

Anhänge

  • 1654713743427.png
    1654713743427.png
    42,8 KB · Aufrufe: 0

httpdigest

Top Contributor
Zum Beispiel so:
Java:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class Parser {
    interface Command {}
    record PenUp() implements Command {}
    record PenDown() implements Command {}
    record Turn(int angle) implements Command {}
    record Go(int steps) implements Command {}
    record Call(String name) implements Command {}
    record Subroutine(String name, List<Command> sequence) implements Command {}
    record Repeat(int n, List<Command> sequence) implements Command {}

    private final byte[] input;
    private int pos;

    public Parser(String input) {
        this.input = input.getBytes();
    }

    private void skipWhitespace() {
        while (Character.isWhitespace(input[pos]))
            pos++;
    }

    private boolean startsWithAndConsume(String begin) {
        int n = begin.length();
        if (input.length - pos >= n && Arrays.equals(input, pos, pos + n, begin.getBytes(), 0, n)) {
            pos += n;
            return true;
        }
        return false;
    }

    private String parseName() {
        int start = pos;
        while (Character.isAlphabetic(input[pos]))
            pos++;
        return new String(input, start, pos-start);
    }

    private int parseNumber() {
        int start = pos;
        if (input[pos] == '-')
            pos++;
        while (Character.isDigit(input[pos]))
            pos++;
        return Integer.parseInt(new String(input, start, pos-start));
    }

    private Command parseWithNumber(Function<Integer, Command> factory) {
        int n = parseNumber();
        pos++;
        return factory.apply(n);
    }

    private Command parse() {
        if (startsWithAndConsume("Subroutine(")) {
            return new Subroutine(parseName(), parseSequence());
        } else if (startsWithAndConsume("Repeat(")) {
            return new Repeat(parseNumber(), parseSequence());
        } else if (startsWithAndConsume("Go(")) {
            return parseWithNumber(Go::new);
        } else if (startsWithAndConsume("Turn(")) {
            return parseWithNumber(Turn::new);
        } else if (startsWithAndConsume("Call(")) {
            String name = parseName();
            pos++;
            return new Call(name);
        } else if (startsWithAndConsume("PenUp()")) {
            return new PenUp();
        } else if (startsWithAndConsume("PenDown()")) {
            return new PenDown();
        } else {
            throw new IllegalStateException("unexpected input");
        }
    }

    public List<Command> parseSequence() {
        List<Command> seq = new ArrayList<>();
        do {
            skipWhitespace();
            seq.add(parse());
        } while (pos < input.length && input[pos++] != ')');
        return seq;
    }

    // Test mit deinem Beispiel-Input
    public static void main(String[] args) {
        String LS = " ";
        System.out.println(new Parser("Subroutine(arc Repeat(10 Go(5) Turn(-5)))" + LS +
                "Subroutine(petal Call(arc) Turn(-130) Call(arc))" + LS +
                "Subroutine(flower Turn(90) PenDown() Repeat(10 Go(30) Turn(-5)) " +
                "Repeat(9 Call(petal) Turn(30)) PenUp() Turn(208) Go(290) Turn(112))" + LS +
                "Repeat(5 Call(flower) Go(100))").parseSequence());
    }
}
 

httpdigest

Top Contributor
@Fodoboo131 aber um nochmal auf dein Problem mit den regulären Ausdrücken zurückzukommen und warum du noch keine Idee für Subroutine und Repeat haben konntest:
Einzelne reguläre Ausdrücke können eben nur reguläre Sprachen definieren. Diese Sprache hier ist aber nicht regulär, sondern lediglich kontext-frei.
Das kann man auf den ersten Blick leicht daran erkennen, wenn es Rekursionen gibt und die Sprache im Prinzip der Dyck-Sprache entspricht: Man muss also für ein Vorkommen von Subroutine bzw. Repeat die öffnende "Klammer" bzw. Beginn des Subroutine/Repeat mit deren schließender "Klammer" bzw. Ende dieser Subroutine/Repeat-Instanz auf derselben Ebene matchen. Das geht mit regulären Ausdrücken nicht. Hierfür braucht es einen Kellerautomaten bzw. einen Stack.
 

Fodoboo131

Mitglied
Zum Beispiel so:
Java:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class Parser {
    interface Command {}
    record PenUp() implements Command {}
    record PenDown() implements Command {}
    record Turn(int angle) implements Command {}
    record Go(int steps) implements Command {}
    record Call(String name) implements Command {}
    record Subroutine(String name, List<Command> sequence) implements Command {}
    record Repeat(int n, List<Command> sequence) implements Command {}

    private final byte[] input;
    private int pos;

    public Parser(String input) {
        this.input = input.getBytes();
    }

    private void skipWhitespace() {
        while (Character.isWhitespace(input[pos]))
            pos++;
    }

    private boolean startsWithAndConsume(String begin) {
        int n = begin.length();
        if (input.length - pos >= n && Arrays.equals(input, pos, pos + n, begin.getBytes(), 0, n)) {
            pos += n;
            return true;
        }
        return false;
    }

    private String parseName() {
        int start = pos;
        while (Character.isAlphabetic(input[pos]))
            pos++;
        return new String(input, start, pos-start);
    }

    private int parseNumber() {
        int start = pos;
        if (input[pos] == '-')
            pos++;
        while (Character.isDigit(input[pos]))
            pos++;
        return Integer.parseInt(new String(input, start, pos-start));
    }

    private Command parseWithNumber(Function<Integer, Command> factory) {
        int n = parseNumber();
        pos++;
        return factory.apply(n);
    }

    private Command parse() {
        if (startsWithAndConsume("Subroutine(")) {
            return new Subroutine(parseName(), parseSequence());
        } else if (startsWithAndConsume("Repeat(")) {
            return new Repeat(parseNumber(), parseSequence());
        } else if (startsWithAndConsume("Go(")) {
            return parseWithNumber(Go::new);
        } else if (startsWithAndConsume("Turn(")) {
            return parseWithNumber(Turn::new);
        } else if (startsWithAndConsume("Call(")) {
            String name = parseName();
            pos++;
            return new Call(name);
        } else if (startsWithAndConsume("PenUp()")) {
            return new PenUp();
        } else if (startsWithAndConsume("PenDown()")) {
            return new PenDown();
        } else {
            throw new IllegalStateException("unexpected input");
        }
    }

    public List<Command> parseSequence() {
        List<Command> seq = new ArrayList<>();
        do {
            skipWhitespace();
            seq.add(parse());
        } while (pos < input.length && input[pos++] != ')');
        return seq;
    }

    // Test mit deinem Beispiel-Input
    public static void main(String[] args) {
        String LS = " ";
        System.out.println(new Parser("Subroutine(arc Repeat(10 Go(5) Turn(-5)))" + LS +
                "Subroutine(petal Call(arc) Turn(-130) Call(arc))" + LS +
                "Subroutine(flower Turn(90) PenDown() Repeat(10 Go(30) Turn(-5)) " +
                "Repeat(9 Call(petal) Turn(30)) PenUp() Turn(208) Go(290) Turn(112))" + LS +
                "Repeat(5 Call(flower) Go(100))").parseSequence());
    }
}
Moin,
Vielen, vielen Dank auf jeden Fall für den Ansatz und die ganze Mühe , ich hätt bei der parseNumber Methode noch ne Frage:
Java:
if (program[pos] == '-')
            pos++;
was genau bringt mir diese if-bedingung, bzw. die wird ja eigentlich nie aufgerufen, oder ? die variable program besteht ja nur aus Bytes.

Danke im Vorraus und Grüße
 

httpdigest

Top Contributor
Naja, das prüft, ob das Byte an Stelle pos (nach int konvertiert) dem int entspricht, der entsteht, wenn man den char '-' nach int konvertiert. Also letztlich ist das der Codepoint des '-' Zeichens.
Du kannst byte mit char vergleichen, weil char letztlich nur ein 2-byte Integer ist. Beide werden dann aufgrund der Java-Regeln nach 4-byte int konvertiert und verglichen.
Dass man in Java char-Literale hat (also dass man '-' statt 45 schreiben kann) ist letztlich nur ein Convenience-Feature von Java.
 

Fodoboo131

Mitglied
@Fodoboo131 aber um nochmal auf dein Problem mit den regulären Ausdrücken zurückzukommen und warum du noch keine Idee für Subroutine und Repeat haben konntest:
Einzelne reguläre Ausdrücke können eben nur reguläre Sprachen definieren. Diese Sprache hier ist aber nicht regulär, sondern lediglich kontext-frei.
Das kann man auf den ersten Blick leicht daran erkennen, wenn es Rekursionen gibt und die Sprache im Prinzip der Dyck-Sprache entspricht: Man muss also für ein Vorkommen von Subroutine bzw. Repeat die öffnende "Klammer" bzw. Beginn des Subroutine/Repeat mit deren schließender "Klammer" bzw. Ende dieser Subroutine/Repeat-Instanz auf derselben Ebene matchen. Das geht mit regulären Ausdrücken nicht. Hierfür braucht es einen Kellerautomaten bzw. einen Stack.
Das erklärt so einiges, jetzt macht ja sogar die Formale Sprachen Vorlesung Sinn :D
 

Fodoboo131

Mitglied
Naja, das prüft, ob das Byte an Stelle pos (nach int konvertiert) dem int entspricht, der entsteht, wenn man den char '-' nach int konvertiert. Also letztlich ist das der Codepoint des '-' Zeichens.
Du kannst byte mit char vergleichen, weil char letztlich nur ein 2-byte Integer ist. Beide werden dann aufgrund der Java-Regeln nach 4-byte int konvertiert und verglichen.
Dass man in Java char-Literale hat (also dass man '-' statt 45 schreiben kann) ist letztlich nur ein Convenience-Feature von Java.
Alles klar, vielen Dank für die Hilfe.
Ich hab die Records weggelassen, weil ich das als Klassen alles schon implementiert hatte, aber dein Code hat mir echt geholfen, vielen Dank !!!😃
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
B Regex Order unwichtig bei Lookaheads? Java Basics - Anfänger-Themen 7
F RegEx Hilfe Java Basics - Anfänger-Themen 5
R RegEx funktioniert nicht Java Basics - Anfänger-Themen 14
B Regex Ausdrücke für Monate Java Basics - Anfänger-Themen 7
W RegEx Java Basics - Anfänger-Themen 4
K REGEX - Rechnungsbetrag wird nicht richtig ausgelesen. Java Basics - Anfänger-Themen 3
X Wie kann man ein Regex erstellen, die 8-Bit-Binär-Zahlen darstellen. Java Basics - Anfänger-Themen 1
A Zusammengesetzte Nomen/Straßennamen mit Regex-Ausdrücken trennen Java Basics - Anfänger-Themen 8
H Java Regex Aufzählung kontrollieren Java Basics - Anfänger-Themen 3
Nooobi Regex und .matches() geben keine Ausnahme Java Basics - Anfänger-Themen 10
sserio Split() -> Regex Frage. Java Basics - Anfänger-Themen 7
K Wie verneine ich einen Regex? Java Basics - Anfänger-Themen 2
L Hilfe bei RegEx Java Basics - Anfänger-Themen 4
Dimax RegEx Java Basics - Anfänger-Themen 10
P Einfacher regulärer Ausdruck (RegEx) für E-Mail-Adressen Java Basics - Anfänger-Themen 2
O Regex bei ReplaceAll ersetzt Wort und Sonderzeichen nicht. Java Basics - Anfänger-Themen 9
O Text mit Regex trennen und wieder zusammenbauen Java Basics - Anfänger-Themen 5
L Regex issue Java Basics - Anfänger-Themen 4
B Regex Kombination benötigt Java Basics - Anfänger-Themen 5
strohkolben Regex Java Basics - Anfänger-Themen 3
OnDemand Regex oder Split? Java Basics - Anfänger-Themen 5
I String.split regex Frage Java Basics - Anfänger-Themen 2
C RegEx Problem Java Basics - Anfänger-Themen 4
Dimax RegEx funktionieren nicht Java Basics - Anfänger-Themen 7
M Regex nur Zahlen und Punkt zulassen, Keine Eingabe(Leeres TextFeld) nicht zulassen Java Basics - Anfänger-Themen 6
K substitute mit regex Java Basics - Anfänger-Themen 10
G RegEx Java Basics - Anfänger-Themen 11
M Regex Erstellung Problem Java Basics - Anfänger-Themen 2
M Regex Probleme (mal wieder) Java Basics - Anfänger-Themen 3
M Regex zum Integer.parseInt Java Basics - Anfänger-Themen 4
G Regex Java Basics - Anfänger-Themen 4
M Regex-Ausdruck: Alle Zeichen bis auf ein bestimmtes erlauben (p{L}) Java Basics - Anfänger-Themen 5
M Regex anpassen Java Basics - Anfänger-Themen 19
C Regex-Problem Java Basics - Anfänger-Themen 4
M Regex für bestimmte Wörter bzw. bestimmte Zeichen erstellen Java Basics - Anfänger-Themen 5
C Positive und negative Zahlen mit Regex extrahieren Java Basics - Anfänger-Themen 8
F RegEx für Email Java Basics - Anfänger-Themen 2
F RegEx "+" nur als Zeichen, nicht als Operator oder Sonstiges Java Basics - Anfänger-Themen 2
FelixN RegEx aus einem String als String-Array zurückgeben Java Basics - Anfänger-Themen 8
FelixN RegEx Erläuterung "ˆ[ˆa-zA-Z]+$" Java Basics - Anfänger-Themen 6
F Regex Hilfe Java Basics - Anfänger-Themen 3
I Regex findet keine Treffer Java Basics - Anfänger-Themen 4
C Regex Java Basics - Anfänger-Themen 2
C Probleme bei Regex Java Basics - Anfänger-Themen 9
B Regex ignorieren von Groß - Kleinschreibung Java Basics - Anfänger-Themen 1
F Regex für Inlineformatierungen Java Basics - Anfänger-Themen 12
W Einfachen, ein beliebiges Zeichen oft erkennenden Regex Parser selber schreiben - wie vorgehen? Java Basics - Anfänger-Themen 12
O Regex Java Basics - Anfänger-Themen 2
F Switch Case Problem mit Regex lösen? Java Basics - Anfänger-Themen 6
H regex-Problem Java Basics - Anfänger-Themen 2
F Ist das ein korrekter Regex-Ausdruck? Java Basics - Anfänger-Themen 12
M Zahlenbereich mit RegEx Java Basics - Anfänger-Themen 3
B Hilfe bei Regex - Erstellung Java Basics - Anfänger-Themen 5
D regex Aufbau Frage Java Basics - Anfänger-Themen 4
W RegEx Matcher + Pattern und Emails Java Basics - Anfänger-Themen 8
R Rechenzeichen als regex Java Basics - Anfänger-Themen 10
S Ich verstehe die RegEx Tabelle von Javadoc nicht so ganz Java Basics - Anfänger-Themen 3
C Erste Schritte Regex - Datei einlesen und splitten Java Basics - Anfänger-Themen 4
F RegEx Problem Java Basics - Anfänger-Themen 8
F Regex Hilfe Java Basics - Anfänger-Themen 14
G String nach Contains/Regex Java Basics - Anfänger-Themen 2
U RegEx alle Kommas bei den Zahlen in Punkt umwandeln Java Basics - Anfänger-Themen 3
K RegEx - Multiple Line Java Basics - Anfänger-Themen 3
S Regex Pattern Java Basics - Anfänger-Themen 3
K Regex: illegal character range Java Basics - Anfänger-Themen 4
F RegEX Hilfe Java Basics - Anfänger-Themen 8
U Regex für kommaseparierte Zahlen Java Basics - Anfänger-Themen 1
K Operatoren Regex für 10er Zahlen Java Basics - Anfänger-Themen 8
V Java Regex richtig parsen Java Basics - Anfänger-Themen 2
E Brauche eine Antwort zum Thema RegEx ( Alternative zur Lösung auch gesucht ) Java Basics - Anfänger-Themen 5
N mein RegEx Java Basics - Anfänger-Themen 2
C Lösung für RegEx in Java gesucht Java Basics - Anfänger-Themen 2
B Java - Reguläre Ausdrücke - RegEx oder Regular Expressions - Eckige Klammern Java Basics - Anfänger-Themen 2
M Regex/matcher Java Basics - Anfänger-Themen 2
T Methoden String.matches() - Regex Java Basics - Anfänger-Themen 2
truesoul Regex für www Java Basics - Anfänger-Themen 0
N Methoden Straßennamen und Hausnummer - RegEx Java Basics - Anfänger-Themen 19
W Erste Schritte Regex negieren Java Basics - Anfänger-Themen 3
W String match mit Wildcard ODER Regex Java Basics - Anfänger-Themen 1
E useDelimiter-Anwednung / Regex Java Basics - Anfänger-Themen 2
C java regex Java Basics - Anfänger-Themen 2
C Regex zum matchen ausserhalb von Tags Java Basics - Anfänger-Themen 3
O Regex zwischenspeichern Java Basics - Anfänger-Themen 6
O Regex Java Basics - Anfänger-Themen 5
R Einfacher Regex Java Basics - Anfänger-Themen 3
V RegEx Java Basics - Anfänger-Themen 15
HoloYoitsu Datentypen replaceAll() mit regex? Java Basics - Anfänger-Themen 5
G regex Verständnis Problem Java Basics - Anfänger-Themen 3
G verwendung von regex Java Basics - Anfänger-Themen 3
X Regex splitten Java Basics - Anfänger-Themen 5
T Regex Problem Java Basics - Anfänger-Themen 4
B String nach regex überprüfen Java Basics - Anfänger-Themen 6
J Regex Ausdrücke im Array - Wieso werden sie nicht erkannt? Java Basics - Anfänger-Themen 4
N Regex Java Basics - Anfänger-Themen 4
F String.replaceAll() funktioniert nicht richtig oder habe ich den falschen Regex?? Java Basics - Anfänger-Themen 3
J Regex mit Return Java Basics - Anfänger-Themen 3
I Rechenoperatoren mit Regex splitten Java Basics - Anfänger-Themen 12
F Methoden replaceAll (regex) HTML Java Basics - Anfänger-Themen 3
H Problem mit Regex in Verbindung mit BufferedReader Java Basics - Anfänger-Themen 4
A Hilfe mit RegEx Java Basics - Anfänger-Themen 8

Ähnliche Java Themen

Neue Themen


Oben