diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..d1ab59b --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1 @@ +language: "pt-BR" diff --git a/README.MD b/README.MD index fcfb2c1..5ab97f2 100644 --- a/README.MD +++ b/README.MD @@ -1 +1,4 @@ fegwegwefgweq + + +TESTANDO O CODE RABBIT diff --git a/main.ts b/main.ts index 5c2c672..05b326d 100644 --- a/main.ts +++ b/main.ts @@ -1,132 +1,193 @@ -function solve(firstNumber: number, secondNumber: number, operator: string): number { - const operations: { [index: string]: Function } = { - "+": () => firstNumber + secondNumber, - "-": () => firstNumber - secondNumber, - "*": () => firstNumber * secondNumber, - "/": () => firstNumber / secondNumber - }; +type Operation = { + priority: number, + solve: (first: number, second: number) => number +}; - return operations[operator](); +interface ExpressionParserInterface { + parse(expression: string): Array } +class ExpressionParser implements ExpressionParserInterface { + #operators: Set = new Set(["+", "-", "*", "/"]); -function solveSimplifiedExpr(output: Array, operators: Array): void { - const secondNumber = Number(output.pop()); - const firstNumber = Number(output.pop()); + #parenthesesStack: Array; + #expression: Array; - const operator = operators.pop(); + #lastToken: string | null; + #hasDecimal: boolean; + #number: string; - if (operator == null) - throw "Operation Not Supported."; + #isDigit(token: string) { + return token.charCodeAt(0) >= "0".charCodeAt(0) + && token.charCodeAt(0) <= "9".charCodeAt(0); + } + #isNegativeSignal(token: string) { + return !this.#isValidNumber() + && token === "-" + && (this.#lastToken == null + || this.#lastToken == "(" + || this.#isOperator(this.#lastToken)); + } - output.push(solve(firstNumber, secondNumber, operator).toString()); -} + #isOperator(token: string) { + return this.#operators.has(token); + } -function isDigit(character: string) { - return character.charCodeAt(0) >= "0".charCodeAt(0) - && character.charCodeAt(0) <= "9".charCodeAt(0); -} + #isValidNumber() { + return this.#number.length > 0; + } + #addNumberToExpression() { + this.#expression.push(this.#number); + this.#hasDecimal = false; + this.#number = ""; + } -function parseExpression(expression: string) { - const operators: Array = ["+", "-", "*", "/"]; - const expr: Array = []; - const parenthesesStack: Array = []; - - let lastToken: string | null = null; - let number = ""; - let hasDecimal = false; - - for (const token of expression.replaceAll(" ", "")) { - if (isDigit(token)) { - number += token; - } else if (token === ".") { - if (hasDecimal || !number.length) - throw "Número decimal mal formado."; - number += token; - hasDecimal = true; - } else if (token === "(") { - parenthesesStack.push(token); - expr.push(token); - hasDecimal = false; - } else if (token === ")") { - if (!parenthesesStack.length) - throw "Parêntese fechado sem abertura correspondente."; - parenthesesStack.pop(); - - if (number.length) { - expr.push(number); - number = ""; - hasDecimal = false; - } + #addDecimalPoint() { + if (this.#hasDecimal || !this.#isValidNumber()) + throw "Número decimal mal formado."; - expr.push(token); - } else if (operators.indexOf(token) >= 0) { - if (!number.length - && token === "-" - && (lastToken == null - || operators.indexOf(lastToken) >= 0 - || lastToken == "(")) { - number += token; - } else { - if (!number.length) - throw `Operador '${token}' mal posicionado na expressão.`; - expr.push(number); - number = ""; - hasDecimal = false; - expr.push(token); - } - } else throw `Caractere inválido na expressão: '${token}'`; + this.#hasDecimal = true; + this.#number += "."; + } + + #openParentheses() { + this.#parenthesesStack.push("("); + this.#expression.push("("); + this.#hasDecimal = false; + } - lastToken = token; + #closeParentheses() { + if (!this.#parenthesesStack.length) + throw "Parêntese fechado sem abertura correspondente."; + this.#parenthesesStack.pop(); + + if (this.#isValidNumber()) + this.#addNumberToExpression(); + + this.#expression.push(")"); } - if (!number.length && ( - lastToken == null - || operators.indexOf(lastToken) >= 0)) - throw `Operador '${lastToken}' mal posicionado na expressão.`; + #handleOperator(operator: string) { + if (this.#isNegativeSignal(operator)) { + this.#number += operator; + } else { + if (!this.#isValidNumber()) + throw `Operador '${operator}' mal posicionado na expressão.`; + this.#addNumberToExpression(); + this.#expression.push(operator); + } + } + + parse(expression: string): Array { + this.#parenthesesStack = []; + this.#expression = []; + + this.#hasDecimal = false; + this.#lastToken = null; + this.#number = ""; + + for (const token of expression.replaceAll(" ", "")) { + if (this.#isDigit(token)) { + this.#number += token; + } else if (token === ".") { + this.#addDecimalPoint(); + } else if (token === "(") { + this.#openParentheses(); + } else if (token === ")") { + this.#closeParentheses(); + } else if (this.#isOperator(token)) { + this.#handleOperator(token); + } else throw `Caractere inválido na expressão: '${token}'`; + + this.#lastToken = token; + } + + if (!this.#isValidNumber() && ( + this.#lastToken == null + || this.#isOperator(this.#lastToken))) + throw `Operador '${this.#lastToken}' mal posicionado na expressão.`; - if (parenthesesStack.length) - throw "Parêntese aberto sem fechamento correspondente."; + if (this.#isValidNumber()) + this.#addNumberToExpression(); - if (number.length) - expr.push(number); + if (this.#parenthesesStack.length) + throw "Parêntese aberto sem fechamento correspondente."; - return expr; + + return this.#expression; + } } -function solveExpression(expression: string) { - const priorities: { [index: string]: number } = { - "+": 1, - "-": 1, - "*": 2, - "/": 2 +class ExpressionSolver { + #operations: { [index: string]: Operation } = { + "+": { priority: 1, solve: (first: number, second: number) => first + second }, + "-": { priority: 1, solve: (first: number, second: number) => first - second }, + "*": { priority: 2, solve: (first: number, second: number) => first * second }, + "/": { priority: 2, solve: (first: number, second: number) => first / second } }; + #parser: ExpressionParserInterface; + #operators: Array; + #output: Array; - const operators: Array = []; - const output: Array = []; - - for (const token of parseExpression(expression)) { - if (token === "(") { - operators.push(token); - } else if (token === ")") { - while (operators[operators.length - 1] !== "(") - solveSimplifiedExpr(output, operators); - operators.pop(); - } else if (priorities.hasOwnProperty(token)) { - while (operators.length - && operators[operators.length - 1] != "(" - && priorities[operators[operators.length - 1]] >= priorities[token]) - solveSimplifiedExpr(output, operators); - operators.push(token); - } else { - output.push(token); - } + constructor(expressionParse: ExpressionParserInterface) { + this.#parser = expressionParse; } - while (operators.length) - solveSimplifiedExpr(output, operators); + #solve() { + const secondNumber = Number(this.#output.pop()); + const firstNumber = Number(this.#output.pop()); + const operator = this.#operators.pop(); + + if (operator == null || !this.#operations.hasOwnProperty(operator)) + throw "Operation Not Supported."; + + const result = this.#operations[operator].solve(firstNumber, secondNumber); + this.#output.push(result.toString()); + } + + #lastOperator() { + return this.#operators[this.#operators.length - 1]; + } + + #isOperator(token: string) { + return this.#operations.hasOwnProperty(token); + } - return Number(output.pop()); + + solve(expression: string): number { + this.#operators = []; + this.#output = []; + + for (const token of this.#parser.parse(expression)) { + if (token === "(") { + this.#operators.push(token); + } else if (token === ")") { + while (this.#lastOperator() !== "(") + this.#solve(); + this.#operators.pop(); + } else if (this.#isOperator(token)) { + while (this.#operators.length + && this.#lastOperator() != "(" + && this.#operations[this.#lastOperator()].priority >= this.#operations[token].priority) + this.#solve(); + this.#operators.push(token); + } else { + this.#output.push(token); + } + } + + while (this.#operators.length) + this.#solve(); + + return Number(this.#output.pop()); + } } + + +const handler = new ExpressionParser(); +const solver = new ExpressionSolver(handler); + +// console.log(solver.solve("1 + 2 * 3")) +console.log(solver.solve("1 + 2 * 3"))