2012-01-04 18:31:19 +00:00
|
|
|
/*\
|
|
|
|
title: js/JavaScriptParser.js
|
|
|
|
|
2012-03-03 18:06:33 +00:00
|
|
|
Parses JavaScript source code into a parse tree using Esprima
|
2012-01-04 18:31:19 +00:00
|
|
|
|
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true */
|
|
|
|
"use strict";
|
|
|
|
|
2012-03-03 18:33:16 +00:00
|
|
|
var Renderer = require("./Renderer.js").Renderer,
|
2012-03-01 23:59:46 +00:00
|
|
|
Dependencies = require("./Dependencies.js").Dependencies,
|
|
|
|
esprima = require("esprima");
|
|
|
|
|
|
|
|
// Initialise the parser
|
|
|
|
var JavaScriptParser = function(options) {
|
|
|
|
this.store = options.store;
|
2012-01-04 18:31:19 +00:00
|
|
|
};
|
|
|
|
|
2012-03-04 12:01:44 +00:00
|
|
|
// Parse a string of JavaScript code and return the parse tree as a wikitext parse tree
|
2012-03-01 23:59:46 +00:00
|
|
|
JavaScriptParser.prototype.parse = function(type,code) {
|
2012-03-04 12:01:44 +00:00
|
|
|
// Simplisticly replace tabs with spaces. Browsers will happily render tabs but most default to 8 character tab stops
|
|
|
|
code = code.replace(/\t/mg," ");
|
2012-03-02 12:09:06 +00:00
|
|
|
// Try to parse the code
|
2012-03-02 18:00:40 +00:00
|
|
|
var parseTree;
|
2012-03-02 12:09:06 +00:00
|
|
|
try {
|
2012-03-02 18:00:40 +00:00
|
|
|
parseTree = esprima.parse(code,{comment: true,tokens: true,range: true});
|
2012-03-02 12:09:06 +00:00
|
|
|
} catch(ex) {
|
|
|
|
// Return a helpful error if the parse failed
|
2012-03-03 18:33:16 +00:00
|
|
|
return new Renderer([
|
2012-03-02 12:09:06 +00:00
|
|
|
Renderer.ElementNode("pre",{"class": "javascript-source"},[
|
|
|
|
Renderer.TextNode(code.substring(0,ex.index)),
|
|
|
|
Renderer.ErrorNode(ex),
|
|
|
|
Renderer.TextNode(code.substring(ex.index))
|
|
|
|
])
|
|
|
|
],new Dependencies(),this.store);
|
|
|
|
}
|
2012-03-02 14:21:02 +00:00
|
|
|
// Helpers to render the comments and tokens with the appropriate classes
|
|
|
|
var self = this,
|
|
|
|
result = [],
|
|
|
|
nextComment = 0,
|
|
|
|
nextToken = 0,
|
|
|
|
currPos = 0;
|
|
|
|
var renderWhitespace = function(nextPos) {
|
|
|
|
if(currPos < nextPos) {
|
|
|
|
result.push(Renderer.TextNode(code.substring(currPos,nextPos)));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
renderComment = function(comment) {
|
|
|
|
var text = comment.value,
|
|
|
|
element,
|
2012-03-02 15:54:05 +00:00
|
|
|
classes = ["javascript-comment"],
|
|
|
|
content = [];
|
2012-03-02 14:21:02 +00:00
|
|
|
renderWhitespace(comment.range[0]);
|
|
|
|
if(comment.type === "Block") {
|
|
|
|
element = "div";
|
|
|
|
classes.push("javascript-block-comment");
|
2012-03-02 15:54:05 +00:00
|
|
|
content.push(Renderer.TextNode("/*"));
|
2012-03-02 14:21:02 +00:00
|
|
|
} else {
|
|
|
|
element = "span";
|
|
|
|
classes.push("javascript-line-comment");
|
2012-03-02 15:54:05 +00:00
|
|
|
content.push(Renderer.TextNode("//"));
|
2012-03-02 14:21:02 +00:00
|
|
|
}
|
2012-03-03 18:39:13 +00:00
|
|
|
content.push.apply(content,self.store.parseText("text/x-tiddlywiki",text).nodes);
|
2012-03-02 15:54:05 +00:00
|
|
|
if(comment.type === "Block") {
|
|
|
|
content.push(Renderer.TextNode("*/"));
|
|
|
|
} else {
|
|
|
|
content.push(Renderer.TextNode("\n"));
|
2012-03-02 14:21:02 +00:00
|
|
|
}
|
2012-03-02 15:54:05 +00:00
|
|
|
result.push(Renderer.ElementNode(element,{"class": classes},content));
|
2012-03-02 14:21:02 +00:00
|
|
|
currPos = comment.range[1] + 1;
|
|
|
|
},
|
|
|
|
renderToken = function(token) {
|
|
|
|
renderWhitespace(token.range[0]);
|
|
|
|
result.push(Renderer.ElementNode("span",{
|
|
|
|
"class": "javascript-" + token.type.toLowerCase()
|
|
|
|
},[
|
|
|
|
Renderer.TextNode(token.value)
|
|
|
|
]));
|
|
|
|
currPos = token.range[1] + 1;
|
|
|
|
};
|
|
|
|
// Process the tokens interleaved with the comments
|
|
|
|
while(nextComment < parseTree.comments.length || nextToken < parseTree.tokens.length) {
|
|
|
|
if(nextComment < parseTree.comments.length && nextToken < parseTree.tokens.length) {
|
|
|
|
if(parseTree.comments[nextComment].range[0] < parseTree.tokens[nextToken].range[0]) {
|
|
|
|
renderComment(parseTree.comments[nextComment++]);
|
|
|
|
} else {
|
|
|
|
renderToken(parseTree.tokens[nextToken++]);
|
|
|
|
}
|
|
|
|
} else if(nextComment < parseTree.comments.length) {
|
|
|
|
renderComment(parseTree.comments[nextComment++]);
|
|
|
|
} else {
|
|
|
|
renderToken(parseTree.tokens[nextToken++]);
|
2012-03-02 00:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
2012-03-02 14:21:02 +00:00
|
|
|
renderWhitespace(code.length);
|
|
|
|
// Wrap the whole lot in a `<PRE>`
|
2012-03-03 18:33:16 +00:00
|
|
|
return new Renderer([
|
2012-03-02 00:48:05 +00:00
|
|
|
Renderer.ElementNode("pre",{"class": "javascript-source"},result)
|
|
|
|
],new Dependencies(),this.store);
|
|
|
|
};
|
|
|
|
|
2012-01-04 18:31:19 +00:00
|
|
|
exports.JavaScriptParser = JavaScriptParser;
|
|
|
|
|
|
|
|
})();
|