Created
July 10, 2020 23:02
-
-
Save GiancarlosIO/5d8673c5a155df572f37a6098e3e1301 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const not = fn => (...args) => !fn(...args); | |
const isZero = (context, event) => event.key === 0; | |
const isNotZero = not(isZero); | |
const isMinus = (context, event) => event.operator === '-'; | |
const isNotMinus = not(isMinus); | |
const divideByZero = (context, event) => { | |
return ( | |
(!context.operand2 || context.operand2 === '0.') && context.operator === '/' | |
); | |
}; | |
const notDivideByZero = not(divideByZero); | |
function doMath(operand1, operand2, operator) { | |
switch (operator) { | |
case '+': | |
return +operand1 + +operand2; | |
case '-': | |
return +operand1 - +operand2; | |
case '/': | |
return +operand1 / +operand2; | |
case 'x': | |
return +operand1 * +operand2; | |
default: | |
return Infinity; | |
} | |
} | |
const calMachine = Machine( | |
{ | |
id: 'calcMachine', | |
context: { | |
display: '0.', | |
operand1: undefined, | |
operand2: undefined, | |
operator: undefined, | |
}, | |
strict: true, | |
initial: 'start', | |
on: { | |
CLEAR_EVERYTHING: { | |
target: '.start', | |
actions: ['reset'], | |
}, | |
}, | |
states: { | |
start: { | |
on: { | |
NUMBER: [ | |
{ | |
cond: 'isZero', | |
target: 'operand1.zero', | |
actions: ['defaultReadout'], | |
}, | |
{ | |
cond: 'isNotZero', | |
target: 'operand1.before_decimal_point', | |
actions: ['setReadoutNum'], | |
}, | |
], | |
OPERATOR: { | |
cond: 'isMinus', | |
target: 'negative_number', | |
actions: ['startNegativeNumber'], | |
}, | |
DECIMAL_POINT: { | |
target: 'operand1.after_decimal_point', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
}, | |
operand1: { | |
on: { | |
OPERATOR: { | |
target: 'operator_entered', | |
actions: ['recordOperator'], | |
}, | |
PERCENTAGE: { | |
target: 'result', | |
actions: ['storeResultAsOperand2', 'computePercentage'], | |
}, | |
CLEAR_ENTRY: { | |
target: 'operand1', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
initial: 'zero', | |
states: { | |
zero: { | |
on: { | |
NUMBER: { | |
target: 'before_decimal_point', | |
actions: 'setReadoutNum', | |
}, | |
DECIMAL_POINT: 'after_decimal_point', | |
}, | |
}, | |
before_decimal_point: { | |
on: { | |
NUMBER: { | |
target: 'before_decimal_point', | |
actions: ['appendNumBeforeDecimal'], | |
}, | |
DECIMAL_POINT: 'after_decimal_point', | |
}, | |
}, | |
after_decimal_point: { | |
on: { | |
NUMBER: { | |
target: 'after_decimal_point', | |
actions: ['appendNumAfterDecimal'], | |
}, | |
}, | |
}, | |
}, | |
}, | |
negative_number: { | |
on: { | |
NUMBER: [ | |
{ | |
cond: 'isZero', | |
target: 'operand1.zero', | |
actions: ['defaultNegativeReadout'], | |
}, | |
{ | |
cond: 'isNotZero', | |
target: 'operand1.before_decimal_point', | |
actions: ['setNegativeReadoutNum'], | |
}, | |
], | |
DECIMAL_POINT: { | |
target: 'operand1.after_decimal_point', | |
actions: ['defaultNegativeReadout'], | |
}, | |
CLEAR_ENTRY: { | |
target: 'start', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
}, | |
operator_entered: { | |
on: { | |
OPERATOR: [ | |
{ | |
cond: 'isNotMinus', | |
target: 'operator_entered', | |
actions: 'setOperator', | |
}, | |
{ | |
cond: 'isMinus', | |
target: 'negative_number_2', | |
actions: ['startNegativeNumber'], | |
}, | |
], | |
NUMBER: [ | |
{ | |
cond: 'isZero', | |
target: 'operand2.zero', | |
actions: ['defaultReadout', 'saveOperand2'], | |
}, | |
{ | |
cond: 'isNotZero', | |
target: 'operand2.before_decimal_point', | |
actions: ['setReadoutNum', 'saveOperand2'], | |
}, | |
], | |
DECIMAL_POINT: { | |
target: 'operand2.after_decimal_point', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
}, | |
operand2: { | |
on: { | |
OPERATOR: [ | |
{ | |
cond: 'notDivideByZero', | |
target: 'operator_entered', | |
actions: [ | |
'storeResultAsOperand2', | |
'compute', | |
'storeResultAsOperand1', | |
'setOperator', | |
], | |
}, | |
{ | |
target: 'alert', | |
}, | |
], | |
EQUALS: [ | |
{ | |
cond: 'notDivideByZero', | |
target: 'result', | |
actions: ['storeResultAsOperand2', 'compute'], | |
}, | |
{ | |
target: 'alert', | |
}, | |
], | |
CLEAR_ENTRY: { | |
target: 'operand2.zero', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
initial: 'zero', | |
states: { | |
zero: { | |
on: { | |
NUMBER: { | |
target: 'before_decimal_point', | |
actions: ['setReadoutNum'], | |
}, | |
DECIMAL_POINT: 'after_decimal_point', | |
}, | |
}, | |
before_decimal_point: { | |
on: { | |
NUMBER: { | |
target: 'before_decimal_point', | |
actions: ['appendNumBeforeDecimal'], | |
}, | |
DECIMAL_POINT: 'after_decimal_point', | |
}, | |
}, | |
after_decimal_point: { | |
on: { | |
NUMBER: { | |
target: 'after_decimal_point', | |
actions: 'appendNumAfterDecimal', | |
}, | |
}, | |
}, | |
}, | |
}, | |
negative_number_2: { | |
on: { | |
NUMBER: [ | |
{ | |
cond: 'isZero', | |
target: 'operand2.zero', | |
actions: ['defaultNegativeReadout'], | |
}, | |
{ | |
cond: 'isNotZero', | |
target: 'operand2.before_decimal_point', | |
actions: ['setNegativeReadoutNum'], | |
}, | |
], | |
DECIMAL_POINT: { | |
target: 'operand2.after_decimal_point', | |
actions: ['defaultNegativeReadout'], | |
}, | |
CLEAR_ENTRY: { | |
target: 'operator_entered', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
}, | |
result: { | |
on: { | |
NUMBER: [ | |
{ | |
cond: 'isZero', | |
target: 'operand1', | |
actions: ['defaultReadout'], | |
}, | |
{ | |
cond: 'isNotZero', | |
target: 'operand1.before_decimal_point', | |
actions: ['setReadoutNum'], | |
}, | |
], | |
PERCENTAGE: { | |
target: 'result', | |
actions: ['storeResultAsOperand2', 'computePercentage'], | |
}, | |
OPERATOR: { | |
target: 'operator_entered', | |
actions: ['storeResultAsOperand1', 'recordOperator'], | |
}, | |
CLEAR_ENTRY: { | |
target: 'start', | |
actions: ['defaultReadout'], | |
}, | |
}, | |
}, | |
alert: { | |
invoke: { | |
src: (context, event) => () => { | |
// eslint-disable-next-line no-alert | |
alert('Cannot divide by zero!'); | |
return Promise.resolve(); | |
}, | |
onDone: { | |
target: 'start', | |
actions: ['reset'], | |
}, | |
}, | |
}, | |
}, | |
}, | |
{ | |
guards: { | |
isMinus, | |
isNotMinus, | |
isZero, | |
isNotZero, | |
notDivideByZero, | |
}, | |
actions: { | |
defaultReadout: assign({ | |
display: () => { | |
console.log('defaultReadout'); | |
return '0.'; | |
}, | |
}), | |
defaultNegativeReadout: assign({ | |
display: () => '-0.', | |
}), | |
appendNumBeforeDecimal: assign({ | |
display: (context, event) => { | |
// from '123.' => '1234.' | |
return `${context.display.slice(0, -1)}${event.key}.`; | |
}, | |
}), | |
appendNumAfterDecimal: assign({ | |
display: (context, event) => { | |
return `${context.display}${event.key}`; | |
}, | |
}), | |
setReadoutNum: assign({ | |
display: (context, event) => { | |
return `${event.key}.`; | |
}, | |
}), | |
setNegativeReadoutNum: assign({ | |
display: (context, event) => `-${event.key}.`, | |
}), | |
startNegativeNumber: assign({ | |
display: () => '-', | |
}), | |
recordOperator: assign({ | |
operand1: context => context.display, | |
operator: (context, event) => event.operator, | |
}), | |
setOperator: assign({ | |
operator: (context, event) => context.operator, | |
}), | |
computePercentage: assign({ | |
display: (context, event) => (+context.display / 100).toString(), | |
}), | |
compute: assign({ | |
display: (context, event) => { | |
const result = doMath( | |
context.operand1, | |
context.operand2, | |
context.operator, | |
); | |
console.log( | |
`doing calculation ${context.operand1} ${context.operator} ${context.operand2} = ${result}`, | |
); | |
return result.toString(); | |
}, | |
}), | |
storeResultAsOperand1: assign({ | |
operand1: context => context.display, | |
}), | |
storeResultAsOperand2: assign({ | |
operand2: context => context.display, | |
}), | |
saveOperand2: assign({ | |
operand2: (context, event) => context.display, | |
}), | |
reset: assign({ | |
display: () => '0.', | |
operand1: (context, event) => undefined, | |
operand2: () => undefined, | |
operator: () => undefined, | |
}), | |
}, | |
}, | |
); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment