209 lines
6.7 KiB
JavaScript
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
|
|
}
|
|
}
|
|
|
|
}
|