const VParserTypes = { content: 0, variable: 1, for: 2, forEach: 3, forContent: 4, forEnd: 5, if: 6, ifContent: 7, ifEnd: 8, assign: 9, include: 10, none: -1, }; class VTpeLParser { constructor(name, content) { let self = this; self.name = name; self.legex = content.trim(); self.index = 0; self.content = ''; self.parsed = []; self.contexts = [0]; self.line = 1; self.charPos = 1; } tokenize() { let self = this; for (self.index = 0; self.index < self.legex.length; self.index++) { let i = self.index, char = self.legex.charAt(i); self.charPos++; if (char === "\n") { self.line++; self.charPos = 0; } if (self.nextContains('/* ', i, true)) { self.extract(' */', VParserTypes.none) } else if (self.nextContains('// ', i, true)) { self.extract("\n", VParserTypes.none); } else if (self.nextContains('', VParserTypes.none); } else if (self.nextContains('{for(', i, true)) { self.extract(')}', VParserTypes.for); self.contexts.push(VParserTypes.for); } else if (self.nextContains('{include(', i, true)) { self.extract(')}', VParserTypes.include); self.contexts.push(VParserTypes.include); } else if (self.nextContains('{foreach(', i, true)) { self.extract(')}', VParserTypes.forEach); self.contexts.push(VParserTypes.forEach); } else if (self.nextContains('{/for}', i, true)) { self.addType(VParserTypes.forEnd); self.contexts.pop(); } else if (self.nextContains('{if(', i, true)) { self.extract(')}', VParserTypes.if); self.contexts.push(VParserTypes.if); } else if (self.nextContains('{/if}', i, true)) { self.addType(VParserTypes.ifEnd); self.contexts.pop(); } else if (self.nextContains('$${', i, true)) { self.extract('}', VParserTypes.assign); } else if (self.nextContains('${', i, true)) { self.extract('}', VParserTypes.variable); } else { self.content += char; } } self.addType(VParserTypes.content); return self.parsed; } addType(type) { let self = this; let content = self.content.replace(/^\n+|\n+$/g, ''), instructions = self.findInstructions(type); self.content = ''; if (type !== VParserTypes.none) { if (type === VParserTypes.content && content === '') { return null; } return self.parsed.push({ content: content, type: type, context: self.contexts[self.contexts.length - 1], instructions: instructions }); } return null; } nextContains(find, index, add = false) { let count = this.nextContainsRaw(this.legex, find, index); if (add && count > 0) { this.index += count; } return count > 0 || count === -1; } nextContainsRaw(raw, find, index) { if (typeof find === "string") { find = find.split(""); } let count = find.length; if (count < 1) { return -1; } for (let i = 0; i < count; i++) { let nc = raw.charAt(index + i); if ((find[i] === '\n' && nc === undefined)) { return count; } if (find[i] !== nc) { return 0; } } return count; } extract(findUntil = '}', type = 1) { let self = this; self.addType(0); findUntil = findUntil.split("") let content = '', index = self.index, legex = self.legex, firstFind = findUntil.shift(); for (let i = self.index; i < legex.length; i++) { let char = legex.charAt(i); if (char === firstFind && self.nextContains(findUntil, i + 1)) { // PrettyConsole.debug("Parser", `[${index} > ${i}] >> ${content}`); // console.debug(`[Parser][${index} > ${i}] >> ${content}`); self.index = i + findUntil.length; self.content = content.trim(); self.addType(type); return; } content += char; } if (firstFind === "\n") { self.index = legex.length; self.content = content.trim(); self.addType(type); return } throw `Template "${self.name}" Parsing Failed because variable at Position: ${index} <${self.line}:${self.charPos}> not closed!`; } // @todo implement split operator Splitter getOperators(string) { let statements = []; let innerCon = ''; for (let i = 0; i < string.length; i++) { let c = string.charAt(i); if (innerCon === '' && c === ' ') { continue; } if (c === '(') { if (innerCon !== '') { statements.push(this.parseOperator(innerCon)); } innerCon = ''; for (let x = 1; x < string.length; x++) { let char = string.charAt(i + x); if (char === ')') { i = i + x; break; } innerCon += char; } statements.push(this.parseOperator(innerCon)); innerCon = ''; } else { innerCon += c; } } if (innerCon !== '') { statements.push(this.parseOperator(innerCon)); } return statements; } parseOperator(operatorString) { return this.operator(operatorString); } findInstructions(type) { if (type === VParserTypes.if) { return this.getOperators(this.content); } // @todo add support for assign, for, foreach and variables... can optimize interpreter times because we dont need to find it then return []; } // right needs to be a string or a operator and should not be null! // left cant be null! this is the case if it's ! operator operator(op, left, right) { return { type: op, lvalue: left, r: right } } }