import { format } from "./StringFormat"; import { TraceSource, DebugLevel } from "../log/TraceSource"; import { ITemplateParser, TokenType } from "./TemplateParser"; import m = require("module"); const trace = TraceSource.get(m.id); type TemplateFn = (obj: object) => string; export class TemplateCompiler { _data: string[]; _code: string[]; _wrapWith = true; constructor() { this._code = []; this._data = []; } compile(parser: ITemplateParser): TemplateFn { this.preamble(); this.visitTemplate(parser); this.postamble(); const text = this._code.join("\n"); try { // tslint:disable-next-line:function-constructor const compiled = new Function("obj, format, $data", text); /** * Функция форматирования по шаблону * * @type{Function} * @param{Object} obj объект с параметрами для подстановки */ return (obj: object) => compiled(obj || {}, format, this._data); } catch (e) { trace.traceEvent(DebugLevel, [e, text, this._data]); throw e; } } preamble() { this._code.push( "var $p = [];", "var print = function(){", " $p.push(format.apply(null,arguments));", "};" ); if (this._wrapWith) this._code.push("with(obj){"); } postamble() { if (this._wrapWith) this._code.push("}"); this._code.push("return $p.join('');"); } visitTemplate(parser: ITemplateParser) { while (parser.next()) { switch (parser.token()) { case TokenType.OpenBlock: this.visitCode(parser); break; case TokenType.OpenInlineBlock: this.visitInline(parser); break; default: this.visitTextFragment(parser); break; } } } visitInline(parser: ITemplateParser) { const code = ["$p.push("]; while (parser.next()) { if (parser.token() === TokenType.CloseBlock) break; code.push(parser.value()); } code.push(");"); this._code.push(code.join("")); } visitCode(parser: ITemplateParser) { const code = []; while (parser.next()) { if (parser.token() === TokenType.CloseBlock) break; code.push(parser.value()); } this._code.push(code.join("")); } visitTextFragment(parser: ITemplateParser) { const i = this._data.push(parser.value()); this._code.push("$p.push($data[" + i + "]);"); } }