Code evaluation during program execution

edited March 2017 in Questions about Code

I am developing a game for helping people to learn coding. Processing 3 in Java mode.

There is a Player class in my code, with functions like player.moveForward(2), player.turnRight() and etc. When these are called, the player object recalculates it's position and in the next draw() cycle it shows in the new place. So far so good.

I would like to let the user move the player in the map using code. So, if I read user input and store "player.moveForward(5);" in a string, how do I make this arbitrary piece of code to be executed during runtime?

I can't parse the code myself because I want people to be able to write more complex things, such as for (int i = 0 ; i < 3; i++) { player.moveForward(); }.


I tried to use the ScriptEngine to dynamically create an script, inject my player object (which handles the calculations), run the user commands, then read the final position to apply changes on the GUI. But it didn't work, the player object doesn't seem to be properly injected, properties and method are not available in the javascript context. If that's really the way to go, what am I doing wrong here?

println(player.colour); // this works fine
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("javascript");
engine.put("player", player);
String instruction = "print(player.colour);"; // this doesn't work as expected
try {
  engine.eval(instruction);
} catch (Exception ex) {
  println (ex.getMessage());
}

Output for the piece of code above:

white
undefined

The Player class has a public String colour;. In the example above I get "undefined" as output. How should I implement my classes to be able to see their properties and methods in the javascript side?

Answers

  • That's fairly complex. Java has to be compiled before its run (it's Object Oriented). So I doubt if it's even possible.

  • Based on the features you want, you may as well just write code to run strings yourself. See this excellent example by @Chrisir which has many features - https://forum.processing.org/two/discussion/20706/how-3d-turtle-like-in-logo-but-3d-math-problem#latest

  • edited March 2017 Answer ✓
    • PDE's pre-compiler automatically prefix methods w/ public for us behind the scene if they don't have 1 already for ".pde" tab files. :bz
    • However, for interfaces, classes, enums, fields and even constructors, we need to explicitly prefix them w/ public if something needs them that way in order to access them. :-\"
    • So, in order for Nashorn to access your Player's colour field, both that field and its class need to be declared as public. :>

    // forum.Processing.org/two/discussion/21709/
    // code-evaluation-during-program-execution#Item_3
    
    // GoToLoop (2017-Mar-30)
    
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptEngine;
    import javax.script.ScriptException;
    
    final ScriptEngine js = new ScriptEngineManager().getEngineByName("Nashorn");
    
    public class Player {
      public color colour = #FF0000;
    }
    
    final Player player = new Player();
    
    static final String SCRIPT =
      "var PApplet = Java.type('processing.core.PApplet');" +
      "print(PApplet.hex(player.colour, 6));" +
      "player.colour = 0xff0080A5;";
    
    void setup() {
      println(hex(player.colour, 6)); // FF0000
    
      js.put("player", player);
    
      try {
        js.eval(SCRIPT); // FF0000
      } 
      catch (final ScriptException ex) {
        System.err.println(ex.getMessage());
      }
    
      println(hex(player.colour, 6)); // 0080A5
    
      exit();
    }
    
  • Quite a few examples are included as textfiles and you can load and execute them (hit # or click mouse on play button)

  • edited March 2017

    @paulera -- re:

    I am developing a game for helping people to learn coding [...] how do I make this arbitrary piece of code to be executed during runtime?

    For a related project you might be interested in checking out @quark 's QScript -- and, in particular, the example "Scripting Game Objects" part 1/2. The runtime-reprogrammable scripting control of game objects is done through a G4P code editor built into the sketch itself.

  • As @GoToLoop said, classes must be declared as public.

    Just changing my class declaration to public class Player { allowed them to be shared on the fly with the script. And solved my problem.

    EXAMPLE

    import javax.script.*;
    
    void setup() {
    
      // creates and object to be manipulated by
      // the script
      MyClass obj = new MyClass();
    
      // object state BEFORE running the script
      println("Before:");
      println("message = " + obj.message);
      println("count = " + obj.count);
    
      // creates a javascript engine
      ScriptEngineManager mgr = new ScriptEngineManager();
      ScriptEngine engine = mgr.getEngineByName("javascript");
    
      // injects the object in the script scope
      engine.put("obj", obj);
    
      // runs the script
      try {
        engine.eval(
          "obj.message=\"Hello World!\";" +
          "while (obj.count < 5) {" +
          "   obj.incCount();" + 
          "}"
        );
      } catch (Exception ex) {
        println (ex.getMessage());
      }
    
      // object state AFTER running the script
      println("After:");
      println("message = " + obj.message);
      println("count = " + obj.count);
    }
    
    public class MyClass {
      public String message;
      public int count = 0;
      public void incCount() {
        this.count++;
      }
    }
    

    Output:

    Before:
    message = null
    count = 0
    After:
    message = Hello World!
    count = 5
    
  • edited March 2017

    Glad it's working now! <:-P
    Remember, methods don't need an explicit public b/c PDE's pre-processor already prefix them for us.

    Also we can use ' instead of " in JS.
    So "obj.message = \"Hello World!\";" can be written like this: "obj.message = 'Hello World!';". :D

  • You should also consider loadStrings() your Nashorn ".js" scripts.
    Visit this forum thread for some utility functions I did for ScriptEngine: :bz
    https://forum.Processing.org/two/discussion/17161/eval-js-with-power-general-help-on-eval-js-eval

  • Thanks @GoToLoop, very much appreciated.

Sign In or Register to comment.