Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DanCouper/09a1ec47b227e07ce7acaee20c94472a to your computer and use it in GitHub Desktop.
Save DanCouper/09a1ec47b227e07ce7acaee20c94472a to your computer and use it in GitHub Desktop.
Calculator using Shunting Yard Algorithm
<body>
<span id="inpos">
<span id="inpbox">
<input id="inp" type="text" placeholder="0">
<span id="close">X</span>
</span>
<span id="enter">ENTER</span>
</span>
<div id="res"></div>
</body>
function queue(q) {
this.q = q;
this.len = function() {
return q.length;
}
this.push = function(ite) {
q.splice(0, 0, ite);
}
this.pop = function() {
return q.splice(0, 1)[0];
}
this.top = function() {
return q[0];
}
this.print = function() {
var s = "";
for (var i = 0; i < this.q.length; i++) {
s += q[i];
}
return s;
}
}
//used for displaying
function rev(q) {
var res = [];
for (var i = q.length - 1; i >= 0; i--) {
res.push(q[i]);
}
return res;
}
//list of precedences and associativities
var ops = {
'+': {
prec: 2,
ass: 'l'
},
'-': {
prec: 2,
ass: 'l'
},
'*': {
prec: 3,
ass: 'l'
},
'/': {
prec: 3,
ass: 'l'
},
'^': {
prec: 4,
ass: 'r'
}
}
function getPrec(ite) {
return ops[ite].prec;
}
function getAss(ite) {
return ops[ite].ass;
}
//-1 = op1 < op2; 0 = op1 == op2; 1 = op1>op2
function precomp(op1, op2) {
if (getPrec(op1) < getPrec(op2))
return -1;
else if (getPrec(op1) == getPrec(op2))
return 0;
else
return 1;
}
function isNum(ite) {
return !isNaN(ite);
}
function isOp(ite) {
return (ite == '+') || (ite == '-') || (ite == '*') || (ite == "/") || (ite == "^")
}
function isUnary(op) {
return false;
}
function isBinary(op) {
return (op == "+") || (op == "-") || (op == "*") || (op == "/") || (op == "^");
}
function doOp(op, num1, num2) {
if (num2 != undefined) {
if (op == "+")
return parseFloat(num1) + parseFloat(num2);
else if (op == "-")
return parseFloat(num2) - parseFloat(num1);
else if (op == "*")
return parseFloat(num1) * parseFloat(num2);
else if (op == "/")
return parseFloat(num2) / parseFloat(num1);
else if (op == "^")
return Math.pow(parseFloat(num2), parseFloat(num1));
}
}
function isParen(ite) {
return (ite == "(") || (ite == ")");
}
function isRightParen(ite) {
return ite == ")";
}
function isLeftParen(ite) {
return ite == "(";
}
//-------------------------------------------------------
function getTks(teststr) {
teststr = teststr.replace(/ /g,'');
var tks = [];
while (teststr.length > 0) {
var numreg = /^\d+\.?\d*|^\(-\d+\.?\d*(?=\))/;
var opreg = /^[+\-*/\^]/;
var parreg = /^[\(\)]/;
var r = "",
r2 = "",
r3 = "";
r = numreg.exec(teststr);
r2 = opreg.exec(teststr);
r3 = parreg.exec(teststr);
// var r = parseFloat(teststr);
if (r != null) {
if (r[0][0] == '(') {
tks.push(snip(r[0], '('))
teststr = snip(teststr, r[0] + ')');
} else {
tks.push(r[0]);
teststr = snip(teststr, r[0]);
}
} else {
var op = teststr.charAt(0);
tks.push(op);
teststr = snip(teststr);
}
}
return tks;
}
function snip(teststr, r) {
var s = "";
if (r == undefined) {
for (var i = 1; i < teststr.length; i++) {
s += teststr[i];
}
return s;
} else {
var l = String(r).length;
for (var i = l; i < teststr.length; i++) {
s += teststr[i];
}
return s;
}
}
function regtest() {
var numreg = /^\d+\.?\d*|^(?=\()-\d+\.?\d*(?=\))/;
var opreg = /^[+\-*/\^]/;
var parreg = /^[\(\)]/;
var teststr = "3+5";
var exp = "3",
res = numreg.exec(teststr);
if (res == null)
console.log('FAIL: numreg.exec("' + teststr + '") is null')
else if (res[0] == exp)
console.log('PASS: numreg.exec("' + teststr + '")[0] == "' + exp)
else
console.log('FAIL: numreg.exec("' + teststr + '")[0] == "' + exp + '"; should be ' + exp + ', got ' + res[0])
teststr = "(-3)+5";
exp = "-3"
res = numreg.exec(teststr);
if (res == null)
console.log('FAIL: numreg.exec("' + teststr + '") is null')
else if (res[0] == exp)
console.log('PASS: numreg.exec("' + teststr + '")[0] == "' + exp)
else
console.log('FAIL: numreg.exec("' + teststr + '")[0] == "' + exp + '"; should be ' + exp + ', got ' + res[0])
teststr = "(-3)+5";
exp = "-3"
res = numreg.exec(teststr);
if (res == null)
console.log('FAIL: numreg.exec("' + teststr + '") is null')
else if (res[0] == exp)
console.log('PASS: numreg.exec("' + teststr + '")[0] == "' + exp)
else
console.log('FAIL: numreg.exec("' + teststr + '")[0] == "' + exp + '"; should be ' + exp + ', got ' + res[0])
}
function getRpn(expr) {
var tks = getTks(expr);
var q = new queue(tks);
var buf1 = new queue([]);
var buf2 = new queue([]);
while (q.len() > 0) {
if (isNum(q.top())) {
buf1.push(q.pop())
} else if (isOp(q.top())) {
if ((buf2.len() > 0) && (isOp(buf2.top())) && ((precomp(q.top(), buf2.top()) <= 0 && getAss(q.top()) == 'l') || (precomp(q.top(), buf2.top()) == -1 && getAss(q.top()) == 'r'))) {
while ((buf2.len() > 0) && (isOp(buf2.top())) && ((precomp(q.top(), buf2.top()) <= 0 && getAss(q.top()) == 'l') || (precomp(q.top(), buf2.top()) == -1 && getAss(q.top()) == 'r'))) {
buf1.push(buf2.pop());
}
buf2.push(q.pop())
} else {
buf2.push(q.pop());
}
} else if (isParen(q.top())) {
if (isRightParen(q.top())) {
while (!isLeftParen(buf2.top())) //until the top of the op stack is left paren...
{
buf1.push(buf2.pop()) //pop tokens off the op stack
}
buf2.pop()
q.pop();
} else {
buf2.push(q.pop()); //if left paren, push onto op stack
}
} else //don't need this
{
console.log("dont know")
q.pop()
}
}
while (buf2.len() > 0) {
buf1.push(buf2.pop())
}
return buf1
}
function calc(rpn) {
var stk = new queue([]);
console.log(rpn.q)
while (rpn.len() > 0) {
if (isNum(rpn.top())) {
console.log("h" + rpn.top())
stk.push(rpn.pop())
}
if (isOp(rpn.top())) {
if (isUnary(rpn.top())) {
var num = stk.pop();
var op = rpn.pop();
var res = doOp(op, num);
stk.push(res)
} else if (isBinary(rpn.top())) {
var num1 = stk.pop();
var num2 = stk.pop();
var op = rpn.pop();
res = doOp(op, num1, num2);
stk.push(res)
}
}
}
console.log(stk.q)
return stk;
}
//--------------------------------------------------------------------------------------------
var teststr3 = "(1+6)+(-4)"
var teststr = "(3+1)*(-2)";
var teststr2 = "3 + 4 * 2 / (1 - 5) * 2 * 3"
var testarr2 = ['3', '+', '4', '*', '2', '/', '(', '1', '+', '-5', ')', '^', '2', '^', '3']
var testarr = ['(', '3', '+', '1', ')', '*', '(', '-2', ')'];
var but = document.getElementById('enter');
var x = document.getElementById('close');
var inp = document.getElementById('inp');
$('#inp').keyup(function(){
var curv = $("#inp").val();
if(curv == "") {
var res = document.getElementById("res");
res.innerHTML = "";
}
})
//submit
but.addEventListener('click', function() {
var inp = document.getElementById('inp').value
var res = document.getElementById('res');
inp = rev(getRpn(inp).q); //extract array of queue(stack) structure
var inp = new queue(inp);
console.log(inp)
res.innerHTML = calc(inp).print();
})
x.addEventListener('click', function(){
$('#inp').val("")
var res = document.getElementById("res");
res.innerHTML = "";
})
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
html,
body {
height: 100%;
overflow: hidden;
-webkit-transition: all .500s;
transition: all .500s;
}
html {
background: #282E33;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-size:5em;
}
span#inpbox {
display: inline-block;
border: 2px solid #5E6C77;
border-radius: 0.5em;
padding: 0px 10px 0px;
color: #5E6C77;
border-radius: .25em;
background: transparent;
}
span#close {
font-family: Helvetica;
cursor:pointer;
}
span#inpbox > input {
border: none;
background: none;
}
#enter {
background: #222;
color: white;
cursor:pointer;
border:1px solid black;
padding:0px 5px;
border-radius:6px;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment