Einfache Scripting Befehle: Java in Java

Ich habe ein Java Programm das ein TextArea hat.

In diesem TextArea kann man jetzt ein kurzes Programm eingeben:

int zaehler = 0;
String infoText = "Stand: ";

public main() {
    
      startLoop();
}

public startLoop() {

     while(true) {
           zaehler++;
           if(zaehler == 3) {
                zaehler = 0;
                sendeAnRoboter(infoText + " Bereit")
           }
     }
}

Wichtig: sendeAnRoboter() wurde nicht definiert, sondern wird in der Script Sprache vordefiniert.

Also im Prinzip was ich möchte ist, innerhalb meines Java-Programs ein Script (in Java Form) aufrufen, dass eine interne Java Funktion sendeAnRoboter() aufruft.

Ich kenne die üblichen Scriptsprachen, Groovy, Beanshell, LUA, … und auch die Begriffe Lexer, Parser, ActionTree etc. Doch ehrlich gesagt, nach tagelangem recherchieren, Lesen, probieren, sehe ich keinen Weg wie ich das oben genannte umsetzen könnte. Vor allem “eigene” Befehle einfach zu generieren, die dann innerhalb des laufenden Java Programms ausgeführt werden.

Was ich gefunden habe sind nur “Compiler” und keine “Interpreter” Lösungen. Und wenn, dann keine Java In Java Interpreter (ich möchte kein Javascript).

Deshalb hier mein hilfesuchender Post… weiß jemand eine simple Lösung, oder überhaupt eine Lösung, ein Tutorial das die Umsetzung nicht nur “streift” und man sich 6 Monate den Rest erarbeiten muss? Ich scheue keine Mühen, aber 5 Tage sind was anderes als ein halbes Jahr :slight_smile:

ANTLR habe ich mir schon mehrmals angesehen und mehrmals ausprobiert, aber es scheitert. Es ist zu komplex. Es gibt zig Dateien und zig Stellen die man editieren muss. Auch durch zahlreiche YouTube Videos (die meistens > 1 Stunde dauern) bin ich nicht wirklich zum Kern vorgedrungen. Vor allem auch, weil hier zu 99% nur Compiler gebaut werden, und keine Echtzeit-Interpreter.

Ich habe mir deshalb einen eigenen Parser, Lexer, Interpreter gebaut, der auch funktioniert, nur 1. er kann keine C/Java Syntax, und er st ein bisschen Langsam. Also eher eine Notlösung um überhaupt eine Scriptsprache in Java zu haben.

Das hier ist meine Übergangslösung:

compiler.addMethod(new Method("replaceStr",
    (byte)opcodeCounter++, 
    new int[] {VAR_NAME, STRING, STRING}, new String[] {"variable","oldChar","newChar"}) {
@Override public void run() {				
    String varName= getNextValueAsVarName();	
    Object value	= getNextValueAsString();	
    Object value2	= getNextValueAsString();			
    replaceStr(varName, value, value2);																
}		
});		

Im Gegensatz zu anderen Lösungen muss ich nicht noch an duzenden anderen Stellen irgendwas editieren. Dieser kurze Block löst/kreiert eine komplette Funktion (Methoden Definition / Interpreter Handling + ByteCode Reads + Java Funktionsaufruf), und springt dann zur Java Methode “replaceStr”. Ich denke einfacher geht es kaum. Aber wie schon erwähnt… das ersetzt keine komplette C/Java Syntax. Ich könnte meins noch weiter ausbauen, doch ich denke das braucht Monate. Und da man ja das Rad nicht neu erfinden soll, dachte ich, es gibt da draussen jemanden der sagt: “alles ganz easy, schau mal hier!”

Wäre nett, wenn meine Suche hier enden würde, und jemand den Link der Links hat, oder selbst ein Tutorial für diesen Fall geschrieben hat. Ich konnte jedenfalls kein einziges Tutorial für diesen speziellen Fall finden. Sonst wäre ich ja nicht hier :slight_smile:

Das hier ist mein letzter Versuch.

Habe endlich ein Beispiel gefunden, das wirklich sehr sehr einfach ist:

… aber “Javascript”.

Gibt es kein Java in dieser einfachen Form für Android?

Bei solchen Belangen schlage ich immer vor, sich Quellcodes fertiger Interpreter und/oder Compiler anzusehen, damit man mal eine Vorstellung davon bekommt, an was man alles denken muss - Tomcat für JSP oder Rhino für JavaScript z.B.

Tutorials für allgemeinen Interpreter-Bau wären evtl. sogar noch hilfreicher.

Das Problem bei Java ist ja, dass es eine Compilersprache ist, wobei der Compiler Bytecode für eine Ziel-VM produziert. Selbst der Java-Interpreter aus den Anfangszeiten interpretierte nur den Byte- und nicht den Quellcode. Deswegen findet man vermutlich auch keine Java-Quellcode-Interpreter.

Ab Java 9: JShell?

