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
10 changes: 10 additions & 0 deletions src/7/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ const str = '1 + 1';
const value = new Calculator(str).calculate(); // value = 2
```

You can also use the command line tool as follows:

```bash
# Using node
node /path/to/calculator.index.js '1 + 1'

# Using ts-node
npx ts-node calculator.index.ts '1 + 1'
```

## Run tests

To run the tests for the Calculator tool, go to the root directory of this repository and run the following command:
Expand Down
25 changes: 25 additions & 0 deletions src/7/calculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,28 @@ import { Postfix } from './postfix';
import { OperatorTokens } from './tokens';

export class Calculator {
/**
* The input provided by the user
*
* @type {string}
*/
text: string;

/**
* The Postfix class instance
*
* @private
* @type {Postfix}
*/
private postfix: Postfix;

/**
* Stack used after infix to postfix conversion.
* Used for calculating the value of the expression.
*
* @private
* @type {Stack<number>}
*/
private stack: Stack<number>;

constructor(text: string) {
Expand All @@ -14,11 +34,14 @@ export class Calculator {
}

public calculate(): number {
// First convert the infix notation to postfix notation.
const queue = this.postfix.parse();

while (queue.size() > 0) {
// Get the next token from the queue.
const str = queue.dequeue()!;

// If the token is an operator, pop two values from the stack,
if (OperatorTokens.has(str)) {
const operator = str;

Expand Down Expand Up @@ -47,8 +70,10 @@ export class Calculator {
throw new Error(`Invalid operator ${str}`);
}

// Perform the operation and push the result to the stack.
this.stack.push(value);
} else {
// If the token is an operand, push it to the stack.
this.stack.push(parseFloat(str));
}
}
Expand Down
51 changes: 49 additions & 2 deletions src/7/postfix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,55 @@ import { Stack } from '../utils/stack';
import { OperatorTokens } from './tokens';

export class Postfix {
/**
* Input infix string
*
* @private
* @type {string}
*/
private text: string;

/**
* Position of the current token
*
* @private
* @type {number}
*/
private pos: number = 0;

/**
* Final output queue
*
* @private
* @type {Queue<string>}
*/
private outputQueue: Queue<string>;

/**
* Data structure to store the operators
*
* @private
* @type {Stack<string>}
*/
private operatorStack: Stack<string>;
private textLength: number;
private operatorCount = 0;
private numberCount = 0;

/**
* Number of operators encountered till `this.pos`
*
* @private
* @type {number}
*/
private operatorCount: number = 0;

/**
* Number of operands encountered till `this.pos`
* @date 9/1/2023 - 1:44:00 PM
*
* @private
* @type {number}
*/
private numberCount: number = 0;

constructor(text: string) {
this.text = text;
Expand Down Expand Up @@ -81,6 +123,7 @@ export class Postfix {
private parseRightParenthesis() {
this.consumeToken();

// Pop all operators from the stack until we encounter a left parenthesis.
while (this.operatorStack.peek() !== '(') {
if (this.operatorStack.size() === 0) {
throw new Error('Mismatched Parenthesis');
Expand All @@ -101,13 +144,15 @@ export class Postfix {
this.consumeToken();
const token1 = OperatorTokens.get(o1)!;

// While there is an operator token, o2, at the top of the stack
while (this.operatorStack.size() > 0) {
const o2 = this.operatorStack.peek()!;

if (o2 === '(') {
break;
}

// If o1 is left-associative and its precedence is less than or equal to that of o2,
const token2 = OperatorTokens.get(o2)!;
if (
token2.precedence > token1.precedence ||
Expand All @@ -126,6 +171,7 @@ export class Postfix {
private parseNumber(): string {
this.numberCount++;
let str = '';

while (this.pos < this.textLength) {
const token = this.getCurrentToken();
if (token === ' ' || token === ')') {
Expand All @@ -134,6 +180,7 @@ export class Postfix {
str += token;
this.consumeToken();
}

return str;
}

Expand Down
30 changes: 29 additions & 1 deletion src/7/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
/**
* Interface used for comparing operators.
* Used while converting infix to postfix notation.
*
* @interface IToken
*/
interface IToken {
/**
* The operator character
*
* @type {string}
*/
char: string;

/**
* Precedence of the operator
*
* @type {number}
*/
precedence: number;

/**
* Whether the operator is left associative or not
*
* @type {boolean}
*/
isLeftAssociative: boolean;
}

Expand All @@ -15,7 +38,12 @@ class Token implements IToken {
}
}

const OperatorTokens = new Map<string, Token>();
/**
* All the operators supported by the calculator.
*
* @type {Map<string, Token>}
*/
const OperatorTokens: Map<string, Token> = new Map<string, Token>();

OperatorTokens.set('^', new Token('^', 4, false));
OperatorTokens.set('*', new Token('*', 3, true));
Expand Down