Skip to content

Commit 28debc7

Browse files
committed
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 "Generate" 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).
1 parent 245f6dd commit 28debc7

File tree

5 files changed

+132
-34
lines changed

5 files changed

+132
-34
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ node_modules
44
# Editor backup files
55
*.bak
66
*~
7-
web/content/assets/js/jison.js
7+
web/content/assets/js/jison.js
8+
web/crash.log

web/content/assets/js/try.js

+101-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
(function ($) {
22

3+
var worker = new Worker('../assets/js/worker.js');
4+
35
var parser,
46
parser2;
57

@@ -13,8 +15,20 @@ print = function (){}
1315

1416
var printOut = function (str) { $("#out").html(str); };
1517

18+
function debounce(timeout, fn) {
19+
var timer;
20+
21+
return function() {
22+
clearTimeout(timer);
23+
24+
timer = setTimeout(function() {
25+
fn();
26+
timer = null;
27+
}, timeout);
28+
};
29+
}
30+
1631
$(document).ready(function () {
17-
$("#process_btn").click(processGrammar);
1832
$("#parse_btn").click(runParser);
1933

2034
$("#examples").change(function(ev) {
@@ -23,52 +37,107 @@ $(document).ready(function () {
2337
$.get("/jison/examples/"+file, function (data) {
2438
$("#grammar").val(data);
2539
$(document.body).removeClass("loading");
40+
processGrammar();
2641
});
2742
});
28-
43+
44+
// recompile the grammar using a web worker,
45+
// as the user types
46+
var onChange = debounce(700, processGrammar);
47+
$('#grammar').bind('input propertychange', onChange);
48+
processGrammar();
2949
});
3050

3151
function processGrammar () {
32-
var type = "lalr";
33-
34-
var grammar = $("#grammar").val();
35-
try {
36-
var cfg = JSON.parse(grammar);
37-
} catch(e) {
52+
53+
function onError(e) {
54+
console.log(e);
55+
$("#gen_out").html("Oops. Make sure your grammar is " +
56+
"in the correct format."+ "\n" + e.stack)
57+
.removeClass('good')
58+
.removeClass('warning')
59+
.addClass('bad');
60+
}
61+
62+
function onSuccess(result) {
63+
3864
try {
39-
var cfg = bnf.parse(grammar);
65+
parser = Jison.Generator(result.cfg, {type: result.type});
4066
} catch (e) {
41-
$("#gen_out").html("Oops. Make sure your grammar is in the correct format.\n"+e).addClass('bad');
42-
return;
67+
return onError(e);
68+
}
69+
70+
$("#out").removeClass("good").removeClass("bad").html('');
71+
$("#gen_out").removeClass("good").removeClass("bad").removeClass('warning');
72+
if (!parser.conflicts) {
73+
$("#gen_out").html('Generated successfully!').addClass('good');
74+
} else {
75+
$("#gen_out").html('Conflicts encountered:<br/>').addClass('bad');
4376
}
44-
}
4577

46-
Jison.print = function () {};
47-
parser = Jison.Generator(cfg, {type: type});
78+
$("#download_btn").click(function () {
79+
window.location.href = "data:application/javascript;charset=utf-8;base64,"+Base64.encode(parser.generate());
80+
}).removeAttr('disabled');
81+
82+
83+
84+
parser.resolutions.forEach(function (res) {
85+
var r = res[2];
86+
if (!r.bydefault) return;
87+
$("#gen_out").append(r.msg+"\n"+"("+r.s+", "+r.r+") -> "+r.action);
88+
});
89+
90+
parser2 = parser.createParser();
91+
}
92+
93+
// for newer browsers
94+
function callWorker(grammar) {
95+
worker.addEventListener('error', onError);
96+
worker.addEventListener('message', function(e) {
97+
onSuccess(e.data.result);
98+
});
99+
100+
// ask the web worker to parse the grammar for us
101+
worker.postMessage(grammar);
102+
}
103+
104+
// for older browsers (IE <=9, Android <=4.3)
105+
function callNonWorker(grammar) {
106+
Jison.print = function () {};
107+
var cfg;
48108

49-
$("#out").removeClass("good").removeClass("bad").html('');
50-
$("#gen_out").removeClass("good").removeClass("bad");
51-
if (!parser.conflicts) {
52-
$("#gen_out").html('Generated successfully!').addClass('good');
109+
try {
110+
cfg = JSON.parse(grammar);
111+
} catch (e) {
112+
try {
113+
cfg = bnf.parse(grammar);
114+
} catch (e) {
115+
return onError(e);
116+
}
117+
}
118+
119+
onSuccess({cfg: cfg, type: 'lalr'});
120+
}
121+
122+
$("#gen_out").html("Parsing...")
123+
.removeClass('good')
124+
.removeClass('bad')
125+
.addClass('warning');
126+
$('#download_btn').attr('disabled', true);
127+
128+
var grammar = $("#grammar").val();
129+
130+
if (typeof Worker !== 'undefined') {
131+
callWorker(grammar);
53132
} else {
54-
$("#gen_out").html('Conflicts encountered:<br/>').addClass('bad');
133+
callNonWorker(grammar);
55134
}
56-
57-
$("#download_btn").click(function () {
58-
window.location.href = "data:application/javascript;charset=utf-8;base64,"+Base64.encode(parser.generate());
59-
}).removeAttr('disabled');
60-
61-
parser.resolutions.forEach(function (res) {
62-
var r = res[2];
63-
if (!r.bydefault) return;
64-
$("#gen_out").append(r.msg+"\n"+"("+r.s+", "+r.r+") -> "+r.action);
65-
});
66-
67-
parser2 = parser.createParser();
68135
}
69136

70137
function runParser () {
71-
if (!parser) processGrammar();
138+
if (!parser) {
139+
processGrammar();
140+
}
72141
printOut("Parsing...");
73142
var source = $("#source").val();
74143
try {

web/content/assets/js/worker.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
importScripts('jison.js');
4+
Jison.print = function () {};
5+
6+
// request to parse a grammar
7+
self.addEventListener('message', function (e) {
8+
if (typeof e.data !== 'string') {
9+
return;
10+
}
11+
12+
var grammar = e.data;
13+
14+
var cfg;
15+
16+
try {
17+
cfg = JSON.parse(grammar);
18+
} catch (e) {
19+
// intentionally throw an error here if it fails to parse
20+
cfg = bnf.parse(grammar);
21+
}
22+
23+
self.postMessage({result: {cfg: cfg, type: "lalr"}});
24+
});

web/content/assets/styles/try.css

+5
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,8 @@ table tr:hover td {
8282
background: #fee;
8383
border: 2px solid red;
8484
}
85+
86+
.warning {
87+
background: #feffef;
88+
border: 2px solid yellow;
89+
}

web/content/try.html

-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ <h2>Describe Your Language</h2>
7979
;
8080
</textarea>
8181
<p>
82-
<button id="process_btn">Generate Parser</button>
8382
<button id="download_btn" disabled>Download</button>
8483
</p>
8584
<pre id="gen_out"></pre>

0 commit comments

Comments
 (0)