Title says everything
A Pen by Alan R. Soares on CodePen.
<div class='container' ng-app='evaluator'> | |
<div class='page-header'> | |
<h1><span class='text-muted'>Simple</span> Arithmetic Expression Evaluator</h1> | |
</div> | |
<form name='evtForm' role="form" class='well' ng-controller='AppCtrl' action='#'> | |
<div class="form-group has-feedback" | |
ng-class="{false: 'has-error', true:'has-success'}[evtForm.expression.$valid]"> | |
<label class='control-label' | |
for="expression">Expression</label> | |
<input id='expression' | |
name='expression' | |
class="form-control" | |
ng-model='expression' | |
ng-pattern='/(\d+(\.\d+)?)(\s+)?([\^*\/\+-])(\s+)?(\d+(\.\d+)?)/' | |
required | |
placeholder='enter expression' /> | |
<span class="glyphicon glyphicon-ok form-control-feedback" | |
ng-class="{true:'glyphicon-ok', false:'glyphicon-remove'}[evtForm.$valid]"></span> | |
<div class='alert alert-danger' | |
ng-show='!evtForm.$valid'> | |
Invalid Expression! | |
</div> | |
</div> | |
<div class='form-group' | |
ng-show='evtForm.$valid'> | |
<label class='control-label'>Result</label> | |
<div class='alert alert-info'> | |
{{result}} | |
</div> | |
</div> | |
<div class='form-group' | |
ng-show='steps'> | |
<label class='control-label'>Steps</label> | |
<ul> | |
<li ng-repeat='step in steps'> | |
{{step}} | |
</li> | |
</ul> | |
</div> | |
</form> | |
</div> |
var app = angular.module('evaluator', []) | |
.controller('AppCtrl', function ($scope, expressionEvaluator) { | |
var ee = expressionEvaluator; | |
var onExpressionChange = function (newValue, oldValue) { | |
if (newValue !== oldValue && ee.isValid(ee.sanitize(newValue))) { | |
ee.resetSteps(); | |
$scope.steps = []; | |
evaluate(newValue); | |
} | |
}; | |
function evaluate(newValue){ | |
$scope.steps = []; | |
$scope.result = ee.evaluate(newValue || $scope.expression); | |
$scope.steps = ee.getSteps(); | |
} | |
(function init() { | |
$scope.steps = []; | |
$scope.$watch('expression', onExpressionChange); | |
$scope.expression = "3 ^ (3 + (1 + 2 - 3)) * 4 / 5"; | |
evaluate(); | |
}()); | |
}) | |
.factory('expressionEvaluator', function () { | |
var mathPatt = /(\d+(\.\d+)?)([\^*\/\+-])(\d+(\.\d+)?)/; | |
var precPatt0 = /(\d+(\.\d+)?)(\^)(\d+(\.\d+)?)/; | |
var precPatt1 = /(\d+(\.\d+)?)([*\/])(\d+(\.\d+)?)/; | |
var subExprPatt = /(?:\()([\d\.\+\/-]+)(?:\))/; | |
var steps = []; | |
function getSteps(){ | |
return steps; | |
} | |
function resetSteps(){ | |
steps = []; | |
} | |
function addStep(step){ | |
steps.push(step); | |
} | |
function isValid(expr) { | |
return mathPatt.test(expr); | |
} | |
function sanitize(expr) { | |
return expr.trim().replace(/\s+/ig, ''); | |
} | |
function getNextSegment(expr) { | |
return precPatt0.exec(expr) || precPatt1.exec(expr) || mathPatt.exec(expr); | |
} | |
function hasSubExpression(expr) { | |
return subExprPatt.test(expr); | |
} | |
function getSubExpression(expr) { | |
return subExprPatt.exec(expr); | |
} | |
function evaluateSegment(segment) { | |
var operator = segment[3]; | |
var elm1 = parseFloat(segment[1]); | |
var elm2 = parseFloat(segment[4]); | |
switch (operator) { | |
case "^": | |
return Math.pow(elm1, elm2); | |
case "*": | |
return elm1 * elm2; | |
case "/": | |
return elm1 / elm2; | |
case "+": | |
return elm1 + elm2; | |
case "-": | |
return elm1 - elm2; | |
} | |
} | |
function replaceSegmentResult(segment, result) { | |
return segment.input.replace(segment[0], result.toString()); | |
} | |
function evaluate(expression) { | |
var expr = sanitize(expression); | |
var lastItem = steps.length >0 ? steps[steps.length-1] : ""; | |
if(!/sub/.test(lastItem)) | |
addStep("evaluating expression: " + expr); | |
var result; | |
while (hasSubExpression(expr)) { | |
var subExprSegment = getSubExpression(expr); | |
var subExpression = subExprSegment[1]; | |
addStep("sub expression: " + subExprSegment[0]); | |
result = evaluate(subExpression); | |
expr = replaceSegmentResult(subExprSegment, result); | |
} | |
while (isValid(expr)) { | |
addStep('segment: ' + expr); | |
var segment = getNextSegment(expr); | |
result = evaluateSegment(segment); | |
expr = replaceSegmentResult(segment, result); | |
} | |
var res = parseFloat(expr); | |
addStep('result: ' + res); | |
return res; | |
} | |
return { | |
evaluate: evaluate, | |
isValid: isValid, | |
sanitize: sanitize, | |
getSteps: getSteps, | |
resetSteps: resetSteps | |
}; | |
}); |
Title says everything
A Pen by Alan R. Soares on CodePen.