Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: "pt-BR"
3 changes: 3 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
fegwegwefgweq


TESTANDO O CODE RABBIT
273 changes: 167 additions & 106 deletions main.ts
Original file line number Diff line number Diff line change
@@ -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<string>
}

class ExpressionParser implements ExpressionParserInterface {
#operators: Set<string> = new Set(["+", "-", "*", "/"]);

function solveSimplifiedExpr(output: Array<string>, operators: Array<string>): void {
const secondNumber = Number(output.pop());
const firstNumber = Number(output.pop());
#parenthesesStack: Array<string>;
#expression: Array<string>;

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<string> = ["+", "-", "*", "/"];
const expr: Array<string> = [];
const parenthesesStack: Array<string> = [];

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.";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prefer throwing Error objects instead of strings.

Using throw new Error(...) ensures stack traces are correctly captured and aligns with standard JavaScript/TypeScript practices.

-throw "Número decimal mal formado.";
+throw new Error("Número decimal mal formado.");

-throw "Parêntese fechado sem abertura correspondente.";
+throw new Error("Parêntese fechado sem abertura correspondente.");

-throw `Operador '${operator}' mal posicionado na expressão.`;
+throw new Error(`Operador '${operator}' mal posicionado na expressão.`);

-throw `Caractere inválido na expressão: '${token}'`;
+throw new Error(`Caractere inválido na expressão: '${token}'`);

-throw `Operador '${this.#lastToken}' mal posicionado na expressão.`;
+throw new Error(`Operador '${this.#lastToken}' mal posicionado na expressão.`);

-throw "Parêntese aberto sem fechamento correspondente.";
+throw new Error("Parêntese aberto sem fechamento correspondente.");

Also applies to: 63-63, 77-77, 102-102, 110-110, 116-116


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<string> {
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<string>;
#output: Array<string>;

const operators: Array<string> = [];
const output: Array<string> = [];

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))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Switch from using hasOwnProperty to Object.hasOwn().

Avoid calling hasOwnProperty on an instance; using Object.hasOwn() is safer and recommended. Also convert string throws to Error objects where applicable.

-if (operator == null || !this.#operations.hasOwnProperty(operator))
-    throw "Operation Not Supported.";
+if (operator == null || !Object.hasOwn(this.#operations, operator))
+    throw new Error("Operation Not Supported.");

...

-return this.#operations.hasOwnProperty(token);
+return Object.hasOwn(this.#operations, token);

Also applies to: 155-155

🧰 Tools
🪛 Biome (1.9.4)

[error] 143-143: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

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"))