Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
.vscode
node_modules
dist
.idea/
venv/
src/lesson2/my_version.js
47 changes: 0 additions & 47 deletions src/lesson2/engine.test.ts

This file was deleted.

42 changes: 0 additions & 42 deletions src/lesson2/engine.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/lesson2/helpers.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/lesson2/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createInterface } from "readline";

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import { runner } from "./runner";

const rl = createInterface({
Expand All @@ -21,6 +23,7 @@ const question = (): Promise<null> =>
});

async function app(): Promise<null> {
console.log("Starting app...");
while (true) {
await question();
}
Expand Down
26 changes: 19 additions & 7 deletions src/lesson2/mathOperators.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
import { mul, div, add, minus } from "./mathOperators";
import mathOperation from "./mathOperators";

describe("mathOperators test cases", () => {
it("mul 1 * 2 to equal 2", () => {
expect(mul(1, 2)).toBe(2);
expect(mathOperation("*", 1, 2)).toBe(2);
});

it("mul 2 * 2 to equal 4", () => {
expect(mul(2, 2)).toBe(4);
expect(mathOperation("*", 2, 2)).toBe(4);
});

it("div 2 / 2 to equal 1", () => {
expect(div(2, 2)).toBe(1);
expect(mathOperation("/", 2, 2)).toBe(1);
});

it("div 4 / 2 to equal 2", () => {
expect(div(4, 2)).toBe(2);
expect(mathOperation("/", 4, 2)).toBe(2);
});

it("add 4 + 2 to equal 6", () => {
expect(add(4, 2)).toBe(6);
expect(mathOperation("+", 4, 2)).toBe(6);
});

it("minus 4 - 2 to equal 2", () => {
expect(minus(4, 2)).toBe(2);
expect(mathOperation("-", 4, 2)).toBe(2);
});

it("factorial 5! to equal 120", () => {
expect(mathOperation("!", 5)).toBe(120);
});

it("exponentiation 2 ^ 5 to equal 32", () => {
expect(mathOperation("^", 2, 5)).toBe(32);
});

it("squaring 5** to equal 525", () => {
expect(mathOperation("**", 5)).toBe(25);
});
});
67 changes: 33 additions & 34 deletions src/lesson2/mathOperators.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
export type ScalarOperationType = (first: number, second: number) => number;

export const mul: ScalarOperationType = (
first: number,
second: number
): number => first * second;

export const div: ScalarOperationType = (
first: number,
second: number
): number => first / second;

export const add: ScalarOperationType = (
first: number,
second: number
): number => first + second;

export const minus: ScalarOperationType = (
first: number,
second: number
): number => first - second;

export const mathOperators: { [key: string]: ScalarOperationType } = {
"*": mul,
"/": div,
"+": add,
"-": minus,
export type UnaryOperationType = (number: number) => number;

const binaryOperations: {
[key: string]: ScalarOperationType;
} = {
"+": (a: number, b: number): number => a + b,
"-": (a: number, b: number): number => a - b,
"*": (a: number, b: number): number => a * b,
"/": (a: number, b: number): number => a / b,
"^": (a: number, b: number): number => Math.pow(a, b),
};

export const mathPriorities: number[] = [1, 2];

const [FIRST, SECOND] = mathPriorities;
const unaryOperations: {
[key: string]: UnaryOperationType;
} = {
"**": (a: number): number => binaryOperations["*"](a, a),
"!": (a: number): number => {
let f = 1;
for (let i = 1; i <= a; i++) {
f *= i;
}
return f;
},
};

export const mathOperatorsPriorities: { [key: string]: number } = {
"*": FIRST,
"/": FIRST,
"+": SECOND,
"-": SECOND,
const mathOperation = (operator: string, a: number, b?: number): number => {
if (b) {
return binaryOperations[operator](a, b);
} else {
console.log(operator);
console.log(a);
console.log(unaryOperations[operator]);
return unaryOperations[operator](a);
}
};

export default mathOperation;
27 changes: 0 additions & 27 deletions src/lesson2/parser.test.ts

This file was deleted.

101 changes: 81 additions & 20 deletions src/lesson2/parser.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,88 @@
import { isNumber } from "./helpers";
import { mathOperators } from "./mathOperators";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const _ = require("lodash");
import mathOperation from "./mathOperators";

export type ParsedLineType = (number | string)[];
const GROUP_PATTERN = /\([-+/*0-9\.]{3,}\)/g;
const DIGIT_PATTERN = /[0-9\.]+/g;
// const OPERATOR_PATTERN = /[-+/*]/g;
const ZERO_PRIORITY_PATTERN = /[0-9\.]+[\^][0-9\.]+/g;
const LOW_PRIORITY_PATTERN = /[0-9\.]+[+-][0-9\.]+/g;
const HIGH_PRIORITY_PATTERN = /[0-9\.]+[*/][0-9\.]+/g;
const UNARY_OPERATION = /[\d\.]+(\*{2,}|!)/g;

export const parser = (line: string): ParsedLineType | null => {
const stack = line.split(" ");
const priorityOperation: {
[key: number]: RegExp;
} = {
0: UNARY_OPERATION,
1: ZERO_PRIORITY_PATTERN,
2: HIGH_PRIORITY_PATTERN,
3: LOW_PRIORITY_PATTERN,
};

const customEval = (subStr: string): number => {
const res: RegExpMatchArray | null = subStr.match(DIGIT_PATTERN);
if (res) {
const operator: string = _.trimEnd(_.trimStart(subStr, res[0]), res[1]);
return mathOperation(operator, Number(res[0]), Number(res[1]));
}
throw new TypeError("Invalid expression");
};

return stack.reduce<ParsedLineType>((result, item, key) => {
const prevItem = stack[key - 1];
const calculateProcess = (pattern: RegExp, subExp: string): string => {
while (true) {
const res = subExp.match(pattern);
if (!res) break;
for (const s of res) {
const mathResult = customEval(s);
const startI = subExp.indexOf(s);
const endI = s.length;
subExp =
subExp.slice(0, startI) +
String(mathResult) +
subExp.slice(startI + endI);
}
}
return subExp;
};

const isValidNumberPush = !isNumber(prevItem) && isNumber(item);
const isValidOperatorPush =
isNumber(prevItem) &&
!isNumber(item) &&
mathOperators.hasOwnProperty(item);
const calculateHighPriority = (subExp: string): string => {
return calculateProcess(HIGH_PRIORITY_PATTERN, subExp);
};

if (isValidNumberPush) {
result.push(Number(item));
} else if (isValidOperatorPush) {
result.push(item);
} else {
throw new TypeError("Unexpected string");
const calculateLowPriority = (subExp: string): string => {
return calculateProcess(LOW_PRIORITY_PATTERN, subExp);
};

const calculateSubExp = (subExp: string): string => {
//return calculateLowPriority(calculateHighPriority(subExp));
for (const priority in priorityOperation) {
subExp = calculateProcess(priorityOperation[priority], subExp);
}
return subExp;
};

const calculate = (expression: string): number => {
// очистка выражения от пробелов
expression = expression.replace(/\s+/g, "");
// будем находить группы до тех пор пока они есть
// вычислять значение, и заменять в оригинальном выражении
while (true) {
// ищем все выражения в скобках
const groups = expression.match(GROUP_PATTERN);
// если ничего не нашли прекращаем
if (!groups) break;
for (const group of groups) {
const subExp = _.trim(group, "()");
const mathResult = calculateSubExp(subExp);
const startI = expression.indexOf(group);
const endI = group.length;
expression =
expression.slice(0, startI) +
String(mathResult) +
expression.slice(startI + endI);
}
return result;
}, []);
}
return Number(calculateSubExp(expression));
};

export default calculate;
Loading