VTepL/src/Parser.js

209 lines
6.7 KiB
JavaScript

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('<!--', i, true)) {
self.extract('-->', 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
}
}
}