Nice… kannte ich auch noch nicht. Bin auch seit Java 8 nicht mehr auf dem Laufenden, weil mich andere Dinge plagen. Die Frage, ob die auch für älteren Code funktioniert, erübrigt sich wohl, solange Java abwärtskompatibel ist, also genügt es, die JShell einfach zu einem Java 8 Environment hinzu zu installieren (Sourceforge)?

JShell hatte ich auch nicht auf dem Schirm, aber das Besipiel aus der JShell JavaDoc sieht ja aus, als wäre es schon recht nah an dem, was hier das Ziel ist.

Du hattest betont, dass das ganze interpretiert sein muss - gibt es dafür einen bestimmten Grund?

Ab irgendeinem Punkt verschwimmt die Grenze zwischen “interpretiert und nicht interpretiert”. Dass der Grund für diese Anforderung war, dass es möglich sein sollte, “das eingegebene ‘direkt’ auszuführen” (d.h. eben nicht ~“eine .class-Datei zu erstellen, die dann in einer neuen JVM-Instanz benutzt werden kann”), dann könnte fast schon das passen, was ich irgendwann mal als Antwort zu “Convert String To Code” auf stackoverflow geschrieben hatte: Das ist eine kleine Utility-Klasse, wo man direkt sowas machen kann:

/**
 * Simple example: Shows how to add and compile a class, and then
 * invoke a static method on the loaded class.
 */
private static void simpleExample()
{
    String classNameA = "ExampleClass";
    String codeA =
        "public class ExampleClass {" + "\n" + 
        "    public static void exampleMethod(String name) {" + "\n" + 
        "        System.out.println(\"Hello, \"+name);" + "\n" + 
        "    }" + "\n" + 
        "}" + "\n";

    RuntimeCompiler r = new RuntimeCompiler();
    r.addClass(classNameA, codeA);
    r.compile();

    MethodInvocationUtils.invokeStaticMethod(
        r.getCompiledClass(classNameA), 
        "exampleMethod", "exampleParameter");
}

Allerdings eben nur in einem JDK, nicht in einer JRE (in letztere sind die Compilertools nicht mit drin) …

JShell kannte ich auch noch nicht. Aber wie ich gesehen habe endet das doch wieder irgendwann bei “var test = …”. “Var” scheint unbedingt notwendig beim interpretieren zu sein.

Siehe das Beispiel Java9/10 hier:

Das simpleExample() wäre natürlich fein, aber da scheint es ja auch Einschränkungen zu geben, und ich suche unter anderem etwas für Java8 und Android. Im Prinzip muss es nicht 1:1 interpretiert sein, sondern in Java eine komplette Text-Datei übersetzt, und dann ausgeführt werden können. Aber das mit JShell geht denke ich nicht, denn dazu nutzt man wohl “jshell.exe”. Eine “interne” Java Lösung, also Java in Java konnte ich nicht finden.

Es scheint wohl wirklich so zu sein, dass Java in Java zu kompliziert ist, sonst würde man so eine Lösung wohl überall finden können. Das einzige was sehr verbreitet ist ist Javascript, also Rhino. Und im Grunde läuft es ja gut, nur Javascript gefällt mir mit seiner abweichenden Form zu C nicht besonders (var). Da muss man ständig umdenken. Also man switcht ständig zwischen Java und der Scriptsprache “Javascript” hin und her. Was nicht gerade “komfortabel” und “logisch” ist. Sich den Rhino Quelltext anzuschauen wäre eine Option, und dann das alles auf Java zu trimmen. Man könnte das “var” zu Objekt machen usw. .

Also denke ich, um den Aufwand klein zu halten, wird es wohl bei Javascript bleiben, wenn nicht noch ein “geht damit doch nicht” dazwischen kommt.

Danke für die vielen Hinweise. Vielleicht baue ich ja doch irgendwann mal die Java in Java Lösung, doch jetzt eher nicht, denn das würde zu lange dauern. Bestimmt 1 Jahr. Und die Zeit habe ich gerade nicht. Ansonsten würde ich, so denke ich, es tun.

Ups, was ist das denn?

Scheint auf BeanShell aufzubauen. Also auf den ersten Blick 1:1 Java/Android

Ist wohl ein Reinfall :slight_smile:

“adb shell am start --activity-clear-top -n org.ligi.ajsha/.ui.EditActivity --es “org.ligi.ajsha.ui.EditActivity.EXTRA_CODE” echo \'\cat file.java`’`”

…also, auch keine Lösung :_)

Habe noch Snapscript gefunden, endet aber auch in “var”:

private var isAlwaysRelevant: Boolean = false;
private var isAlive: Boolean = false;

Habe ein Example für BeanShell gefunden, das wohl funktioniert:

Allerdings ist die Performance gegenüber Javascript (Rhino) eher bescheiden:

http://pankaj-k.net/spubs/articles/beanshell_rhino_and_java_perf_comparison/index.html

Du kannst ganz normal Java in der JShell ausführen. var ist nur seit java 10 ein keyword und hat nichts mit JavaScript zu tun.