The following is a "just for fun" attempt at controlling some stacks in JS.
What I would like to know is how to replace the single step logic in the
"runProgram()" function.
Currently it uses a confirm() command, but I would like to use something
else to single-step through the stack actions.
It causes the screen to darken and put an obtrusive box in the middle of the screen.
Any recommended changes are welcome.
Code:
<!DOCTYPE HTML>
<html>
<head>
<title> Stack Test </title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
#stackDisplay {
height:100px; width:200px; overflow:auto; margin: 0px 5px;
background-color:yellow; float:left; border:1px solid black;
}
#operandDisplay {
height:100px; width:200px; overflow:auto; margin: 0px 5px;
background-color:cyan; float:left; border:1px solid black;
}
#program {
height:100px; width:200px; margin: 0px 5px;
background-color:lightblue; float:left; border:1px solid black;
}
</style>
</head>
<body>
<div id="stackDisplay"></div>
<div id="operandDisplay"></div>
<textarea id="program"></textarea>
<br style="clear:both">
<input type="checkbox" id="SStep"> Single Step <button onclick="runProgram()">Run</button>
<script type="text/javascript">
function $_(IDS) { return document.getElementById(IDS); }
var TOS = NOS = act = 0; // TOS=TopOfStack, NOS=NextOnStack
var stack = []; var operand = [];
function runProgram() {
TOS = NOS = act = 0; // TOS=TopOfStack, NOS=NextOnStack
stack = []; operand = [];
$_('stackDisplay').innerHTML = '';
$_('operandDisplay').innerHTML = '';
strToTokens($_('program').value);
var flag = true;
while (((operand.length) > 0) && (flag)) {
act = operand.shift();
if ('*/+-'.indexOf(act) != -1) { Act2Stack(act) }
if ('dup,swap,drop'.indexOf(act) != -1) { Act1Stack(act); }
showStacks();
if ($_('SStep').checked) { flag = confirm('Next'); }
}
}
function strToTokens(str) {
var arr = str.split(' ');
for (var i=0; i<arr.length; i++) {
if (isNaN(arr[i])) { operand.push(arr[i]); }
else { stack.push(arr[i]); }
}
}
function showStacks() {
$_('stackDisplay').innerHTML = stack.join('<br>');
$_('operandDisplay').innerHTML = operand.join('<br>');
return true;
}
function Act1Stack(act) {
var tmp;
switch (act) {
case 'dup' : stack.unshift(stack[stack.length-1]); break;
case 'swap' : tmp = stack.shift(); TOS = stack.shift();
stack.unshift(tmp); stack.unshift(TOS); break;
case 'drop' : tmp = stack.shift(); break;
}
}
function Act2Stack(act) {
TOS = stack.shift()*1;
NOS = stack.shift()*1;
switch (act) {
case '*' : TOS = TOS * NOS; break;
case '/' : TOS = TOS / NOS; break;
case '+' : TOS = TOS + NOS; break;
case '-' : TOS = TOS - NOS; break;
} stack.unshift(TOS);
}
// Testing program
var str = '';
str = '123 45 + 3 / 3 - -1 * drop\n'; // -53
str += '123 45 swap - 3 / 3 + -1 * dup'; // 23
$_('program').value = str.replace(/\n/g,' ');
</script>
</body>
</html>
If it were not switchable, I’d just use something like an iterator (similar to the PHP Iterators) where you tie the call to next() to a button click event.
__________________
please post your code wrapped in [CODE] [/CODE] tags
I may be wrong, but I think the question was more about how to "pause" the loop so that the user can see what's going on(?). If that's so, the question just depends on how you want to present it to the user. You could use a timeout (which would give the user only a certain time, no confirmation feedback needed/possible) or you could use a "Next Step" button, which I'd prefer.
If you use a button, you can pretty much get rid of the loop altogether and simply reduce it to an if statement.
By the way: If you need comments to explain what variables are for, you are likely to have named them wrong. Just name them "topofStack" and "nextOnStack" – you can remove pointcless, repeated comments and make the code more readable.
By the way: If you need comments to explain what variables are for, you are likely to have named them wrong. Just name them "topofStack" and "nextOnStack" – you can remove pointcless, repeated comments and make the code more readable.
I am afraid that I do not agree. TOS and NOS seem eminently appropriate abbreviations to me, as the comments explain them very clearly.
TOS (or topOfStack) occurs 11 times in the code. Apart from the extra typing required (77 characters) there arises the risk of typo errors such as topofStack instead of topOfStack. (Assuming you want consistency with NextOnStack). Not pointcless at all.
__________________
All the code given in this post has been tested and is intended to address the question asked.
Unless stated otherwise it is not just a demonstration.
If you care about a few characters, there is always minification I'd rather have my code document itself than having to document it manually, which introduces the risk of rotting comments that are incomplete, not precise and ultimatively simply wrong. Risks, that my IDE cannot protect me from – unlike typos which are virtually inexistent these days with code-completion and all that stuff.
Comments, if used, should explain the code, not explain the name I'm giving to variables. And If it wasn't for the comments, it would've at least taken me some time to figure out what NOS and TOS are meant to represent.
Quote:
A comment that has gotten old, irrelevant, and incorrect is obsolete. Comments get old quickly. It is best not to write a comment that will become obsolete. If you find an obsolete comment, it is best to update it or get rid of it as quickly as possible. Obsolete comments tend to migrate away from the code they once described. They become floating islands of irrelevance and misdirection in the code. – Robert C. Martin
However, I know that to an extent this is a matter of habit and taste. By the way, Philip, I know you're not a friend of lengthy names. I wonder what you'd think if you were to read through our code at work with test function names like
Code:
@Test
public void givenSomeDescriptionOfTheScenarioWhenSomeActionIsTakenThenCompareInputAndOutputToHaveTheExpectedValuesAndStructure () {
[...]
}
(Edit: Maybe I should add that I'm not exactly a fan of these super-long names either, but they tend to be really useful when debugging tests)
And you don't need to be afraid to disagree. I, for once, could live with both naming methods. I was just pointing out my preferences. Descriptive names versus too long names sure isn't always an easy topic for me either.
Last edited by Airblader; 03-04-2013 at 05:39 PM..
(Edit: Maybe I should add that I'm not exactly a fan of these super-long names either, but they tend to be really useful when debugging tests)
And you don't need to be afraid to disagree. I, for once, could live with both naming methods. I was just pointing out my preferences. Descriptive names versus too long names sure isn't always an easy topic for me either.
Same as before. You work in an environment where other people have to understand your code with ease, and perhaps modify it, and vice-versa. The only person who has to understand my code is myself.
My policy is that names should be short, as short as is convenient, but still meaningful. Say uname for user name, dy for day and mth for month, but still pretty obvious. Not something impenetrable like a, fred or xyz. I don't indent for the reason I have already explained, but usually leave blank lines before/after loops, between functions etc. I grew up in an environment where variable names had to be as short as possible to save precious bytes - you could not afford lengthy names such as your test functions.
__________________
All the code given in this post has been tested and is intended to address the question asked.
Unless stated otherwise it is not just a demonstration.
@Airblader: Thank you. The change to an if statement did the trick. I was able to view the changes without the shading and placement of the alert message.
@Philip M: Thank you for the short variable name defense. I, too, was brought up in an environment of limited memory.
So long as the variable name is defined (commented?) somewhere, I have no problems understanding the meaning.
The longer variable names are useful and I can see the benefit of their use in more complicated programs.
MY problem with them is it gives me more opportunities to make spelling mistakes.
This was just an attempt at a FORTH style language using javascript as a RPN calculator, so for that reason I was keeping the variable names short.
@felgall: Would you have a short example of how a function could be created to overwrite the confirmation function?
I tried something like that but could not figure out how to use it without an infinite pause waiting for the operator to enter something.
My current logic for the single-step action was a change along this line...
Code:
var SSflag = false;
function RUN() {
if ( !SSflag ) {
TOS = NOS = act = 0;
stack = []; operand = [];
$_('stackDisplay').innerHTML = '';
$_('operandDisplay').innerHTML = '';
strToTokens($_('program').value);
} runProgram();
}
function runProgram() {
if ($_('SStep').checked) { SSflag = true;
if ((stack.length > 0) && (operand.length >= 0)) {
showStacks(); act = operand.shift(); takeAction(act);
} else { alert('End of Program'); } // SSflag = false;}
} else { SSflag = false; // run program at full speed
while (operand.length > 0) { takeAction(operand.shift()); } showStacks();
}
}
I can provide the rest of the code for anyone interested, but the crux of the probem (single step vs full speed)
is solved in the runProgram() function above.