sync/gen_statemachine.ysl2
author Edouard Tisserant <edouard@pep-project.org>
Mon, 08 May 2017 22:09:00 +0200
changeset 1763 8428975706d9
parent 1731 cb009a53b1c8
child 1816 dc9bc5fa3f18
child 2287 026ab4dae779
permissions -rw-r--r--
Merged
vb@1513
     1
// This file is under GNU General Public License 3.0
vb@1513
     2
// see LICENSE.txt
vb@1513
     3
vb@577
     4
// generate state machine code
vb@577
     5
vb@577
     6
// Copyleft (c) 2016, p≡p foundation
vb@577
     7
vb@577
     8
// Written by Volker Birk
vb@577
     9
vb@577
    10
include yslt.yml2
vb@577
    11
vb@577
    12
tstylesheet {
edouard@1460
    13
    include standardlib.ysl2
vb@623
    14
    include ./functions.ysl2
Edouard@605
    15
vb@811
    16
    template "/protocol" {
edouard@1467
    17
        document "generated/Makefile.protocols", "text"
vb@811
    18
            apply "fsm", 0, mode="make";
vb@811
    19
        apply "fsm", 0, mode=gen;
vb@811
    20
    }
vb@811
    21
vb@811
    22
    template "fsm", mode=make
vb@811
    23
    ||
vb@811
    24
    «@filename»_fsm.c: ../sync/devicegroup.fsm
vb@811
    25
    \tmake -C ../«@filename»
vb@811
    26
    ||
vb@811
    27
vb@811
    28
    template "fsm", mode=gen {
edouard@1467
    29
        document "generated/{@filename}_fsm.h", "text" {
vb@609
    30
        ||
vb@609
    31
        #pragma once
vb@577
    32
vb@609
    33
        // state machine for «@name»
vb@577
    34
vb@1099
    35
        #include "message_api.h"
vb@654
    36
        
vb@654
    37
        #ifdef __cplusplus
vb@654
    38
        extern "C" {
vb@654
    39
        #endif
vb@608
    40
vb@609
    41
        // types
Edouard@605
    42
vb@609
    43
        typedef pEp_identity * Identity;
vb@939
    44
        typedef stringlist_t * Stringlist;
vb@583
    45
vb@609
    46
        // error values
vb@577
    47
vb@609
    48
        typedef enum _fsm_error {
vb@964
    49
            // these error values are corresponding to
vb@964
    50
            // PEP_SYNC_STATEMACHINE_ERROR - value
vb@743
    51
            invalid_state = -2,
vb@959
    52
            invalid_event = -3,
vb@959
    53
            invalid_condition = -4,
vb@964
    54
            invalid_action = -5,
vb@964
    55
vb@964
    56
            // out of memory condition
vb@964
    57
            invalid_out_of_memory = -128
vb@609
    58
        } fsm_error;
vb@583
    59
vb@951
    60
        // conditions
vb@951
    61
vb@959
    62
        `` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
vb@951
    63
vb@609
    64
        // states
vb@578
    65
vb@609
    66
        typedef enum _«@name»_state {
vb@1043
    67
            // error values also in this namespace
vb@1043
    68
            «@name»_state_invalid_state = (int) invalid_state,
vb@1043
    69
            «@name»_state_invalid_event = (int) invalid_event,
vb@1043
    70
            «@name»_state_invalid_condition = (int) invalid_condition,
vb@1043
    71
            «@name»_state_invalid_action = (int) invalid_action,
vb@1043
    72
            «@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
vb@1043
    73
vb@951
    74
            «@name»_state_NONE = 0,
vb@623
    75
        `` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
vb@609
    76
        } «@name»_state;
vb@583
    77
vb@609
    78
        // events
vb@578
    79
vb@609
    80
        typedef enum _«@name»_event {
vb@951
    81
            «@name»_event_NONE = 0,
vb@711
    82
        ||
vb@711
    83
        for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
vb@711
    84
            const "name", "@name";
vb@711
    85
            |> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
vb@711
    86
        }
vb@711
    87
        for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
vb@711
    88
            |> «@name»`if "position()!=last()" > , `
vb@711
    89
        ||
vb@609
    90
        } «@name»_event;
vb@583
    91
vb@609
    92
        // actions
vb@582
    93
vb@690
    94
        `` const "name", "@name"
vb@939
    95
        `` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
Edouard@605
    96
edouard@1205
    97
        // event injector
edouard@1205
    98
edouard@1205
    99
        PEP_STATUS inject_DeviceState_event(
edouard@1205
   100
            PEP_SESSION session, 
edouard@1205
   101
            DeviceState_event event,
edouard@1205
   102
            Identity partner,
edouard@1205
   103
            void *extra);
edouard@1205
   104
vb@951
   105
        // message receiver
vb@951
   106
        
edouard@1165
   107
        PEP_STATUS receive_DeviceState_msg(
edouard@1165
   108
                PEP_SESSION session, 
edouard@1165
   109
                message *src, 
edouard@1165
   110
                PEP_rating rating, 
edouard@1165
   111
                stringlist_t *keylist
edouard@1165
   112
            );
vb@951
   113
vb@626
   114
        // state machine
vb@626
   115
vb@626
   116
        «@name»_state fsm_«@name»(
vb@627
   117
                PEP_SESSION session,
vb@626
   118
                «@name»_state state,
vb@626
   119
                «@name»_event event,
vb@939
   120
                Identity partner,
edouard@1445
   121
                void *extra,
edouard@1445
   122
                time_t *timeout
vb@626
   123
            );
vb@626
   124
vb@609
   125
        // driver
Edouard@605
   126
vb@782
   127
        DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
vb@690
   128
                PEP_SESSION session,
vb@690
   129
                «@name»_event event,
vb@690
   130
                Identity partner,
edouard@1445
   131
                void *extra,
edouard@1445
   132
                time_t *timeout
vb@690
   133
            );
Edouard@605
   134
vb@654
   135
        #ifdef __cplusplus
vb@654
   136
        }
vb@654
   137
        #endif
vb@654
   138
vb@609
   139
        ||
vb@711
   140
        }
