From 65375657a6ec90b7051e9d80d3c35c51e78362f9 Mon Sep 17 00:00:00 2001 From: ElementG9 Date: Fri, 12 Jun 2020 17:13:11 -0600 Subject: [PATCH] Remove other/ from gitignore --- .gitignore | 1 - other/repl.js | 11 +++ other/rpn.js | 43 ++++++++++ other/shuntingyard.js | 178 ++++++++++++++++++++++++++++++++++++++++++ other/temp.js | 178 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 other/repl.js create mode 100644 other/rpn.js create mode 100644 other/shuntingyard.js create mode 100644 other/temp.js diff --git a/.gitignore b/.gitignore index b23e1a4..724053e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -other/ package-lock.json node_modules/ jsdoc/ diff --git a/other/repl.js b/other/repl.js new file mode 100644 index 0000000..e8cd9dd --- /dev/null +++ b/other/repl.js @@ -0,0 +1,11 @@ +const rl = require('readline-sync'); +function repl(prompt, func) { + let answer; + while (answer != 'exit') { + answer = rl.question(prompt); + if (answer == 'exit') + continue; + func(answer); + } +} +module.exports = repl; diff --git a/other/rpn.js b/other/rpn.js new file mode 100644 index 0000000..e490609 --- /dev/null +++ b/other/rpn.js @@ -0,0 +1,43 @@ +// This is an unrelated file to Pivot. +// I wanted to put this in the repository in order to test another system of operators. +// This is reverse polish notation, implemented in JavaScript. +var solve = (exp) => { + var stack = []; + var expression = exp.split(" "); + for (var j = 0; j < expression.length; j++) { + var key = expression[j]; + if (key.match(/\d/)) { + stack.push(parseInt(key)); + } else if (key.match(/\w/)) { + if (Object.keys(progData).includes(key)) { + stack.push(progData[key]); + } else { + stack.push(key); + } + } + switch (key) { + case "+": // add + var opItems = stack.splice(stack.length - 2, 2); + var result = opItems[0] + opItems[1]; + stack.push(result); + break; + case "-": // subtract + var opItems = stack.splice(stack.length - 2, 2); + var result = opItems[0] - opItems[1]; + stack.push(result); + break; + case "*": // multiply + var opItems = stack.splice(stack.length - 2, 2); + var result = opItems[0] * opItems[1]; + stack.push(result); + break; + case "/": // divide + var opItems = stack.splice(stack.length - 2, 2); + var result = opItems[0] / opItems[1]; + stack.push(result); + break; + } + } + return stack; +}; +module.exports = solve; diff --git a/other/shuntingyard.js b/other/shuntingyard.js new file mode 100644 index 0000000..12bf33b --- /dev/null +++ b/other/shuntingyard.js @@ -0,0 +1,178 @@ +const repl = require('./repl.js'); +// My implementation of the algorithm from https://en.wikipedia.org/wiki/Shunting-yard_algorithm. +function shuntingYardSolve(exp) { + exp = exp.split(''); + // Remove whitespace. + exp = exp.filter(e => /\S+/.test(e)); + let output = []; + let opStack = []; + let inNumber = false; + let numStack = ''; + for (let i = 0; i < exp.length; i++) { + if (/\d|\./.test(exp[i])) { + if (inNumber) + numStack += exp[i]; + else { + inNumber = true; + numStack = exp[i]; + } + } else if (/\w/.test(exp[i])) { + if (inNumber) { + inNumber = false; + output.push((numStack)); + numStack = ''; + } + output.push(exp[i]); + } else if (/\+|-|\*|\/|=|\^/.test(exp[i])) { + if (inNumber) { + inNumber = false; + output.push((numStack)); + numStack = ''; + } + if (exp[i] == '-') { + if (i == 0) + exp[i] = ':'; + else if (exp[i - 1] == '(' || /\+|-|\*|\/|=|\^/.test(exp[i - 1])) + exp[i] = ':'; + } + let op = { + value: exp[i] + }; + if (exp[i] == ':' || exp[i] == '^') + op.precedence = 4; + else if (exp[i] == '*' || exp[i] == '/') + op.precedence = 3; + else if (exp[i] == '+' || exp[i] == '-') + op.precedence = 2; + else if (exp[i] == '=') + op.precedence = 1; + if (typeof opStack.slice(-1)[0] != 'undefined') + while (opStack.slice(-1)[0].precedence > op.precedence && opStack.slice(-1)[0].value != '(') + output.push(opStack.pop().value); + opStack.push(op); + } else if (exp[i] == '(') { + if (inNumber) { + inNumber = false; + output.push((numStack)); + numStack = ''; + } + opStack.push({ + value: exp[i], + precedence: 5 + }); + } else if (exp[i] == ')') { + if (inNumber) { + inNumber = false; + output.push((numStack)); + numStack = ''; + } + if (typeof opStack.slice(-1)[0] != 'undefined') { + while (opStack.slice(-1)[0].value != '(') + output.push(opStack.pop().value); + if (opStack.slice(-1)[0].value == '(') + opStack.pop(); + } else throw new SyntaxError('Mismatched parentheses') + } + } + if (numStack.length > 0) + output.push((numStack)); + while (opStack.length > 0) + output.push(opStack.pop().value); + return output; +} +// Reverse Polish notation implemented in JavaScript. +function rpnSolve(exp, data) { + let stack = []; + for (let i = 0; i < exp.length; i++) { + let key = exp[i]; + if (key.match(/\d|[A-Za-z]/)) + stack.push((key)); + let opItems; + let result; + switch (key) { + case '+': // Add. + opItems = stack.splice(stack.length - 2, 2); + opItems = opItems.map(item => { + if (typeof data[item] != 'undefined') + return parseFloat(data[item]); + else return parseFloat(item); + }); + result = opItems[0] + opItems[1]; + stack.push(result); + break; + case '-': // Subtract. + opItems = stack.splice(stack.length - 2, 2); + opItems = opItems.map(item => { + if (typeof data[item] != 'undefined') + return parseFloat(data[item]); + else return parseFloat(item); + }); + result = opItems[0] - opItems[1]; + stack.push(result); + break; + case '*': // Multiply + opItems = stack.splice(stack.length - 2, 2); + opItems = opItems.map(item => { + if (typeof data[item] != 'undefined') + return parseFloat(data[item]); + else return parseFloat(item); + }); + result = opItems[0] * opItems[1]; + stack.push(result); + break; + case '/': // Divide + opItems = stack.splice(stack.length - 2, 2); + opItems = opItems.map(item => { + if (typeof data[item] != 'undefined') + return parseFloat(data[item]); + else return parseFloat(item); + }); + result = opItems[0] / opItems[1]; + stack.push(result); + break; + case '^': // Exponentiation + opItems = stack.splice(stack.length - 2, 2); + opItems = opItems.map(item => { + if (typeof data[item] != 'undefined') + return parseFloat(data[item]); + else return parseFloat(item); + }); + result = opItems[0] ** opItems[1]; + stack.push(result); + break; + case ':': // Unary negation + opItems = stack.splice(stack.length - 1, 1); + opItems = opItems.map(item => { + if (typeof data[item] != 'undefined') + return parseFloat(data[item]); + else return parseFloat(item); + }); + result = -opItems[0]; + stack.push(result); + break; + case '=': // Assignment + opItems = stack.splice(stack.length - 2, 2); + data[opItems[0]] = parseFloat(opItems[1]); + stack.push(parseFloat(opItems[1])); + break; + } + } + return { + result: stack[0], + data: data + }; +}; + +function solve(exp) { + let data = {}; + exp = exp.split(';'); + exp.forEach(e => { + console.log(shuntingYardSolve(e).join(' ')); + let result = rpnSolve(shuntingYardSolve(e), data); + data = result.data; + console.log(result); + }); +}; + +console.log('Math Solver by ElementG9:'); +repl('> ', solve); diff --git a/other/temp.js b/other/temp.js new file mode 100644 index 0000000..69b636d --- /dev/null +++ b/other/temp.js @@ -0,0 +1,178 @@ +function isLetter(char) { + return /[A-Za-z]/.test(char); +} +function isOperator(char) { + return /\+|\-|\*|\/|\=|\=\=|\>|\<|\>\=|\<\=|\=\>|\;/.test(char); +} +function isDigit(char) { + return /\d/.test(char); +} +function isWhitespace(char) { + return /\s/.test(char); +} +function Token(type, subtype, value) { + this.type = type; + this.subtype = subtype; + this.value = value; +} +function opType(operator) { + if (false) + return 'left'; + else if (false) + return 'right'; + else if (/\;/.test(operator)) + return 'none'; + else + return 'dual'; +} +function isKeyword(value) { + return value == 'let'; +} +function oldTokenize(rawCode) { + let chars = rawCode.split(''); + // Create letter, operator, number, and string buffers. + let lb = []; + let ob = []; + let nb = []; + let sb = []; + // Create token array. + let tokens = []; + let stringData = { + inString: false, + stringType: null + }; + // Check for comments. + for (let i = 0; i < chars.length - 1; i++) { + let char = chars[i]; + if (char == '/' && chars[i + 1] == '/') { + // Remove the characters from the comment. + chars.splice(i, chars.indexOf('\n', i) - i); + // Adjust the index accordingly. + i -= chars.indexOf('\n', i) - i; + } + if (char == '/' && chars[i + 1] == '*') { + // If multiline comment /* find the next */ + let endindex; + for (let j = 0; j < chars.length - 1; j++) { + if (chars[j] == '*' && chars[j + 1] == '/') { + endindex = j + 2; + console.log(chars.splice(i, (j + 2) - i)); + } + } + // Adjust the index accordingly. + i -= endindex - i; + } + } + // Check for characters to be escaped. + for (let i = 0; i < chars.length; i++) { + let char = chars[i]; + if ((char == '\\') && (i + 1 != chars.length)) { + chars.splice(i, 2, `${char}${chars[i+1]}`); + continue; + } + } + // Loop through all the characters. + for (let i = 0; i < chars.length; i++) { + let char = chars[i]; + // Behave differently in/out of a string. + if (stringData.inString) { + if (char == `'`) { + stringData.inString = false; + tokens.push(new Token('string', 'n/a', sb.join(''))); + sb = []; + } else { + sb.push(char); + } + } else { + if (char == `'`) { + stringData.inString = true; + } else { + // Parsing chars, ignoring strings. + if (isLetter(char)) { + lb.push(char); + if (ob.length > 0) { + let op = ob.join(''); + tokens.push(new Token('operator', opType(op), op)); + ob = []; + } + if (nb.length > 0) { + tokens.push(new Token('number', 'n/a', nb.join(''))); + nb = []; + } + } else if (isOperator(char)) { + ob.push(char); + if (lb.length > 0) { + tokens.push(new Token('variable', 'n/a', lb.join(''))); + lb = []; + } + if (nb.length > 0) { + tokens.push(new Token('number', 'n/a', nb.join(''))); + nb = []; + } + } else if (isDigit(char)) { + nb.push(char); + if (lb.length > 0) { + tokens.push(new Token('variable', 'n/a', lb.join(''))); + lb = []; + } + if (ob.length > 0) { + let op = ob.join(''); + tokens.push(new Token('operator', opType(op), op)); + ob = []; + } + } else if (isWhitespace(char)) { + // Close all buffers. + if (lb.length > 0) { + tokens.push(new Token('variable', 'n/a', lb.join(''))); + lb = []; + } + if (ob.length > 0) { + let op = ob.join(''); + tokens.push(new Token('operator', opType(op), op)); + ob = []; + } + if (nb.length > 0) { + tokens.push(new Token('number', 'n/a', nb.join(''))); + nb = []; + } + } else if (isDelimiter(char)) { + // Close all buffers. + if (lb.length > 0) { + tokens.push(new Token('variable', 'n/a', lb.join(''))); + lb = []; + } + if (ob.length > 0) { + let op = ob.join(''); + tokens.push(new Token('operator', opType(op), op)); + ob = []; + } + if (nb.length > 0) { + tokens.push(new Token('number', 'n/a', nb.join(''))); + nb = []; + } + // Categorize and push. + if (char == '(' || char == ')') { + tokens.push(new Token('delimiter', char == '(' ? 'left' : 'right', 'parenthesis')); + } else if (char == '[' || char == ']') { + tokens.push(new Token('delimiter', char == '[' ? 'left' : 'right', 'bracket')); + } else if (char == '{' || char == '}') { + tokens.push(new Token('delimiter', char == '{' ? 'left' : 'right', 'brace')); + } + } + } + } + } + // Check for keywords. + for (let i = 0; i < tokens.length; i++) { + if (tokens[i].type == 'variable' && isKeyword(tokens[i].value)) { + tokens[i].type = 'keyword'; + } + } + // Add indexes. + let layer = 0; + for (let i = 0; i < tokens.length; i++) { + tokens[i].index = i; + } + return tokens; +}; +console.log(oldTokenize(`let x = 'it\\'s cool outside';`));