Search code examples
gostate-machineragel

Difference between Ragel transition actions and state actions


State machines, the terminology, and the tools are all new to me, though I've been trying to wrap my head around them lately with various online resources. This started when I wanted to build a faster parser than regex in Ragel and Go. I'm stumped on chapter 3 of the Ragel docs which covers actions.

It's not clear to me what the difference is between the actions tied to the state transitions vs the states themselves. The examples only have errors for the state embedded actions, so I'm not sure when you would use the to and from operators. I made a simple example:

package main

import (
    "fmt"
)

%% machine scanner;

%%{
    action fooStart { fmt.Println("foo start")}
    action fooEnd   { fmt.Println("foo end")}
    action barStart { fmt.Println("bar start")}
    action barEnd   { fmt.Println("bar end")}
    action bazStart { fmt.Println("baz start")}
    action bazEnd   { fmt.Println("baz end")}

    main := "foo" >fooStart @fooEnd "bar" >barStart @barEnd "baz" >bazStart @bazEnd;
}%%

%% write data;

func main() {
    ParseEmbedding([]byte("foobarbaz"))
}

func ParseEmbedding(data []byte) {


    cs, p, pe := 0, 0, len(data)
    %%{
    write init;
    write exec;
    }%%
}

Which outputs the following which is what I expect:

foo start
foo end
bar start
bar end
baz start
baz end

However, if I replace those with state embedded actions using the same surrounding code:

main := "foo" >~fooStart %*fooEnd "bar" >~barStart %*barEnd "baz" >~bazStart %*bazEnd;

I get the following output of which the order and missing lines don't make sense to me:

bar start
foo end
baz start
bar end

My initial impression is state embedded actions are used when you want to only run actions at certain points in the entire state machine instead of the more granular transitions. That still leaves me confused as to the order they are executed in, as well as why the start "to-state" action for "foo" is not executed.

So my question is, what are some practical reasons or examples to use state actions instead of transition actions? How do they work differently in layman's terms?

Here is a graph of the state embedded actions. state embedded actions


Solution

  • Normally you want to use the transition actions. I actually rarely use the state-based actions. Mostly only when I want to initialize something and I want it to execute before the character is looked at. For example, before a condition is executed. State-based actions are currently the only way to make that happen.

    For a while I kind of dreamed of a solution where you could embed transition-based actions that run before the conditions are tested so you wouldn't need the state based actions for init. Using state-based actions always felt a bit like a hack.