2014-01-30

State Machines

I've been enthralled with state machines lately. It's become unhealthy. I don't remember if it started by playing around with lex and yacc, or if it was when I found Mongrel2 and read this post. But from that point on, I've been obsessed with finite state machines, parsers, regular expressions.

Recently, when after a stack overflow question pushed me into hand coding another state machine based parser, I read this blog post, which introduced a method of implementing state machines that I hadn't seen before. I'm sure it's not new. In fact it probably dates back to the era of machine code. But it was new to me, and I like it better than tables or nested conditionals. The basic idea is to break out the code for each state into its own function and then use a function pointer to track the state instead of an enum and switch.

Of course, I couldn't leave well enough alone. I thought I could do one better if I returned the new function pointer instead of storing it in a reference parameter. As it happens, C is a little funny about returning a pointer to a function with the same signature as the function doing the returning... something to do with infinitely recursive type definitions I guess. So the best I could come up with was a struct containing just the function pointer. That doesn't add too much syntactically when calling the functions, and modern compilers optimize it to just returning the function pointer in a register, but returning them becomes a little more unwieldy. You either need to define const structs somewhere (which is no better than enums), declare and define a local struct to be returned (more lines in each function), or restrict to C99 and use compound literals. If you can think of a better way to tackle this, please let me know. Here is how those three look in code.

typedef struct _state state;
struct _state
{
    state (*func)(char);
};

state init(char);
state final(char);

#if CONST
const state INIT = {init};
const state FINAL = {final};
#endif

state
init(char input)
{
#if LOCAL
    state next;
#endif

    switch(input)
    {
#if CONST

    case 'a':
        return FINAL;
    default:
        return INIT;

#elif LOCAL

    case 'a':
        next.func = final;
        return next;
    default:
        next.func = init;
        return next;

#elif C99

    case 'a':
        return (state){final};
    default:
        return (state){init};

#endif;
    }
}

Here's an example of a simple rpn calculator. I decided to go the route of using const structs, even though it pollutes the global namespace. I did this mainly because it maintains C89 compatibility while requiring less typing than locals or the original design of saving to a reference parameter.

#include <stdio.h>
#include <stack.h>

typedef struct state_ state;

struct state_
{
    state (*func)(char);
};

state initial(char);
state number(char);

const state INITIAL = {initial};
const state NUMBER = {number};
const state END;

state
initial(char input)
{
    int a;

    if(input == 'p')
    {
        printf("%i\n", pop());
        return INITIAL;
    }
    else if(input == '+')
    {
        a = pop();
        *top() += a;
        return INITIAL;
    }
    else if(input == '*')
    {
        a = pop();
        *top() *= a;
        return INITIAL;
    }
    else if(input >= '0' && input <= '9')
    {
        ungetc(input, stdin);
        push(0);
        return NUMBER;
    }
    else
    {
        return END;
    }
}

state
number(char input)
{
    if(input >= '0' && input <= '9')
    {
        *top() *= 10;
        *top() += input - '0';
        return NUMBER;
    }
    else
    {
        ungetc(input, stdin);
        return INITIAL;
    }
}

int
main(void)
{
    int ch;
    state s = INITIAL;

    while((ch = getchar()) != EOF && s.func)
    {
        s = s.func(ch);
    }

    return 0;
}

No comments:

Post a Comment