sync/gen_statemachine.ysl2
author Volker Birk <vb@pep.foundation>
Tue, 07 Aug 2018 17:16:03 +0200
branchsync
changeset 2829 e444c3c960bb
parent 2287 026ab4dae779
child 2830 d6f044e43e1a
permissions -rw-r--r--
cut things in
krista@2271
     1
// This file is under GNU General Public License 3.0
krista@2271
     2
// see LICENSE.txt
krista@2271
     3
krista@2271
     4
// generate state machine code
krista@2271
     5
vb@2829
     6
// Copyleft (c) 2016, 2017, p≡p foundation
krista@2271
     7
krista@2271
     8
// Written by Volker Birk
krista@2271
     9
krista@2271
    10
include yslt.yml2
krista@2271
    11
krista@2271
    12
tstylesheet {
krista@2271
    13
    include standardlib.ysl2
krista@2271
    14
    include ./functions.ysl2
krista@2271
    15
krista@2271
    16
    template "/protocol" {
vb@2829
    17
        document "generated/{@name}_impl.h", "text" {
vb@2829
    18
        ||
vb@2829
    19
        // This file is under GNU General Public License 3.0
vb@2829
    20
        // see LICENSE.txt
krista@2271
    21
krista@2271
    22
        #pragma once
krista@2271
    23
vb@2829
    24
        #include "fsm_common.h"
krista@2271
    25
        #include "message_api.h"
vb@2829
    26
        #include "../asn.1/Sync.h"
krista@2271
    27
        
krista@2271
    28
        #ifdef __cplusplus
krista@2271
    29
        extern "C" {
krista@2271
    30
        #endif
krista@2271
    31
vb@2829
    32
        // event struct
krista@2271
    33
vb@2829
    34
        typedef struct _«@name»_event {
vb@2829
    35
            «@name»_PR fsm;
vb@2829
    36
            int event;
vb@2829
    37
            «@name»_t *msg;
vb@2829
    38
        } «@name»_event_t;
krista@2271
    39
krista@2271
    40
        // conditions
krista@2271
    41
vb@2829
    42
        ||
vb@2829
    43
        for "func:distinctName(*//condition)"
vb@2829
    44
            | PEP_STATUS «@name»(PEP_SESSION session, bool *result);
vb@2829
    45
        ||
vb@2829
    46
        // actions
vb@2829
    47
vb@2829
    48
        ||
vb@2829
    49
        const "name", "@name";
vb@2829
    50
        for "func:distinctName(*//action[not(starts-with(@name, 'send'))])"
vb@2829
    51
            | PEP_STATUS «@name»(PEP_SESSION session);
vb@2829
    52
        ||
vb@2829
    53
vb@2829
    54
        // send event to own state machine, use state to generate
vb@2829
    55
        // «@name» message if necessary
vb@2829
    56
vb@2829
    57
        PEP_STATUS «@name»_send(
vb@2829
    58
                PEP_SESSION session, 
vb@2829
    59
                «@name»_PR fsm,
vb@2829
    60
                int message_type
vb@2829
    61
            );
vb@2829
    62
vb@2829
    63
        // send message to partners
vb@2829
    64
vb@2829
    65
        PEP_STATUS send_«@name»_message(
vb@2829
    66
                PEP_SESSION session, 
vb@2829
    67
                «@name»_PR fsm,
vb@2829
    68
                int event
vb@2829
    69
            );
vb@2829
    70
vb@2829
    71
        // receive event, free «@name»_event_t structure if call does not fail
vb@2829
    72
        // with PEP_ILLEGAL_VALUE
vb@2829
    73
vb@2829
    74
        PEP_STATUS recv_«@name»_event(
vb@2829
    75
                PEP_SESSION session, 
vb@2829
    76
                «@name»_event_t *ev
vb@2829
    77
            );
vb@2829
    78
    
vb@2829
    79
        // state machine driver
vb@2829
    80
        // if fsm or event set to 0 use fields in src if present
vb@2829
    81
vb@2829
    82
        PEP_STATUS «@name»_driver(
vb@2829
    83
                PEP_SESSION session,
vb@2829
    84
                «@name»_PR fsm,
vb@2829
    85
                int event
vb@2829
    86
            );
vb@2829
    87
vb@2829
    88
        PEP_STATUS inject_«@name»_event(
vb@2829
    89
                PEP_SESSION session, 
vb@2829
    90
                «@name»_PR fsm,
vb@2829
    91
                int event
vb@2829
    92
            );
vb@2829
    93
vb@2829
    94
vb@2829
    95
        #ifdef __cplusplus
vb@2829
    96
        }
vb@2829
    97
        #endif
vb@2829
    98
vb@2829
    99
        ||
vb@2829
   100
        }
vb@2829
   101
vb@2829
   102
        document "generated/{@name}_impl.c", "text"
vb@2829
   103
        ||
vb@2829
   104
        // This file is under GNU General Public License 3.0
vb@2829
   105
        // see LICENSE.txt
vb@2829
   106
    
vb@2829
   107
        #include "«@name»_impl.h"
vb@2829
   108
        #include "pEp_internal.h"
vb@2829
   109
        `` for "fsm" | #include "«@name»_fsm.h"
vb@2829
   110
vb@2829
   111
        PEP_STATUS «@name»_driver(
vb@2829
   112
                PEP_SESSION session,
vb@2829
   113
                «@name»_PR fsm,
vb@2829
   114
                int event
vb@2829
   115
            )
vb@2829
   116
        {
vb@2829
   117
            assert(session && fsm);
vb@2829
   118
            if (!(session && fsm))
vb@2829
   119
                return PEP_ILLEGAL_VALUE;
vb@2829
   120
vb@2829
   121
            switch (fsm) {
vb@2829
   122
                `` apply "fsm", 2, mode=driver               
vb@2829
   123
                default:
vb@2829
   124
                    return PEP_ILLEGAL_VALUE;
vb@2829
   125
            }
vb@2829
   126
vb@2829
   127
            return PEP_STATUS_OK;
vb@2829
   128
        }
vb@2829
   129
vb@2829
   130
        PEP_STATUS inject_«@name»_event(
vb@2829
   131
                PEP_SESSION session, 
vb@2829
   132
                «@name»_PR fsm,
vb@2829
   133
                int event
vb@2829
   134
            )
vb@2829
   135
        {
vb@2829
   136
            «@name»_t *msg = NULL;
vb@2829
   137
            «@name»_event_t *ev = NULL;
vb@2829
   138
vb@2829
   139
            assert(session && fsm > 0 && event > None);
vb@2829
   140
            if (!(session && fsm > 0 && event > None))
vb@2829
   141
                return PEP_ILLEGAL_VALUE;
vb@2829
   142
vb@2829
   143
            PEP_STATUS status = PEP_STATUS_OK;
vb@2829
   144
vb@2829
   145
            if (!session->inject_«yml:lcase(@name)»_msg) {
vb@2829
   146
               status = PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
vb@2829
   147
               goto error;
vb@2829
   148
            }
vb@2829
   149
vb@2829
   150
            if (event < Extra) {
vb@2829
   151
                msg = new_«@name»_message(fsm, event);
vb@2829
   152
                assert(msg);
vb@2829
   153
                if (!msg) {
vb@2829
   154
                    status = PEP_OUT_OF_MEMORY;
vb@2829
   155
                    goto error;
vb@2829
   156
                }
vb@2829
   157
vb@2829
   158
                status = update_«@name»_message(session, fsm, event, msg);
vb@2829
   159
                if (status)
vb@2829
   160
                    goto error;
vb@2829
   161
            }
vb@2829
   162
vb@2829
   163
            ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
vb@2829
   164
            assert(ev);
vb@2829
   165
            if (!ev) {
vb@2829
   166
                status = PEP_OUT_OF_MEMORY;
vb@2829
   167
                goto error;
vb@2829
   168
            }
vb@2829
   169
            
vb@2829
   170
            ev->fsm = fsm;
vb@2829
   171
            ev->event = event;
vb@2829
   172
            ev->msg = msg;
vb@2829
   173
vb@2829
   174
            int result = session->inject_«yml:lcase(@name)»_msg(ev,
vb@2829
   175
                    session->«yml:lcase(@name)»_management);
vb@2829
   176
            if (result) {
vb@2829
   177
                status = PEP_STATEMACHINE_ERROR;
vb@2829
   178
                goto error;
vb@2829
   179
            }
vb@2829
   180
vb@2829
   181
            goto the_end;
vb@2829
   182
vb@2829
   183
        error:
vb@2829
   184
            free(ev);
vb@2829
   185
            free_«@name»_message(msg);
vb@2829
   186
vb@2829
   187
        the_end:
vb@2829
   188
            return status;
vb@2829
   189
        }
vb@2829
   190
vb@2829
   191
        PEP_STATUS «@name»_send(
vb@2829
   192
                PEP_SESSION session, 
vb@2829
   193
                «@name»_PR fsm,
vb@2829
   194
                int message_type
vb@2829
   195
            )
vb@2829
   196
        {
vb@2829
   197
            assert(session && fsm > 0 && message_type > 1 && message_type < Extra);
vb@2829
   198
            if (!(session && fsm > 0 && message_type > 1 && message_type < Extra))
vb@2829
   199
                return PEP_ILLEGAL_VALUE;
vb@2829
   200
vb@2829
   201
            PEP_STATUS status = PEP_STATUS_OK;
vb@2829
   202
vb@2829
   203
            «@name»_t *msg = new_«@name»_message(fsm, message_type);
vb@2829
   204
            assert(msg);
vb@2829
   205
            if (!msg) {
vb@2829
   206
                status = PEP_OUT_OF_MEMORY;
vb@2829
   207
                goto error;
vb@2829
   208
            }
vb@2829
   209
vb@2829
   210
            status = update_«@name»_message(session, fsm, message_type, msg);
vb@2829
   211
            if (status)
vb@2829
   212
                goto error;
vb@2829
   213
vb@2829
   214
            goto the_end;
vb@2829
   215
vb@2829
   216
        error:
vb@2829
   217
            free_«@name»_message(msg);
vb@2829
   218
vb@2829
   219
        the_end:
vb@2829
   220
            return status;
vb@2829
   221
        }
vb@2829
   222
vb@2829
   223
        PEP_STATUS recv_«@name»_event(
vb@2829
   224
                PEP_SESSION session, 
vb@2829
   225
                «@name»_event_t *ev
vb@2829
   226
            )
vb@2829
   227
        {
vb@2829
   228
            assert(session && ev);
vb@2829
   229
            if (!(session && ev))
vb@2829
   230
                return PEP_ILLEGAL_VALUE;
vb@2829
   231
vb@2829
   232
            assert(ev->fsm >= None && ev->event >= None);
vb@2829
   233
            if (!(ev->fsm >= None && ev->event >= None))
vb@2829
   234
                return PEP_ILLEGAL_VALUE;
vb@2829
   235
vb@2829
   236
            PEP_STATUS status = PEP_STATUS_OK;
vb@2829
   237
vb@2829
   238
            if (ev->event < Extra) {
vb@2829
   239
                «@name»_PR fsm = (int) None;
vb@2829
   240
                int event = None;
vb@2829
   241
vb@2829
   242
                status = update_«@name»_state(session, ev->msg, &fsm, &event);
vb@2829
   243
                if (status)
vb@2829
   244
                    goto error;
vb@2829
   245
vb@2829
   246
                if (ev->fsm) {
vb@2829
   247
                    if (ev->fsm != fsm |`> |` ev->event != event) {
vb@2829
   248
                        status = PEP_SYNC_ILLEGAL_MESSAGE;
vb@2829
   249
                        goto error;
vb@2829
   250
                    }
vb@2829
   251
                }
vb@2829
   252
                else {
vb@2829
   253
                    if (ev->event) {
vb@2829
   254
                        status = PEP_SYNC_ILLEGAL_MESSAGE;
vb@2829
   255
                        goto error;
vb@2829
   256
                    }
vb@2829
   257
                    ev->fsm = fsm;
vb@2829
   258
                    ev->event = event;
vb@2829
   259
                }
vb@2829
   260
            }
vb@2829
   261
vb@2829
   262
            free_«@name»_message(ev->msg);
vb@2829
   263
            free(ev);
vb@2829
   264
            status = «@name»_driver(session, ev->fsm, ev->event);
vb@2829
   265
            return status;
vb@2829
   266
vb@2829
   267
        error:
vb@2829
   268
            free_«@name»_message(ev->msg);
vb@2829
   269
            free(ev);
vb@2829
   270
            return status;
vb@2829
   271
        }
vb@2829
   272
vb@2829
   273
        ||
vb@2829
   274
vb@2829
   275
        apply "fsm", 0, mode=gen;
vb@2829
   276
    }
vb@2829
   277
vb@2829
   278
    template "fsm", mode=driver
vb@2829
   279
    ||
vb@2829
   280
    case Sync_PR_«yml:lcase(@name)»: {
vb@2829
   281
        int state = session->sync_state.«yml:lcase(@name)».state;
vb@2829
   282
        state = fsm_«@name»(session, state, event);
vb@2829
   283
        if (state > 0)
vb@2829
   284
            session->sync_state.«yml:lcase(@name)».state = state;
vb@2829
   285
        else if (state < 0)
vb@2829
   286
            return PEP_STATEMACHINE_ERROR - state;
vb@2829
   287
        break;
vb@2829
   288
    }
vb@2829
   289
vb@2829
   290
    ||
vb@2829
   291
vb@2829
   292
    template "fsm", mode=gen {
vb@2829
   293
        document "generated/{@name}_fsm.h", "text" {
vb@2829
   294
        ||
vb@2829
   295
        // This file is under GNU General Public License 3.0
vb@2829
   296
        // see LICENSE.txt
vb@2829
   297
vb@2829
   298
        #pragma once
vb@2829
   299
vb@2829
   300
        #include "«../@name»_impl.h"
vb@2829
   301
vb@2829
   302
        #ifdef __cplusplus
vb@2829
   303
        extern "C" {
vb@2829
   304
        #endif
vb@2829
   305
vb@2829
   306
        // state machine for «@name»
krista@2271
   307
krista@2271
   308
        // states
krista@2271
   309
krista@2271
   310
        typedef enum _«@name»_state {
vb@2829
   311
            «@name»_state_None = None,
vb@2829
   312
            «@name»_state_Init = Init,
vb@2829
   313
        ||
vb@2829
   314
        for "func:distinctName(state[not(@name='InitState')])"
vb@2829
   315
            |> «@name»`if "position()!=last()" > , `
vb@2829
   316
        ||
krista@2271
   317
        } «@name»_state;
krista@2271
   318
krista@2271
   319
        // events
krista@2271
   320
krista@2271
   321
        typedef enum _«@name»_event {
vb@2829
   322
            «@name»_event_None = None,
vb@2829
   323
            «@name»_event_Init = Init,
krista@2271
   324
        ||
vb@2829
   325
        for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
krista@2271
   326
            const "name", "@name";
vb@2829
   327
            |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
krista@2271
   328
        }
vb@2829
   329
        |> «@name»_event_Extra = Extra,
vb@2829
   330
        for "func:distinctName(state/event[not(../../message/@name=@name)])" {
vb@2829
   331
            if "@name!='Init'"
vb@2829
   332
                |> «@name»`if "position()!=last()" > , `
vb@2829
   333
        }
krista@2271
   334
        ||
krista@2271
   335
        } «@name»_event;
krista@2271
   336
vb@2829
   337
        // state machine
krista@2271
   338
vb@2829
   339
        const char *«@name»_state_name(int state);
krista@2271
   340
krista@2271
   341
        «@name»_state fsm_«@name»(
krista@2271
   342
                PEP_SESSION session,
krista@2271
   343
                «@name»_state state,
vb@2829
   344
                «@name»_event event
krista@2271
   345
            );
krista@2271
   346
krista@2271
   347
        #ifdef __cplusplus
krista@2271
   348
        }
krista@2271
   349
        #endif
krista@2271
   350
krista@2271
   351
        ||
krista@2271
   352
        }
vb@2829
   353
vb@2829
   354
        document "generated/{@name}_fsm.c", "text" {
krista@2271
   355
        ||
vb@2829
   356
        // This file is under GNU General Public License 3.0
vb@2829
   357
        // see LICENSE.txt
krista@2271
   358
vb@2829
   359
        #include "«@name»_fsm.h"
krista@2271
   360
vb@2829
   361
        const char *«@name»_state_name(int state)
vb@2829
   362
        {
vb@2829
   363
            switch (state) {
vb@2829
   364
                case End:
vb@2829
   365
                    return "End";
vb@2829
   366
                case None:
vb@2829
   367
                    return "None";
vb@2829
   368
                case Init:
vb@2829
   369
                    return "InitState";
vb@2829
   370
        ||
vb@2829
   371
        for "func:distinctName(state[not(@name='InitState')])" {
vb@2829
   372
            |>> case «@name»:
vb@2829
   373
            |>>> return "«@name»";
vb@2829
   374
        }
vb@2829
   375
        ||
vb@2829
   376
                default:
vb@2829
   377
                    return "unknown state";
vb@2829
   378
            }
vb@2829
   379
        }
krista@2271
   380
vb@2829
   381
        static char *_str(int n, bool hex)
vb@2829
   382
        {
vb@2829
   383
            char *buf = calloc(1, 24);
vb@2829
   384
            assert(buf);
vb@2829
   385
            if (!buf)
vb@2829
   386
                return NULL;
vb@2829
   387
vb@2829
   388
            if (hex)
vb@2829
   389
                snprintf(buf, 24, "%.4x", n);
vb@2829
   390
            else
vb@2829
   391
                snprintf(buf, 24, "%d", n);
vb@2829
   392
            return buf;
vb@2829
   393
        }
vb@2829
   394
vb@2829
   395
        #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
vb@2829
   396
vb@2829
   397
        static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
vb@2829
   398
        {
vb@2829
   399
            char *_buf = _str(n, hex);
vb@2829
   400
            if (!_buf)
vb@2829
   401
                return PEP_OUT_OF_MEMORY;
vb@2829
   402
            PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
vb@2829
   403
            free(_buf);
vb@2829
   404
            return status;
vb@2829
   405
        }
vb@2829
   406
vb@2829
   407
        #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
vb@2829
   408
        #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
vb@2829
   409
vb@2829
   410
        #ifndef SERVICE_LOG
vb@2829
   411
        // SERVICE LOG is meant to check session->service_log in runtime config;
vb@2829
   412
        // for older engines log more than needed
vb@2829
   413
        #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
vb@2829
   414
        #endif 
vb@2829
   415
vb@2829
   416
        #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
vb@2829
   417
vb@2829
   418
        «@name»_state fsm_«@name»(
krista@2271
   419
                PEP_SESSION session,
vb@2829
   420
                «@name»_state state,
vb@2829
   421
                «@name»_event event
krista@2271
   422
            )
krista@2271
   423
        {
krista@2271
   424
            assert(session);
krista@2271
   425
            if (!session)
vb@2829
   426
                return invalid_state;
krista@2271
   427
krista@2271
   428
            switch (state) {
vb@2829
   429
                case None:
vb@2829
   430
                    «@name»_SERVICE_LOG("transition to state Init", "None is not a valid state");
vb@2829
   431
                    return Init;
vb@2829
   432
                
vb@2829
   433
                `` apply "state", 2, mode=fsm
krista@2271
   434
                default:
vb@2829
   435
                    «@name»_ERR_LOG_INT("«@name»_fsm() called with invalid state", state);
vb@2829
   436
                    return invalid_state;
krista@2271
   437
            }
vb@2829
   438
            
krista@2271
   439
            return state;
krista@2271
   440
        }
krista@2271
   441
krista@2271
   442
        ||
vb@2829
   443
        }
vb@2829
   444
    }