edouard@1467
   141
        document "generated/{@filename}_driver.c", "text"
vb@690
   142
        ||
vb@690
   143
        // Driver for «@name» state machine
vb@690
   144
vb@690
   145
        #include <assert.h>
vb@690
   146
        #include "pEp_internal.h"
vb@690
   147
vb@690
   148
vb@743
   149
        DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
vb@690
   150
                PEP_SESSION session,
vb@690
   151
                «@name»_event event,
vb@690
   152
                Identity partner,
edouard@1445
   153
                void *extra,
edouard@1445
   154
                time_t *timeout
vb@690
   155
            )
vb@690
   156
        {
vb@951
   157
            assert(session);
vb@951
   158
            if (!session)
vb@951
   159
                return PEP_ILLEGAL_VALUE;
vb@690
   160
edouard@1152
   161
            while(true)
edouard@1152
   162
            {
edouard@1152
   163
                «@name»_state new_state = fsm_«@name»(session,
edouard@1445
   164
                    session->«@filename»_state, event, partner, extra, timeout);
edouard@1157
   165
edouard@1152
   166
                if (new_state == «@name»_state_invalid_out_of_memory)
edouard@1152
   167
                    return PEP_OUT_OF_MEMORY;
edouard@1157
   168
edouard@1152
   169
                if (new_state < 0)
edouard@1152
   170
                    return PEP_SYNC_STATEMACHINE_ERROR - new_state;
edouard@1152
   171
                
edouard@1157
   172
                if (new_state == session->«@filename»_state)
edouard@1152
   173
                    break;
vb@690
   174
edouard@1157
   175
                event = Init;
edouard@1157
   176
                extra = NULL;
edouard@1152
   177
                session->«@filename»_state = new_state;
edouard@1152
   178
            } 
edouard@1152
   179
vb@951
   180
            return PEP_STATUS_OK;
vb@690
   181
        }
vb@690
   182
vb@690
   183
        ||
