Skip to content

Commit

Permalink
Implement hierarchical state machine logic for calculator. Tests pass…
Browse files Browse the repository at this point in the history
… and UI works.
  • Loading branch information
asolove committed Jul 4, 2017
1 parent d1fde99 commit c93fb84
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 7 deletions.
8 changes: 6 additions & 2 deletions motivation/naive/application.re
@@ -1,6 +1,10 @@
module NaiveView = View.Make(Updater.NaiveUpdater);
module StatechartView = View.Make(Calculator_state);

ReactDOMRe.renderToElementWithId <NaiveView /> "index";
ReactDOMRe.renderToElementWithId <StatechartView /> "index";

module NaiveTests = Tests.Make(Updater.NaiveUpdater);
NaiveTests.test();
NaiveTests.test();

module StatechartTests = Tests.Make(Calculator_state);
StatechartTests.test();
78 changes: 78 additions & 0 deletions motivation/naive/calculator_state.re
@@ -0,0 +1,78 @@
open Operations;

type doneState =
| Start string
| Result string
;

let name = "Statechart";

type t =
| Done doneState
| NegOp1
| Operand1 Operand.t
| OperatorEntered operation float
| NegOp2 operation float
| Operand2 Operand.t operation float
;

let showState = fun
| Done (Start s) => "Done Start " ^ s
| Done (Result s) => "Done Result " ^ s
| NegOp1 => "NegOp1"
| Operand1 o => "Operand1 " ^ Operand.showState o
| OperatorEntered _ _ => "OperationEntered"
| NegOp2 _ _ => "NegOp1"
| Operand2 _ _ _ => "Operand2"
;

let readout = fun
| Done (Start s) => s
| Done (Result s) => s
| NegOp1 => "-0."
| Operand1 o => Operand.readout o
| OperatorEntered _o _f => "0."
| NegOp2 _op _f => "-0."
| Operand2 o _op _f => Operand.readout o
;

let initialState = Done (Start "0.");

let percent s => string_of_float ((float_of_string s) /. 100.0);

let update state action =>
switch (state, action) {
| (_s, Cancel) => initialState

| (Done _, Digit d) => Operand1 (Operand.fromDigit d Positive)
| (Done _, Decimal) => Operand1 (Operand.fromDecimal Positive)
| (Done (Start _), Op Subtract) => NegOp1

| (Done (Result s), Op op) => OperatorEntered op (float_of_string s)

| (NegOp1, CancelEntry) => Done (Start "0.")
| (NegOp1, Digit d) => Operand1 (Operand.fromDigit d Negative)
| (NegOp1, Decimal) => Operand1 (Operand.fromDecimal Negative)

| (Operand1 _, CancelEntry) => Done (Start "0.")
| (Operand1 o1, Percent) => Done (Start (string_of_float (Operand.value o1 /. 100.0)))
| (Operand1 o1, Op op) => OperatorEntered op (Operand.value o1)
| (Operand1 o1, action) => Operand1 (Operand.update o1 action)

| (OperatorEntered op f, Op Subtract) => NegOp2 op f
| (OperatorEntered _op f, Op op) => OperatorEntered op f
| (OperatorEntered op f, Digit d) => Operand2 (Operand.fromDigit d Positive) op f

| (NegOp2 op f, CancelEntry) => OperatorEntered op f
| (NegOp2 op f, Digit d) => Operand2 (Operand.fromDigit d Negative) op f
| (NegOp2 op f, Decimal) => Operand2 (Operand.fromDecimal Negative) op f

| (Operand2 n2 op n1, Equal) => Done (Result (string_of_float (evaluate n1 op (Operand.value n2))))
| (Operand2 _n op n1, CancelEntry) => OperatorEntered op n1
| (Operand2 n2 op1 n1, Op op2) => OperatorEntered op2 (evaluate n1 op1 (Operand.value n2))
| (Operand2 n2 op n1, action) => Operand2 (Operand.update n2 action) op n1

| (state, action) => {
failwith ("Cannot perform action " ^ (showAction action) ^ " in state " ^ (showState state))
}
};
51 changes: 51 additions & 0 deletions motivation/naive/operand.re
@@ -0,0 +1,51 @@
open Operations;

type t =
| Zero sign
| BeforeDecimal sign string
| AfterDecimal sign string int
;

let signReadout = fun
| Positive => ""
| Negative => "-"
;

let readout = fun
| Zero sign => signReadout sign ^ "0."
| BeforeDecimal sign n => signReadout sign ^ n ^ "."
| AfterDecimal sign n2 n => (signReadout sign) ^ (string_of_int n) ^ "." ^ n2
;

let value operandState => float_of_string (readout operandState);
let showState state =>
switch(state) {
| Zero _ => "Zero " ^ readout state
| BeforeDecimal _ _ => "BeforeDecimal " ^ readout state
| AfterDecimal _ _ _ => "AfterDecimal " ^ readout state
};

let fromDigit d sign =>
if (d == 0) {
Zero sign
} else {
BeforeDecimal sign (string_of_int d)
};

let fromDecimal sign => AfterDecimal sign "" 0;


let update state action =>
switch (state, action) {
| (Zero s, Digit d) => BeforeDecimal s (string_of_int d)
| (Zero s, Decimal) => AfterDecimal s "" 0

| (BeforeDecimal s n, Digit d) => BeforeDecimal s (n ^ (string_of_int d))
| (BeforeDecimal s n, Decimal) => AfterDecimal s "" (int_of_string n)

| (AfterDecimal s n2 n, Digit d) => AfterDecimal s (n2^string_of_int d) n

| (state, action) => {
failwith ("Cannot perform action " ^ (showAction action) ^ " in state " ^ (showState state))
}
};
14 changes: 14 additions & 0 deletions motivation/naive/operations.re
Expand Up @@ -13,6 +13,18 @@ type action =
| CancelEntry
;

let showAction = fun
| Digit d => "Digit " ^ (string_of_int d)
| Decimal => "Decimal"
| Op Add => "Op Add"
| Op Subtract => "Op Subtract"
| Op Multiply => "Op Multiply"
| Op Divide => "Op Divide"
| Equal => "Equal"
| Percent => "Percent"
| Cancel => "Cancel"
| CancelEntry => "CancelEntry"
;

let evaluate n1 op n2 =>
switch op {
Expand All @@ -21,3 +33,5 @@ let evaluate n1 op n2 =>
| Multiply => n1 *. n2
| Divide => n1 /. n2
};

type sign = Positive | Negative;
5 changes: 0 additions & 5 deletions motivation/naive/tests.re
Expand Up @@ -50,10 +50,5 @@ module Make = fun(Updater: Updater.Updater) => {
"After previous operation, decimal resets display"
[Digit 1, Op Multiply, Digit 2, Equal, Decimal]
"0.";

assertResult
"After previous equals, next digit starts new number"
[Digit 1, Decimal, Equal, Digit 1]
"1.";
};
}

0 comments on commit c93fb84

Please sign in to comment.