jsparse demo improvements (lexer tab)

This commit is contained in:
David Greenspan
2012-09-29 21:33:19 -07:00
parent f3f64922c4
commit 3201fe5180
3 changed files with 166 additions and 88 deletions

View File

@@ -25,38 +25,82 @@ html, body { height: 100%; }
bottom: 0;
}
#inputarea textarea {
#inputarea {
border: 0;
border-right: 1px solid #555;
position: absolute;
height: 100%;
left: 0;
right: 50%;
}
#inputarea textarea {
position: absolute;
width: 100%;
height: 100%;
font-family: monospace;
font-size: 100%;
border: 0;
}
#outputoptions {
position: absolute;
left: 50%;
right: 0;
overflow: visible;
bottom: 0;
height: 29px;
border-top: 1px solid #555;
background: #ccc;
}
#output {
position: absolute;
height: 100%;
left: 50%;
right: 0;
overflow: auto;
top: 0;
bottom: 30px;
font-family: monospace;
}
#outputoptions .output-type {
text-decoration: none;
font-family: sans-serif;
font-size: 14px;
display: inline-block;
background: #e2e2e2;
vertical-align: top;
position: relative;
top: -1px;
padding: 3px 8px;
margin-left: 6px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
border: 1px solid #777;
border-top: 1px solid #555;
cursor: pointer;
}
#outputoptions .output-type.selected {
font-weight: bold;
background: #fff;
border-top: 1px solid #fff;
}
#inputarea textarea, #output {
line-height: 130%;
}
.lex { border: 1px solid #333; }
.lex { border: 1px solid #333; cursor: pointer; }
.lex_keyword { background: #0f0; }
.lex_identifier { background: #ff0; }
.lex_punctuation { background: #0ff; }
.lex_error { background: #f00; }
.lex_whitespace { background: #fcc; }
.lex_whitespace, .lex_newline, .lex_eof { background: #fcc; }
.lex_comment { background: #ccc; }
.lex_regex { background: #f0f; }

View File

@@ -22,6 +22,11 @@ whitespace is significant here; browser swallows initial
}}<textarea id="inputtextarea">
{{input}}</textarea>
</div>
<div id="outputoptions">
{{#each outputTypes}}
<ins class="output-type {{is_outputtype_selected value}}">{{name}}</ins>
{{/each}}
</div>
<div id="output">
{{output}}
</div>

View File

@@ -4,6 +4,8 @@ if (Meteor.is_client) {
Meteor.startup(function () {
if (! Session.get("input"))
Session.set("input", "var x = 3");
if (! Session.get("output-type"))
Session.set("output-type", "parse");
});
Template.page.input = function () {
@@ -13,92 +15,103 @@ if (Meteor.is_client) {
Template.page.output = function () {
var input = Session.get("input") || "";
// LEXER
/*
if (! input)
return "";
var L = new Lexer(input);
var html = "";
while (L.next() !== 'EOF') {
if (L.type === "NEWLINE") {
html += '<br>';
} else {
var text = Handlebars._escape(L.text || ' ');
text = text.replace(/(?!.)\s/g, '<br>'); // for multiline comments
text = text.replace(/\s/g, '&nbsp;');
html += '<span class="lex lex_' + L.type.toLowerCase() + '">' +
text + '</span>';
if (L.type === "ERROR")
break;
}
}*/
// PARSER
var html;
var tree = null;
var parser = new JSParser(input);
try {
tree = parser.getSyntaxTree();
} catch (parseError) {
var errorLexeme = parser.lexer.lastLexeme;
html = Handlebars._escape(
input.substring(0, errorLexeme.startPos()));
html += Spark.setDataContext(
errorLexeme,
'<span class="parseerror">' +
Handlebars._escape(errorLexeme.text() || '<EOF>') +
'</span>');
html = html.replace(/(?!.)\s/g, '<br>');
html += '<div class="parseerrormessage">' +
Handlebars._escape(parseError.toString()) + '</div>';
}
if (tree) {
var curPos = 0;
var unclosedInfos = [];
var toHtml = function (obj) {
if (obj instanceof ParseNode) {
var head = obj.name || '';
var children = obj.children;
var info = { startPos: curPos };
var isStatement = (head.indexOf('Stmnt') >= 0);
var html = Spark.setDataContext(
info,
'<div class="box named' + (isStatement ? ' statement' : '') +
'"><div class="box head">' + Handlebars._escape(head) + '</div>' +
_.map(children, toHtml).join('') + '</div>');
unclosedInfos.push(info);
return html;
} else if (obj.text) {
// token
_.each(unclosedInfos, function (info) {
info.endPos = curPos;
});
curPos = obj.endPos();
unclosedInfos.length = 0;
var text = obj.text();
// insert zero-width spaces to allow wrapping
text = text.replace(/.{20}/g, "$&\n");
text = Handlebars._escape(text);
text = text.replace(/\n/g, '&#8203;');
return Spark.setDataContext(
obj,
'<div class="box token">' + text + '</div>');
var outputType = Session.get("output-type");
if (outputType === "lex") {
// LEXER
var lexer = new JSLexer(input);
var html = "";
var L;
do {
L = lexer.next();
var content;
if (L.type() === "NEWLINE") {
content = '&nbsp;<br>';
} else if (L.type() === "EOF") {
content = Handlebars._escape("<EOF>");
} else {
// other?
return '<div class="box other">' +
Handlebars._escape(JSON.stringify(obj)) + '</div>';
content = Handlebars._escape(L.text() || ' ');
content = content.replace(/(?!.)\s/g, '<br>'); // for multiline comments
content = content.replace(/\s/g, '&nbsp;');
}
};
html = toHtml(tree);
curPos = parser.lexer.pos;
_.each(unclosedInfos, function (info) {
info.endPos = curPos;
});
}
html += Spark.setDataContext(
L,
'<span class="lex lex_' + L.type().toLowerCase() + '" ' +
'title="' + Handlebars._escape(L.type()) + '">' +
content + '</span>');
} while (! L.isError() && ! L.isEOF());
return new Handlebars.SafeString(html);
} else if (outputType === "parse") {
// PARSER
var html;
var tree = null;
var parser = new JSParser(input);
try {
tree = parser.getSyntaxTree();
} catch (parseError) {
var errorLexeme = parser.lexer.lastLexeme;
html = Handlebars._escape(
input.substring(0, errorLexeme.startPos()));
html += Spark.setDataContext(
errorLexeme,
'<span class="parseerror">' +
Handlebars._escape(errorLexeme.text() || '<EOF>') +
'</span>');
html = html.replace(/(?!.)\s/g, '<br>');
html += '<div class="parseerrormessage">' +
Handlebars._escape(parseError.toString()) + '</div>';
}
if (tree) {
var curPos = 0;
var unclosedInfos = [];
var toHtml = function (obj) {
if (obj instanceof ParseNode) {
var head = obj.name || '';
var children = obj.children;
var info = { startPos: curPos };
var isStatement = (head.indexOf('Stmnt') >= 0);
var html = Spark.setDataContext(
info,
'<div class="box named' + (isStatement ? ' statement' : '') +
'"><div class="box head">' + Handlebars._escape(head) + '</div>' +
_.map(children, toHtml).join('') + '</div>');
unclosedInfos.push(info);
return html;
} else if (obj.text) {
// token
_.each(unclosedInfos, function (info) {
info.endPos = curPos;
});
curPos = obj.endPos();
unclosedInfos.length = 0;
var text = obj.text();
// insert zero-width spaces to allow wrapping
text = text.replace(/.{20}/g, "$&\n");
text = Handlebars._escape(text);
text = text.replace(/\n/g, '&#8203;');
return Spark.setDataContext(
obj,
'<div class="box token">' + text + '</div>');
} else {
// other?
return '<div class="box other">' +
Handlebars._escape(JSON.stringify(obj)) + '</div>';
}
};
html = toHtml(tree);
curPos = parser.lexer.pos;
_.each(unclosedInfos, function (info) {
info.endPos = curPos;
});
}
return new Handlebars.SafeString(html);
return new Handlebars.SafeString(html);
}
else return ''; // unknown output tab?
};
Template.page.events({
@@ -125,9 +138,25 @@ if (Meteor.is_client) {
'click .parseerror': function (event) {
selectInputText(this.startPos(), this.endPos());
return false;
}
},
'click .output-type': function (event) {
Session.set("output-type", this.value);
},
'click .lex': function (event) {
selectInputText(this.startPos(), this.endPos());
return false;
},
});
Template.page.outputTypes = [
{name: "Lex", value: "lex"},
{name: "Parse", value: "parse"}
];
Template.page.is_outputtype_selected = function (which) {
return Session.equals("output-type", which) ? "selected" : "";
};
var selectTextInArea = function (e, start, end){
e.focus();
if (e.setSelectionRange) {