edouard@1467
   184
        document "generated/{@filename}_fsm.c", "text"
vb@609
   185
        ||
vb@1476
   186
        #include "pEp_internal.h"
vb@807
   187
        #include "«@filename»_fsm.h"
edouard@1597
   188
        #include "«@filename»_impl.h"
vb@609
   189
edouard@1590
   190
        // local definitions for «@name»'s state machine 
edouard@1590
   191
edouard@1595
   192
        `` apply "state", 0 mode="declStatePayload"
edouard@1590
   193
vb@609
   194
        // state machine for «@name»
vb@609
   195
vb@609
   196
        «@name»_state fsm_«@name»(
vb@627
   197
                PEP_SESSION session,
vb@609
   198
                «@name»_state state,
vb@609
   199
                «@name»_event event,
vb@939
   200
                Identity partner,
edouard@1445
   201
                void *extra,
edouard@1445
   202
                time_t *timeout
vb@609
   203
            )
vb@609
   204
        {
vb@959
   205
            PEP_STATUS status = PEP_STATUS_OK;
vb@959
   206
vb@609
   207
            switch (state) {
vb@951
   208
            `` apply "state", 2
vb@609
   209
                default:
vb@743
   210
                    return («@name»_state) invalid_state;
vb@609
   211
            }
vb@609
   212
vb@609
   213
            return state;
vb@577
   214
        }
vb@582
   215
vb@609
   216
        ||
vb@577
   217
    }
vb@577
   218
edouard@1590
   219
    template "state" {
edouard@1590
   220
        ||
edouard@1590
   221
        case «@name»:
edouard@1590
   222
        {
edouard@1590
   223
            DEBUG_LOG("Entering FSM state", "«../@filename»_fsm.c", "state=«@name»")
edouard@1590
   224
        ||
edouard@1590
   225
edouard@1590
   226
        if "count(parm) > 0" 
edouard@1590
   227
        || 
edouard@1590
   228
            assert(session->sync_state_payload);
edouard@1590
   229
            if(!session->sync_state_payload) return («../@name»_state) invalid_state;
edouard@1590
   230
            `` apply "parm", 1 mode="unpackStatePayloadParm" with "stateName", "@name"
edouard@1590
   231
        ||
edouard@1590
   232
edouard@1590
   233
        ||
edouard@1590
   234
            switch (event) {
edouard@1590
   235
        ||
edouard@1590
   236
edouard@1590
   237
        if "not(event[@name='Init'])" 
edouard@1590
   238
        ||
edouard@1590
   239
                case Init: 
edouard@1590
   240
                    DEBUG_LOG("FSM event", "«../@filename»_fsm.c, state=«@name»", "event=Init") 
edouard@1731
   241
                    *timeout = «@timeout»;
edouard@1590
   242
                    break;
edouard@1590
   243
        ||
edouard@1590
   244
edouard@1590
   245
        apply "event", 2;
edouard@1590
   246
edouard@1590
   247
        ||
edouard@1590
   248
                default:
edouard@1590
   249
                    return («../@name»_state) invalid_event;
edouard@1590
   250
            }
edouard@1590
   251
            break;
edouard@1590
   252
        }
edouard@1590
   253
        ||
edouard@1590
   254
    }
edouard@1590
   255
edouard@1595
   256
    function "pEp_type" {
edouard@1595
   257
        param "type";
edouard@1595
   258
edouard@1595
   259
        choose {
edouard@1595
   260
            when "$type = 'Identity'" > Identity
edouard@1595
   261
            when "$type = 'IdentityList'" > identity_list*
edouard@1597
   262
            when "$type = 'GroupKeys'" > group_keys_extra_t*
edouard@1595
   263
            otherwise value "$type";
edouard@1595
   264
        }
edouard@1595
   265
    }
edouard@1595
   266
