eval JS with power // general help on eval js.eval

edited April 2017 in Questions about Code

Hello,

I have a question to gotoloops for js.eval

How can I say 3 to the power of 2 (3^2)? How can I say 3! or 6 % 2 or squareroot of 81?

Is there a general help on the beast?

thanks!

Best, Chrisir ;-)

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

// for evaluation of math 
javax.script.ScriptEngine js1; 



//
String textLocal = "3 * 4" ; 

void setup() {

  js1 = new ScriptEngineManager().getEngineByName("js");
}

void draw() {

  textLocal = "4 ^ 2" ; 


  if (!textLocal.equals("")) {

    String test1 = evalJS(this, js1, textLocal);

    if (!test1.equals("")) {

      println( test1 ) ; // success
    }
  }
}
//

String evalJS(PApplet pa, ScriptEngine js, String expression) {
  js.put("p", pa);

  try {
    Object obj1=js.eval("" + expression);
    return obj1.toString();
  }

  catch (ScriptException cause) {
    //throw new RuntimeException(cause);
    return "";
  }
} // 
// 

Answers

  • JavaScript, Java, C, C++, C# share almost all of their operators.
    "4 ^ 2" is in JS as it is in Java: the XOR bitwise operator.

    The way to get 4 power 2 is via Math's pow() method: "Math.pow(4, 2);" https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow

  • ??

    That didn't answer my question...

    what do I put in line 20 above for "Math.pow(4, 2);" and for 6! and 4%3 .... ? And 81 sqrt ?

  • but thanks...... ;-)

  • @gotoloop

    Or did you mean line 20 ??

    Thanks!

  • ups, I see....

  • edited June 2016

    Reposting ScriptEngine wrapper functions here.
    initJSProcessingEnv(), evalJS(), compileJS(), runJS(), invokeJS().

    They're about 150 lines. So it's best to paste them in a separate ".pde" tab, away from our sight. :P

    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    
    import javax.script.Invocable;
    import javax.script.Compilable;
    import javax.script.CompiledScript;
    
    static final void initJSProcessingEnv
      (final ScriptEngine js, final PApplet pa) {
      js.put("p", pa); // puts sketch's PApplet as p into JS.
    
      js.put("w", pa.width);  // puts width's  current value as w into JS.
      js.put("h", pa.height); // puts height's current value as h into JS.
    
      evalJS(js, // imports Processing's core classes.
        "var PApplet         = Java.type('processing.core.PApplet'),", 
        "    PConstants      = Java.type('processing.core.PConstants')", 
        "    PFont           = Java.type('processing.core.PFont'),", 
        "    PGraphics       = Java.type('processing.core.PGraphics'),", 
        "    PImage          = Java.type('processing.core.PImage'),", 
        "    PMatrix         = Java.type('processing.core.PMatrix'),", 
        "    PMatrix2D       = Java.type('processing.core.PMatrix2D'),", 
        "    PMatrix3D       = Java.type('processing.core.PMatrix3D'),", 
        "    PShape          = Java.type('processing.core.PShape'),", 
        "    PShapeOBJ       = Java.type('processing.core.PShapeOBJ'),", 
        "    PShapeSVG       = Java.type('processing.core.PShapeSVG'),", 
        "    PStyle          = Java.type('processing.core.PStyle'),", 
        "    PSurface        = Java.type('processing.core.PSurface'),", 
        "    PSurfaceNone    = Java.type('processing.core.PSurfaceNone'),", 
        "    PVector         = Java.type('processing.core.PVector')");
    
      evalJS(js, // imports Processing's data classes.
        "var FloatDict       = Java.type('processing.data.FloatDict'),", 
        "    FloatList       = Java.type('processing.data.FloatList'),", 
        "    IntDict         = Java.type('processing.data.IntDict'),", 
        "    IntList         = Java.type('processing.data.IntList'),", 
        "    JSONArray       = Java.type('processing.data.JSONArray'),", 
        "    JSONObject      = Java.type('processing.data.JSONObject'),", 
        "    JSONTokener     = Java.type('processing.data.JSONTokener'),", 
        "    Sort            = Java.type('processing.data.Sort'),", 
        "    StringDict      = Java.type('processing.data.StringDict'),", 
        "    StringList      = Java.type('processing.data.StringList'),", 
        "    Table           = Java.type('processing.data.Table'),", 
        "    TableRow        = Java.type('processing.data.TableRow'),", 
        "    XML             = Java.type('processing.data.XML')");
    
      evalJS(js, // imports Processing's event classes.
        "var Event           = Java.type('processing.event.Event'),", 
        "    KeyEvent        = Java.type('processing.event.KeyEvent'),", 
        "    MouseEvent      = Java.type('processing.event.MouseEvent'),", 
        "    TouchEvent      = Java.type('processing.event.TouchEvent')");
    
      evalJS(js, // imports Processing's opengl classes.
        "var FontTexture     = Java.type('processing.opengl.FontTexture'),", 
        "    FrameBuffer     = Java.type('processing.opengl.FrameBuffer')", 
        "    LinePath        = Java.type('processing.opengl.LinePath'),", 
        "    LineStroker     = Java.type('processing.opengl.LineStroker'),", 
        "    PGL             = Java.type('processing.opengl.PGL'),", 
        "    PGraphics2D     = Java.type('processing.opengl.PGraphics2D'),", 
        "    PGraphics3D     = Java.type('processing.opengl.PGraphics3D'),", 
        "    PGraphicsOpenGL = Java.type('processing.opengl.PGraphicsOpenGL'),", 
        "    PJOGL           = Java.type('processing.opengl.PJOGL'),", 
        "    PShader         = Java.type('processing.opengl.PShader'),", 
        "    PShapeOpenGL    = Java.type('processing.opengl.PShapeOpenGL'),", 
        "    PSurfaceJOGL    = Java.type('processing.opengl.PSurfaceJOGL'),", 
        "    Texture         = Java.type('processing.opengl.Texture'),", 
        "    VertexBuffer    = Java.type('processing.opengl.VertexBuffer')");
    
      evalJS(js, // imports some Java's classes.
        "var ArrayList       = java.util.ArrayList,", 
        "    HashMap         = java.util.HashMap,", 
        "    File            = java.io.File,", 
        "    BufferedReader  = java.io.BufferedReader,", 
        "    PrintWriter     = java.io.PrintWriter,", 
        "    InputStream     = java.io.InputStream,", 
        "    OutputStream    = java.io.OutputStream,", 
        "    IOException     = java.io.IOException");
    
      evalJS(js, // transfers all PConstants' to JS global scope.
        "for each (var f in PConstants.class.getFields()) {", 
        "  var name = f.getName(), field = PConstants[name]", 
        "  if (!this[name])  this[name] = field", 
        "}");
    
      evalJS(js, // transfers all PApplet's static fields to JS global scope.
        "for each (var f in PApplet.class.getFields()) {", 
        "  var name = f.getName(), field = PApplet[name]", 
        "  if (field && !this[name])  this[name] = field", 
        "}");
    
      evalJS(js, // transfers all PApplet's static methods to JS global scope.
        "for each (var m in PApplet.class.getDeclaredMethods()) {", 
        "  var name = m.getName(), method = PApplet[name]", 
        "  if (method && !this[name])  this[name] = method", 
        "}");
    
      evalJS(js, // transfers all JS' Math static properties to global scope.
        "for each (var prop in Object.getOwnPropertyNames(Math))"
        + "if (!this[prop])  this[prop] = Math[prop]");
    }
    
    @ SafeVarargs static final Object evalJS
      (final ScriptEngine js, final String... statements) {
      final String expression = PApplet.join(statements, PConstants.ENTER);
    
      try {
        return js.eval(expression);
      }
      catch (final ScriptException cause) {
        PApplet.println(cause);
        System.err.println(expression);
        throw new RuntimeException(cause);
      }
    }
    
    @ SafeVarargs static final CompiledScript compileJS
      (final ScriptEngine js, final String... statements) {
      final String expression = PApplet.join(statements, PConstants.ENTER);
    
      try {
        return ((Compilable) js).compile(expression);
      }
      catch (final ScriptException cause) {
        PApplet.println(cause);
        System.err.println(expression);
        throw new RuntimeException(cause);
      }
    }
    
    static final Object runJS
      (final CompiledScript compiled) {
      try {
        return compiled.eval();
      }
      catch (final ScriptException cause) {
        PApplet.println(cause);
        throw new RuntimeException(cause);
      }
    }
    
    @ SafeVarargs static final Object invokeJS
      (final ScriptEngine js, final String funct, final Object... args) {
      try {
        return ((Invocable) js).invokeFunction(funct, args);
      }
      catch (final Exception cause) {
        PApplet.println(cause);
        System.err.println(funct);
        PApplet.printArray(args);
        throw new RuntimeException(cause);
      }
    }
    
  • edited June 2016

    And I've also tweaked some factorial JS function from:
    http://StackOverflow.com/questions/3959211/fast-factorial-function-in-javascript#3959275

    // StackOverflow.com/questions/3959211/
    // fast-factorial-function-in-javascript#3959275
    
    // Mod by GoToLoop (2016-Jun-17)
    
    final CompiledScript factorial = compileJS(js, 
      "factorial.MAX  = 100", 
      "_fact._memoize = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]", 
    
      "function factorial(n) _fact(min(abs(n | 0), factorial.MAX), _fact._memoize)", 
      "function _fact(n, f)  f[n] || (f[n] = _fact(n-1, f) * n)");
    

    And some sample on how to use it:

    runJS(factorial);
    
    long fact20 = (long) (double) evalJS(js, 
      "var fact = factorial(20)", 
      "print('Factorial of 20 ->', fact)", 
      "fact");
    println("Factorial of 20 ->", fact20, ENTER);
    
    double fact60 = (double) evalJS(js, 
      "var fact = factorial(60)", 
      "print('Factorial of 60 ->', fact)", 
      "fact");
    println("Factorial of 60 ->", fact60);
    
  • edited June 2016

    To finalize, a full old sketch converted to Nashorn JS:
    http://studio.ProcessingTogether.com/sp/pad/export/ro.9G5yFfYFFDXi1

    Don't forget to paste the 2 tabs above, since the main tab here depends on them all: >-)

    /**
     * Talking Faces [Nashorn JS] (v1.12)
     * by  Casselli (2015/Feb/07)
     * mod GoToLoop (2016/Jun/16)
     *
     * forum.Processing.org/two/discussion/17161/
     * eval-js-with-power-general-help-on-eval-js-eval#Item_9
     *
     * forum.Processing.org/two/discussion/15151/
     * how-to-convert-string-to-a-line-of-code#Item_9
     *
     * forum.Processing.org/two/discussion/9323/
     * creating-a-extra-function-in-order-to-create-
     * multiple-instances-of-the-same-group-of-primitives#Item_6
     *
     * studio.ProcessingTogether.com/sp/pad/export/ro.9G5yFfYFFDXi1
     */
    
    final ScriptEngine js = new ScriptEngineManager().getEngineByName("Nashorn");
    
    final CompiledScript globals = compileJS(js, 
      "BG = ~~0xff403090, FACES = 4, FPS = 12", 
      "faces = Array(FACES)");
    
    final CompiledScript setup = compileJS(js, 
      "p.frameRate(FPS)", 
      "p.ellipseMode(Face.MODE), p.rectMode(Face.MODE)", 
      "p.stroke(Face.STROKE), p.strokeWeight(Face.WEIGHT)");
    
    final CompiledScript draw = compileJS(js, 
      "p.background(BG)", 
      "for each (var f in faces)  f.display()");
    
    final CompiledScript initFaces = compileJS(js, 
      "faces[0] = new Face(w>>2,   h>>2,   w/3,     h/2.5)", 
      "faces[1] = new Face(3*w>>2, h>>2,   w/3/1.2, h/2.5/1.2)", 
      "faces[2] = new Face(w>>2,   3*h>>2, w/3/2,   h/2.5/2)", 
      "faces[3] = new Face(3*w>>2, 3*h>>2, w/3*1.2, h/2.5*1.2)");
    
    final CompiledScript classFace = compileJS(js, 
      "function Face(x, y, w, h) {", // Face's constructor + properties.
      "  this.x = x, this.y = y, this.w = w, this.h = h, this.m = .5*(w+h)", 
      "}", 
    
      // Face's static constant(s):
      "Face.MODE = CENTER, Face.WEIGHT = 1.5, Face.STROKE = 0", 
      "Face.FACE = ~~0xffFFFF00, Face.NOSE = ~~0xffFF0000", 
      "Face.EYES = 0, Face.MOUTH = ~~0xffFF00FF", 
    
      // Face's prototype method(s):
      "Face.prototype.display = function () {", 
      "  p.fill(Face.FACE), p.ellipse(this.x, this.y, this.w, this.h)", 
      "  p.fill(Face.NOSE), p.ellipse(this.x, this.y, this.w/6, this.h>>2)", 
    
      "  var ww = this.w/6.5, yh = this.y - (this.h>>3), mm = this.m/9.65", 
      "  p.fill(Face.EYES)", 
      "  p.ellipse(this.x - ww, yh, mm, mm)", 
      "  p.ellipse(this.x + ww, yh, mm, mm)", 
    
      "  p.fill(Face.MOUTH)", 
      "  p.rect(this.x, this.y + this.h/3.2, this.w/2.6, p.random(this.h>>4))", 
      "}");
    
    void setup() {
      size(600, 600);
      smooth(4);
    
      initJSProcessingEnv(js, this);
    
      runJS(globals);
      runJS(classFace);
      runJS(setup);
      runJS(initFaces);
    
      runJS(factorial);
    
      long fact20 = ((Number) evalJS(js, 
        "var fact = factorial(20)", 
        "print('Factorial of 20 ->', fact)", 
        "fact")).longValue();
      println("Factorial of 20 ->", fact20, ENTER);
    
      double fact80 = ((Number) evalJS(js, 
        "var fact = factorial(80)", 
        "print('Factorial of 80 ->', fact)", 
        "fact")).doubleValue();
      println("Factorial of 80 ->", fact80);
    }
    
    void draw() {
      runJS(draw);
    }
    
  • Thank you so much.

    To be honest , I wanted a way to eval equations like 3 + 4 for my little ChatBot. Google can answer stuff like 3+4 or 4^3 (4 to the power of 3).

    So I tried eval and some of it like 3*4 worked right away.

    Now I realized that this eval can do full codes and not only formulas

    Is there another way to just evaluate math formulas typed by the user?

    Thanks, Chrisir

  • edited June 2016

    Now I realized that this eval() can do full codes and not only formulas.

    Yup! Nashorn is a full ECMA5 JS implementation embedded into Java's API. ~O)

    And here's a nice trick for wrappers evalJS() & compileJS():
    Their last parameter is String... statements. It means they can accept an array of strings.
    Thus we can pass loadStrings() to them. That is, we can load scripts from files. *-:)

    Is there another way to just evaluate math formulas typed by the user?

    My initJSProcessingEnv() transfers all Processing's static API to the JS' global scope.
    Therefore we can eval() strings like: sqrt(9), pow(3, 2), cos(TAU), etc.

    But watch out: Nashorn got access to the whole Java's API + Processing's API.
    It means n1 can type in whole programs for eval() w/ malicious intent and hack the computer! :-SS

  • initJSProcessingEnv

    seems great

    Is there a way only eval math formula without the security issue big as a truck?

  • Gotoloop could you answer this bit for me

    Thank you...

  • edited June 2016 Answer ✓

    Hmm. That's gonna need some study from here:

    Maybe I may try out to come up w/ some solution based on it... :-\"

Sign In or Register to comment.