<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="main.css">
<script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>
<script src="common.js"></script>
<script src="infix.js"></script>
<script src="postfix.js"></script>
<script src="main.js"></script>
</head>
<body>
<h1>Infix to Postfix - Calculator</h1>
<div>
<div>
#a = <input type="text" value="1,2,3,4,5" id="variable_a" /><br/>
#b = <input type="text" value="300, 200" id="variable_b" /><br/>
<input type="text" value="5+2*$sum(1,2,3) + 2 + $sum($avg(#b), #a, 7)" id="expression" />
<button type="button" id="btnCalc">Calculate</button>
</div>
<div class="hide">
Postfix expression: <span id="spanPostfix"></span>
</div>
<div>
<h3>Result:</h3>
<span id="spanResult"></span>
</div>
<div>
<h3>Example:</h3>
<span id="example1"></span>
</div>
</div>
</body>
</html>
Infix to Postfix converter and Postfix calculator
See more about the project on my http://www.ivansivak.net/blog/simple-math-evaluator-infix-to-postfix-converter-and-calculator-reverse-polish-notation
var Common = (function(){
var tokenType = { operand: 1, operator: 2, leftPar: 3, rightPar: 4, variable: 5, func: 6 , not: 7, comparator: 8, comma: 9, wall:10},
operators = ['+', '-', '*', '/' ];
var determineToken = function (char) {
if (~operators.indexOf(char)) return tokenType.operator;
if (char[0] == '$') return tokenType.func;
if (char[0] == '#') return tokenType.variable;
if (char[0] == '|') return tokenType.wall;
if (char == '<' || char == '>' || char == '<=' || char == '>=') return tokenType.comparator;
switch (char) {
case '(':
return tokenType.leftPar;
case ')':
return tokenType.rightPar;
case '!':
return tokenType.not;
case ',':
return tokenType.comma;
default:
return tokenType.operand;
}
};
var trimSpaces = function (str) {
return str.replace(/\s+/g, '');
};
var isAlphaNumeric = function (str) {
return /^[a-z0-9]+$/i.test(str);
};
var tokenize = function (str) {
var num = '',
res = [],
leftParseStack = [];
for (var i = 0; i < str.length; i++){
console.log(str[i] + ':' + Common.determineToken(str[i]));
switch (Common.determineToken(str[i])) {
case Common.tokenType.func:
num += (str[i]);
while(isAlphaNumeric(str[i+1]) && i< str.length){
i++;
num += (str[i]);
}
if (str[i+1] == '(') {
i++;
num += (str[i]);
} else {
//invalid expression, missing ( for function
}
res.push(num);
leftParseStack.push(Common.tokenType.func);
num = '';
break;
case Common.tokenType.variable:
num += (str[i]);
while(isAlphaNumeric(str[i+1]) && i< str.length){
i++;
num += (str[i]);
}
res.push(num);
num = '';
break;
case Common.tokenType.comparator:
if ($.isNumeric(num)) res.push(parseFloat(num));
num = (str[i]);
if(str[i+1]=='='){
i++;
num += (str[i]);
}
res.push(num);
num = '';
break;
case Common.tokenType.operand:
num += $.trim(str[i]);
break;
case Common.tokenType.operator:
if ($.isNumeric(num)) res.push(parseFloat(num));
res.push(str[i]);
num = '';
break;
case Common.tokenType.comma:
if ($.isNumeric(num)) res.push(parseFloat(num));
num = '';
res.push(str[i]);
break;
case Common.tokenType.leftPar:
if ($.isNumeric(num)) res.push(parseFloat(num));
res.push(str[i]);
num = '';
leftParseStack.push(Common.tokenType.leftPar);
break;
case Common.tokenType.rightPar:
if (Common.tokenType.func == leftParseStack.pop()){
if ($.isNumeric(num)) res.push(parseFloat(num));
res.push('|');
num = '';
} else {
if ($.isNumeric(num)) res.push(parseFloat(num));
res.push(str[i]);
num = '';
}
break;
}
}
if (num != '') res.push(num);
return res;
}
return {
tokenType: tokenType,
determineToken: determineToken,
trimSpaces: trimSpaces,
tokenize: tokenize
}
})();
/**
* Examples:
* ============================================
* 2*3-4/5 = 23*45/-
* 5+3*2 = 5 3 2*+
* 10 + 3 * 5 / (16 - 4) = 10 3 5 * 16 4 - / +
* 2 * 5 + 3 - 2 * (8 / (4 / 2)) + (3 * 3) = 2 5 * 3 + 2 8 4 2 / / * - 3 3 * +
*/
var Infix = (function() {
var postfix,
stack;
var precedence = function(operator) {
switch (operator) {
case '*':
case '/':
return 4;
case '+':
case '-':
case '>':
case '<':
return 3;
case ',':
return 5;
case '(':
return 2;
case '!':
return 6;
default:
if (operator[0] == '$') {
return 2;
}
if (operator[0] == '#') {
//return 4;
}
}
};
var processOperand = function(char) {
postfix += char + ' ';
};
var processOperator = function(char) {
while (stack.length > 0 &&
(precedence(stack[stack.length - 1]) >= precedence(char))) {
postfix += stack.pop() + ' ';
}
stack.push(char);
};
var processLeftPar = function(char) {
stack.push(char);
};
var processRightPar = function(char) {
var j = stack.length - 1,
ce;
while (true && stack.length > 0) {
ce = stack.pop();
if (ce == '(') break;
postfix += ce + ' ';
}
};
var processWall = function(char) {
var j = stack.length - 1,
ce;
while (true) {
ce = stack.pop();
if (ce.length > 1 && ce[ce.length-1] == '('){
postfix += ce + ' ';
break;
}
postfix += ce + ' ';
if (stack.length ==0) break;
}
};
var processFunc = function(char) {
postfix += '|' + ' ';
stack.push(char);
};
var processComma = function(char) {
var j = stack.length - 1,
ce;
while (true) {
ce = stack.pop();
if (ce.length > 1 && ce[ce.length-1] == '('){
ce = stack.push(ce);
break;
}
postfix += ce + ' ';
if (stack.length ==0) break;
}
};
var processToken = function(char) {
var type = Common.determineToken(char);
switch (type) {
case Common.tokenType.operand:
processOperand(char);
break;
case Common.tokenType.operator:
processOperator(char);
break;
case Common.tokenType.leftPar:
processLeftPar(char);
break;
case Common.tokenType.rightPar:
processRightPar(char);
break;
case Common.tokenType.func:
processFunc(char);
break;
case Common.tokenType.not:
processOperator(char);
break;
case Common.tokenType.variable:
processOperator(char);
break;
case Common.tokenType.comparator:
processOperator(char);
break;
case Common.tokenType.wall:
processWall(char);
break;
case Common.tokenType.comma:
processComma(char);
break;
}
};
var start = function(infix) {
var tokens = Common.tokenize(infix);
console.log(tokens);
for (var i = 0; i < tokens.length; i++) {
processToken(tokens[i]);
console.log(stack);
console.log(postfix);
}
return postfix;
};
var toPostfix = function(infix) {
postfix = '';
stack = [];
stack.push('(');
return start(infix + ')');
};
return {
toPostfix: toPostfix
}
})();
//fork from http://www.ivansivak.net/blog/simple-math-evaluator-infix-to-postfix-converter-and-calculator-reverse-polish-notation
//add support to function operator, variable operation, comparator
$(document).ready(function () {
$('#btnCalc').click(function () {
var infix = $('#expression').val(),
postfix = Infix.toPostfix(infix);
$('#spanPostfix').text(postfix).parent().fadeIn();
$('#spanResult').text(Postfix.calc(postfix));
});
var example = ['$avg(#a)', '1>2', '$sum(#a)>6', '$sum(#a)', '$avg(#b)+$sum(#a)'];
var exampleTxt = '';
for (var i =0 ; i <example.length ; i++){
$('#example1').append(
'<p>' + example[i] + ' = ' + Postfix.calc(Infix.toPostfix(example[i]))) +'</p>'
;
}
//$('#example2').text(Postfix.calc(Infix.toPostfix('2>1')));
//$('#example3').text(Postfix.calc(Infix.toPostfix()));
//$('#example4').text(Postfix.calc(Infix.toPostfix()));
//$('#example5').text(Postfix.calc(Infix.toPostfix()));
});
#spanResult{
font-size: 16px;
font-weight: bold;
color: rgb(teal);
}
.hide{
display: none;
}
input[type="text"]{
width: 250px;
}
var Postfix = (function () {
var stack = [];
var processOperand = function (char) {
stack.push(parseFloat(char));
};
var sumOf = function (hashTag){
return 100; //testing
};
var valueOf = function (hashTag){
if (hashTag == '#a'){
return 11; //testing
} else {
return 0;
}
};
var div100 = function (value){
return value / 100;
};
var processFunc = function (char) {
var temp = [];
while (true){
var right = stack.pop();
if (right[right.length-1] == '|') break;
temp.push(right);
}
if (char == '$sum('){
var result = 0;
for (var i=0;i<temp.length;i++){
result+=temp[i];
}
stack.push(result);
}
if (char == '$avg('){
var result = 0;
for (var i=0;i<temp.length;i++){
result+=temp[i];
}
stack.push(result/temp.length);
}
};
var processVariable = function (char) {
switch (char) {
case '#a':
var var_a = $('#variable_a').val();
var_a.split(',').forEach(function (va) {
stack.push(parseFloat(va));
});
break;
case '#b':
var var_b = $('#variable_b').val();
var_b.split(',').forEach(function (va) {
stack.push(parseFloat(va));
});
break;
}
};
var processUnionOperator = function (char) {
var right = (stack.pop());
switch (char) {
case '!':
stack.push(right==1?0:1);
break;
case '$sumOf':
stack.push(sumOf(right));
break;
case '$valueOf':
stack.push(valueOf(right));
break;
case '$div100':
stack.push(div100(right));
break;
}
};
var processWall = function (char) {
stack.push(char);
};
var processOperator = function (char) {
var right = (stack.pop()),
left = (stack.pop());
switch (char) {
case '+':
stack.push(left + right);
break;
case '-':
stack.push(left - right);
break;
case '*':
stack.push(left * right);
break;
case '/':
stack.push(left / right);
break;
case '>':
stack.push(left > right ? 1:0);
break;
case '>':
stack.push(left >= right ? 1:0);
break;
case '<':
stack.push(right > left ? 1:0);
break;
case '<=':
stack.push(right >= left ? 1:0);
break;
case '=':
stack.push(right = left ? 1:0);
break;
case '!=':
stack.push(right != left ? 1:0);
break;
}
};
var processToken = function (char) {
console.log('processToken' + char);
var type = Common.determineToken(char);
switch (type) {
case Common.tokenType.operand:
processOperand(char);
break;
case Common.tokenType.operator:
case Common.tokenType.comparator:
processOperator(char);
break;
case Common.tokenType.func:
processFunc(char);
break;
case Common.tokenType.variable:
processVariable(char);
break;
case Common.tokenType.not:
processUnionOperator(char);
break;
case Common.tokenType.wall:
processWall(char);
break;
}
};
var calc = function (postfix) {
var tokens;
stack = [];
tokens = postfix.split(' ');
tokens.splice(tokens.length - 1, 1);
while(tokens.length>0){
processToken(tokens.shift());
}
/*for (var i = 0; i < tokens.length; i++) {
processToken(tokens[i]);
}*/
return stack.pop();
};
return {
calc: calc
}
})();