Parses a string and evaluates the expression. I didn't realize how hard doing this actually was.
A Pen by Dave Pagurek on CodePen.
Parses a string and evaluates the expression. I didn't realize how hard doing this actually was.
A Pen by Dave Pagurek on CodePen.
<div id="top"><h1>Expression Calculator</h1> | |
<input type="text" value="(10^2/(2*50))-2(30)" id="input" /><input type="submit" value="Simplify" id="simplify" /></div> | |
<div id="wrapper"> | |
<div id="result"></div> | |
</div> |
//Module for input checking and parsing | |
var Worker = (function() { | |
var w={}; | |
//Divides input up into Sections and Singles | |
w.parse = function(value) { | |
if (!value) return 0; | |
var sections = []; | |
//Remove excess whitespace | |
value.replace(/\s/g, ''); | |
//get rid of unnecessary brackets surrounding the section | |
while (value.substr(0, 1)=="(" && value.substr(value.length-1, 1)==")") { | |
var openBrackets=1; | |
for (var i=1; i<value.length&&openBrackets>0; i++) { | |
if (value.substr(i, 1)=="(") openBrackets++; | |
if (value.substr(i, 1)==")") openBrackets--; | |
} | |
i-=1; | |
if (openBrackets!=0 || i!=value.length-1) { | |
break; | |
} else { | |
value=value.substring(1, value.length-1); | |
} | |
} | |
var addition = value.lastIndexOf("+"); | |
var subtraction = value.lastIndexOf("-"); | |
var multiplication = value.lastIndexOf("*"); | |
var division = value.lastIndexOf("/"); | |
var exponent = value.lastIndexOf("^"); | |
var bracket1 = value.lastIndexOf("("); | |
/* make sure we pick out operators that AREN'T IN BRACKETS | |
by checking if the signs are in brackets and searching | |
again if they are */ | |
var redos=[0, 0, 0, 0, 0, 0]; | |
if (addition==-1) redos[0]=1; | |
if (subtraction==-1) redos[1]=1; | |
if (multiplication==-1) redos[2]=1; | |
if (division==-1) redos[3]=1; | |
if (exponent==-1) redos[4]=1; | |
if (bracket1==-1) redos[5]=1; | |
while (redos[0]==0 || redos[1]==0 || redos[2]==0 || redos[3]==0 || redos[4]==0 || redos[5]==0) { | |
var openBrackets=0; | |
for (var i=0; i<value.length; i++) { | |
if (value.substr(i, 1)=="(") { | |
if (i==bracket1) { | |
if (openBrackets>0) { | |
bracket1 = value.substring(0, bracket1).lastIndexOf("("); | |
} else { | |
redos[5]=1; | |
} | |
} | |
openBrackets++; | |
} else if (value.substr(i, 1)==")") { | |
openBrackets--; | |
} else if (i==addition) { | |
if (openBrackets>0) { | |
addition = value.substring(0, addition).lastIndexOf("+"); | |
} else { | |
redos[0]=1; | |
} | |
} else if (i==subtraction) { | |
if (openBrackets>0) { | |
subtraction = value.substring(0, subtraction).lastIndexOf("-"); | |
} else { | |
redos[1]=1; | |
} | |
} else if (i==multiplication) { | |
if (openBrackets>0) { | |
multiplication = value.substring(0, multiplication).lastIndexOf("*"); | |
} else { | |
redos[2]=1; | |
} | |
} else if (i==division) { | |
if (openBrackets>0) { | |
division = value.substring(0, division).lastIndexOf("/"); | |
} else { | |
redos[3]=1; | |
} | |
} else if (i==exponent) { | |
if (openBrackets>0) { | |
exponent = value.substring(0, exponent).lastIndexOf("^"); | |
} else { | |
redos[4]=1; | |
} | |
} | |
} | |
if (addition==-1) redos[0]=1; | |
if (subtraction==-1) redos[1]=1; | |
if (multiplication==-1) redos[2]=1; | |
if (division==-1) redos[3]=1; | |
if (exponent==-1) redos[4]=1; | |
if (bracket1==-1) redos[5]=1; | |
} | |
console.log(value); | |
//reverse BEDMAS | |
if (addition != -1 && (subtraction == -1 || addition>subtraction)) { | |
sections.push(new Segment(value.substring(0, addition))); | |
sections.push(new Single(value.substring(addition, addition+1), "operator")); | |
sections.push(new Segment(value.substring(addition+1))); | |
} else if (subtraction != -1) { | |
sections.push(new Segment(value.substring(0, subtraction))); | |
sections.push(new Single(value.substring(subtraction, subtraction+1), "operator")); | |
sections.push(new Segment(value.substring(subtraction+1))); | |
} else if (multiplication != -1 && (division == -1 || multiplication>division)) { | |
sections.push(new Segment(value.substring(0, multiplication))); | |
sections.push(new Single(value.substring(multiplication, multiplication+1), "operator")); | |
sections.push(new Segment(value.substring(multiplication+1))); | |
} else if (division != -1) { | |
sections.push(new Segment(value.substring(0, division))); | |
sections.push(new Single(value.substring(division, division+1), "operator")); | |
sections.push(new Segment(value.substring(division+1))); | |
} else if (exponent != -1) { | |
sections.push(new Segment(value.substring(0, exponent))); | |
sections.push(new Single(value.substring(exponent, exponent+1), "operator")); | |
sections.push(new Segment(value.substring(exponent+1))); | |
} else if (bracket1 != -1) { | |
var openBrackets=1; | |
for (var i=bracket1+1; i<value.length&&openBrackets>0; i++) { | |
if (value.substr(i, 1)=="(") openBrackets++; | |
if (value.substr(i, 1)==")") openBrackets--; | |
} | |
if (openBrackets==0) { | |
var bracket2=i-1; | |
if (bracket1>0) sections.push(new Segment(value.substring(0, bracket1))); | |
if (bracket2-bracket1!=1) sections.push(new Segment(value.substring(bracket1+1, bracket2))); | |
if (bracket2!=value.length-1) sections.push(new Segment(value.substring(bracket2+1))); | |
} else { | |
console.log("Brackets nesting error: " + value); | |
return 0; | |
} | |
} else { | |
sections.push(new Single(value, "value")); | |
} | |
return sections; | |
}; | |
//Checks to see if brackets are properly nested in a string | |
w.properBrackets = function(value) { | |
var openBrackets=0; | |
for (var i=0; i<value.length; i++) { | |
if (value.substr(i, 1)=="(") openBrackets++; | |
if (value.substr(i, 1)==")") openBrackets--; | |
} | |
return openBrackets==0; | |
}; | |
return w; | |
}()); | |
//Class for a single number or operator | |
function Single(value, type) { | |
this.value = value; | |
this.type = type; | |
this.solve = function() { | |
if (this.type == "value") { | |
return parseFloat(this.value); | |
} else { | |
return 0; | |
} | |
}; | |
this.display = function() { | |
return this.value; | |
}; | |
this.pretty = function() { | |
var str="<div class='group " + this.type + "' val='" + this.value + "'>"; | |
if (this.value!="/" && this.value!="^" && this.value!="*") { | |
str+= this.value; | |
} | |
if (this.value=="*") { | |
str += "×"; | |
} | |
str+="</div>"; | |
return str; | |
} | |
} | |
//Class for a segment of math (a container) | |
function Segment(value) { | |
this.sections = value?Worker.parse(value):[]; | |
this.type = "section"; | |
//Recursively solve children | |
this.solve = function() { | |
var i=0; | |
var value=0; | |
while (i<this.sections.length) { | |
if (i+1>=this.sections.length) { | |
value = this.sections[i].solve(); | |
i++; | |
} else if (this.sections[i+1].type=="operator") { | |
if (this.sections[i+1].value == "+") { | |
value += this.sections[i].solve() + this.sections[i+2].solve(); | |
} else if (this.sections[i+1].value == "-") { | |
value += this.sections[i].solve() - this.sections[i+2].solve(); | |
} else if (this.sections[i+1].value == "*") { | |
value += this.sections[i].solve() * this.sections[i+2].solve(); | |
} else if (this.sections[i+1].value == "/") { | |
value += this.sections[i].solve() / this.sections[i+2].solve(); | |
} else if (this.sections[i+1].value == "^") { | |
value += Math.pow(this.sections[i].solve(), this.sections[i+2].solve()); | |
} | |
i+=3; | |
} else { | |
value += this.sections[i].solve() * this.sections[i+1].solve(); | |
i+=2; | |
} | |
} | |
return value; | |
}; | |
//Display grouped blocks | |
this.display = function() { | |
var str=""; | |
for (var i=0; i<this.sections.length; i++) { | |
str+="<div class='box'>" + this.sections[i].display(); | |
if (this.sections[i].type=="section") str+= "<div class='answer'>= " + this.sections[i].solve() + "</div>"; | |
str+="</div>"; | |
} | |
return str; | |
}; | |
this.pretty = function(css) { | |
var str="<div class='group " + this.type; | |
if (css) str+= " " + css; | |
str+= "' val='" + this.value + "'>"; | |
for (var i=0; i<this.sections.length; i++) { | |
if ((i+1<this.sections.length && this.sections[i+1].type!="operator")||(i-1>=0 && this.sections[i-1].type!="operator")) { | |
str+=this.sections[i].pretty("brackets"); | |
} else { | |
str+=this.sections[i].pretty(); | |
} | |
} | |
str+="</div>"; | |
return str; | |
}; | |
} | |
function simplifyText(event) { | |
var input = document.getElementById("input").value; | |
if (Worker.properBrackets(input)) { | |
document.getElementById("wrapper").className=""; | |
var timer = setTimeout(function() { | |
var expression = new Segment(input); | |
document.getElementById("result").innerHTML = expression.pretty("middle") + "<div class='display'>" + expression.display() + "</div><div class='final answer'>= " + expression.solve() + "</div>"; | |
var matches = document.querySelectorAll("[val='/']"); | |
var i; | |
for (i=0; i<matches.length; i++) { | |
matches[i].previousSibling.style.verticalAlign = "bottom"; | |
} | |
matches = document.querySelectorAll("[val='^']"); | |
for (i=0; i<matches.length; i++) { | |
matches[i].previousSibling.style.verticalAlign = "middle"; | |
} | |
document.getElementById("wrapper").className="solved"; | |
}, 800); | |
} else { | |
document.getElementById("result").innerHTML = "<div class='error'>Error: Improperly nested brackets. Remember to only use round brackets and close all opened brackets.</div>"; | |
} | |
} | |
window.onload = function() { | |
document.getElementById("simplify").addEventListener("click", simplifyText); | |
simplifyText(); | |
}; |
@import url(http://fonts.googleapis.com/css?family=Open+Sans:300); | |
body { | |
background-color:#34495e; | |
color:#FFF; | |
font-family: sans-serif; | |
padding:0; | |
margin:0; | |
} | |
#top { | |
padding:20px; | |
} | |
h1 { | |
font-family: 'Open Sans', sans-serif; | |
font-size:30px; | |
margin:0 0 10px 0; | |
} | |
input[type="submit"], input[type="button"], a.button { | |
background-color:#FFF; | |
color:#000; | |
padding:5px; | |
border: 5px solid #FFF; | |
text-decoration:none; | |
display:inline-block; | |
height:40px; | |
font-size:16px; | |
font-family:sans-serif; | |
margin:0 0 0 10px; | |
} | |
input[type="submit"]:hover, input[type="button"]:hover, a.button:hover { | |
border: 5px solid #DDD; | |
} | |
input[type="text"] { | |
display:inline-block; | |
height:20px; | |
width: 290px; | |
padding: 10px; | |
font-size: inherit; | |
border: 0; | |
} | |
#result { | |
margin-top:10px; | |
padding:0 20px; | |
overflow:hidden; | |
background-color:#2c3e50; | |
max-height:0; | |
-webkit-transition: all 0.3s ease-out; | |
-moz-transition: all 0.3s ease-out; | |
-ms-transition: all 0.3s ease-out; | |
-o-transition: all 0.3s ease-out; | |
transition: all 0.3s ease-out; | |
} | |
.solved #result { | |
max-height:2000px; | |
padding:20px; | |
color:#FFF; | |
} | |
.box { | |
color:#000; | |
padding:5px; | |
margin:5px; | |
float:left; | |
display:inline-block; | |
border:5px solid #DDD; | |
background-color:#FFF; | |
} | |
.display > .box { | |
margin: 5px 10px 5px 0; | |
} | |
.answer { | |
clear:both; | |
text-align:center; | |
} | |
.final { | |
color:#FFF; | |
font-size:60px; | |
text-align:left; | |
font-family: 'Open Sans', sans-serif; | |
} | |
.group { | |
font-family: 'Open Sans', sans-serif; | |
display:inline-block; | |
vertical-align:inherit; | |
text-align:center; | |
margin:1px 1px 0 0; | |
padding:0; | |
} | |
.middle { | |
vertical-align:middle; | |
} | |
.brackets:before { | |
content:"("; | |
} | |
.brackets:after { | |
content:")"; | |
} | |
.group[val="^"], .group[val="^"]+.group { | |
font-size: 0.7em; | |
vertical-align:super; | |
} | |
.group[val="/"], .group[val="/"]+.group { | |
clear:both; | |
display:block; | |
} | |
.group[val="/"]+.group { | |
border-top:1px solid #FFF; | |
} | |
.group[val="*"], .group[val="+"], .group[val="-"] { | |
padding:0 3px; | |
} | |
.display { | |
margin-top:10px; | |
} | |
.error { | |
background-color:#6B1010; | |
padding:10px; | |
opacity:0.7; | |
} |