2

It should be simple to create JavaScript intepreter in JavaScript using eval. I got this (using jQuery terminal):

term = $('#term_demo').terminal(function(command, term) {
    if (command !== '') {
        var result = window.eval("(" + command + ")");
        if (result !== undefined) {
            term.echo(String(result));
        }
    } else {
       term.echo('');
    }
}, {
    greetings: 'Javascript Interpreter',
    name: 'js_demo',
    height: 200,
    prompt: 'js> '
});

Demo

but it don't work when I execute function foo() { ... } the foo is not defined I need to use foo = function() { ... }. eval act like executed within (function() { <code> })(). Can it be there more complicated code that will not work too?

Is it possible to create a JavaScript interpreter using simple code without use the of js.js, that will work the same as browser console?

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • jQuery terminal does not really look "simple". What is your goal, and where are you stuck? – Rob W Jun 24 '12 at 12:40
  • @RobW By simple I mean the interpreter function (8 line of code and it can have 2), this don't work `function foo(x) { return x+x; }` and `foo(10)` – jcubic Jun 24 '12 at 12:47
  • Why don't you want to use js.js? – madfriend Jun 24 '12 at 13:17
  • @Alex Because js.js is `594KB` and JS already have interpreter it's call `eval` and it have `4b` (few more if I want to actually use in the code). – jcubic Jun 27 '12 at 11:28

2 Answers2

2

I've created a bookmarklet which appends a kind of REPL in a page, designed for the major five browsers (Chrome 1+, IE 6+, Firefox 1+, Safari 3+, Opera 9+Can't remember the exacte version).
The core component, which evaluates the code is posted below, slightly modified + annotated.

/**
 * Evaluates some code in the global scope.
 * @param String code: Code to eval
 * @return Stringified result, prefixed with 'E:' if error.
 */
function globalEval(/*string*/ code) {
    var win = window, r, raw;
    try {
        if (win.execScript) { // eval in IE sucks, so use execScript instead
            r = win.$_$_$globalEval$_$_$ = {c:code};
            win.execScript('try{$_$_$globalEval$_$_$.r=eval($_$_$globalEval$_$_$.c);}catch(e){$_$_$globalEval$_$_$.e=e}');
            // /*Optional clean-up:*/ delete win.$_$_$globalEval$_$_$;
            if (r.e) throw r.e; // Error
            raw = r.r;
        } else {
            raw = win.eval(code);
        }
        r = '' + raw; // Stringify in the try-block
                      // It is possible that an error is thrown
                      // for example, for the following code: ({toString:1})
    } catch(err) {
        r = (err + ''); // Convert error to string
        // IE: If found, "[object" will be at index zero, which is falsy
        if (!r.indexOf('[object')) r = err.message;
        // r = 
        r = 'E:' + (raw=r);
    } finally {
        // raw = unmodified result (or Error instance)
        // FOR THIS EXAMPLE, raw is not used, and string r is returned
        return /*string*/ r;
    }
}

I've implemented the functionality in a form, which contains several controls including an input+output textarea.

Note: The code is evaluated in the global context. And such, any variables in code will be leaked to the global scope. For an interpreter, you could use an iframe to create a new scope (and modify var win in my function).

var win = frames['name_of_frame'], ... // or
var win = frame_DOM_element.contentWindow, ...
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • I prefer global scope since I want to give a way to interact with the page. I thought that I need to put command within parentheses but it look like I don't. Thanks. – jcubic Jun 24 '12 at 13:35
0

The parenthesis you're appending around your incoming command produces illegal syntax. You should wrap it with an anonymous self-executing function instead.

Example: http://jsfiddle.net/bW6Fv/1/

var command = "function foo(x){return x+x;} alert(foo(10));";

window.eval("!function(){" + command + "}()");​

EDIT

If you want your evaluated scripts to be available globally, you'll have to leave them unwrapped or explicitly assign values to the global window object.

Example: http://jsfiddle.net/bW6Fv/2/

var command = "window.foo = function(x){return x+x;}; alert(foo(10));";

window.eval("!function(){ " + command + "}();");
window.eval("alert(foo(20));");

command = "function bar(x){return x / 2;}; alert(bar(10));";

window.eval(command);
window.eval("alert(bar(20));");
Brandon Boone
  • 16,281
  • 4
  • 73
  • 100
  • Thanks I use it in one code, but what about state between calls `eval("(function() { function foo(x) { return x+x; } })()"` and `eval("(function() { alert(foo(10)); }"` different context foo is undefined, how can I handle them? – jcubic Jun 24 '12 at 13:20
  • @jcubic In your first code, `function foo` is created **locally**. You have to hack a lot if you want to change that JavaScript **feature**. – Rob W Jun 24 '12 at 13:28