forked from DebaucheryLibrarian/traxxx
154 lines
3.7 KiB
JavaScript
Executable File
154 lines
3.7 KiB
JavaScript
Executable File
'use strict';
|
|
|
|
const arrayEqual = require('array-equal');
|
|
|
|
/* eslint-disable */
|
|
function evaluateBranch(tree, modifiers) {
|
|
const result = tree.map((expression) => {
|
|
if (expression.type === 'group') {
|
|
return evaluateBranch(expression.values, expression.modifiers);
|
|
} if (expression.modifiers.length === 0) {
|
|
return ''; // This is a trigger to stringify the previous values
|
|
} if (arrayEqual(['plus'], expression.modifiers)) {
|
|
return 0;
|
|
} if (arrayEqual(['negate', 'negate'], expression.modifiers)) {
|
|
return true;
|
|
} if (arrayEqual(['negate', 'plus'], expression.modifiers)) {
|
|
return true;
|
|
} if (arrayEqual(['plus', 'plus', 'negate'], expression.modifiers)) {
|
|
return true;
|
|
} if (arrayEqual(['plus', 'negate', 'negate'], expression.modifiers)) {
|
|
return 1;
|
|
}
|
|
throw new Error(`Found unrecognized modifier pattern: ${expression.modifiers}`);
|
|
}).reduce((combined, value) => {
|
|
if (value === '') {
|
|
return combined.toString();
|
|
}
|
|
if (value === true) {
|
|
value = 1;
|
|
}
|
|
|
|
if (typeof combined === 'string') {
|
|
return combined + value.toString();
|
|
}
|
|
return combined + value;
|
|
}, 0);
|
|
|
|
if (modifiers == null) {
|
|
return result;
|
|
}
|
|
if (arrayEqual(['plus'], modifiers)) {
|
|
return parseInt(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function evaluate(tree) {
|
|
return evaluateBranch(tree);
|
|
}
|
|
|
|
function parse(string) {
|
|
const length = string.length;
|
|
let byte;
|
|
|
|
let stateFinishedItem = false;
|
|
const modifierStack = [[]];
|
|
const itemStack = [[]];
|
|
let currentStack = itemStack[0];
|
|
let currentModifiers = modifierStack[0];
|
|
let stackLevel = 0;
|
|
|
|
for (let pos = 0; pos < length; pos++) {
|
|
byte = string[pos];
|
|
|
|
switch (byte) {
|
|
case '+':
|
|
if (pos === 0 || stateFinishedItem === false) {
|
|
// Modifier / number-cast
|
|
currentModifiers.push('plus');
|
|
stateFinishedItem = false;
|
|
} else {
|
|
// Addition, we don't need to do anything here
|
|
}
|
|
break;
|
|
case '!':
|
|
stateFinishedItem = false;
|
|
currentModifiers.push('negate');
|
|
break;
|
|
case '(':
|
|
stateFinishedItem = false;
|
|
stackLevel++;
|
|
itemStack[stackLevel] = currentStack = [];
|
|
modifierStack[stackLevel] = currentModifiers = [];
|
|
break;
|
|
case ')':
|
|
if (stackLevel === 0) {
|
|
throw new Error('Encountered ) without matching (');
|
|
}
|
|
|
|
stackLevel--;
|
|
stateFinishedItem = true;
|
|
|
|
currentStack = itemStack[stackLevel];
|
|
currentStack.push({
|
|
type: 'group',
|
|
values: itemStack[stackLevel + 1],
|
|
modifiers: modifierStack[stackLevel],
|
|
});
|
|
|
|
currentModifiers = modifierStack[stackLevel] = [];
|
|
break;
|
|
case '[':
|
|
if (string[pos + 1] === ']') {
|
|
// Reached the brackets; end of the modifier sequence
|
|
currentStack.push({
|
|
type: 'brackets',
|
|
modifiers: currentModifiers,
|
|
});
|
|
|
|
currentModifiers = [];
|
|
pos += 1; // Skip over the closing bracket
|
|
stateFinishedItem = true;
|
|
} else {
|
|
throw new Error(`Invalid byte found; expected ] but got ${string[pos + 1]}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
return itemStack[0];
|
|
}
|
|
|
|
function parseExpression(string) {
|
|
return evaluate(parse(string));
|
|
}
|
|
|
|
function findAll(regex, target) {
|
|
const results = []; let
|
|
match;
|
|
|
|
while (match = regex.exec(target)) {
|
|
results.push(match);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
function cf(testcase) {
|
|
let [_, parent, child, initialExpression] = /var s,t,o,p,b,r,e,a,k,i,n,g,f,\s*([a-zA-Z]+)={"([a-zA-Z]+)":([^}]+)};/.exec(testcase);
|
|
|
|
const modifyingExpressions = findAll(new RegExp(`${parent}\.${child}\s*([*+-])=\s*([^;]+)`, 'g'), testcase).map(match => ({
|
|
operation: match[1],
|
|
expression: match[2],
|
|
})).map(({ operation, expression }) => ({
|
|
operation,
|
|
expression: parseExpression(expression),
|
|
}));
|
|
|
|
initialExpression = parseExpression(initialExpression);
|
|
|
|
return { parent, child, initialExpression, modifyingExpressions };
|
|
};
|
|
|
|
module.exports = cf;
|