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;
 |