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