Advertisement
Guest User

Untitled

a guest
Apr 22nd, 2019
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 'use strict';
  2.  
  3. const vars = {
  4.     'x': 0,
  5.     'y': 1,
  6.     'z': 2
  7. };
  8.  
  9. function OperationFactory(op, opName) {
  10.     const Operation = function (...args) {
  11.         this.args = args;
  12.     };
  13.     Operation.prototype.evaluate = function (...values) {
  14.         return op(...this.args.map((arg) => arg.evaluate(...values)));
  15.     };
  16.     Operation.prototype.toString = function () {
  17.         return this.args.join(" ") + " " + opName;
  18.     };
  19.     Operation.prototype.prefix = function () {
  20.         return "(" + opName + " " + this.args.map((curArg) => curArg.prefix()).join(" ") + ")";
  21.     };
  22.     return Operation;
  23. }
  24.  
  25. function OperandFactory(evalFunc) {
  26.     const Operand = function (value) {
  27.         this.value = value;
  28.     };
  29.     Operand.prototype.evaluate = evalFunc;
  30.  
  31.     Operand.prototype.toString = function () {
  32.         return this.value.toString();
  33.     };
  34.     Operand.prototype.prefix = Operand.prototype.toString;
  35.  
  36.     return Operand;
  37. }
  38.  
  39. const Const = new OperandFactory(function () {
  40.     return this.value;
  41. });
  42.  
  43. const Variable = new OperandFactory(function (...values) {
  44.     const ind = vars[this.value];
  45.     return values[ind];
  46. });
  47.  
  48. const Add = OperationFactory((x, y) => x + y, "+");
  49.  
  50. const Subtract = OperationFactory((x, y) => x - y, "-");
  51.  
  52. const Multiply = OperationFactory((x, y) => x * y, "*");
  53.  
  54. const Divide = OperationFactory((x, y) => x / y, "/");
  55.  
  56. const Negate = OperationFactory((x) => -x, "negate");
  57.  
  58. const Sum = OperationFactory((...values) => values.reduce((a, b) => a + b, 0), 'sum');
  59.  
  60. const Avg = OperationFactory((...values) => values.reduce((a, b) => a + b, 0) / values.length, 'avg');
  61.  
  62. function opsFill(name, op, argsCnt) {
  63.     ops[name] = {Op: op, numOfArgs: argsCnt};
  64. }
  65.  
  66. let ops = {};
  67.  
  68. opsFill('+', Add, 2);
  69. opsFill('*', Multiply, 2);
  70. opsFill('-', Subtract, 2);
  71. opsFill('/', Divide, 2);
  72. opsFill('sum', Sum, undefined);
  73. opsFill('avg', Avg, undefined);
  74. opsFill('negate', Negate, 1);
  75.  
  76. const ParsePrefixError = function (message, expression, index) {
  77.     this.name = "ParsingException";
  78.     if (arguments.length > 2) {
  79.         this.message = "Expected " + message + ', found ' + expression[index - 1]
  80.             + " at index " + index + '\n"' + expression + '"\n';
  81.     } else {
  82.         this.message = message + '\n"' + expression + '"\n';
  83.     }
  84. };
  85.  
  86. ParsePrefixError.prototype = Error.prototype;
  87.  
  88. const Tokenizer = function (string) {
  89.     this.index = 0;
  90.     this.prevToken = '';
  91.     this.curToken = '';
  92.  
  93.     const isWhitespace = function (c) {
  94.         return /\s/.test(c);
  95.     };
  96.  
  97.     this.checkPos = () => this.index < string.length;
  98.  
  99.     this.symbol = () => string[this.index];
  100.  
  101.     this.isBracket = () => this.symbol() === ')' || this.symbol() === '(';
  102.  
  103.     this.next = () => {
  104.         this.index++;
  105.     };
  106.  
  107.     this.nextToken = function () {
  108.         this.prevToken = this.curToken;
  109.         while (this.index < string.length && isWhitespace(string[this.index])) {
  110.             this.index++;
  111.         }
  112.         this.curToken = '';
  113.         if (this.isBracket()) {
  114.             this.curToken = this.symbol();
  115.             this.next();
  116.         } else {
  117.             while (this.checkPos() && !(this.isBracket() || isWhitespace(this.symbol()))) {
  118.                 this.curToken += this.symbol();
  119.                 this.next();
  120.             }
  121.         }
  122.     };
  123. };
  124.  
  125. const parseOperand = function (tokenizer, parseExpression, expression) {
  126.     if (tokenizer.curToken === '(') {
  127.         return parseExpression();
  128.     } else if (tokenizer.curToken in vars) {
  129.         tokenizer.nextToken();
  130.         return new Variable(tokenizer.prevToken)
  131.     } else if (tokenizer.curToken !== '' && !isNaN(tokenizer.curToken)) {
  132.         tokenizer.nextToken();
  133.         return new Const(parseInt(tokenizer.prevToken, 10));
  134.     } else {
  135.         throw new ParsePrefixError('operand', expression, tokenizer.index,);
  136.     }
  137. };
  138.  
  139. const parsePrefix = function (expression) {
  140.     const tokenizer = new Tokenizer(expression);
  141.  
  142.     if (expression === '') {
  143.         throw new ParsePrefixError('Expression is not defined', '');
  144.     }
  145.  
  146.     let parseExpression = function () {
  147.         if (tokenizer.curToken === '(') {
  148.             tokenizer.nextToken();
  149.             if (!(tokenizer.curToken in ops)) {
  150.                 throw new ParsePrefixError('operation', expression, tokenizer.index);
  151.             }
  152.             const opParams = ops[tokenizer.curToken];
  153.             tokenizer.nextToken();
  154.             let curArgs = [];
  155.             while (tokenizer.curToken !== ')') {
  156.                 curArgs.push(parseOperand(tokenizer, parseExpression, expression));
  157.                 if (tokenizer.curToken === '') {
  158.                     throw new ParsePrefixError('Missing closing parenthesis', expression);
  159.                 }
  160.             }
  161.             const argsCnt = opParams.numOfArgs;
  162.             const curArgsCnt = curArgs.length;
  163.             if (!isNaN(argsCnt) && argsCnt !== curArgsCnt) {
  164.                 throw new ParsePrefixError('Expected ' + argsCnt + ' operand(s), found ' + curArgsCnt, expression);
  165.             }
  166.             tokenizer.nextToken();
  167.             return new opParams.Op(...curArgs);
  168.         } else {
  169.             return parseOperand(tokenizer, parseExpression, expression);
  170.         }
  171.     };
  172.  
  173.     tokenizer.nextToken();
  174.     const result = parseExpression();
  175.     if (tokenizer.curToken !== '') {
  176.         throw new ParsePrefixError('end of expression', expression, tokenizer.index);
  177.     }
  178.     return result;
  179. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement