sync/gen_statemachine.ysl2
author Edouard Tisserant <edouard@pep-project.org>
Mon, 20 Feb 2017 10:29:45 +0100
branchKeySyncWaitForAccept
changeset 1590 6e007351ccde
parent 1587 6db4fde2cdae
child 1595 ddf2993d75fe
permissions -rw-r--r--
KeySync : added composit state payload to hold multiple state parameters
     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 
    46         // error values
    47 
    48         typedef enum _fsm_error {
    49             // these error values are corresponding to
    50             // PEP_SYNC_STATEMACHINE_ERROR - value
    51             invalid_state = -2,
    52             invalid_event = -3,
    53             invalid_condition = -4,
    54             invalid_action = -5,
    55 
    56             // out of memory condition
    57             invalid_out_of_memory = -128
    58         } fsm_error;
    59 
    60         // conditions
    61 
    62         `` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
    63 
    64         // states
    65 
    66         typedef enum _«@name»_state {
    67             // error values also in this namespace
    68             «@name»_state_invalid_state = (int) invalid_state,
    69             «@name»_state_invalid_event = (int) invalid_event,
    70             «@name»_state_invalid_condition = (int) invalid_condition,
    71             «@name»_state_invalid_action = (int) invalid_action,
    72             «@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
    73 
    74             «@name»_state_NONE = 0,
    75         `` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
    76         } «@name»_state;
    77 
    78         // events
    79 
    80         typedef enum _«@name»_event {
    81             «@name»_event_NONE = 0,
    82         ||
    83         for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
    84             const "name", "@name";
    85             |> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
    86         }
    87         for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
    88             |> «@name»`if "position()!=last()" > , `
    89         ||
    90         } «@name»_event;
    91 
    92         // actions
    93 
    94         `` const "name", "@name"
    95         `` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
    96 
    97         // event injector
    98 
    99         PEP_STATUS inject_DeviceState_event(
   100             PEP_SESSION session, 
   101             DeviceState_event event,
   102             Identity partner,
   103             void *extra);
   104 
   105         // message receiver
   106         
   107         PEP_STATUS receive_DeviceState_msg(
   108                 PEP_SESSION session, 
   109                 message *src, 
   110                 PEP_rating rating, 
   111                 stringlist_t *keylist
   112             );
   113 
   114         // state machine
   115 
   116         «@name»_state fsm_«@name»(
   117                 PEP_SESSION session,
   118                 «@name»_state state,
   119                 «@name»_event event,
   120                 Identity partner,
   121                 void *extra,
   122                 time_t *timeout
   123             );
   124 
   125         // driver
   126 
   127         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   128                 PEP_SESSION session,
   129                 «@name»_event event,
   130                 Identity partner,
   131                 void *extra,
   132                 time_t *timeout
   133             );
   134 
   135         #ifdef __cplusplus
   136         }
   137         #endif
   138 
   139         ||
   140         }
   141         document "generated/{@filename}_driver.c", "text"
   142         ||
   143         // Driver for «@name» state machine
   144 
   145         #include <assert.h>
   146         #include "pEp_internal.h"
   147 
   148 
   149         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   150                 PEP_SESSION session,
   151                 «@name»_event event,
   152                 Identity partner,
   153                 void *extra,
   154                 time_t *timeout
   155             )
   156         {
   157             assert(session);
   158             if (!session)
   159                 return PEP_ILLEGAL_VALUE;
   160 
   161             while(true)
   162             {
   163                 «@name»_state new_state = fsm_«@name»(session,
   164                     session->«@filename»_state, event, partner, extra, timeout);
   165 
   166                 if (new_state == «@name»_state_invalid_out_of_memory)
   167                     return PEP_OUT_OF_MEMORY;
   168 
   169                 if (new_state < 0)
   170                     return PEP_SYNC_STATEMACHINE_ERROR - new_state;
   171                 
   172                 if (new_state == session->«@filename»_state)
   173                     break;
   174 
   175                 event = Init;
   176                 extra = NULL;
   177                 session->«@filename»_state = new_state;
   178             } 
   179 
   180             return PEP_STATUS_OK;
   181         }
   182 
   183         ||
   184         document "generated/{@filename}_fsm.c", "text"
   185         ||
   186         #include "pEp_internal.h"
   187         #include "«@filename»_fsm.h"
   188 
   189         // local definitions for «@name»'s state machine 
   190 
   191         `` apply "state", 2 mode="declStatePayload"
   192 
   193         // state machine for «@name»
   194 
   195         «@name»_state fsm_«@name»(
   196                 PEP_SESSION session,
   197                 «@name»_state state,
   198                 «@name»_event event,
   199                 Identity partner,
   200                 void *extra,
   201                 time_t *timeout
   202             )
   203         {
   204             int cond_result;
   205             PEP_STATUS status = PEP_STATUS_OK;
   206 
   207             switch (state) {
   208             `` apply "state", 2
   209                 default:
   210                     return («@name»_state) invalid_state;
   211             }
   212 
   213             return state;
   214         }
   215 
   216         ||
   217     }
   218 
   219     template "state" {
   220         ||
   221         case «@name»:
   222         {
   223             DEBUG_LOG("Entering FSM state", "«../@filename»_fsm.c", "state=«@name»")
   224         ||
   225 
   226         if "count(parm) > 0" 
   227         || 
   228             assert(session->sync_state_payload);
   229             if(!session->sync_state_payload) return («../@name»_state) invalid_state;
   230             `` apply "parm", 1 mode="unpackStatePayloadParm" with "stateName", "@name"
   231         ||
   232 
   233         ||
   234             *timeout = «@timeout»;
   235             switch (event) {
   236         ||
   237 
   238         if "not(event[@name='Init'])" 
   239         ||
   240                 case Init: 
   241                     DEBUG_LOG("FSM event", "«../@filename»_fsm.c, state=«@name»", "event=Init") 
   242                     break;
   243         ||
   244 
   245         apply "event", 2;
   246 
   247         ||
   248                 default:
   249                     return («../@name»_state) invalid_event;
   250             }
   251             break;
   252         }
   253         ||
   254     }
   255 
   256     template "parm" mode="unpackStatePayloadParm" 
   257     {
   258         param "stateName";
   259         | «name(*[1])» «name(*[2])» = ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»;
   260     }
   261 
   262     template "state" mode="declStatePayload" if "count(parm) > 0"
   263     ||
   264     typedef struct _«@name»_state_payload {
   265         `` apply "parm", 1 mode="declStatePayloadParm"
   266     } «@name»_state_payload_t;
   267 
   268     ||
   269 
   270     template "parm" mode="declStatePayloadParm" 
   271     | «name(*[1])» «name(*[2])»;
   272 
   273     template "event"
   274     ||
   275     case «@name»:
   276     {
   277         DEBUG_LOG("FSM event", "«../../@filename»_fsm.c, state=«../@name»", "event=«@name»")
   278     `` apply "action|transition|condition";
   279     `` if "name(*[position()=last()]) != 'transition'" |> break;
   280     }
   281     ||
   282 
   283     template "action" {
   284         | DEBUG_LOG("FSM action", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "action=«@name»")
   285         indent(0);
   286         > status = «@name»(session, state, 
   287         choose {
   288             when "parm" > «name(parm/*)»
   289             otherwise > NULL
   290         }
   291         choose {
   292             when "parm[2]" > , extra /*«name(parm[2]/*)»*/
   293             otherwise > , NULL
   294         }
   295         > );\n
   296         | if (status == PEP_OUT_OF_MEMORY)
   297         |> return (int) invalid_out_of_memory;
   298         | if (status != PEP_STATUS_OK)
   299         |> return (int) invalid_action;
   300     }
   301 
   302     template "condition" {
   303         | cond_result = «@name»(session`apply "parm", 0`);
   304         | #ifndef NDEBUG
   305         | char resstr[11] = {0,};
   306         | snprintf(resstr,10,"result=%d",cond_result);
   307         | #endif
   308         | DEBUG_LOG("FSM condition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name», condition=«@name»", resstr)
   309         | if (cond_result < 0)
   310         |> return cond_result;
   311         | if (cond_result) {
   312         apply "action|transition|condition";
   313         | }
   314         const "else", "./following-sibling::*[position()=1][name(.)='else']";
   315         if "$else" {
   316         | else {
   317         apply "$else/action|transition|condition";
   318         | }
   319         }
   320     }
   321 
   322     template "parm" choose {
   323         when "count(*) = 1"
   324             > , «name(*)»
   325         otherwise
   326             > , «name(*[1])» «name(*[2])»
   327     }
   328 
   329     template "transition"{
   330         const "stateparm", "ancestor::state/child::parm";
   331         if "count($stateparm) > 0" {
   332             ||
   333             assert(session->sync_state_payload);
   334             if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_state;
   335             `` apply "$stateparm", 0 mode="freeStatePayloadParm" with "stateName", "ancestor::state/@name"
   336             free(session->sync_state_payload);
   337             session->sync_state_payload = NULL;
   338             ||
   339         }
   340         if "count(parm) > 0" {
   341             const "nextstatename", "@target";
   342             const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
   343             if "count(parm) != count($nextstateparm)" 
   344                 error > different state parameters and transition parameters count state=«ancestor::state/@name», event=«ancestor::event/@name» target=«@target»
   345             ||
   346             session->sync_state_payload = malloc(sizeof(«$nextstatename»_state_payload_t));
   347             assert(session->sync_state_payload);
   348             if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_out_of_memory;
   349             ||
   350             apply "$nextstateparm", 0 mode="dupStatePayloadParm" {
   351                 with "stateName", "$nextstatename";
   352                 with "transitionParms", "parm";
   353             }
   354         }
   355         | DEBUG_LOG("FSM transition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "target=«@target»")
   356         | return «@target»;
   357     }
   358 
   359     template "parm" mode="freeStatePayloadParm" 
   360     {
   361         param "stateName";
   362         | free_«yml:lcase(name(*[1]))»(((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»);
   363     }
   364 
   365     template "parm" mode="dupStatePayloadParm" 
   366     {
   367         param "stateName";
   368         param "transitionParms";
   369         | ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])» =
   370         |     «yml:lcase(name(*[1]))»_dup(«name($transitionParms[position()]/*)»);
   371     }
   372 }
   373