freemarker-parser
Version:
Freemarker Parser is a javascript implementation of the Freemarker
130 lines • 5.19 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parser = void 0;
const ParseError_1 = __importDefault(require("./errors/ParseError"));
const Symbols_1 = require("./Symbols");
const Tokenizer_1 = require("./Tokenizer");
const Directives_1 = require("./enum/Directives");
const NodeTypes_1 = require("./enum/NodeTypes");
const ProgramNode_1 = __importDefault(require("./nodes/ProgramNode"));
const ParserLocation_1 = require("./ParserLocation");
const Nodes_1 = __importDefault(require("./utils/Nodes"));
const defaultConfig_1 = __importDefault(require("./defaultConfig"));
class Parser extends ParserLocation_1.ParserLocation {
constructor() {
super(...arguments);
this.options = defaultConfig_1.default;
}
parse(template, options = {}) {
super.parse(template);
const ast = new ProgramNode_1.default(0, template.length - 1);
const stack = [];
let parent = ast;
let tokens = [];
this.addLocation(parent);
this.options = Object.assign(Object.assign({}, defaultConfig_1.default), options);
try {
const tokenizer = new Tokenizer_1.Tokenizer(this.options);
tokens = tokenizer.parse(template);
}
catch (error) {
ast.addError(error);
}
if (tokens.length === 0) {
this.addLocationToProgram(ast);
return { ast, tokens };
}
let token = null;
for (token of tokens) {
try {
const tokenType = this.tokenToNodeType(token);
if (token.type === Symbols_1.NodeType.CloseDirective ||
token.type === Symbols_1.NodeType.CloseMacro) {
if (token.params) {
ast.addError(new ParseError_1.default(`Close tag '${tokenType}' should have no params`, token));
continue;
}
if (parent.type !== tokenType) {
ast.addError(new ParseError_1.default(`Unexpected close tag '${tokenType}'`, token));
continue;
}
parent = stack.pop(); // its always
}
else {
const node = this.addNodeChild(parent, token);
if (node !== parent && node.hasBody) {
if (!this.isPartial(tokenType, parent.type)) {
stack.push(parent);
}
parent = node;
}
}
}
catch (error) {
ast.addError(error);
}
}
if (stack.length > 0) {
ast.addError(new ParseError_1.default(`Unclosed tag '${parent.type}'`, parent));
}
this.addLocationToProgram(ast);
return { ast, tokens };
}
addLocationToProgram(parent) {
if (this.options.parseLocation) {
if (parent.errors) {
for (const node of parent.errors) {
this.addLocation(node);
}
}
}
}
addNodeChild(parent, token) {
const tokenType = this.tokenToNodeType(token);
const node = Nodes_1.default[tokenType](token, parent);
if (node) {
this.addLocation(node);
}
if (parent !== node) {
parent.addToNode(node);
}
return node;
}
isPartial(type, parentType) {
switch (type) {
case NodeTypes_1.NodeTypes.ConditionElse:
return NodeTypes_1.NodeTypes.Condition === parentType;
case NodeTypes_1.NodeTypes.Else:
return (NodeTypes_1.NodeTypes.Condition === parentType || NodeTypes_1.NodeTypes.List === parentType);
case NodeTypes_1.NodeTypes.Recover:
return NodeTypes_1.NodeTypes.Attempt === parentType;
}
return false;
}
tokenToNodeType(token) {
switch (token.type) {
case Symbols_1.NodeType.CloseDirective:
case Symbols_1.NodeType.OpenDirective:
if (token.text in Directives_1.Directives) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return Directives_1.Directives[token.text];
}
break;
case Symbols_1.NodeType.Interpolation:
return NodeTypes_1.NodeTypes.Interpolation;
case Symbols_1.NodeType.Text:
return NodeTypes_1.NodeTypes.Text;
case Symbols_1.NodeType.CloseMacro:
case Symbols_1.NodeType.OpenMacro:
return NodeTypes_1.NodeTypes.MacroCall;
case Symbols_1.NodeType.Comment:
return NodeTypes_1.NodeTypes.Comment;
}
throw new ParseError_1.default(`Unknown token \`${token.type}\` - \`${token.text}\``, token);
}
}
exports.Parser = Parser;
//# sourceMappingURL=Parser.js.map