BadVPN – Rev 1

Subversion Repositories:
Rev:
<!DOCTYPE html>
<html>
<head>
<title>NCD in Javascript</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>

<script src="emncd.js"></script>

<script>

var cfunc_emncd_start = Module.cwrap('emncd_start', 'null', ['string']);
var cfunc_emncd_stop = Module.cwrap('emncd_stop', 'null', []);

function on_start ()
{
    var code = document.getElementById('codetext').value;
    
    var loglevel = 0;
    for (i = 0; i <= 5; i++) {
        if (document.getElementById('loglevel' + i).checked) {
            loglevel = i;
        }
    }

    cfunc_emncd_start(code, loglevel);
}

function on_stop ()
{
    cfunc_emncd_stop();
}

function load_example (num)
{
    document.getElementById('codetext').value = document.getElementById('example' + num).value;
}

</script>

This is a quick port of my <a href="http://code.google.com/p/badvpn/wiki/NCD">NCD programming language</a>
to Javascript using the <a href="https://github.com/kripken/emscripten">Emscripten</a> compiler.
<br />

Please open the Javascript console so you can see the output (Chrome: CTRL+Shift+J. Firefox: CTRL+Shift+K).
<br />

<textarea id="example1" style="display:none;">
process hello {
    println("hello, world");
    exit("0");
}
</textarea>

<textarea id="example2" style="display:none;">
process foo {
    println("Starting up, please wait...");
    rprintln("Goodbye World!");

    sleep("500", "300"); # sleeps 500ms on init and 300ms on deinit

    println("Hello World!");
    rprintln("Shutting down, please wait...");
}
</textarea>

<textarea id="example3" style="display:none;">
process hello {
    var("0") ctr;

    blocker() blk;
    blk->up();
    blk->use();

    num_modulo(ctr, "3") m_three;
    num_modulo(ctr, "5") m_five;
    num_equal(m_three, "0") d_three;
    num_equal(m_five, "0") d_five;
    and(d_three, d_five) d_three_five;

    If (d_three_five) {
        var("fizzbuzz") text;
    } Elif (d_three) {
        var("fizz") text;
    } Elif (d_five) {
        var("buzz") text;
    } else {
        var(ctr) text;
    } branch;

    println(branch.text);

    num_add(ctr, "1") i;
    num_modulo(i, "20") i;
    ctr->set(i);

    sleep("100", "0");
    blk->downup();

}
</textarea>

<textarea id="example4" style="display:none;">
process main {
    var({"0", "1", "3", "2", "2", "3", "1", "1", "6", "end"}) list;
    value(["1":"one", "2":"two", "3":"three"]) map;

    call("replace", {"_caller.list", "_caller.map"}) replace;

    to_string(replace.result) str;
    println(str);
    exit("0");
}

template replace {
    alias(_arg0) list;
    alias(_arg1) map;

    value({}) new_list;
    Foreach (list As elem) {
        map->try_get(elem) y;
        If (y.exists) {
            new_list->insert(new_list.length, y);
        } Else {
            new_list->insert(new_list.length, elem);
        };
    };

    alias("new_list") result;
}
</textarea>

<textarea id="example5" style="display:none;">
process hello {
    println("Hello, NCD in Javascript!");
    println("Will now wait 2 seconds.");
    rprintln("Goodbye.");

    sleep("2000", "3000");
    rprintln("Waiting 3 seconds before dying.");

    call("func", {"FirstArg", "SecondArg"});
    
    var({"one", "two", "three"}) list;
    to_string(list) str;
    println(str);
    
    Foreach (list As elem) {
        println("start ", elem);
        rprintln("stop ", elem);
    };

    println("We're finished. Press \"Request termination\" to unwind us.");
    rprintln("Terminating...");
}

template func {
    println("func here, my args are: ", _arg0, " ", _arg1);
    rprintln("func going away");
}
</textarea>

