infix-postfix
Advanced tools
Comparing version 2.0.1 to 3.0.0
@@ -1,11 +0,16 @@ | ||
import "./stringUtils"; | ||
import "./arrayUtils"; | ||
declare const _default: (expression: string, showSteps?: boolean) => string; | ||
/** | ||
* Converts infix expressions to postfix (rpn) expressions. | ||
* @param expression the infix expression | ||
* @param showSteps whether to print debug information to the console (default is false) | ||
* @returns the postfix expression | ||
*/ | ||
declare class InfixToPostfix { | ||
private expression; | ||
private queue; | ||
private current; | ||
private postfixExpression; | ||
toString(): string; | ||
private toObjectArray; | ||
toArray(): (string | number)[]; | ||
constructor(expression: string); | ||
private appendCurrent; | ||
private parseToQueue; | ||
private parseQueue; | ||
} | ||
declare const _default: (expression: string) => InfixToPostfix; | ||
export default _default; | ||
//# sourceMappingURL=InfixToPostfix.d.ts.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,204 +25,188 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Operand_1 = __importDefault(require("./Operand")); | ||
require("./stringUtils"); | ||
require("./arrayUtils"); | ||
function getPresendence(op) { | ||
switch (op) { | ||
case "(": | ||
case ")": | ||
return 5; | ||
case "plus_exp": | ||
case "neg_exp": | ||
return 4; | ||
case "^": | ||
return 3; | ||
case "plus": | ||
case "−": | ||
return 2; | ||
case "*": | ||
case "/": | ||
return 1; | ||
case "-": | ||
case "+": | ||
return 0; | ||
const Expression_1 = __importDefault(require("./Expression")); | ||
const Operand_1 = __importStar(require("./Operand")); | ||
const CloseMultiplication_1 = __importDefault(require("./operators/CloseMultiplication")); | ||
const CloseNegation_1 = __importDefault(require("./operators/CloseNegation")); | ||
const ClosePlus_1 = __importDefault(require("./operators/ClosePlus")); | ||
const ClosingBracket_1 = __importDefault(require("./operators/ClosingBracket")); | ||
const Exponentiation_1 = __importDefault(require("./operators/Exponentiation")); | ||
const Multiplication_1 = __importDefault(require("./operators/Multiplication")); | ||
const Negation_1 = __importDefault(require("./operators/Negation")); | ||
const OpeningBracket_1 = __importDefault(require("./operators/OpeningBracket")); | ||
const Operator_1 = __importDefault(require("./operators/Operator")); | ||
const OperatorUtils_1 = __importDefault(require("./operators/OperatorUtils")); | ||
const Plus_1 = __importDefault(require("./operators/Plus")); | ||
const Stack_1 = __importDefault(require("./Stack")); | ||
class InfixToPostfix { | ||
constructor(expression) { | ||
this.queue = new Stack_1.default(); | ||
this.current = null; | ||
this.postfixExpression = new Stack_1.default(); | ||
this.expression = new Expression_1.default(expression); | ||
this.parseToQueue(); | ||
this.parseQueue(); | ||
} | ||
} | ||
function isOperator(char) { | ||
return char === "+" || char === "-" || char === "*" || char === "/" || char === "^" || char === "(" || char === ")"; | ||
} | ||
function infixToPostfixOperator(op) { | ||
switch (op) { | ||
case "(": | ||
case ")": | ||
return op; | ||
case "^": | ||
return "^"; | ||
case "*": | ||
return "*"; | ||
case "/": | ||
return "/"; | ||
case "+": | ||
return "+"; | ||
case "-": | ||
return "-"; | ||
toString() { | ||
return this.toObjectArray().join(" "); | ||
} | ||
} | ||
function invert(op) { | ||
switch (op) { | ||
case "(": | ||
return ")"; | ||
case ")": | ||
return "("; | ||
case "^": | ||
return ")"; | ||
case "*": | ||
return "/"; | ||
case "/": | ||
return "*"; | ||
case "+": | ||
return "-"; | ||
case "-": | ||
return "+"; | ||
case "−": | ||
return "plus"; | ||
case "plus": | ||
return "−"; | ||
case "neg_exp": | ||
return "plus_exp"; | ||
case "plus_exp": | ||
return "neg_exp"; | ||
toObjectArray() { | ||
return this.postfixExpression.toArray(); | ||
} | ||
} | ||
/** | ||
* Converts infix expressions to postfix (rpn) expressions. | ||
* @param expression the infix expression | ||
* @param showSteps whether to print debug information to the console (default is false) | ||
* @returns the postfix expression | ||
*/ | ||
exports.default = (expression, showSteps = false) => { | ||
const expressionRaw = expression; | ||
let result = ""; | ||
const stack = []; | ||
let charBefore = null; | ||
let operand = null; | ||
if (showSteps) { | ||
console.log("#####################################"); | ||
console.log("Starting..."); | ||
toArray() { | ||
const objectArray = this.toObjectArray(); | ||
const array = []; | ||
for (const item of objectArray) { | ||
if (item instanceof Operator_1.default) { | ||
array.push(item.symbol); | ||
} | ||
else { | ||
if (item.type === Operand_1.OperandType.Number) | ||
array.push(Number(item.value)); | ||
else | ||
array.push(item.value); | ||
} | ||
} | ||
return array; | ||
} | ||
for (let i = 0; i < expression.length; i++) { | ||
const char = expression[i]; | ||
if (showSteps) { | ||
console.log(`Result: '${result}'`); | ||
console.log("Stack: ", stack); | ||
console.log("Operand: '" + (operand ? operand.getValue() : "") + "'"); | ||
console.log("#####################################"); | ||
console.log(`Input: '${char}'`); | ||
appendCurrent() { | ||
const last = this.queue.getLast(); | ||
// (a+b)(d-e) => (a+b)*(d-e), a(2) => a*(2) | ||
if (this.current instanceof OpeningBracket_1.default && | ||
(last instanceof Operand_1.default || last instanceof ClosingBracket_1.default)) { | ||
this.queue.push(new Multiplication_1.default()); | ||
} | ||
if (i > 0) | ||
charBefore = expression[i - 1]; | ||
if (isOperator(char)) { | ||
const currentOp = char; | ||
let postOp = infixToPostfixOperator(currentOp); | ||
if (operand) { | ||
result += operand.getValue() + " "; | ||
operand = null; | ||
} | ||
let afterOperator = true; | ||
if (charBefore) | ||
afterOperator = isOperator(charBefore); | ||
if (afterOperator) { | ||
if (currentOp === "-" && charBefore !== ")") { | ||
if (charBefore === "-") { | ||
expression = expression.splice(i - 1, 2, "+"); | ||
stack[stack.length - 1] = invert(stack.top()); | ||
i -= 1; | ||
continue; | ||
// (3+4)2 = (3+4)*2 | ||
if (last instanceof ClosingBracket_1.default && this.current instanceof Operand_1.default) { | ||
this.queue.push(new Multiplication_1.default()); | ||
} | ||
// 2 a => 2*a | ||
if (this.current instanceof Operand_1.default && last instanceof Operand_1.default) { | ||
this.queue.push(new Multiplication_1.default()); | ||
} | ||
// 2^-2/4 => 2^(-2)/4 | ||
if (last instanceof Exponentiation_1.default) { | ||
if (this.current instanceof Negation_1.default) | ||
this.current = new CloseNegation_1.default(); | ||
if (this.current instanceof Plus_1.default) | ||
this.current = new ClosePlus_1.default(); | ||
} | ||
this.queue.push(this.current); | ||
this.current = null; | ||
} | ||
parseToQueue() { | ||
while (this.expression.hasNext()) { | ||
const currentCharacter = this.expression.next(); | ||
if (OperatorUtils_1.default.isOperatorSymbol(currentCharacter)) { | ||
const operatorChar = currentCharacter; | ||
if (this.current instanceof Operand_1.default) { | ||
this.appendCurrent(); | ||
//this.current = null; | ||
} | ||
else if (this.current instanceof Operator_1.default) { | ||
const merged = this.current.merge(operatorChar); | ||
if (merged) { | ||
this.current = merged; | ||
} | ||
else if (charBefore === "+") { | ||
expression = expression.splice(i - 1, 1); | ||
stack[stack.length - 1] = invert(stack.top()); | ||
i -= 1; | ||
continue; | ||
} | ||
else if (charBefore === "^") { | ||
postOp = "neg_exp"; | ||
} | ||
else { | ||
postOp = "−"; | ||
this.appendCurrent(); | ||
//this.current = null; | ||
} | ||
} | ||
else if (currentOp === "+" && charBefore !== ")") { | ||
expression = expression.splice(i, 1); | ||
i -= 1; | ||
continue; | ||
if (this.current === null) { | ||
const last = this.queue.getLast(); | ||
const preferUnary = !last || (last instanceof Operator_1.default && !(last instanceof ClosingBracket_1.default)); | ||
this.current = OperatorUtils_1.default.parse(operatorChar, preferUnary); | ||
} | ||
else if (currentOp !== "(" && charBefore !== ")") { | ||
throw new Error(`Invalid infix expression '${expressionRaw}'`); | ||
} | ||
else if (currentCharacter.match(/[^\s]/)) { | ||
if (this.current instanceof Operator_1.default) { | ||
this.appendCurrent(); | ||
} | ||
if (this.current === null) { | ||
this.current = new Operand_1.default(currentCharacter); | ||
} | ||
else { | ||
const merged = this.current.append(currentCharacter); | ||
if (!merged) { | ||
// 2a => 2&a | ||
this.appendCurrent(); | ||
this.queue.push(new CloseMultiplication_1.default()); | ||
this.current = new Operand_1.default(currentCharacter); | ||
} | ||
} | ||
} | ||
const currentPres = getPresendence(postOp); | ||
for (let j = stack.length - 1; j >= 0; j--) { | ||
const op = stack[j]; | ||
if (op === "plus") | ||
else if (currentCharacter.match(/\s/)) { | ||
if (this.current === null || this.current instanceof Operator_1.default) | ||
continue; | ||
else if (postOp === ")") { | ||
if (op === "(") { | ||
stack.pop(); | ||
break; | ||
} | ||
else | ||
result += stack.pop() + " "; | ||
// b a !== ba | ||
if (this.current instanceof Operand_1.default) { | ||
this.appendCurrent(); | ||
} | ||
else { | ||
if (op === "(") | ||
break; | ||
else if (getPresendence(op) >= currentPres) { | ||
result += stack.pop() + " "; | ||
} | ||
} | ||
if (this.current) | ||
this.appendCurrent(); | ||
} | ||
parseQueue() { | ||
const queueArray = this.queue.toArray(); | ||
const operatorStack = new Stack_1.default(); | ||
for (const item of queueArray) { | ||
if (item instanceof Operator_1.default) { | ||
while (operatorStack.hasItem()) { | ||
const op = operatorStack.getLast(); | ||
if (item instanceof ClosingBracket_1.default) { | ||
// If the current operator is ')' resolve all operators until a '(' operator is reached | ||
if (!(op instanceof OpeningBracket_1.default)) { | ||
// Skip plus operators (they can get ignored for mathematical reasons) | ||
if (op instanceof Plus_1.default) | ||
operatorStack.pop(); | ||
// Append other operators | ||
else | ||
this.postfixExpression.push(operatorStack.pop()); | ||
} | ||
else { | ||
// Pop '(' but don't append it | ||
operatorStack.pop(); | ||
break; | ||
} | ||
} | ||
else { | ||
break; | ||
// If the current operator is a common one resolve all operators | ||
// until a '(' is reached or an operator has a smaller presendence | ||
if (!(op instanceof OpeningBracket_1.default) && op.presendence >= item.presendence) { | ||
// Skip plus operators (they can get ignored for mathematical reasons) | ||
if (op instanceof Plus_1.default) | ||
operatorStack.pop(); | ||
// Append other operators | ||
else | ||
this.postfixExpression.push(operatorStack.pop()); | ||
} | ||
else { | ||
// Stop resolving if the operator has a smaller presendence or if it is a '(' | ||
break; | ||
} | ||
} | ||
} | ||
if (!(item instanceof ClosingBracket_1.default)) | ||
operatorStack.push(item); | ||
} | ||
if (postOp !== ")") | ||
stack.push(postOp); | ||
else { | ||
this.postfixExpression.push(item); | ||
} | ||
} | ||
else if (char.match(/[^\s]/)) { | ||
if (!operand) { | ||
operand = new Operand_1.default(char); | ||
// Resolve remained operators from their stack | ||
while (operatorStack.hasItem()) { | ||
const op = operatorStack.getLast(); | ||
if (op instanceof Plus_1.default || op instanceof OpeningBracket_1.default || op instanceof ClosingBracket_1.default) { | ||
operatorStack.pop(); | ||
} | ||
else { | ||
const success = operand.append(char); | ||
if (!success) { | ||
result += operand.getValue() + " "; | ||
operand = new Operand_1.default(char); | ||
stack.push("*"); | ||
} | ||
this.postfixExpression.push(operatorStack.pop()); | ||
} | ||
} | ||
else if (char.match(/\s/)) { | ||
if (operand === null) | ||
continue; | ||
result += operand.getValue() + " "; | ||
operand = null; | ||
stack.push("*"); | ||
} | ||
} | ||
if (operand) | ||
result += operand.getValue() + " "; | ||
for (let j = stack.length - 1; j >= 0; j--) { | ||
const op = stack[j]; | ||
if (op === "plus" || op === "(" || op === ")") { | ||
stack.pop(); | ||
} | ||
else { | ||
result += stack.pop() + " "; | ||
} | ||
} | ||
console.log(`Result: '${result}'`); | ||
console.log("Stack:", stack); | ||
console.log("Operand: '" + (operand ? operand.getValue() : "") + "'"); | ||
console.log("#####################################"); | ||
return result.replace(/neg_exp/g, "−").replace(/plus_exp/g, "").trim(); | ||
} | ||
exports.default = (expression) => { | ||
return new InfixToPostfix(expression); | ||
}; | ||
//# sourceMappingURL=InfixToPostfix.js.map |
@@ -0,1 +1,3 @@ | ||
/// <reference types="node" /> | ||
import { inspect } from "util"; | ||
export declare enum OperandType { | ||
@@ -6,3 +8,3 @@ Number = 0, | ||
export default class Operand { | ||
private value; | ||
value: string; | ||
type: OperandType; | ||
@@ -13,5 +15,6 @@ private dotCount; | ||
isEmpty(): boolean; | ||
getValue(): string; | ||
static parseOperandType(value: string): OperandType; | ||
[inspect.custom](depth?: any, options?: any): string; | ||
toString(): string; | ||
} | ||
//# sourceMappingURL=Operand.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.OperandType = void 0; | ||
const util_1 = require("util"); | ||
var OperandType; | ||
@@ -33,5 +34,2 @@ (function (OperandType) { | ||
} | ||
getValue() { | ||
return this.value; | ||
} | ||
static parseOperandType(value) { | ||
@@ -43,4 +41,12 @@ if (value.match(/[0-9.]/)) | ||
} | ||
[util_1.inspect.custom](depth, options) { | ||
// console.log(options.stylize.toString()) | ||
// console.log(inspect.styles) | ||
return options.stylize(`${this.value}`, "special"); | ||
} | ||
toString() { | ||
return this.value.toString(); | ||
} | ||
} | ||
exports.default = Operand; | ||
//# sourceMappingURL=Operand.js.map |
@@ -6,4 +6,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const index_1 = __importDefault(require("./index")); | ||
console.log(index_1.default(" 12 N m")); | ||
const InfixToPostfix_1 = __importDefault(require("./InfixToPostfix")); | ||
console.log(InfixToPostfix_1.default("a(3)2").toString()); | ||
//# sourceMappingURL=test.js.map |
{ | ||
"name": "infix-postfix", | ||
"version": "2.0.1", | ||
"version": "3.0.0", | ||
"description": "infix to postfix converter", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -20,9 +20,26 @@ # infix-postfix | ||
#### Converting | ||
##### to a string | ||
```javascript | ||
console.log(infixToPostfix("(a+2b)^-2")); | ||
console.log(infixToPostfix("(a+2b)^-2").toString()); | ||
/* Output: | ||
a 2 b * + 2 − ^ | ||
*/ | ||
``` | ||
##### to an array | ||
```javascript | ||
console.log(infixToPostfix("(a+2b)^-2").toArray()); | ||
/* Output: | ||
[ | ||
'a', 2, 'b', '*', | ||
'+', 2, '−', '^' | ||
] | ||
*/ | ||
``` | ||
## Examples | ||
`2+3*4` ▶ `2 3 4 * +`<br> | ||
`a b(3+4)2` ▶ `a b * 3 4 + * 2 *`<br> | ||
`-12-3` ▶ `12 − 3 -`<br> | ||
@@ -43,10 +60,10 @@ `12^-2` ▶ `12 2 − ^`<br> | ||
#### Number parsing support | ||
Numbers get parsed. Obviously only works with the `toArray()` method. | ||
#### Variable support | ||
Any variable not containing operator characters (`+`, `-`, ...) is supported. For example `a-6` gets parsed to `a 6 -`. | ||
#### Variable multiplication shortcut support | ||
#### Multiplication shortcut support | ||
A common parser would parse `b/10b` to `b 10b /` - which is unfortunately wrong. This parser recognizes that `10` and `b` are different operands. It parses the string to `b 10 b * /`. | ||
Added to that `a b` gets parsed to `a b *`. | ||
#### Behind the scenes | ||
To understand how this converter works set the function's second argument to true: `infixToPostfix("2*2", true);`. This will print every step in detail to the console. | ||
Added to that `a b` gets parsed to `a b *`. Furthermore `a(3)2` is translated to `a 3 * 2 *`. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
55174
82
812
68
1