sync/gen_statemachine.ysl2
author Volker Birk <vb@pep.foundation>
Tue, 27 Dec 2016 21:13:41 +0100
changeset 1513 e7f7e42385b5
parent 1490 e375c22bc587
child 1587 6db4fde2cdae
permissions -rw-r--r--
adding license info to each file
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 // generate state machine code
     5 
     6 // Copyleft (c) 2016, p≡p foundation
     7 
     8 // Written by Volker Birk
     9 
    10 include yslt.yml2
    11 
    12 tstylesheet {
    13     include standardlib.ysl2
    14     include ./functions.ysl2
    15 
    16     template "/protocol" {
    17         document "generated/Makefile.protocols", "text"
    18             apply "fsm", 0, mode="make";
    19         apply "fsm", 0, mode=gen;
    20     }
    21 
    22     template "fsm", mode=make
    23     ||
    24     «@filename»_fsm.c: ../sync/devicegroup.fsm
    25     \tmake -C ../«@filename»
    26     ||
    27 
    28     template "fsm", mode=gen {
    29         document "generated/{@filename}_fsm.h", "text" {
    30         ||
    31         #pragma once
    32 
    33         // state machine for «@name»
    34 
    35         #include "message_api.h"
    36         
    37         #ifdef __cplusplus
    38         extern "C" {
    39         #endif
    40 
    41         // types
    42 
    43         typedef pEp_identity * Identity;
    44         typedef stringlist_t * Stringlist;
    45         typedef union _param { Identity partner; stringlist_t *keylist; } param_t;
    46 
    47         // error values
    48 
    49         typedef enum _fsm_error {
    50             // these error values are corresponding to
    51             // PEP_SYNC_STATEMACHINE_ERROR - value
    52             invalid_state = -2,
    53             invalid_event = -3,
    54             invalid_condition = -4,
    55             invalid_action = -5,
    56 
    57             // out of memory condition
    58             invalid_out_of_memory = -128
    59         } fsm_error;
    60 
    61         // conditions
    62 
    63         `` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
    64 
    65         // states
    66 
    67         typedef enum _«@name»_state {
    68             // error values also in this namespace
    69             «@name»_state_invalid_state = (int) invalid_state,
    70             «@name»_state_invalid_event = (int) invalid_event,
    71             «@name»_state_invalid_condition = (int) invalid_condition,
    72             «@name»_state_invalid_action = (int) invalid_action,
    73             «@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
    74 
    75             «@name»_state_NONE = 0,
    76         `` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
    77         } «@name»_state;
    78 
    79         // events
    80 
    81         typedef enum _«@name»_event {
    82             «@name»_event_NONE = 0,
    83         ||
    84         for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
    85             const "name", "@name";
    86             |> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
    87         }
    88         for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
    89             |> «@name»`if "position()!=last()" > , `
    90         ||
    91         } «@name»_event;
    92 
    93         // actions
    94 
    95         `` const "name", "@name"
    96         `` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
    97 
    98         // event injector
    99 
   100         PEP_STATUS inject_DeviceState_event(
   101             PEP_SESSION session, 
   102             DeviceState_event event,
   103             Identity partner,
   104             void *extra);
   105 
   106         // message receiver
   107         
   108         PEP_STATUS receive_DeviceState_msg(
   109                 PEP_SESSION session, 
   110                 message *src, 
   111                 PEP_rating rating, 
   112                 stringlist_t *keylist
   113             );
   114 
   115         // state machine
   116 
   117         «@name»_state fsm_«@name»(
   118                 PEP_SESSION session,
   119                 «@name»_state state,
   120                 «@name»_event event,
   121                 Identity partner,
   122                 void *extra,
   123                 time_t *timeout
   124             );
   125 
   126         // driver
   127 
   128         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   129                 PEP_SESSION session,
   130                 «@name»_event event,
   131                 Identity partner,
   132                 void *extra,
   133                 time_t *timeout
   134             );
   135 
   136         #ifdef __cplusplus
   137         }
   138         #endif
   139 
   140         ||
   141         }
   142         document "generated/{@filename}_driver.c", "text"
   143         ||
   144         // Driver for «@name» state machine
   145 
   146         #include <assert.h>
   147         #include "pEp_internal.h"
   148 
   149 
   150         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   151                 PEP_SESSION session,
   152                 «@name»_event event,
   153                 Identity partner,
   154                 void *extra,
   155                 time_t *timeout
   156             )
   157         {
   158             assert(session);
   159             if (!session)
   160                 return PEP_ILLEGAL_VALUE;
   161 
   162             while(true)
   163             {
   164                 «@name»_state new_state = fsm_«@name»(session,
   165                     session->«@filename»_state, event, partner, extra, timeout);
   166 
   167                 if (new_state == «@name»_state_invalid_out_of_memory)
   168                     return PEP_OUT_OF_MEMORY;
   169 
   170                 if (new_state < 0)
   171                     return PEP_SYNC_STATEMACHINE_ERROR - new_state;
   172                 
   173                 if (new_state == session->«@filename»_state)
   174                     break;
   175 
   176                 event = Init;
   177                 extra = NULL;
   178                 session->«@filename»_state = new_state;
   179             } 
   180 
   181             return PEP_STATUS_OK;
   182         }
   183 
   184         ||
   185         document "generated/{@filename}_fsm.c", "text"
   186         ||
   187         #include "pEp_internal.h"
   188         #include "«@filename»_fsm.h"
   189 
   190         // state machine for «@name»
   191 
   192         «@name»_state fsm_«@name»(
   193                 PEP_SESSION session,
   194                 «@name»_state state,
   195                 «@name»_event event,
   196                 Identity partner,
   197                 void *extra,
   198                 time_t *timeout
   199             )
   200         {
   201             int cond_result;
   202             PEP_STATUS status = PEP_STATUS_OK;
   203 
   204             switch (state) {
   205             `` apply "state", 2
   206                 default:
   207                     return («@name»_state) invalid_state;
   208             }
   209 
   210             return state;
   211         }
   212 
   213         ||
   214     }
   215 
   216     template "state"
   217     ||
   218     case «@name»:
   219     {
   220         `` if "count(parm) > 1" error | # TODO composite state payload 
   221         `` apply "parm", 1 mode="stateParm"
   222         *timeout = «@timeout»;
   223         DEBUG_LOG("Entering FSM state", "«../@filename»_fsm.c", "state=«@name»")
   224         switch (event) {
   225         `` if "not(event[@name='Init'])" |>> case Init: DEBUG_LOG("FSM event", "«../@filename»_fsm.c, state=«@name»", "event=Init") break;
   226         `` apply "event", 2
   227             default:
   228                 return («../@name»_state) invalid_event;
   229         }
   230         break;
   231     }
   232     ||
   233 
   234     template "parm" mode="stateParm" 
   235     {
   236         | «name(*[1])» «name(*[2])» = («name(*[1])»)session->sync_state_payload;
   237     }
   238 
   239     template "event"
   240     ||
   241     case «@name»:
   242     {
   243         DEBUG_LOG("FSM event", "«../../@filename»_fsm.c, state=«../@name»", "event=«@name»")
   244     `` apply "action|transition|condition";
   245     `` if "name(*[position()=last()]) != 'transition'" |> break;
   246     }
   247     ||
   248 
   249     template "action" {
   250         | DEBUG_LOG("FSM action", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "action=«@name»")
   251         indent(0);
   252         > status = «@name»(session, state, 
   253         choose {
   254             when "parm" > «name(parm/*)»
   255             otherwise > NULL
   256         }
   257         choose {
   258             when "parm[2]" > , extra /*«name(parm[2]/*)»*/
   259             otherwise > , NULL
   260         }
   261         > );\n
   262         | if (status == PEP_OUT_OF_MEMORY)
   263         |> return (int) invalid_out_of_memory;
   264         | if (status != PEP_STATUS_OK)
   265         |> return (int) invalid_action;
   266     }
   267 
   268     template "condition" {
   269         | cond_result = «@name»(session`apply "parm", 0`);
   270         | #ifndef NDEBUG
   271         | char resstr[11] = {0,};
   272         | snprintf(resstr,10,"result=%d",cond_result);
   273         | #endif
   274         | DEBUG_LOG("FSM condition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name», condition=«@name»", resstr)
   275         | if (cond_result < 0)
   276         |> return cond_result;
   277         | if (cond_result) {
   278         apply "action|transition|condition";
   279         | }
   280         const "else", "./following-sibling::*[position()=1][name(.)='else']";
   281         if "$else" {
   282         | else {
   283         apply "$else/action|transition|condition";
   284         | }
   285         }
   286     }
   287 
   288     template "parm" choose {
   289         when "count(*) = 1"
   290             > , «name(*)»
   291         otherwise
   292             > , «name(*[1])» «name(*[2])»
   293     }
   294 
   295     template "transition"{
   296         const "stateparm", "ancestor::state/child::parm";
   297         if "$stateparm" {
   298             | if(session->sync_state_payload){
   299             |     free_«yml:lcase(name($stateparm[1]/*))»((«name($stateparm[1]/*)»)session->sync_state_payload);
   300             |     session->sync_state_payload = NULL;
   301             | }
   302         }
   303         if "parm" {
   304             const "nextstatename", "@target";
   305             const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
   306             | session->sync_state_payload = «yml:lcase(name($nextstateparm/*))»_dup(«name(parm/*)»);
   307         }
   308         | DEBUG_LOG("FSM transition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "target=«@target»")
   309         | return «@target»;
   310     }
   311 }
   312