Skip to content

Instantly share code, notes, and snippets.

@freshwater
Created September 30, 2012 03:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save freshwater/3805768 to your computer and use it in GitHub Desktop.
Save freshwater/3805768 to your computer and use it in GitHub Desktop.
Reversible state machine for a calculator application.
package com.hudcalc.hudcalc;
import com.hudcalc.hudcalc.SymbolList.Symbol;
public class StateMachine
{
// maximum input length for each number, in digits
int maxLength = 8;
enum State {
S1, D, S1MINUS, SN1, SNDOTD,
SN2, S2DOTD, S2, S2N1, S2N2, DONE,
NOTRANSFER, ERROR
}
private Symbol[] symbolStack = new Symbol[30];
private State[] stateStack = new State[30];
int[] numberSize = new int[] { 0, 0 };
int currentNumber = 0;
int arithmeticSymbolLocation = 0;
boolean secondNumberHasDot = false;
int pointer = 0;
public StateMachine( String initial )
{
this();
char[] chars = initial.toCharArray();
for (int i = 0; i < chars.length; i++) {
processToken(chars[i]);
}
}
public StateMachine()
{
// initialize stacks
stateStack[0] = State.S1;
for (int i = 0; i < symbolStack.length; i++) {
symbolStack[i] = Symbol.EMPTY;
}
}
/** returns symbols in string form **/
@Override
public String toString()
{
return new SymbolList(symbolStack).toString();
}
public State transferState( State state, Symbol sym )
{
//-
switch(state)
{
case S1: switch(sym) {
case MINUS: return State.S1MINUS;
case DOT: return State.S2N1;
case DIGIT: return State.SNDOTD;
}
break;
case S1MINUS: switch(sym) {
case DOT: return State.SN1;
case DIGIT: return State.SNDOTD;
}
break;
case SNDOTD: switch(sym) {
case DOT: return State.SN2;
case DIGIT: return State.SNDOTD;
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:return State.S2;
}
break;
case SN1: switch(sym) {
case DIGIT: return State.SN2;
}
break;
case SN2: switch(sym) {
case DIGIT: return State.SN2;
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:return State.S2;
}
break;
case S2: switch(sym) {
case DOT: return State.S2N1;
case DIGIT: return State.S2DOTD;
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:return State.S2;
}
break;
case S2DOTD: switch(sym) {
case DOT: return State.S2N1;
case DIGIT: return State.S2DOTD;
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:return State.S2DOTD;
}
break;
case S2N1: switch(sym) {
case DIGIT: return State.S2N2;
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:return State.S2N1;
}
break;
case S2N2: switch(sym) {
case DIGIT: return State.S2N2;
case EQUAL: return State.DONE;
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:return State.S2N2;
}
}
//-
return State.NOTRANSFER;
}
// second number has digits?
public boolean isCalculable()
{
return numberSize[1] > 0;
}
public String getFirstNumber()
{
if (currentNumber == 0) {
return new SymbolList(symbolStack).toString().substring(0, pointer);
} else {
return new SymbolList(symbolStack).toString().substring(0, arithmeticSymbolLocation - 1);
}
}
public String getSecondNumber()
{
if (arithmeticSymbolLocation > 0 && pointer > arithmeticSymbolLocation) {
return new SymbolList(symbolStack).toString().substring(arithmeticSymbolLocation, pointer);
} else {
return "";
}
}
public String getArithmeticSymbol()
{
if (arithmeticSymbolLocation > 0) {
return symbolStack[arithmeticSymbolLocation].toString();
} else {
return "";
}
}
public String getAnswer()
{
if( isCalculable() ) {
return Arithmetic.getAnswer(
getFirstNumber(), getSecondNumber(), getArithmeticSymbol());
} else {
return "";
}
}
public void processToken( char token )
{
State nextState = State.ERROR;
Symbol sym = Symbol.ERROR;
switch(token)
{
// D
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
nextState = transferState(stateStack[pointer], Symbol.DIGIT);
// transition exists?
if (nextState != State.NOTRANSFER)
{
if (numberSize[currentNumber] < maxLength)
{
pointer++;
stateStack[pointer] = nextState;
symbolStack[pointer] = Symbol.toSymbol(token);
numberSize[currentNumber]++;
}
}
break;
// .
case '.':
nextState = transferState(stateStack[pointer], Symbol.DOT);
if (nextState != State.NOTRANSFER)
{
if (numberSize[currentNumber] < maxLength)
{
pointer++;
stateStack[pointer] = nextState;
symbolStack[pointer] = Symbol.toSymbol(token);
if (currentNumber == 1)
{
secondNumberHasDot = true;
}
}
}
break;
// -
case '-':
nextState = transferState(stateStack[pointer], Symbol.MINUS);
if (nextState != State.NOTRANSFER)
{
// first number is negatable
if (pointer == 0)
{
pointer++;
stateStack[pointer] = nextState;
symbolStack[pointer] = Symbol.toSymbol(token);
}
else
{
if (currentNumber == 0)
{
currentNumber = 1;
pointer++;
stateStack[pointer] = nextState;
symbolStack[pointer] = Symbol.toSymbol(token);
arithmeticSymbolLocation = pointer;
}
if (currentNumber == 1)
{
symbolStack[arithmeticSymbolLocation] = Symbol.toSymbol(token);
}
}
}
break;
// + x /
case '+': sym = Symbol.PLUS;
case 'x': sym = Symbol.TIMES;
case '/': sym = Symbol.DIVIDE;
nextState = transferState(stateStack[pointer], sym);
if (nextState != State.NOTRANSFER)
{
// in first number
if (currentNumber == 0)
{
currentNumber = 1;
pointer++;
stateStack[pointer] = nextState;
symbolStack[pointer] = Symbol.toSymbol(token);
arithmeticSymbolLocation = pointer;
}
// in second number
if (currentNumber == 1)
{
symbolStack[arithmeticSymbolLocation] = Symbol.toSymbol(token);
}
}
break;
// <
case '<':
if (pointer > 0)
{
switch(symbolStack[pointer].toChar())
{
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
numberSize[currentNumber]--;
break;
case '+':case '-':case 'x':case '/':
currentNumber = 0;
arithmeticSymbolLocation = 0;
case '.':
if (currentNumber == 1)
{
secondNumberHasDot = false;
}
}
symbolStack[pointer] = Symbol.EMPTY;
pointer--;
}
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment