Skip to main content
Code Review

Return to Question

replaced http://meta.codereview.stackexchange.com/ with https://codereview.meta.stackexchange.com/
Source Link

The April 2015 Community Challenge requires building a calculator.

The April 2015 Community Challenge requires building a calculator.

Notice removed Draw attention by Community Bot
Bounty Ended with Snowbody's answer chosen by Community Bot
Notice added Draw attention by Jeroen Vannevel
Bounty Started worth 100 reputation by Jeroen Vannevel
use space or del, some broswers don't support del.
Source Link
rolfl
  • 98.1k
  • 17
  • 219
  • 419
var calc = {
 stack: {
 values: new Array(1024),
 size: 0,
 decimal: 0
 },
 
 display: {
 value: 0.0,
 integral: true
 },
 
 layout: [
 {
 compact: false,
 buttons: ["seven", "eight", "nine", "root", "clear"]
 },
 {
 compact: false,
 buttons: ["four", "five", "six", "times", "divide"]
 },
 {
 compact: false,
 buttons: ["one", "two", "three", "plus", "minus"]
 },
 {
 compact: false,
 buttons: ["zero", "point", "plusminus", "exp", "equals"]
 },
 ],
 keys: {},
 buttons: {
 zero: {
 action: function() {return digit(0);},
 label: "0",
 key: [48]
 },
 one: {
 action: function() {return digit(1);},
 label: "1",
 key: [49]
 },
 two: {
 action: function() {return digit(2);},
 label: "2",
 key: [50]
 },
 three: {
 action: function() {return digit(3);},
 label: "3",
 key: [51]
 },
 four: {
 action: function() {return digit(4);},
 label: "4",
 key: [52]
 },
 five: {
 action: function() {return digit(5);},
 label: "5",
 key: [53]
 },
 six: {
 action: function() {return digit(6);},
 label: "6",
 key: [54]
 },
 seven: {
 action: function() {return digit(7);},
 label: "7",
 key: [55]
 },
 eight: {
 action: function() {return digit(8);},
 label: "8",
 key: [56]
 },
 nine: {
 action: function() {return digit(9);},
 label: "9",
 key: [57]
 },
 point: {
 action: function() {
 if (calc.stack.decimal === 0) {
 calc.stack.decimal = 1;
 }
 return peek();
 },
 label: ".",
 key: [46]
 },
 root: {
 action: function() {
 return unaryOp(function(val) {
 return Math.sqrt(val);
 });
 },
 label: "r\u221A",
 color: "black",
 key: [114]
 },
 plusminus: {
 action: function() {
 return unaryOp(function(val) {
 return -val;
 });
 },
 label: "~\u00B1",
 key: [126]
 },
 plus: {
 action: function() {
 return binaryOp(calc.buttons.plus);
 },
 operate: function(left, right) {
 return left + right;
 },
 label: "+",
 color: "black",
 key: [43]
 },
 minus: {
 action: function() {
 return binaryOp(calc.buttons.minus);
 },
 operate: function(left, right) {
 return left - right;
 },
 label: "-",
 color: "black",
 key: [45]
 },
 times: {
 action: function() {
 return binaryOp(calc.buttons.times);
 },
 operate: function(left, right) {
 return left * right;
 },
 label: "*",
 color: "black",
 key: [42,120]
 },
 divide: {
 action: function() {
 return binaryOp(calc.buttons.divide);
 },
 operate: function(left, right) {
 return left / right;
 },
 label: "/\u00F7",
 color: "black",
 key: [47]
 },
 exp: {
 action: function() {
 return binaryOp(calc.buttons.exp);
 },
 operate: function(left, right) {
 return Math.pow(left , right);
 },
 label: "EXP^",
 color: "black",
 key: [94]
 },
 equals: {
 action: function() {resolve();},
 label: "=",
 color: "black",
 key: [61,13]
 },
 clear: {
 action: function() {reset();},
 label: "del",
 color: "orange",
 key: [127][32, 127]
 }
 }
 
};
function peek() {
 if (calc.stack.size == 0) {
 throw "Empty stack peek()";
 }
 return calc.stack.values[calc.stack.size - 1];
}
function pop() {
 if (calc.stack.size == 0) {
 throw "Empty stack pop()";
 }
 calc.stack.size--;
 var val = calc.stack.values[calc.stack.size];
 calc.stack.values[calc.stack.size] = null;
 return val;
}
function push(val) {
 calc.stack.values[calc.stack.size++] = val;
 return val;
}
function cleared() {
 return calc.stack.size == 0;
}
function reset() {
 while(calc.stack.size != 0) {
 pop();
 }
 push(0);
 calc.stack.decimal = 0;
}
function updateDisplay() {
 var disp = "";
 for (var i = 0; i < calc.stack.size; i++) {
 if (typeof calc.stack.values[i] == "number") {
 disp += " " + calc.stack.values[i];
 } else {
 disp += " " + calc.stack.values[i].label;
 }
 }
 d3.select("#display")
 .attr("value", "" + disp);
}
function resolve() {
 var val = pop();
 while (!cleared()) {
 var op = pop();
 var left = pop();
 var eq = op.operate(left,val);
 console.log("Compute: " + left + " " + op.label + " " + val + " -> " + eq);
 val = eq;
 }
 push(val);
 calc.stack.decimal = 0;
}
function unaryOp(fn) {
 // unary expects a value at stack-top.
 if ("number" != typeof peek()) {
 console.log("Evict " + pop());
 }
 push(fn(pop()));
}
function binaryOp(fn) {
 resolve();
 push(fn);
 push(0);
}
function digit(val) {
 if (calc.stack.decimal === 0) {
 return push(pop() * 10 + val);
 }
 calc.stack.decimal /= 10.0;
 var current = pop();
 var n = calc.stack.decimal * val;
 var tp = n + current;
 //console.log("Current " + current + " decimal " + calc.stack.decimal + "... new " + n + " to push " + tp);
 return push(tp);
}
function pressButton(name) {
 var detail = calc.buttons[name];
 //console.log("Button pressed: " + name);
 detail.action();
 d3.select("#" + name)
 .style("background-color", "red")
 .transition()
 .style("background-color", detail.color);
 updateDisplay();
}
function keyHook(kevent) {
 var k = kevent.keyCode || kevent.which;
 if (calc.keys[k]) {
 console.log("Keypress code " + kevent.keyCode + " which: " + kevent.which + " linked to " + calc.keys[k]);
 pressButton(calc.keys[k]);
 return;
 }
 console.log("Unrecognized Keypress " + k);
}
function setup() {
 
 var buttons = d3.select("#buttons");
 
 calc.layout.forEach(function(row) {
 row.buttons.forEach(function(bid){
 var b = calc.buttons[bid];
 b.id = bid;
 b.color = b.color || "darkslategray";
 var title = "Key:";
 b.key.forEach(function(k){
 var kname = String.fromCharCode(k);
 title = title + " "'" + kname;kname + "'";
 calc.keys[k] = function() {
  pressButton(bid);
 };bid;
 });
 buttons.append("input")
 .classed("button",true)
 .style("background-color", b.color)
 .attr("title", title)
 .attr("type", "button")
 .attr("id", bid)
 .attr("value", b.label)
 .attr("onclick", "pressButton(this.id);");
 });
 buttons.append("br");
 });
 window.onkeypress=keyHook;
 reset();
 updateDisplay();
}
setup();
 #display {
 text-align: right;
 font: bold 50px monospace;
 background-color: darkgray;
 width: 100%;
 box-sizing: border-box;
 }
 .button {
 font: bold 35px monospace;
 color: white;
 margin: 10px;
 width: 3em;
 }
 table {
 border-collapse: collapse;
 margin: 0 auto
 }
 table.main {
 background-color: lightgray;
 border: 2px solid darkgray;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
 <table class="main">
 <caption>CodeReview Calculator</caption> 
 <tr>
 <td>
 <input id="display" value="0" size="15" readonly="true" >
 </td>
 </tr>
 <tr>
 <td>
 <div id="buttons"></div>
 </td>
 </tr>
 
 </table>
var calc = {
 stack: {
 values: new Array(1024),
 size: 0,
 decimal: 0
 },
 
 display: {
 value: 0.0,
 integral: true
 },
 
 layout: [
 {
 compact: false,
 buttons: ["seven", "eight", "nine", "root", "clear"]
 },
 {
 compact: false,
 buttons: ["four", "five", "six", "times", "divide"]
 },
 {
 compact: false,
 buttons: ["one", "two", "three", "plus", "minus"]
 },
 {
 compact: false,
 buttons: ["zero", "point", "plusminus", "exp", "equals"]
 },
 ],
 keys: {},
 buttons: {
 zero: {
 action: function() {return digit(0);},
 label: "0",
 key: [48]
 },
 one: {
 action: function() {return digit(1);},
 label: "1",
 key: [49]
 },
 two: {
 action: function() {return digit(2);},
 label: "2",
 key: [50]
 },
 three: {
 action: function() {return digit(3);},
 label: "3",
 key: [51]
 },
 four: {
 action: function() {return digit(4);},
 label: "4",
 key: [52]
 },
 five: {
 action: function() {return digit(5);},
 label: "5",
 key: [53]
 },
 six: {
 action: function() {return digit(6);},
 label: "6",
 key: [54]
 },
 seven: {
 action: function() {return digit(7);},
 label: "7",
 key: [55]
 },
 eight: {
 action: function() {return digit(8);},
 label: "8",
 key: [56]
 },
 nine: {
 action: function() {return digit(9);},
 label: "9",
 key: [57]
 },
 point: {
 action: function() {
 if (calc.stack.decimal === 0) {
 calc.stack.decimal = 1;
 }
 return peek();
 },
 label: ".",
 key: [46]
 },
 root: {
 action: function() {
 return unaryOp(function(val) {
 return Math.sqrt(val);
 });
 },
 label: "r\u221A",
 color: "black",
 key: [114]
 },
 plusminus: {
 action: function() {
 return unaryOp(function(val) {
 return -val;
 });
 },
 label: "~\u00B1",
 key: [126]
 },
 plus: {
 action: function() {
 return binaryOp(calc.buttons.plus);
 },
 operate: function(left, right) {
 return left + right;
 },
 label: "+",
 color: "black",
 key: [43]
 },
 minus: {
 action: function() {
 return binaryOp(calc.buttons.minus);
 },
 operate: function(left, right) {
 return left - right;
 },
 label: "-",
 color: "black",
 key: [45]
 },
 times: {
 action: function() {
 return binaryOp(calc.buttons.times);
 },
 operate: function(left, right) {
 return left * right;
 },
 label: "*",
 color: "black",
 key: [42,120]
 },
 divide: {
 action: function() {
 return binaryOp(calc.buttons.divide);
 },
 operate: function(left, right) {
 return left / right;
 },
 label: "/\u00F7",
 color: "black",
 key: [47]
 },
 exp: {
 action: function() {
 return binaryOp(calc.buttons.exp);
 },
 operate: function(left, right) {
 return Math.pow(left , right);
 },
 label: "EXP^",
 color: "black",
 key: [94]
 },
 equals: {
 action: function() {resolve();},
 label: "=",
 color: "black",
 key: [61,13]
 },
 clear: {
 action: function() {reset();},
 label: "del",
 color: "orange",
 key: [127]
 }
 }
 
};
function peek() {
 if (calc.stack.size == 0) {
 throw "Empty stack peek()";
 }
 return calc.stack.values[calc.stack.size - 1];
}
function pop() {
 if (calc.stack.size == 0) {
 throw "Empty stack pop()";
 }
 calc.stack.size--;
 var val = calc.stack.values[calc.stack.size];
 calc.stack.values[calc.stack.size] = null;
 return val;
}
function push(val) {
 calc.stack.values[calc.stack.size++] = val;
 return val;
}
function cleared() {
 return calc.stack.size == 0;
}
function reset() {
 while(calc.stack.size != 0) {
 pop();
 }
 push(0);
 calc.stack.decimal = 0;
}
function updateDisplay() {
 var disp = "";
 for (var i = 0; i < calc.stack.size; i++) {
 if (typeof calc.stack.values[i] == "number") {
 disp += " " + calc.stack.values[i];
 } else {
 disp += " " + calc.stack.values[i].label;
 }
 }
 d3.select("#display")
 .attr("value", "" + disp);
}
function resolve() {
 var val = pop();
 while (!cleared()) {
 var op = pop();
 var left = pop();
 var eq = op.operate(left,val);
 console.log("Compute: " + left + " " + op.label + " " + val + " -> " + eq);
 val = eq;
 }
 push(val);
 calc.stack.decimal = 0;
}
function unaryOp(fn) {
 // unary expects a value at stack-top.
 if ("number" != typeof peek()) {
 console.log("Evict " + pop());
 }
 push(fn(pop()));
}
function binaryOp(fn) {
 resolve();
 push(fn);
 push(0);
}
function digit(val) {
 if (calc.stack.decimal === 0) {
 return push(pop() * 10 + val);
 }
 calc.stack.decimal /= 10.0;
 var current = pop();
 var n = calc.stack.decimal * val;
 var tp = n + current;
 //console.log("Current " + current + " decimal " + calc.stack.decimal + "... new " + n + " to push " + tp);
 return push(tp);
}
function pressButton(name) {
 var detail = calc.buttons[name];
 //console.log("Button pressed: " + name);
 detail.action();
 d3.select("#" + name)
 .style("background-color", "red")
 .transition()
 .style("background-color", detail.color);
 updateDisplay();
}
function keyHook(kevent) {
 var k = kevent.keyCode || kevent.which;
 if (calc.keys[k]) {
 calc.keys[k]();
 return;
 }
 console.log("Unrecognized Keypress " + k);
}
function setup() {
 
 var buttons = d3.select("#buttons");
 
 calc.layout.forEach(function(row) {
 row.buttons.forEach(function(bid){
 var b = calc.buttons[bid];
 b.id = bid;
 b.color = b.color || "darkslategray";
 var title = "Key:";
 b.key.forEach(function(k){
 var kname = String.fromCharCode(k);
 title = title + " " + kname;
 calc.keys[k] = function() {
  pressButton(bid);
 };
 });
 buttons.append("input")
 .classed("button",true)
 .style("background-color", b.color)
 .attr("title", title)
 .attr("type", "button")
 .attr("id", bid)
 .attr("value", b.label)
 .attr("onclick", "pressButton(this.id);");
 });
 buttons.append("br");
 });
 window.onkeypress=keyHook;
 reset();
 updateDisplay();
}
setup();
 #display {
 text-align: right;
 font: bold 50px monospace;
 background-color: darkgray;
 width: 100%;
 box-sizing: border-box;
 }
 .button {
 font: bold 35px monospace;
 color: white;
 margin: 10px;
 width: 3em;
 }
 table {
 border-collapse: collapse;
 margin: 0 auto
 }
 table.main {
 background-color: lightgray;
 border: 2px solid darkgray;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
 <table class="main">
 <caption>CodeReview Calculator</caption> 
 <tr>
 <td>
 <input id="display" value="0" size="15" readonly="true" >
 </td>
 </tr>
 <tr>
 <td>
 <div id="buttons"></div>
 </td>
 </tr>
 
 </table>
var calc = {
 stack: {
 values: new Array(1024),
 size: 0,
 decimal: 0
 },
 
 display: {
 value: 0.0,
 integral: true
 },
 
 layout: [
 {
 compact: false,
 buttons: ["seven", "eight", "nine", "root", "clear"]
 },
 {
 compact: false,
 buttons: ["four", "five", "six", "times", "divide"]
 },
 {
 compact: false,
 buttons: ["one", "two", "three", "plus", "minus"]
 },
 {
 compact: false,
 buttons: ["zero", "point", "plusminus", "exp", "equals"]
 },
 ],
 keys: {},
 buttons: {
 zero: {
 action: function() {return digit(0);},
 label: "0",
 key: [48]
 },
 one: {
 action: function() {return digit(1);},
 label: "1",
 key: [49]
 },
 two: {
 action: function() {return digit(2);},
 label: "2",
 key: [50]
 },
 three: {
 action: function() {return digit(3);},
 label: "3",
 key: [51]
 },
 four: {
 action: function() {return digit(4);},
 label: "4",
 key: [52]
 },
 five: {
 action: function() {return digit(5);},
 label: "5",
 key: [53]
 },
 six: {
 action: function() {return digit(6);},
 label: "6",
 key: [54]
 },
 seven: {
 action: function() {return digit(7);},
 label: "7",
 key: [55]
 },
 eight: {
 action: function() {return digit(8);},
 label: "8",
 key: [56]
 },
 nine: {
 action: function() {return digit(9);},
 label: "9",
 key: [57]
 },
 point: {
 action: function() {
 if (calc.stack.decimal === 0) {
 calc.stack.decimal = 1;
 }
 return peek();
 },
 label: ".",
 key: [46]
 },
 root: {
 action: function() {
 return unaryOp(function(val) {
 return Math.sqrt(val);
 });
 },
 label: "r\u221A",
 color: "black",
 key: [114]
 },
 plusminus: {
 action: function() {
 return unaryOp(function(val) {
 return -val;
 });
 },
 label: "~\u00B1",
 key: [126]
 },
 plus: {
 action: function() {
 return binaryOp(calc.buttons.plus);
 },
 operate: function(left, right) {
 return left + right;
 },
 label: "+",
 color: "black",
 key: [43]
 },
 minus: {
 action: function() {
 return binaryOp(calc.buttons.minus);
 },
 operate: function(left, right) {
 return left - right;
 },
 label: "-",
 color: "black",
 key: [45]
 },
 times: {
 action: function() {
 return binaryOp(calc.buttons.times);
 },
 operate: function(left, right) {
 return left * right;
 },
 label: "*",
 color: "black",
 key: [42,120]
 },
 divide: {
 action: function() {
 return binaryOp(calc.buttons.divide);
 },
 operate: function(left, right) {
 return left / right;
 },
 label: "/\u00F7",
 color: "black",
 key: [47]
 },
 exp: {
 action: function() {
 return binaryOp(calc.buttons.exp);
 },
 operate: function(left, right) {
 return Math.pow(left , right);
 },
 label: "EXP^",
 color: "black",
 key: [94]
 },
 equals: {
 action: function() {resolve();},
 label: "=",
 color: "black",
 key: [61,13]
 },
 clear: {
 action: function() {reset();},
 label: "del",
 color: "orange",
 key: [32, 127]
 }
 }
 
};
function peek() {
 if (calc.stack.size == 0) {
 throw "Empty stack peek()";
 }
 return calc.stack.values[calc.stack.size - 1];
}
function pop() {
 if (calc.stack.size == 0) {
 throw "Empty stack pop()";
 }
 calc.stack.size--;
 var val = calc.stack.values[calc.stack.size];
 calc.stack.values[calc.stack.size] = null;
 return val;
}
function push(val) {
 calc.stack.values[calc.stack.size++] = val;
 return val;
}
function cleared() {
 return calc.stack.size == 0;
}
function reset() {
 while(calc.stack.size != 0) {
 pop();
 }
 push(0);
 calc.stack.decimal = 0;
}
function updateDisplay() {
 var disp = "";
 for (var i = 0; i < calc.stack.size; i++) {
 if (typeof calc.stack.values[i] == "number") {
 disp += " " + calc.stack.values[i];
 } else {
 disp += " " + calc.stack.values[i].label;
 }
 }
 d3.select("#display")
 .attr("value", "" + disp);
}
function resolve() {
 var val = pop();
 while (!cleared()) {
 var op = pop();
 var left = pop();
 var eq = op.operate(left,val);
 console.log("Compute: " + left + " " + op.label + " " + val + " -> " + eq);
 val = eq;
 }
 push(val);
 calc.stack.decimal = 0;
}
function unaryOp(fn) {
 // unary expects a value at stack-top.
 if ("number" != typeof peek()) {
 console.log("Evict " + pop());
 }
 push(fn(pop()));
}
function binaryOp(fn) {
 resolve();
 push(fn);
 push(0);
}
function digit(val) {
 if (calc.stack.decimal === 0) {
 return push(pop() * 10 + val);
 }
 calc.stack.decimal /= 10.0;
 var current = pop();
 var n = calc.stack.decimal * val;
 var tp = n + current;
 //console.log("Current " + current + " decimal " + calc.stack.decimal + "... new " + n + " to push " + tp);
 return push(tp);
}
function pressButton(name) {
 var detail = calc.buttons[name];
 //console.log("Button pressed: " + name);
 detail.action();
 d3.select("#" + name)
 .style("background-color", "red")
 .transition()
 .style("background-color", detail.color);
 updateDisplay();
}
function keyHook(kevent) {
 var k = kevent.keyCode || kevent.which;
 if (calc.keys[k]) {
 console.log("Keypress code " + kevent.keyCode + " which: " + kevent.which + " linked to " + calc.keys[k]);
 pressButton(calc.keys[k]);
 return;
 }
 console.log("Unrecognized Keypress " + k);
}
function setup() {
 
 var buttons = d3.select("#buttons");
 
 calc.layout.forEach(function(row) {
 row.buttons.forEach(function(bid){
 var b = calc.buttons[bid];
 b.id = bid;
 b.color = b.color || "darkslategray";
 var title = "Key:";
 b.key.forEach(function(k){
 var kname = String.fromCharCode(k);
 title = title + " '" + kname + "'";
 calc.keys[k] = bid;
 });
 buttons.append("input")
 .classed("button",true)
 .style("background-color", b.color)
 .attr("title", title)
 .attr("type", "button")
 .attr("id", bid)
 .attr("value", b.label)
 .attr("onclick", "pressButton(this.id);");
 });
 buttons.append("br");
 });
 window.onkeypress=keyHook;
 reset();
 updateDisplay();
}
setup();
 #display {
 text-align: right;
 font: bold 50px monospace;
 background-color: darkgray;
 width: 100%;
 box-sizing: border-box;
 }
 .button {
 font: bold 35px monospace;
 color: white;
 margin: 10px;
 width: 3em;
 }
 table {
 border-collapse: collapse;
 margin: 0 auto
 }
 table.main {
 background-color: lightgray;
 border: 2px solid darkgray;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
 <table class="main">
 <caption>CodeReview Calculator</caption> 
 <tr>
 <td>
 <input id="display" value="0" size="15" readonly="true" >
 </td>
 </tr>
 <tr>
 <td>
 <div id="buttons"></div>
 </td>
 </tr>
 
 </table>
add standalone link
Source Link
rolfl
  • 98.1k
  • 17
  • 219
  • 419

Note: I found that the snippet does odd things with key-presses occasionally: here is the standalone 'original' version that I 'hacked' to make in to a snippet: standalone calc.html . I find the experience of the standalone version is better.

Note: I found that the snippet does odd things with key-presses occasionally: here is the standalone 'original' version that I 'hacked' to make in to a snippet: standalone calc.html . I find the experience of the standalone version is better.

Tweeted twitter.com/#!/StackCodeReview/status/586719639415218176
fix issue with tabs/space combination. all spaces now.
Source Link
rolfl
  • 98.1k
  • 17
  • 219
  • 419
Loading
reset decimal point after resolve
Source Link
rolfl
  • 98.1k
  • 17
  • 219
  • 419
Loading
Source Link
rolfl
  • 98.1k
  • 17
  • 219
  • 419
Loading
default

AltStyle によって変換されたページ (->オリジナル) /