<!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>