JS-Interpreter icon indicating copy to clipboard operation
JS-Interpreter copied to clipboard

Creating a function?

Open mercmobily opened this issue 7 years ago • 3 comments

Hi,

Is it possible to create a function, and then run the interpreter while passing arguments to the created function?

I need to replicate this:

var f = new Function('return a + b);
res = f(10,5)

But without using new Function (which is obviously an eval) I "kind of" managed by writing a function that creates the string that defines the function and passes it the params:

global.jsRun = function (code, ...args) {
  var fullCode = `function run(){${code}\n}\nrun(${args.map(a => JSON.stringify(a)).join(',')})`
  console.log(`code: ${fullCode}`)
  var myInterpreter = new global.JSInterpreter.Interpreter(fullCode)
  myInterpreter.run()
  return myInterpreter.value
}

However, one of the parameters is a rather large object, and it messses up the code. Is there a way to create a function and then pass it parameters directly from the API, rather than just evaluate code?

THANK YOU!!!

mercmobily avatar Sep 12 '18 01:09 mercmobily

Well I think I got somewhere. Here it is: (remember that this is node)

global.acorn = require('./node_modules/js-interpreter/acorn.js')
global.JSInterpreter = require('./node_modules/js-interpreter/interpreter.js')
global.jsRun = function (code, ...args) {
  var fullCode = `var arguments = JSON.parse(_args);${code}`

  var initFunc = function (interpreter, scope) {
    interpreter.setProperty(scope, '_args', JSON.stringify(args))
  }
  var myInterpreter = new global.JSInterpreter.Interpreter(fullCode, initFunc)
  myInterpreter.run()
  return myInterpreter.value
}

console.log(jsRun('arguments[0]', 10, 20))

OR, to actually run it in a function (so that 38472384 already written calculators don't have to be rewritten since they use return):

// JSInterpreter runner function
global.acorn = require('./node_modules/js-interpreter/acorn.js')
global.JSInterpreter = require('./node_modules/js-interpreter/interpreter.js')
global.jsRun = function (code, ...args) {
  var fullCode = `var args = JSON.parse(_args);function f(){${code}};f(args)`

  var initFunc = function (interpreter, scope) {
    interpreter.setProperty(scope, '_args', JSON.stringify(args))
  }
  var myInterpreter = new global.JSInterpreter.Interpreter(fullCode, initFunc)
  myInterpreter.run()
  return myInterpreter.value
}

console.log(global.jsRun('return args[0] + args[1]', 10, 20))

Sounds OK doesn't it? The code itself doesn't have anything extra except the very first line; so, any errors will give out the right line...

mercmobily avatar Sep 12 '18 02:09 mercmobily

A better approach is to put the values you want to pass into variables in the global scope:

var a = /* ... */, b = /* ... */;
var myInterpreter = new Interpreter('a + b');
myInterpreter.setValueToScope('a', myInterpreter.nativeToPseudo(a));
myInterpreter.setValueToScope('b', myInterpreter.nativeToPseudo(b));
myInterpreter.run();
var r = myInterpreter.pseudoToNative(myInterpreter.value);

Of course if you want to treat user-supplied code as the body of a function (so the user can use return to explicitly specify the result value) then just wrap the user code in an IIFE:

var userCode = /* ... */;
var code = '(function(a, b) {' + userCode + '})(a, b)';
var myInterpreter = new Interpreter(code);
/* ... */

cpcallen avatar Sep 12 '18 16:09 cpcallen

Hey... just one note: neither of them are documented. Or did I miss them? Would you mind me doing a PR to improve documentation? If so, are there other methods that are worthwhile knowing?

mercmobily avatar Sep 13 '18 01:09 mercmobily