p≡p engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

415 lines
12 KiB

// This file is under GNU General Public License 3.0
// see LICENSE.txt
// generate state machine code
// Copyleft (c) 2016, p≡p foundation
// Written by Volker Birk
include yslt.yml2
tstylesheet {
include standardlib.ysl2
include ./functions.ysl2
template "/protocol" {
document "generated/Makefile.protocols", "text"
apply "fsm", 0, mode="make";
apply "fsm", 0, mode=gen;
}
template "fsm", mode=make
||
«@filename»_fsm.c: ../sync/devicegroup.fsm
\tmake -C ../«@filename»
||
template "fsm", mode=gen {
document "generated/{@filename}_fsm.h", "text" {
||
#pragma once
// state machine for «@name»
#include "message_api.h"
#ifdef __cplusplus
extern "C" {
#endif
// types
typedef pEp_identity * Identity;
typedef stringlist_t * Stringlist;
// error values
typedef enum _fsm_error {
// these error values are corresponding to
// PEP_SYNC_STATEMACHINE_ERROR - value
invalid_state = -2,
invalid_event = -3,
invalid_condition = -4,
invalid_action = -5,
// out of memory condition
invalid_out_of_memory = -128
} fsm_error;
// conditions
`` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
// states
typedef enum _«@name»_state {
// error values also in this namespace
«@name»_state_invalid_state = (int) invalid_state,
«@name»_state_invalid_event = (int) invalid_event,
«@name»_state_invalid_condition = (int) invalid_condition,
«@name»_state_invalid_action = (int) invalid_action,
«@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
«@name»_state_NONE = 0,
`` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
} «@name»_state;
// events
typedef enum _«@name»_event {
«@name»_event_NONE = 0,
||
for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
const "name", "@name";
|> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
}
for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
|> «@name»`if "position()!=last()" > , `
||
} «@name»_event;
// actions
`` const "name", "@name"
`` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
// event injector
PEP_STATUS inject_DeviceState_event(
PEP_SESSION session,
DeviceState_event event,
Identity partner,
void *extra);
// message receiver
PEP_STATUS receive_DeviceState_msg(
PEP_SESSION session,
message *src,
PEP_rating rating,
stringlist_t *keylist
);
// state machine
«@name»_state fsm_«@name»(
PEP_SESSION session,
«@name»_state state,
«@name»_event event,
Identity partner,
void *extra,
time_t *timeout
);
// driver
DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
PEP_SESSION session,
«@name»_event event,
Identity partner,
void *extra,
time_t *timeout
);
#ifdef __cplusplus
}
#endif
||
}
document "generated/{@filename}_driver.c", "text"
||
// Driver for «@name» state machine
#include <assert.h>
#include "pEp_internal.h"
DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
PEP_SESSION session,
«@name»_event event,
Identity partner,
void *extra,
time_t *timeout
)
{
assert(session);
if (!session)
return PEP_ILLEGAL_VALUE;
while(true)
{
«@name»_state new_state = fsm_«@name»(session,
session->«@filename»_state, event, partner, extra, timeout);
if (new_state == «@name»_state_invalid_out_of_memory)
return PEP_OUT_OF_MEMORY;
if (new_state < 0)
return PEP_SYNC_STATEMACHINE_ERROR - new_state;
if (new_state == session->«@filename»_state)
break;
event = Init;
extra = NULL;
session->«@filename»_state = new_state;
}
return PEP_STATUS_OK;
}
||
document "generated/{@filename}_fsm.c", "text"
||
#include "pEp_internal.h"
#include "«@filename»_fsm.h"
#include "«@filename»_impl.h"
// local definitions for «@name»'s state machine
`` apply "state", 0 mode="declStatePayload"
// state machine for «@name»
«@name»_state fsm_«@name»(
PEP_SESSION session,
«@name»_state state,
«@name»_event event,
Identity partner,
void *extra,
time_t *timeout
)
{
PEP_STATUS status = PEP_STATUS_OK;
switch (state) {
`` apply "state", 2
default:
return («@name»_state) invalid_state;
}
return state;
}
||
}
template "state" {
||
case «@name»:
{
DEBUG_LOG("Entering FSM state", "«../@filename»_fsm.c", "state=«@name»")
||
if "count(parm) > 0"
||
assert(session->sync_state_payload);
if(!session->sync_state_payload) return («../@name»_state) invalid_state;
`` apply "parm", 1 mode="unpackStatePayloadParm" with "stateName", "@name"
||
||
switch (event) {
||
if "not(event[@name='Init'])"
||
case Init:
DEBUG_LOG("FSM event", "«../@filename»_fsm.c, state=«@name»", "event=Init")
*timeout = «@timeout»;
break;
||
apply "event", 2;
||
default:
return («../@name»_state) invalid_event;
}
break;
}
||
}
function "pEp_type" {
param "type";
choose {
when "$type = 'Identity'" > Identity
when "$type = 'IdentityList'" > identity_list*
when "$type = 'GroupKeys'" > group_keys_extra_t*
otherwise value "$type";
}
}
function "pEp_type_op_radix" {
param "type";
choose {
when "$type = 'Identity'" > identity
when "$type = 'IdentityList'" > identity_list
when "$type = 'GroupKeys'" > group_keys_extra
otherwise call "pEp_type" with "type", "$type";
}
}
template "parm" mode="unpackStatePayloadParm"
{
param "stateName";
const "pEpType" call "pEp_type" with "type","name(*[1])";
| «$pEpType» «name(*[2])» = ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»;
}
template "state" mode="declStatePayload" if "count(parm) > 0"
||
typedef struct _«@name»_state_payload {
`` apply "parm", 1 mode="declStatePayloadParm"
} «@name»_state_payload_t;
||
template "parm" mode="declStatePayloadParm" {
const "pEpType" call "pEp_type" with "type","name(*[1])";
| «$pEpType» «name(*[2])»;
}
template "event" {
||
case «@name»:
{
DEBUG_LOG("FSM event", "«../../@filename»_fsm.c, state=«../@name»", "event=«@name»")
`` if "@name='Init'" |> *timeout = «../@timeout»;
||
if "count(parm) > 1" {
// TODO get ride of void *extra, pass per-event struct incl all params.
const "extrapEpType" call "pEp_type" with "type","name(parm[2]/*[1])";
const "extraArgName","name(parm[2]/*[2])";
|> «$extrapEpType» «$extraArgName» = («$extrapEpType»)extra;
}
||
`` apply "action|transition|condition";
`` if "name(*[position()=last()]) != 'transition'" |> break;
}
||
}
template "action" {
| DEBUG_LOG("FSM action", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "action=«@name»")
indent(0);
> status = «@name»(session, state,
choose {
when "parm" > «name(parm/*)»
otherwise > NULL
}
choose {
when "count(parm) > 1" > , «name(parm[2]/*)»
otherwise > , NULL
}
> );\n
| if (status == PEP_OUT_OF_MEMORY)
|> return (int) invalid_out_of_memory;
| if (status != PEP_STATUS_OK)
|> return (int) invalid_action;
}
template "condition" {
| {
|> int cond_result = «@name»(session`apply "parm", 0`);
|> #ifndef NDEBUG
|> char resstr[11] = {0,};
|> snprintf(resstr,10,"result=%d",cond_result);
|> #endif
|> DEBUG_LOG("FSM condition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name», condition=«@name»", resstr)
|> if (cond_result < 0)
|>> return cond_result;
|> if (cond_result) {
apply "action|transition|condition";
|> }
const "alternative", "./following-sibling::*[position()=1][name(.)='alternative']";
if "$alternative" {
|> else {
apply "$alternative/action|$alternative/transition|$alternative/condition";
|> }
}
| }
}
template "parm" choose {
when "count(*) = 1"
> , «name(*)»
otherwise
> , «name(*[1])» «name(*[2])»
}
template "transition"{
const "stateparm", "ancestor::state/child::parm";
if "count($stateparm) > 0" {
||
assert(session->sync_state_payload);
if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_state;
`` apply "$stateparm", 0 mode="freeStatePayloadParm" with "stateName", "ancestor::state/@name"
free(session->sync_state_payload);
session->sync_state_payload = NULL;
||
}
if "count(parm) > 0" {
const "nextstatename", "@target";
const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
if "count(parm) != count($nextstateparm)"
error > different state parameters and transition parameters count state=«ancestor::state/@name», event=«ancestor::event/@name» target=«@target»
||
session->sync_state_payload = malloc(sizeof(«$nextstatename»_state_payload_t));
assert(session->sync_state_payload);
if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_out_of_memory;
||
apply "$nextstateparm", 0 mode="dupStatePayloadParm" {
with "stateName", "$nextstatename";
with "transitionParms", "parm";
}
}
| DEBUG_LOG("FSM transition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "target=«@target»")
| return «@target»;
}
template "parm" mode="freeStatePayloadParm"
{
param "stateName";
const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])";
| free_«$pEpTypeOpRadix»(((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»);
}
template "parm" mode="dupStatePayloadParm"
{
param "stateName";
param "transitionParms";
const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])";
const "pos", "position()";
| ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])» =
| «$pEpTypeOpRadix»_dup(«name($transitionParms[$pos]/*)»);
}
}