Shunting-yard and RPN algorithm visualization
Made by Michael Buen
HTML
<div ng-app ng-controller="Controller">
<div ng-show='normal != null'>{{expression}}</div>
<br/>
<div ng-show='normal == null'>
<input type='text' style='width: 300px' ng-model='expression' />
<br/><br/>
<input type='checkbox' ng-model='yieldIntermediateResults '/> Yield Intermediate Results
<br/></br>
<input type='button' value='Structure' ng-click='structure()' />
</div>
<div ng-show='normal.length > 0 || opStack.length > 0'>
<input type='button' value='Convert to Reversed Polish Notation' ng-click='convertToRpn()' />
<br/><br/>
</div>
<div ng-show='normal.length > 0'>
Normal: <span class='box' ng-repeat='x in normal'>{{x}}</span>
<br/><br/>
</div>
<div ng-show='normal.length > 0 || rpn.length > 0'>
RPN: <span class='box' ng-repeat='x in rpn'>{{x}}</span>
<hr/>
</div>
<div ng-show='opStack.length > 0'>
<div class='box' style='width: 10px' ng-repeat="op in opStack.slice(0).reverse()" >{{op}}</div>
</div>
<div ng-show='yieldIntermediateResults && ((normal != null && normal.length > 0) || opStack.length > 0)'>
__OP STACK__
<hr/>
</div>
<div ng-show="(normal.length == 00 && rpn.length > 0 && opStack.length == 0)" >
<input type='button' value='Solve RPN' ng-click='solveRpn()'/>
<br/>
</div>
<div ng-show="yieldIntermediateResults && (normal.length == 00 && rpn.length > 0 && opStack.length == 0)" >
<div class='box' style='width: 15px' ng-repeat="v in values.slice(0).reverse()" >{{v}}</div>
__VALUES STACK__
<hr/>
</div>
<div ng-show='rpn.length == 0 && values.length == 1'>Answer is {{values[0]}}</div>
</div>
CSS
.box {
border-width: 1px;
border-style: solid;
margin: 2px;
padding: 3px;
border-color: red;
}
JavaScript
function Controller($scope) {
// $scope is similar to *this* keyword of OOP, it disambiguates local variable
// against the class-level variable
// ====Model====
// result: 11
// $scope.expression = '1 + 2 * 3 - 4 + 5 * 6 / 2 - 2 * 3 ^ 2 + ( 2 + 5 ) * 2';
$scope.expression = '2 + 3 * 4 - 5 + 7 * 6 / 3 - 2 * 3 ^ 2 + ( 5 - 2 ) * 2';
// result: 16
// $scope.expression = '1 + 2 * 3 - 4 + 5 * 6 / 2 - 2';
$scope.yieldIntermediateResults = true;
$scope.normal = null;
$scope.rpn = [];
$scope.opStack = [];
$scope.values = [];
// ====Controller's Actions====
$scope.structure = function() {
$scope.normal = $scope.expression.split(' ');
};
$scope.convertToRpn = function() {
while ((elem = $scope.normal.shift()) != null) {
if (elem == '(' || elem == ')') {
if (elem == '(')
$scope.opStack.push(elem);
else if (elem == ')') {
while((op = $scope.opStack.pop()) != '(') {
$scope.rpn.push(op);
}
}
}
else if (!elem.isOperator()) {
$scope.rpn.push(elem);
}
else {
var peek = $scope.opStack[$scope.opStack.length - 1];
while ($scope.opStack.length > 0 &&
(
(elem.isLeftAssociative() && elem.operatorPrecedence() <= peek.operatorPrecedence())
||
elem.operatorPrecedence() < peek.operatorPrecedence()
)
) {
$scope.rpn.push($scope.opStack.pop());
...