diff options
Diffstat (limited to 'agen5/fsm-macro.tlib')
-rw-r--r-- | agen5/fsm-macro.tlib | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/agen5/fsm-macro.tlib b/agen5/fsm-macro.tlib new file mode 100644 index 0000000..24315ef --- /dev/null +++ b/agen5/fsm-macro.tlib @@ -0,0 +1,402 @@ +[= AutoGen5 Template -*- Mode: Scheme -*- + +#; Time-stamp: "2012-03-31 17:04:56 bkorb" +#; +#; This file is part of AutoGen. +#; AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved +#; +#; AutoGen is free software: you can redistribute it and/or modify it +#; under the terms of the GNU General Public License as published by the +#; Free Software Foundation, either version 3 of the License, or +#; (at your option) any later version. +#; +#; AutoGen is distributed in the hope that it will be useful, but +#; WITHOUT ANY WARRANTY; without even the implied warranty of +#; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +#; See the GNU General Public License for more details. +#; +#; You should have received a copy of the GNU General Public License along +#; with this program. If not, see <http://www.gnu.org/licenses/>. + +=][= + +(define exit-proc (get "exit-proc" "exit")) + +=][= + +DEFINE emit-invalid-msg =][= + + ;; "defs" indexes will be diverted. + (define tmp-text "") + (define str-table (string-append "z" Pfx "Strings")) + (string-table-new str-table) + (define add-tbl-str (lambda (ent-str) + (set! index-list (string-append index-list + (number->string (string-table-add str-table ent-str)) "\n" )) )) + + (string-table-add str-table "** OUT-OF-RANGE **") + + (ag-fprintf 0 "\n#define %sFsmErr_off %d\n" Pfx + (string-table-add str-table + "FSM Error: in state %d (%s), event %d (%s) is invalid\n")) + + (define invalid-ix (string-table-add str-table "invalid")) + (ag-fprintf 0 "#define %sEvInvalid_off %d\n" Pfx invalid-ix) + + (define index-list (sprintf "%d\n" (string-table-add str-table "init"))) + (ag-fprintf 0 "#define %sStInit_off %s\n" Pfx index-list) + + (out-push-new) + +=][= + + FOR state =][= + (add-tbl-str (get-down "state")) =][= + ENDFOR state =][= + + (ag-fprintf 0 "\nstatic const size_t asz%sStates[%d] = {\n" + Pfx (+ 1 (count "state")) ) + (emit (shell (string-append + "${CLexe-columns} --spread=1 -I4 -S, <<_EOF_\n" + index-list + "_EOF_" + ))) + + (set! index-list "") + + " };\n" =][= + + FOR event =][= + + (set! tmp-text (get "event")) + (set! tmp-text + (if (exist? tmp-text) (get tmp-text) (string-downcase! tmp-text))) + (add-tbl-str tmp-text) + =][= + + ENDFOR event =][= + + (ag-fprintf 0 "\nstatic const size_t asz%sEvents[%d] = {\n" + Pfx (+ 1 (count "event")) ) + + (emit (shell (string-append + "${CLexe-columns} --spread=1 -I4 -S, <<_EOF_\n" + index-list + (sprintf "%d\n" invalid-ix) + "_EOF_"))) + (emit " };\n") + + (out-suspend "strings") + (emit-string-table str-table) + (out-resume "strings") + (emit (out-pop #t)) =] + +#define [=(. PFX) + =]_EVT_NAME(t) ( (((unsigned)(t)) >= [=(+ 1 (count "event"))=]) \ + ? z[=(. Pfx)=]Strings : z[=(. Pfx) + =]Strings + asz[=(. Pfx)=]Events[t]) + +#define [=(. PFX) + =]_STATE_NAME(s) ( (((unsigned)(s)) >= [=(+ 1 (count "state"))=]) \ + ? z[=(. Pfx)=]Strings : z[=(. Pfx) + =]Strings + asz[=(. Pfx)=]States[s]) + +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +static int [=(. pfx)=]_invalid_transition( te_[=(. pfx)=]_state st, te_[= + (. pfx)=]_event evt ); +[= + + IF (not (exist? "handler-file")) =] +/* * * * * * * * * THE CODE STARTS HERE * * * * * * * * + * + * Print out an invalid transition message and return EXIT_FAILURE + */ +static int +[=(. pfx)=]_invalid_transition( te_[=(. pfx)=]_state st, te_[= + (. pfx)=]_event evt ) +{ +[= +(set! tmp-text (sprintf "\ + char const * fmt = z%1$sStrings + %1$sFsmErr_off; + fprintf( stderr, fmt, st, %2$s_STATE_NAME(st), evt, %2$s_EVT_NAME(evt));" + Pfx PFX )) + +(extract fsm-source " /* %s == INVALID TRANS MSG == %s */" "" tmp-text)=] + + return EXIT_FAILURE; +} +[= + + ENDIF not exist handler-file =][= + +ENDDEF emit-invalid-msg + +;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][= + +DEFINE emit-cookie-args =][= + FOR cookie =] + [=cookie=],[= + ENDFOR =][= +ENDDEF emit-cookie-args + +;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][= + +DEFINE build-callback =][= + + CASE cb-name =][= + = noop =][= + * =] +static te_[=(. pfx)=]_state +[=cb_prefix=]_[=cb_name=]([= emit-cookie-args =] + te_[=(. pfx)=]_state initial, + te_[=(. pfx)=]_state maybe_next, + te_[=(. pfx)=]_event trans_evt ) +{ +[= (extract fsm-source (string-append +"/* %s == " (string-tr! (get "cb_name") "a-z_-" "A-Z ") " == %s */" ) +"" +(if (= (get "cb-name") "invalid") + (string-append " " exit-proc "(" pfx + "_invalid_transition(initial, trans_evt));") + " return maybe_next;" )) =] +} +[= + ESAC =][= + +ENDDEF build-callback + +# # # # # # # =][= + +DEFINE callbacks =][= + + `set -- \`sed 's/,//' ${tmp_dir}/xlist\`` =][= + + WHILE `echo $#` =][= + + INVOKE build-callback + cb_prefix = (string-append pfx "_do") + cb_name = (shell "echo $1 ; shift") =][= + + ENDWHILE echo $# =][= + +ENDDEF callbacks + +# # # # # # # =][= + +DEFINE run-callback + +=] + if (pT != NULL) + nxtSt = (*pT)( [= + FOR cookie =][= + (shellf "echo '%s'|sed 's,.*[ \t*],,'" (get "cookie")) =], [= + + ENDFOR =][=(. pfx)=]_state, nxtSt, trans_evt );[= + +ENDDEF run-callback + +;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][= + +DEFINE build-switch =] + case [=cb_prefix=]_[=cb_name=]:[= + + IF (== (get "cb_name") "NOOP") =] break;[= + ELSE =] +[= +(set! example-code (if (= (get "cb_name") "invalid") (string-append + " " exit-proc "(" pfx "_invalid_transition(" pfx + "_state, trans_evt));") + (string-append " nxtSt = HANDLE_" (get "cb_name") "();") )) + +(extract fsm-source + (string-append " /* %s == " (get "cb_name") " == %s */") + "" example-code ) =] + break; + +[=ENDIF=][= + +ENDDEF build-switch + +;;; # # # # # =][= + +DEFINE run-switch =][= + +(define example-code "") =] + + switch (trans) {[= + `set -- \`sed 's/,//' ${tmp_dir}/xlist\`` =][= + + WHILE `echo $#` =][= + + invoke build-switch + cb_prefix = (string-append PFX "_TR") + cb_name = (shell "echo $1 ; shift") =][= + + ENDWHILE echo $# =] + default: +[=(extract fsm-source " /* %s == BROKEN MACHINE == %s */" "" + (string-append " " exit-proc "(" pfx "_invalid_transition(" + pfx "_state, trans_evt));" ))=] + }[= + +ENDDEF run-switch + +;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][= + +DEFINE preamble =][= + +IF (emit (dne " * " "/* ")) + (== (suffix) "c") =][= + +(set! fsm-source (string-append tmp-dir "/fsm.code")) +(set-writable) =][= + +ELSE =][= + +(make-tmp-dir) +(define fsm-source (string-append tmp-dir "/fsm.header")) +(shellf "test -f %1$s-fsm.h && cp %1$s-fsm.h ${tmp_dir}/fsm.header + test -f %1$s-fsm.c && cp %1$s-fsm.c ${tmp_dir}/fsm.code" (base-name)) + +(define pfx (string->c-name! (string-downcase! + (if (exist? "prefix") (get "prefix") (base-name)) ))) +(define PFX (string-upcase pfx)) +(define Pfx (string-capitalize pfx)) +(define t-trans (string-append "t_" pfx "_transition")) + +=][= + +ENDIF =] + * + * Automated Finite State Machine + * +[=(license-description "mbsd" "AutoFSM" " * " "Bruce Korb")=] + */[= + +ENDDEF preamble + +;;; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][= + +DEFINE compute-transitions =][= + +;;; Initialize every possible transition as invalid +;;; +(define tr-name (if (=* (get "method") "call") + (string-append pfx "_do_invalid") + (string-append PFX "_TR_INVALID") )) +(shellf + "ev_list='%s' ; st_list='INIT %s' + for f in $ev_list ; do for g in $st_list + do eval FSM_TRANS_${g}_${f}=\"'{ %s_ST_INVALID, %s }'\" + export FSM_TRANS_${g}_${f} + done ; done" + + (stack-up "event") + (stack-up "state") + PFX tr-name ) + +(define tev "") +(define tst "") +(define ttype "") +(define next "") +(define proc-ptr-type + (lambda (tp) + (if (= tp "noop") "NULL" + (string-downcase! (string-append pfx "_do_" tp)) ))) + +=][=# +;;; Now replace the initial values with proper ones gotten from +;;; the trasition definitions. +;;; +;;; It is actually possible to have multiple specifications for a +;;; single state/event pair, however the last state/event assignment +;;; will supply the value for the transition table. Different +;;; transitions may also specify the same transition method. +;;; For that, we unique sort the list and eliminate dups. +;;; The unique-ified list is used to produce the callout table. +;;; +=][= +FOR transition =][= + + IF (== (get "tst") "*") =][= + + ;; This transition applies for all states + ;; + (set! tev (get-up "tev")) + (set! ttype (if (exist? "ttype") (get "ttype") + (string-append "${f}_" tev) )) + + (set! tr-name (if (=* (get "method") "call") + (proc-ptr-type ttype) + (string-upcase! (string-append PFX "_TR_" ttype)) )) + (set! next (if (exist? "next") (get-up "next") "${f}")) + + (shellf + "for f in ${st_list} ; do F=${f} + eval FSM_TRANS_${f}_%s=\"'{ %s_ST_%s, %s }'\" + done" + tev PFX next tr-name ) =][= + + ELIF (== (get "tev") "*") =][= + + ;; This transition applies for all transitions in a certain state + ;; + (set! tst (get-up "tst")) + (set! ttype (if (exist? "ttype") + (get-up "ttype") (string-append tst "_${f}") )) + + (set! tr-name (if (=* (get "method") "call") + (proc-ptr-type ttype) + (string-append PFX "_TR_" ttype) )) + + (set! next (if (exist? "next") (get-up "next") tst)) + + (shellf + "for f in ${ev_list} ; do + eval FSM_TRANS_%s_${f}=\"'{ %s_ST_%s, %s }'\" + done" + tst PFX next tr-name) =][= + + ELSE =][= + + FOR tst =][= + + (set! tst (get-up "tst")) + (set! next (if (exist? "next") (get-up "next") tst)) + + =][= + FOR tev =][= + + (set! tev (get-up "tev")) + (set! ttype (string-downcase! (if (exist? "ttype") (get "ttype") + (string-append tst "_" tev) ))) + + (set! tr-name (if (=* (get "method") "call") + (proc-ptr-type ttype) + (string-upcase! (string-append PFX "_TR_" ttype)) )) + + (shellf "FSM_TRANS_%s_%s=\"{ %s_ST_%s, %s }\"" + tst tev PFX next tr-name) =][= + + ENDFOR tev =][= + ENDFOR tst =][= + ENDIF tst or ttkn as '*' =][= +ENDFOR transition =][= + +(define trans-ct + (shellf + "env | egrep '^FSM_TRANS_' | \ + sed '/, NULL }/d;s/^.*%s//;s/ .*$/,/' | \ + sort -u > ${tmp_dir}/xlist + echo `wc -l < ${tmp_dir}/xlist` " + (if (=* (get "method") "call") + (string-append pfx "_do_") + (string-append PFX "_TR_")) +) ) =][= + +ENDDEF compute-transitions + +# end of agen5/fsm-macro.tlib =] |