From dfbdd2f47d46c730a3c3cd388c7ad6a26c4bc24b Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Mon, 2 Mar 2015 23:36:42 -0500 Subject: [PATCH] Add web workers, parse automatically I thought the demo might be nicer if it used web workers to parse the grammar in the background. That way it doesn't block the DOM, so it's a bit smoother. Additionally, since we're not blocking the DOM, we can just automatically re-parse the file as the user types. I set a debounce of 700ms so that it doesn't fire too frequently. I also removed the "Parse" button entirely, and set it up so there's a little "Parsing..." message while it's parsing. I also made to sure to do a fallback if web workers aren't supported in the browser (and I tested that it works. You can try a demo [here](https://nolanlawson.s3.amazonaws.com/jison/try/index.html). --- .gitignore | 3 +- web/content/assets/js/try.js | 133 +++++++++++++++++++++++------- web/content/assets/js/worker.js | 24 ++++++ web/content/assets/styles/try.css | 5 ++ web/content/try.html | 1 - 5 files changed, 132 insertions(+), 34 deletions(-) create mode 100644 web/content/assets/js/worker.js diff --git a/.gitignore b/.gitignore index 3f1af7895..49b724ab5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules # Editor backup files *.bak *~ -web/content/assets/js/jison.js \ No newline at end of file +web/content/assets/js/jison.js +web/crash.log diff --git a/web/content/assets/js/try.js b/web/content/assets/js/try.js index 29ebbcc38..2557c2d07 100644 --- a/web/content/assets/js/try.js +++ b/web/content/assets/js/try.js @@ -1,5 +1,7 @@ (function ($) { +var worker = new Worker('../assets/js/worker.js'); + var parser, parser2; @@ -13,8 +15,20 @@ print = function (){} var printOut = function (str) { $("#out").html(str); }; +function debounce(timeout, fn) { + var timer; + + return function() { + clearTimeout(timer); + + timer = setTimeout(function() { + fn(); + timer = null; + }, timeout); + }; +} + $(document).ready(function () { - $("#process_btn").click(processGrammar); $("#parse_btn").click(runParser); $("#examples").change(function(ev) { @@ -23,52 +37,107 @@ $(document).ready(function () { $.get("/jison/examples/"+file, function (data) { $("#grammar").val(data); $(document.body).removeClass("loading"); + processGrammar(); }); }); - + + // recompile the grammar using a web worker, + // as the user types + var onChange = debounce(700, processGrammar); + $('#grammar').bind('input propertychange', onChange); + processGrammar(); }); function processGrammar () { - var type = "lalr"; - - var grammar = $("#grammar").val(); - try { - var cfg = JSON.parse(grammar); - } catch(e) { + + function onError(e) { + console.log(e); + $("#gen_out").html("Oops. Make sure your grammar is " + + "in the correct format."+ "\n" + e.stack) + .removeClass('good') + .removeClass('warning') + .addClass('bad'); + } + + function onSuccess(result) { + try { - var cfg = bnf.parse(grammar); + parser = Jison.Generator(result.cfg, {type: result.type}); } catch (e) { - $("#gen_out").html("Oops. Make sure your grammar is in the correct format.\n"+e).addClass('bad'); - return; + return onError(e); + } + + $("#out").removeClass("good").removeClass("bad").html(''); + $("#gen_out").removeClass("good").removeClass("bad").removeClass('warning'); + if (!parser.conflicts) { + $("#gen_out").html('Generated successfully!').addClass('good'); + } else { + $("#gen_out").html('Conflicts encountered:
').addClass('bad'); } - } - Jison.print = function () {}; - parser = Jison.Generator(cfg, {type: type}); + $("#download_btn").click(function () { + window.location.href = "data:application/javascript;charset=utf-8;base64,"+Base64.encode(parser.generate()); + }).removeAttr('disabled'); + + + + parser.resolutions.forEach(function (res) { + var r = res[2]; + if (!r.bydefault) return; + $("#gen_out").append(r.msg+"\n"+"("+r.s+", "+r.r+") -> "+r.action); + }); + + parser2 = parser.createParser(); + } + + // for newer browsers + function callWorker(grammar) { + worker.addEventListener('error', onError); + worker.addEventListener('message', function(e) { + onSuccess(e.data.result); + }); + + // ask the web worker to parse the grammar for us + worker.postMessage(grammar); + } + + // for older browsers (IE <=9, Android <=4.3) + function callNonWorker(grammar) { + Jison.print = function () {}; + var cfg; - $("#out").removeClass("good").removeClass("bad").html(''); - $("#gen_out").removeClass("good").removeClass("bad"); - if (!parser.conflicts) { - $("#gen_out").html('Generated successfully!').addClass('good'); + try { + cfg = JSON.parse(grammar); + } catch (e) { + try { + cfg = bnf.parse(grammar); + } catch (e) { + return onError(e); + } + } + + onSuccess({cfg: cfg, type: 'lalr'}); + } + + $("#gen_out").html("Parsing...") + .removeClass('good') + .removeClass('bad') + .addClass('warning'); + $('#download_btn').attr('disabled', true); + + var grammar = $("#grammar").val(); + + if (typeof Worker !== 'undefined') { + callWorker(grammar); } else { - $("#gen_out").html('Conflicts encountered:
').addClass('bad'); + callNonWorker(grammar); } - - $("#download_btn").click(function () { - window.location.href = "data:application/javascript;charset=utf-8;base64,"+Base64.encode(parser.generate()); - }).removeAttr('disabled'); - - parser.resolutions.forEach(function (res) { - var r = res[2]; - if (!r.bydefault) return; - $("#gen_out").append(r.msg+"\n"+"("+r.s+", "+r.r+") -> "+r.action); - }); - - parser2 = parser.createParser(); } function runParser () { - if (!parser) processGrammar(); + if (!parser) { + processGrammar(); + } printOut("Parsing..."); var source = $("#source").val(); try { diff --git a/web/content/assets/js/worker.js b/web/content/assets/js/worker.js new file mode 100644 index 000000000..120596ed3 --- /dev/null +++ b/web/content/assets/js/worker.js @@ -0,0 +1,24 @@ +'use strict'; + +importScripts('jison.js'); +Jison.print = function () {}; + +// request to parse a grammar +self.addEventListener('message', function (e) { + if (typeof e.data !== 'string') { + return; + } + + var grammar = e.data; + + var cfg; + + try { + cfg = JSON.parse(grammar); + } catch (e) { + // intentionally throw an error here if it fails to parse + cfg = bnf.parse(grammar); + } + + self.postMessage({result: {cfg: cfg, type: "lalr"}}); +}); \ No newline at end of file diff --git a/web/content/assets/styles/try.css b/web/content/assets/styles/try.css index f6c402a81..ed7711704 100644 --- a/web/content/assets/styles/try.css +++ b/web/content/assets/styles/try.css @@ -82,3 +82,8 @@ table tr:hover td { background: #fee; border: 2px solid red; } + +.warning { + background: #feffef; + border: 2px solid yellow; +} \ No newline at end of file diff --git a/web/content/try.html b/web/content/try.html index 4a0de15c9..cc0946662 100644 --- a/web/content/try.html +++ b/web/content/try.html @@ -79,7 +79,6 @@

Describe Your Language

;

-