<textarea id="example6" style="display:none;">
process main {
    # Turing machine specification.
    var("B") blank;
    var([
        {"0", "0"}:{"0", "0", "right"},
        {"0", "1"}:{"1", "x", "right"},
        {"1", "1"}:{"1", "1", "right"},
        {"1", "0"}:{"2", "0", "right"},
        {"2", "0"}:{"2", "0", "right"},
        {"2", "1"}:{"3", "1", "right"},
        {"3", "1"}:{"3", "1", "right"},
        {"3", "0"}:{"4", "1", "left"},
        {"3", "B"}:{"4", "1", "left"},
        {"4", "1"}:{"4", "1", "left"},
        {"4", "0"}:{"5", "0", "left"},
        {"5", "0"}:{"5", "0", "left"},
        {"5", "1"}:{"6", "1", "left"},
        {"5", "x"}:{"h", "x", "stay"},
        {"6", "1"}:{"6", "1", "left"},
        {"6", "x"}:{"0", "x", "right"},
        {"6", "0"}:{"0", "0", "right"}
    ]) rules;
    var("0") initial_state;
    var({}) initial_tape_left;
    var({
        "1", "1", "1", "1", "0", "0", "1", "1", "1", "1"
    }) initial_tape_right;

    # Perform the computation, stopping when no rule matches.
    call("turing", {blank, rules, initial_state, initial_tape_left, initial_tape_right}) results;

    # Print results.
    to_string(results.tape_left) tape_left;
    to_string(results.tape_right) tape_right;
    to_string({results.side, results.pos}) head_pos;
    to_string(results.state) head_state;
    println("Tape L: ", tape_left);
    println("Tape R: ", tape_right);
    println("Head position: ", head_pos);
    println("Head state: ", head_state);

    exit("0");
}

template turing {
    alias("_arg0") blank;
    value(_arg1) rules;
    alias("_arg2") initial_state;
    alias("_arg3") initial_tape_left;
    alias("_arg4") initial_tape_right;

    # Head state.
    var(initial_state) state;

    # Tape. Positions go like this: ... L2 L1 L0 R0 R1 R2 ... 
    value(initial_tape_left) tape_left;
    value(initial_tape_right) tape_right;

    # Make sure each side of the tape has at least one symbol so we can flip easily.
    tape_left->insert(tape_left.length, blank);
    tape_right->insert(tape_right.length, blank);

    # Head position.
    var("right") side;
    var("0") pos;

    # Enter loop.
    blocker() loop_blk;
    loop_blk->up();
    loop_blk->use();

    # Get symbol under head.
    concat("tape_", side) tape_name;
    alias(tape_name) cur_tape;
    cur_tape->get(pos) symbol;

    # Look for a matching rule.
    rules->try_get({state, symbol}) rule;

    If (rule.exists) {
        # Extract directions from rule.
        rule->get("0") new_state;
        rule->get("1") new_symbol;
        rule->get("2") move;

        # Change head state.
        state->set(new_state);

        # Replace symbol under head.
        cur_tape->remove(pos);
        cur_tape->insert(pos, new_symbol);

        # Branch based on how we move.
        strcmp(move, side) is_outside;
        strcmp(move, "stay") is_stay;
        strcmp(pos, "0") is_zero;
        If (is_outside) {
            # Increment position.
            num_add(pos, "1") new_pos;
            pos->set(new_pos);

            # If the new position is out of range, extend tape.
            strcmp(pos, cur_tape.length) need_extend;
            If (need_extend) {
                cur_tape->insert(pos, blank);
            };
        } elif (is_stay) {
            # Nop.
            getargs();
        } elif (is_zero) {
            # Flip side, leave pos at zero.
            side->set(move);
        } else {
            # Decrement position.
            num_subtract(pos, "1") new_pos;
            pos->set(new_pos);
        };

        # Continue loop.
        loop_blk->downup();
    };
}
</textarea>

Examples:
<button onclick="load_example(1)">Hello World</button>
<button onclick="load_example(2)">Sleep</button>
<button onclick="load_example(3)">FizzBuzz</button>
<button onclick="load_example(4)">Replace</button>
<button onclick="load_example(5)">Foreach, call</button>
<button onclick="load_example(6)">Turing Machine</button>
<br />

<textarea rows=30 cols=80 id="codetext">
process hello {
    println("hello, world");
    exit("0");
}
</textarea>
<br />

Loglevel: 
<input type="radio" name="loglevel" id="loglevel0"> None
<input type="radio" name="loglevel" id="loglevel1"> Error
<input type="radio" name="loglevel" id="loglevel2" checked> Warning
<input type="radio" name="loglevel" id="loglevel3"> Notice
<input type="radio" name="loglevel" id="loglevel4"> Info
<input type="radio" name="loglevel" id="loglevel5"> Debug <br />

<button onclick="on_start()">Start NCD</button>
<button onclick="on_stop()">Request termination</button>
<br />
Note: if you get the interpreter stuck and unable to terminate, just refresh the page

</body>
</html>