sync/gen_statemachine.ysl2
author Krista Bennett <krista@pep-project.org>
Thu, 26 Oct 2017 15:56:01 +0200
branchtest_diphoton
changeset 2208 b19261f4fed4
parent 1816 dc9bc5fa3f18
permissions -rw-r--r--
merged in default
     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         #include "«@filename»_impl.h"
   189 
   190         // local definitions for «@name»'s state machine 
   191 
   192         `` apply "state", 0 mode="declStatePayload"
   193 
   194         // state machine for «@name»
   195 
   196         «@name»_state fsm_«@name»(
   197                 PEP_SESSION session,
   198                 «@name»_state state,
   199                 «@name»_event event,
   200                 Identity partner,
   201                 void *extra,
   202                 time_t *timeout
   203             )
   204         {
   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             switch (event) {
   235         ||
   236 
   237         if "not(event[@name='Init'])" 
   238         ||
   239                 case Init: 
   240                     DEBUG_LOG("FSM event", "«../@filename»_fsm.c, state=«@name»", "event=Init") 
   241                     *timeout = «@timeout»;
   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     function "pEp_type" {
   257         param "type";
   258 
   259         choose {
   260             when "$type = 'Identity'" > Identity
   261             when "$type = 'IdentityList'" > identity_list*
   262             when "$type = 'GroupKeys'" > group_keys_extra_t*
   263             otherwise value "$type";
   264         }
   265     }
   266 
   267     function "pEp_type_op_radix" {
   268         param "type";
   269 
   270         choose {
   271             when "$type = 'Identity'" > identity
   272             when "$type = 'IdentityList'" > identity_list
   273             when "$type = 'GroupKeys'" > group_keys_extra
   274             otherwise call "pEp_type" with "type", "$type";
   275         }
   276     }
   277 
   278     template "parm" mode="unpackStatePayloadParm" 
   279     {
   280         param "stateName";
   281         const "pEpType" call "pEp_type" with "type","name(*[1])"; 
   282         | «$pEpType» «name(*[2])» = ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»;
   283     }
   284 
   285     template "state" mode="declStatePayload" if "count(parm) > 0"
   286     ||
   287     typedef struct _«@name»_state_payload {
   288         `` apply "parm", 1 mode="declStatePayloadParm"
   289     } «@name»_state_payload_t;
   290 
   291     ||
   292 
   293     template "parm" mode="declStatePayloadParm" {
   294         const "pEpType" call "pEp_type" with "type","name(*[1])"; 
   295         | «$pEpType» «name(*[2])»;
   296     }
   297 
   298     template "event" {
   299         ||
   300         case «@name»:
   301         {
   302             DEBUG_LOG("FSM event", "«../../@filename»_fsm.c, state=«../@name»", "event=«@name»")
   303         `` if "@name='Init'" |> *timeout = «../@timeout»;
   304         ||
   305 
   306         if "count(parm) > 1" {
   307             // TODO get ride of void *extra, pass per-event struct incl all params.
   308             const "extrapEpType" call "pEp_type" with "type","name(parm[2]/*[1])"; 
   309             const "extraArgName","name(parm[2]/*[2])"; 
   310             |> «$extrapEpType» «$extraArgName» = («$extrapEpType»)extra;
   311         }
   312 
   313         ||
   314         `` apply "action|transition|condition";
   315         `` if "name(*[position()=last()]) != 'transition'" |> break;
   316         }
   317         ||
   318     }
   319 
   320     template "action" {
   321         | DEBUG_LOG("FSM action", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "action=«@name»")
   322         indent(0);
   323         > status = «@name»(session, state, 
   324         choose {
   325             when "parm" > «name(parm/*)»
   326             otherwise > NULL
   327         }
   328         choose {
   329             when "count(parm) > 1" > , «name(parm[2]/*)»
   330             otherwise > , NULL
   331         }
   332         > );\n
   333         | if (status == PEP_OUT_OF_MEMORY)
   334         |> return (int) invalid_out_of_memory;
   335         | if (status != PEP_STATUS_OK)
   336         |> return (int) invalid_action;
   337     }
   338 
   339     template "condition" {
   340         | {
   341         |> int cond_result = «@name»(session`apply "parm", 0`);
   342         |> // #ifndef NDEBUG
   343         |> char resstr[11] = {0,};
   344         |> snprintf(resstr,10,"result=%d",cond_result);
   345         |> // #endif
   346         |> DEBUG_LOG("FSM condition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name», condition=«@name»", resstr)
   347         |> if (cond_result < 0)
   348         |>> return cond_result;
   349         |> if (cond_result) {
   350         apply "action|transition|condition";
   351         |> }
   352         const "alternative", "./following-sibling::*[position()=1][name(.)='alternative']";
   353         if "$alternative" {
   354         |> else {
   355         apply "$alternative/action|$alternative/transition|$alternative/condition";
   356         |> }
   357         }
   358         | }
   359     }
   360 
   361     template "parm" choose {
   362         when "count(*) = 1"
   363             > , «name(*)»
   364         otherwise
   365             > , «name(*[1])» «name(*[2])»
   366     }
   367 
   368     template "transition"{
   369         const "stateparm", "ancestor::state/child::parm";
   370         if "count($stateparm) > 0" {
   371             ||
   372             assert(session->sync_state_payload);
   373             if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_state;
   374             `` apply "$stateparm", 0 mode="freeStatePayloadParm" with "stateName", "ancestor::state/@name"
   375             free(session->sync_state_payload);
   376             session->sync_state_payload = NULL;
   377             ||
   378         }
   379         if "count(parm) > 0" {
   380             const "nextstatename", "@target";
   381             const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
   382             if "count(parm) != count($nextstateparm)" 
   383                 error > different state parameters and transition parameters count state=«ancestor::state/@name», event=«ancestor::event/@name» target=«@target»
   384             ||
   385             session->sync_state_payload = malloc(sizeof(«$nextstatename»_state_payload_t));
   386             assert(session->sync_state_payload);
   387             if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_out_of_memory;
   388             ||
   389             apply "$nextstateparm", 0 mode="dupStatePayloadParm" {
   390                 with "stateName", "$nextstatename";
   391                 with "transitionParms", "parm";
   392             }
   393         }
   394         | DEBUG_LOG("FSM transition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "target=«@target»")
   395         | return «@target»;
   396     }
   397 
   398     template "parm" mode="freeStatePayloadParm" 
   399     {
   400         param "stateName";
   401         const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])"; 
   402         | free_«$pEpTypeOpRadix»(((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»);
   403     }
   404 
   405     template "parm" mode="dupStatePayloadParm" 
   406     {
   407         param "stateName";
   408         param "transitionParms";
   409         const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])"; 
   410         const "pos", "position()";
   411         | ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])» =
   412         |     «$pEpTypeOpRadix»_dup(«name($transitionParms[$pos]/*)»);
   413     }
   414 }
   415