Loading calc.html +25 −1 Original line number Diff line number Diff line Loading @@ -251,7 +251,7 @@ <td title="sum all numbers in stack" data-bt-name="sum-all">Σ</td> <td title="square root (Q)" data-bt-name="square-root">√</td> <td title="power (^)" data-bt-name="power">^</td> <td title="round (R)" data-bt-name="round">ROUND</td> <td title="round (R)" data-bt-name="round">ROND</td> </tr> </table> </div> Loading Loading @@ -439,6 +439,29 @@ console.assert(m.numberStack.length === 2); console.assert(m.numberStack[0] === -9); console.assert(m.numberStack[1] === 29 * 2); m.sendKey("backspace"); m.sendKey("backspace"); // no number on stack now. // test round, sqrt, power m.sendKey("num1"); m.sendKey("dot"); m.sendKey("num2"); m.sendKey("return"); m.sendKey("round"); console.assert(m.numberStack.length === 1); console.assert(m.numberStack[0] === 1); m.sendKey("num9"); m.sendKey("times"); m.sendKey("square-root"); console.assert(m.numberStack.length === 1); console.assert(m.numberStack[0] === 3); m.sendKey("num2"); m.sendKey("power"); console.assert(m.numberStack.length === 1); console.assert(m.numberStack[0] === 9); }; const rpnTestBackspaceOperator = function () { const m = new fsm.RPNCalculator(); Loading Loading @@ -799,6 +822,7 @@ "^": "power", "Q": "square-root", "U": "undo", "R": "round", // below is not from emacs calc "q": "backspace", }; Loading fsm.js +50 −23 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ var fsm = function () { undo: "undo", "return": "return", change_sign: "change-sign", round: "round", }; // map keyName to short key name. const shortKeyNames = { Loading @@ -129,6 +130,7 @@ var fsm = function () { "undo": "undo", "return": "ret", "change-sign": "chs", round: "rond", }; const msgTooFewElementsOnStack = "Too few elements on stack"; const trailHistorySize = 100; Loading Loading @@ -174,6 +176,39 @@ var fsm = function () { this.clearErrorMsg = function () { this.lastError = null; }; /** * do calculator for an unary operator. * this handles not enough element in stack problem. * * it may modify this.numberStack and this.lastError. * * Return true on success, return false on failure. * You may do early return if this function fails. this.lastError will * be set when false is returned. */ this.doUnaryOperator = function (keyName, func) { this.snapshots.push(this.save()); const num = this.numberStack.pop(); if (num === undefined) { this.setErrorMsg(msgTooFewElementsOnStack); this.snapshots.pop(); return false; } const result = func(num); if (result === undefined || result === null || result === false) { // this does consume the number. Just don't push result // to number stack. if (this.lastError === null) { this.setErrorMsg("result of this operator is undefined or null"); } return false; } this.numberStack.push(result); this.trail.push([shortKeyName(keyName), result]); return true; }; /** * do calculation for a binary operator. * this handles not enough element in stack problem. Loading Loading @@ -229,7 +264,7 @@ var fsm = function () { keyNames.change_sign, keyNames.plus, keyNames.minus, keyNames.times, keyNames.divide, keyNames.swap, keyNames.undo, keyNames.sum_all, keyNames.square_root, keyNames.power, keyNames.power, keyNames.round, ]; const isOperator = function (keyName) { return operatorKeyList.indexOf(keyName) !== -1; Loading Loading @@ -341,35 +376,27 @@ var fsm = function () { } else { switch (keyName) { case keyNames.change_sign: this.snapshots.push(this.save()); num = this.numberStack.pop(); if (num === undefined) { this.setErrorMsg(msgTooFewElementsOnStack); this.snapshots.pop(); if (! this.doUnaryOperator(keyName, (num) => -num)) { return; } this.numberStack.push(-num); debug("trail push " + [keyName, shortKeyName(keyName), -num]); this.trail.push([shortKeyName(keyName), -num]); break; case keyNames.square_root: this.snapshots.push(this.save()); num = this.numberStack.pop(); if (num === undefined) { this.setErrorMsg(msgTooFewElementsOnStack); this.snapshots.pop(); case keyNames.round: if (! this.doUnaryOperator(keyName, (num) => Math.round(num))) { return; } break; case keyNames.square_root: if (! this.doUnaryOperator(keyName, (num) => { if (num < 0) { // consumes the number and show error to user. this.setErrorMsg("square root of negative not supported"); this.snapshots.pop(); return; } r = Math.pow(num, 0.5); this.numberStack.push(r); debug("trail push " + [keyName, shortKeyName(keyName), r]); this.trail.push([shortKeyName(keyName), r]); return Math.pow(num, 0.5); })) { return; } break; case keyNames.plus: if (! this.doBinaryOperator(keyName, (lhs, rhs) => lhs + rhs)) { Loading operational +5 −3 Original line number Diff line number Diff line Loading @@ -28,16 +28,18 @@ Time-stamp: <2017-01-28> - update fsm.js keyNames, shortKeyNames, operatorKeyList. - add operator handling code in fsm.js. If you can use this.doBinaryOperator(), use that is easier. If you can use this.doUnaryOperator() or this.doBinaryOperator(), use that is easier. General steps: - take snapshot. - manipulate numberStack. - if there is error, call this.setErrorMsg(), pop snapshot and do early return. - if there is no error, push result in numberStack, push new op and result to this.trail. - add event handler in calc.html. set data-bt-name on td. if there is a hotkey, handle that hotkey. if there is a hotkey, handle that hotkey. just update keyMap map. ** 2017-01-26 design data structure for the calculator. - this can be written and tested independently from the web UI. Loading Loading @@ -127,7 +129,6 @@ the parent DOM. * current :entry: ** ** 2017-01-28 add a round operator. ** 2017-01-27 add some visual feedback when clicking a button. like in material design. ** 2017-01-27 UI problem: when input a very large number, #number-display and keyboard will grow. should set a max-width on #number-display. Loading @@ -135,6 +136,7 @@ should set a max-width on #number-display. overflow: auto doesn't work with <td> in firefox. * done :entry: ** 2017-01-28 add a round operator. ** 2017-01-28 try it on mate8. I can transfer files using bluetooth. Loading Loading
calc.html +25 −1 Original line number Diff line number Diff line Loading @@ -251,7 +251,7 @@ <td title="sum all numbers in stack" data-bt-name="sum-all">Σ</td> <td title="square root (Q)" data-bt-name="square-root">√</td> <td title="power (^)" data-bt-name="power">^</td> <td title="round (R)" data-bt-name="round">ROUND</td> <td title="round (R)" data-bt-name="round">ROND</td> </tr> </table> </div> Loading Loading @@ -439,6 +439,29 @@ console.assert(m.numberStack.length === 2); console.assert(m.numberStack[0] === -9); console.assert(m.numberStack[1] === 29 * 2); m.sendKey("backspace"); m.sendKey("backspace"); // no number on stack now. // test round, sqrt, power m.sendKey("num1"); m.sendKey("dot"); m.sendKey("num2"); m.sendKey("return"); m.sendKey("round"); console.assert(m.numberStack.length === 1); console.assert(m.numberStack[0] === 1); m.sendKey("num9"); m.sendKey("times"); m.sendKey("square-root"); console.assert(m.numberStack.length === 1); console.assert(m.numberStack[0] === 3); m.sendKey("num2"); m.sendKey("power"); console.assert(m.numberStack.length === 1); console.assert(m.numberStack[0] === 9); }; const rpnTestBackspaceOperator = function () { const m = new fsm.RPNCalculator(); Loading Loading @@ -799,6 +822,7 @@ "^": "power", "Q": "square-root", "U": "undo", "R": "round", // below is not from emacs calc "q": "backspace", }; Loading
fsm.js +50 −23 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ var fsm = function () { undo: "undo", "return": "return", change_sign: "change-sign", round: "round", }; // map keyName to short key name. const shortKeyNames = { Loading @@ -129,6 +130,7 @@ var fsm = function () { "undo": "undo", "return": "ret", "change-sign": "chs", round: "rond", }; const msgTooFewElementsOnStack = "Too few elements on stack"; const trailHistorySize = 100; Loading Loading @@ -174,6 +176,39 @@ var fsm = function () { this.clearErrorMsg = function () { this.lastError = null; }; /** * do calculator for an unary operator. * this handles not enough element in stack problem. * * it may modify this.numberStack and this.lastError. * * Return true on success, return false on failure. * You may do early return if this function fails. this.lastError will * be set when false is returned. */ this.doUnaryOperator = function (keyName, func) { this.snapshots.push(this.save()); const num = this.numberStack.pop(); if (num === undefined) { this.setErrorMsg(msgTooFewElementsOnStack); this.snapshots.pop(); return false; } const result = func(num); if (result === undefined || result === null || result === false) { // this does consume the number. Just don't push result // to number stack. if (this.lastError === null) { this.setErrorMsg("result of this operator is undefined or null"); } return false; } this.numberStack.push(result); this.trail.push([shortKeyName(keyName), result]); return true; }; /** * do calculation for a binary operator. * this handles not enough element in stack problem. Loading Loading @@ -229,7 +264,7 @@ var fsm = function () { keyNames.change_sign, keyNames.plus, keyNames.minus, keyNames.times, keyNames.divide, keyNames.swap, keyNames.undo, keyNames.sum_all, keyNames.square_root, keyNames.power, keyNames.power, keyNames.round, ]; const isOperator = function (keyName) { return operatorKeyList.indexOf(keyName) !== -1; Loading Loading @@ -341,35 +376,27 @@ var fsm = function () { } else { switch (keyName) { case keyNames.change_sign: this.snapshots.push(this.save()); num = this.numberStack.pop(); if (num === undefined) { this.setErrorMsg(msgTooFewElementsOnStack); this.snapshots.pop(); if (! this.doUnaryOperator(keyName, (num) => -num)) { return; } this.numberStack.push(-num); debug("trail push " + [keyName, shortKeyName(keyName), -num]); this.trail.push([shortKeyName(keyName), -num]); break; case keyNames.square_root: this.snapshots.push(this.save()); num = this.numberStack.pop(); if (num === undefined) { this.setErrorMsg(msgTooFewElementsOnStack); this.snapshots.pop(); case keyNames.round: if (! this.doUnaryOperator(keyName, (num) => Math.round(num))) { return; } break; case keyNames.square_root: if (! this.doUnaryOperator(keyName, (num) => { if (num < 0) { // consumes the number and show error to user. this.setErrorMsg("square root of negative not supported"); this.snapshots.pop(); return; } r = Math.pow(num, 0.5); this.numberStack.push(r); debug("trail push " + [keyName, shortKeyName(keyName), r]); this.trail.push([shortKeyName(keyName), r]); return Math.pow(num, 0.5); })) { return; } break; case keyNames.plus: if (! this.doBinaryOperator(keyName, (lhs, rhs) => lhs + rhs)) { Loading
operational +5 −3 Original line number Diff line number Diff line Loading @@ -28,16 +28,18 @@ Time-stamp: <2017-01-28> - update fsm.js keyNames, shortKeyNames, operatorKeyList. - add operator handling code in fsm.js. If you can use this.doBinaryOperator(), use that is easier. If you can use this.doUnaryOperator() or this.doBinaryOperator(), use that is easier. General steps: - take snapshot. - manipulate numberStack. - if there is error, call this.setErrorMsg(), pop snapshot and do early return. - if there is no error, push result in numberStack, push new op and result to this.trail. - add event handler in calc.html. set data-bt-name on td. if there is a hotkey, handle that hotkey. if there is a hotkey, handle that hotkey. just update keyMap map. ** 2017-01-26 design data structure for the calculator. - this can be written and tested independently from the web UI. Loading Loading @@ -127,7 +129,6 @@ the parent DOM. * current :entry: ** ** 2017-01-28 add a round operator. ** 2017-01-27 add some visual feedback when clicking a button. like in material design. ** 2017-01-27 UI problem: when input a very large number, #number-display and keyboard will grow. should set a max-width on #number-display. Loading @@ -135,6 +136,7 @@ should set a max-width on #number-display. overflow: auto doesn't work with <td> in firefox. * done :entry: ** 2017-01-28 add a round operator. ** 2017-01-28 try it on mate8. I can transfer files using bluetooth. Loading