2012-01-04 18:31:19 +00:00
|
|
|
/*\
|
|
|
|
title: js/JavaScriptParseTree.js
|
|
|
|
|
2012-01-05 10:00:21 +00:00
|
|
|
This object stores a JavaScript parse tree in the format produced by the pegjs JavaScript parser.
|
|
|
|
|
|
|
|
The parse tree represents the syntactical structure of a JavaScript program, represented as a tree
|
|
|
|
structure built from JavaScript arrays and objects. The nodes of the tree are objects with a "type"
|
2012-02-09 13:15:37 +00:00
|
|
|
field that indicates the type of the node (see the function compileNode() for a list of the types).
|
2012-01-05 10:00:21 +00:00
|
|
|
Depending on the type, other fields provide further details about the node.
|
2012-01-04 18:31:19 +00:00
|
|
|
|
2012-01-07 11:43:24 +00:00
|
|
|
The pegjs parser uses "StringLiteral" nodes to represent individual string literals. TiddlyWiki adds
|
|
|
|
support for nodes of type "StringLiterals" that represent a contiguous sequence of string constants.
|
|
|
|
This simplifies coalescing adjacent constants into a single string.
|
|
|
|
|
2012-01-04 18:31:19 +00:00
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var utils = require("./Utils.js");
|
|
|
|
|
|
|
|
// Create a new JavaScript tree object
|
2012-01-05 10:00:21 +00:00
|
|
|
var JavaScriptParseTree = function(tree) {
|
2012-01-04 18:31:19 +00:00
|
|
|
this.tree = tree;
|
|
|
|
};
|
|
|
|
|
2012-02-09 13:15:37 +00:00
|
|
|
JavaScriptParseTree.prototype.compile = function(targetType) {
|
|
|
|
if(targetType === "application/javascript") {
|
|
|
|
return this.compileJS();
|
|
|
|
} else if(targetType === "text/html") {
|
|
|
|
return {render: this.toString};
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
JavaScriptParseTree.prototype.toString = function() {
|
|
|
|
return JSON.stringify(this.tree);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Compile the entire JavaScript tree object to a renderer object
|
|
|
|
JavaScriptParseTree.prototype.compileJS = function() {
|
|
|
|
/*jslint evil: true */
|
2012-01-17 15:20:46 +00:00
|
|
|
var output = [];
|
2012-01-24 16:26:37 +00:00
|
|
|
if(this.tree instanceof Array) {
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileSubTree(output,this.tree);
|
2012-01-24 16:26:37 +00:00
|
|
|
} else {
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,this.tree);
|
2012-01-24 16:26:37 +00:00
|
|
|
}
|
2012-01-17 15:20:46 +00:00
|
|
|
var r = output.join("");
|
2012-02-09 13:15:37 +00:00
|
|
|
return {render: eval(r)};
|
2012-01-04 18:31:19 +00:00
|
|
|
};
|
|
|
|
|
2012-02-09 13:15:37 +00:00
|
|
|
// Compile a subtree of the parse tree to an array of fragments of JavaScript source code
|
|
|
|
JavaScriptParseTree.prototype.compileSubTree = function(output,tree) {
|
2012-01-04 18:31:19 +00:00
|
|
|
for(var t=0; t<tree.length; t++) {
|
|
|
|
if(t) {
|
|
|
|
this.output.push(";\n");
|
|
|
|
}
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,tree[t]);
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-02-09 13:15:37 +00:00
|
|
|
/*
|
|
|
|
Compile a JavaScript node to an array of fragments of JavaScript source code
|
|
|
|
|
|
|
|
The code currently inserts some unneeded parenthesis because it doesn't look
|
|
|
|
at the binding order of operators to determine if they are needed.
|
|
|
|
*/
|
|
|
|
JavaScriptParseTree.prototype.compileNode = function(output,node) {
|
2012-01-04 18:31:19 +00:00
|
|
|
var p;
|
|
|
|
switch(node.type) {
|
|
|
|
case "StringLiteral":
|
2012-01-24 18:09:54 +00:00
|
|
|
output.push('"' + utils.stringify(node.value) + '"');
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "StringLiterals":
|
2012-01-24 18:09:54 +00:00
|
|
|
output.push('"' + utils.stringify(node.value.join("")) + '"');
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "FunctionCall":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("(");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.name);
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(")(");
|
2012-01-04 18:31:19 +00:00
|
|
|
for(p=0; p<node["arguments"].length; p++) {
|
|
|
|
if(p) {
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(",");
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node["arguments"][p]);
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(")");
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "PropertyAccess":
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.base);
|
2012-01-04 18:31:19 +00:00
|
|
|
if(typeof node.name === "string") {
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("." + node.name);
|
2012-01-04 18:31:19 +00:00
|
|
|
} else {
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("[");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.name);
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("]");
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "ArrayLiteral":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("[");
|
2012-01-04 18:31:19 +00:00
|
|
|
for(p=0; p<node.elements.length; p++) {
|
|
|
|
if(p) {
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(",");
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.elements[p]);
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("]");
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "Variable":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(node.name);
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "ObjectLiteral":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("{");
|
2012-01-04 18:31:19 +00:00
|
|
|
for(p=0; p<node.properties.length; p++) {
|
|
|
|
if(p) {
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(",");
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.properties[p]);
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("}");
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "PropertyAssignment":
|
2012-01-31 11:58:53 +00:00
|
|
|
output.push("'");
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(node.name);
|
2012-01-31 11:58:53 +00:00
|
|
|
output.push("':");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.value);
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "BinaryExpression":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("(");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.left);
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(")");
|
|
|
|
output.push(node.operator);
|
|
|
|
output.push("(");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.right);
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(")");
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "NumericLiteral":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(node.value);
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "Function":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("(function ");
|
2012-01-04 18:31:19 +00:00
|
|
|
if(node.name !== null) {
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push(node.name);
|
2012-01-04 18:31:19 +00:00
|
|
|
}
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("(");
|
|
|
|
output.push(node.params.join(","));
|
|
|
|
output.push("){");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileSubTree(output,node.elements);
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("})");
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "ReturnStatement":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("return ");
|
2012-02-09 13:15:37 +00:00
|
|
|
this.compileNode(output,node.value);
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
case "This":
|
2012-01-17 15:20:46 +00:00
|
|
|
output.push("this");
|
2012-01-04 18:31:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.log(node);
|
|
|
|
throw "Unknown JavaScript node type: " + node.type;
|
|
|
|
//break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.JavaScriptParseTree = JavaScriptParseTree;
|
|
|
|
|
|
|
|
})();
|