UNPKG

@malagu/core

Version:
158 lines (141 loc) 5.34 kB
import { ExpressionCompiler, JexlEngineProvider } from './expression-protocol'; import { Component, Autowired } from '../annotation'; interface MiddleExpression { expression: any; nextText?: string; } @Component(ExpressionCompiler) export class ExpressionCompilerImpl implements ExpressionCompiler { private ESCAPE_CHAR = '\\'; private SPECIAL_CHAR = '$'; private BRACKET_BEGIN = '{'; private BRACKET_END = '}'; @Autowired(JexlEngineProvider) protected readonly jexlEngineProvider: JexlEngineProvider<any>; compileSections(text: string): any[] { if (!text || text.indexOf(this.SPECIAL_CHAR) < 0) { return []; } const sections: any[] = []; let middleText: string | undefined = text; while (middleText) { const me: MiddleExpression | undefined = this.middleCompile(middleText); if (!me) { sections.push(middleText); middleText = undefined; } else { sections.push(me.expression); middleText = me.nextText; } } return sections; } protected middleCompile(text: string): MiddleExpression | undefined { let me: MiddleExpression | undefined; if (text.startsWith('${{')) { me = this.nextMiddleExpression(text.substring(3), 2); } else if (text.startsWith('${')) { me = this.nextMiddleExpression(text.substring(2)); } else { me = this.nextString(text); } return me; } protected nextMiddleExpression(text: string, bracketBeginCharNum = 1): MiddleExpression | undefined { let stringed = false; let escaped = false; let bracketBeginCharFound = 0; const section: string[] = []; for (let i = 0; i < text.length; i++) { const c = text[i]; if (!escaped) { if ('\'' === c || '\"' === c) { stringed = !stringed; section.push(c); continue; } else if (c === this.ESCAPE_CHAR) { escaped = true; continue; } } if (stringed) { section.push(c); escaped = false; } else if (escaped) { if (this.SPECIAL_CHAR === c || this.BRACKET_BEGIN === c || this.BRACKET_END === c) { section.push(c); } else { section.push(this.ESCAPE_CHAR); section.push(c); } escaped = false; } else if (this.BRACKET_BEGIN === c) { bracketBeginCharFound++; section.push(c); } else if (this.BRACKET_END === c) { if (bracketBeginCharFound === 0 && bracketBeginCharNum === 1) { const jexlEngine = this.jexlEngineProvider.provide(); const expression = jexlEngine.createExpression(section.join('')); let nextText; if (i !== text.length - 1) { nextText = text.substring(i + 1); } return { expression, nextText }; } else if (bracketBeginCharFound > 0) { bracketBeginCharFound--; section.push(c); } else { bracketBeginCharNum--; } } else { section.push(c); } } } protected nextString(text: string): MiddleExpression { let escaped = false; let specialCharFound = false; const section: string[] = []; for (let i = 0; i < text.length; i++) { const c = text[i]; if (!escaped) { if ('\'' === c || '\"' === c) { section.push(c); continue; } else if (c === this.ESCAPE_CHAR) { escaped = true; continue; } } if (escaped) { if (this.SPECIAL_CHAR === c || this.BRACKET_BEGIN === c || this.BRACKET_END === c) { section.push(c); } else { section.push(this.ESCAPE_CHAR); section.push(c); } escaped = false; } else if (specialCharFound) { if (this.BRACKET_BEGIN === c) { const expression = section.join(''); const nextText = text.substring(i - 1); return { expression, nextText }; } else { specialCharFound = false; section.push(this.SPECIAL_CHAR); section.push(c); } } else { if (this.SPECIAL_CHAR === c) { specialCharFound = true; } else { section.push(c); } } } return { expression: section.join('') }; } }