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

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