vb@2829
   445
    
vb@2829
   446
    template "state", mode=fsm {
vb@2829
   447
        choose {
vb@2829
   448
            when "@name='InitState'" | case «../@name»_state_Init:
vb@2829
   449
            otherwise | case «@name»:
vb@2829
   450
        }
vb@2829
   451
        ||
vb@2829
   452
            «../@name»_SERVICE_LOG("in state", "«@name»");
vb@2829
   453
vb@2829
   454
            switch (event) {
vb@2829
   455
                case None:
vb@2829
   456
                    «../@name»_SERVICE_LOG("received None event", "ignoring");
vb@2829
   457
                    return state;
vb@2829
   458
     
vb@2829
   459
                `` apply "event", 2, mode=fsm
vb@2829
   460
                default:
vb@2829
   461
                    «../@name»_ERR_LOG_INT("«../@name»_fsm() called with invalid event", event);
vb@2829
   462
                    return invalid_event;
vb@2829
   463
            }
vb@2829
   464
            break;
vb@2829
   465
vb@2829
   466
        ||
krista@2271
   467
    }
krista@2271
   468
vb@2829
   469
    template "event", mode=fsm {
vb@2829
   470
        | case «@name»: {
vb@2829
   471
        if "condition|action" |> PEP_STATUS status;
vb@2829
   472
        if "condition" |> bool result = false;
vb@2829
   473
        if "condition|action" |
krista@2271
   474
        ||
vb@2829
   475
            «../../@name»_SERVICE_LOG("received event", "«@name»");
vb@2829
   476
            `` apply "transition|action|condition" with "protocol", "../../..", with "fsm", "../.."
vb@2829
   477
        ||
vb@2829
   478
        if "name(*[last()])!='transition'" {
vb@2829
   479
            |
vb@2829
   480
            |> KeySync_SERVICE_LOG("remaining in state", "«../@name»");
vb@2829
   481
            |> break;
vb@2829
   482
        }
vb@2829
   483
        ||
vb@2829
   484
        }
vb@2829
   485
        
vb@2829
   486
        ||
vb@2829
   487
    }
