├── .gitignore ├── Evaluator ├── evaluate.js └── order.js ├── Lexer └── lexer.js ├── Mathjax ├── mathjax.js └── mathjax_utils.js ├── Parser └── parser.js ├── README.md ├── constants ├── nodeType.js └── tokenType.js ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /Evaluator/evaluate.js: -------------------------------------------------------------------------------- 1 | const NODE = require("../constants/nodeType"); 2 | 3 | function intDivide(a, b) { 4 | var result = a / b; 5 | if (result >= 0) return Math.floor(result); 6 | else return Math.ceil(result); 7 | } 8 | 9 | function pow(a, b) { 10 | return Math.pow(a, b); 11 | } 12 | 13 | function nthRoot(a, b) { 14 | return pow(a, 1 / b); 15 | } 16 | 17 | function logNBaseX(n, x) { 18 | return Math.log(n) / Math.log(x); 19 | } 20 | 21 | function naturalLog(n) { 22 | return Math.log(n); 23 | } 24 | 25 | function convertDecimalToBinary(n) { 26 | return parseInt(n).toString(2); 27 | } 28 | 29 | function convertBinaryToDecimal(b) { 30 | return parseFloat(parseInt(b.toString(), 2).toString()); 31 | } 32 | 33 | function convertDecimalToHexaDecimal(n) { 34 | return parseInt(n).toString(16); 35 | } 36 | 37 | function convertHexaDecimalToDecimal(b) { 38 | return parseFloat(parseInt(b.toString(), 16).toString()); 39 | } 40 | 41 | function convertDecimalToOctal(n) { 42 | return parseInt(n).toString(8); 43 | } 44 | 45 | function convertOctalToDecimal(b) { 46 | return parseFloat(parseInt(b.toString(), 8).toString()); 47 | } 48 | 49 | function evaluate(tree) { 50 | // Number 51 | if (tree.nodeType === NODE.TYPE.NUMBER) { 52 | return tree.value; 53 | } 54 | // Binary String 55 | else if (tree.nodeType === NODE.TYPE.BINARY_STRING) { 56 | return convertBinaryToDecimal(tree.value); 57 | } 58 | // HexaDecimal String 59 | else if (tree.nodeType === NODE.TYPE.HEXADECIMAL_STRING) { 60 | return convertHexaDecimalToDecimal(tree.value); 61 | } 62 | // Octal String 63 | else if (tree.nodeType === NODE.TYPE.OCTAL_STRING) { 64 | return convertOctalToDecimal(tree.value); 65 | } 66 | // Add Node 67 | else if (tree.nodeType === NODE.TYPE.ADD) { 68 | return evaluate(tree.node1) + evaluate(tree.node2); 69 | } 70 | // Sub Node 71 | else if (tree.nodeType === NODE.TYPE.SUB) { 72 | return evaluate(tree.node1) - evaluate(tree.node2); 73 | } 74 | // Mul Node 75 | else if (tree.nodeType === NODE.TYPE.MULTIPLY) { 76 | return evaluate(tree.node1) * evaluate(tree.node2); 77 | } 78 | // Div Node 79 | else if (tree.nodeType === NODE.TYPE.DIVIDE) { 80 | return evaluate(tree.node1) / evaluate(tree.node2); 81 | } 82 | // Mod Node 83 | else if (tree.nodeType === NODE.TYPE.MOD) { 84 | return evaluate(tree.node1) % evaluate(tree.node2); 85 | } 86 | // Pow Node 87 | else if (tree.nodeType === NODE.TYPE.POW) { 88 | return pow(evaluate(tree.node1), evaluate(tree.node2)); 89 | } 90 | // Int Divide Node 91 | else if (tree.nodeType === NODE.TYPE.INT_DIVIDE) { 92 | return intDivide(evaluate(tree.node1), evaluate(tree.node2)); 93 | } 94 | // NTH Root Node 95 | else if (tree.nodeType === NODE.TYPE.NTH_ROOT) { 96 | return nthRoot(evaluate(tree.node1), evaluate(tree.node2)); 97 | } 98 | // Log Base Node 99 | else if (tree.nodeType === NODE.TYPE.LOGNBASEX) { 100 | return logNBaseX(evaluate(tree.node1), evaluate(tree.node2)); 101 | } 102 | // Natural Log Node 103 | else if (tree.nodeType === NODE.TYPE.NAT_LOG) { 104 | return naturalLog(evaluate(tree.node)); 105 | } 106 | // Binary Conversion Node 107 | else if (tree.nodeType === NODE.TYPE.BINARY) { 108 | return `b${convertDecimalToBinary(evaluate(tree.node))}`; 109 | } 110 | // HexaDecimal Conversion Node 111 | else if (tree.nodeType === NODE.TYPE.HEXADECIMAL) { 112 | return `h${convertDecimalToHexaDecimal(evaluate(tree.node)).toUpperCase()}`; 113 | } 114 | // Octal Conversion Node 115 | else if (tree.nodeType === NODE.TYPE.OCTAL) { 116 | return `o${convertDecimalToOctal(evaluate(tree.node))}`; 117 | } 118 | // Bitwise And Node 119 | else if (tree.nodeType === NODE.TYPE.BITWISE_AND) { 120 | return evaluate(tree.node1) & evaluate(tree.node2); 121 | } 122 | // Bitwise Or Node 123 | else if (tree.nodeType === NODE.TYPE.BITWISE_OR) { 124 | return evaluate(tree.node1) | evaluate(tree.node2); 125 | } 126 | } 127 | 128 | module.exports = evaluate; 129 | -------------------------------------------------------------------------------- /Evaluator/order.js: -------------------------------------------------------------------------------- 1 | const NODE = require("../constants/nodeType"); 2 | 3 | function orderOfEvaluation(tree = {}) { 4 | if (tree.nodeType === NODE.TYPE.NUMBER) { 5 | return tree.value; 6 | } else if (tree.nodeType === NODE.TYPE.ADD) { 7 | return ( 8 | "(" + 9 | orderOfEvaluation(tree.node1) + 10 | " + " + 11 | orderOfEvaluation(tree.node2) + 12 | ")" 13 | ); 14 | } else if (tree.nodeType === NODE.TYPE.SUB) { 15 | return ( 16 | "(" + 17 | orderOfEvaluation(tree.node1) + 18 | " - " + 19 | orderOfEvaluation(tree.node2) + 20 | ")" 21 | ); 22 | } else if (tree.nodeType === NODE.TYPE.MULTIPLY) { 23 | return ( 24 | "(" + 25 | orderOfEvaluation(tree.node1) + 26 | " * " + 27 | orderOfEvaluation(tree.node2) + 28 | ")" 29 | ); 30 | } else if (tree.nodeType === NODE.TYPE.DIVIDE) { 31 | return ( 32 | "(" + 33 | orderOfEvaluation(tree.node1) + 34 | " / " + 35 | orderOfEvaluation(tree.node2) + 36 | ")" 37 | ); 38 | } else if (tree.nodeType === NODE.TYPE.MOD) { 39 | return ( 40 | "(" + 41 | orderOfEvaluation(tree.node1) + 42 | " % " + 43 | orderOfEvaluation(tree.node2) + 44 | ")" 45 | ); 46 | } else if (tree.nodeType === NODE.TYPE.POW) { 47 | return ( 48 | "(" + 49 | orderOfEvaluation(tree.node1) + 50 | " ** " + 51 | orderOfEvaluation(tree.node2) + 52 | ")" 53 | ); 54 | } else if (tree.nodeType === NODE.TYPE.INT_DIVIDE) { 55 | return ( 56 | "(" + 57 | orderOfEvaluation(tree.node1) + 58 | " // " + 59 | orderOfEvaluation(tree.node2) + 60 | ")" 61 | ); 62 | } else if (tree.nodeType === NODE.TYPE.NTH_ROOT) { 63 | return ( 64 | "(" + 65 | orderOfEvaluation(tree.node1) + 66 | " # " + 67 | orderOfEvaluation(tree.node2) + 68 | ")" 69 | ); 70 | } else if (tree.nodeType === NODE.TYPE.LOGNBASEX) { 71 | return ( 72 | "(" + 73 | orderOfEvaluation(tree.node1) + 74 | " LB " + 75 | orderOfEvaluation(tree.node2) + 76 | ")" 77 | ); 78 | } else if (tree.nodeType === NODE.TYPE.NAT_LOG) { 79 | return "(" + "LN " + orderOfEvaluation(tree.node) + ")"; 80 | } 81 | } 82 | 83 | module.exports = orderOfEvaluation; 84 | -------------------------------------------------------------------------------- /Lexer/lexer.js: -------------------------------------------------------------------------------- 1 | const TOKEN = require("./../constants/tokenType"); 2 | 3 | const WHITESPACE = " \n\t"; 4 | const DIGITS = "0123456789"; 5 | const BINARY_DIGITS = "01"; 6 | const HEXADECIMAL_DIGITS = DIGITS + "ABCDEF"; 7 | const OCTAL_DIGITS = "01234567"; 8 | 9 | class Token { 10 | constructor(type, value = undefined) { 11 | this.type = type; 12 | this.value = value; 13 | } 14 | } 15 | 16 | class Lexer { 17 | constructor(text) { 18 | this.currentChar = ""; 19 | this.index = 0; 20 | this.text = text; 21 | this.tokens = []; 22 | 23 | this.advance(); 24 | } 25 | 26 | advance() { 27 | try { 28 | this.currentChar = this.text[this.index++]; 29 | } catch (e) { 30 | console.log(e); 31 | this.currentChar = undefined; 32 | } 33 | } 34 | 35 | generateTokens() { 36 | while (this.currentChar !== undefined) { 37 | // Whitespaces 38 | if (WHITESPACE.includes(this.currentChar)) this.advance(); 39 | // Decimal and Integers 40 | else if (this.currentChar === "." || DIGITS.includes(this.currentChar)) { 41 | this.tokens.push(this.generateNumber()); 42 | } 43 | // Left Parenthesis 44 | else if (this.currentChar === TOKEN.OPERATOR.LPAREN) { 45 | this.advance(); 46 | this.tokens.push(new Token(TOKEN.TYPE.LPAREN, TOKEN.OPERATOR.LPAREN)); 47 | } 48 | // Right Parenthesis 49 | else if (this.currentChar === TOKEN.OPERATOR.RPAREN) { 50 | this.advance(); 51 | this.tokens.push(new Token(TOKEN.TYPE.RPAREN, TOKEN.OPERATOR.RPAREN)); 52 | } 53 | // Plus 54 | else if (this.currentChar === TOKEN.OPERATOR.PLUS) { 55 | this.advance(); 56 | this.tokens.push(new Token(TOKEN.TYPE.PLUS, TOKEN.OPERATOR.PLUS)); 57 | } 58 | // Minus 59 | else if (this.currentChar === TOKEN.OPERATOR.MINUS) { 60 | this.advance(); 61 | this.tokens.push(new Token(TOKEN.TYPE.MINUS, TOKEN.OPERATOR.MINUS)); 62 | } 63 | // Multiply 64 | else if (this.currentChar === TOKEN.OPERATOR.MULTIPLY) { 65 | this.advance(); 66 | // Pow 67 | if (this.currentChar === TOKEN.OPERATOR.MULTIPLY) { 68 | this.advance(); 69 | this.tokens.push(new Token(TOKEN.TYPE.POW, TOKEN.OPERATOR.POW)); 70 | } else 71 | this.tokens.push( 72 | new Token(TOKEN.TYPE.MULTIPLY, TOKEN.OPERATOR.MULTIPLY) 73 | ); 74 | } 75 | // Divide 76 | else if (this.currentChar === TOKEN.OPERATOR.DIVIDE) { 77 | this.advance(); 78 | // Pow 79 | if (this.currentChar === TOKEN.OPERATOR.DIVIDE) { 80 | this.advance(); 81 | this.tokens.push( 82 | new Token(TOKEN.TYPE.INT_DIVIDE, TOKEN.OPERATOR.INT_DIVIDE) 83 | ); 84 | } else 85 | this.tokens.push(new Token(TOKEN.TYPE.DIVIDE, TOKEN.OPERATOR.DIVIDE)); 86 | } 87 | // Mod 88 | else if (this.currentChar === TOKEN.OPERATOR.MOD) { 89 | this.advance(); 90 | this.tokens.push(new Token(TOKEN.TYPE.MOD, TOKEN.OPERATOR.MOD)); 91 | } 92 | // Nth Root 93 | else if (this.currentChar === TOKEN.OPERATOR.NTH_ROOT) { 94 | this.advance(); 95 | this.tokens.push( 96 | new Token(TOKEN.TYPE.NTH_ROOT, TOKEN.OPERATOR.NTH_ROOT) 97 | ); 98 | } 99 | // Logarithm 100 | else if (this.currentChar === "L") { 101 | this.advance(); 102 | // Custom Base 103 | if (this.currentChar === "B") { 104 | this.advance(); 105 | this.tokens.push( 106 | new Token(TOKEN.TYPE.LOGNBASEX, TOKEN.OPERATOR.LOGNBASEX) 107 | ); 108 | } 109 | // Natural Log 110 | else if (this.currentChar === "N") { 111 | this.advance(); 112 | this.tokens.push( 113 | new Token(TOKEN.TYPE.NAT_LOG, TOKEN.OPERATOR.NAT_LOG) 114 | ); 115 | } 116 | } 117 | // Binary String 118 | else if (this.currentChar === TOKEN.OPERATOR.BINARY_STRING) { 119 | this.advance(); 120 | this.tokens.push(this.generateString(TOKEN.OPERATOR.BINARY_STRING)); 121 | } 122 | // HexaDecimal String 123 | else if (this.currentChar === TOKEN.OPERATOR.HEXADECIMAL_STRING) { 124 | this.advance(); 125 | this.tokens.push( 126 | this.generateString(TOKEN.OPERATOR.HEXADECIMAL_STRING) 127 | ); 128 | } 129 | // Octal String 130 | else if (this.currentChar === TOKEN.OPERATOR.OCTAL_STRING) { 131 | this.advance(); 132 | this.tokens.push(this.generateString(TOKEN.OPERATOR.OCTAL_STRING)); 133 | } 134 | // Binary Conversion 135 | else if (this.currentChar === TOKEN.OPERATOR.BINARY) { 136 | this.advance(); 137 | this.tokens.push(new Token(TOKEN.TYPE.BINARY, TOKEN.OPERATOR.BINARY)); 138 | } 139 | // HexaDecimal Conversion 140 | else if (this.currentChar === TOKEN.OPERATOR.HEXADECIMAL) { 141 | this.advance(); 142 | this.tokens.push( 143 | new Token(TOKEN.TYPE.HEXADECIMAL, TOKEN.OPERATOR.HEXADECIMAL) 144 | ); 145 | } 146 | // Octal Conversion 147 | else if (this.currentChar === TOKEN.OPERATOR.OCTAL) { 148 | this.advance(); 149 | this.tokens.push(new Token(TOKEN.TYPE.OCTAL, TOKEN.OPERATOR.OCTAL)); 150 | } 151 | // Constant E 152 | else if (this.currentChar === TOKEN.OPERATOR.E) { 153 | this.advance(); 154 | this.tokens.push(new Token(TOKEN.TYPE.NUMBER, Math.exp(1))); 155 | } 156 | // Constant PI 157 | else if (this.currentChar === "p") { 158 | this.advance(); 159 | if (this.currentChar === "i") { 160 | this.advance(); 161 | this.tokens.push(new Token(TOKEN.TYPE.NUMBER, Math.PI)); 162 | } 163 | } 164 | // Bitwise AND 165 | else if (this.currentChar === TOKEN.OPERATOR.BITWISE_AND) { 166 | this.advance(); 167 | this.tokens.push( 168 | new Token(TOKEN.TYPE.BITWISE_AND, TOKEN.OPERATOR.BITWISE_AND) 169 | ); 170 | } 171 | // Bitwise OR 172 | else if (this.currentChar === TOKEN.OPERATOR.BITWISE_OR) { 173 | this.advance(); 174 | this.tokens.push( 175 | new Token(TOKEN.TYPE.BITWISE_OR, TOKEN.OPERATOR.BITWISE_OR) 176 | ); 177 | } 178 | } 179 | 180 | return this.tokens; 181 | } 182 | 183 | generateString(TYPE) { 184 | let ACCEPTED_CHARS = ""; 185 | let targetTokenType = ""; 186 | 187 | switch (TYPE) { 188 | case TOKEN.OPERATOR.BINARY_STRING: 189 | ACCEPTED_CHARS = BINARY_DIGITS; 190 | targetTokenType = TOKEN.TYPE.BINARY_STRING; 191 | break; 192 | case TOKEN.OPERATOR.HEXADECIMAL_STRING: 193 | ACCEPTED_CHARS = HEXADECIMAL_DIGITS.toLowerCase(); 194 | targetTokenType = TOKEN.TYPE.HEXADECIMAL_STRING; 195 | break; 196 | case TOKEN.OPERATOR.OCTAL_STRING: 197 | ACCEPTED_CHARS = OCTAL_DIGITS; 198 | targetTokenType = TOKEN.TYPE.OCTAL_STRING; 199 | break; 200 | default: 201 | return; 202 | } 203 | 204 | let currentString = this.currentChar; 205 | this.advance(); 206 | 207 | while ( 208 | this.currentChar !== undefined && 209 | ACCEPTED_CHARS.includes(this.currentChar) 210 | ) { 211 | currentString += this.currentChar; 212 | this.advance(); 213 | } 214 | 215 | return new Token( 216 | targetTokenType, 217 | TYPE === TOKEN.OPERATOR.HEXADECIMAL 218 | ? currentString.toUpperCase() 219 | : currentString 220 | ); 221 | } 222 | 223 | generateNumber() { 224 | let decimalPointCount = 0; 225 | let currentNumber = this.currentChar; 226 | 227 | this.advance(); 228 | 229 | while ( 230 | this.currentChar !== undefined && 231 | (this.currentChar === "." || DIGITS.includes(this.currentChar)) 232 | ) { 233 | if (this.currentChar === ".") { 234 | decimalPointCount++; 235 | if (decimalPointCount > 1) break; 236 | } 237 | 238 | currentNumber += this.currentChar; 239 | this.advance(); 240 | } 241 | 242 | if (currentNumber.startsWith(".")) currentNumber = "0" + currentNumber; 243 | if (currentNumber.endsWith(".")) currentNumber += "0"; 244 | 245 | return new Token( 246 | TOKEN.TYPE.NUMBER, 247 | decimalPointCount === 0 248 | ? parseInt(currentNumber) 249 | : parseFloat(currentNumber) 250 | ); 251 | } 252 | } 253 | 254 | module.exports = Lexer; 255 | -------------------------------------------------------------------------------- /Mathjax/mathjax.js: -------------------------------------------------------------------------------- 1 | const util = require("util"); 2 | 3 | const mathJax = require("./mathjax_utils"); 4 | 5 | const Lexer = require("../Lexer/lexer"); 6 | const Parser = require("../Parser/parser"); 7 | const Evaluator = require("../Evaluator/evaluate"); 8 | const Order = require("../Evaluator/order"); 9 | 10 | const text = "LN 9 + 128 LB 2"; 11 | 12 | const tree = new Parser(new Lexer(text).generateTokens()).parse(); 13 | 14 | console.log(util.inspect(tree, { showHidden: false, depth: null }) + "\n"); 15 | console.log(mathJax(tree)); 16 | -------------------------------------------------------------------------------- /Mathjax/mathjax_utils.js: -------------------------------------------------------------------------------- 1 | const NODE = require("../constants/nodeType"); 2 | /* 3 | { 4 | nodeType: 'AddNode', 5 | node1: { nodeType: 'NumberNode', value: 2 }, 6 | node2: { nodeType: 'NumberNode', value: 3 } 7 | } 8 | */ 9 | 10 | const mathJax = (tree = {}) => { 11 | if (tree.nodeType === NODE.TYPE.NUMBER) { 12 | return `${tree.value}`; 13 | } else if (tree.nodeType === NODE.TYPE.ADD) { 14 | return `${mathJax(tree.node1)} + ${mathJax(tree.node2)}`; 15 | } else if (tree.nodeType === NODE.TYPE.SUB) { 16 | return `${mathJax(tree.node1)} - ${mathJax(tree.node2)}`; 17 | } else if (tree.nodeType === NODE.TYPE.MULTIPLY) { 18 | return `${mathJax(tree.node1)} * ${mathJax(tree.node2)}`; 19 | } else if (tree.nodeType === NODE.TYPE.DIVIDE) { 20 | return `\\frac {${mathJax(tree.node1)}} {${mathJax(tree.node2)}}`; 21 | } else if (tree.nodeType === NODE.TYPE.LOGNBASEX) { 22 | return `\\log_${mathJax(tree.node2)} ({${mathJax(tree.node1)}})`; 23 | } else if (tree.nodeType === NODE.TYPE.NAT_LOG) { 24 | if (tree.node.nodeType === NODE.TYPE.NUMBER) 25 | return `\\ln {${mathJax(tree.node)}}`; 26 | else return `\\ln {(${mathJax(tree.node)})}`; 27 | } else if (tree.nodeType === NODE.TYPE.NTH_ROOT) { 28 | if (tree.node2.nodeType !== NODE.TYPE.NUMBER) { 29 | return `\\sqrt[${mathJax(tree.node2)}]{(${mathJax(tree.node1)})}`; 30 | } else { 31 | if (tree.node2.value === 2) { 32 | return `\\sqrt{${mathJax(tree.node1)}}`; 33 | } else { 34 | return `\\sqrt[${mathJax(tree.node2)}]{(${mathJax(tree.node1)})}`; 35 | } 36 | } 37 | } 38 | }; 39 | 40 | module.exports = mathJax; 41 | -------------------------------------------------------------------------------- /Parser/parser.js: -------------------------------------------------------------------------------- 1 | const TOKEN = require("../constants/tokenType"); 2 | const NODE = require("../constants/nodeType"); 3 | 4 | class Parser { 5 | constructor(tokens) { 6 | this.tokens = tokens; 7 | this.index = 0; 8 | this.currentToken = undefined; 9 | this.advance(); 10 | } 11 | 12 | advance() { 13 | try { 14 | this.currentToken = this.tokens[this.index++]; 15 | } catch (e) { 16 | console.log(e); 17 | this.currentToken = undefined; 18 | } 19 | } 20 | 21 | raiseError() { 22 | throw new Error("Invalid Syntax"); 23 | } 24 | 25 | parse() { 26 | if (this.currentToken === undefined) return undefined; 27 | let result = this.expr(); 28 | if (this.currentToken !== undefined) this.raiseError(); 29 | 30 | return result; 31 | } 32 | 33 | expr() { 34 | let result = this.term(); 35 | 36 | while ( 37 | this.currentToken !== undefined && 38 | [TOKEN.TYPE.PLUS, TOKEN.TYPE.MINUS].includes(this.currentToken.type) 39 | ) { 40 | if (this.currentToken.type === TOKEN.TYPE.PLUS) { 41 | this.advance(); 42 | result = { 43 | nodeType: NODE.TYPE.ADD, 44 | node1: result, 45 | node2: this.term(), 46 | }; 47 | } else if (this.currentToken.type === TOKEN.TYPE.MINUS) { 48 | this.advance(); 49 | result = { 50 | nodeType: NODE.TYPE.SUB, 51 | node1: result, 52 | node2: this.term(), 53 | }; 54 | } 55 | } 56 | 57 | return result; 58 | } 59 | 60 | term() { 61 | let result = this.factor(); 62 | 63 | while ( 64 | this.currentToken !== undefined && 65 | [ 66 | TOKEN.TYPE.MULTIPLY, 67 | TOKEN.TYPE.DIVIDE, 68 | TOKEN.TYPE.MOD, 69 | TOKEN.TYPE.INT_DIVIDE, 70 | TOKEN.TYPE.POW, 71 | TOKEN.TYPE.NTH_ROOT, 72 | TOKEN.TYPE.LOGNBASEX, 73 | TOKEN.TYPE.BITWISE_AND, 74 | TOKEN.TYPE.BITWISE_OR, 75 | ].includes(this.currentToken.type) 76 | ) { 77 | if (this.currentToken.type === TOKEN.TYPE.MULTIPLY) { 78 | this.advance(); 79 | result = { 80 | nodeType: NODE.TYPE.MULTIPLY, 81 | node1: result, 82 | node2: this.factor(), 83 | }; 84 | } else if (this.currentToken.type === TOKEN.TYPE.DIVIDE) { 85 | this.advance(); 86 | result = { 87 | nodeType: NODE.TYPE.DIVIDE, 88 | node1: result, 89 | node2: this.factor(), 90 | }; 91 | } else if (this.currentToken.type === TOKEN.TYPE.MOD) { 92 | this.advance(); 93 | result = { 94 | nodeType: NODE.TYPE.MOD, 95 | node1: result, 96 | node2: this.factor(), 97 | }; 98 | } else if (this.currentToken.type === TOKEN.TYPE.INT_DIVIDE) { 99 | this.advance(); 100 | result = { 101 | nodeType: NODE.TYPE.INT_DIVIDE, 102 | node1: result, 103 | node2: this.factor(), 104 | }; 105 | } else if (this.currentToken.type === TOKEN.TYPE.POW) { 106 | this.advance(); 107 | result = { 108 | nodeType: NODE.TYPE.POW, 109 | node1: result, 110 | node2: this.expr(), 111 | }; 112 | } else if (this.currentToken.type === TOKEN.TYPE.NTH_ROOT) { 113 | this.advance(); 114 | result = { 115 | nodeType: NODE.TYPE.NTH_ROOT, 116 | node1: result, 117 | node2: this.factor(), 118 | }; 119 | } else if (this.currentToken.type === TOKEN.TYPE.LOGNBASEX) { 120 | this.advance(); 121 | result = { 122 | nodeType: NODE.TYPE.LOGNBASEX, 123 | node1: result, 124 | node2: this.factor(), 125 | }; 126 | } else if (this.currentToken.type === TOKEN.TYPE.BITWISE_AND) { 127 | this.advance(); 128 | result = { 129 | nodeType: NODE.TYPE.BITWISE_AND, 130 | node1: result, 131 | node2: this.factor(), 132 | }; 133 | } else if (this.currentToken.type === TOKEN.TYPE.BITWISE_OR) { 134 | this.advance(); 135 | result = { 136 | nodeType: NODE.TYPE.BITWISE_OR, 137 | node1: result, 138 | node2: this.factor(), 139 | }; 140 | } 141 | } 142 | 143 | return result; 144 | } 145 | 146 | factor() { 147 | if (this.currentToken.type === TOKEN.TYPE.LPAREN) { 148 | this.advance(); 149 | let result = this.expr(); 150 | if (this.currentToken.type !== TOKEN.TYPE.RPAREN) this.raiseError(); 151 | this.advance(); 152 | 153 | return result; 154 | } else if (this.currentToken.type === TOKEN.TYPE.NUMBER) { 155 | let value = this.currentToken.value; 156 | this.advance(); 157 | return { 158 | nodeType: NODE.TYPE.NUMBER, 159 | value: value, 160 | }; 161 | } else if (this.currentToken.type === TOKEN.TYPE.BINARY_STRING) { 162 | let value = this.currentToken.value; 163 | this.advance(); 164 | return { 165 | nodeType: NODE.TYPE.BINARY_STRING, 166 | value: value, 167 | }; 168 | } else if (this.currentToken.type === TOKEN.TYPE.HEXADECIMAL_STRING) { 169 | let value = this.currentToken.value; 170 | this.advance(); 171 | return { 172 | nodeType: NODE.TYPE.HEXADECIMAL_STRING, 173 | value: value, 174 | }; 175 | } else if (this.currentToken.type === TOKEN.TYPE.OCTAL_STRING) { 176 | let value = this.currentToken.value; 177 | this.advance(); 178 | return { 179 | nodeType: NODE.TYPE.OCTAL_STRING, 180 | value: value, 181 | }; 182 | } else if (this.currentToken.type === TOKEN.TYPE.PLUS) { 183 | this.advance(); 184 | return { 185 | nodeType: NODE.TYPE.PLUS, 186 | node: this.factor(), 187 | }; 188 | } else if (this.currentToken.type === TOKEN.TYPE.MINUS) { 189 | this.advance(); 190 | return { 191 | nodeType: NODE.TYPE.MINUS, 192 | node: this.factor(), 193 | }; 194 | } else if (this.currentToken.type === TOKEN.TYPE.NAT_LOG) { 195 | this.advance(); 196 | return { 197 | nodeType: NODE.TYPE.NAT_LOG, 198 | node: this.factor(), 199 | }; 200 | } else if (this.currentToken.type === TOKEN.TYPE.BINARY) { 201 | this.advance(); 202 | return { 203 | nodeType: NODE.TYPE.BINARY, 204 | node: this.factor(), 205 | }; 206 | } else if (this.currentToken.type === TOKEN.TYPE.HEXADECIMAL) { 207 | this.advance(); 208 | return { 209 | nodeType: NODE.TYPE.HEXADECIMAL, 210 | node: this.factor(), 211 | }; 212 | } else if (this.currentToken.type === TOKEN.TYPE.OCTAL) { 213 | this.advance(); 214 | return { 215 | nodeType: NODE.TYPE.OCTAL, 216 | node: this.factor(), 217 | }; 218 | } 219 | 220 | this.raiseError(); 221 | } 222 | } 223 | 224 | module.exports = Parser; 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ```js 2 | Expression 3 | ((((((1 + 2) * 3 + (4 / 2)) ** 2) + 7) LB 2) + 3) * e * pi 4 | 5 | Tokens 6 | [ Token { type: 'TT_LPAREN', value: '(' }, 7 | Token { type: 'TT_LPAREN', value: '(' }, 8 | Token { type: 'TT_LPAREN', value: '(' }, 9 | Token { type: 'TT_LPAREN', value: '(' }, 10 | Token { type: 'TT_LPAREN', value: '(' }, 11 | Token { type: 'TT_LPAREN', value: '(' }, 12 | Token { type: 'TT_NUMBER', value: 1 }, 13 | Token { type: 'TT_PLUS', value: '+' }, 14 | Token { type: 'TT_NUMBER', value: 2 }, 15 | Token { type: 'TT_RPAREN', value: ')' }, 16 | Token { type: 'TT_MULTIPLY', value: '*' }, 17 | Token { type: 'TT_NUMBER', value: 3 }, 18 | Token { type: 'TT_PLUS', value: '+' }, 19 | Token { type: 'TT_LPAREN', value: '(' }, 20 | Token { type: 'TT_NUMBER', value: 4 }, 21 | Token { type: 'TT_DIVIDE', value: '/' }, 22 | Token { type: 'TT_NUMBER', value: 2 }, 23 | Token { type: 'TT_RPAREN', value: ')' }, 24 | Token { type: 'TT_RPAREN', value: ')' }, 25 | Token { type: 'TT_POW', value: '**' }, 26 | Token { type: 'TT_NUMBER', value: 2 }, 27 | Token { type: 'TT_RPAREN', value: ')' }, 28 | Token { type: 'TT_PLUS', value: '+' }, 29 | Token { type: 'TT_NUMBER', value: 7 }, 30 | Token { type: 'TT_RPAREN', value: ')' }, 31 | Token { type: 'TT_LOG_BASE', value: 'LB' }, 32 | Token { type: 'TT_NUMBER', value: 2 }, 33 | Token { type: 'TT_RPAREN', value: ')' }, 34 | Token { type: 'TT_PLUS', value: '+' }, 35 | Token { type: 'TT_NUMBER', value: 3 }, 36 | Token { type: 'TT_RPAREN', value: ')' }, 37 | Token { type: 'TT_MULTIPLY', value: '*' }, 38 | Token { type: 'TT_NUMBER', value: 2.718281828459045 }, 39 | Token { type: 'TT_MULTIPLY', value: '*' }, 40 | Token { type: 'TT_NUMBER', value: 3.141592653589793 } ] 41 | 42 | Tree 43 | { nodeType: 'NT_MULIPLY', 44 | node1: 45 | { nodeType: 'NT_MULIPLY', 46 | node1: 47 | { nodeType: 'NT_ADD', 48 | node1: 49 | { nodeType: 'NT_LOG_BASE', 50 | node1: 51 | { nodeType: 'NT_ADD', 52 | node1: 53 | { nodeType: 'NT_POW', 54 | node1: 55 | { nodeType: 'NT_ADD', 56 | node1: 57 | { nodeType: 'NT_MULIPLY', 58 | node1: 59 | { nodeType: 'NT_ADD', 60 | node1: { nodeType: 'NT_NUMBER', value: 1 }, 61 | node2: { nodeType: 'NT_NUMBER', value: 2 } }, 62 | node2: { nodeType: 'NT_NUMBER', value: 3 } }, 63 | node2: 64 | { nodeType: 'NT_DIVIDE', 65 | node1: { nodeType: 'NT_NUMBER', value: 4 }, 66 | node2: { nodeType: 'NT_NUMBER', value: 2 } } }, 67 | node2: { nodeType: 'NT_NUMBER', value: 2 } }, 68 | node2: { nodeType: 'NT_NUMBER', value: 7 } }, 69 | node2: { nodeType: 'NT_NUMBER', value: 2 } }, 70 | node2: { nodeType: 'NT_NUMBER', value: 3 } }, 71 | node2: { nodeType: 'NT_NUMBER', value: 2.718281828459045 } }, 72 | node2: { nodeType: 'NT_NUMBER', value: 3.141592653589793 } } 73 | 74 | Order Of Evaluation => (((((((((1 + 2) * 3) + (4 / 2)) ** 2) + 7) LB 2) + 3) * 2.718281828459045) * 3.141592653589793) 75 | 76 | Evaluated Result 77 | 85.39734222673566 78 | ``` 79 | -------------------------------------------------------------------------------- /constants/nodeType.js: -------------------------------------------------------------------------------- 1 | const NODE = { 2 | TYPE: { 3 | NUMBER: "NT_NUMBER", 4 | 5 | PLUS: "NT_PLUS", 6 | MINUS: "NT_MINUS", 7 | 8 | ADD: "NT_ADD", 9 | SUB: "NT_SUB", 10 | MULTIPLY: "NT_MULIPLY", 11 | DIVIDE: "NT_DIVIDE", 12 | 13 | MOD: "NT_MOD", 14 | INT_DIVIDE: "NT_INT_DIVIDE", 15 | POW: "NT_POW", 16 | 17 | BITWISE_AND: "NT_BITWISE_AND", 18 | BITWISE_OR: "NT_BITWISE_OR", 19 | 20 | LOGNBASEX: "NT_LOG_BASE", 21 | NAT_LOG: "NT_NAT_LOG", 22 | NTH_ROOT: "NT_NTH_ROOT", 23 | 24 | BINARY_STRING: "NT_BINARY_STRING", 25 | BINARY: "NT_BINARY", 26 | 27 | HEXADECIMAL_STRING: "NT_HEXADECIMAL_STRING", 28 | HEXADECIMAL: "NT_HEXADECIMAL", 29 | 30 | OCTAL_STRING: "NT_OCTAL_STRING", 31 | OCTAL: "NT_OCTAL", 32 | }, 33 | }; 34 | 35 | module.exports = NODE; 36 | -------------------------------------------------------------------------------- /constants/tokenType.js: -------------------------------------------------------------------------------- 1 | const TOKEN = { 2 | TYPE: { 3 | LPAREN: "TT_LPAREN", 4 | RPAREN: "TT_RPAREN", 5 | 6 | NUMBER: "TT_NUMBER", 7 | 8 | PLUS: "TT_PLUS", 9 | MINUS: "TT_MINUS", 10 | MULTIPLY: "TT_MULTIPLY", 11 | DIVIDE: "TT_DIVIDE", 12 | INT_DIVIDE: "TT_INT_DIVIDE", 13 | MOD: "TT_MOD", 14 | 15 | POW: "TT_POW", 16 | NTH_ROOT: "TT_NTH_ROOT", 17 | 18 | LOGNBASEX: "TT_LOG_BASE", 19 | NAT_LOG: "TT_NAT_LOG", 20 | 21 | BITWISE_AND: "TT_BITWISE_AND", 22 | BITWISE_OR: "TT_BITWISE_OR", 23 | 24 | BINARY_STRING: "TT_BINARY_STRING", 25 | HEXADECIMAL_STRING: "TT_HEXADECIMAL_STRING", 26 | OCTAL_STRING: "TT_OCTAL_STRING", 27 | 28 | BINARY: "TT_BINARY", 29 | HEXADECIMAL: "TT_HEXADECIMAL", 30 | OCTAL: "TT_OCTAL", 31 | }, 32 | 33 | OPERATOR: { 34 | LPAREN: "(", 35 | RPAREN: ")", 36 | 37 | PLUS: "+", 38 | MINUS: "-", 39 | MULTIPLY: "*", 40 | DIVIDE: "/", 41 | INT_DIVIDE: "//", 42 | MOD: "%", 43 | 44 | POW: "**", 45 | NTH_ROOT: "#", 46 | 47 | LOGNBASEX: "LB", 48 | NAT_LOG: "LN", 49 | 50 | BITWISE_AND: "&", 51 | BITWISE_OR: "|", 52 | 53 | BINARY_STRING: "b", 54 | HEXADECIMAL_STRING: "h", 55 | OCTAL_STRING: "o", 56 | 57 | BINARY: "B", 58 | HEXADECIMAL: "H", 59 | OCTAL: "O", 60 | 61 | E: "e", 62 | PI: "pi", 63 | }, 64 | }; 65 | 66 | module.exports = TOKEN; 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const util = require("util"); 2 | 3 | const Lexer = require("./Lexer/lexer"); 4 | const Parser = require("./Parser/parser"); 5 | const Evaluate = require("./Evaluator/evaluate"); 6 | const Order = require("./Evaluator/order"); 7 | 8 | const text = "((((((1 + 2) * 3 + (4 / 2)) ** 2) + 7) LB 2) + 3) * e * pi"; 9 | // const text = "O(b100 + b100)"; 10 | 11 | const lexer = new Lexer(text); 12 | const tokens = lexer.generateTokens(); 13 | 14 | const parser = new Parser(tokens); 15 | const tree = parser.parse(); 16 | 17 | console.log("Expression\n" + text + "\n"); 18 | console.log("Tokens"); 19 | console.log(tokens); 20 | console.log( 21 | "\nTree\n" + util.inspect(tree, { showHidden: false, depth: null }) 22 | ); 23 | console.log("\nOrder Of Evaluation => " + Order(tree)); 24 | console.log("\nEvaluated Result\n" + Evaluate(tree)); 25 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-js-interpreter", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-js-interpreter", 3 | "version": "1.0.0", 4 | "description": "A Simple JS Interpreter", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "math": "node Mathjax/mathjax.js", 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "format": "prettier --write \"./**/*.(j|t)s\"" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/dipeshpatil/simple-js-interpreter.git" 15 | }, 16 | "author": "Dipesh Patil", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/dipeshpatil/simple-js-interpreter/issues" 20 | }, 21 | "homepage": "https://github.com/dipeshpatil/simple-js-interpreter#readme" 22 | } 23 | --------------------------------------------------------------------------------