edouard@1595
   267
    function "pEp_type_op_radix" {
edouard@1595
   268
        param "type";
edouard@1595
   269
edouard@1595
   270
        choose {
edouard@1595
   271
            when "$type = 'Identity'" > identity
edouard@1595
   272
            when "$type = 'IdentityList'" > identity_list
edouard@1597
   273
            when "$type = 'GroupKeys'" > group_keys_extra
edouard@1595
   274
            otherwise call "pEp_type" with "type", "$type";
edouard@1595
   275
        }
edouard@1595
   276
    }
edouard@1595
   277
edouard@1590
   278
    template "parm" mode="unpackStatePayloadParm" 
edouard@1590
   279
    {
edouard@1590
   280
        param "stateName";
edouard@1595
   281
        const "pEpType" call "pEp_type" with "type","name(*[1])"; 
edouard@1595
   282
        | «$pEpType» «name(*[2])» = ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»;
edouard@1590
   283
    }
edouard@1590
   284
edouard@1590
   285
    template "state" mode="declStatePayload" if "count(parm) > 0"
vb@577
   286
    ||
edouard@1590
   287
    typedef struct _«@name»_state_payload {
edouard@1590
   288
        `` apply "parm", 1 mode="declStatePayloadParm"
edouard@1590
   289
    } «@name»_state_payload_t;
edouard@1590
   290
edouard@1460
   291
    ||
vb@577
   292
edouard@1595
   293
    template "parm" mode="declStatePayloadParm" {
edouard@1595
   294
        const "pEpType" call "pEp_type" with "type","name(*[1])"; 
edouard@1595
   295
        | «$pEpType» «name(*[2])»;
edouard@1595
   296
    }
vb@578
   297
edouard@1595
   298
    template "event" {
edouard@1595
   299
        ||
edouard@1595
   300
        case «@name»:
edouard@1595
   301
        {
edouard@1595
   302
            DEBUG_LOG("FSM event", "«../../@filename»_fsm.c, state=«../@name»", "event=«@name»")
edouard@1731
   303
        `` if "@name='Init'" |> *timeout = «../@timeout»;
edouard@1595
   304
        ||
edouard@1731
   305
edouard@1595
   306
        if "count(parm) > 1" {
edouard@1595
   307
            // TODO get ride of void *extra, pass per-event struct incl all params.
edouard@1595
   308
            const "extrapEpType" call "pEp_type" with "type","name(parm[2]/*[1])"; 
edouard@1595
   309
            const "extraArgName","name(parm[2]/*[2])"; 
edouard@1595
   310
            |> «$extrapEpType» «$extraArgName» = («$extrapEpType»)extra;
edouard@1595
   311
        }
edouard@1731
   312
edouard@1595
   313
        ||
edouard@1595
   314
        `` apply "action|transition|condition";
edouard@1595
   315
        `` if "name(*[position()=last()]) != 'transition'" |> break;
edouard@1595
   316
        }
edouard@1595
   317
        ||
edouard@1460
   318
    }
vb@578
   319
