Search code examples
ragel

How to use ragel labels outside the scope of machine instantiation


When an action is executed I want the action to trigger a jump to another state under certain conditions. For code readability I'd also like to define the action outside of the machine instantiation.

How can I access the state labels generated by ragel outside the scope of the machine instantiation?

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static int cs=1;

%%{
    machine simpleMachine;  
    write data;
}%%

//The code for ActionA and ActionB is defined outside the scope 
//of the instatiation for readability

void a(void)
{   //Called for ActionA
}

void b(void)
{   //Called for ActionB
    int var2=0;
    if (var2==0)
    {
        %%{
            fgoto STATE_A; 
            #This fgoto generates a ragel parse error
            #how to use the STATE_A label outside 
            #of the machine definition?
        }%%
    }
}
void ExecuteSM(const char *p)
{   const char *pe = p + strlen( p );
    int var1=0; 
    %%{
        EVENT1 = 'a';
        EVENT2 = 'b';
        EVENT3=  'c';

        action ActionA
        {   a();
            if (var1==0)
            {
                fgoto STATE_B; //This fgoto compiles OK
            }
        }
        action ActionB
        {
            b();//I'd like to execute an fgoto in the function b() but I get a 
               //parse error
        }
        myfsm := (
                #STATE          EVENT               ACTION              NEXT_STATE
            start:(             EVENT1              >ActionA            ->STATE_A), 
            STATE_A:(           EVENT2              >ActionB            ->STATE_B),
            STATE_B:(           EVENT3)             
        );
    write init nocs;
    write exec;
    }%%
}
int main()
{
    ExecuteSM("abc");
    return 0;
}

Solution

  • fgoto is only available in code blocks, i.e. between { and } in a Ragel FSM specification.

    In your example, you have put the fgoto outside of a code block (the %%{ }%% just opens/closes a multi-line FSM specification).

    If your objective is to put the action-code where your functions a()/b() currently are, you can just do that - as actions. This would improve code-readability at least as much as your current idea.

    Your example thus modified:

    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    static int cs=0;
    
    %%{
      machine simpleMachine;  
      write data;
    
      action ActionA
      {
        if (var1 == 0)
          fgoto STATE_B; //This fgoto compiles OK
      }
      action ActionB
      {
        int var2 = 0;
        if (var2 == 0)
          fgoto STATE_A; 
      }
    }%%
    
    void ExecuteSM(const char *p)
    {
        const char *pe = p + strlen(p);
        int var1 = 0; 
        %%{
            EVENT1 = 'a';
            EVENT2 = 'b';
            EVENT3 = 'c';
    
            myfsm := (
                #STATE       EVENT       ACTION       NEXT_STATE
                start:  (    EVENT1      >ActionA     ->STATE_A), 
                STATE_A:(    EVENT2      >ActionB     ->STATE_B),
                STATE_B:(    EVENT3)             
            );
            write exec;
        }%%
    }
    int main()
    {
        %% write init;
        ExecuteSM("abc");
        return 0;
    }
    

    Note that I've also changed your init nocs statement. Custom initialization should only be necessary in very special circumstances - and then it is preferably to use symbolic state names.

    I've left the cs declaration at translation unit level, although in that example declaring (and initializing) it locally in ExecuteSM would be more appropriate.