marko/compiler/Compiler.js
Patrick Steele-Idem 069b3e5ba9 Initial commit for marko v3 with htmljs-parser
Work-in-progress. Lots of failing tests.
2015-11-24 14:30:32 -07:00

124 lines
4.3 KiB
JavaScript

'use strict';
var ok = require('assert').ok;
var HtmlJsParser = require('./HtmlJsParser');
var CodeGenerator = require('./CodeGenerator');
var Builder = require('./Builder');
var path = require('path');
var taglibLookup = require('./taglib-lookup');
var createError = require('raptor-util/createError');
class Compiler {
constructor(options) {
options = options || {};
this.builder = options.builder || new Builder();
this.parser = options.parser;
if (!this.parser) {
this.parser = new HtmlJsParser({
compiler: this
});
}
this.codeGenerator = options.codeGenerator || new CodeGenerator({
builder: this.builder
});
this._reset();
}
_reset() {
this.path = null;
this.dirname = null;
this.taglibLookup = null;
}
transformNode(node) {
try {
this.taglibLookup.forEachNodeTransformer(node, function (transformer) {
if (!node.isTransformerApplied(transformer)) {
//Check to make sure a transformer of a certain type is only applied once to a node
node.setTransformerApplied(transformer);
//Mark the node as have been transformed by the current transformer
this._transformerApplied = true;
//Set the flag to indicate that a node was transformed
// node.compiler = this;
var transformerFunc = transformer.getFunc();
transformerFunc.call(transformer, node, this); //Have the transformer process the node (NOTE: Just because a node is being processed by the transformer doesn't mean that it has to modify the parse tree)
}
}, this);
} catch (e) {
throw createError(new Error('Unable to compile template at path "' + this.path + '". Error: ' + e.message), e);
}
}
transformTree(rootNode) {
var self = this;
function transformTreeHelper(node) {
self.transformNode(node);
/*
* Now process the child nodes by looping over the child nodes
* and transforming the subtree recursively
*
* NOTE: The length of the childNodes array might change as the tree is being performed.
* The checks to prevent transformers from being applied multiple times makes
* sure that this is not a problem.
*/
node.forEachChild(function (childNode) {
if (childNode.isDetached()) {
return; //The child node might have been removed from the tree
}
transformTreeHelper(childNode);
});
}
/*
* The tree is continuously transformed until we go through an entire pass where
* there were no new nodes that needed to be transformed. This loop makes sure that
* nodes added by transformers are also transformed.
*/
do {
this._transformerApplied = false;
//Reset the flag to indicate that no transforms were yet applied to any of the nodes for this pass
transformTreeHelper(rootNode); //Run the transforms on the tree
} while (this._transformerApplied);
return rootNode;
}
compile(src, templatePath) {
ok(src);
ok(templatePath);
this._reset();
this.path = templatePath;
this.dirname = path.dirname(templatePath);
this.taglibLookup = taglibLookup.buildLookup(this.dirname);
var ast = this.parser.parse(src, this.path);
// console.log('ROOT', JSON.stringify(ast, null, 2));
var transformedAST = this.transformTree(ast);
// console.log('transformedAST', JSON.stringify(ast, null, 2));
var self = this;
var compiledSrc = this.codeGenerator.generateCode(transformedAST, {
onError: function(eventArgs) {
var node = eventArgs.node;
var message = eventArgs.message;
self.addError(node, message);
}
});
return compiledSrc;
}
addError(node, message) {
throw new Error('addError() not fully implemented. Error: ' + message); // TODO
}
}
module.exports = Compiler;