vb@582
   320
    template "action" {
edouard@1490
   321
        | DEBUG_LOG("FSM action", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "action=«@name»")
vb@582
   322
        indent(0);
vb@959
   323
        > status = «@name»(session, state, 
vb@582
   324
        choose {
vb@582
   325
            when "parm" > «name(parm/*)»
vb@582
   326
            otherwise > NULL
vb@582
   327
        }
edouard@1166
   328
        choose {
edouard@1595
   329
            when "count(parm) > 1" > , «name(parm[2]/*)»
edouard@1166
   330
            otherwise > , NULL
edouard@1166
   331
        }
edouard@1166
   332
        > );\n
vb@964
   333
        | if (status == PEP_OUT_OF_MEMORY)
vb@1043
   334
        |> return (int) invalid_out_of_memory;
vb@959
   335
        | if (status != PEP_STATUS_OK)
vb@1043
   336
        |> return (int) invalid_action;
vb@582
   337
    }
vb@582
   338
vb@951
   339
    template "condition" {
edouard@1601
   340
        | {
edouard@1601
   341
        |> int cond_result = «@name»(session`apply "parm", 0`);
edouard@1601
   342
        |> #ifndef NDEBUG
edouard@1601
   343
        |> char resstr[11] = {0,};
edouard@1601
   344
        |> snprintf(resstr,10,"result=%d",cond_result);
edouard@1601
   345
        |> #endif
edouard@1601
   346
        |> DEBUG_LOG("FSM condition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name», condition=«@name»", resstr)
edouard@1601
   347
        |> if (cond_result < 0)
edouard@1601
   348
        |>> return cond_result;
edouard@1601
   349
        |> if (cond_result) {
vb@951
   350
        apply "action|transition|condition";
edouard@1601
   351
        |> }
edouard@1709
   352
        const "alternative", "./following-sibling::*[position()=1][name(.)='alternative']";
edouard@1709
   353
        if "$alternative" {
edouard@1601
   354
        |> else {
edouard@1709
   355
        apply "$alternative/action|$alternative/transition|$alternative/condition";
edouard@1601
   356
        |> }
edouard@1601
   357
        }
edouard@1477
   358
        | }
vb@951
   359
    }
vb@951
   360
vb@951
   361
    template "parm" choose {
vb@951
   362
        when "count(*) = 1"
vb@951
   363
            > , «name(*)»
vb@951
   364
        otherwise
vb@951
   365
            > , «name(*[1])» «name(*[2])»
vb@951
   366
    }
vb@951
   367
edouard@1460
   368
    template "transition"{
edouard@1460
   369
        const "stateparm", "ancestor::state/child::parm";
edouard@1590
   370
        if "count($stateparm) > 0" {
edouard@1590
   371
            ||
edouard@1590
   372
            assert(session->sync_state_payload);
edouard@1590
   373
            if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_state;
edouard@1590
   374
            `` apply "$stateparm", 0 mode="freeStatePayloadParm" with "stateName", "ancestor::state/@name"
edouard@1590
   375
            free(session->sync_state_payload);
edouard@1590
   376
            session->sync_state_payload = NULL;
edouard@1590
   377
            ||
edouard@1460
   378
        }
edouard@1590
   379
        if "count(parm) > 0" {
edouard@1460
   380
            const "nextstatename", "@target";
edouard@1460
   381
            const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
edouard@1590
   382
            if "count(parm) != count($nextstateparm)" 
edouard@1590
   383
                error > different state parameters and transition parameters count state=«ancestor::state/@name», event=«ancestor::event/@name» target=«@target»
edouard@1590
   384
            ||
edouard@1590
   385
            session->sync_state_payload = malloc(sizeof(«$nextstatename»_state_payload_t));
edouard@1590
   386
            assert(session->sync_state_payload);
edouard@1590
   387
            if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_out_of_memory;
edouard@1590
   388
            ||
edouard@1590
   389
            apply "$nextstateparm", 0 mode="dupStatePayloadParm" {
edouard@1590
   390
                with "stateName", "$nextstatename";
edouard@1590
   391
                with "transitionParms", "parm";
edouard@1590
   392
            }
edouard@1460
   393
        }
edouard@1490
   394
        | DEBUG_LOG("FSM transition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "target=«@target»")
edouard@1460
   395
        | return «@target»;
edouard@1460
   396
    }
edouard@1590
   397
edouard@1590
   398
    template "parm" mode="freeStatePayloadParm" 
edouard@1590
   399
    {
edouard@1590
   400
        param "stateName";
edouard@1595
   401
        const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])"; 
edouard@1595
   402
        | free_«$pEpTypeOpRadix»(((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»);
edouard@1590
   403
    }
edouard@1590
   404
edouard@1590
   405
    template "parm" mode="dupStatePayloadParm" 
edouard@1590
   406
    {
edouard@1590
   407
        param "stateName";
edouard@1590
   408
        param "transitionParms";
edouard@1595
   409
        const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])"; 
edouard@1595
   410
        const "pos", "position()";
edouard@1590
   411
        | ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])» =
edouard@1595
   412
        |     «$pEpTypeOpRadix»_dup(«name($transitionParms[$pos]/*)»);
edouard@1590
   413
    }
vb@577
   414
}
vb@577
   415