vb@2829
   488
vb@2829
   489
    template "transition" {
vb@2829
   490
        param "fsm";
krista@2271
   491
        ||
krista@2271
   492
vb@2829
   493
        «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
vb@2829
   494
        state = «@target»;
vb@2829
   495
        break;
krista@2271
   496
        ||
vb@2829
   497
    }
krista@2271
   498
vb@2829
   499
    template "action" {
vb@2829
   500
        param "protocol";
vb@2829
   501
        param "fsm";
vb@2829
   502
        choose {
vb@2829
   503
            when "starts-with(@name, 'send')" {
vb@2829
   504
                const "name", "substring(@name, 5)";
vb@2829
   505
                ||
vb@2829
   506
vb@2829
   507
                «$fsm/@name»_SERVICE_LOG("send message", "«$name»");
vb@2829
   508
                status = «$protocol/@name»_send(session, «$fsm/@id», «$name»);
vb@2829
   509
                ||
vb@2829
   510
            }
vb@2829
   511
            otherwise
vb@2829
   512
                ||
vb@2829
   513
vb@2829
   514
                «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
vb@2829
   515
                status = «@name»(session);
vb@2829
   516
                ||
vb@2829
   517
        }
krista@2271
   518
        ||
vb@2829
   519
        if (status) {
vb@2829
   520
            «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
vb@2829
   521
            return invalid_action;
krista@2271
   522
        }
krista@2271
   523
        ||
krista@2271
   524
    }
krista@2271
   525
vb@2829
   526
    template "condition" {
vb@2829
   527
        param "protocol";
vb@2829
   528
        param "fsm";
krista@2271
   529
        ||
krista@2271
   530
vb@2829
   531
        status = «@name»(session, &result);
vb@2829
   532
        if (status) {
vb@2829
   533
            «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
vb@2829
   534
            return invalid_condition;
krista@2271
   535
        }
vb@2829
   536
        if (result) {
vb@2829
   537
            KeySync_SERVICE_LOG("condition applies", "«@name»");
krista@2271
   538
        ||
vb@2829
   539
            apply "transition|action|condition"
vb@2829
   540
                with "protocol", "$protocol", with "fsm", "$fsm";
krista@2271
   541
        | }
krista@2271
   542
    }
krista@2271
   543
}
krista@2271
   544