From 85b76476de71f43d3eb25d6bef4ee6d84cb71f6c Mon Sep 17 00:00:00 2001 From: Adrian Thurston Date: Sun, 18 Oct 2020 11:44:43 -0700 Subject: lift all source code into src/ dir --- Makefile.am | 2 +- aapl/.gitignore | 2 - aapl/COPYING | 20 - aapl/ChangeLog | 562 -------- aapl/Makefile.am | 7 - aapl/README | 6 - aapl/astring.h | 862 ------------ aapl/avlbasic.h | 66 - aapl/avlcommon.h | 1637 ----------------------- aapl/avlibasic.h | 68 - aapl/avlikeyless.h | 65 - aapl/avlimap.h | 78 -- aapl/avlimel.h | 80 -- aapl/avlimelkey.h | 77 -- aapl/avliset.h | 76 -- aapl/avlitree.h | 79 -- aapl/avlkeyless.h | 59 - aapl/avlmap.h | 75 -- aapl/avlmel.h | 75 -- aapl/avlmelkey.h | 72 - aapl/avlset.h | 71 - aapl/avltree.h | 74 -- aapl/bstcommon.h | 815 ------------ aapl/bstmap.h | 114 -- aapl/bstset.h | 87 -- aapl/bsttable.h | 85 -- aapl/bubblesort.h | 95 -- aapl/buffer.h | 60 - aapl/compare.h | 269 ---- aapl/dlcommon.h | 791 ----------- aapl/dlist.h | 65 - aapl/dlistmel.h | 72 - aapl/dlistval.h | 72 - aapl/insertsort.h | 95 -- aapl/mergesort.h | 141 -- aapl/quicksort.h | 186 --- aapl/resize.h | 345 ----- aapl/rope.h | 237 ---- aapl/sbstmap.h | 122 -- aapl/sbstset.h | 95 -- aapl/sbsttable.h | 94 -- aapl/svector.h | 1351 ------------------- aapl/table.h | 253 ---- aapl/vector.h | 1190 ----------------- aapl/version.mk | 2 - cgil/.gitignore | 2 - cgil/Makefile.am | 9 - cgil/ragel.lm | 1027 --------------- cgil/ril.lm | 284 ---- cgil/rlhc-c.lm | 462 ------- cgil/rlhc-crack.lm | 536 -------- cgil/rlhc-csharp.lm | 480 ------- cgil/rlhc-d.lm | 511 -------- cgil/rlhc-go.lm | 454 ------- cgil/rlhc-java.lm | 504 ------- cgil/rlhc-js.lm | 504 ------- cgil/rlhc-julia.lm | 561 -------- cgil/rlhc-main.lm | 19 - cgil/rlhc-ocaml.lm | 609 --------- cgil/rlhc-ruby.lm | 527 -------- cgil/rlhc-rust.lm | 534 -------- configure.ac | 10 +- libfsm/.exrc | 28 - libfsm/.gitignore | 57 - libfsm/CMakeLists.txt | 154 --- libfsm/Makefile.am | 43 - libfsm/actexp.cc | 218 --- libfsm/actexp.h | 62 - libfsm/action.h | 119 -- libfsm/actloop.cc | 229 ---- libfsm/actloop.h | 63 - libfsm/allocgen.cc | 136 -- libfsm/asm.cc | 2046 ----------------------------- libfsm/asm.h | 248 ---- libfsm/binary.cc | 819 ------------ libfsm/binary.h | 98 -- libfsm/binbreak.cc | 132 -- libfsm/binbreak.h | 71 - libfsm/bingoto.cc | 131 -- libfsm/bingoto.h | 71 - libfsm/binvar.cc | 137 -- libfsm/binvar.h | 72 - libfsm/codegen.cc | 1202 ----------------- libfsm/codegen.h | 460 ------- libfsm/common.cc | 352 ----- libfsm/common.h | 504 ------- libfsm/config.h.cmake.in | 13 - libfsm/dot.cc | 392 ------ libfsm/dot.h | 53 - libfsm/flat.cc | 576 -------- libfsm/flat.h | 94 -- libfsm/flatbreak.cc | 118 -- libfsm/flatbreak.h | 72 - libfsm/flatgoto.cc | 118 -- libfsm/flatgoto.h | 72 - libfsm/flatvar.cc | 117 -- libfsm/flatvar.h | 70 - libfsm/fsmap.cc | 1200 ----------------- libfsm/fsmattach.cc | 857 ------------ libfsm/fsmbase.cc | 854 ------------ libfsm/fsmcond.cc | 520 -------- libfsm/fsmgraph.cc | 1948 --------------------------- libfsm/fsmgraph.h | 2694 -------------------------------------- libfsm/fsmmin.cc | 934 ------------- libfsm/fsmnfa.cc | 660 ---------- libfsm/fsmstate.cc | 603 --------- libfsm/gendata.cc | 1732 ------------------------ libfsm/gendata.h | 477 ------- libfsm/goto.cc | 978 -------------- libfsm/goto.h | 226 ---- libfsm/gotoexp.cc | 207 --- libfsm/gotoexp.h | 75 -- libfsm/gotoloop.cc | 226 ---- libfsm/gotoloop.h | 72 - libfsm/idbase.cc | 421 ------ libfsm/idbase.h | 7 - libfsm/ipgoto.cc | 764 ----------- libfsm/ipgoto.h | 129 -- libfsm/parsedata.h | 27 - libfsm/ragel-config.cmake.in | 3 - libfsm/ragel.h | 108 -- libfsm/redfsm.cc | 1192 ----------------- libfsm/redfsm.h | 889 ------------- libfsm/switch.cc | 1036 --------------- libfsm/switch.h | 106 -- libfsm/switchbreak.cc | 75 -- libfsm/switchbreak.h | 70 - libfsm/switchgoto.cc | 73 -- libfsm/switchgoto.h | 70 - libfsm/switchvar.cc | 76 -- libfsm/switchvar.h | 72 - libfsm/tabbreak.cc | 379 ------ libfsm/tabgoto.cc | 330 ----- libfsm/tables.cc | 81 -- libfsm/tables.h | 265 ---- libfsm/tabvar.cc | 332 ----- src/Makefile.am | 4 +- src/aapl/.gitignore | 2 + src/aapl/COPYING | 20 + src/aapl/ChangeLog | 562 ++++++++ src/aapl/Makefile.am | 7 + src/aapl/README | 6 + src/aapl/astring.h | 862 ++++++++++++ src/aapl/avlbasic.h | 66 + src/aapl/avlcommon.h | 1637 +++++++++++++++++++++++ src/aapl/avlibasic.h | 68 + src/aapl/avlikeyless.h | 65 + src/aapl/avlimap.h | 78 ++ src/aapl/avlimel.h | 80 ++ src/aapl/avlimelkey.h | 77 ++ src/aapl/avliset.h | 76 ++ src/aapl/avlitree.h | 79 ++ src/aapl/avlkeyless.h | 59 + src/aapl/avlmap.h | 75 ++ src/aapl/avlmel.h | 75 ++ src/aapl/avlmelkey.h | 72 + src/aapl/avlset.h | 71 + src/aapl/avltree.h | 74 ++ src/aapl/bstcommon.h | 815 ++++++++++++ src/aapl/bstmap.h | 114 ++ src/aapl/bstset.h | 87 ++ src/aapl/bsttable.h | 85 ++ src/aapl/bubblesort.h | 95 ++ src/aapl/buffer.h | 60 + src/aapl/compare.h | 269 ++++ src/aapl/dlcommon.h | 791 +++++++++++ src/aapl/dlist.h | 65 + src/aapl/dlistmel.h | 72 + src/aapl/dlistval.h | 72 + src/aapl/insertsort.h | 95 ++ src/aapl/mergesort.h | 141 ++ src/aapl/quicksort.h | 186 +++ src/aapl/resize.h | 345 +++++ src/aapl/rope.h | 237 ++++ src/aapl/sbstmap.h | 122 ++ src/aapl/sbstset.h | 95 ++ src/aapl/sbsttable.h | 94 ++ src/aapl/svector.h | 1351 +++++++++++++++++++ src/aapl/table.h | 253 ++++ src/aapl/vector.h | 1190 +++++++++++++++++ src/aapl/version.mk | 2 + src/cgil/.gitignore | 2 + src/cgil/Makefile.am | 9 + src/cgil/ragel.lm | 1027 +++++++++++++++ src/cgil/ril.lm | 284 ++++ src/cgil/rlhc-c.lm | 462 +++++++ src/cgil/rlhc-crack.lm | 536 ++++++++ src/cgil/rlhc-csharp.lm | 480 +++++++ src/cgil/rlhc-d.lm | 511 ++++++++ src/cgil/rlhc-go.lm | 454 +++++++ src/cgil/rlhc-java.lm | 504 +++++++ src/cgil/rlhc-js.lm | 504 +++++++ src/cgil/rlhc-julia.lm | 561 ++++++++ src/cgil/rlhc-main.lm | 19 + src/cgil/rlhc-ocaml.lm | 609 +++++++++ src/cgil/rlhc-ruby.lm | 527 ++++++++ src/cgil/rlhc-rust.lm | 534 ++++++++ src/libfsm/.exrc | 28 + src/libfsm/.gitignore | 57 + src/libfsm/CMakeLists.txt | 154 +++ src/libfsm/Makefile.am | 43 + src/libfsm/actexp.cc | 218 +++ src/libfsm/actexp.h | 62 + src/libfsm/action.h | 119 ++ src/libfsm/actloop.cc | 229 ++++ src/libfsm/actloop.h | 63 + src/libfsm/allocgen.cc | 136 ++ src/libfsm/asm.cc | 2046 +++++++++++++++++++++++++++++ src/libfsm/asm.h | 248 ++++ src/libfsm/binary.cc | 819 ++++++++++++ src/libfsm/binary.h | 98 ++ src/libfsm/binbreak.cc | 132 ++ src/libfsm/binbreak.h | 71 + src/libfsm/bingoto.cc | 131 ++ src/libfsm/bingoto.h | 71 + src/libfsm/binvar.cc | 137 ++ src/libfsm/binvar.h | 72 + src/libfsm/codegen.cc | 1202 +++++++++++++++++ src/libfsm/codegen.h | 460 +++++++ src/libfsm/common.cc | 352 +++++ src/libfsm/common.h | 504 +++++++ src/libfsm/config.h.cmake.in | 13 + src/libfsm/dot.cc | 392 ++++++ src/libfsm/dot.h | 53 + src/libfsm/flat.cc | 576 ++++++++ src/libfsm/flat.h | 94 ++ src/libfsm/flatbreak.cc | 118 ++ src/libfsm/flatbreak.h | 72 + src/libfsm/flatgoto.cc | 118 ++ src/libfsm/flatgoto.h | 72 + src/libfsm/flatvar.cc | 117 ++ src/libfsm/flatvar.h | 70 + src/libfsm/fsmap.cc | 1200 +++++++++++++++++ src/libfsm/fsmattach.cc | 857 ++++++++++++ src/libfsm/fsmbase.cc | 854 ++++++++++++ src/libfsm/fsmcond.cc | 520 ++++++++ src/libfsm/fsmgraph.cc | 1948 +++++++++++++++++++++++++++ src/libfsm/fsmgraph.h | 2694 ++++++++++++++++++++++++++++++++++++++ src/libfsm/fsmmin.cc | 934 +++++++++++++ src/libfsm/fsmnfa.cc | 660 ++++++++++ src/libfsm/fsmstate.cc | 603 +++++++++ src/libfsm/gendata.cc | 1732 ++++++++++++++++++++++++ src/libfsm/gendata.h | 477 +++++++ src/libfsm/goto.cc | 978 ++++++++++++++ src/libfsm/goto.h | 226 ++++ src/libfsm/gotoexp.cc | 207 +++ src/libfsm/gotoexp.h | 75 ++ src/libfsm/gotoloop.cc | 226 ++++ src/libfsm/gotoloop.h | 72 + src/libfsm/idbase.cc | 421 ++++++ src/libfsm/idbase.h | 7 + src/libfsm/ipgoto.cc | 764 +++++++++++ src/libfsm/ipgoto.h | 129 ++ src/libfsm/parsedata.h | 27 + src/libfsm/ragel-config.cmake.in | 3 + src/libfsm/ragel.h | 108 ++ src/libfsm/redfsm.cc | 1192 +++++++++++++++++ src/libfsm/redfsm.h | 889 +++++++++++++ src/libfsm/switch.cc | 1036 +++++++++++++++ src/libfsm/switch.h | 106 ++ src/libfsm/switchbreak.cc | 75 ++ src/libfsm/switchbreak.h | 70 + src/libfsm/switchgoto.cc | 73 ++ src/libfsm/switchgoto.h | 70 + src/libfsm/switchvar.cc | 76 ++ src/libfsm/switchvar.h | 72 + src/libfsm/tabbreak.cc | 379 ++++++ src/libfsm/tabgoto.cc | 330 +++++ src/libfsm/tables.cc | 81 ++ src/libfsm/tables.h | 265 ++++ src/libfsm/tabvar.cc | 332 +++++ 271 files changed, 47694 insertions(+), 47692 deletions(-) delete mode 100644 aapl/.gitignore delete mode 100644 aapl/COPYING delete mode 100644 aapl/ChangeLog delete mode 100644 aapl/Makefile.am delete mode 100644 aapl/README delete mode 100644 aapl/astring.h delete mode 100644 aapl/avlbasic.h delete mode 100644 aapl/avlcommon.h delete mode 100644 aapl/avlibasic.h delete mode 100644 aapl/avlikeyless.h delete mode 100644 aapl/avlimap.h delete mode 100644 aapl/avlimel.h delete mode 100644 aapl/avlimelkey.h delete mode 100644 aapl/avliset.h delete mode 100644 aapl/avlitree.h delete mode 100644 aapl/avlkeyless.h delete mode 100644 aapl/avlmap.h delete mode 100644 aapl/avlmel.h delete mode 100644 aapl/avlmelkey.h delete mode 100644 aapl/avlset.h delete mode 100644 aapl/avltree.h delete mode 100644 aapl/bstcommon.h delete mode 100644 aapl/bstmap.h delete mode 100644 aapl/bstset.h delete mode 100644 aapl/bsttable.h delete mode 100644 aapl/bubblesort.h delete mode 100644 aapl/buffer.h delete mode 100644 aapl/compare.h delete mode 100644 aapl/dlcommon.h delete mode 100644 aapl/dlist.h delete mode 100644 aapl/dlistmel.h delete mode 100644 aapl/dlistval.h delete mode 100644 aapl/insertsort.h delete mode 100644 aapl/mergesort.h delete mode 100644 aapl/quicksort.h delete mode 100644 aapl/resize.h delete mode 100644 aapl/rope.h delete mode 100644 aapl/sbstmap.h delete mode 100644 aapl/sbstset.h delete mode 100644 aapl/sbsttable.h delete mode 100644 aapl/svector.h delete mode 100644 aapl/table.h delete mode 100644 aapl/vector.h delete mode 100644 aapl/version.mk delete mode 100644 cgil/.gitignore delete mode 100644 cgil/Makefile.am delete mode 100644 cgil/ragel.lm delete mode 100644 cgil/ril.lm delete mode 100644 cgil/rlhc-c.lm delete mode 100644 cgil/rlhc-crack.lm delete mode 100644 cgil/rlhc-csharp.lm delete mode 100644 cgil/rlhc-d.lm delete mode 100644 cgil/rlhc-go.lm delete mode 100644 cgil/rlhc-java.lm delete mode 100644 cgil/rlhc-js.lm delete mode 100644 cgil/rlhc-julia.lm delete mode 100644 cgil/rlhc-main.lm delete mode 100644 cgil/rlhc-ocaml.lm delete mode 100644 cgil/rlhc-ruby.lm delete mode 100644 cgil/rlhc-rust.lm delete mode 100644 libfsm/.exrc delete mode 100644 libfsm/.gitignore delete mode 100644 libfsm/CMakeLists.txt delete mode 100644 libfsm/Makefile.am delete mode 100644 libfsm/actexp.cc delete mode 100644 libfsm/actexp.h delete mode 100644 libfsm/action.h delete mode 100644 libfsm/actloop.cc delete mode 100644 libfsm/actloop.h delete mode 100644 libfsm/allocgen.cc delete mode 100644 libfsm/asm.cc delete mode 100644 libfsm/asm.h delete mode 100644 libfsm/binary.cc delete mode 100644 libfsm/binary.h delete mode 100644 libfsm/binbreak.cc delete mode 100644 libfsm/binbreak.h delete mode 100644 libfsm/bingoto.cc delete mode 100644 libfsm/bingoto.h delete mode 100644 libfsm/binvar.cc delete mode 100644 libfsm/binvar.h delete mode 100644 libfsm/codegen.cc delete mode 100644 libfsm/codegen.h delete mode 100644 libfsm/common.cc delete mode 100644 libfsm/common.h delete mode 100644 libfsm/config.h.cmake.in delete mode 100644 libfsm/dot.cc delete mode 100644 libfsm/dot.h delete mode 100644 libfsm/flat.cc delete mode 100644 libfsm/flat.h delete mode 100644 libfsm/flatbreak.cc delete mode 100644 libfsm/flatbreak.h delete mode 100644 libfsm/flatgoto.cc delete mode 100644 libfsm/flatgoto.h delete mode 100644 libfsm/flatvar.cc delete mode 100644 libfsm/flatvar.h delete mode 100644 libfsm/fsmap.cc delete mode 100644 libfsm/fsmattach.cc delete mode 100644 libfsm/fsmbase.cc delete mode 100644 libfsm/fsmcond.cc delete mode 100644 libfsm/fsmgraph.cc delete mode 100644 libfsm/fsmgraph.h delete mode 100644 libfsm/fsmmin.cc delete mode 100644 libfsm/fsmnfa.cc delete mode 100644 libfsm/fsmstate.cc delete mode 100644 libfsm/gendata.cc delete mode 100644 libfsm/gendata.h delete mode 100644 libfsm/goto.cc delete mode 100644 libfsm/goto.h delete mode 100644 libfsm/gotoexp.cc delete mode 100644 libfsm/gotoexp.h delete mode 100644 libfsm/gotoloop.cc delete mode 100644 libfsm/gotoloop.h delete mode 100644 libfsm/idbase.cc delete mode 100644 libfsm/idbase.h delete mode 100644 libfsm/ipgoto.cc delete mode 100644 libfsm/ipgoto.h delete mode 100644 libfsm/parsedata.h delete mode 100644 libfsm/ragel-config.cmake.in delete mode 100644 libfsm/ragel.h delete mode 100644 libfsm/redfsm.cc delete mode 100644 libfsm/redfsm.h delete mode 100644 libfsm/switch.cc delete mode 100644 libfsm/switch.h delete mode 100644 libfsm/switchbreak.cc delete mode 100644 libfsm/switchbreak.h delete mode 100644 libfsm/switchgoto.cc delete mode 100644 libfsm/switchgoto.h delete mode 100644 libfsm/switchvar.cc delete mode 100644 libfsm/switchvar.h delete mode 100644 libfsm/tabbreak.cc delete mode 100644 libfsm/tabgoto.cc delete mode 100644 libfsm/tables.cc delete mode 100644 libfsm/tables.h delete mode 100644 libfsm/tabvar.cc create mode 100644 src/aapl/.gitignore create mode 100644 src/aapl/COPYING create mode 100644 src/aapl/ChangeLog create mode 100644 src/aapl/Makefile.am create mode 100644 src/aapl/README create mode 100644 src/aapl/astring.h create mode 100644 src/aapl/avlbasic.h create mode 100644 src/aapl/avlcommon.h create mode 100644 src/aapl/avlibasic.h create mode 100644 src/aapl/avlikeyless.h create mode 100644 src/aapl/avlimap.h create mode 100644 src/aapl/avlimel.h create mode 100644 src/aapl/avlimelkey.h create mode 100644 src/aapl/avliset.h create mode 100644 src/aapl/avlitree.h create mode 100644 src/aapl/avlkeyless.h create mode 100644 src/aapl/avlmap.h create mode 100644 src/aapl/avlmel.h create mode 100644 src/aapl/avlmelkey.h create mode 100644 src/aapl/avlset.h create mode 100644 src/aapl/avltree.h create mode 100644 src/aapl/bstcommon.h create mode 100644 src/aapl/bstmap.h create mode 100644 src/aapl/bstset.h create mode 100644 src/aapl/bsttable.h create mode 100644 src/aapl/bubblesort.h create mode 100644 src/aapl/buffer.h create mode 100644 src/aapl/compare.h create mode 100644 src/aapl/dlcommon.h create mode 100644 src/aapl/dlist.h create mode 100644 src/aapl/dlistmel.h create mode 100644 src/aapl/dlistval.h create mode 100644 src/aapl/insertsort.h create mode 100644 src/aapl/mergesort.h create mode 100644 src/aapl/quicksort.h create mode 100644 src/aapl/resize.h create mode 100644 src/aapl/rope.h create mode 100644 src/aapl/sbstmap.h create mode 100644 src/aapl/sbstset.h create mode 100644 src/aapl/sbsttable.h create mode 100644 src/aapl/svector.h create mode 100644 src/aapl/table.h create mode 100644 src/aapl/vector.h create mode 100644 src/aapl/version.mk create mode 100644 src/cgil/.gitignore create mode 100644 src/cgil/Makefile.am create mode 100644 src/cgil/ragel.lm create mode 100644 src/cgil/ril.lm create mode 100644 src/cgil/rlhc-c.lm create mode 100644 src/cgil/rlhc-crack.lm create mode 100644 src/cgil/rlhc-csharp.lm create mode 100644 src/cgil/rlhc-d.lm create mode 100644 src/cgil/rlhc-go.lm create mode 100644 src/cgil/rlhc-java.lm create mode 100644 src/cgil/rlhc-js.lm create mode 100644 src/cgil/rlhc-julia.lm create mode 100644 src/cgil/rlhc-main.lm create mode 100644 src/cgil/rlhc-ocaml.lm create mode 100644 src/cgil/rlhc-ruby.lm create mode 100644 src/cgil/rlhc-rust.lm create mode 100644 src/libfsm/.exrc create mode 100644 src/libfsm/.gitignore create mode 100644 src/libfsm/CMakeLists.txt create mode 100644 src/libfsm/Makefile.am create mode 100644 src/libfsm/actexp.cc create mode 100644 src/libfsm/actexp.h create mode 100644 src/libfsm/action.h create mode 100644 src/libfsm/actloop.cc create mode 100644 src/libfsm/actloop.h create mode 100644 src/libfsm/allocgen.cc create mode 100644 src/libfsm/asm.cc create mode 100644 src/libfsm/asm.h create mode 100644 src/libfsm/binary.cc create mode 100644 src/libfsm/binary.h create mode 100644 src/libfsm/binbreak.cc create mode 100644 src/libfsm/binbreak.h create mode 100644 src/libfsm/bingoto.cc create mode 100644 src/libfsm/bingoto.h create mode 100644 src/libfsm/binvar.cc create mode 100644 src/libfsm/binvar.h create mode 100644 src/libfsm/codegen.cc create mode 100644 src/libfsm/codegen.h create mode 100644 src/libfsm/common.cc create mode 100644 src/libfsm/common.h create mode 100644 src/libfsm/config.h.cmake.in create mode 100644 src/libfsm/dot.cc create mode 100644 src/libfsm/dot.h create mode 100644 src/libfsm/flat.cc create mode 100644 src/libfsm/flat.h create mode 100644 src/libfsm/flatbreak.cc create mode 100644 src/libfsm/flatbreak.h create mode 100644 src/libfsm/flatgoto.cc create mode 100644 src/libfsm/flatgoto.h create mode 100644 src/libfsm/flatvar.cc create mode 100644 src/libfsm/flatvar.h create mode 100644 src/libfsm/fsmap.cc create mode 100644 src/libfsm/fsmattach.cc create mode 100644 src/libfsm/fsmbase.cc create mode 100644 src/libfsm/fsmcond.cc create mode 100644 src/libfsm/fsmgraph.cc create mode 100644 src/libfsm/fsmgraph.h create mode 100644 src/libfsm/fsmmin.cc create mode 100644 src/libfsm/fsmnfa.cc create mode 100644 src/libfsm/fsmstate.cc create mode 100644 src/libfsm/gendata.cc create mode 100644 src/libfsm/gendata.h create mode 100644 src/libfsm/goto.cc create mode 100644 src/libfsm/goto.h create mode 100644 src/libfsm/gotoexp.cc create mode 100644 src/libfsm/gotoexp.h create mode 100644 src/libfsm/gotoloop.cc create mode 100644 src/libfsm/gotoloop.h create mode 100644 src/libfsm/idbase.cc create mode 100644 src/libfsm/idbase.h create mode 100644 src/libfsm/ipgoto.cc create mode 100644 src/libfsm/ipgoto.h create mode 100644 src/libfsm/parsedata.h create mode 100644 src/libfsm/ragel-config.cmake.in create mode 100644 src/libfsm/ragel.h create mode 100644 src/libfsm/redfsm.cc create mode 100644 src/libfsm/redfsm.h create mode 100644 src/libfsm/switch.cc create mode 100644 src/libfsm/switch.h create mode 100644 src/libfsm/switchbreak.cc create mode 100644 src/libfsm/switchbreak.h create mode 100644 src/libfsm/switchgoto.cc create mode 100644 src/libfsm/switchgoto.h create mode 100644 src/libfsm/switchvar.cc create mode 100644 src/libfsm/switchvar.h create mode 100644 src/libfsm/tabbreak.cc create mode 100644 src/libfsm/tabgoto.cc create mode 100644 src/libfsm/tables.cc create mode 100644 src/libfsm/tables.h create mode 100644 src/libfsm/tabvar.cc diff --git a/Makefile.am b/Makefile.am index c57c4071..0eb09cb4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -SUBDIRS = aapl libfsm src cgil doc test +SUBDIRS = src doc test dist_doc_DATA = colm.vim EXTRA_DIST = colm.vim sedsubst diff --git a/aapl/.gitignore b/aapl/.gitignore deleted file mode 100644 index b336cc7c..00000000 --- a/aapl/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Makefile -/Makefile.in diff --git a/aapl/COPYING b/aapl/COPYING deleted file mode 100644 index e246673a..00000000 --- a/aapl/COPYING +++ /dev/null @@ -1,20 +0,0 @@ - -Copyright (c) 2001-2016 Adrian Thurston et al. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/aapl/ChangeLog b/aapl/ChangeLog deleted file mode 100644 index 483ad825..00000000 --- a/aapl/ChangeLog +++ /dev/null @@ -1,562 +0,0 @@ -Aapl 2.14 - March 17, 2006 -========================== - -Added a transfer function to the double list. This function should supersede - the use of shallowCopy + abandon. In the by-value list the destination list - is emptied first. - -Added a transfer function to the Avl tree. - -All double lists and Avl trees now implement deep copies in the - operator=(...) functions and copy constructors. Previously only the by-value - versions of these structures would do deep copies. - -Removed the deep and shallow copy functions from the double lists and Avl - trees. These structures should now be consistent accross all variants except - for the fact that the by-value versions delete elements in destructors and - when overwriting and the others simply abandon their elements (which may not - be allocated on the stack). - -Aapl 2.13 - Jan 25, 2006 -======================== - -The vector and binary search table constructors that set an initial amount - of allocation have been removed. They have been replaced with constructors - that insert initial values, which is a more common task and is a more - intuitive use for contructors. - -Removed the String class. Better to use the stl string class. Aapl::String - does not provide any real advantage over the STL version. Aapl::String has - not been heavily tested and does not have very much functionality. - -Removed the "tiny" versions of templates. These templates had functionality - reduced in the interest of reducing compiled binary size. These templates - did not find any real world use, nor were they heavily tested. - -Removed the "simple" versions of vectors. These should be implemented as - template specializations, not new classes. May be brought back as such in - the future. - -Added vinsert and vremove routines for accessing the insert and remove of - the underlying vector of binary search tables. Previously, these were - accessed by prefixing the insert call with the vector base class name, - however this method is somewhat inconvenient, because it either requires all - the template arguments to be given or an additional typedef to be made. - Prefixing the call with the letter v is simpler. - -Aapl 2.12 - May 15, 2005 -======================== - -Documentation updates to trees, lists, compare classes, binary search tables - and various other places. - -Added iterator documentation and example. - -Added proper includes for all examples, which now all compile. - -Table comparisons now properly inherit the CompareT class, enabling table - comparisons to call non-static element compare classes. - -Removed the Deque structure. This structure was never used, incomplete and - poorly tested. In most problems for which it is a candidate (large - collections of objects in sequential ordering that are not required to be in - contiguous memory), a simple replacement can easily be implemented with a - small amount of code. Rather than be allowed to go unused and unmaintained, - it is removed. - -In the AvlTreeEl structures for Maps and Sets, getKey can be const. - -Removed the non-standard malloc include. - -Aapl 2.11 - May 30, 2004 -======================== - -Moved from ints to longs for most integer values to get 64 bit numbers on 64 - bit machines. Fixes alignment problems on 64 bit machines. - -Added AvliBasic, the linked version of the basic tree where the entire - element is the key. - -Updated documentation. - -Aapl 2.10 -- Feb 5, 2004 -======================== - -Fixes for two-stage lookup. Compiles with gcc-3.4 - Details at http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html#Name%20lookup - -Fixed wrong Vector::Iter::pos() routines. - -FsmGraph code removed from aapl, as an increasing amount of diverse - specialization is required in applications. No longer feels like a general - purpose template. - -Aapl 2.9 -- Dec 8, 2003 -======================= - -Rewrote Deque to look and behave more like the rest of Aapl. Split it into - Deque and DequeSimp. Deque supports complex classes with constructors and - destructors. DequeSimp is for simple classes that can be coppied with - memcpy. Wrote stress tests for both. - -SVector::makeRawSpaceFor fixed to detach from the vector when the current - storage is shared. This affected SBstTable::insert() routines and caused - them to modify shared data, instead of behaving like copy on write as they - should. - -Tiny templates renamed to be consistent with the non-tiny templates. The - tiny versions now only have a T prepended to the name, the second character - is no longer lowercased. This changes Tavl* to TAvl*, TdList* to TDList* and - TsVect* to TSVect*. - -Removed AvlPmap, AvlPset, AvliPmap, and AvliPset. Rationale is that these - are too application specific. They require a pointer as the key (not - anything that dereferences the pointer) and so do not generalize very well. - Due to their lack of generality, they belong in application code. They - allowed a savings in code size when many instantiations were necessary, - however, the savings was only slightly better than TAvlTree. - -Test programs split into stress tests and non stress tests. All stress tests - run indefinitely as long as they don't encounter an error condition, in - which case they terminate by an assertion failure. They can be given a time - limit to run for by specifying the number of seconds on the command line. - -FsmGraph given repetition operators for repeating a machine n times. - -Concatenation operator accepts an optional list of states to draw the epsilon - transition instead of the from graph's final state set. - -Useless shared table header template parameter removed. - -Aapl 2.8.2 -- Aug 18, 2003 -========================== - -AvlSet::Iter and DListVal::Iter data access operators (*, ->) now return the - node in the tree, rather than value. This is to make all iterator behaviour - consistent: return the element. This also allows for O(1) statements like - tree.detach( iter ); - -DList and DListMel and Avl trees that are not sets or maps no longer do a - deep copy in the copy constructor and assignement operator. This is to stay - consistent with the fact that these lists and trees are not responsible for - managing the memory of the elements they contain. - -Table compare split into static and non-static versions. Default is the - static version, which compiles out the 'this' pointer. If an item compare - requires access to class data, the CmpTableNs can be used. - -The user's Fsm class is now required as a template parameter to FsmTrans, - FsmState and FsmGraph. - -All callbacks moved into FsmGraph class to allow the access of user data in - FsmGraph. - -Added callbacks for state creation and destruction to FsmGraph. - -EpsilonOp now takes a list of fsms to draw ops to. Allows the merging in of - several machines using only a single invocation of the NFA to DFA algorithm. - -FsmKeyOps is now expected to be a base class of the derived fsm class (ie a - sibling of FsmGraph). - -In transition lists are encapsulated in a class with an iterator. - -Unused leavingFsm parameter to starOp and concatOp removed. - -Fixed documentation building warnings. - -Documented iterator classes. - -Aapl 2.8.1 -- Jun 11, 2003 -========================== - -All iterator endpoint test functions have been renamed. - Iter::more() -> Iter::lte() - Iter::done() -> Iter::end() - Iter::revMore() -> Iter::gtb() - Iter::revDone() -> Iter::beg() - The rationale is that it is not obvious that more() and done() are direction - specific. It is more clear that lte() (less than end) needs to be changed to - gtb() (greater than beginning) when reversing the direction of a traversal. - -All avl tree element classes have been renamed to make element names - consistent. - AvlNode -> AvlTreeEl - AvliNode -> AvliTreeEl - AvlMapNode -> AvlMapEl - AvliMapNode -> AvliMapEl - AvlSetNode -> AvlSetEl - AvliSetNode -> AvliSetEl - TavlNode -> TavlTreeEl - TavliNode -> TavliTreeEl - TavlMapNode -> TavlMapEl - TavliMapNode -> TavliMapEl - TavlSetNode -> TavlSetEl - TavliSetNode -> TavliSetEl - AvlPmapNode -> AvlPmapEl - AvliPmapNode -> AvliPmapEl - AvlPsetNode -> AvlPsetEl - AvliPsetNode -> AvliPsetEl - FsmSDNode -> StateDictEl - AvlTree::nodeCount -> AvlTree::treeSize - -All binary search table and avl tree classes now inherit from the compare - class. This allows the compare function to be non-static and for it make use - of state that is common to the data structure. - -BstTable(int allocLen) no longer uses Vector::setAsNew and therefore Element - does not require Element() constructor. - -Aapl 2.8.0 -- Jan 5, 2003 -========================= - -Switched to the LGPL. - -Added get() to String class. Returns the data ptr. - -Added operator<<(ostream &, String&). Previously relied on the implicit - cast to char* which did not work all the time. - -Iterators renamed and rewritten. The new style for checking if the iterator - is done is to call more() or done() if moving forwards and revMore() or - revDone() if moving backwards. Iterators now have first() and last() calls - for checking if the iterator is on the first and last element. The next() - and prev() calls now return the next and previous. THEY NO LONGER MOVE THE - ITERATOR FORWARD OR BACKWARDS. Increment() and decrement() are now available - for moving the iterator. - -Various fixes for the intel C++ compiler. - -Shared vector table header renamed from TabHead to STabHead. - -Started Tiny DList, Tiny VectSimp, Tiny SVectSimp, and Tiny AvlTree. These - classes reduce final executable size by reusing core code across - instantiations with different types. They can be used identically to the - non-tiny counterpart if only iterators are used for accesing the structure. - The catch is that the underlying data structures have generic pointers that - are not directly usable without casting. - -Aapl 2.7.0 -- Dec 20, 2002 -========================== - -DoubleList::length -> DoubleList::listLen and added length() function. This - is to keep consistent with vectors and strings. - -Sorting routines now inherit from the compare class and use a non-static - compare routine. This lets the compare have state. This will likely be - extended to all structures that use a compare class. - -Constructor added to string that does sprintf style formatting. - -Routines added to string for setting from a char*, len pair. - -Table class (used by vector) member length changed to tabLen and the - the length() const routine added. This makes SVector a more easy substitute - for Vector. Previously the length member was an int in Vector and a function - call in SVector. Now it is a function in both. - -String::str -> String::data and String::getLen() -> String::length() to make - access of string data consistent with access of table data. - -Binary search tweaked to use a shift in place of a divide. - -FsmGraph code from rlfsm moved back into Aapl and Reglang FSM terminated. - Now that the graph code has been separted from the priority and action code, - the base template is leaner and more generalized. It now fits well in Aapl - as a generic programming construct. Also, rlfsm was not enjoying much - exposure and no sense in letting the fsmgraph code fight for itself. The - relevant fsmgraph changes since the split at 2.5.0 follow: - -Support of arbitrary key types for graph completed. - -Dramatic performance improvements in basic fsm operations. Kleen star, - union, and concatenation do not require a walk of all states and can go very - fast when there is no overlap between the machines. - -Fsm Range transitions implemented. Can now efficiently specify machines that - span very large ranges. - -More efficient partition based fsm minimization implemented. Algorithm is - similar to Hopcroft's minimization algorithm. - -Fsm Graph split into base code that implements FSM operations, NFA-DFA - conversion and minimization and a superclass that implements priority and - action setting. Allows the base code to be easily reused for other - applications that require different state and transition properties. The - superclass is left as user code in Ragel and is not included in Aapl. - -Can now have various classes of named entry points in an fsm graph for - maintaining entry other than the start state. This is useful for making - actions that jump into or call various named locations in the machine. - -Various bugs in fsm graph code fixed. - -Aapl 2.6.0 -- Nov 4, 2002 -========================= - -Added AvlPmap, AvlPset, AvliPmap and AvliPset classes. These are - instantiations of AvlMap and AvlSet with void* types and a small inline - wrapper interface for doing type conversions. If many different maps are - used for storing integers or pointers then these classes can cut down on - code bloat. - -Added AvlTree::remove, which will detach and delete elements from the tree. - -Removed set, unSet and isSet from AvlTree. These functions are redundant and - clutter the interface. - insert/remove/find functionality just for convenience. Prefer to remove them - than to clutter the interface with inconsistently named functions. - -Fixed the return type of BstTable::removeMulti. It should be an int (not - bool) because it returns the number of items removed. - -Added SVector and SVectSimp: implicitly shared copy-on-write vectors. - -Added ResizeCtLin: Identical to ResizeLin, except the step is specified at - compile time using a template argument. - -Removed deepCopy from the classes that by default behave as a deep copy. - Found the duplicate function to make it confusing as to how the structure's - operator= behaves. - -File rename of shrstr to astring, to make it easier to find. - -BsTable renamed to BstTable to stay consistenty named with the other Bst - classes. Also renamed the file from bstable.h to bsttable.h for consistency. - -Compare class routines are now Compare::compare (used to be - Compare::Compare). This is to keep with the lowercase function name - convention. - -Added the insertion of whole elements to BsTable and BstMap (already exists - for BstMap). - -Added the insertion of entire other tables to BsTable, BstMap and BstSet. - -The getKey function for binary search table must now be const as well as the - existing requirement of returning a const object. This was needed for the - insertion of whole elements so the requirement was made for all - insert/remove/find routines in order to stay consistent. - -Removed set, unSet and isSet from BstSet. These functions duplicated the - insert/remove/find functionality just for convenience. Prefer to remove them - than to clutter the interface with inconsistently named functions. - -Aapl 2.5.4 -- Sept 20, 2002 -=========================== - -All of Aapl is now in the Aapl:: namespace, which is disabled by default. It - can be turned on by defining #define AAPL_NAMESPACE. - -Mergesort uses only memcpy to move data around, instead of the inconsistent - use of the = operator. Classes that are sorted have no way of knowing that - they are being moved around in mem. - -Implemented QuickSort, BubbleSort and Insertion Sort. - -QuickSort uses InsertSort when the array being sorted is small. - -MergeSort uses BubbleSort when the array be sorted is small. - -Implemented an implicitly shared string class. - -Aapl 2.5.3 -- Aug 17, 2002 -========================== - -Much work done on the user documentation. - -AvlMap and AvlSet destructors now delete all elements. The idea is that - these classes manage memory on behalf of the user, so cleanup. Also, a deep - copy will cause the existing contents to be deleted. The remaining avl trees - do not assume ownership of element and thus do not delete anything. - -Aapl 2.5.2 -- Aug 14, 2002 -========================== - -Bug fixed in Vector::replace. When overwriting data without overwriting up - to the end of the vector or extending it, the vector would shrink in size. - This is because the tableLength was getting set to the end of the overwrite - region in every case. - -Bug fixed in empty and abandon of Avli* trees. They now clear the list head - and tail pointers. - -shallowCopy, deepCopy and the = operator were added to double list, vector, - and avl tree. The = operator implements the deep copy functionality. - -The Vector class was heavily rewritten. The purpose of the rewrite is to - have a vector that does not require the class that it contains to have a - default constructor. In many cases adding a default constructor violates the - design of a class. A class should not be required to have a default - constructor just because it is going into a vector. To have this feature, - the new vector differs from the old vector in a few ways. If the vector is - instructed to add elements off the end of the vector then undefined - behaviour results. The new vector will not implictly create new items. - Also, new items cannot be added to the list by giving a null data pointer. - Instead insertNew, appendNew, etc. can be used. - -DListVal destructor now deletes all elements. The reasoning is that DListVal - manages elements and so it should clean up after itself. - -remove routines added to double list classes. They are the same as detach - except they also delete elements. - -Default down resize of ResizeRunTime fixed: now is Exponential as is the up - resizing. - -Aapl 2.5.1 -- Aug 9, 2002 -========================= - -Class and function descriptions from doc/*.txt moved to source code in - doxygen format. - -Iterators slimmed down in anticipation of the more coplicated iterator for - avltree. Iterators now provide basic moving forward and backward and looking - at current value. Looking ahead and moving ahead by more than one space is no - longer supported. - -Assignment operator of all iterators now return a reference to this. - -Implemented iterator for avl tree (non Avli* trees). The iterator can be - started on either end and can move forwards or backwards. Does not support - trees larger than 32 element in height (should be sufficient). - -Aapl 2.5.0 -- Jun 22, 2002 -========================== - -Doxygen docs started. - -ExpnResize -> ResizeExpn, ConstResize -> ResizeConst, LinResize -> ResizeLin - Name changes that will keep a lexographically sorted list of classes in good - order. - -StrCmp -> CmpStr, OrdCmp -> CmpOrd, TableCmp -> CmpTable for same reason as - above. - -BstTable fixed so that Resize class is passed to underlying vector. - -Pdp Endianness support removed as it is untested. Don't have a pdp endian - machine on which to test. - -cc file extension changed to cpp for better portability. - - ******** Aapl split out into three libs: Aapl, Autil, Reglang FSM *********** - - Aapl is: A generic programming template library. Aapl is an alternative to - the STL. It currently contains Linked List, Avl Tree, Vector, Binary Search - Table, Double Ended Queue, Merge Sort. - - Autil is: A library of simple utility classes that are easy to come by. - Autil contains paramater parser, byte ordering facilities, character - classes. - - Reglang FSM is: A template library of algorithms and data structures for - compiling state machines from regular languages. - -Aapl 2.4.1 -- May 4, 2002 -========================= - -Fixed automatic dependencies, were not working correctly. - -C++ standards compliant I/O and namespaces. - -Aapl 2.4.0 -- May 3, 2002 -========================= - -Vector now uses malloc, realloc and free so the performance gain of - of using realloc over new, memcpy, delete in resizing can be realized. - -Buffer Renamed to VectSimp. The name buffer is way overused and brings - preconceptions with it. VectSimp now has the same functionality as vector. - It is a 'flavour' of vector. The main difference is that is does not use - copy constructors/destructors. It uses memcpy to put data in and as a result - can go much faster. The idea is that it is used on 'simple data'. - -The type of resizing that vector uses is no longer controlled by including - different files and using different names (ie, VectorC no longer exists). - The old style (last seen in v1.1.2) of using a template parameter has been - resurrected. It was previously removed because it caused very long symbol - names. That problem has been fixed by making the class that goes into that - template parameter not a template. It is a normal class and as such will - cause symbol names to increase a constant amount. The old way was the class - was a template taking type T so that essentially doubled the length of the - symbol. The purpose of this change is to eliminate the many files required - to have all the different resizing types. Any class that inherits from - vector needs to have many flavours in order to support the different - resizing types and that gets ugly. Providing resizing options to the bstable - classes would require 30 files. - -Default step for linear resizing is changed from 10 to 256. - -FsmGraph structure supports Default transitions. Default transitions can be - used in place of newing up a transition for every char in the alphabet in - order to get the 'dot' and 'dot star' fsms. It is incredibly more efficient - and will make large integer alphabets feasible. - -The conversion from FsmGraph to FsmMachine is now moved into a class called - FsmBuild. - -FsmMachine no longer stores transition indices as a linear chunk. Now - stores key, index pairs. This will facilitate using FsmMachine with very - large alphabets because large index tables will not be allocated. when there - is great variation in transition keys. - -FsmMachine stores only offsets, no pointers. - -Aapl assert is removed in favour of using system assert. Can't see a reason - to duplicate system include. - -Lowercased the interface to BsTable. - -Makefiles use automatic dependencies. - -AvlTree interface was lowercased/consolodated. - -Large switches at the top of *common.h files were removed. The defines were - put into the files that include the common files. - -BsTable interface was lowercased/consolodated. - -BstSet replaces VectSet. - -Configure script allows you to specify what objects to build using the - --enable-src option - -AvlTree verification is moved out of the main tree and into the test cases. - -Aapl 2.3.1 -- March 25, 2002 -============================ - -Fixed errors in the event loop with respect to unregistering. - -Added Signal handling to event loop. - -Breaking out of event loop is now much cleaner. - -Added CONTENTS file. - -Removed trivial class File. I think most would rather code this kind - of stuff on their own, or use a more featureful library. - -Removed Data base class. Again, kind of trivial. Better left to the user. - -DList::detach{Front,End} -> DList::detach{First,Last}. This change was - made in order to be more consistent with the iterator convention of - using first, last, sfirst, slast. - -Wrote documentation for double list. - -Aapl 2.3.0 -- March 19, 2002 -============================ - IMPORTANT: This release marks the beginning of a major change in the - naming conventions. Previously, naming conventions had function names - starting with an upper case. The member functions will slowly be changed - to start with a lower case. Some functions names will also be changed - to follow the conventions in doc/conventions.txt. Vector and DList have - already been changed. - - These changes are being made to make aapl more compatible with existing - template libraries and to provide a more consistent interface across - the classes. - - -Vector and double list get iterators. - -Fixed buffer. Restructuring vector broke it. - -Lower cased names in vector. - -Lower cased names in doublelist. - -Vector::Overwrite -> Vector::replace - -Vector::Delete -> Vector::remove - -DList::AddEnd -> DoubleList::append() - -DList::AddFront -> DoubleList::prepend() - -DList::DeleteElements -> DoubleList::empty() - -DList::EmptyList -> DoubleList::abandon() - -Added DListVal, which is the 'by-value-type' double list that does - not require a DListEl class to be defined. - -Began conventions doc. - -Rewrote the connection/selector code. It is now simpler and more generic. - Docs on the way. The code is now in event.{h,cc}. The select loop can - reliably translate asyncronous signals into events in the event loop. - However, code is not yet set up for processing the signal events. The event - loop handles signals using sigsetjmp/siglongjmp and avoides the classic unix - select loop race condition with respect to signals. - -Aapl 2.2.0 -- Feb 25, 2002 -========================== - -Added AvlSet. AvlSet is like AvlMap except you give only a key. It also has - the set interface: Set, UnSet and IsSet that return boolean values and don't - really care to speak about element in a tree. - -Added walkable versions of avl tree. There is a walkable version of all - existing trees. They are AvliTree, AvliMel, AvliMelKey, AvliMap, and AvliSet. - Implemented by having the element inherit from DListEl and the tree inherit - from DList. All operations on the tree remain O(log(N)). Maintaining the list - pointers adds a constant amount of work. Adds 8 bytes to each element (for a - total of 24) and 12 bytes to the tree (for a total of 20). - -Added a few more sections to docs for AvlTree - -Aapl 2.1.0 -- Feb 22, 2002 -========================== - IMPORTANT: VectorC(int) no longer has the same meaning. Previously it set - allocation length with no size. Now it sets size with no allocation so - it is effectively useless as the table cannot grow it's allocation. It - will assert fail. Use VectorC(int, int) instead. See Vector docs. - - -Further split Vector into all possible types of upresizing and down - resizing. Existing Vectors remain unchanged except for constructor of - VectorC. It is now possible to have a vector that up resizes linear and - down exponential or up exponential and no down (constant), etc. Also - added VectorR, which allows for runtime setting of how to resize. - -Implemented a step for the linear vector. Now the following is true: - If the sequence of size requests is linear, the number of resizes is - also linear. - -Wrote docs for Vector. - -Aapl 2.0.0 -- Feb 17, 2002 -========================== - IMPORTANT: The 2.x.x version of Aapl is not backwards compatible with - 1.x.x version. Code that compiles against 1.x.x may not compile against - 2.x.x. The changes mostly amount to two things: - - 1) Making data and function class member naming convetions consistent. All - variables start with a lower case whereas functions, classes, constants - and type names all start with an upper case. - - 2) Shortening the symbols in resulting object code. Aapl classes achieved - great versatility by providing many tempate parameters mostly with - default values. Now this versatility is achieved by splitting the class - up into many different classes, each with thier own 'flavour' of the data - structure. This split is accomplished using preprocessor defines so there - is no code duplication. Classes now have shorter names and only required - template paramaters. This dramatically cuts down on long symbols and - eliminates compiler warnings on platforms that restrict symbol lengths. - - -Made all data member names consistent with general coding style: constant - identifiers start with upper case, variables (members too) start with lower. - -Split up DoubleList into DList and DListMel. Goal is shorter - symbol names by making two specialzed classes instead of one general class. - -Split up AvlTree into 4 classes. Same goal as split of DoubleList. - AvlTree now supports having multiple keys in the same object as well as - multiple tree pointers. This means a single object can be in different - trees that each use different keys. - -General rearranging of code in fsmgraph and changing templates to result - in shorter symbols. Try to keep symbol lengths < 255. - -Split up Vector into 3 classes, one for each table type. - -Split up VectorSet into 3 classes, one for each table type. Also renamed - it to VectSet for conciseness. - -Split up Binary Search table into 6 classes, one for each table type times - regular bst table and bstmap. The bstmap has the template semantics of - the old bstable. BsTable lets you give the whole object you want to put in - the table in the same manner as vector and avltree. - -Both AvlTree and BsTable use GetKey() functions in the element/element types. - Previously they required that the key member be named 'key.' It can now - be named anything, and it must be returned by GetKey. - -Added copy constructor for AvlTree. - -Added Avl test progs for the various flavours. - -Started some real docs. Currently only AvlTree is documented. - -Aapl 1.2.2 -- Feb 2, 2002 -========================= - -Started ChangeLog file. - -Parser Gen is now gone from aapl. It is not generic reusable code. It now - lives in Keller which will be released soon. - -Aapl 1.2.1 -- Jan 29, 2002 -========================== - -Can choose between assertions that dump details of the assertion - failure to the screen then segfault or just segfault using a define. - -Bugfix in avltree insertion. Only showed up when re-using avl element. - The insert routine was not nulling out appropriate pointers when adding - a leaf element. Worked correctly when always newing up element (how avl tree - is mostly used) as nulling out pointers is done by the element constructor. - -File Buffer size up to 1K. Was at 10 bytes for debugging/testing purposes. - -Transition count variables gone from fsm graph. Computing this is silly as - the vectors that implement the out transitions - -Transition priorities routines in FSM Graph no longer operate as a 'delta' - but instead as absolute. - -Function keys are no long a duple, instead just one integer. Using two - integers is overkill and not likely to be used. To acieve the same effect as - a duple, break the bits of the single integer into multiple fields. - -Allow the shifting of the priorities of start transitions to above some - value. Useful before staring machines so that out transitions of a final - state run before the transitions of the start state when going from a final - state back into the machine. - -Allow the clearing of transition functions. - -Cleaned up FsmAttachStates: no longer reusing transitions when replacing, - old trans are deleted and a new one is made. This eliminates the need for - the ResetTransition routine. - -When building machines, use max int and min int as the defaults for highIndex - and lowIndex. - -When building machines, transition 0 is always the error transition, - no longer insert it into the transition list on demand. - -When building machines, compute the global high and low index. Used by ragel. - -Out priorities have a concept of being set vs not being set. - -Concatenation and Star can now behave as if they do not cause leaving the fsm. - Allows the user to turn off picking up of out transitions and out priorities. - -Aapl 1.2 -- Aug 28, 2001 -======================== - -Initial release. diff --git a/aapl/Makefile.am b/aapl/Makefile.am deleted file mode 100644 index b3cd0c94..00000000 --- a/aapl/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -aaplinclude_HEADERS = \ - avlbasic.h avlimel.h avlmap.h bstcommon.h compare.h insertsort.h \ - sbstset.h avlcommon.h avlimelkey.h avlmel.h bstmap.h dlcommon.h \ - mergesort.h sbsttable.h avlibasic.h avliset.h avlmelkey.h bstset.h \ - dlist.h quicksort.h svector.h avlikeyless.h avlitree.h avlset.h \ - bsttable.h dlistmel.h resize.h table.h avlimap.h avlkeyless.h avltree.h \ - bubblesort.h dlistval.h sbstmap.h vector.h astring.h buffer.h rope.h diff --git a/aapl/README b/aapl/README deleted file mode 100644 index a2fa5e65..00000000 --- a/aapl/README +++ /dev/null @@ -1,6 +0,0 @@ -This directory contains the Aapl source distribution. For the -documentation, build scripts, test programs, ChangeLog, etc. get the -aapldev package. - -AaplDev and other information about Aapl is available from -http://www.elude.ca/aapl/ diff --git a/aapl/astring.h b/aapl/astring.h deleted file mode 100644 index 29d876f4..00000000 --- a/aapl/astring.h +++ /dev/null @@ -1,862 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_ASTRING_H -#define _AAPL_ASTRING_H - -#include -#include -#include -#include -#include -#include -#include - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -#ifdef AAPL_DOCUMENTATION - -/** - * \defgroup astring String - * \brief Implicitly shared copy-on-write string. - * - * @{ - */ - -/** - * \class String - * \brief Implicitly shared copy-on-write string. - */ - -/*@}*/ - -class String -{ -public: - /** - * \brief Create a null string. Data points to NULL. - */ - String(); - - /** - * \brief Construct a string from a c-style string. - * - * A new buffer is allocated for the c string. Initially, this string will - * be the only String class referencing the data. - */ - String( const char *s ); - - /** - * \brief Construct a string from a c-style string of specific length. - * - * A new buffer is allocated for the c string. Initially, this string will - * be the only String class referencing the data. - */ - String( const char *s, long len ); - - /** - * \brief Construct a string from another String. - * - * A refernce to the buffer allocated for s is taken. A new buffer is - * not allocated. - */ - String( const String &s ); - - /** - * \brief Construct a string using snprintf. - * - * Requires a maximum length for the resulting string. If the formatting - * (not including trailing null) requires more space than maxLen, the - * result will be truncated to maxLen long. Only the length actually - * written will be used by the new string. This string will be the only - * String class referencing the data. - */ - String( long maxLen, const char *format, ... ) - - /** - * \brief Clean up the string. - * - * If the string is not null, the referenced data is detached. If no other - * string refernces the detached data, it is deleted. - */ - ~String(); - - /** - * \brief Set the string from a c-style string. - * - * If this string is not null, the current buffer is dereferenced and - * possibly deleted. A new buffer is allocated (or possibly the old buffer - * reused) for the string. Initially, this string will be the only String - * class referencing the data. - * - * If s is null, then this string becomes a null ptr. - * - * \returns A reference to this. - */ - String &operator=( const char *s ); - - /** - * \brief Set the string from a c-style of specific length. - * - * If this string is not null, the current buffer is dereferenced and - * possibly deleted. A new buffer is allocated (or possibly the old buffer - * reused) for the string. Initially, this string will be the only String - * class referencing the data. - * - * If s is null, then this string becomes a null ptr. - * - * \returns A reference to this. - */ - void setAs( const char *s, long len ); - - /** - * \brief Set the string from a single char. - * - * The current buffer is dereferenced and possibly deleted. A new buffer - * is allocated (or possibly the old buffer reused) for the string. - * Initially, this string will be the only String class referencing the - * data. - * - * If s is null, then this string becomes a null ptr. - * - * \returns A reference to this. - */ - String &operator=( const char c ); - - - /** - * \brief Set the string from another String. - * - * If this string is not null, the current buffer is dereferenced and - * possibly deleted. A reference to the buffer allocated for s is taken. - * A new buffer is not allocated. - * - * If s is null, then this string becomes a null ptr. - * - * \returns a reference to this. - */ - String &operator=( const String &s ); - - /** - * \brief Append a c string to the end of this string. - * - * If this string shares its allocation with another, a copy is first - * taken. The buffer for this string is grown and s is appended to the - * end. - * - * If s is null nothing happens. - * - * \returns a reference to this. - */ - String &operator+=( const char *s ); - - /** - * \brief Append a c string of specific length to the end of this string. - * - * If this string shares its allocation with another, a copy is first - * taken. The buffer for this string is grown and s is appended to the - * end. - * - * If s is null nothing happens. - * - * \returns a reference to this. - */ - void append( const char *s, long len ); - - /** - * \brief Append a single char to the end of this string. - * - * If this string shares its allocation with another, a copy is first - * taken. The buffer for this string is grown and s is appended to the - * end. - * - * \returns a reference to this. - */ - String &operator+=( const char c ); - - /** - * \brief Append a String to the end of this string. - * - * If this string shares its allocation with another, a copy is first - * taken. The buffer for this string is grown and the data of s is - * appeneded to the end. - * - * If s is null nothing happens. - * - * returns a reference to this. - */ - String &operator+=( const String &s ); - - /** - * \brief Cast to a char star. - * - * \returns the string data. A null string returns 0. - */ - operator char*() const; - - /** - * \brief Get a pointer to the data. - * - * \returns the string Data - */ - char *get() const; - - /** - * \brief Get the length of the string - * - * If the string is null, then undefined behaviour results. - * - * \returns the length of the string. - */ - long length() const; - - /** - * \brief Pointer to the data. - * - * Publically accessible pointer to the data. Immediately in front of the - * string data block is the string header which stores the refcount and - * length. Consequently, care should be taken if modifying this pointer. - */ - char *data; -}; - -/** - * \relates String - * \brief Concatenate a c-style string and a String. - * - * \returns The concatenation of the two strings in a String. - */ -String operator+( const String &s1, const char *s2 ); - -/** - * \relates String - * \brief Concatenate a String and a c-style string. - * - * \returns The concatenation of the two strings in a String. - */ -String operator+( const char *s1, const String &s2 ); - -/** - * \relates String - * \brief Concatenate two String classes. - * - * \returns The concatenation of the two strings in a String. - */ -String operator+( const String &s1, const String &s2 ); - -#endif - -template class StrTmpl -{ -public: - class Fresh {}; - - /* Header located just before string data. Keeps the length and a refcount on - * the data. */ - struct Head - { - long refCount; - long length; - }; - - /** - * \brief Create a null string. - */ - StrTmpl() : data(0) { } - - /* Clean up the string. */ - ~StrTmpl(); - - /* Construct a string from a c-style string. */ - StrTmpl( const char *s ); - - /* Construct a string from a c-style string of specific len. */ - StrTmpl( const char *s, long len ); - - /* Allocate len spaces. */ - StrTmpl( const Fresh &, long len ); - - /* Construct a string from another StrTmpl. */ - StrTmpl( const StrTmpl &s ); - - /* Construct a string from with, sprintf. */ - StrTmpl( long lenGuess, const char *format, ... ); - - /* Set the string from a c-style string. */ - StrTmpl &operator=( const char *s ); - - /* Set the string from a c-style string of specific len. */ - void setAs( const char *s, long len ); - - /* Allocate len spaces. */ - void setAs( const Fresh &, long len ); - - void chop( long len ); - - /* Construct a string from with, sprintf. */ - void setAs( long lenGuess, const char *format, ... ); - - /* Set the string from a single char. */ - StrTmpl &operator=( const char c ); - - /* Set the string from another StrTmpl. */ - StrTmpl &operator=( const StrTmpl &s ); - - /* Append a c string to the end of this string. */ - StrTmpl &operator+=( const char *s ); - - /* Append a c string to the end of this string of specifi len. */ - void append( const char *s, long len ); - - /* Append a single char to the end of this string. */ - StrTmpl &operator+=( const char c ); - - /* Append an StrTmpl to the end of this string. */ - StrTmpl &operator+=( const StrTmpl &s ); - - /* Cast to a char star. */ - operator char*() const { return data; } - - /* Get a pointer to the data. */ - char *get() const { return data; } - - /* Return the length of the string. Must check for null data pointer. */ - long length() const { return data ? (((Head*)data)-1)->length : 0; } - - void empty() { setAs( (const char *)0, 0 ); } - - /** - * \brief Pointer to the data. - */ - char *data; - -protected: - /* Make space for a string of length len to be appended. */ - char *appendSpace( long len ); - void initSpace( long length ); - void setSpace( long length ); - - template friend StrTmpl operator+( - const StrTmpl &s1, const char *s2 ); - template friend StrTmpl operator+( - const char *s1, const StrTmpl &s2 ); - template friend StrTmpl operator+( - const StrTmpl &s1, const StrTmpl &s2 ); - -private: - /* A dummy struct solely to make a constructor that will never be - * ambiguous with the public constructors. */ - struct DisAmbig { }; - StrTmpl( char *data, const DisAmbig & ) : data(data) { } -}; - -/* Free all mem used by the string. */ -template StrTmpl::~StrTmpl() -{ - if ( data != 0 ) { - /* If we are the only ones referencing the string, then delete it. */ - Head *head = ((Head*) data) - 1; - head->refCount -= 1; - if ( head->refCount == 0 ) - free( head ); - } -} - -/* Create from a c-style string. */ -template StrTmpl::StrTmpl( const char *s ) -{ - if ( s == 0 ) - data = 0; - else { - /* Find the length and allocate the space for the shared string. */ - long length = strlen( s ); - - /* Init space for the data. */ - initSpace( length ); - - /* Copy in the data. */ - memcpy( data, s, length+1 ); - } -} - -/* Create from a c-style string. */ -template StrTmpl::StrTmpl( const char *s, long length ) -{ - if ( s == 0 ) - data = 0; - else { - /* Init space for the data. */ - initSpace( length ); - - /* Copy in the data. */ - memcpy( data, s, length ); - data[length] = 0; - } -} - -/* Create from a c-style string. */ -template StrTmpl::StrTmpl( const Fresh &, long length ) -{ - /* Init space for the data. */ - initSpace( length ); - data[length] = 0; -} - -/* Create from another string class. */ -template StrTmpl::StrTmpl( const StrTmpl &s ) -{ - if ( s.data == 0 ) - data = 0; - else { - /* Take a reference to the string. */ - Head *strHead = ((Head*)s.data) - 1; - strHead->refCount += 1; - data = (char*) (strHead+1); - } -} - -/* Construct a string from with, sprintf. */ -template StrTmpl::StrTmpl( long lenGuess, const char *format, ... ) -{ - /* Set the string for len. */ - initSpace( lenGuess ); - - va_list args; - - /* Write to the temporary buffer. */ - va_start( args, format ); - - long written = vsnprintf( data, lenGuess+1, format, args ); - if ( written > lenGuess ) { - setSpace( written ); - written = vsnprintf( data, written+1, format, args ); - } - chop( written ); - - va_end( args ); -} - -/* Construct a string from with, sprintf. */ -template void StrTmpl::setAs( long lenGuess, const char *format, ... ) -{ - /* Set the string for len. */ - setSpace( lenGuess ); - - va_list args; - - /* Write to the temporary buffer. */ - va_start( args, format ); - - long written = vsnprintf( data, lenGuess+1, format, args ); - if ( written > lenGuess ) { - setSpace( written ); - written = vsnprintf( data, written+1, format, args ); - } - chop( written ); - - va_end( args ); -} - -template void StrTmpl::initSpace( long length ) -{ - /* Find the length and allocate the space for the shared string. */ - Head *head = (Head*) malloc( sizeof(Head) + length+1 ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Init the header. */ - head->refCount = 1; - head->length = length; - - /* Save the pointer to the data. */ - data = (char*) (head+1); -} - - -/* Set this string to be the c string exactly. The old string is discarded. - * Returns a reference to this. */ -template StrTmpl &StrTmpl::operator=( const char *s ) -{ - if ( s == 0 ) { - /* Just free the data, we are being set to null. */ - if ( data != 0 ) { - Head *head = ((Head*)data) - 1; - head->refCount -= 1; - if ( head->refCount == 0 ) - free(head); - data = 0; - } - } - else { - /* Find the length of the string we are setting. */ - long length = strlen( s ); - - /* Set the string for len. */ - setSpace( length ); - - /* Copy in the data. */ - memcpy( data, s, length+1 ); - } - return *this; -} - -/* Set this string to be the c string exactly. The old string is discarded. - * Returns a reference to this. */ -template void StrTmpl::setAs( const char *s, long length ) -{ - if ( s == 0 ) { - /* Just free the data, we are being set to null. */ - if ( data != 0 ) { - Head *head = ((Head*)data) - 1; - head->refCount -= 1; - if ( head->refCount == 0 ) - free(head); - data = 0; - } - } - else { - /* Set the string for len. */ - setSpace( length ); - - /* Copy in the data. */ - memcpy( data, s, length ); - data[length] = 0; - } -} - -template void StrTmpl::chop( long length ) -{ - /* Detach from the existing string. */ - Head *head = ((Head*)data) - 1; - assert( head->refCount == 1 ); - assert( length <= head->length ); - head->length = length; - data[length] = 0; -} - -/* Set this string to be the c string exactly. The old string is discarded. - * Returns a reference to this. */ -template void StrTmpl::setAs( const Fresh &, long length ) -{ - setSpace( length ); - data[length] = 0; -} - -/* Set this string to be the single char exactly. The old string is discarded. - * Returns a reference to this. */ -template StrTmpl &StrTmpl::operator=( const char c ) -{ - /* Set to length 1. */ - setSpace( 1 ); - - /* Copy in the data. */ - data[0] = c; - data[1] = 0; - - /* Return ourselves. */ - return *this; -} - -/* Set this string to be the StrTmpl s exactly. The old string is - * discarded. */ -template StrTmpl &StrTmpl::operator=( const StrTmpl &s ) -{ - /* Detach from the existing string. */ - if ( data != 0 ) { - Head *head = ((Head*)data) - 1; - head->refCount -= 1; - if ( head->refCount == 0 ) - free( head ); - } - - if ( s.data != 0 ) { - /* Take a reference to the string. */ - Head *strHead = ((Head*)s.data) - 1; - strHead->refCount += 1; - data = (char*)(strHead+1); - } - else { - /* Setting from a null string, just null our pointer. */ - data = 0; - } - return *this; -} - -/* Prepare the string to be set to something else of the given length. */ -template void StrTmpl::setSpace( long length ) -{ - /* Detach from the existing string. */ - Head *head = ((Head*)data) - 1; - if ( data != 0 && --head->refCount == 0 ) { - /* Resuse the space. */ - head = (Head*) realloc( head, sizeof(Head) + length+1 ); - } - else { - /* Need to make new space, there is no usable old space. */ - head = (Head*) malloc( sizeof(Head) + length+1 ); - } - if ( head == 0 ) - throw std::bad_alloc(); - - /* Init the header. */ - head->refCount = 1; - head->length = length; - - /* Copy in the data and save the pointer to it. */ - data = (char*) (head+1); -} - -/* Append a c-style string to the end of this string. Returns a reference to - * this */ -template StrTmpl &StrTmpl::operator+=( const char *s ) -{ - /* Find the length of the string appended. */ - if ( s != 0 ) { - /* Get the string length and make space on the end. */ - long addedLen = strlen( s ); - char *dest = appendSpace( addedLen ); - - /* Copy the data in. Plus one for the null. */ - memcpy( dest, s, addedLen+1 ); - } - return *this; -} - -/* Append a c-style string of specific length to the end of this string. - * Returns a reference to this */ -template void StrTmpl::append( const char *s, long length ) -{ - /* Find the length of the string appended. */ - if ( s != 0 ) { - /* Make space on the end. */ - char *dest = appendSpace( length ); - - /* Copy the data in. Plus one for the null. */ - memcpy( dest, s, length ); - dest[length] = 0; - } -} - -/* Append a single char to the end of this string. Returns a reference to - * this */ -template StrTmpl &StrTmpl::operator+=( const char c ) -{ - /* Grow on the end. */ - char *dst = appendSpace( 1 ); - - /* Append a single charachter. */ - dst[0] = c; - dst[1] = 0; - return *this; -} - - -/* Append an StrTmpl string to the end of this string. Returns a reference - * to this */ -template StrTmpl &StrTmpl::operator+=( const StrTmpl &s ) -{ - /* Find the length of the string appended. */ - if ( s.data != 0 ) { - /* Find the length to append. */ - long addedLen = (((Head*)s.data) - 1)->length; - - /* Make space on the end to put the string. */ - char *dest = appendSpace( addedLen ); - - /* Append the data, add one for the null. */ - memcpy( dest, s.data, addedLen+1 ); - } - return *this; -} - -/* Make space for a string of length len to be appended. */ -template char *StrTmpl::appendSpace( long len ) -{ - if ( data == 0 ) { - initSpace( len ); - return data; - } - else { - /* Find the length of this and the string appended. */ - Head *head = (((Head*)data) - 1); - long thisLen = head->length; - - if ( head->refCount == 1 ) { - /* No other string is using the space, grow this space. */ - head = (Head*) realloc( head, - sizeof(Head) + thisLen + len + 1 ); - if ( head == 0 ) - throw std::bad_alloc(); - data = (char*) (head+1); - - /* Adjust the length. */ - head->length += len; - } - else { - /* Another string is using this space, make new space. */ - head->refCount -= 1; - Head *newHead = (Head*) malloc( - sizeof(Head) + thisLen + len + 1 ); - if ( newHead == 0 ) - throw std::bad_alloc(); - data = (char*) (newHead+1); - - /* Set the new header and data from this. */ - newHead->refCount = 1; - newHead->length = thisLen + len; - memcpy( data, head+1, thisLen ); - } - - /* Return writing position. */ - return data + thisLen; - } -} - -/* Concatenate a String and a c-style string. */ -template StrTmpl operator+( const StrTmpl &s1, const char *s2 ) -{ - /* Find s2 length and alloc the space for the result. */ - long str1Len = (((typename StrTmpl::Head*)(s1.data)) - 1)->length; - long str2Len = strlen( s2 ); - - typename StrTmpl::Head *head = (typename StrTmpl::Head*) - malloc( sizeof(typename StrTmpl::Head) + str1Len + str2Len + 1 ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Set up the header. */ - head->refCount = 1; - head->length = str1Len + str2Len; - - /* Save the pointer to data and copy the data in. */ - char *data = (char*) (head+1); - memcpy( data, s1.data, str1Len ); - memcpy( data + str1Len, s2, str2Len + 1 ); - return StrTmpl( data, typename StrTmpl::DisAmbig() ); -} - -/* Concatenate a c-style string and a String. */ -template StrTmpl operator+( const char *s1, const StrTmpl &s2 ) -{ - /* Find s2 length and alloc the space for the result. */ - long str1Len = strlen( s1 ); - long str2Len = (((typename StrTmpl::Head*)(s2.data)) - 1)->length; - - typename StrTmpl::Head *head = (typename StrTmpl::Head*) - malloc( sizeof(typename StrTmpl::Head) + str1Len + str2Len + 1 ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Set up the header. */ - head->refCount = 1; - head->length = str1Len + str2Len; - - /* Save the pointer to data and copy the data in. */ - char *data = (char*) (head+1); - memcpy( data, s1, str1Len ); - memcpy( data + str1Len, s2.data, str2Len + 1 ); - return StrTmpl( data, typename StrTmpl::DisAmbig() ); -} - -/* Add two StrTmpl strings. */ -template StrTmpl operator+( const StrTmpl &s1, const StrTmpl &s2 ) -{ - /* Find s2 length and alloc the space for the result. */ - long str1Len = (((typename StrTmpl::Head*)(s1.data)) - 1)->length; - long str2Len = (((typename StrTmpl::Head*)(s2.data)) - 1)->length; - typename StrTmpl::Head *head = (typename StrTmpl::Head*) - malloc( sizeof(typename StrTmpl::Head) + str1Len + str2Len + 1 ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Set up the header. */ - head->refCount = 1; - head->length = str1Len + str2Len; - - /* Save the pointer to data and copy the data in. */ - char *data = (char*) (head+1); - memcpy( data, s1.data, str1Len ); - memcpy( data + str1Len, s2.data, str2Len + 1 ); - return StrTmpl( data, typename StrTmpl::DisAmbig() ); -} - -/* Operator used in case the compiler does not support the conversion. */ -template inline std::ostream &operator<<( std::ostream &o, const StrTmpl &s ) -{ - return o.write( s.data, s.length() ); -} - -typedef StrTmpl String; - -/* - * StringStream for appending to streams with an ostream. - */ -struct StringOutBuf -: - public std::streambuf -{ - StringOutBuf( String &s ) - : - s(s) - { - } - - int_type overflow( int_type c ) - { - if ( c != EOF ) { - char z = c; - s.append( &z, 1 ); - } - return c; - } - - std::streamsize xsputn( const char *data, std::streamsize num ) - { - s.append( data, num ); - return num; - } - - String &s; -}; - -struct StringStream -: - public std::ostream -{ - StringStream( String &s ) - : - std::ostream( 0 ), - buf( s ) - { - rdbuf( &buf ); - } - - StringOutBuf buf; -}; - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_ASTRING_H */ diff --git a/aapl/avlbasic.h b/aapl/avlbasic.h deleted file mode 100644 index 4e31dd97..00000000 --- a/aapl/avlbasic.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLBASIC_H -#define _AAPL_AVLBASIC_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlBasic - * \brief AVL Tree in which the entire element structure is the key. - * - * AvlBasic is an AVL tree that does not distinguish between the element that - * it contains and the key. The entire element structure is the key that is - * used to compare the relative ordering of elements. This is similar to the - * BstSet structure. - * - * AvlBasic does not assume ownership of elements in the tree. Items must be - * explicitly de-allocated. - */ - -/*@}*/ - -#define BASE_EL(name) name -#define BASEKEY(name) name -#define AVLMEL_CLASSDEF class Element, class Compare -#define AVLMEL_TEMPDEF class Element, class Compare -#define AVLMEL_TEMPUSE Element, Compare -#define AvlTree AvlBasic -#define AVL_BASIC - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef AVL_BASIC - -#endif /* _AAPL_AVLBASIC_H */ diff --git a/aapl/avlcommon.h b/aapl/avlcommon.h deleted file mode 100644 index a1f86692..00000000 --- a/aapl/avlcommon.h +++ /dev/null @@ -1,1637 +0,0 @@ -/* - * Copyright 2001 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* This header is not wrapped in ifndef becuase it is not intended to - * be included by the user. */ - -#include - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -#ifdef WALKABLE -/* This is used by AvlTree, AvlMel and AvlMelKey so it - * must be protected by global ifdefs. */ -#ifndef __AAPL_AVLI_EL__ -#define __AAPL_AVLI_EL__ - -/** - * \brief Tree element properties for linked AVL trees. - * - * AvliTreeEl needs to be inherited by classes that intend to be element in an - * AvliTree. - */ -template struct AvliTreeEl -{ - /** - * \brief Tree pointers connecting element in a tree. - */ - SubClassEl *left, *right, *parent; - - /** - * \brief Linked list pointers. - */ - SubClassEl *prev, *next; - - /** - * \brief Height of the tree rooted at this element. - * - * Height is required by the AVL balancing algorithm. - */ - long height; -}; -#endif /* __AAPL_AVLI_EL__ */ - -#else /* not WALKABLE */ - -/* This is used by All the non walkable trees so it must be - * protected by a global ifdef. */ -#ifndef __AAPL_AVL_EL__ -#define __AAPL_AVL_EL__ -/** - * \brief Tree element properties for linked AVL trees. - * - * AvlTreeEl needs to be inherited by classes that intend to be element in an - * AvlTree. - */ -template struct AvlTreeEl -{ - /** - * \brief Tree pointers connecting element in a tree. - */ - SubClassEl *left, *right, *parent; - - /** - * \brief Height of the tree rooted at this element. - * - * Height is required by the AVL balancing algorithm. - */ - long height; -}; -#endif /* __AAPL_AVL_EL__ */ -#endif /* def WALKABLE */ - - -#if defined( AVLTREE_MAP ) - -#ifdef WALKABLE - -/** - * \brief Tree element for AvliMap - * - * Stores the key and value pair. - */ -template struct AvliMapEl : - public AvliTreeEl< AvliMapEl > -{ - AvliMapEl(const Key &key) - : key(key) { } - AvliMapEl(const Key &key, const Value &value) - : key(key), value(value) { } - - const Key &getKey() const { return key; } - - /** \brief The key. */ - Key key; - - /** \brief The value. */ - Value value; -}; -#else /* not WALKABLE */ - -/** - * \brief Tree element for AvlMap - * - * Stores the key and value pair. - */ -template struct AvlMapEl : - public AvlTreeEl< AvlMapEl > -{ - AvlMapEl(const Key &key) - : key(key) { } - AvlMapEl(const Key &key, const Value &value) - : key(key), value(value) { } - - const Key &getKey() const { return key; } - - /** \brief The key. */ - Key key; - - /** \brief The value. */ - Value value; -}; -#endif /* def WALKABLE */ - -#elif defined( AVLTREE_SET ) - -#ifdef WALKABLE -/** - * \brief Tree element for AvliSet - * - * Stores the key. - */ -template struct AvliSetEl : - public AvliTreeEl< AvliSetEl > -{ - AvliSetEl(const Key &key) : key(key) { } - - const Key &getKey() const { return key; } - - /** \brief The key. */ - Key key; -}; -#else /* not WALKABLE */ -/** - * \brief Tree element for AvlSet - * - * Stores the key. - */ -template struct AvlSetEl : - public AvlTreeEl< AvlSetEl > -{ - AvlSetEl(const Key &key) : key(key) { } - - const Key &getKey() const { return key; } - - /** \brief The key. */ - Key key; -}; -#endif /* def WALKABLE */ - -#endif /* AVLTREE_SET */ - -/* Common AvlTree Class */ -template < AVLMEL_CLASSDEF > class AvlTree -#if !defined( AVL_KEYLESS ) && defined ( WALKABLE ) - : public Compare, public BASELIST -#elif !defined( AVL_KEYLESS ) - : public Compare -#elif defined( WALKABLE ) - : public BASELIST -#endif -{ -public: - typedef Element El; - - /** - * \brief Create an empty tree. - */ -#ifdef WALKABLE - AvlTree() : root(0), treeSize(0) { } -#else - AvlTree() : root(0), head(0), tail(0), treeSize(0) { } -#endif - - /** - * \brief Perform a deep copy of the tree. - * - * Each element is duplicated for the new tree. Copy constructors are used - * to create the new elements. - */ - AvlTree(const AvlTree &other); - -#if defined( AVLTREE_MAP ) || defined( AVLTREE_SET ) - /** - * \brief Clear the contents of the tree. - * - * All element are deleted. - */ - ~AvlTree() { empty(); } - - /** - * \brief Perform a deep copy of the tree. - * - * Each element is duplicated for the new tree. Copy constructors are used - * to create the new element. If this tree contains items, they are first - * deleted. - * - * \returns A reference to this. - */ - AvlTree &operator=( const AvlTree &tree ); - - /** - * \brief Transfer the elements of another tree into this. - * - * First deletes all elements in this tree. - */ - void transfer( AvlTree &tree ); -#else - /** - * \brief Abandon all elements in the tree. - * - * Tree elements are not deleted. - */ - ~AvlTree() {} - - /** - * \brief Perform a deep copy of the tree. - * - * Each element is duplicated for the new tree. Copy constructors are used - * to create the new element. If this tree contains items, they are - * abandoned. - * - * \returns A reference to this. - */ - AvlTree &operator=( const AvlTree &tree ); - - /** - * \brief Transfer the elements of another tree into this. - * - * All elements in this tree are abandoned first. - */ - void transfer( AvlTree &tree ); -#endif - -#ifndef AVL_KEYLESS - /* Insert a element into the tree. */ - Element *insert( Element *element, Element **lastFound = 0 ); - -#ifdef AVL_BASIC - /* Find a element in the tree. Returns the element if - * element exists, false otherwise. */ - Element *find( const Element *element ) const; - -#else - Element *insert( const Key &key, Element **lastFound = 0 ); - -#ifdef AVLTREE_MAP - Element *insert( const Key &key, const Value &val, - Element **lastFound = 0 ); -#endif - - /* Find a element in the tree. Returns the element if - * key exists, false otherwise. */ - Element *find( const Key &key ) const; - - /* Detach a element from the tree. */ - Element *detach( const Key &key ); - - /* Detach and delete a element from the tree. */ - bool remove( const Key &key ); -#endif /* AVL_BASIC */ -#endif /* AVL_KEYLESS */ - - /* Detach a element from the tree. */ - Element *detach( Element *element ); - - /* Detach and delete a element from the tree. */ - void remove( Element *element ); - - /* Free all memory used by tree. */ - void empty(); - - /* Abandon all element in the tree. Does not delete element. */ - void abandon(); - - /** Root element of the tree. */ - Element *root; - -#ifndef WALKABLE - Element *head, *tail; -#endif - - /** The number of element in the tree. */ - long treeSize; - - /** \brief Return the number of elements in the tree. */ - long length() const { return treeSize; } - - /** \brief Return the number of elements in the tree. */ - long size() const { return treeSize; } - - /* Various classes for setting the iterator */ - struct Iter; - struct IterFirst { IterFirst( const AvlTree &t ) : t(t) { } const AvlTree &t; }; - struct IterLast { IterLast( const AvlTree &t ) : t(t) { } const AvlTree &t; }; - struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; - struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; - -#ifdef WALKABLE - /** - * \brief Avl Tree Iterator. - * \ingroup iterators - */ - struct Iter - { - /* Default construct. */ - Iter() : ptr(0) { } - - /* Construct from an avl tree and iterator-setting classes. */ - Iter( const AvlTree &t ) : ptr(t.head) { } - Iter( const IterFirst &af ) : ptr(af.t.head) { } - Iter( const IterLast &al ) : ptr(al.t.tail) { } - Iter( const IterNext &an ) : ptr(findNext(an.i.ptr)) { } - Iter( const IterPrev &ap ) : ptr(findPrev(ap.i.ptr)) { } - - /* Assign from a tree and iterator-setting classes. */ - Iter &operator=( const AvlTree &tree ) { ptr = tree.head; return *this; } - Iter &operator=( const IterFirst &af ) { ptr = af.t.head; return *this; } - Iter &operator=( const IterLast &al ) { ptr = al.t.tail; return *this; } - Iter &operator=( const IterNext &an ) { ptr = findNext(an.i.ptr); return *this; } - Iter &operator=( const IterPrev &ap ) { ptr = findPrev(ap.i.ptr); return *this; } - - /** \brief Less than end? */ - bool lte() const { return ptr != 0; } - - /** \brief At end? */ - bool end() const { return ptr == 0; } - - /** \brief Greater than beginning? */ - bool gtb() const { return ptr != 0; } - - /** \brief At beginning? */ - bool beg() const { return ptr == 0; } - - /** \brief At first element? */ - bool first() const { return ptr && ptr->BASE_EL(prev) == 0; } - - /** \brief At last element? */ - bool last() const { return ptr && ptr->BASE_EL(next) == 0; } - - /** \brief Implicit cast to Element*. */ - operator Element*() const { return ptr; } - - /** \brief Dereference operator returns Element&. */ - Element &operator *() const { return *ptr; } - - /** \brief Arrow operator returns Element*. */ - Element *operator->() const { return ptr; } - - /** \brief Move to next item. */ - inline Element *operator++(); - - /** \brief Move to next item. */ - inline Element *operator++(int); - - /** \brief Move to next item. */ - inline Element *increment(); - - /** \brief Move to previous item. */ - inline Element *operator--(); - - /** \brief Move to previous item. */ - inline Element *operator--(int); - - /** \brief Move to previous item. */ - inline Element *decrement(); - - /** \brief Return the next item. Does not modify this. */ - IterNext next() const { return IterNext( *this ); } - - /** \brief Return the previous item. Does not modify this. */ - IterPrev prev() const { return IterPrev( *this ); } - - private: - static Element *findPrev( Element *element ) { return element->BASE_EL(prev); } - static Element *findNext( Element *element ) { return element->BASE_EL(next); } - - public: - - /** \brief The iterator is simply a pointer. */ - Element *ptr; - }; - -#else - - /** - * \brief Avl Tree Iterator. - * \ingroup iterators - */ - struct Iter - { - /* Default construct. */ - Iter() : ptr(0), tree(0) { } - - /* Construct from a tree and iterator-setting classes. */ - Iter( const AvlTree &t ) : ptr(t.head), tree(&t) { } - Iter( const IterFirst &af ) : ptr(af.t.head), tree(&af.t) { } - Iter( const IterLast &al ) : ptr(al.t.tail), tree(&al.t) { } - Iter( const IterNext &an ) : ptr(findNext(an.i.ptr)), tree(an.i.tree) { } - Iter( const IterPrev &ap ) : ptr(findPrev(ap.i.ptr)), tree(ap.i.tree) { } - - /* Assign from a tree and iterator-setting classes. */ - Iter &operator=( const AvlTree &t ) - { ptr = t.head; tree = &t; return *this; } - Iter &operator=( const IterFirst &af ) - { ptr = af.t.head; tree = &af.t; return *this; } - Iter &operator=( const IterLast &al ) - { ptr = al.t.tail; tree = &al.t; return *this; } - Iter &operator=( const IterNext &an ) - { ptr = findNext(an.i.ptr); tree = an.i.tree; return *this; } - Iter &operator=( const IterPrev &ap ) - { ptr = findPrev(ap.i.ptr); tree = ap.i.tree; return *this; } - - /** \brief Less than end? */ - bool lte() const { return ptr != 0; } - - /** \brief At end? */ - bool end() const { return ptr == 0; } - - /** \brief Greater than beginning? */ - bool gtb() const { return ptr != 0; } - - /** \brief At beginning? */ - bool beg() const { return ptr == 0; } - - /** \brief At first element? */ - bool first() const { return ptr && ptr == tree->head; } - - /** \brief At last element? */ - bool last() const { return ptr && ptr == tree->tail; } - - /** \brief Implicit cast to Element*. */ - operator Element*() const { return ptr; } - - /** \brief Dereference operator returns Element&. */ - Element &operator *() const { return *ptr; } - - /** \brief Arrow operator returns Element*. */ - Element *operator->() const { return ptr; } - - /** \brief Move to next item. */ - inline Element *operator++(); - - /** \brief Move to next item. */ - inline Element *operator++(int); - - /** \brief Move to next item. */ - inline Element *increment(); - - /** \brief Move to previous item. */ - inline Element *operator--(); - - /** \brief Move to previous item. */ - inline Element *operator--(int); - - /** \brief Move to previous item. */ - inline Element *decrement(); - - /** \brief Return the next item. Does not modify this. */ - IterNext next() const { return IterNext( *this ); } - - /** \brief Return the previous item. Does not modify this. */ - IterPrev prev() const { return IterPrev( *this ); } - - private: - static Element *findPrev( Element *element ); - static Element *findNext( Element *element ); - - public: - /** \brief The iterator is simply a pointer. */ - Element *ptr; - - /* The list is not walkable so we need to keep a pointerto the tree - * so we can test against head and tail in O(1) time. */ - const AvlTree *tree; - }; -#endif - - /** \brief Return first element. */ - IterFirst first() { return IterFirst( *this ); } - - /** \brief Return last element. */ - IterLast last() { return IterLast( *this ); } - -protected: - /* Recursive worker for the copy constructor. */ - Element *copyBranch( Element *element ); - - /* Recursively delete element in the tree. */ - void deleteChildrenOf(Element *n); - - /* rebalance the tree beginning at the leaf whose - * grandparent is unbalanced. */ - Element *rebalance(Element *start); - - /* Move up the tree from a given element, recalculating the heights. */ - void recalcHeights(Element *start); - - /* Move up the tree and find the first element whose - * grand-parent is unbalanced. */ - Element *findFirstUnbalGP(Element *start); - - /* Move up the tree and find the first element which is unbalanced. */ - Element *findFirstUnbalEl(Element *start); - - /* Replace a element in the tree with another element not in the tree. */ - void replaceEl(Element *element, Element *replacement); - - /* Remove a element from the tree and put another (normally a child of element) - * in its place. */ - void removeEl(Element *element, Element *filler); - - /* Once an insertion point is found at a leaf then do the insert. */ - void attachRebal( Element *element, Element *parentEl, Element *lastLess ); -}; - -/* Copy constructor. New up each item. */ -template AvlTree:: - AvlTree(const AvlTree &other) -#if !defined( AVL_KEYLESS ) && defined ( WALKABLE ) - /* BASELIST should be made empty. The copyBranch function - * will fill in the details for us. */ - : Compare( other ), BASELIST() -#elif !defined( AVL_KEYLESS ) - : Compare( other ) -#elif defined( WALKABLE ) - : BASELIST( ) -#endif -{ - treeSize = other.treeSize; - root = other.root; - -#ifndef WALKABLE - head = 0; - tail = 0; -#endif - - /* If there is a root, copy the tree. */ - if ( other.root != 0 ) - root = copyBranch( other.root ); -} - -#if defined( AVLTREE_MAP ) || defined( AVLTREE_SET ) - -/* Assignment does deep copy. */ -template AvlTree &AvlTree:: - operator=( const AvlTree &other ) -{ - /* Clear the tree first. */ - empty(); - - /* Reset the list pointers, the tree copy will fill in the list for us. */ -#ifdef WALKABLE - BASELIST::abandon(); -#else - head = 0; - tail = 0; -#endif - - /* Copy the entire tree. */ - treeSize = other.treeSize; - root = other.root; - if ( other.root != 0 ) - root = copyBranch( other.root ); - return *this; -} - -template void AvlTree:: - transfer(AvlTree &other) -{ - /* Clear the tree first. */ - empty(); - - treeSize = other.treeSize; - root = other.root; - -#ifdef WALKABLE - BASELIST::head = other.BASELIST::head; - BASELIST::tail = other.BASELIST::tail; - BASELIST::listLen = other.BASELIST::listLen; -#else - head = other.head; - tail = other.tail; -#endif - - other.abandon(); -} - -#else /* ! AVLTREE_MAP && ! AVLTREE_SET */ - -/* Assignment does deep copy. This version does not clear the tree first. */ -template AvlTree &AvlTree:: - operator=( const AvlTree &other ) -{ - /* Reset the list pointers, the tree copy will fill in the list for us. */ -#ifdef WALKABLE - BASELIST::abandon(); -#else - head = 0; - tail = 0; -#endif - - /* Copy the entire tree. */ - treeSize = other.treeSize; - root = other.root; - if ( other.root != 0 ) - root = copyBranch( other.root ); - return *this; -} - -template void AvlTree:: - transfer(AvlTree &other) -{ - treeSize = other.treeSize; - root = other.root; - -#ifdef WALKABLE - BASELIST::head = other.BASELIST::head; - BASELIST::tail = other.BASELIST::tail; - BASELIST::listLen = other.BASELIST::listLen; -#else - head = other.head; - tail = other.tail; -#endif - - other.abandon(); -} - -#endif - -/* - * Iterator operators. - */ - -/* Prefix ++ */ -template Element *AvlTree::Iter:: - operator++() -{ - return ptr = findNext( ptr ); -} - -/* Postfix ++ */ -template Element *AvlTree::Iter:: - operator++(int) -{ - Element *rtn = ptr; - ptr = findNext( ptr ); - return rtn; -} - -/* increment */ -template Element *AvlTree::Iter:: - increment() -{ - return ptr = findNext( ptr ); -} - -/* Prefix -- */ -template Element *AvlTree::Iter:: - operator--() -{ - return ptr = findPrev( ptr ); -} - -/* Postfix -- */ -template Element *AvlTree::Iter:: - operator--(int) -{ - Element *rtn = ptr; - ptr = findPrev( ptr ); - return rtn; -} - -/* decrement */ -template Element *AvlTree::Iter:: - decrement() -{ - return ptr = findPrev( ptr ); -} - -#ifndef WALKABLE - -/* Move ahead one. */ -template Element *AvlTree::Iter:: - findNext( Element *element ) -{ - /* Try to go right once then infinite left. */ - if ( element->BASE_EL(right) != 0 ) { - element = element->BASE_EL(right); - while ( element->BASE_EL(left) != 0 ) - element = element->BASE_EL(left); - } - else { - /* Go up to parent until we were just a left child. */ - while ( true ) { - Element *last = element; - element = element->BASE_EL(parent); - if ( element == 0 || element->BASE_EL(left) == last ) - break; - } - } - return element; -} - -/* Move back one. */ -template Element *AvlTree::Iter:: - findPrev( Element *element ) -{ - /* Try to go left once then infinite right. */ - if ( element->BASE_EL(left) != 0 ) { - element = element->BASE_EL(left); - while ( element->BASE_EL(right) != 0 ) - element = element->BASE_EL(right); - } - else { - /* Go up to parent until we were just a left child. */ - while ( true ) { - Element *last = element; - element = element->BASE_EL(parent); - if ( element == 0 || element->BASE_EL(right) == last ) - break; - } - } - return element; -} - -#endif - - -/* Recursive worker for tree copying. */ -template Element *AvlTree:: - copyBranch( Element *element ) -{ - /* Duplicate element. Either the base element's copy constructor or defaul - * constructor will get called. Both will suffice for initting the - * pointers to null when they need to be. */ - Element *retVal = new Element(*element); - - /* If the left tree is there, copy it. */ - if ( retVal->BASE_EL(left) ) { - retVal->BASE_EL(left) = copyBranch(retVal->BASE_EL(left)); - retVal->BASE_EL(left)->BASE_EL(parent) = retVal; - } - -#ifdef WALKABLE - BASELIST::addAfter( BASELIST::tail, retVal ); -#else - if ( head == 0 ) - head = retVal; - tail = retVal; -#endif - - /* If the right tree is there, copy it. */ - if ( retVal->BASE_EL(right) ) { - retVal->BASE_EL(right) = copyBranch(retVal->BASE_EL(right)); - retVal->BASE_EL(right)->BASE_EL(parent) = retVal; - } - return retVal; -} - -/* Once an insertion position is found, attach a element to the tree. */ -template void AvlTree:: - attachRebal( Element *element, Element *parentEl, Element *lastLess ) -{ - /* Increment the number of element in the tree. */ - treeSize += 1; - - /* Set element's parent. */ - element->BASE_EL(parent) = parentEl; - - /* New element always starts as a leaf with height 1. */ - element->BASE_EL(left) = 0; - element->BASE_EL(right) = 0; - element->BASE_EL(height) = 1; - - /* Are we inserting in the tree somewhere? */ - if ( parentEl != 0 ) { - /* We have a parent so we are somewhere in the tree. If the parent - * equals lastLess, then the last traversal in the insertion went - * left, otherwise it went right. */ - if ( lastLess == parentEl ) { - parentEl->BASE_EL(left) = element; -#ifdef WALKABLE - BASELIST::addBefore( parentEl, element ); -#endif - } - else { - parentEl->BASE_EL(right) = element; -#ifdef WALKABLE - BASELIST::addAfter( parentEl, element ); -#endif - } - -#ifndef WALKABLE - /* Maintain the first and last pointers. */ - if ( head->BASE_EL(left) == element ) - head = element; - - /* Maintain the first and last pointers. */ - if ( tail->BASE_EL(right) == element ) - tail = element; -#endif - } - else { - /* No parent element so we are inserting the root. */ - root = element; -#ifdef WALKABLE - BASELIST::addAfter( BASELIST::tail, element ); -#else - head = tail = element; -#endif - } - - - /* Recalculate the heights. */ - recalcHeights(parentEl); - - /* Find the first unbalance. */ - Element *ub = findFirstUnbalGP(element); - - /* rebalance. */ - if ( ub != 0 ) - { - /* We assert that after this single rotation the - * tree is now properly balanced. */ - rebalance(ub); - } -} - -#ifndef AVL_KEYLESS - -/** - * \brief Insert an existing element into the tree. - * - * If the insert succeeds and lastFound is given then it is set to the element - * inserted. If the insert fails then lastFound is set to the existing element in - * the tree that has the same key as element. If the element's avl pointers are - * already in use then undefined behaviour results. - * - * \returns The element inserted upon success, null upon failure. - */ -template Element *AvlTree:: - insert( Element *element, Element **lastFound ) -{ - long keyRelation; - Element *curEl = root, *parentEl = 0; - Element *lastLess = 0; - - while (true) { - if ( curEl == 0 ) { - /* We are at an external element and did not find the key we were - * looking for. Attach underneath the leaf and rebalance. */ - attachRebal( element, parentEl, lastLess ); - - if ( lastFound != 0 ) - *lastFound = element; - return element; - } - -#ifdef AVL_BASIC - keyRelation = this->compare( *element, *curEl ); -#else - keyRelation = this->compare( element->BASEKEY(getKey()), - curEl->BASEKEY(getKey()) ); -#endif - - /* Do we go left? */ - if ( keyRelation < 0 ) { - parentEl = lastLess = curEl; - curEl = curEl->BASE_EL(left); - } - /* Do we go right? */ - else if ( keyRelation > 0 ) { - parentEl = curEl; - curEl = curEl->BASE_EL(right); - } - /* We have hit the target. */ - else { - if ( lastFound != 0 ) - *lastFound = curEl; - return 0; - } - } -} - -#ifdef AVL_BASIC - -/** - * \brief Find a element in the tree with the given key. - * - * \returns The element if key exists, null if the key does not exist. - */ -template Element *AvlTree:: - find( const Element *element ) const -{ - Element *curEl = root; - long keyRelation; - - while (curEl) { - keyRelation = this->compare( *element, *curEl ); - - /* Do we go left? */ - if ( keyRelation < 0 ) - curEl = curEl->BASE_EL(left); - /* Do we go right? */ - else if ( keyRelation > 0 ) - curEl = curEl->BASE_EL(right); - /* We have hit the target. */ - else { - return curEl; - } - } - return 0; -} - -#else - -/** - * \brief Insert a new element into the tree with given key. - * - * If the key is not already in the tree then a new element is made using the - * Element(const Key &key) constructor and the insert succeeds. If lastFound is - * given then it is set to the element inserted. If the insert fails then - * lastFound is set to the existing element in the tree that has the same key as - * element. - * - * \returns The new element upon success, null upon failure. - */ -template Element *AvlTree:: - insert( const Key &key, Element **lastFound ) -{ - long keyRelation; - Element *curEl = root, *parentEl = 0; - Element *lastLess = 0; - - while (true) { - if ( curEl == 0 ) { - /* We are at an external element and did not find the key we were - * looking for. Create the new element, attach it underneath the leaf - * and rebalance. */ - Element *element = new Element( key ); - attachRebal( element, parentEl, lastLess ); - - if ( lastFound != 0 ) - *lastFound = element; - return element; - } - - keyRelation = this->compare( key, curEl->BASEKEY(getKey()) ); - - /* Do we go left? */ - if ( keyRelation < 0 ) { - parentEl = lastLess = curEl; - curEl = curEl->BASE_EL(left); - } - /* Do we go right? */ - else if ( keyRelation > 0 ) { - parentEl = curEl; - curEl = curEl->BASE_EL(right); - } - /* We have hit the target. */ - else { - if ( lastFound != 0 ) - *lastFound = curEl; - return 0; - } - } -} - -#ifdef AVLTREE_MAP -/** - * \brief Insert a new element into the tree with key and value. - * - * If the key is not already in the tree then a new element is constructed and - * the insert succeeds. If lastFound is given then it is set to the element - * inserted. If the insert fails then lastFound is set to the existing element in - * the tree that has the same key as element. This insert routine is only - * available in AvlMap because it is the only class that knows about a Value - * type. - * - * \returns The new element upon success, null upon failure. - */ -template Element *AvlTree:: - insert( const Key &key, const Value &val, Element **lastFound ) -{ - long keyRelation; - Element *curEl = root, *parentEl = 0; - Element *lastLess = 0; - - while (true) { - if ( curEl == 0 ) { - /* We are at an external element and did not find the key we were - * looking for. Create the new element, attach it underneath the leaf - * and rebalance. */ - Element *element = new Element( key, val ); - attachRebal( element, parentEl, lastLess ); - - if ( lastFound != 0 ) - *lastFound = element; - return element; - } - - keyRelation = this->compare(key, curEl->getKey()); - - /* Do we go left? */ - if ( keyRelation < 0 ) { - parentEl = lastLess = curEl; - curEl = curEl->BASE_EL(left); - } - /* Do we go right? */ - else if ( keyRelation > 0 ) { - parentEl = curEl; - curEl = curEl->BASE_EL(right); - } - /* We have hit the target. */ - else { - if ( lastFound != 0 ) - *lastFound = curEl; - return 0; - } - } -} -#endif /* AVLTREE_MAP */ - - -/** - * \brief Find a element in the tree with the given key. - * - * \returns The element if key exists, null if the key does not exist. - */ -template Element *AvlTree:: - find( const Key &key ) const -{ - Element *curEl = root; - long keyRelation; - - while (curEl) { - keyRelation = this->compare( key, curEl->BASEKEY(getKey()) ); - - /* Do we go left? */ - if ( keyRelation < 0 ) - curEl = curEl->BASE_EL(left); - /* Do we go right? */ - else if ( keyRelation > 0 ) - curEl = curEl->BASE_EL(right); - /* We have hit the target. */ - else { - return curEl; - } - } - return 0; -} - - -/** - * \brief Find a element, then detach it from the tree. - * - * The element is not deleted. - * - * \returns The element detached if the key is found, othewise returns null. - */ -template Element *AvlTree:: - detach(const Key &key) -{ - Element *element = find( key ); - if ( element ) { - detach(element); - } - - return element; -} - -/** - * \brief Find, detach and delete a element from the tree. - * - * \returns True if the element was found and deleted, false otherwise. - */ -template bool AvlTree:: - remove(const Key &key) -{ - /* Assume not found. */ - bool retVal = false; - - /* Look for the key. */ - Element *element = find( key ); - if ( element != 0 ) { - /* If found, detach the element and delete. */ - detach( element ); - delete element; - retVal = true; - } - - return retVal; -} - -#endif /* AVL_BASIC */ -#endif /* AVL_KEYLESS */ - - -/** - * \brief Detach and delete a element from the tree. - * - * If the element is not in the tree then undefined behaviour results. - */ -template void AvlTree:: - remove(Element *element) -{ - /* Detach and delete. */ - detach(element); - delete element; -} - -/** - * \brief Detach a element from the tree. - * - * If the element is not in the tree then undefined behaviour results. - * - * \returns The element given. - */ -template Element *AvlTree:: - detach(Element *element) -{ - Element *replacement, *fixfrom; - long lheight, rheight; - -#ifdef WALKABLE - /* Remove the element from the ordered list. */ - BASELIST::detach( element ); -#endif - - /* Update treeSize. */ - treeSize--; - - /* Find a replacement element. */ - if (element->BASE_EL(right)) - { - /* Find the leftmost element of the right subtree. */ - replacement = element->BASE_EL(right); - while (replacement->BASE_EL(left)) - replacement = replacement->BASE_EL(left); - - /* If replacing the element the with its child then we need to start - * fixing at the replacement, otherwise we start fixing at the - * parent of the replacement. */ - if (replacement->BASE_EL(parent) == element) - fixfrom = replacement; - else - fixfrom = replacement->BASE_EL(parent); - -#ifndef WALKABLE - if ( element == head ) - head = replacement; -#endif - - removeEl(replacement, replacement->BASE_EL(right)); - replaceEl(element, replacement); - } - else if (element->BASE_EL(left)) - { - /* Find the rightmost element of the left subtree. */ - replacement = element->BASE_EL(left); - while (replacement->BASE_EL(right)) - replacement = replacement->BASE_EL(right); - - /* If replacing the element the with its child then we need to start - * fixing at the replacement, otherwise we start fixing at the - * parent of the replacement. */ - if (replacement->BASE_EL(parent) == element) - fixfrom = replacement; - else - fixfrom = replacement->BASE_EL(parent); - -#ifndef WALKABLE - if ( element == tail ) - tail = replacement; -#endif - - removeEl(replacement, replacement->BASE_EL(left)); - replaceEl(element, replacement); - } - else - { - /* We need to start fixing at the parent of the element. */ - fixfrom = element->BASE_EL(parent); - -#ifndef WALKABLE - if ( element == head ) - head = element->BASE_EL(parent); - if ( element == tail ) - tail = element->BASE_EL(parent); -#endif - - /* The element we are deleting is a leaf element. */ - removeEl(element, 0); - } - - /* If fixfrom is null it means we just deleted - * the root of the tree. */ - if ( fixfrom == 0 ) - return element; - - /* Fix the heights after the deletion. */ - recalcHeights(fixfrom); - - /* Fix every unbalanced element going up in the tree. */ - Element *ub = findFirstUnbalEl(fixfrom); - while ( ub ) - { - /* Find the element to rebalance by moving down from the first unbalanced - * element 2 levels in the direction of the greatest heights. On the - * second move down, the heights may be equal ( but not on the first ). - * In which case go in the direction of the first move. */ - lheight = ub->BASE_EL(left) ? ub->BASE_EL(left)->BASE_EL(height) : 0; - rheight = ub->BASE_EL(right) ? ub->BASE_EL(right)->BASE_EL(height) : 0; - assert( lheight != rheight ); - if (rheight > lheight) - { - ub = ub->BASE_EL(right); - lheight = ub->BASE_EL(left) ? - ub->BASE_EL(left)->BASE_EL(height) : 0; - rheight = ub->BASE_EL(right) ? - ub->BASE_EL(right)->BASE_EL(height) : 0; - if (rheight > lheight) - ub = ub->BASE_EL(right); - else if (rheight < lheight) - ub = ub->BASE_EL(left); - else - ub = ub->BASE_EL(right); - } - else - { - ub = ub->BASE_EL(left); - lheight = ub->BASE_EL(left) ? - ub->BASE_EL(left)->BASE_EL(height) : 0; - rheight = ub->BASE_EL(right) ? - ub->BASE_EL(right)->BASE_EL(height) : 0; - if (rheight > lheight) - ub = ub->BASE_EL(right); - else if (rheight < lheight) - ub = ub->BASE_EL(left); - else - ub = ub->BASE_EL(left); - } - - - /* rebalance returns the grandparant of the subtree formed - * by the element that were rebalanced. - * We must continue upward from there rebalancing. */ - fixfrom = rebalance(ub); - - /* Find the next unbalaced element. */ - ub = findFirstUnbalEl(fixfrom); - } - - return element; -} - - -/** - * \brief Empty the tree and delete all the element. - * - * Resets the tree to its initial state. - */ -template void AvlTree::empty() -{ - if ( root ) { - /* Recursively delete from the tree structure. */ - deleteChildrenOf(root); - delete root; - root = 0; - treeSize = 0; - -#ifdef WALKABLE - BASELIST::abandon(); -#else - head = tail = 0; -#endif - } -} - -/** - * \brief Forget all element in the tree. - * - * Does not delete element. Resets the the tree to it's initial state. - */ -template void AvlTree::abandon() -{ - root = 0; - treeSize = 0; - -#ifdef WALKABLE - BASELIST::abandon(); -#else - head = tail = 0; -#endif -} - -/* Recursively delete all the children of a element. */ -template void AvlTree:: - deleteChildrenOf( Element *element ) -{ - /* Recurse left. */ - if (element->BASE_EL(left)) { - deleteChildrenOf(element->BASE_EL(left)); - - /* Delete left element. */ - delete element->BASE_EL(left); - element->BASE_EL(left) = 0; - } - - /* Recurse right. */ - if (element->BASE_EL(right)) { - deleteChildrenOf(element->BASE_EL(right)); - - /* Delete right element. */ - delete element->BASE_EL(right); - element->BASE_EL(left) = 0; - } -} - -/* rebalance from a element whose gradparent is unbalanced. Only - * call on a element that has a grandparent. */ -template Element *AvlTree:: - rebalance(Element *n) -{ - long lheight, rheight; - Element *a, *b, *c; - Element *t1, *t2, *t3, *t4; - - Element *p = n->BASE_EL(parent); /* parent (Non-NUL). L*/ - Element *gp = p->BASE_EL(parent); /* Grand-parent (Non-NULL). */ - Element *ggp = gp->BASE_EL(parent); /* Great grand-parent (may be NULL). */ - - if (gp->BASE_EL(right) == p) - { - /* gp - * \ - * p - */ - if (p->BASE_EL(right) == n) - { - /* gp - * \ - * p - * \ - * n - */ - a = gp; - b = p; - c = n; - t1 = gp->BASE_EL(left); - t2 = p->BASE_EL(left); - t3 = n->BASE_EL(left); - t4 = n->BASE_EL(right); - } - else - { - /* gp - * \ - * p - * / - * n - */ - a = gp; - b = n; - c = p; - t1 = gp->BASE_EL(left); - t2 = n->BASE_EL(left); - t3 = n->BASE_EL(right); - t4 = p->BASE_EL(right); - } - } - else - { - /* gp - * / - * p - */ - if (p->BASE_EL(right) == n) - { - /* gp - * / - * p - * \ - * n - */ - a = p; - b = n; - c = gp; - t1 = p->BASE_EL(left); - t2 = n->BASE_EL(left); - t3 = n->BASE_EL(right); - t4 = gp->BASE_EL(right); - } - else - { - /* gp - * / - * p - * / - * n - */ - a = n; - b = p; - c = gp; - t1 = n->BASE_EL(left); - t2 = n->BASE_EL(right); - t3 = p->BASE_EL(right); - t4 = gp->BASE_EL(right); - } - } - - /* Perform rotation. - */ - - /* Tie b to the great grandparent. */ - if ( ggp == 0 ) - root = b; - else if ( ggp->BASE_EL(left) == gp ) - ggp->BASE_EL(left) = b; - else - ggp->BASE_EL(right) = b; - b->BASE_EL(parent) = ggp; - - /* Tie a as a leftchild of b. */ - b->BASE_EL(left) = a; - a->BASE_EL(parent) = b; - - /* Tie c as a rightchild of b. */ - b->BASE_EL(right) = c; - c->BASE_EL(parent) = b; - - /* Tie t1 as a leftchild of a. */ - a->BASE_EL(left) = t1; - if ( t1 != 0 ) t1->BASE_EL(parent) = a; - - /* Tie t2 as a rightchild of a. */ - a->BASE_EL(right) = t2; - if ( t2 != 0 ) t2->BASE_EL(parent) = a; - - /* Tie t3 as a leftchild of c. */ - c->BASE_EL(left) = t3; - if ( t3 != 0 ) t3->BASE_EL(parent) = c; - - /* Tie t4 as a rightchild of c. */ - c->BASE_EL(right) = t4; - if ( t4 != 0 ) t4->BASE_EL(parent) = c; - - /* The heights are all recalculated manualy and the great - * grand-parent is passed to recalcHeights() to ensure - * the heights are correct up the tree. - * - * Note that recalcHeights() cuts out when it comes across - * a height that hasn't changed. - */ - - /* Fix height of a. */ - lheight = a->BASE_EL(left) ? a->BASE_EL(left)->BASE_EL(height) : 0; - rheight = a->BASE_EL(right) ? a->BASE_EL(right)->BASE_EL(height) : 0; - a->BASE_EL(height) = (lheight > rheight ? lheight : rheight) + 1; - - /* Fix height of c. */ - lheight = c->BASE_EL(left) ? c->BASE_EL(left)->BASE_EL(height) : 0; - rheight = c->BASE_EL(right) ? c->BASE_EL(right)->BASE_EL(height) : 0; - c->BASE_EL(height) = (lheight > rheight ? lheight : rheight) + 1; - - /* Fix height of b. */ - lheight = a->BASE_EL(height); - rheight = c->BASE_EL(height); - b->BASE_EL(height) = (lheight > rheight ? lheight : rheight) + 1; - - /* Fix height of b's parents. */ - recalcHeights(ggp); - return ggp; -} - -/* Recalculates the heights of all the ancestors of element. */ -template void AvlTree:: - recalcHeights(Element *element) -{ - long lheight, rheight, new_height; - while ( element != 0 ) - { - lheight = element->BASE_EL(left) ? element->BASE_EL(left)->BASE_EL(height) : 0; - rheight = element->BASE_EL(right) ? element->BASE_EL(right)->BASE_EL(height) : 0; - - new_height = (lheight > rheight ? lheight : rheight) + 1; - - /* If there is no chage in the height, then there will be no - * change in any of the ancestor's height. We can stop going up. - * If there was a change, continue upward. */ - if (new_height == element->BASE_EL(height)) - return; - else - element->BASE_EL(height) = new_height; - - element = element->BASE_EL(parent); - } -} - -/* Finds the first element whose grandparent is unbalanced. */ -template Element *AvlTree:: - findFirstUnbalGP(Element *element) -{ - long lheight, rheight, balanceProp; - Element *gp; - - if ( element == 0 || element->BASE_EL(parent) == 0 || - element->BASE_EL(parent)->BASE_EL(parent) == 0 ) - return 0; - - /* Don't do anything if we we have no grandparent. */ - gp = element->BASE_EL(parent)->BASE_EL(parent); - while ( gp != 0 ) - { - lheight = gp->BASE_EL(left) ? gp->BASE_EL(left)->BASE_EL(height) : 0; - rheight = gp->BASE_EL(right) ? gp->BASE_EL(right)->BASE_EL(height) : 0; - balanceProp = lheight - rheight; - - if ( balanceProp < -1 || balanceProp > 1 ) - return element; - - element = element->BASE_EL(parent); - gp = gp->BASE_EL(parent); - } - return 0; -} - - -/* Finds the first element that is unbalanced. */ -template Element *AvlTree:: - findFirstUnbalEl(Element *element) -{ - if ( element == 0 ) - return 0; - - while ( element != 0 ) - { - long lheight = element->BASE_EL(left) ? - element->BASE_EL(left)->BASE_EL(height) : 0; - long rheight = element->BASE_EL(right) ? - element->BASE_EL(right)->BASE_EL(height) : 0; - long balanceProp = lheight - rheight; - - if ( balanceProp < -1 || balanceProp > 1 ) - return element; - - element = element->BASE_EL(parent); - } - return 0; -} - -/* Replace a element in the tree with another element not in the tree. */ -template void AvlTree:: - replaceEl(Element *element, Element *replacement) -{ - Element *parent = element->BASE_EL(parent), - *left = element->BASE_EL(left), - *right = element->BASE_EL(right); - - replacement->BASE_EL(left) = left; - if (left) - left->BASE_EL(parent) = replacement; - replacement->BASE_EL(right) = right; - if (right) - right->BASE_EL(parent) = replacement; - - replacement->BASE_EL(parent) = parent; - if (parent) - { - if (parent->BASE_EL(left) == element) - parent->BASE_EL(left) = replacement; - else - parent->BASE_EL(right) = replacement; - } - else - root = replacement; - - replacement->BASE_EL(height) = element->BASE_EL(height); -} - -/* Removes a element from a tree and puts filler in it's place. - * Filler should be null or a child of element. */ -template void AvlTree:: - removeEl(Element *element, Element *filler) -{ - Element *parent = element->BASE_EL(parent); - - if (parent) - { - if (parent->BASE_EL(left) == element) - parent->BASE_EL(left) = filler; - else - parent->BASE_EL(right) = filler; - } - else - root = filler; - - if (filler) - filler->BASE_EL(parent) = parent; - - return; -} - -#ifdef AAPL_NAMESPACE -} -#endif diff --git a/aapl/avlibasic.h b/aapl/avlibasic.h deleted file mode 100644 index 5f9b2c7f..00000000 --- a/aapl/avlibasic.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLIBASIC_H -#define _AAPL_AVLIBASIC_H - -#include "compare.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliBasic - * \brief Linked AVL Tree in which the entire element structure is the key. - * - * AvliBasic is a linked AVL tree that does not distinguish between the - * element that it contains and the key. The entire element structure is the - * key that is used to compare the relative ordering of elements. This is - * similar to the BstSet structure. - * - * AvliBasic does not assume ownership of elements in the tree. Items must be - * explicitly de-allocated. - */ - -/*@}*/ - -#define BASE_EL(name) name -#define BASEKEY(name) name -#define AVLMEL_CLASSDEF class Element, class Compare -#define AVLMEL_TEMPDEF class Element, class Compare -#define AVLMEL_TEMPUSE Element, Compare -#define AvlTree AvliBasic -#define AVL_BASIC -#define WALKABLE - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef AVL_BASIC -#undef WALKABLE - -#endif /* _AAPL_AVLIBASIC_H */ diff --git a/aapl/avlikeyless.h b/aapl/avlikeyless.h deleted file mode 100644 index 8d90443b..00000000 --- a/aapl/avlikeyless.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2002, 2003 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLIKEYLESS_H -#define _AAPL_AVLIKEYLESS_H - -#include "compare.h" -#include "dlistmel.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliKeyless - * \brief Linked AVL tree that has no insert/find/remove functions that take a - * key. - * - * AvliKeyless is an implementation of the AVL tree rebalancing functionality - * only. It provides the common code for the tiny AVL tree implementations. - */ - -/*@}*/ - -#define BASE_EL(name) name -#define BASELIST DListMel< Element, AvliTreeEl > -#define AVLMEL_CLASSDEF class Element -#define AVLMEL_TEMPDEF class Element -#define AVLMEL_TEMPUSE Element -#define AvlTree AvliKeyless -#define WALKABLE -#define AVL_KEYLESS - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASELIST -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef WALKABLE -#undef AVL_KEYLESS - -#endif /* _AAPL_AVLIKEYLESS_H */ diff --git a/aapl/avlimap.h b/aapl/avlimap.h deleted file mode 100644 index f2b1b0c5..00000000 --- a/aapl/avlimap.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLIMAP_H -#define _AAPL_AVLIMAP_H - -#include "compare.h" -#include "dlist.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliMap - * \brief Linked key and value oriented AVL tree. - * - * AvliMap stores key and value pairs in elements that managed by the tree. It - * is intendend to be similar to map template found in the STL. AvliMap - * requires that a Key type, a Value type, and a class containing a compare() - * routine for Key be given. Items can be inserted with just a key or with a - * key and value pair. - * - * AvliMap assumes all elements in the tree are allocated on the heap and are - * to be managed by the tree. This means that the class destructor will delete - * the contents of the tree. A deep copy will cause existing elements to be - * deleted first. - * - * \include ex_avlimap.cpp - */ - -/*@}*/ - -#define AVLTREE_MAP -#define BASE_EL(name) name -#define BASEKEY(name) name -#define BASELIST DList< AvliMapEl > -#define AVLMEL_CLASSDEF class Key, class Value, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Key, class Value, class Compare -#define AVLMEL_TEMPUSE Key, Value, Compare -#define AvlTree AvliMap -#define Element AvliMapEl -#define WALKABLE - -#include "avlcommon.h" - -#undef AVLTREE_MAP -#undef BASE_EL -#undef BASEKEY -#undef BASELIST -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef Element -#undef WALKABLE - -#endif /* _AAPL_AVLIMAP_H */ diff --git a/aapl/avlimel.h b/aapl/avlimel.h deleted file mode 100644 index 9a7ff951..00000000 --- a/aapl/avlimel.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLIMEL_H -#define _AAPL_AVLIMEL_H - -#include "compare.h" -#include "dlistmel.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliMel - * \brief Linked AVL tree for element appearing in multiple trees. - * - * AvliMel allows for an element to simultaneously be in multiple trees without - * the trees interferring with one another. For each tree that the element is - * to appear in, there must be a distinct set of AVL Tree management data that - * can be unambiguously referenced with some base class name. This name - * is passed to the tree as a template parameter and is used in the tree - * algorithms. - * - * The element must use the same key type and value in each tree that it - * appears in. If distinct keys are required, the AvliMelKey structure is - * available. - * - * AvliMel does not assume ownership of elements in the tree. The destructor - * will not delete the elements. If the user wishes to explicitly deallocate - * all the items in the tree the empty() routine is available. - * - * \include ex_avlimel.cpp - */ - -/*@}*/ - -#define BASE_EL(name) BaseEl::name -#define BASEKEY(name) name -#define BASELIST DListMel< Element, BaseEl > -#define AVLMEL_CLASSDEF class Element, class Key, \ - class BaseEl, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Element, class Key, \ - class BaseEl, class Compare -#define AVLMEL_TEMPUSE Element, Key, BaseEl, Compare -#define AvlTree AvliMel -#define WALKABLE - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef BASELIST -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef WALKABLE - -#endif /* _AAPL_AVLIMEL_H */ diff --git a/aapl/avlimelkey.h b/aapl/avlimelkey.h deleted file mode 100644 index 41d4ae49..00000000 --- a/aapl/avlimelkey.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLIMELKEY_H -#define _AAPL_AVLIMELKEY_H - -#include "compare.h" -#include "dlistmel.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliMelKey - * \brief Linked AVL tree for element appearing in multiple trees with different keys. - * - * AvliMelKey is similar to AvliMel, except that an additional template - * parameter, BaseKey, is provided for resolving ambiguous references to - * getKey(). This means that if an element is stored in multiple trees, each - * tree can use a different key for ordering the elements in it. Using - * AvliMelKey an array of data structures can be indexed with an O(log(n)) - * search on two or more of the values contained within it and without - * allocating any additional data. - * - * AvliMelKey does not assume ownership of elements in the tree. The destructor - * will not delete the elements. If the user wishes to explicitly deallocate - * all the items in the tree the empty() routine is available. - * - * \include ex_avlimelkey.cpp - */ - -/*@}*/ - -#define BASE_EL(name) BaseEl::name -#define BASEKEY(name) BaseKey::name -#define BASELIST DListMel< Element, BaseEl > -#define AVLMEL_CLASSDEF class Element, class Key, class BaseEl, \ - class BaseKey, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Element, class Key, class BaseEl, \ - class BaseKey, class Compare -#define AVLMEL_TEMPUSE Element, Key, BaseEl, BaseKey, Compare -#define AvlTree AvliMelKey -#define WALKABLE - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef BASELIST -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef WALKABLE - -#endif /* _AAPL_AVLIMELKEY_H */ diff --git a/aapl/avliset.h b/aapl/avliset.h deleted file mode 100644 index e309c3ad..00000000 --- a/aapl/avliset.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLISET_H -#define _AAPL_AVLISET_H - -#include "compare.h" -#include "dlist.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliSet - * \brief Linked Key-only oriented tree. - * - * AvliSet stores only keys in elements that are managed by the tree. AvliSet - * requires that a Key type and a class containing a compare() routine - * for Key be given. Items are inserted with just a key value. - * - * AvliSet assumes all elements in the tree are allocated on the heap and are - * to be managed by the tree. This means that the class destructor will delete - * the contents of the tree. A deep copy will cause existing elements to be - * deleted first. - * - * \include ex_avliset.cpp - */ - -/*@}*/ - -#define AVLTREE_SET -#define BASE_EL(name) name -#define BASEKEY(name) name -#define BASELIST DList< AvliSetEl > -#define AVLMEL_CLASSDEF class Key, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Key, class Compare -#define AVLMEL_TEMPUSE Key, Compare -#define AvlTree AvliSet -#define Element AvliSetEl -#define WALKABLE - -#include "avlcommon.h" - -#undef AVLTREE_SET -#undef BASE_EL -#undef BASEKEY -#undef BASELIST -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef Element -#undef WALKABLE - -#endif /* _AAPL_AVLISET_H */ diff --git a/aapl/avlitree.h b/aapl/avlitree.h deleted file mode 100644 index 54c8f30f..00000000 --- a/aapl/avlitree.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLITREE_H -#define _AAPL_AVLITREE_H - -#include "compare.h" -#include "dlistmel.h" - -/** - * \addtogroup avlitree - * @{ - */ - -/** - * \class AvliTree - * \brief Linked AVL tree. - * - * AvliTree is the standard linked by-structure AVL tree. To use this - * structure the user must define an element type and give it the necessary - * properties. At the very least it must have a getKey() function that will be - * used to compare the relative ordering of elements and tree management data - * necessary for the AVL algorithm. An element type can acquire the management - * data by inheriting the AvliTreeEl class. - * - * AvliTree does not presume to manage the allocation of elements in the tree. - * The destructor will not delete the items in the tree, instead the elements - * must be explicitly de-allocated by the user if necessary and when it is - * safe to do so. The empty() routine will traverse the tree and delete all - * items. - * - * Since the tree does not manage the elements, it can contain elements that - * are allocated statically or that are part of another data structure. - * - * \include ex_avlitree.cpp - */ - -/*@}*/ - -#define BASE_EL(name) name -#define BASEKEY(name) name -#define BASELIST DListMel< Element, AvliTreeEl > -#define AVLMEL_CLASSDEF class Element, class Key, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Element, class Key, class Compare -#define AVLMEL_TEMPUSE Element, Key, Compare -#define AvlTree AvliTree -#define WALKABLE - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef BASELIST -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef WALKABLE - -#endif /* _AAPL_AVLITREE_H */ diff --git a/aapl/avlkeyless.h b/aapl/avlkeyless.h deleted file mode 100644 index 9ba769c2..00000000 --- a/aapl/avlkeyless.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2002, 2003 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLKEYLESS_H -#define _AAPL_AVLKEYLESS_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlKeyless - * \brief AVL tree that has no insert/find/remove functions that take a key. - * - * AvlKeyless is an implementation of the AVL tree rebalancing functionality - * only. It provides the common code for the tiny AVL tree implementations. - */ - -/*@}*/ - -#define BASE_EL(name) name -#define AVLMEL_CLASSDEF class Element -#define AVLMEL_TEMPDEF class Element -#define AVLMEL_TEMPUSE Element -#define AvlTree AvlKeyless -#define AVL_KEYLESS - -#include "avlcommon.h" - -#undef BASE_EL -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef AVL_KEYLESS - -#endif /* _AAPL_AVLKEYLESS_H */ diff --git a/aapl/avlmap.h b/aapl/avlmap.h deleted file mode 100644 index 9b52c8ba..00000000 --- a/aapl/avlmap.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLMAP_H -#define _AAPL_AVLMAP_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlMap - * \brief Key and value oriented AVL tree. - * - * AvlMap stores key and value pairs in elements that managed by the tree. It - * is intendend to be similar to map template found in the STL. AvlMap - * requires that a Key type, a Value type, and a class containing a compare() - * routine for Key be given. Items can be inserted with just a key or with a - * key and value pair. - * - * AvlMap assumes all elements in the tree are allocated on the heap and are - * to be managed by the tree. This means that the class destructor will delete - * the contents of the tree. A deep copy will cause existing elements to be - * deleted first. - * - * \include ex_avlmap.cpp - */ - -/*@}*/ - -#define AVLTREE_MAP -#define BASE_EL(name) name -#define BASEKEY(name) name -#define AVLMEL_CLASSDEF class Key, class Value, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Key, class Value, class Compare -#define AVLMEL_TEMPUSE Key, Value, Compare -#define AvlTree AvlMap -#define Element AvlMapEl - -#include "avlcommon.h" - -#undef AVLTREE_MAP -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef Element - - - -#endif /* _AAPL_AVLMAP_H */ diff --git a/aapl/avlmel.h b/aapl/avlmel.h deleted file mode 100644 index fe7671b8..00000000 --- a/aapl/avlmel.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLMEL_H -#define _AAPL_AVLMEL_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlMel - * \brief AVL tree for elements appearing in multiple trees. - * - * AvlMel allows for an element to simultaneously be in multiple trees without - * the trees interferring with one another. For each tree that the element is - * to appear in, there must be a distinct set of AVL Tree management data that - * can be unambiguously referenced with some base class name. This name - * is passed to the tree as a template parameter and is used in the tree - * algorithms. - * - * The element must use the same key type and value in each tree that it - * appears in. If distinct keys are required, the AvlMelKey structure is - * available. - * - * AvlMel does not assume ownership of elements in the tree. The destructor - * will not delete the elements. If the user wishes to explicitly deallocate - * all the items in the tree the empty() routine is available. - * - * \include ex_avlmel.cpp - */ - -/*@}*/ - -#define BASE_EL(name) BaseEl::name -#define BASEKEY(name) name -#define AVLMEL_CLASSDEF class Element, class Key, \ - class BaseEl, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Element, class Key, \ - class BaseEl, class Compare -#define AVLMEL_TEMPUSE Element, Key, BaseEl, Compare -#define AvlTree AvlMel - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree - -#endif /* _AAPL_AVLMEL_H */ diff --git a/aapl/avlmelkey.h b/aapl/avlmelkey.h deleted file mode 100644 index cce7126f..00000000 --- a/aapl/avlmelkey.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLMELKEY_H -#define _AAPL_AVLMELKEY_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlMelKey - * \brief AVL tree for elements appearing in multiple trees with different keys. - * - * AvlMelKey is similar to AvlMel, except that an additional template - * parameter, BaseKey, is provided for resolving ambiguous references to - * getKey(). This means that if an element is stored in multiple trees, each - * tree can use a different key for ordering the elements in it. Using - * AvlMelKey an array of data structures can be indexed with an O(log(n)) - * search on two or more of the values contained within it and without - * allocating any additional data. - * - * AvlMelKey does not assume ownership of elements in the tree. The destructor - * will not delete the elements. If the user wishes to explicitly deallocate - * all the items in the tree the empty() routine is available. - * - * \include ex_avlmelkey.cpp - */ - -/*@}*/ - -#define BASE_EL(name) BaseEl::name -#define BASEKEY(name) BaseKey::name -#define AVLMEL_CLASSDEF class Element, class Key, class BaseEl, \ - class BaseKey, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Element, class Key, class BaseEl, \ - class BaseKey, class Compare -#define AVLMEL_TEMPUSE Element, Key, BaseEl, BaseKey, Compare -#define AvlTree AvlMelKey - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree - -#endif /* _AAPL_AVLMELKEY_H */ diff --git a/aapl/avlset.h b/aapl/avlset.h deleted file mode 100644 index 97e5c484..00000000 --- a/aapl/avlset.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLSET_H -#define _AAPL_AVLSET_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlSet - * \brief Key-only oriented tree. - * - * AvlSet stores only keys in elements that are managed by the tree. AvlSet - * requires that a Key type and a class containing a compare() routine - * for Key be given. Items are inserted with just a key value. - * - * AvlSet assumes all elements in the tree are allocated on the heap and are - * to be managed by the tree. This means that the class destructor will delete - * the contents of the tree. A deep copy will cause existing elements to be - * deleted first. - * - * \include ex_avlset.cpp - */ - -/*@}*/ - -#define AVLTREE_SET -#define BASE_EL(name) name -#define BASEKEY(name) name -#define AVLMEL_CLASSDEF class Key, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Key, class Compare -#define AVLMEL_TEMPUSE Key, Compare -#define AvlTree AvlSet -#define Element AvlSetEl - -#include "avlcommon.h" - -#undef AVLTREE_SET -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree -#undef Element - -#endif /* _AAPL_AVLSET_H */ diff --git a/aapl/avltree.h b/aapl/avltree.h deleted file mode 100644 index 9237810e..00000000 --- a/aapl/avltree.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_AVLTREE_H -#define _AAPL_AVLTREE_H - -#include "compare.h" - -/** - * \addtogroup avltree - * @{ - */ - -/** - * \class AvlTree - * \brief Basic AVL tree. - * - * AvlTree is the standard by-structure AVL tree. To use this structure the - * user must define an element type and give it the necessary properties. At - * the very least it must have a getKey() function that will be used to - * compare the relative ordering of elements and tree management data - * necessary for the AVL algorithm. An element type can acquire the management - * data by inheriting the AvlTreeEl class. - * - * AvlTree does not presume to manage the allocation of elements in the tree. - * The destructor will not delete the items in the tree, instead the elements - * must be explicitly de-allocated by the user if necessary and when it is - * safe to do so. The empty() routine will traverse the tree and delete all - * items. - * - * Since the tree does not manage the elements, it can contain elements that - * are allocated statically or that are part of another data structure. - * - * \include ex_avltree.cpp - */ - -/*@}*/ - -#define BASE_EL(name) name -#define BASEKEY(name) name -#define AVLMEL_CLASSDEF class Element, class Key, class Compare = CmpOrd -#define AVLMEL_TEMPDEF class Element, class Key, class Compare -#define AVLMEL_TEMPUSE Element, Key, Compare -#define AvlTree AvlTree - -#include "avlcommon.h" - -#undef BASE_EL -#undef BASEKEY -#undef AVLMEL_CLASSDEF -#undef AVLMEL_TEMPDEF -#undef AVLMEL_TEMPUSE -#undef AvlTree - -#endif /* _AAPL_AVLTREE_H */ diff --git a/aapl/bstcommon.h b/aapl/bstcommon.h deleted file mode 100644 index 391b4ff1..00000000 --- a/aapl/bstcommon.h +++ /dev/null @@ -1,815 +0,0 @@ -/* - * Copyright 2001 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* This header is not wrapped in ifndefs because it is - * not intended to be included by users directly. */ - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/* Binary Search Table */ -template < BST_TEMPL_DECLARE > class BstTable : - public Compare, - public Vector< Element, Resize > -{ - typedef Vector BaseVector; - typedef Table BaseTable; - -public: - /** - * \brief Default constructor. - * - * Create an empty binary search table. - */ - BstTable() { } - - /** - * \brief Construct with initial value. - * - * Constructs a binary search table with an initial item. Uses the default - * constructor for initializing Value. - */ - BstTable(const Key &key) - { insert(key); } - -#if defined( BSTMAP ) - /** - * \brief Construct with initial value. - * - * Constructs a binary search table with an initial key/value pair. - */ - BstTable(const Key &key, const Value &val) - { insert(key, val); } -#endif - -#if ! defined( BSTSET ) - /** - * \brief Construct with initial value. - * - * Constructs a binary search table with an initial Element. - */ - BstTable(const Element &el) - { insert(el); } -#endif - - Element *insert(const Key &key, Element **lastFound = 0); - Element *insertMulti(const Key &key); - - bool insert(const BstTable &other); - void insertMulti(const BstTable &other); - -#if defined( BSTMAP ) - Element *insert(const Key &key, const Value &val, - Element **lastFound = 0); - Element *insertMulti(const Key &key, const Value &val ); -#endif - -#if ! defined( BSTSET ) - Element *insert(const Element &el, Element **lastFound = 0); - Element *insertMulti(const Element &el); -#endif - - Element *find(const Key &key, Element **lastFound = 0) const; - bool findMulti( const Key &key, Element *&lower, - Element *&upper ) const; - - bool remove(const Key &key); - bool remove(Element *item); - long removeMulti(const Key &key); - long removeMulti(Element *lower, Element *upper); - - /* The following provide access to the underlying insert and remove - * functions that my be hidden by the BST insert and remove. The insertDup - * and insertNew functions will never be hidden. They are provided for - * consistency. The difference between the non-shared and the shared - * tables is the documentation reference to the invoked function. */ - -#if !defined( SHARED_BST ) - /*@{*/ - - /** \brief Call the insert of the underlying vector. - * - * Provides to access to the vector insert, which may become hidden. Care - * should be taken to ensure that after the insert the ordering of - * elements is preserved. - * Invokes Vector::insert( long pos, const T &val ). - */ - void vinsert(long pos, const Element &val) - { Vector< Element, Resize >::insert( pos, &val, 1 ); } - - /** \brief Call the insert of the underlying vector. - * - * Provides to access to the vector insert, which may become hidden. Care - * should be taken to ensure that after the insert the ordering of - * elements is preserved. - * Invokes Vector::insert( long pos, const T *val, long len ). - */ - void vinsert(long pos, const Element *val, long len) - { Vector< Element, Resize >::insert( pos, val, len ); } - - /** \brief Call the insert of the underlying vector. - * - * Provides to access to the vector insert, which may become hidden. Care - * should be taken to ensure that after the insert the ordering of - * elements is preserved. - * Invokes Vector::insert( long pos, const Vector &v ). - */ - void vinsert(long pos, const BstTable &v) - { Vector< Element, Resize >::insert( pos, v.data, v.tabLen ); } - - /*@}*/ - - /*@{*/ - - /** \brief Call the remove of the underlying vector. - * - * Provides access to the vector remove, which may become hidden. - * Invokes Vector::remove( long pos ). - */ - void vremove(long pos) - { Vector< Element, Resize >::remove( pos, 1 ); } - - /** \brief Call the remove of the underlying vector. - * - * Proves access to the vector remove, which may become hidden. - * Invokes Vector::remove( long pos, long len ). - */ - void vremove(long pos, long len) - { Vector< Element, Resize >::remove( pos, len ); } - - /*@}*/ -#else /* SHARED_BST */ - /*@{*/ - - /** \brief Call the insert of the underlying vector. - * - * Provides to access to the vector insert, which may become hidden. Care - * should be taken to ensure that after the insert the ordering of - * elements is preserved. - * Invokes SVector::insert( long pos, const T &val ). - */ - void vinsert(long pos, const Element &val) - { Vector< Element, Resize >::insert( pos, &val, 1 ); } - - /** \brief Call the insert of the underlying vector. - * - * Provides to access to the vector insert, which may become hidden. Care - * should be taken to ensure that after the insert the ordering of - * elements is preserved. - * Invokes SVector::insert( long pos, const T *val, long len ). - */ - void vinsert(long pos, const Element *val, long len) - { Vector< Element, Resize >::insert( pos, val, len ); } - - /** \brief Call the insert of the underlying vector. - * - * Provides to access to the vector insert, which may become hidden. Care - * should be taken to ensure that after the insert the ordering of - * elements is preserved. - * Invokes SVector::insert( long pos, const SVector &v ). - */ - void vinsert(long pos, const BstTable &v) - { Vector< Element, Resize >::insert( pos, v.data, v.length() ); } - - /*@}*/ - - /*@{*/ - - /** \brief Call the remove of the underlying vector. - * - * Provides access to the vector remove, which may become hidden. - * Invokes SVector::remove( long pos ). - */ - void vremove(long pos) - { Vector< Element, Resize >::remove( pos, 1 ); } - - /** \brief Call the remove of the underlying vector. - * - * Proves access to the vector remove, which may become hidden. - * Invokes SVector::remove( long pos, long len ). - */ - void vremove(long pos, long len) - { Vector< Element, Resize >::remove( pos, len ); } - - /*@}*/ - -#endif /* SHARED_BST */ -}; - - -#if 0 -#if defined( SHARED_BST ) -/** - * \brief Construct a binary search table with an initial amount of - * allocation. - * - * The table is initialized to have room for allocLength elements. The - * table starts empty. - */ -template BstTable:: - BstTable( long allocLen ) -{ - /* Allocate the space if we are given a positive allocLen. */ - if ( allocLen > 0 ) { - /* Allocate the data needed. */ - STabHead *head = (STabHead*) - malloc( sizeof(STabHead) + sizeof(Element) * allocLen ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Set up the header and save the data pointer. */ - head->refCount = 1; - head->allocLen = allocLen; - head->tabLen = 0; - BaseTable::data = (Element*) (head + 1); - } -} -#else -/** - * \brief Construct a binary search table with an initial amount of - * allocation. - * - * The table is initialized to have room for allocLength elements. The - * table starts empty. - */ -template BstTable:: - BstTable( long allocLen ) -{ - /* Allocate the space if we are given a positive allocLen. */ - BaseTable::allocLen = allocLen; - if ( BaseTable::allocLen > 0 ) { - BaseTable::data = (Element*) malloc(sizeof(Element) * BaseTable::allocLen); - if ( BaseTable::data == NULL ) - throw std::bad_alloc(); - } -} - -#endif -#endif - -/** - * \brief Find the element with the given key and remove it. - * - * If multiple elements with the given key exist, then it is unspecified which - * element will be removed. - * - * \returns True if an element is found and consequently removed, false - * otherwise. - */ -template bool BstTable:: - remove(const Key &key) -{ - Element *el = find(key); - if ( el != 0 ) { - Vector< Element >::remove(el - BaseTable::data); - return true; - } - return false; -} - -/** - * \brief Remove the element pointed to by item. - * - * If item does not point to an element in the tree, then undefined behaviour - * results. If item is null, then remove has no effect. - * - * \returns True if item is not null, false otherwise. - */ -template bool BstTable:: - remove( Element *item ) -{ - if ( item != 0 ) { - Vector< Element >::remove(item - BaseTable::data); - return true; - } - return false; -} - -/** - * \brief Find and remove the entire range of elements with the given key. - * - * \returns The number of elements removed. - */ -template long BstTable:: - removeMulti(const Key &key) -{ - Element *low, *high; - if ( findMulti(key, low, high) ) { - /* Get the length of the range. */ - long num = high - low + 1; - Vector< Element >::remove(low - BaseTable::data, num); - return num; - } - - return 0; -} - -template long BstTable:: - removeMulti(Element *lower, Element *upper) -{ - /* Get the length of the range. */ - long num = upper - lower + 1; - Vector< Element >::remove(lower - BaseTable::data, num); - return num; -} - - -/** - * \brief Find a range of elements with the given key. - * - * If any elements with the given key exist then lower and upper are set to - * the low and high ends of the continous range of elements with the key. - * Lower and upper will point to the first and last elements with the key. - * - * \returns True if any elements are found, false otherwise. - */ -template bool BstTable:: - findMulti(const Key &key, Element *&low, Element *&high ) const -{ - const Element *lower, *mid, *upper; - long keyRelation; - const long tblLen = BaseTable::length(); - - if ( BaseTable::data == 0 ) - return false; - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the fd in the array. */ - return false; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = this->compare(key, GET_KEY(*mid)); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - Element *lowEnd = BaseTable::data - 1; - Element *highEnd = BaseTable::data + tblLen; - - lower = mid - 1; - while ( lower != lowEnd && - this->compare(key, GET_KEY(*lower)) == 0 ) - lower--; - - upper = mid + 1; - while ( upper != highEnd && - this->compare(key, GET_KEY(*upper)) == 0 ) - upper++; - - low = (Element*)lower + 1; - high = (Element*)upper - 1; - return true; - } - } -} - -/** - * \brief Find an element with the given key. - * - * If the find succeeds then lastFound is set to the element found. If the - * find fails then lastFound is set the location where the key would be - * inserted. If there is more than one element in the tree with the given key, - * then it is unspecified which element is returned as the match. - * - * \returns The element found on success, null on failure. - */ -template Element *BstTable:: - find( const Key &key, Element **lastFound ) const -{ - const Element *lower, *mid, *upper; - long keyRelation; - const long tblLen = BaseTable::length(); - - if ( BaseTable::data == 0 ) - return 0; - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the key. Last found gets the insert location. */ - if ( lastFound != 0 ) - *lastFound = (Element*)lower; - return 0; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = this->compare(key, GET_KEY(*mid)); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - /* Key is found. Last found gets the found record. */ - if ( lastFound != 0 ) - *lastFound = (Element*)mid; - return (Element*)mid; - } - } -} - -template Element *BstTable:: - insert(const Key &key, Element **lastFound) -{ - const Element *lower, *mid, *upper; - long keyRelation, insertPos; - const long tblLen = BaseTable::length(); - - if ( tblLen == 0 ) { - /* If the table is empty then go straight to insert. */ - lower = BaseTable::data; - goto insert; - } - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the key in the array. - * Place to insert at is lower. */ - goto insert; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = this->compare(key, GET_KEY(*mid)); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - if ( lastFound != 0 ) - *lastFound = (Element*)mid; - return 0; - } - } - -insert: - /* Get the insert pos. */ - insertPos = lower - BaseTable::data; - - /* Do the insert. After makeRawSpaceFor, lower pointer is no good. */ - BaseVector::makeRawSpaceFor(insertPos, 1); - new(BaseTable::data + insertPos) Element(key); - - /* Set lastFound */ - if ( lastFound != 0 ) - *lastFound = BaseTable::data + insertPos; - return BaseTable::data + insertPos; -} - - -template Element *BstTable:: - insertMulti(const Key &key) -{ - const Element *lower, *mid, *upper; - long keyRelation, insertPos; - const long tblLen = BaseTable::length(); - - if ( tblLen == 0 ) { - /* If the table is empty then go straight to insert. */ - lower = BaseTable::data; - goto insert; - } - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the key in the array. - * Place to insert at is lower. */ - goto insert; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = this->compare(key, GET_KEY(*mid)); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - lower = mid; - goto insert; - } - } - -insert: - /* Get the insert pos. */ - insertPos = lower - BaseTable::data; - - /* Do the insert. */ - BaseVector::makeRawSpaceFor(insertPos, 1); - new(BaseTable::data + insertPos) Element(key); - - /* Return the element inserted. */ - return BaseTable::data + insertPos; -} - -/** - * \brief Insert each element from other. - * - * Always attempts to insert all elements even if the insert of some item from - * other fails. - * - * \returns True if all items inserted successfully, false if any insert - * failed. - */ -template bool BstTable:: - insert(const BstTable &other) -{ - bool allSuccess = true; - long otherLen = other.length(); - for ( long i = 0; i < otherLen; i++ ) { - Element *el = insert( other.data[i] ); - if ( el == 0 ) - allSuccess = false; - } - return allSuccess; -} - -/** - * \brief Insert each element from other even if the elements exist already. - * - * No individual insertMulti can fail. - */ -template void BstTable:: - insertMulti(const BstTable &other) -{ - long otherLen = other.length(); - for ( long i = 0; i < otherLen; i++ ) - insertMulti( other.data[i] ); -} - -#if ! defined( BSTSET ) - -/** - * \brief Insert the given element. - * - * If the key in the given element does not already exist in the table then a - * new element is inserted. They element copy constructor is used to place the - * element into the table. If lastFound is given, it is set to the new element - * created. If the insert fails then lastFound is set to the existing element - * of the same key. - * - * \returns The new element created upon success, null upon failure. - */ -template Element *BstTable:: - insert(const Element &el, Element **lastFound ) -{ - const Element *lower, *mid, *upper; - long keyRelation, insertPos; - const long tblLen = BaseTable::length(); - - if ( tblLen == 0 ) { - /* If the table is empty then go straight to insert. */ - lower = BaseTable::data; - goto insert; - } - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the key in the array. - * Place to insert at is lower. */ - goto insert; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = this->compare(GET_KEY(el), GET_KEY(*mid)); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - if ( lastFound != 0 ) - *lastFound = (Element*)mid; - return 0; - } - } - -insert: - /* Get the insert pos. */ - insertPos = lower - BaseTable::data; - - /* Do the insert. After makeRawSpaceFor, lower pointer is no good. */ - BaseVector::makeRawSpaceFor(insertPos, 1); - new(BaseTable::data + insertPos) Element(el); - - /* Set lastFound */ - if ( lastFound != 0 ) - *lastFound = BaseTable::data + insertPos; - return BaseTable::data + insertPos; -} - -/** - * \brief Insert the given element even if it exists already. - * - * If the key in the given element exists already then the new element is - * placed next to some other element of the same key. InsertMulti cannot fail. - * The element copy constructor is used to place the element in the table. - * - * \returns The new element created. - */ -template Element *BstTable:: - insertMulti(const Element &el) -{ - const Element *lower, *mid, *upper; - long keyRelation, insertPos; - const long tblLen = BaseTable::length(); - - if ( tblLen == 0 ) { - /* If the table is empty then go straight to insert. */ - lower = BaseTable::data; - goto insert; - } - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the fd in the array. - * Place to insert at is lower. */ - goto insert; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = this->compare(GET_KEY(el), GET_KEY(*mid)); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - lower = mid; - goto insert; - } - } - -insert: - /* Get the insert pos. */ - insertPos = lower - BaseTable::data; - - /* Do the insert. */ - BaseVector::makeRawSpaceFor(insertPos, 1); - new(BaseTable::data + insertPos) Element(el); - - /* Return the element inserted. */ - return BaseTable::data + insertPos; -} -#endif - - -#if defined( BSTMAP ) - -/** - * \brief Insert the given key-value pair. - * - * If the given key does not already exist in the table then the key-value - * pair is inserted. Copy constructors are used to place the pair in the - * table. If lastFound is given, it is set to the new entry created. If the - * insert fails then lastFound is set to the existing pair of the same key. - * - * \returns The new element created upon success, null upon failure. - */ -template Element *BstTable:: - insert(const Key &key, const Value &val, Element **lastFound) -{ - const Element *lower, *mid, *upper; - long keyRelation, insertPos; - const long tblLen = BaseTable::length(); - - if ( tblLen == 0 ) { - /* If the table is empty then go straight to insert. */ - lower = BaseTable::data; - goto insert; - } - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the fd in the array. - * Place to insert at is lower. */ - goto insert; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = Compare::compare(key, mid->key); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - if ( lastFound != NULL ) - *lastFound = (Element*)mid; - return 0; - } - } - -insert: - /* Get the insert pos. */ - insertPos = lower - BaseTable::data; - - /* Do the insert. */ - BaseVector::makeRawSpaceFor(insertPos, 1); - new(BaseTable::data + insertPos) Element(key, val); - - /* Set lastFound */ - if ( lastFound != NULL ) - *lastFound = BaseTable::data + insertPos; - return BaseTable::data + insertPos; -} - - -/** - * \brief Insert the given key-value pair even if the key exists already. - * - * If the key exists already then the key-value pair is placed next to some - * other pair of the same key. InsertMulti cannot fail. Copy constructors are - * used to place the pair in the table. - * - * \returns The new element created. - */ -template Element *BstTable:: - insertMulti(const Key &key, const Value &val) -{ - const Element *lower, *mid, *upper; - long keyRelation, insertPos; - const long tblLen = BaseTable::length(); - - if ( tblLen == 0 ) { - /* If the table is empty then go straight to insert. */ - lower = BaseTable::data; - goto insert; - } - - lower = BaseTable::data; - upper = BaseTable::data + tblLen - 1; - while ( true ) { - if ( upper < lower ) { - /* Did not find the key in the array. - * Place to insert at is lower. */ - goto insert; - } - - mid = lower + ((upper-lower)>>1); - keyRelation = Compare::compare(key, mid->key); - - if ( keyRelation < 0 ) - upper = mid - 1; - else if ( keyRelation > 0 ) - lower = mid + 1; - else { - lower = mid; - goto insert; - } - } - -insert: - /* Get the insert pos. */ - insertPos = lower - BaseTable::data; - - /* Do the insert. */ - BaseVector::makeRawSpaceFor(insertPos, 1); - new(BaseTable::data + insertPos) Element(key, val); - - /* Return the element inserted. */ - return BaseTable::data + insertPos; -} - -#endif - -#ifdef AAPL_NAMESPACE -} -#endif diff --git a/aapl/bstmap.h b/aapl/bstmap.h deleted file mode 100644 index 4a5f5310..00000000 --- a/aapl/bstmap.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_BSTMAP_H -#define _AAPL_BSTMAP_H - -#include "compare.h" -#include "vector.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \brief Element for BstMap. - * - * Stores the key and value pair. - */ -template struct BstMapEl -{ - BstMapEl() {} - BstMapEl(const Key &key) : key(key) {} - BstMapEl(const Key &key, const Value &val) : key(key), value(val) {} - - /** \brief The key */ - Key key; - - /** \brief The value. */ - Value value; -}; - -#ifdef AAPL_NAMESPACE -} -#endif - -/** - * \addtogroup bst - * @{ - */ - -/** - * \class BstMap - * \brief Binary search table for key and value pairs. - * - * BstMap stores key and value pairs in each element. The key and value can be - * any type. A compare class for the key must be supplied. - */ - -/*@}*/ - -#define BST_TEMPL_DECLARE class Key, class Value, \ - class Compare = CmpOrd, class Resize = ResizeExpn -#define BST_TEMPL_DEF class Key, class Value, class Compare, class Resize -#define BST_TEMPL_USE Key, Value, Compare, Resize -#define GET_KEY(el) ((el).key) -#define BstTable BstMap -#define Element BstMapEl -#define BSTMAP - -#include "bstcommon.h" - -#undef BST_TEMPL_DECLARE -#undef BST_TEMPL_DEF -#undef BST_TEMPL_USE -#undef GET_KEY -#undef BstTable -#undef Element -#undef BSTMAP - -/** - * \fn BstMap::insert(const Key &key, BstMapEl **lastFound) - * \brief Insert the given key. - * - * If the given key does not already exist in the table then a new element - * having key is inserted. They key copy constructor and value default - * constructor are used to place the pair in the table. If lastFound is given, - * it is set to the new entry created. If the insert fails then lastFound is - * set to the existing pair of the same key. - * - * \returns The new element created upon success, null upon failure. - */ - -/** - * \fn BstMap::insertMulti(const Key &key) - * \brief Insert the given key even if it exists already. - * - * If the key exists already then the new element having key is placed next - * to some other pair of the same key. InsertMulti cannot fail. The key copy - * constructor and the value default constructor are used to place the pair in - * the table. - * - * \returns The new element created. - */ - -#endif /* _AAPL_BSTMAP_H */ diff --git a/aapl/bstset.h b/aapl/bstset.h deleted file mode 100644 index 14e0375a..00000000 --- a/aapl/bstset.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_BSTSET_H -#define _AAPL_BSTSET_H - -/** - * \addtogroup bst - * @{ - */ - -/** - * \class BstSet - * \brief Binary search table for types that are the key. - * - * BstSet is suitable for types that comprise the entire key. Rather than look - * into the element to retrieve the key, the element is the key. A class that - * contains a comparison routine for the key must be given. - */ - -/*@}*/ - -#include "compare.h" -#include "vector.h" - -#define BST_TEMPL_DECLARE class Key, class Compare = CmpOrd, \ - class Resize = ResizeExpn -#define BST_TEMPL_DEF class Key, class Compare, class Resize -#define BST_TEMPL_USE Key, Compare, Resize -#define GET_KEY(el) (el) -#define BstTable BstSet -#define Element Key -#define BSTSET - -#include "bstcommon.h" - -#undef BST_TEMPL_DECLARE -#undef BST_TEMPL_DEF -#undef BST_TEMPL_USE -#undef GET_KEY -#undef BstTable -#undef Element -#undef BSTSET - -/** - * \fn BstSet::insert(const Key &key, Key **lastFound) - * \brief Insert the given key. - * - * If the given key does not already exist in the table then it is inserted. - * The key's copy constructor is used to place the item in the table. If - * lastFound is given, it is set to the new entry created. If the insert fails - * then lastFound is set to the existing key of the same value. - * - * \returns The new element created upon success, null upon failure. - */ - -/** - * \fn BstSet::insertMulti(const Key &key) - * \brief Insert the given key even if it exists already. - * - * If the key exists already then it is placed next to some other key of the - * same value. InsertMulti cannot fail. The key's copy constructor is used to - * place the item in the table. - * - * \returns The new element created. - */ - -#endif /* _AAPL_BSTSET_H */ diff --git a/aapl/bsttable.h b/aapl/bsttable.h deleted file mode 100644 index cb9a056a..00000000 --- a/aapl/bsttable.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_BSTTABLE_H -#define _AAPL_BSTTABLE_H - -#include "compare.h" -#include "vector.h" - -/** - * \addtogroup bst - * @{ - */ - -/** - * \class BstTable - * \brief Binary search table for structures that contain a key. - * - * This is the basic binary search table. It can be used to contain a - * structure that has a key and possibly some data. The key should be a member - * of the element class and accessible with getKey(). A class containing the - * compare routine must be supplied. - */ - -/*@}*/ - -#define BST_TEMPL_DECLARE class Element, class Key, \ - class Compare = CmpOrd, class Resize = ResizeExpn -#define BST_TEMPL_DEF class Element, class Key, class Compare, class Resize -#define BST_TEMPL_USE Element, Key, Compare, Resize -#define GET_KEY(el) ((el).getKey()) -#define BSTTABLE - -#include "bstcommon.h" - -#undef BST_TEMPL_DECLARE -#undef BST_TEMPL_DEF -#undef BST_TEMPL_USE -#undef GET_KEY -#undef BSTTABLE - -/** - * \fn BstTable::insert(const Key &key, Element **lastFound) - * \brief Insert a new element with the given key. - * - * If the given key does not already exist in the table a new element is - * inserted with the given key. A constructor taking only const Key& is used - * to initialize the new element. If lastFound is given, it is set to the new - * element created. If the insert fails then lastFound is set to the existing - * element with the same key. - * - * \returns The new element created upon success, null upon failure. - */ - -/** - * \fn BstTable::insertMulti(const Key &key) - * \brief Insert a new element even if the key exists already. - * - * If the key exists already then the new element is placed next to some - * element with the same key. InsertMulti cannot fail. A constructor taking - * only const Key& is used to initialize the new element. - * - * \returns The new element created. - */ - -#endif /* _AAPL_BSTTABLE_H */ diff --git a/aapl/bubblesort.h b/aapl/bubblesort.h deleted file mode 100644 index 42482991..00000000 --- a/aapl/bubblesort.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_BUBBLESORT_H -#define _AAPL_BUBBLESORT_H - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup sort - * @{ - */ - -/** - * \class BubbleSort - * \brief Bubble sort an array of data. - * - * BubbleSort can be used to sort any array of objects of type T provided a - * compare class is given. BubbleSort is in-place. It does not require any - * temporary storage. - * - * Objects are not made aware that they are being moved around in memory. - * Assignment operators, constructors and destructors are never invoked by the - * sort. - * - * BubbleSort runs in O(n^2) time. It is most useful when sorting arrays that - * are nearly sorted. It is best when neighbouring pairs are out of place. - * BubbleSort is a stable sort, meaning that objects with the same key have - * their relative ordering preserved. - */ - -/*@}*/ - -/* BubbleSort. */ -template class BubbleSort - : public Compare -{ -public: - /* Sorting interface routine. */ - void sort(T *data, long len); -}; - - -/** - * \brief Bubble sort an array of data. - */ -template void BubbleSort:: - sort(T *data, long len) -{ - bool changed = true; - for ( long pass = 1; changed && pass < len; pass ++ ) { - changed = false; - for ( long i = 0; i < len-pass; i++ ) { - /* Do we swap pos with the next one? */ - if ( this->compare( data[i], data[i+1] ) > 0 ) { - char tmp[sizeof(T)]; - - /* Swap the two items. */ - memcpy( tmp, data+i, sizeof(T) ); - memcpy( data+i, data+i+1, sizeof(T) ); - memcpy( data+i+1, tmp, sizeof(T) ); - - /* Note that we made a change. */ - changed = true; - } - } - } -} - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_BUBBLESORT_H */ diff --git a/aapl/buffer.h b/aapl/buffer.h deleted file mode 100644 index 2aef08ca..00000000 --- a/aapl/buffer.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2003 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_BUFFER_H -#define _AAPL_BUFFER_H - -/* Buffer is an automatically grown buffer for collecting tokens. Always reuses - * space. Never down resizes. Clear is a cheap operation. */ -struct Buffer -{ - static const int BUFFER_INITIAL_SIZE = 4096; - - Buffer() - { - data = (char*) malloc( BUFFER_INITIAL_SIZE ); - allocated = BUFFER_INITIAL_SIZE; - length = 0; - } - - ~Buffer() - { - free(data); - } - - void append( char p ) - { - if ( length == allocated ) { - allocated *= 2; - data = (char*) realloc( data, allocated ); - } - data[length++] = p; - } - - void clear() { length = 0; } - - char *data; - int allocated; - int length; -}; - -#endif diff --git a/aapl/compare.h b/aapl/compare.h deleted file mode 100644 index bbfa2136..00000000 --- a/aapl/compare.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2001 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_COMPARE_H -#define _AAPL_COMPARE_H - -#include -#include -#include "table.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \defgroup compare Compare - * \brief Basic compare clases. - * - * Compare classes are used by data structures that need to know the relative - * ordering of elemets. To become a compare class, a class must imlement a - * routine long compare(const T &key1, const T &key2) that behaves just like - * strcmp. - * - * Compare classes are passed to the template data structure as a template - * parameter and are inherited. In most cases the compare routine will base - * the key comparision only on the two keys and the compare routine can - * therefore be static. Though sometimes it is useful to include data in the - * compare class and use this data in the comparison. For example the compare - * class may contain a pointer to some other data structure to which the - * comparison is delegated. - * - * @{ - */ - -/** - * \brief Compare two null terminated character sequences. - * - * This comparision class is a wrapper for strcmp. - */ -struct CmpStr -{ - /** - * \brief Compare two null terminated string types. - */ - static inline long compare(const char *k1, const char *k2) - { return strcmp(k1, k2); } -}; - -struct CmpString -{ - static inline long compare(const std::string &k1, const std::string &k2) - { return k1.compare( k2 ); } -}; - - -/** - * \brief Compare a type for which < and > are implemented. - * - * CmpOrd is suitable for simple types such as integers and pointers that by - * default have the less-than and greater-than operators defined. - */ -template struct CmpOrd -{ - /** - * \brief Compare two ordinal types. - * - * This compare routine copies its arguements in by value. - */ - static inline long compare(const T k1, const T k2) - { - if (k1 < k2) - return -1; - else if (k1 > k2) - return 1; - else - return 0; - } -}; - -/** - * \brief Compare two tables of type T - * - * Table comparison is useful for keying a data structure on a vector or - * binary search table. T is the element type stored in the table. - * CompareT is the comparison structure used to compare the individual values - * in the table. - */ -template < class T, class CompareT = CmpOrd > struct CmpTable - : public CompareT -{ - /** - * \brief Compare two tables storing type T. - */ - static inline long compare(const Table &t1, const Table &t2) - { - if ( t1.tabLen < t2.tabLen ) - return -1; - else if ( t1.tabLen > t2.tabLen ) - return 1; - else - { - T *i1 = t1.data, *i2 = t2.data; - long len = t1.tabLen, cmpResult; - for ( long pos = 0; pos < len; - pos += 1, i1 += 1, i2 += 1 ) - { - cmpResult = CompareT::compare(*i1, *i2); - if ( cmpResult != 0 ) - return cmpResult; - } - return 0; - } - } -}; - -/** - * \brief Compare two tables of type T -- non-static version. - * - * CmpTableNs is identical to CmpTable, however the compare routine is - * non-static. If the CompareT class contains a non-static compare, then this - * version must be used because a static member cannot invoke a non-static - * member. - * - * Table comparison is useful for keying a data structure on a vector or binary - * search table. T is the element type stored in the table. CompareT - * is the comparison structure used to compare the individual values in the - * table. - */ -template < class T, class CompareT = CmpOrd > struct CmpTableNs - : public CompareT -{ - /** - * \brief Compare two tables storing type T. - */ - inline long compare(const Table &t1, const Table &t2) - { - if ( t1.tabLen < t2.tabLen ) - return -1; - else if ( t1.tabLen > t2.tabLen ) - return 1; - else - { - T *i1 = t1.data, *i2 = t2.data; - long len = t1.tabLen, cmpResult; - for ( long pos = 0; pos < len; - pos += 1, i1 += 1, i2 += 1 ) - { - cmpResult = CompareT::compare(*i1, *i2); - if ( cmpResult != 0 ) - return cmpResult; - } - return 0; - } - } -}; - -/** - * \brief Compare two implicitly shared tables of type T - * - * This table comparison is for data structures based on implicitly - * shared tables. - * - * Table comparison is useful for keying a data structure on a vector or - * binary search table. T is the element type stored in the table. - * CompareT is the comparison structure used to compare the individual values - * in the table. - */ -template < class T, class CompareT = CmpOrd > struct CmpSTable : public CompareT -{ - /** - * \brief Compare two tables storing type T. - */ - static inline long compare(const STable &t1, const STable &t2) - { - long t1Length = t1.length(); - long t2Length = t2.length(); - - /* Compare lengths. */ - if ( t1Length < t2Length ) - return -1; - else if ( t1Length > t2Length ) - return 1; - else { - /* Compare the table data. */ - T *i1 = t1.data, *i2 = t2.data; - for ( long pos = 0; pos < t1Length; - pos += 1, i1 += 1, i2 += 1 ) - { - long cmpResult = CompareT::compare(*i1, *i2); - if ( cmpResult != 0 ) - return cmpResult; - } - return 0; - } - } -}; - -/** - * \brief Compare two implicitly shared tables of type T -- non-static - * version. - * - * This is a non-static table comparison for data structures based on - * implicitly shared tables. If the CompareT class contains a non-static - * compare, then this version must be used because a static member cannot - * invoke a non-static member. - * - * Table comparison is useful for keying a data structure on a vector or - * binary search table. T is the element type stored in the table. - * CompareT is the comparison structure used to compare the individual values - * in the table. - */ -template < class T, class CompareT = CmpOrd > struct CmpSTableNs - : public CompareT -{ - /** - * \brief Compare two tables storing type T. - */ - inline long compare(const STable &t1, const STable &t2) - { - long t1Length = t1.length(); - long t2Length = t2.length(); - - /* Compare lengths. */ - if ( t1Length < t2Length ) - return -1; - else if ( t1Length > t2Length ) - return 1; - else { - /* Compare the table data. */ - T *i1 = t1.data, *i2 = t2.data; - for ( long pos = 0; pos < t1Length; - pos += 1, i1 += 1, i2 += 1 ) - { - long cmpResult = CompareT::compare(*i1, *i2); - if ( cmpResult != 0 ) - return cmpResult; - } - return 0; - } - } -}; - - -/*@}*/ - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_COMPARE_H */ diff --git a/aapl/dlcommon.h b/aapl/dlcommon.h deleted file mode 100644 index 92d45259..00000000 --- a/aapl/dlcommon.h +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright 2001 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* This header is not wrapped in ifndef becuase it is not intended to - * be included by the user. */ - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -#if defined( DOUBLELIST_VALUE ) -/** - * \brief Double list element for DListVal. - * - * DListValEl stores the type T of DListVal by value. - */ -template struct DListValEl -{ - /** - * \brief Construct a DListValEl with a given value. - * - * The only constructor available initializes the value element. This - * enforces that DListVal elements are never created without having their - * value intialzed by the user. T's copy constructor is used to copy the - * value in. - */ - DListValEl( const T &val ) : value(val) { } - - /** - * \brief Value stored by the list element. - * - * Value is always copied into new list elements using the copy - * constructor. - */ - T value; - - /** - * \brief List previous pointer. - * - * Points to the previous item in the list. If this is the first item in - * the list, then prev is NULL. If this element is not in a list then - * prev is undefined. - */ - DListValEl *prev; - - /** - * \brief List next pointer. - * - * Points to the next item in the list. If this is the list item in the - * list, then next is NULL. If this element is not in a list then next is - * undefined. - */ - DListValEl *next; -}; -#else - -#ifndef __AAPL_DOUBLE_LIST_EL -#define __AAPL_DOUBLE_LIST_EL -/** - * \brief Double list element properties. - * - * This class can be inherited to make a class suitable to be a double list - * element. It simply provides the next and previous pointers. An alternative - * is to put the next and previous pointers in the class directly. - */ -template struct DListEl -{ - /** - * \brief List previous pointer. - * - * Points to the previous item in the list. If this is the first item in - * the list, then prev is NULL. If this element is not in a list then - * prev is undefined. - */ - Element *prev; - - /** - * \brief List next pointer. - * - * Points to the next item in the list. If this is the list item in the - * list, then next is NULL. If this element is not in a list then next is - * undefined. - */ - Element *next; -}; -#endif /* __AAPL_DOUBLE_LIST_EL */ - -#endif - -/* Doubly Linked List */ -template class DList -{ -public: - /** \brief Initialize an empty list. */ - DList() : head(0), tail(0), listLen(0) {} - - /** - * \brief Perform a deep copy of the list. - * - * The elements of the other list are duplicated and put into this list. - * Elements are copied using the copy constructor. - */ - DList(const DList &other); - -#ifdef DOUBLELIST_VALUE - /** - * \brief Clear the double list contents. - * - * All elements are deleted. - */ - ~DList() { empty(); } - - /** - * \brief Assign another list into this list using a deep copy. - * - * The elements of the other list are duplicated and put into this list. - * Each list item is created using the copy constructor. If this list - * contains any elements before the copy, they are deleted first. - * - * \returns A reference to this. - */ - DList &operator=(const DList &other); - - /** - * \brief Transfer the contents of another list into this list. - * - * The elements of the other list moved in. The other list will be empty - * afterwards. If this list contains any elements before the copy, then - * they are deleted. - */ - void transfer(DList &other); -#else - /** - * \brief Abandon all elements in the list. - * - * List elements are not deleted. - */ - ~DList() {} - - /** - * \brief Perform a deep copy of the list. - * - * The elements of the other list are duplicated and put into this list. - * Each list item is created using the copy constructor. If this list - * contains any elements before the copy, they are abandoned. - * - * \returns A reference to this. - */ - DList &operator=(const DList &other); - - /** - * \brief Transfer the contents of another list into this list. - * - * The elements of the other list moved in. The other list will be empty - * afterwards. If this list contains any elements before the copy, they - * are abandoned. - */ - void transfer(DList &other); -#endif - - -#ifdef DOUBLELIST_VALUE - /** - * \brief Make a new element and prepend it to the front of the list. - * - * The item is copied into the new element using the copy constructor. - * Equivalent to list.addBefore(list.head, item). - */ - void prepend(const T &item); - - /** - * \brief Make a new element and append it to the end of the list. - * - * The item is copied into the new element using the copy constructor. - * Equivalent to list.addAfter(list.tail, item). - */ - void append(const T &item); - - /** - * \brief Make a new element and insert it immediately after an element in - * the list. - * - * The item is copied into the new element using the copy constructor. If - * prev_el is NULL then the new element is prepended to the front of the - * list. If prev_el is not already in the list then undefined behaviour - * results. Equivalent to list.addAfter(prev_el, new DListValEl(item)). - */ - void addAfter(Element *prev_el, const T &item); - - /** - * \brief Make a new element and insert it immediately before an element - * in the list. - * - * The item is copied into the new element using the copy construcotor. If - * next_el is NULL then the new element is appended to the end of the - * list. If next_el is not already in the list then undefined behaviour - * results. Equivalent to list.addBefore(next_el, new DListValEl(item)). - */ - void addBefore(Element *next_el, const T &item); -#endif - - /** - * \brief Prepend a single element to the front of the list. - * - * If new_el is already an element of some list, then undefined behaviour - * results. Equivalent to list.addBefore(list.head, new_el). - */ - void prepend(Element *new_el) { addBefore(head, new_el); } - - /** - * \brief Append a single element to the end of the list. - * - * If new_el is alreay an element of some list, then undefined behaviour - * results. Equivalent to list.addAfter(list.tail, new_el). - */ - void append(Element *new_el) { addAfter(tail, new_el); } - - /** - * \brief Prepend an entire list to the beginning of this list. - * - * All items are moved, not copied. Afterwards, the other list is emtpy. - * All items are prepended at once, so this is an O(1) operation. - * Equivalent to list.addBefore(list.head, dl). - */ - void prepend(DList &dl) { addBefore(head, dl); } - - /** - * \brief Append an entire list to the end of the list. - * - * All items are moved, not copied. Afterwards, the other list is empty. - * All items are appened at once, so this is an O(1) operation. - * Equivalent to list.addAfter(list.tail, dl). - */ - void append(DList &dl) { addAfter(tail, dl); } - - void addAfter(Element *prev_el, Element *new_el); - void addBefore(Element *next_el, Element *new_el); - - void addAfter(Element *prev_el, DList &dl); - void addBefore(Element *next_el, DList &dl); - - /** - * \brief Detach the head of the list - * - * The element detached is not deleted. If there is no head of the list - * (the list is empty) then undefined behaviour results. Equivalent to - * list.detach(list.head). - * - * \returns The element detached. - */ - Element *detachFirst() { return detach(head); } - - /** - * \brief Detach the tail of the list - * - * The element detached is not deleted. If there is no tail of the list - * (the list is empty) then undefined behaviour results. Equivalent to - * list.detach(list.tail). - * - * \returns The element detached. - */ - Element *detachLast() { return detach(tail); } - - /* Detaches an element from the list. Does not free any memory. */ - Element *detach(Element *el); - - /** - * \brief Detach and delete the first element in the list. - * - * If there is no first element (the list is empty) then undefined - * behaviour results. Equivalent to delete list.detach(list.head); - */ - void removeFirst() { delete detach( head ); } - - /** - * \brief Detach and delete the last element in the list. - * - * If there is no last element (the list is emtpy) then undefined - * behaviour results. Equivalent to delete list.detach(list.tail); - */ - void removeLast() { delete detach( tail ); } - - /** - * \brief Detach and delete an element from the list. - * - * If the element is not in the list, then undefined behaviour results. - * Equivalent to delete list.detach(el); - */ - void remove(Element *el) { delete detach( el ); } - - void empty(); - void abandon(); - - /** \brief The number of elements in the list. */ - long length() const { return listLen; } - - /** \brief Head and tail of the linked list. */ - Element *head, *tail; - - /** \brief The number of element in the list. */ - long listLen; - - /* Convenience access. */ - long size() const { return listLen; } - - /* Forward this so a ref can be used. */ - struct Iter; - - /* Class for setting the iterator. */ - struct IterFirst { IterFirst( const DList &l ) : l(l) { } const DList &l; }; - struct IterLast { IterLast( const DList &l ) : l(l) { } const DList &l; }; - struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; - struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; - - /** - * \brief Double List Iterator. - * \ingroup iterators - */ - struct Iter - { - /* Default construct. */ - Iter() : ptr(0) { } - - /* Construct from a double list. */ - Iter( const DList &dl ) : ptr(dl.head) { } - Iter( Element *el ) : ptr(el) { } - Iter( const IterFirst &dlf ) : ptr(dlf.l.head) { } - Iter( const IterLast &dll ) : ptr(dll.l.tail) { } - Iter( const IterNext &dln ) : ptr(dln.i.ptr->BASE_EL(next)) { } - Iter( const IterPrev &dlp ) : ptr(dlp.i.ptr->BASE_EL(prev)) { } - - /* Assign from a double list. */ - Iter &operator=( const DList &dl ) { ptr = dl.head; return *this; } - Iter &operator=( Element *el ) { ptr = el; return *this; } - Iter &operator=( const IterFirst &af ) { ptr = af.l.head; return *this; } - Iter &operator=( const IterLast &al ) { ptr = al.l.tail; return *this; } - Iter &operator=( const IterNext &an ) { ptr = an.i.ptr->BASE_EL(next); return *this; } - Iter &operator=( const IterPrev &ap ) { ptr = ap.i.ptr->BASE_EL(prev); return *this; } - - /** \brief Less than end? */ - bool lte() const { return ptr != 0; } - - /** \brief At end? */ - bool end() const { return ptr == 0; } - - /** \brief Greater than beginning? */ - bool gtb() const { return ptr != 0; } - - /** \brief At beginning? */ - bool beg() const { return ptr == 0; } - - /** \brief At first element? */ - bool first() const { return ptr && ptr->BASE_EL(prev) == 0; } - - /** \brief At last element? */ - bool last() const { return ptr && ptr->BASE_EL(next) == 0; } - - /** \brief Implicit cast to Element*. */ - operator Element*() const { return ptr; } - - /** \brief Dereference operator returns Element&. */ - Element &operator *() const { return *ptr; } - - /** \brief Arrow operator returns Element*. */ - Element *operator->() const { return ptr; } - - /** \brief Move to next item. */ - inline Element *operator++() { return ptr = ptr->BASE_EL(next); } - - /** \brief Move to next item. */ - inline Element *increment() { return ptr = ptr->BASE_EL(next); } - - /** \brief Move to next item. */ - inline Element *operator++(int); - - /** \brief Move to previous item. */ - inline Element *operator--() { return ptr = ptr->BASE_EL(prev); } - - /** \brief Move to previous item. */ - inline Element *decrement() { return ptr = ptr->BASE_EL(prev); } - - /** \brief Move to previous item. */ - inline Element *operator--(int); - - /** \brief Return the next item. Does not modify this. */ - inline IterNext next() const { return IterNext(*this); } - - /** \brief Return the prev item. Does not modify this. */ - inline IterPrev prev() const { return IterPrev(*this); } - - /** \brief The iterator is simply a pointer. */ - Element *ptr; - }; - - /** \brief Return first element. */ - IterFirst first() { return IterFirst(*this); } - - /** \brief Return last element. */ - IterLast last() { return IterLast(*this); } -}; - -/* Copy constructor, does a deep copy of other. */ -template DList:: - DList(const DList &other) : - head(0), tail(0), listLen(0) -{ - Element *el = other.head; - while( el != 0 ) { - append( new Element(*el) ); - el = el->BASE_EL(next); - } -} - -#ifdef DOUBLELIST_VALUE - -/* Assignement operator does deep copy. */ -template DList &DList:: - operator=(const DList &other) -{ - /* Free the old list. The value list assumes items were allocated on the - * heap by itself. */ - empty(); - - Element *el = other.head; - while( el != 0 ) { - append( new Element(*el) ); - el = el->BASE_EL(next); - } - return *this; -} - -template void DList:: - transfer(DList &other) -{ - /* Free the old list. The value list assumes items were allocated on the - * heap by itself. */ - empty(); - - head = other.head; - tail = other.tail; - listLen = other.listLen; - - other.abandon(); -} - -#else - -/* Assignement operator does deep copy. */ -template DList &DList:: - operator=(const DList &other) -{ - Element *el = other.head; - while( el != 0 ) { - append( new Element(*el) ); - el = el->BASE_EL(next); - } - return *this; -} - -template void DList:: - transfer(DList &other) -{ - head = other.head; - tail = other.tail; - listLen = other.listLen; - - other.abandon(); -} - -#endif - -#ifdef DOUBLELIST_VALUE - -/* Prepend a new item. Inlining this bloats the caller with new overhead. */ -template void DList:: - prepend(const T &item) -{ - addBefore(head, new Element(item)); -} - -/* Append a new item. Inlining this bloats the caller with the new overhead. */ -template void DList:: - append(const T &item) -{ - addAfter(tail, new Element(item)); -} - -/* Add a new item after a prev element. Inlining this bloats the caller with - * the new overhead. */ -template void DList:: - addAfter(Element *prev_el, const T &item) -{ - addAfter(prev_el, new Element(item)); -} - -/* Add a new item before a next element. Inlining this bloats the caller with - * the new overhead. */ -template void DList:: - addBefore(Element *next_el, const T &item) -{ - addBefore(next_el, new Element(item)); -} - -#endif - -/* - * The larger iterator operators. - */ - -/* Postfix ++ */ -template Element *DList::Iter:: - operator++(int) -{ - Element *rtn = ptr; - ptr = ptr->BASE_EL(next); - return rtn; -} - -/* Postfix -- */ -template Element *DList::Iter:: - operator--(int) -{ - Element *rtn = ptr; - ptr = ptr->BASE_EL(prev); - return rtn; -} - -/** - * \brief Insert an element immediately after an element in the list. - * - * If prev_el is NULL then new_el is prepended to the front of the list. If - * prev_el is not in the list or if new_el is already in a list, then - * undefined behaviour results. - */ -template void DList:: - addAfter(Element *prev_el, Element *new_el) -{ - /* Set the previous pointer of new_el to prev_el. We do - * this regardless of the state of the list. */ - new_el->BASE_EL(prev) = prev_el; - - /* Set forward pointers. */ - if (prev_el == 0) { - /* There was no prev_el, we are inserting at the head. */ - new_el->BASE_EL(next) = head; - head = new_el; - } - else { - /* There was a prev_el, we can access previous next. */ - new_el->BASE_EL(next) = prev_el->BASE_EL(next); - prev_el->BASE_EL(next) = new_el; - } - - /* Set reverse pointers. */ - if (new_el->BASE_EL(next) == 0) { - /* There is no next element. Set the tail pointer. */ - tail = new_el; - } - else { - /* There is a next element. Set it's prev pointer. */ - new_el->BASE_EL(next)->BASE_EL(prev) = new_el; - } - - /* Update list length. */ - listLen++; -} - -/** - * \brief Insert an element immediatly before an element in the list. - * - * If next_el is NULL then new_el is appended to the end of the list. If - * next_el is not in the list or if new_el is already in a list, then - * undefined behaviour results. - */ -template void DList:: - addBefore(Element *next_el, Element *new_el) -{ - /* Set the next pointer of the new element to next_el. We do - * this regardless of the state of the list. */ - new_el->BASE_EL(next) = next_el; - - /* Set reverse pointers. */ - if (next_el == 0) { - /* There is no next elememnt. We are inserting at the tail. */ - new_el->BASE_EL(prev) = tail; - tail = new_el; - } - else { - /* There is a next element and we can access next's previous. */ - new_el->BASE_EL(prev) = next_el->BASE_EL(prev); - next_el->BASE_EL(prev) = new_el; - } - - /* Set forward pointers. */ - if (new_el->BASE_EL(prev) == 0) { - /* There is no previous element. Set the head pointer.*/ - head = new_el; - } - else { - /* There is a previous element, set it's next pointer to new_el. */ - new_el->BASE_EL(prev)->BASE_EL(next) = new_el; - } - - /* Update list length. */ - listLen++; -} - -/** - * \brief Insert an entire list immediatly after an element in this list. - * - * Elements are moved, not copied. Afterwards, the other list is empty. If - * prev_el is NULL then the elements are prepended to the front of the list. - * If prev_el is not in the list then undefined behaviour results. All - * elements are inserted into the list at once, so this is an O(1) operation. - */ -template void DList:: - addAfter( Element *prev_el, DList &dl ) -{ - /* Do not bother if dl has no elements. */ - if ( dl.listLen == 0 ) - return; - - /* Set the previous pointer of dl.head to prev_el. We do - * this regardless of the state of the list. */ - dl.head->BASE_EL(prev) = prev_el; - - /* Set forward pointers. */ - if (prev_el == 0) { - /* There was no prev_el, we are inserting at the head. */ - dl.tail->BASE_EL(next) = head; - head = dl.head; - } - else { - /* There was a prev_el, we can access previous next. */ - dl.tail->BASE_EL(next) = prev_el->BASE_EL(next); - prev_el->BASE_EL(next) = dl.head; - } - - /* Set reverse pointers. */ - if (dl.tail->BASE_EL(next) == 0) { - /* There is no next element. Set the tail pointer. */ - tail = dl.tail; - } - else { - /* There is a next element. Set it's prev pointer. */ - dl.tail->BASE_EL(next)->BASE_EL(prev) = dl.tail; - } - - /* Update the list length. */ - listLen += dl.listLen; - - /* Empty out dl. */ - dl.head = dl.tail = 0; - dl.listLen = 0; -} - -/** - * \brief Insert an entire list immediately before an element in this list. - * - * Elements are moved, not copied. Afterwards, the other list is empty. If - * next_el is NULL then the elements are appended to the end of the list. If - * next_el is not in the list then undefined behaviour results. All elements - * are inserted at once, so this is an O(1) operation. - */ -template void DList:: - addBefore( Element *next_el, DList &dl ) -{ - /* Do not bother if dl has no elements. */ - if ( dl.listLen == 0 ) - return; - - /* Set the next pointer of dl.tail to next_el. We do - * this regardless of the state of the list. */ - dl.tail->BASE_EL(next) = next_el; - - /* Set reverse pointers. */ - if (next_el == 0) { - /* There is no next elememnt. We are inserting at the tail. */ - dl.head->BASE_EL(prev) = tail; - tail = dl.tail; - } - else { - /* There is a next element and we can access next's previous. */ - dl.head->BASE_EL(prev) = next_el->BASE_EL(prev); - next_el->BASE_EL(prev) = dl.tail; - } - - /* Set forward pointers. */ - if (dl.head->BASE_EL(prev) == 0) { - /* There is no previous element. Set the head pointer.*/ - head = dl.head; - } - else { - /* There is a previous element, set it's next pointer to new_el. */ - dl.head->BASE_EL(prev)->BASE_EL(next) = dl.head; - } - - /* Update list length. */ - listLen += dl.listLen; - - /* Empty out dl. */ - dl.head = dl.tail = 0; - dl.listLen = 0; -} - - -/** - * \brief Detach an element from the list. - * - * The element is not deleted. If the element is not in the list, then - * undefined behaviour results. - * - * \returns The element detached. - */ -template Element *DList:: - detach(Element *el) -{ - /* Set forward pointers to skip over el. */ - if (el->BASE_EL(prev) == 0) - head = el->BASE_EL(next); - else { - el->BASE_EL(prev)->BASE_EL(next) = - el->BASE_EL(next); - } - - /* Set reverse pointers to skip over el. */ - if (el->BASE_EL(next) == 0) - tail = el->BASE_EL(prev); - else { - el->BASE_EL(next)->BASE_EL(prev) = - el->BASE_EL(prev); - } - - /* Update List length and return element we detached. */ - listLen--; - return el; -} - -/** - * \brief Clear the list by deleting all elements. - * - * Each item in the list is deleted. The list is reset to its initial state. - */ -template void DList::empty() -{ - Element *nextToGo = 0, *cur = head; - - while (cur != 0) - { - nextToGo = cur->BASE_EL(next); - delete cur; - cur = nextToGo; - } - head = tail = 0; - listLen = 0; -} - -/** - * \brief Clear the list by forgetting all elements. - * - * All elements are abandoned, not deleted. The list is reset to it's initial - * state. - */ -template void DList::abandon() -{ - head = tail = 0; - listLen = 0; -} - -#ifdef AAPL_NAMESPACE -} -#endif diff --git a/aapl/dlist.h b/aapl/dlist.h deleted file mode 100644 index 9663caca..00000000 --- a/aapl/dlist.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2001 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_DLIST_H -#define _AAPL_DLIST_H - -#define BASE_EL(name) name -#define DLMEL_TEMPDEF class Element -#define DLMEL_TEMPUSE Element -#define DList DList - -/** - * \addtogroup dlist - * @{ - */ - -/** - * \class DList - * \brief Basic doubly linked list. - * - * DList is the standard by-structure list type. This class requires the - * programmer to declare a list element type that has the necessary next and - * previous pointers in it. This can be achieved by inheriting from the - * DListEl class or by simply adding next and previous pointers directly into - * the list element class. - * - * DList does not assume ownership of elements in the list. If the elements - * are known to reside on the heap, the provided empty() routine can be used to - * delete all elements, however the destructor will not call this routine, it - * will simply abandon all the elements. It is up to the programmer to - * explicitly de-allocate items when necessary. - * - * \include ex_dlist.cpp - */ - -/*@}*/ - -#include "dlcommon.h" - -#undef BASE_EL -#undef DLMEL_TEMPDEF -#undef DLMEL_TEMPUSE -#undef DList - -#endif /* _AAPL_DLIST_H */ - diff --git a/aapl/dlistmel.h b/aapl/dlistmel.h deleted file mode 100644 index f2004b81..00000000 --- a/aapl/dlistmel.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2001 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_DLISTMEL_H -#define _AAPL_DLISTMEL_H - -/** - * \addtogroup dlist - * @{ - */ - -/** - * \class DListMel - * \brief Doubly linked list for elements that may appear in multiple lists. - * - * This class is similar to DList, except that the user defined list element - * can inherit from multple DListEl classes and consequently be an element in - * multiple lists. In other words, DListMel allows a single instance of a data - * structure to be an element in multiple lists without the lists interfereing - * with one another. - * - * For each list that an element class is to appear in, the element must have - * unique next and previous pointers that can be unambiguously refered to with - * some base class name. This name is given to DListMel as a template argument - * so it can use the correct next and previous pointers in its list - * operations. - * - * DListMel does not assume ownership of elements in the list. If the elements - * are known to reside on the heap and are not contained in any other list or - * data structure, the provided empty() routine can be used to delete all - * elements, however the destructor will not call this routine, it will simply - * abandon all the elements. It is up to the programmer to explicitly - * de-allocate items when it is safe to do so. - * - * \include ex_dlistmel.cpp - */ - -/*@}*/ - -#define BASE_EL(name) BaseEl::name -#define DLMEL_TEMPDEF class Element, class BaseEl -#define DLMEL_TEMPUSE Element, BaseEl -#define DList DListMel - -#include "dlcommon.h" - -#undef BASE_EL -#undef DLMEL_TEMPDEF -#undef DLMEL_TEMPUSE -#undef DList - -#endif /* _AAPL_DLISTMEL_H */ - diff --git a/aapl/dlistval.h b/aapl/dlistval.h deleted file mode 100644 index efdb1cc1..00000000 --- a/aapl/dlistval.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_DLISTVAL_H -#define _AAPL_DLISTVAL_H - -/** - * \addtogroup dlist - * @{ - */ - -/** - * \class DListVal - * \brief By-value doubly linked list. - * - * This class is a doubly linked list that does not require a list element - * type to be declared. The user instead gives a type that is to be stored in - * the list element. When inserting a new data item, the value is copied into - * a newly allocated element. This list is inteded to behave and be utilized - * like the list template found in the STL. - * - * DListVal is different from the other lists in that it allocates elements - * itself. The raw element insert interface is still exposed for convenience, - * however, the list assumes all elements in the list are allocated on the - * heap and are to be managed by the list. The destructor WILL delete the - * contents of the list. If the list is ever copied in from another list, the - * existing contents are deleted first. This is in contrast to DList and - * DListMel, which will never delete their contents to allow for statically - * allocated elements. - * - * \include ex_dlistval.cpp - */ - -/*@}*/ - -#define BASE_EL(name) name -#define DLMEL_TEMPDEF class T -#define DLMEL_TEMPUSE T -#define DList DListVal -#define Element DListValEl -#define DOUBLELIST_VALUE - -#include "dlcommon.h" - -#undef BASE_EL -#undef DLMEL_TEMPDEF -#undef DLMEL_TEMPUSE -#undef DList -#undef Element -#undef DOUBLELIST_VALUE - -#endif /* _AAPL_DLISTVAL_H */ - diff --git a/aapl/insertsort.h b/aapl/insertsort.h deleted file mode 100644 index 386fd9c6..00000000 --- a/aapl/insertsort.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_INSERTSORT_H -#define _AAPL_INSERTSORT_H - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup sort - * @{ - */ - -/** - * \class InsertSort - * \brief Insertion sort an array of data. - * - * InsertSort can be used to sort any array of objects of type T provided a - * compare class is given. InsertSort is in-place. It does not require any - * temporary storage. - * - * Objects are not made aware that they are being moved around in memory. - * Assignment operators, constructors and destructors are never invoked by the - * sort. - * - * InsertSort runs in O(n^2) time. It is most useful when sorting small arrays. - * where it can outperform the O(n*log(n)) sorters due to its simplicity. - * InsertSort is a not a stable sort. Elements with the same key will not have - * their relative ordering preserved. - */ - -/*@}*/ - -/* InsertSort. */ -template class InsertSort - : public Compare -{ -public: - /* Sorting interface routine. */ - void sort(T *data, long len); -}; - - -/** - * \brief Insertion sort an array of data. - */ -template - void InsertSort::sort(T *data, long len) -{ - /* For each next largest spot in the sorted array... */ - for ( T *dest = data; dest < data+len-1; dest++ ) { - /* Find the next smallest element in the unsorted array. */ - T *smallest = dest; - for ( T *src = dest+1; src < data+len; src++ ) { - /* If src is smaller than the current src, then use it. */ - if ( this->compare( *src, *smallest ) < 0 ) - smallest = src; - } - - if ( smallest != dest ) { - /* Swap dest, smallest. */ - char tmp[sizeof(T)]; - memcpy( tmp, dest, sizeof(T) ); - memcpy( dest, smallest, sizeof(T) ); - memcpy( smallest, tmp, sizeof(T) ); - } - } -} - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_INSERTSORT_H */ diff --git a/aapl/mergesort.h b/aapl/mergesort.h deleted file mode 100644 index 83f8b67b..00000000 --- a/aapl/mergesort.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2001, 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_MERGESORT_H -#define _AAPL_MERGESORT_H - -#include "bubblesort.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup sort - * @{ - */ - -/** - * \class MergeSort - * \brief Merge sort an array of data. - * - * MergeSort can be used to sort any array of objects of type T provided a - * compare class is given. MergeSort is not in-place, it requires temporary - * storage equal to the size of the array. The temporary storage is allocated - * on the heap. - * - * Objects are not made aware that they are being moved around in memory. - * Assignment operators, constructors and destructors are never invoked by the - * sort. - * - * MergeSort runs in worst case O(n*log(n)) time. In most cases it is slower - * than QuickSort because more copying is neccessary. But on the other hand, - * it is a stable sort, meaning that objects with the same key have their - * relative ordering preserved. Also, its worst case is better. MergeSort - * switches to a BubbleSort when the size of the array being sorted is small. - * This happens when directly sorting a small array or when MergeSort calls - * itself recursively on a small portion of a larger array. - */ - -/*@}*/ - - -/* MergeSort. */ -template class MergeSort - : public BubbleSort -{ -public: - /* Sorting interface routine. */ - void sort(T *data, long len); - -private: - /* Recursive worker. */ - void doSort(T *tmpStor, T *data, long len); -}; - -#define _MS_BUBBLE_THRESH 16 - -/* Recursive mergesort worker. Split data, make recursive calls, merge - * results. */ -template< class T, class Compare> void MergeSort:: - doSort(T *tmpStor, T *data, long len) -{ - if ( len <= 1 ) - return; - - if ( len <= _MS_BUBBLE_THRESH ) { - BubbleSort::sort( data, len ); - return; - } - - long mid = len / 2; - - doSort( tmpStor, data, mid ); - doSort( tmpStor + mid, data + mid, len - mid ); - - /* Merge the data. */ - T *endLower = data + mid, *lower = data; - T *endUpper = data + len, *upper = data + mid; - T *dest = tmpStor; - while ( true ) { - if ( lower == endLower ) { - /* Possibly upper left. */ - if ( upper != endUpper ) - memcpy( dest, upper, (endUpper - upper) * sizeof(T) ); - break; - } - else if ( upper == endUpper ) { - /* Only lower left. */ - if ( lower != endLower ) - memcpy( dest, lower, (endLower - lower) * sizeof(T) ); - break; - } - else { - /* Both upper and lower left. */ - if ( this->compare(*lower, *upper) <= 0 ) - memcpy( dest++, lower++, sizeof(T) ); - else - memcpy( dest++, upper++, sizeof(T) ); - } - } - - /* Copy back from the tmpStor array. */ - memcpy( data, tmpStor, sizeof( T ) * len ); -} - -/** - * \brief Merge sort an array of data. - */ -template< class T, class Compare> - void MergeSort::sort(T *data, long len) -{ - /* Allocate the tmp space needed by merge sort, sort and free. */ - T *tmpStor = (T*) new char[sizeof(T) * len]; - doSort( tmpStor, data, len ); - delete[] (char*) tmpStor; -} - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_MERGESORT_H */ diff --git a/aapl/quicksort.h b/aapl/quicksort.h deleted file mode 100644 index f23ec2ee..00000000 --- a/aapl/quicksort.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_QUICKSORT_H -#define _AAPL_QUICKSORT_H - -#include "insertsort.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup sort - * @{ - */ - -/** - * \class QuickSort - * \brief Quick sort an array of data. - * - * QuickSort can be used to sort any array of objects of type T provided a - * compare class is given. QuickSort is in-place. It does not require any - * temporary storage. - * - * Objects are not made aware that they are being moved around in memory. - * Assignment operators, constructors and destructors are never invoked by the - * sort. - * - * QuickSort runs in O(n*log(n)) time in the average case. It is faster than - * mergsort in the average case because it does less moving of data. The - * performance of quicksort depends mostly on the choice of pivot. This - * implementation picks the pivot as the median of first, middle, last. This - * choice of pivot avoids the O(n^2) worst case for input already sorted, but - * it is still possible to encounter the O(n^2) worst case. For example an - * array of identical elements will run in O(n^2) - * - * QuickSort is not a stable sort. Elements with the same key will not have - * their relative ordering preserved. QuickSort switches to an InsertSort - * when the size of the array being sorted is small. This happens when - * directly sorting a small array or when QuickSort calls iteself recursively - * on a small portion of a larger array. - */ - -/*@}*/ - -/* QuickSort. */ -template class QuickSort : - public InsertSort -{ -public: - /* Sorting interface routine. */ - void sort(T *data, long len); - -private: - /* Recursive worker. */ - void doSort(T *start, T *end); - T *partition(T *start, T *end); - inline T *median(T *start, T *end); -}; - -#define _QS_INSERTION_THRESH 16 - -/* Finds the median of start, middle, end. */ -template T *QuickSort:: - median(T *start, T *end) -{ - T *pivot, *mid = start + (end-start)/2; - - /* CChoose the pivot. */ - if ( this->compare(*start, *mid) < 0 ) { - if ( this->compare(*mid, *end) < 0 ) - pivot = mid; - else if ( this->compare(*start, *end) < 0 ) - pivot = end; - else - pivot = start; - } - else if ( this->compare(*start, *end) < 0 ) - pivot = start; - else if ( this->compare(*mid, *end) < 0 ) - pivot = end; - else - pivot = mid; - - return pivot; -} - -template T *QuickSort:: - partition(T *start, T *end) -{ - /* Use the median of start, middle, end as the pivot. First save - * it off then move the last element to the free spot. */ - char pcPivot[sizeof(T)]; - T *pivot = median(start, end); - - memcpy( pcPivot, pivot, sizeof(T) ); - if ( pivot != end ) - memcpy( pivot, end, sizeof(T) ); - - T *first = start-1; - T *last = end; - pivot = (T*) pcPivot; - - /* Shuffle element to the correct side of the pivot, ending - * up with the free spot where the pivot will go. */ - while ( true ) { - /* Throw one element ahead to the free spot at last. */ - while ( true ) { - first += 1; - if ( first == last ) - goto done; - if ( this->compare( *first, *pivot ) > 0 ) { - memcpy(last, first, sizeof(T)); - break; - } - } - - /* Throw one element back to the free spot at first. */ - while ( true ) { - last -= 1; - if ( last == first ) - goto done; - if ( this->compare( *last, *pivot ) < 0 ) { - memcpy(first, last, sizeof(T)); - break; - } - } - } -done: - /* Put the pivot into the middle spot for it. */ - memcpy( first, pivot, sizeof(T) ); - return first; -} - - -template< class T, class Compare> void QuickSort:: - doSort(T *start, T *end) -{ - long len = end - start + 1; - if ( len > _QS_INSERTION_THRESH ) { - /* Use quicksort. */ - T *pivot = partition( start, end ); - doSort(start, pivot-1); - doSort(pivot+1, end); - } - else if ( len > 1 ) { - /* Array is small, use insertion sort. */ - InsertSort::sort( start, len ); - } -} - -/** - * \brief Quick sort an array of data. - */ -template< class T, class Compare> - void QuickSort::sort(T *data, long len) -{ - /* Call recursive worker. */ - doSort(data, data+len-1); -} - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_QUICKSORT_H */ diff --git a/aapl/resize.h b/aapl/resize.h deleted file mode 100644 index 6cc1090f..00000000 --- a/aapl/resize.h +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_RESIZE_H -#define _AAPL_RESIZE_H - -#include - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/* This step is expressed in units of T. Changing this requires changes to - * docs in ResizeLin constructor. */ -#define LIN_DEFAULT_STEP 256 - -/* - * Resizing macros giving different resize methods. - */ - -/* If needed is greater than existing, give twice needed. */ -#define EXPN_UP( existing, needed ) \ - needed > existing ? (needed<<1) : existing - -/* If needed is less than 1 quarter existing, give twice needed. */ -#define EXPN_DOWN( existing, needed ) \ - needed < (existing>>2) ? (needed<<1) : existing - -/* If needed is greater than existing, give needed plus step. */ -#define LIN_UP( existing, needed ) \ - needed > existing ? (needed+step) : existing - -/* If needed is less than existing - 2 * step then give needed plus step. */ -#define LIN_DOWN( existing, needed ) \ - needed < (existing-(step<<1)) ? (needed+step) : existing - -/* Return existing. */ -#define CONST_UP( existing, needed ) existing - -/* Return existing. */ -#define CONST_DOWN( existing, needed ) existing - -/** - * \addtogroup vector - * @{ - */ - -/** \class ResizeLin - * \brief Linear table resizer. - * - * When an up resize or a down resize is needed, ResizeLin allocates the space - * needed plus some user defined step. The result is that when growing the - * vector in a linear fashion, the number of resizes is also linear. - * - * If only up resizing is done, then there will never be more than step unused - * spaces in the vector. If down resizing is done as well, there will never be - * more than 2*step unused spaces in the vector. The up resizing and down - * resizing policies are offset to improve performance when repeatedly - * inserting and removing a small number of elements relative to the step. - * This scheme guarantees that repetitive inserting and removing of a small - * number of elements will never result in repetative reallocation. - * - * The vectors pass sizes to the resizer in units of T, so the step gets - * interpreted as units of T. - */ - -/*@}*/ - -/* Linear resizing. */ -class ResizeLin -{ -protected: - /** - * \brief Default constructor. - * - * Intializes resize step to 256 units of the table type T. - */ - ResizeLin() : step(LIN_DEFAULT_STEP) { } - - /** - * \brief Determine the new table size when up resizing. - * - * If the existing size is insufficient for the space needed, then allocate - * the space needed plus the step. The step is in units of T. - */ - inline long upResize( long existing, long needed ) - { return LIN_UP(existing, needed); } - - /** - * \brief Determine the new table size when down resizing. - * - * If space needed is less than the existing - 2*step, then allocate the - * space needed space plus the step. The step is in units of T. - */ - inline long downResize( long existing, long needed ) - { return LIN_DOWN(existing, needed); } - -public: - /** - * \brief Step for linear resize. - * - * Amount of extra space in units of T added each time a resize must take - * place. This may be changed at any time. The step should be >= 0. - */ - long step; -}; - -/** - * \addtogroup vector - * @{ - */ - -/** \class ResizeCtLin - * \brief Linear table resizer with compile time step. - * - * When an up resize or a down resize is needed, ResizeCtLin allocates the - * space needed plus some compile time defined step. The result is that when - * growing the vector in a linear fashion, the number of resizes is also - * linear. - * - * If only up resizing is done, then there will never be more than step unused - * spaces in the vector. If down resizing is done as well, there will never be - * more than 2*step unused spaces in the vector. The up resizing and down - * resizing policies are offset to improve performance when repeatedly - * inserting and removing a small number of elements relative to the step. - * This scheme guarantees that repetitive inserting and removing of a small - * number of elements will never result in repetative reallocation. - * - * The vectors pass sizes to the resizer in units of T, so the step gets - * interpreted as units of T. - */ - -/*@}*/ - -/* Linear resizing. */ -template class ResizeCtLin -{ -protected: - /** - * \brief Determine the new table size when up resizing. - * - * If the existing size is insufficient for the space needed, then allocate - * the space needed plus the step. The step is in units of T. - */ - inline long upResize( long existing, long needed ) - { return LIN_UP(existing, needed); } - - /** - * \brief Determine the new table size when down resizing. - * - * If space needed is less than the existing - 2*step, then allocate the - * space needed space plus the step. The step is in units of T. - */ - inline long downResize( long existing, long needed ) - { return LIN_DOWN(existing, needed); } -}; - -/** - * \addtogroup vector - * @{ - */ - -/** \class ResizeConst - * \brief Constant table resizer. - * - * When an up resize is needed the existing size is always used. ResizeConst - * does not allow dynamic resizing. To use ResizeConst, the vector needs to be - * constructed with and initial allocation amount otherwise it will be - * unusable. - */ - -/*@}*/ - -/* Constant table resizing. */ -class ResizeConst -{ -protected: - /* Assert don't need more than exists. Return existing. */ - static inline long upResize( long existing, long needed ); - - /** - * \brief Determine the new table size when down resizing. - * - * Always returns the existing table size. - */ - static inline long downResize( long existing, long needed ) - { return CONST_DOWN(existing, needed); } -}; - -/** - * \brief Determine the new table size when up resizing. - * - * If the existing size is insufficient for the space needed, then an assertion - * will fail. Otherwise returns the existing size. - */ -inline long ResizeConst::upResize( long existing, long needed ) -{ - assert( needed <= existing ); - return CONST_UP(existing, needed); -} - -/** - * \addtogroup vector - * @{ - */ - -/** \class ResizeRunTime - * \brief Run time settable table resizer. - * - * ResizeRunTime can have it's up and down resizing policies set at run time. - * Both up and down policies can be set independently to one of Exponential, - * Linear, or Constant. See the documentation for ResizeExpn, ResizeLin, and - * ResizeConst for the details of the resizing policies. - * - * The policies may be changed at any time. The default policies are - * both Exponential. - */ - -/*@}*/ - -/* Run time resizing. */ -class ResizeRunTime -{ -protected: - /** - * \brief Default constuctor. - * - * The up and down resizing it initialized to Exponetial. The step - * defaults to 256 units of T. - */ - inline ResizeRunTime(); - - /** - * \brief Resizing policies. - */ - enum ResizeType { - Exponential, /*!< Exponential resizing. */ - Linear, /*!< Linear resizing. */ - Constant /*!< Constant table size. */ - }; - - inline long upResize( long existing, long needed ); - inline long downResize( long existing, long needed ); - -public: - /** - * \brief Step for linear resize. - * - * Amount of extra space in units of T added each time a resize must take - * place. This may be changed at any time. The step should be >= 0. - */ - long step; - - /** - * \brief Up resizing policy. - */ - ResizeType upResizeType; - - /** - * \brief Down resizing policy. - */ - ResizeType downResizeType; -}; - -inline ResizeRunTime::ResizeRunTime() -: - step( LIN_DEFAULT_STEP ), - upResizeType( Exponential ), - downResizeType( Exponential ) -{ -} - -/** - * \brief Determine the new table size when up resizing. - * - * Type of up resizing is determined by upResizeType. Exponential, Linear and - * Constant resizing is the same as that of ResizeExpn, ResizeLin and - * ResizeConst. - */ -inline long ResizeRunTime::upResize( long existing, long needed ) -{ - switch ( upResizeType ) { - case Exponential: - return EXPN_UP(existing, needed); - case Linear: - return LIN_UP(existing, needed); - case Constant: - assert( needed <= existing ); - return CONST_UP(existing, needed); - } - return 0; -}; - -/** - * \brief Determine the new table size when down resizing. - * - * Type of down resizing is determined by downResiizeType. Exponential, Linear - * and Constant resizing is the same as that of ResizeExpn, ResizeLin and - * ResizeConst. - */ -inline long ResizeRunTime::downResize( long existing, long needed ) -{ - switch ( downResizeType ) { - case Exponential: - return EXPN_DOWN(existing, needed); - case Linear: - return LIN_DOWN(existing, needed); - case Constant: - return CONST_DOWN(existing, needed); - } - return 0; -} - -/* Don't need these anymore. */ -#undef EXPN_UP -#undef EXPN_DOWN -#undef LIN_UP -#undef LIN_DOWN -#undef CONST_UP -#undef CONST_DOWN - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_RESIZE_H */ diff --git a/aapl/rope.h b/aapl/rope.h deleted file mode 100644 index 7a5e7b58..00000000 --- a/aapl/rope.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2016 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_ROPE_H -#define _AAPL_ROPE_H - -#include -#include -#include - -struct RopeBlock -{ - static const int BLOCK_SZ = 8192; - - RopeBlock() - : - size(BLOCK_SZ), - hoff(0), - toff(0) - { - } - - int size; - int hoff; - int toff; - RopeBlock *next; -}; - -struct Rope -{ - Rope() - : - hblk(0), - tblk(0), - ropeLen(0) - { - } - - /* Write to the tail block, at tail offset. */ - RopeBlock *hblk; - RopeBlock *tblk; - - /* Number of bytes in rope. */ - int ropeLen; - - RopeBlock *allocateBlock( int supporting ) - { - int size = ( supporting > RopeBlock::BLOCK_SZ ) ? supporting : RopeBlock::BLOCK_SZ; - char *bd = new char[sizeof(RopeBlock) + size]; - RopeBlock *block = (RopeBlock*) bd; - block->size = size; - block->hoff = 0; - block->toff = 0; - block->next = 0; - return block; - } - - char *data( RopeBlock *rb ) - { return (char*)rb + sizeof( RopeBlock ) + rb->hoff; } - - char *writeTo( RopeBlock *rb ) - { return (char*)rb + sizeof( RopeBlock ) + rb->toff; } - - int length( RopeBlock *rb ) - { return rb->toff - rb->hoff; } - - int length() - { return ropeLen; } - - int available( RopeBlock *rb ) - { return rb->size - rb->toff; } - - char *append( const char *src, int len ) - { - if ( tblk == 0 ) { - /* There are no blocks. */ - hblk = tblk = allocateBlock( len ); - } - else { - int avail = available( tblk ); - - /* Move to the next block? */ - if ( len > avail ) { - RopeBlock *block = allocateBlock( len ); - tblk->next = block; - tblk = block; - } - } - - char *ret = writeTo(tblk); - tblk->toff += len; - ropeLen += len; - - if ( src != 0 ) - memcpy( ret, src, len ); - return ret; - } - - char *appendBlock( int len ) - { - if ( tblk == 0 ) { - /* There are no blocks. */ - hblk = tblk = allocateBlock( len ); - } - else { - RopeBlock *block = allocateBlock( len ); - tblk->next = block; - tblk = block; - } - - char *ret = writeTo(tblk); - tblk->toff += len; - ropeLen += len; - return ret; - } - - /* Transfer data from other. Leaves it empty. */ - void append( Rope &other ) - { - if ( hblk == 0 ) { - transfer( other ); - } - else if ( other.hblk == 0 ) { - /* nothing to do, other list empty. */ - } - else { - tblk->next = other.hblk; - tblk = other.tblk; - ropeLen += other.ropeLen; - } - - other.abandon(); - } - - void empty() - { - RopeBlock *blk = hblk; - while ( blk != 0 ) { - RopeBlock *next = blk->next; - delete[] (char*)blk; - blk = next; - } - - hblk = 0; - tblk = 0; - ropeLen = 0; - } - - void abandon() - { - hblk = 0; - tblk = 0; - ropeLen = 0; - } - - void transfer( Rope &from ) - { - empty(); - - this->hblk = from.hblk; - this->tblk = from.tblk; - this->ropeLen = from.ropeLen; - - from.hblk = from.tblk = 0; - from.ropeLen = 0; - } -}; - - -/* - * StringStream for appending to streams with an ostream. - */ -struct RopeOutBuf -: - public std::streambuf -{ - RopeOutBuf( Rope &r ) - : - r(r) - { - } - - int_type overflow( int_type c ) - { - if ( c != EOF ) { - char z = c; - r.append( &z, 1 ); - } - return c; - } - - std::streamsize xsputn( const char *data, std::streamsize num ) - { - r.append( data, num ); - return num; - } - - Rope &r; -}; - -struct RopeStream -: - public std::ostream -{ - RopeStream( Rope &r ) - : - std::ostream( 0 ), - buf( r ) - { - rdbuf( &buf ); - } - - RopeOutBuf buf; -}; - - -#endif - diff --git a/aapl/sbstmap.h b/aapl/sbstmap.h deleted file mode 100644 index 3e159ab2..00000000 --- a/aapl/sbstmap.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_SBSTMAP_H -#define _AAPL_SBSTMAP_H - -#include "compare.h" -#include "svector.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \brief Element for BstMap. - * - * Stores the key and value pair. - */ -template struct SBstMapEl -{ - SBstMapEl() {} - SBstMapEl(const Key &key) : key(key) {} - SBstMapEl(const Key &key, const Value &val) : key(key), value(val) {} - - /** \brief The key */ - Key key; - - /** \brief The value. */ - Value value; -}; - -#ifdef AAPL_NAMESPACE -} -#endif - -/** - * \addtogroup bst - * @{ - */ - -/** - * \class SBstMap - * \brief Copy-on-write binary search table for key and value pairs. - * - * This is a map style binary search table that employs the copy-on-write - * mechanism for table data. BstMap stores key and value pairs in each - * element. The key and value can be any type. A compare class for the key - * must be supplied. - */ - -/*@}*/ - -#define BST_TEMPL_DECLARE class Key, class Value, \ - class Compare = CmpOrd, class Resize = ResizeExpn -#define BST_TEMPL_DEF class Key, class Value, class Compare, class Resize -#define BST_TEMPL_USE Key, Value, Compare, Resize -#define GET_KEY(el) ((el).key) -#define BstTable SBstMap -#define Vector SVector -#define Table STable -#define Element SBstMapEl -#define BSTMAP -#define SHARED_BST - -#include "bstcommon.h" - -#undef BST_TEMPL_DECLARE -#undef BST_TEMPL_DEF -#undef BST_TEMPL_USE -#undef GET_KEY -#undef BstTable -#undef Vector -#undef Table -#undef Element -#undef BSTMAP -#undef SHARED_BST - -/** - * \fn SBstMap::insert(const Key &key, BstMapEl **lastFound) - * \brief Insert the given key. - * - * If the given key does not already exist in the table then a new element - * having key is inserted. They key copy constructor and value default - * constructor are used to place the pair in the table. If lastFound is given, - * it is set to the new entry created. If the insert fails then lastFound is - * set to the existing pair of the same key. - * - * \returns The new element created upon success, null upon failure. - */ - -/** - * \fn SBstMap::insertMulti(const Key &key) - * \brief Insert the given key even if it exists already. - * - * If the key exists already then the new element having key is placed next - * to some other pair of the same key. InsertMulti cannot fail. The key copy - * constructor and the value default constructor are used to place the pair in - * the table. - * - * \returns The new element created. - */ - -#endif /* _AAPL_SBSTMAP_H */ diff --git a/aapl/sbstset.h b/aapl/sbstset.h deleted file mode 100644 index 947f78dc..00000000 --- a/aapl/sbstset.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_SBSTSET_H -#define _AAPL_SBSTSET_H - -/** - * \addtogroup bst - * @{ - */ - -/** - * \class SBstSet - * \brief Copy-on-write binary search table for types that are the key. - * - * This is a set style binary search table that employs the copy-on-write - * mechanism for storing table data. BstSet is suitable for types that - * comprise the entire key. Rather than look into the element to retrieve the - * key, the element is the key. A class that contains a comparison routine - * for the key must be given. - */ - -/*@}*/ - -#include "compare.h" -#include "svector.h" - -#define BST_TEMPL_DECLARE class Key, class Compare = CmpOrd, \ - class Resize = ResizeExpn -#define BST_TEMPL_DEF class Key, class Compare, class Resize -#define BST_TEMPL_USE Key, Compare, Resize -#define GET_KEY(el) (el) -#define BstTable SBstSet -#define Vector SVector -#define Table STable -#define Element Key -#define BSTSET -#define SHARED_BST - -#include "bstcommon.h" - -#undef BST_TEMPL_DECLARE -#undef BST_TEMPL_DEF -#undef BST_TEMPL_USE -#undef GET_KEY -#undef BstTable -#undef Vector -#undef Table -#undef Element -#undef BSTSET -#undef SHARED_BST - -/** - * \fn SBstSet::insert(const Key &key, Key **lastFound) - * \brief Insert the given key. - * - * If the given key does not already exist in the table then it is inserted. - * The key's copy constructor is used to place the item in the table. If - * lastFound is given, it is set to the new entry created. If the insert fails - * then lastFound is set to the existing key of the same value. - * - * \returns The new element created upon success, null upon failure. - */ - -/** - * \fn SBstSet::insertMulti(const Key &key) - * \brief Insert the given key even if it exists already. - * - * If the key exists already then it is placed next to some other key of the - * same value. InsertMulti cannot fail. The key's copy constructor is used to - * place the item in the table. - * - * \returns The new element created. - */ - -#endif /* _AAPL_SBSTSET_H */ diff --git a/aapl/sbsttable.h b/aapl/sbsttable.h deleted file mode 100644 index 9cfed437..00000000 --- a/aapl/sbsttable.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_SBSTTABLE_H -#define _AAPL_SBSTTABLE_H - -#include "compare.h" -#include "svector.h" - -/** - * \addtogroup bst - * @{ - */ - -/** - * \class SBstTable - * \brief Copy-on-write binary search table for structures that contain a key. - * - * This is a basic binary search table that employs a copy-on-write data - * storage mechanism. It can be used to contain a structure that has a key and - * possibly some data. The key should be a member of the element class and - * accessible with getKey(). A class containing the compare routine must be - * supplied. - */ - -/*@}*/ - -#define BST_TEMPL_DECLARE class Element, class Key, \ - class Compare = CmpOrd, class Resize = ResizeExpn -#define BST_TEMPL_DEF class Element, class Key, class Compare, class Resize -#define BST_TEMPL_USE Element, Key, Compare, Resize -#define GET_KEY(el) ((el).getKey()) -#define BstTable SBstTable -#define Vector SVector -#define Table STable -#define BSTTABLE -#define SHARED_BST - -#include "bstcommon.h" - -#undef BST_TEMPL_DECLARE -#undef BST_TEMPL_DEF -#undef BST_TEMPL_USE -#undef GET_KEY -#undef BstTable -#undef Vector -#undef Table -#undef BSTTABLE -#undef SHARED_BST - -/** - * \fn SBstTable::insert(const Key &key, Element **lastFound) - * \brief Insert a new element with the given key. - * - * If the given key does not already exist in the table a new element is - * inserted with the given key. A constructor taking only const Key& is used - * to initialize the new element. If lastFound is given, it is set to the new - * element created. If the insert fails then lastFound is set to the existing - * element with the same key. - * - * \returns The new element created upon success, null upon failure. - */ - -/** - * \fn SBstTable::insertMulti(const Key &key) - * \brief Insert a new element even if the key exists already. - * - * If the key exists already then the new element is placed next to some - * element with the same key. InsertMulti cannot fail. A constructor taking - * only const Key& is used to initialize the new element. - * - * \returns The new element created. - */ - -#endif /* _AAPL_SBSTTABLE_H */ diff --git a/aapl/svector.h b/aapl/svector.h deleted file mode 100644 index 54db2007..00000000 --- a/aapl/svector.h +++ /dev/null @@ -1,1351 +0,0 @@ -/* - * Copyright 2002, 2006 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_SVECTOR_H -#define _AAPL_SVECTOR_H - -#include -#include -#include -#include -#include "table.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup vector - * @{ - */ - -/** \class SVector - * \brief Copy-on-write dynamic array. - * - * SVector is a variant of Vector that employs copy-on-write behaviour. The - * SVector copy constructor and = operator make shallow copies. If a vector - * that references shared data is modified with insert, replace, append, - * prepend, setAs or remove, a new copy is made so as not to interfere with - * the shared data. However, shared individual elements may be modified by - * bypassing the SVector interface. - * - * SVector is a dynamic array that can be used to contain complex data - * structures that have constructors and destructors as well as simple types - * such as integers and pointers. - * - * SVector supports inserting, overwriting, and removing single or multiple - * elements at once. Constructors and destructors are called wherever - * appropriate. For example, before an element is overwritten, it's - * destructor is called. - * - * SVector provides automatic resizing of allocated memory as needed and - * offers different allocation schemes for controlling how the automatic - * allocation is done. Two senses of the the length of the data is - * maintained: the amount of raw memory allocated to the vector and the number - * of actual elements in the vector. The various allocation schemes control - * how the allocated space is changed in relation to the number of elements in - * the vector. - */ - -/*@}*/ - -/* SVector */ -template < class T, class Resize = ResizeExpn > class SVector : - public STable, public Resize -{ -private: - typedef STable BaseTable; - -public: - /** - * \brief Initialize an empty vector with no space allocated. - * - * If a linear resizer is used, the step defaults to 256 units of T. For a - * runtime vector both up and down allocation schemes default to - * Exponential. - */ - SVector() { } - - /** - * \brief Create a vector that contains an initial element. - * - * The vector becomes one element in length. The element's copy - * constructor is used to place the value in the vector. - */ - SVector(const T &val) { setAs(&val, 1); } - - /** - * \brief Create a vector that contains an array of elements. - * - * The vector becomes len elements in length. Copy constructors are used - * to place the new elements in the vector. - */ - SVector(const T *val, long len) { setAs(val, len); } - - /* Shallow copy. */ - SVector( const SVector &v ); - - /** - * \brief Free all memory used by the vector. - * - * The vector is reset to zero elements. Destructors are called on all - * elements in the vector. The space allocated for the vector is freed. - */ - ~SVector() { empty(); } - - /* Delete all items. */ - void empty(); - - /** - * \brief Deep copy another vector into this vector. - * - * Copies the entire contents of the other vector into this vector. Any - * existing contents are first deleted. Equivalent to setAs. - */ - void deepCopy( const SVector &v ) { setAs(v.data, v.length()); } - - /* Perform a shallow copy of another vector. */ - SVector &operator=( const SVector &v ); - - - /*@{*/ - /** - * \brief Insert one element at position pos. - * - * Elements in the vector from pos onward are shifted one space to the - * right. The copy constructor is used to place the element into this - * vector. If pos is greater than the length of the vector then undefined - * behaviour results. If pos is negative then it is treated as an offset - * relative to the length of the vector. - */ - void insert(long pos, const T &val) { insert(pos, &val, 1); } - - /* Insert an array of values. */ - void insert(long pos, const T *val, long len); - - /** - * \brief Insert all the elements from another vector at position pos. - * - * Elements in this vector from pos onward are shifted v.length() spaces - * to the right. The element's copy constructor is used to copy the items - * into this vector. The other vector is left unchanged. If pos is off the - * end of the vector, then undefined behaviour results. If pos is negative - * then it is treated as an offset relative to the length of the vector. - * Equivalent to vector.insert(pos, other.data, other.length()). - */ - void insert(long pos, const SVector &v) { insert(pos, v.data, v.length()); } - - /* Insert len copies of val into the vector. */ - void insertDup(long pos, const T &val, long len); - - /** - * \brief Insert one new element using the default constrcutor. - * - * Elements in the vector from pos onward are shifted one space to the right. - * The default constructor is used to init the new element. If pos is greater - * than the length of the vector then undefined behaviour results. If pos is - * negative then it is treated as an offset relative to the length of the - * vector. - */ - void insertNew(long pos) { insertNew(pos, 1); } - - /* Insert len new items using default constructor. */ - void insertNew(long pos, long len); - /*@}*/ - - /*@{*/ - /** - * \brief Remove one element at position pos. - * - * The element's destructor is called. Elements to the right of pos are - * shifted one space to the left to take up the free space. If pos is greater - * than or equal to the length of the vector then undefined behavior results. - * If pos is negative then it is treated as an offset relative to the length - * of the vector. - */ - void remove(long pos) { remove(pos, 1); } - - /* Delete a number of elements. */ - void remove(long pos, long len); - /*@}*/ - - /*@{*/ - /** - * \brief Replace one element at position pos. - * - * If there is an existing element at position pos (if pos is less than the - * length of the vector) then its destructor is called before the space is - * used. The copy constructor is used to place the element into the vector. - * If pos is greater than the length of the vector then undefined behaviour - * results. If pos is negative then it is treated as an offset relative to - * the length of the vector. - */ - void replace(long pos, const T &val) { replace(pos, &val, 1); } - - /* Replace with an array of values. */ - void replace(long pos, const T *val, long len); - - /** - * \brief Replace at position pos with all the elements of another vector. - * - * Replace at position pos with all the elements of another vector. The other - * vector is left unchanged. If there are existing elements at the positions - * to be replaced, then destructors are called before the space is used. Copy - * constructors are used to place the elements into this vector. It is - * allowable for the pos and length of the other vector to specify a - * replacement that overwrites existing elements and creates new ones. If pos - * is greater than the length of the vector then undefined behaviour results. - * If pos is negative, then it is treated as an offset relative to the length - * of the vector. - */ - void replace(long pos, const SVector &v) { replace(pos, v.data, v.length()); } - - /* Replace len items with len copies of val. */ - void replaceDup(long pos, const T &val, long len); - - /** - * \brief Replace at position pos with one new element. - * - * If there is an existing element at the position to be replaced (pos is - * less than the length of the vector) then the element's destructor is - * called before the space is used. The default constructor is used to - * initialize the new element. If pos is greater than the length of the - * vector then undefined behaviour results. If pos is negative, then it is - * treated as an offset relative to the length of the vector. - */ - void replaceNew(long pos) { replaceNew(pos, 1); } - - /* Replace len items at pos with newly constructed objects. */ - void replaceNew(long pos, long len); - /*@}*/ - - /*@{*/ - - /** - * \brief Set the contents of the vector to be val exactly. - * - * The vector becomes one element in length. Destructors are called on any - * existing elements in the vector. The element's copy constructor is used to - * place the val in the vector. - */ - void setAs(const T &val) { setAs(&val, 1); } - - /* Set to the contents of an array. */ - void setAs(const T *val, long len); - - /** - * \brief Set the vector to exactly the contents of another vector. - * - * The vector becomes v.length() elements in length. Destructors are called - * on any existing elements. Copy constructors are used to place the new - * elements in the vector. - */ - void setAs(const SVector &v) { setAs(v.data, v.length()); } - - /* Set as len copies of item. */ - void setAsDup(const T &item, long len); - - /** - * \brief Set the vector to exactly one new item. - * - * The vector becomes one element in length. Destructors are called on any - * existing elements in the vector. The default constructor is used to - * init the new item. - */ - void setAsNew() { setAsNew(1); } - - /* Set as newly constructed objects using the default constructor. */ - void setAsNew(long len); - /*@}*/ - - /*@{*/ - /** - * \brief Append one elment to the end of the vector. - * - * Copy constructor is used to place the element in the vector. - */ - void append(const T &val) { replace(BaseTable::length(), &val, 1); } - - /** - * \brief Append len elements to the end of the vector. - * - * Copy constructors are used to place the elements in the vector. - */ - void append(const T *val, long len) { replace(BaseTable::length(), val, len); } - - /** - * \brief Append the contents of another vector. - * - * The other vector is left unchanged. Copy constructors are used to place - * the elements in the vector. - */ - void append(const SVector &v) - { replace(BaseTable::length(), v.data, v.length()); } - - /** - * \brief Append len copies of item. - * - * The copy constructor is used to place the item in the vector. - */ - void appendDup(const T &item, long len) { replaceDup(BaseTable::length(), item, len); } - - /** - * \brief Append a single newly created item. - * - * The new element is initialized with the default constructor. - */ - void appendNew() { replaceNew(BaseTable::length(), 1); } - - /** - * \brief Append len newly created items. - * - * The new elements are initialized with the default constructor. - */ - void appendNew(long len) { replaceNew(BaseTable::length(), len); } - /*@}*/ - - - /*@{*/ - /** - * \brief Prepend one elment to the front of the vector. - * - * Copy constructor is used to place the element in the vector. - */ - void prepend(const T &val) { insert(0, &val, 1); } - - /** - * \brief Prepend len elements to the front of the vector. - * - * Copy constructors are used to place the elements in the vector. - */ - void prepend(const T *val, long len) { insert(0, val, len); } - - /** - * \brief Prepend the contents of another vector. - * - * The other vector is left unchanged. Copy constructors are used to place - * the elements in the vector. - */ - void prepend(const SVector &v) { insert(0, v.data, v.length()); } - - /** - * \brief Prepend len copies of item. - * - * The copy constructor is used to place the item in the vector. - */ - void prependDup(const T &item, long len) { insertDup(0, item, len); } - - /** - * \brief Prepend a single newly created item. - * - * The new element is initialized with the default constructor. - */ - void prependNew() { insertNew(0, 1); } - - /** - * \brief Prepend len newly created items. - * - * The new elements are initialized with the default constructor. - */ - void prependNew(long len) { insertNew(0, len); } - /*@}*/ - - /* Convenience access. */ - T &operator[](int i) const { return BaseTable::data[i]; } - long size() const { return BaseTable::length(); } - - /* Various classes for setting the iterator */ - struct Iter; - struct IterFirst { IterFirst( const SVector &v ) : v(v) { } const SVector &v; }; - struct IterLast { IterLast( const SVector &v ) : v(v) { } const SVector &v; }; - struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; - struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; - - /** - * \brief Shared Vector Iterator. - * \ingroup iterators - */ - struct Iter - { - /* Construct, assign. */ - Iter() : ptr(0), ptrBeg(0), ptrEnd(0) { } - - /* Construct. */ - Iter( const SVector &v ); - Iter( const IterFirst &vf ); - Iter( const IterLast &vl ); - inline Iter( const IterNext &vn ); - inline Iter( const IterPrev &vp ); - - /* Assign. */ - Iter &operator=( const SVector &v ); - Iter &operator=( const IterFirst &vf ); - Iter &operator=( const IterLast &vl ); - inline Iter &operator=( const IterNext &vf ); - inline Iter &operator=( const IterPrev &vl ); - - /** \brief Less than end? */ - bool lte() const { return ptr != ptrEnd; } - - /** \brief At end? */ - bool end() const { return ptr == ptrEnd; } - - /** \brief Greater than beginning? */ - bool gtb() const { return ptr != ptrBeg; } - - /** \brief At beginning? */ - bool beg() const { return ptr == ptrBeg; } - - /** \brief At first element? */ - bool first() const { return ptr == ptrBeg+1; } - - /** \brief At last element? */ - bool last() const { return ptr == ptrEnd-1; } - - /* Return the position. */ - long pos() const { return ptr - ptrBeg - 1; } - T &operator[](int i) const { return ptr[i]; } - - /** \brief Implicit cast to T*. */ - operator T*() const { return ptr; } - - /** \brief Dereference operator returns T&. */ - T &operator *() const { return *ptr; } - - /** \brief Arrow operator returns T*. */ - T *operator->() const { return ptr; } - - /** \brief Move to next item. */ - T *operator++() { return ++ptr; } - - /** \brief Move to next item. */ - T *operator++(int) { return ptr++; } - - /** \brief Move to next item. */ - T *increment() { return ++ptr; } - - /** \brief Move to previous item. */ - T *operator--() { return --ptr; } - - /** \brief Move to previous item. */ - T *operator--(int) { return ptr--; } - - /** \brief Move to previous item. */ - T *decrement() { return --ptr; } - - /** \brief Return the next item. Does not modify this. */ - inline IterNext next() const { return IterNext(*this); } - - /** \brief Return the previous item. Does not modify this. */ - inline IterPrev prev() const { return IterPrev(*this); } - - /** \brief The iterator is simply a pointer. */ - T *ptr; - - /* For testing endpoints. */ - T *ptrBeg, *ptrEnd; - }; - - /** \brief Return first element. */ - IterFirst first() { return IterFirst( *this ); } - - /** \brief Return last element. */ - IterLast last() { return IterLast( *this ); } - -protected: - void makeRawSpaceFor(long pos, long len); - - void setAsCommon(long len); - long replaceCommon(long pos, long len); - long insertCommon(long pos, long len); - - void upResize(long len); - void upResizeDup(long len); - void upResizeFromEmpty(long len); - void downResize(long len); - void downResizeDup(long len); -}; - -/** - * \brief Perform a shallow copy of the vector. - * - * Takes a reference to the contents of the other vector. - */ -template SVector:: - SVector(const SVector &v) -{ - /* Take a reference to other, if any data is allocated. */ - if ( v.data == 0 ) - BaseTable::data = 0; - else { - /* Get the source header, up the refcount and ref it. */ - STabHead *srcHead = ((STabHead*) v.data) - 1; - srcHead->refCount += 1; - BaseTable::data = (T*) (srcHead + 1); - } -} - -/** - * \brief Shallow copy another vector into this vector. - * - * Takes a reference to the other vector. The contents of this vector are - * first emptied. - * - * \returns A reference to this. - */ -template SVector & - SVector:: operator=( const SVector &v ) -{ - /* First clean out the current contents. */ - empty(); - - /* Take a reference to other, if any data is allocated. */ - if ( v.data == 0 ) - BaseTable::data = 0; - else { - /* Get the source header, up the refcount and ref it. */ - STabHead *srcHead = ((STabHead*) v.data) - 1; - srcHead->refCount += 1; - BaseTable::data = (T*) (srcHead + 1); - } - return *this; -} - -/* Init a vector iterator with just a vector. */ -template SVector:: - Iter::Iter( const SVector &v ) -{ - long length; - if ( v.data == 0 || (length=(((STabHead*)v.data)-1)->tabLen) == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = v.data; - ptrBeg = v.data-1; - ptrEnd = v.data+length; - } -} - -/* Init a vector iterator with the first of a vector. */ -template SVector:: - Iter::Iter( const IterFirst &vf ) -{ - long length; - if ( vf.v.data == 0 || (length=(((STabHead*)vf.v.data)-1)->tabLen) == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vf.v.data; - ptrBeg = vf.v.data-1; - ptrEnd = vf.v.data+length; - } -} - -/* Init a vector iterator with the last of a vector. */ -template SVector:: - Iter::Iter( const IterLast &vl ) -{ - long length; - if ( vl.v.data == 0 || (length=(((STabHead*)vl.v.data)-1)->tabLen) == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vl.v.data+length-1; - ptrBeg = vl.v.data-1; - ptrEnd = vl.v.data+length; - } -} - -/* Init a vector iterator with the next of some other iterator. */ -template SVector:: - Iter::Iter( const IterNext &vn ) -: - ptr(vn.i.ptr+1), - ptrBeg(vn.i.ptrBeg), - ptrEnd(vn.i.ptrEnd) -{ -} - -/* Init a vector iterator with the prev of some other iterator. */ -template SVector:: - Iter::Iter( const IterPrev &vp ) -: - ptr(vp.i.ptr-1), - ptrBeg(vp.i.ptrBeg), - ptrEnd(vp.i.ptrEnd) -{ -} - -/* Set a vector iterator with some vector. */ -template typename SVector::Iter & - SVector::Iter::operator=( const SVector &v ) -{ - long length; - if ( v.data == 0 || (length=(((STabHead*)v.data)-1)->tabLen) == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = v.data; - ptrBeg = v.data-1; - ptrEnd = v.data+length; - } - return *this; -} - -/* Set a vector iterator with the first element in a vector. */ -template typename SVector::Iter & - SVector::Iter::operator=( const IterFirst &vf ) -{ - long length; - if ( vf.v.data == 0 || (length=(((STabHead*)vf.v.data)-1)->tabLen) == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vf.v.data; - ptrBeg = vf.v.data-1; - ptrEnd = vf.v.data+length; - } - return *this; -} - -/* Set a vector iterator with the last element in a vector. */ -template typename SVector::Iter & - SVector::Iter::operator=( const IterLast &vl ) -{ - long length; - if ( vl.v.data == 0 || (length=(((STabHead*)vl.v.data)-1)->tabLen) == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vl.v.data+length-1; - ptrBeg = vl.v.data-1; - ptrEnd = vl.v.data+length; - } - return *this; -} - -/* Set a vector iterator with the next of some other iterator. */ -template typename SVector::Iter & - SVector::Iter::operator=( const IterNext &vn ) -{ - ptr = vn.i.ptr+1; - ptrBeg = vn.i.ptrBeg; - ptrEnd = vn.i.ptrEnd; - return *this; -} - -/* Set a vector iterator with the prev of some other iterator. */ -template typename SVector::Iter & - SVector::Iter::operator=( const IterPrev &vp ) -{ - ptr = vp.i.ptr-1; - ptrBeg = vp.i.ptrBeg; - ptrEnd = vp.i.ptrEnd; - return *this; -} - -/* Up resize the data for len elements using Resize::upResize to tell us the - * new length. Reads and writes allocLen. Does not read or write length. - * Assumes that there is some data allocated already. */ -template void SVector:: - upResize(long len) -{ - /* Get the current header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* Ask the resizer what the new length will be. */ - long newLen = Resize::upResize(head->allocLen, len); - - /* Did the data grow? */ - if ( newLen > head->allocLen ) { - head->allocLen = newLen; - - /* Table exists already, resize it up. */ - head = (STabHead*) realloc( head, sizeof(STabHead) + - sizeof(T) * newLen ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Save the data pointer. */ - BaseTable::data = (T*) (head + 1); - } -} - -/* Allocates a new buffer for an up resize that requires a duplication of the - * data. Uses Resize::upResize to get the allocation length. Reads and writes - * allocLen. This upResize does write the new length. Assumes that there is - * some data allocated already. */ -template void SVector:: - upResizeDup(long len) -{ - /* Get the current header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* Ask the resizer what the new length will be. */ - long newLen = Resize::upResize(head->allocLen, len); - - /* Dereferencing the existing data, decrement the refcount. */ - head->refCount -= 1; - - /* Table exists already, resize it up. */ - head = (STabHead*) malloc( sizeof(STabHead) + sizeof(T) * newLen ); - if ( head == 0 ) - throw std::bad_alloc(); - - head->refCount = 1; - head->allocLen = newLen; - head->tabLen = len; - - /* Save the data pointer. */ - BaseTable::data = (T*) (head + 1); -} - -/* Up resize the data for len elements using Resize::upResize to tell us the - * new length. Reads and writes allocLen. This upresize DOES write length. - * Assumes that no data is allocated. */ -template void SVector:: - upResizeFromEmpty(long len) -{ - /* There is no table yet. If the len is zero, then there is no need to - * create a table. */ - if ( len > 0 ) { - /* Ask the resizer what the new length will be. */ - long newLen = Resize::upResize(0, len); - - /* If len is greater than zero then we are always allocating the table. */ - STabHead *head = (STabHead*) malloc( sizeof(STabHead) + - sizeof(T) * newLen ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Set up the header and save the data pointer. Note that we set the - * length here. This differs from the other upResizes. */ - head->refCount = 1; - head->allocLen = newLen; - head->tabLen = len; - BaseTable::data = (T*) (head + 1); - } -} - -/* Down resize the data for len elements using Resize::downResize to determine - * the new length. Reads and writes allocLen. Does not read or write length. */ -template void SVector:: - downResize(long len) -{ - /* If there is already no length, then there is nothing we can do. */ - if ( BaseTable::data != 0 ) { - /* Get the current header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* Ask the resizer what the new length will be. */ - long newLen = Resize::downResize( head->allocLen, len ); - - /* Did the data shrink? */ - if ( newLen < head->allocLen ) { - if ( newLen == 0 ) { - /* Simply free the data. */ - free( head ); - BaseTable::data = 0; - } - else { - /* Save the new allocated length. */ - head->allocLen = newLen; - - /* Not shrinking to size zero, realloc it to the smaller size. */ - head = (STabHead*) realloc( head, sizeof(STabHead) + - sizeof(T) * newLen ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Save the new data ptr. */ - BaseTable::data = (T*) (head + 1); - } - } - } -} - -/* Allocate a new buffer for a down resize and duplication of the array. The - * new array will be len long and allocation size will be determined using - * Resize::downResize with the old array's allocLen. Does not actually copy - * any data. Reads and writes allocLen and writes the new len. */ -template void SVector:: - downResizeDup(long len) -{ - /* If there is already no length, then there is nothing we can do. */ - if ( BaseTable::data != 0 ) { - /* Get the current header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* Ask the resizer what the new length will be. */ - long newLen = Resize::downResize( head->allocLen, len ); - - /* Detaching from the existing head, decrement the refcount. */ - head->refCount -= 1; - - /* Not shrinking to size zero, malloc it to the smaller size. */ - head = (STabHead*) malloc( sizeof(STabHead) + sizeof(T) * newLen ); - if ( head == 0 ) - throw std::bad_alloc(); - - /* Save the new allocated length. */ - head->refCount = 1; - head->allocLen = newLen; - head->tabLen = len; - - /* Save the data pointer. */ - BaseTable::data = (T*) (head + 1); - } -} - -/** - * \brief Free all memory used by the vector. - * - * The vector is reset to zero elements. Destructors are called on all - * elements in the vector. The space allocated for the vector is freed. - */ -template void SVector:: - empty() -{ - if ( BaseTable::data != 0 ) { - /* Get the header and drop the refcount on the data. */ - STabHead *head = ((STabHead*) BaseTable::data) - 1; - head->refCount -= 1; - - /* If the refcount just went down to zero nobody else is referencing - * the data. */ - if ( head->refCount == 0 ) { - /* Call All destructors. */ - T *pos = BaseTable::data; - for ( long i = 0; i < head->tabLen; pos++, i++ ) - pos->~T(); - - /* Free the data space. */ - free( head ); - } - - /* Clear the pointer. */ - BaseTable::data = 0; - } -} - -/* Prepare for setting the contents of the vector to some array len long. - * Handles reusing the existing space, detaching from a common space or - * growing from zero length automatically. */ -template void SVector:: - setAsCommon(long len) -{ - if ( BaseTable::data != 0 ) { - /* Get the header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* If the refCount is one, then we can reuse the space. Otherwise we - * must detach from the referenced data create new space. */ - if ( head->refCount == 1 ) { - /* Call All destructors. */ - T *pos = BaseTable::data; - for ( long i = 0; i < head->tabLen; pos++, i++ ) - pos->~T(); - - /* Adjust the allocated length. */ - if ( len < head->tabLen ) - downResize( len ); - else if ( len > head->tabLen ) - upResize( len ); - - if ( BaseTable::data != 0 ) { - /* Get the header again and set the length. */ - head = ((STabHead*)BaseTable::data) - 1; - head->tabLen = len; - } - } - else { - /* Just detach from the data. */ - head->refCount -= 1; - BaseTable::data = 0; - - /* Make enough space. This will set the length. */ - upResizeFromEmpty( len ); - } - } - else { - /* The table is currently empty. Make enough space. This will set the - * length. */ - upResizeFromEmpty( len ); - } -} - -/** - * \brief Set the contents of the vector to be len elements exactly. - * - * The vector becomes len elements in length. Destructors are called on any - * existing elements in the vector. Copy constructors are used to place the - * new elements in the vector. - */ -template void SVector:: - setAs(const T *val, long len) -{ - /* Common stuff for setting the array to len long. */ - setAsCommon( len ); - - /* Copy data in. */ - T *dst = BaseTable::data; - const T *src = val; - for ( long i = 0; i < len; i++, dst++, src++ ) - new(dst) T(*src); -} - - -/** - * \brief Set the vector to len copies of item. - * - * The vector becomes len elements in length. Destructors are called on any - * existing elements in the vector. The element's copy constructor is used to - * copy the item into the vector. - */ -template void SVector:: - setAsDup(const T &item, long len) -{ - /* Do the common stuff for setting the array to len long. */ - setAsCommon( len ); - - /* Copy item in one spot at a time. */ - T *dst = BaseTable::data; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(item); -} - -/** - * \brief Set the vector to exactly len new items. - * - * The vector becomes len elements in length. Destructors are called on any - * existing elements in the vector. Default constructors are used to init the - * new items. - */ -template void SVector:: - setAsNew(long len) -{ - /* Do the common stuff for setting the array to len long. */ - setAsCommon( len ); - - /* Create items using default constructor. */ - T *dst = BaseTable::data; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(); -} - -/* Make space in vector for a replacement at pos of len items. Handles reusing - * existing space, detaching or growing from zero space. */ -template long SVector:: - replaceCommon(long pos, long len) -{ - if ( BaseTable::data != 0 ) { - /* Get the header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* If we are given a negative position to replace at then treat it as - * a position relative to the length. This doesn't have any meaning - * unless the length is at least one. */ - if ( pos < 0 ) - pos = head->tabLen + pos; - - /* The end is the one past the last item that we want to write to. */ - long i, endPos = pos + len; - - if ( head->refCount == 1 ) { - /* We can reuse the space. Make sure we have enough space. */ - if ( endPos > head->tabLen ) { - upResize( endPos ); - - /* Get the header again, whose addr may have changed after - * resizing. */ - head = ((STabHead*)BaseTable::data) - 1; - - /* Delete any objects we need to delete. */ - T *item = BaseTable::data + pos; - for ( i = pos; i < head->tabLen; i++, item++ ) - item->~T(); - - /* We are extending the vector, set the new data length. */ - head->tabLen = endPos; - } - else { - /* Delete any objects we need to delete. */ - T *item = BaseTable::data + pos; - for ( i = pos; i < endPos; i++, item++ ) - item->~T(); - } - } - else { - /* Use endPos to calc the end of the vector. */ - long newLen = endPos; - if ( newLen < head->tabLen ) - newLen = head->tabLen; - - /* Duplicate and grow up to endPos. This will set the length. */ - upResizeDup( newLen ); - - /* Copy from src up to pos. */ - const T *src = (T*) (head + 1); - T *dst = BaseTable::data; - for ( i = 0; i < pos; i++, dst++, src++) - new(dst) T(*src); - - /* Copy any items after the replace range. */ - for ( i += len, src += len, dst += len; - i < head->tabLen; i++, dst++, src++ ) - new(dst) T(*src); - } - } - else { - /* There is no data initially, must grow from zero. This will set the - * new length. */ - upResizeFromEmpty( len ); - } - - return pos; -} - - -/** - * \brief Replace len elements at position pos. - * - * If there are existing elements at the positions to be replaced, then - * destructors are called before the space is used. Copy constructors are used - * to place the elements into the vector. It is allowable for the pos and - * length to specify a replacement that overwrites existing elements and - * creates new ones. If pos is greater than the length of the vector then - * undefined behaviour results. If pos is negative, then it is treated as an - * offset relative to the length of the vector. - */ -template void SVector:: - replace(long pos, const T *val, long len) -{ - /* Common work for replacing in the vector. */ - pos = replaceCommon( pos, len ); - - /* Copy data in using copy constructor. */ - T *dst = BaseTable::data + pos; - const T *src = val; - for ( long i = 0; i < len; i++, dst++, src++ ) - new(dst) T(*src); -} - -/** - * \brief Replace at position pos with len copies of an item. - * - * If there are existing elements at the positions to be replaced, then - * destructors are called before the space is used. The copy constructor is - * used to place the element into this vector. It is allowable for the pos and - * length to specify a replacement that overwrites existing elements and - * creates new ones. If pos is greater than the length of the vector then - * undefined behaviour results. If pos is negative, then it is treated as an - * offset relative to the length of the vector. - */ -template void SVector:: - replaceDup(long pos, const T &val, long len) -{ - /* Common replacement stuff. */ - pos = replaceCommon( pos, len ); - - /* Copy data in using copy constructor. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(val); -} - -/** - * \brief Replace at position pos with len new elements. - * - * If there are existing elements at the positions to be replaced, then - * destructors are called before the space is used. The default constructor is - * used to initialize the new elements. It is allowable for the pos and length - * to specify a replacement that overwrites existing elements and creates new - * ones. If pos is greater than the length of the vector then undefined - * behaviour results. If pos is negative, then it is treated as an offset - * relative to the length of the vector. - */ -template void SVector:: - replaceNew(long pos, long len) -{ - /* Do the common replacement stuff. */ - pos = replaceCommon( pos, len ); - - /* Copy data in using copy constructor. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(); -} - -/** - * \brief Remove len elements at position pos. - * - * Destructor is called on all elements removed. Elements to the right of pos - * are shifted len spaces to the left to take up the free space. If pos is - * greater than or equal to the length of the vector then undefined behavior - * results. If pos is negative then it is treated as an offset relative to the - * length of the vector. - */ -template void SVector:: - remove(long pos, long len) -{ - /* If there is no data, we can't delete anything anyways. */ - if ( BaseTable::data != 0 ) { - /* Get the header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* If we are given a negative position to remove at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = head->tabLen + pos; - - /* The first position after the last item deleted. */ - long endPos = pos + len; - - /* The New data length. */ - long i, newLen = head->tabLen - len; - - if ( head->refCount == 1 ) { - /* We are the only ones using the data. We can reuse - * the existing space. */ - - /* The place in the data we are deleting at. */ - T *dst = BaseTable::data + pos; - - /* Call Destructors. */ - T *item = BaseTable::data + pos; - for ( i = 0; i < len; i += 1, item += 1 ) - item->~T(); - - /* Shift data over if necessary. */ - long lenToSlideOver = head->tabLen - endPos; - if ( len > 0 && lenToSlideOver > 0 ) - memmove(BaseTable::data + pos, dst + len, sizeof(T)*lenToSlideOver); - - /* Shrink the data if necessary. */ - downResize( newLen ); - - if ( BaseTable::data != 0 ) { - /* Get the header again (because of the resize) and set the - * new data length. */ - head = ((STabHead*)BaseTable::data) - 1; - head->tabLen = newLen; - } - } - else { - /* Must detach from the common data. Just copy the non-deleted - * items from the common data. */ - - /* Duplicate and grow down to newLen. This will set the length. */ - downResizeDup( newLen ); - - /* Copy over just the non-deleted parts. */ - const T *src = (T*) (head + 1); - T *dst = BaseTable::data; - for ( i = 0; i < pos; i++, dst++, src++ ) - new(dst) T(*src); - - /* ... and the second half. */ - for ( i += len, src += len; i < head->tabLen; i++, src++, dst++ ) - new(dst) T(*src); - } - } -} - -/* Shift over existing data. Handles reusing existing space, detaching or - * growing from zero space. */ -template long SVector:: - insertCommon(long pos, long len) -{ - if ( BaseTable::data != 0 ) { - /* Get the header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* If we are given a negative position to insert at then treat it as a - * position relative to the length. This only has meaning if there is - * existing data. */ - if ( pos < 0 ) - pos = head->tabLen + pos; - - /* Calculate the new length. */ - long i, newLen = head->tabLen + len; - - if ( head->refCount == 1 ) { - /* Up resize, we are growing. */ - upResize( newLen ); - - /* Get the header again, (the addr may have changed after - * resizing). */ - head = ((STabHead*)BaseTable::data) - 1; - - /* Shift over data at insert spot if needed. */ - if ( len > 0 && pos < head->tabLen ) { - memmove( BaseTable::data + pos + len, BaseTable::data + pos, - sizeof(T)*(head->tabLen - pos) ); - } - - /* Grow the length by the len inserted. */ - head->tabLen += len; - } - else { - /* Need to detach from the existing array. Copy over the other - * parts. This will set the length. */ - upResizeDup( newLen ); - - /* Copy over the parts around the insert. */ - const T *src = (T*) (head + 1); - T *dst = BaseTable::data; - for ( i = 0; i < pos; i++, dst++, src++ ) - new(dst) T(*src); - - /* ... and the second half. */ - for ( dst += len; i < head->tabLen; i++, src++, dst++ ) - new(dst) T(*src); - } - } - else { - /* There is no existing data. Start from zero. This will set the - * length. */ - upResizeFromEmpty( len ); - } - - return pos; -} - - -/** - * \brief Insert len elements at position pos. - * - * Elements in the vector from pos onward are shifted len spaces to the right. - * The copy constructor is used to place the elements into this vector. If pos - * is greater than the length of the vector then undefined behaviour results. - * If pos is negative then it is treated as an offset relative to the length - * of the vector. - */ -template void SVector:: - insert(long pos, const T *val, long len) -{ - /* Do the common insertion stuff. */ - pos = insertCommon( pos, len ); - - /* Copy data in element by element. */ - T *dst = BaseTable::data + pos; - const T *src = val; - for ( long i = 0; i < len; i++, dst++, src++ ) - new(dst) T(*src); -} - -/** - * \brief Insert len copies of item at position pos. - * - * Elements in the vector from pos onward are shifted len spaces to the right. - * The copy constructor is used to place the element into this vector. If pos - * is greater than the length of the vector then undefined behaviour results. - * If pos is negative then it is treated as an offset relative to the length - * of the vector. - */ -template void SVector:: - insertDup(long pos, const T &item, long len) -{ - /* Do the common insertion stuff. */ - pos = insertCommon( pos, len ); - - /* Copy the data item in one at a time. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(item); -} - - -/** - * \brief Insert len new elements using the default constructor. - * - * Elements in the vector from pos onward are shifted len spaces to the right. - * Default constructors are used to init the new elements. If pos is off the - * end of the vector then undefined behaviour results. If pos is negative then - * it is treated as an offset relative to the length of the vector. - */ -template void SVector:: - insertNew(long pos, long len) -{ - /* Do the common insertion stuff. */ - pos = insertCommon( pos, len ); - - /* Init new data with default constructors. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(); -} - -/* Makes space for len items, Does not init the items in any way. If pos is - * greater than the length of the vector then undefined behaviour results. - * Updates the length of the vector. */ -template void SVector:: - makeRawSpaceFor(long pos, long len) -{ - if ( BaseTable::data != 0 ) { - /* Get the header. */ - STabHead *head = ((STabHead*)BaseTable::data) - 1; - - /* Calculate the new length. */ - long i, newLen = head->tabLen + len; - - if ( head->refCount == 1 ) { - /* Up resize, we are growing. */ - upResize( newLen ); - - /* Get the header again, (the addr may have changed after - * resizing). */ - head = ((STabHead*)BaseTable::data) - 1; - - /* Shift over data at insert spot if needed. */ - if ( len > 0 && pos < head->tabLen ) { - memmove( BaseTable::data + pos + len, BaseTable::data + pos, - sizeof(T)*(head->tabLen - pos) ); - } - - /* Grow the length by the len inserted. */ - head->tabLen += len; - } - else { - /* Need to detach from the existing array. Copy over the other - * parts. This will set the length. */ - upResizeDup( newLen ); - - /* Copy over the parts around the insert. */ - const T *src = (T*) (head + 1); - T *dst = BaseTable::data; - for ( i = 0; i < pos; i++, dst++, src++ ) - new(dst) T(*src); - - /* ... and the second half. */ - for ( dst += len; i < head->tabLen; i++, src++, dst++ ) - new(dst) T(*src); - } - } - else { - /* There is no existing data. Start from zero. This will set the - * length. */ - upResizeFromEmpty( len ); - } -} - - -#ifdef AAPL_NAMESPACE -} -#endif - - -#endif /* _AAPL_SVECTOR_H */ diff --git a/aapl/table.h b/aapl/table.h deleted file mode 100644 index 303f473e..00000000 --- a/aapl/table.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2001, 2002 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_TABLE_H -#define _AAPL_TABLE_H - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup vector - * @{ - */ - -/** \class Table - * \brief Base class for dynamic arrays. - * - * Table is used as the common data storage class for vectors. It does not - * provide any methods to operate on the data and as such it is not intended - * to be used directly. It exists so that algorithms that operatate on dynamic - * arrays can be written without knowing about the various vector classes that - * my exist. - */ - -/*@}*/ - -/* Table class. */ -template class Table -{ -public: - /* Default Constructor. */ - inline Table(); - - /** - * \brief Get the length of the vector. - * - * \returns the length of the vector. - */ - long length() const - { return tabLen; } - - /** - * \brief Table data. - * - * The pointer to the elements in the vector. Modifying the vector may - * cause this pointer to change. - */ - T *data; - - /** - * \brief Table length. - * - * The number of items of type T in the table. - */ - long tabLen; - - /** - * \brief Allocated length. - * - * The number of items for which there is room in the current allocation. - */ - long allocLen; -}; - -/** - * \brief Default constructor - * - * Initialize table data to empty. - */ -template inline Table::Table() -: - data(0), - tabLen(0), - allocLen(0) -{ -} - -/* Default shared table header class. */ -struct STabHead -{ - /** - * \brief Table length. - * - * The number of items of type T in the table. - */ - long tabLen; - - /** - * \brief Allocated length. - * - * The number of items for which there is room in the current allocation. - */ - long allocLen; - - /** - * \brief Ref Count. - * - * The number of shared vectors referencing this data. - */ - long refCount; -}; - -/** - * \addtogroup vector - * @{ - */ - -/** \class STable - * \brief Base class for implicitly shared dynamic arrays. - * - * STable is used as the common data storage class for shared vectors. It does - * not provide any methods to operate on the data and as such it is not - * intended to be used directly. It exists so that algorithms that operatate - * on dynamic arrays can be written without knowing about the various shared - * vector classes that my exist. - */ - -/*@}*/ - -/* STable class. */ -template class STable -{ -public: - /* Default Constructor. */ - inline STable(); - - /** - * \brief Get the length of the shared vector. - * - * \returns the length of the shared vector. - */ - long length() const - { return data == 0 ? 0 : (((STabHead*)data) - 1)->tabLen; } - - /** - * \brief Get header of the shared vector. - * - * \returns the header of the shared vector. - */ - STabHead *header() const - { return data == 0 ? 0 : (((STabHead*)data) - 1); } - - /** - * \brief Table data. - * - * The pointer to the elements in the vector. The shared table header is - * located just behind the data. Modifying the vector may cause this - * pointer to change. - */ - T *data; -}; - -/** - * \brief Default constructor - * - * Initialize shared table data to empty. - */ -template inline STable::STable() -: - data(0) -{ -} - -/* If needed is greater than existing, give twice needed. */ -#define EXPN_UP( existing, needed ) \ - needed > existing ? (needed<<1) : existing - -/* If needed is less than 1 quarter existing, give twice needed. */ -#define EXPN_DOWN( existing, needed ) \ - needed < (existing>>2) ? (needed<<1) : existing - -/** - * \addtogroup vector - * @{ - */ - -/** \class ResizeExpn - * \brief Exponential table resizer. - * - * ResizeExpn is the default table resizer. When an up resize is needed, space - * is doubled. When a down resize is needed, space is halved. The result is - * that when growing the vector in a linear fashion, the number of resizes of - * the allocated space behaves logarithmically. - * - * If only up resizes are done, there will never be more than 2 times the - * needed space allocated. If down resizes are done as well, there will never - * be more than 4 times the needed space allocated. ResizeExpn uses this 50% - * usage policy on up resizing and 25% usage policy on down resizing to - * improve performance when repeatedly inserting and removing a small number - * of elements relative to the size of the array. This scheme guarantees that - * repetitive inserting and removing of a small number of elements will never - * result in repetative reallocation. - * - * The sizes passed to the resizer from the vectors are in units of T. - */ - -/*@}*/ - -/* Exponential resizer. */ -class ResizeExpn -{ -protected: - /** - * \brief Determine the new table size when up resizing. - * - * If the existing size is insufficient for the space needed then allocate - * twice the space needed. Otherwise use the existing size. - * - * \returns The new table size. - */ - static inline long upResize( long existing, long needed ) - { return EXPN_UP( existing, needed ); } - - /** - * \brief Determine the new table size when down resizing. - * - * If the space needed is less than one quarter of the existing size then - * allocate twice the space needed. Otherwise use the exitsing size. - * - * \returns The new table size. - */ - static inline long downResize( long existing, long needed ) - { return EXPN_DOWN( existing, needed ); } -}; - -#undef EXPN_UP -#undef EXPN_DOWN - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_TABLE_H */ diff --git a/aapl/vector.h b/aapl/vector.h deleted file mode 100644 index 0ec385d5..00000000 --- a/aapl/vector.h +++ /dev/null @@ -1,1190 +0,0 @@ -/* - * Copyright 2002, 2006 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _AAPL_VECTOR_H -#define _AAPL_VECTOR_H - -#include -#include -#include -#include -#include "table.h" - -#ifdef AAPL_NAMESPACE -namespace Aapl { -#endif - -/** - * \addtogroup vector - * @{ - */ - -/** \class Vector - * \brief Dynamic array. - * - * This is typical vector implementation. It is a dynamic array that can be - * used to contain complex data structures that have constructors and - * destructors as well as simple types such as integers and pointers. - * - * Vector supports inserting, overwriting, and removing single or multiple - * elements at once. Constructors and destructors are called wherever - * appropriate. For example, before an element is overwritten, it's - * destructor is called. - * - * Vector provides automatic resizing of allocated memory as needed and offers - * different allocation schemes for controlling how the automatic allocation - * is done. Two senses of the the length of the data is maintained: the - * amount of raw memory allocated to the vector and the number of actual - * elements in the vector. The various allocation schemes control how the - * allocated space is changed in relation to the number of elements in the - * vector. - * - * \include ex_vector.cpp - */ - -/*@}*/ - -template < class T, class Resize = ResizeExpn > class Vector - : public Table, public Resize -{ -private: - typedef Table BaseTable; - -public: - /** - * \brief Initialize an empty vector with no space allocated. - * - * If a linear resizer is used, the step defaults to 256 units of T. For a - * runtime vector both up and down allocation schemes default to - * Exponential. - */ - Vector() { } - - /** - * \brief Create a vector that contains an initial element. - * - * The vector becomes one element in length. The element's copy - * constructor is used to place the value in the vector. - */ - Vector(const T &val) { setAs(&val, 1); } - - /** - * \brief Create a vector that contains an array of elements. - * - * The vector becomes len elements in length. Copy constructors are used - * to place the new elements in the vector. - */ - Vector(const T *val, long len) { setAs(val, len); } - - /* Deep copy. */ - Vector( const Vector &v ); - - /* Free all mem used by the vector. */ - ~Vector() { empty(); } - - /* Delete all items. */ - void empty(); - - /* Abandon the contents of the vector without deleteing. */ - void abandon(); - - /* Transfers the elements of another vector into this vector. First emptys - * the current vector. */ - void transfer( Vector &v ); - - /* Perform a deep copy of another vector into this vector. */ - Vector &operator=( const Vector &v ); - - /* Stack operations. */ - void push( const T &t ) { append( t ); } - void pop() { remove( BaseTable::tabLen - 1 ); } - T &top() { return BaseTable::data[BaseTable::tabLen - 1]; } - - /*@{*/ - /** - * \brief Insert one element at position pos. - * - * Elements in the vector from pos onward are shifted one space to the - * right. The copy constructor is used to place the element into this - * vector. If pos is greater than the length of the vector then undefined - * behaviour results. If pos is negative then it is treated as an offset - * relative to the length of the vector. - */ - void insert(long pos, const T &val) { insert(pos, &val, 1); } - - /* Insert an array of values. */ - void insert(long pos, const T *val, long len); - - /** - * \brief Insert all the elements from another vector at position pos. - * - * Elements in this vector from pos onward are shifted v.tabLen spaces to - * the right. The element's copy constructor is used to copy the items - * into this vector. The other vector is left unchanged. If pos is off the - * end of the vector, then undefined behaviour results. If pos is negative - * then it is treated as an offset relative to the length of the vector. - * Equivalent to vector.insert(pos, other.data, other.tabLen). - */ - void insert(long pos, const Vector &v) { insert(pos, v.data, v.tabLen); } - - /* Insert len copies of val into the vector. */ - void insertDup(long pos, const T &val, long len); - - /** - * \brief Insert one new element using the default constrcutor. - * - * Elements in the vector from pos onward are shifted one space to the - * right. The default constructor is used to init the new element. If pos - * is greater than the length of the vector then undefined behaviour - * results. If pos is negative then it is treated as an offset relative to - * the length of the vector. - */ - void insertNew(long pos) { insertNew(pos, 1); } - - /* Insert len new items using default constructor. */ - void insertNew(long pos, long len); - /*@}*/ - - /*@{*/ - /** - * \brief Remove one element at position pos. - * - * The element's destructor is called. Elements to the right of pos are - * shifted one space to the left to take up the free space. If pos is greater - * than or equal to the length of the vector then undefined behavior results. - * If pos is negative then it is treated as an offset relative to the length - * of the vector. - */ - void remove(long pos) { remove(pos, 1); } - - /* Delete a number of elements. */ - void remove(long pos, long len); - /*@}*/ - - /*@{*/ - /** - * \brief Replace one element at position pos. - * - * If there is an existing element at position pos (if pos is less than - * the length of the vector) then its destructor is called before the - * space is used. The copy constructor is used to place the element into - * the vector. If pos is greater than the length of the vector then - * undefined behaviour results. If pos is negative then it is treated as - * an offset relative to the length of the vector. - */ - void replace(long pos, const T &val) { replace(pos, &val, 1); } - - /* Replace with an array of values. */ - void replace(long pos, const T *val, long len); - - /** - * \brief Replace at position pos with all the elements of another vector. - * - * Replace at position pos with all the elements of another vector. The - * other vector is left unchanged. If there are existing elements at the - * positions to be replaced, then destructors are called before the space - * is used. Copy constructors are used to place the elements into this - * vector. It is allowable for the pos and length of the other vector to - * specify a replacement that overwrites existing elements and creates new - * ones. If pos is greater than the length of the vector then undefined - * behaviour results. If pos is negative, then it is treated as an offset - * relative to the length of the vector. - */ - void replace(long pos, const Vector &v) { replace(pos, v.data, v.tabLen); } - - /* Replace len items with len copies of val. */ - void replaceDup(long pos, const T &val, long len); - - /** - * \brief Replace at position pos with one new element. - * - * If there is an existing element at the position to be replaced (pos is - * less than the length of the vector) then the element's destructor is - * called before the space is used. The default constructor is used to - * initialize the new element. If pos is greater than the length of the - * vector then undefined behaviour results. If pos is negative, then it is - * treated as an offset relative to the length of the vector. - */ - void replaceNew(long pos) { replaceNew(pos, 1); } - - /* Replace len items at pos with newly constructed objects. */ - void replaceNew(long pos, long len); - /*@}*/ - - /*@{*/ - /** - * \brief Set the contents of the vector to be val exactly. - * - * The vector becomes one element in length. Destructors are called on any - * existing elements in the vector. The element's copy constructor is used - * to place the val in the vector. - */ - void setAs(const T &val) { setAs(&val, 1); } - - /* Set to the contents of an array. */ - void setAs(const T *val, long len); - - /** - * \brief Set the vector to exactly the contents of another vector. - * - * The vector becomes v.tabLen elements in length. Destructors are called - * on any existing elements. Copy constructors are used to place the new - * elements in the vector. - */ - void setAs(const Vector &v) { setAs(v.data, v.tabLen); } - - /* Set as len copies of item. */ - void setAsDup(const T &item, long len); - - /** - * \brief Set the vector to exactly one new item. - * - * The vector becomes one element in length. Destructors are called on any - * existing elements in the vector. The default constructor is used to - * init the new item. - */ - void setAsNew() { setAsNew(1); } - - /* Set as newly constructed objects using the default constructor. */ - void setAsNew(long len); - /*@}*/ - - /*@{*/ - /** - * \brief Append one elment to the end of the vector. - * - * Copy constructor is used to place the element in the vector. - */ - void append(const T &val) { replace(BaseTable::tabLen, &val, 1); } - - /** - * \brief Append len elements to the end of the vector. - * - * Copy constructors are used to place the elements in the vector. - */ - void append(const T *val, long len) { replace(BaseTable::tabLen, val, len); } - - /** - * \brief Append the contents of another vector. - * - * The other vector is left unchanged. Copy constructors are used to place the - * elements in the vector. - */ - void append(const Vector &v) { replace(BaseTable::tabLen, v.data, v.tabLen); } - - /** - * \brief Append len copies of item. - * - * The copy constructor is used to place the item in the vector. - */ - void appendDup(const T &item, long len) { replaceDup(BaseTable::tabLen, item, len); } - - /** - * \brief Append a single newly created item. - * - * The new element is initialized with the default constructor. - */ - void appendNew() { replaceNew(BaseTable::tabLen, 1); } - - /** - * \brief Append len newly created items. - * - * The new elements are initialized with the default constructor. - */ - void appendNew(long len) { replaceNew(BaseTable::tabLen, len); } - /*@}*/ - - /*@{*/ - /** \fn Vector::prepend(const T &val) - * \brief Prepend one elment to the front of the vector. - * - * Copy constructor is used to place the element in the vector. - */ - void prepend(const T &val) { insert(0, &val, 1); } - - /** - * \brief Prepend len elements to the front of the vector. - * - * Copy constructors are used to place the elements in the vector. - */ - void prepend(const T *val, long len) { insert(0, val, len); } - - /** - * \brief Prepend the contents of another vector. - * - * The other vector is left unchanged. Copy constructors are used to place the - * elements in the vector. - */ - void prepend(const Vector &v) { insert(0, v.data, v.tabLen); } - - /** - * \brief Prepend len copies of item. - * - * The copy constructor is used to place the item in the vector. - */ - void prependDup(const T &item, long len) { insertDup(0, item, len); } - - /** - * \brief Prepend a single newly created item. - * - * The new element is initialized with the default constructor. - */ - void prependNew() { insertNew(0, 1); } - - /** - * \brief Prepend len newly created items. - * - * The new elements are initialized with the default constructor. - */ - void prependNew(long len) { insertNew(0, len); } - /*@}*/ - - /* Convenience access. */ - T &operator[](int i) const { return BaseTable::data[i]; } - long size() const { return BaseTable::tabLen; } - - /* Forward this so a ref can be used. */ - struct Iter; - - /* Various classes for setting the iterator */ - struct IterFirst { IterFirst( const Vector &v ) : v(v) { } const Vector &v; }; - struct IterLast { IterLast( const Vector &v ) : v(v) { } const Vector &v; }; - struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; - struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; - - /** - * \brief Vector Iterator. - * \ingroup iterators - */ - struct Iter - { - /* Construct, assign. */ - Iter() : ptr(0), ptrBeg(0), ptrEnd(0) { } - - /* Construct. */ - Iter( const Vector &v ); - Iter( const IterFirst &vf ); - Iter( const IterLast &vl ); - inline Iter( const IterNext &vn ); - inline Iter( const IterPrev &vp ); - - /* Assign. */ - Iter &operator=( const Vector &v ); - Iter &operator=( const IterFirst &vf ); - Iter &operator=( const IterLast &vl ); - inline Iter &operator=( const IterNext &vf ); - inline Iter &operator=( const IterPrev &vl ); - - /** \brief Less than end? */ - bool lte() const { return ptr != ptrEnd; } - - /** \brief At end? */ - bool end() const { return ptr == ptrEnd; } - - /** \brief Greater than beginning? */ - bool gtb() const { return ptr != ptrBeg; } - - /** \brief At beginning? */ - bool beg() const { return ptr == ptrBeg; } - - /** \brief At first element? */ - bool first() const { return ptr == ptrBeg+1; } - - /** \brief At last element? */ - bool last() const { return ptr == ptrEnd-1; } - - /* Return the position. */ - long pos() const { return ptr - ptrBeg - 1; } - T &operator[](int i) const { return ptr[i]; } - - /** \brief Implicit cast to T*. */ - operator T*() const { return ptr; } - - /** \brief Dereference operator returns T&. */ - T &operator *() const { return *ptr; } - - /** \brief Arrow operator returns T*. */ - T *operator->() const { return ptr; } - - /** \brief Move to next item. */ - T *operator++() { return ++ptr; } - - /** \brief Move to next item. */ - T *operator++(int) { return ptr++; } - - /** \brief Move to next item. */ - T *increment() { return ++ptr; } - - /** \brief Move n items forward. */ - T *operator+=(long n) { return ptr+=n; } - - /** \brief Move to previous item. */ - T *operator--() { return --ptr; } - - /** \brief Move to previous item. */ - T *operator--(int) { return ptr--; } - - /** \brief Move to previous item. */ - T *decrement() { return --ptr; } - - /** \brief Move n items back. */ - T *operator-=(long n) { return ptr-=n; } - - /** \brief Return the next item. Does not modify this. */ - inline IterNext next() const { return IterNext(*this); } - - /** \brief Return the previous item. Does not modify this. */ - inline IterPrev prev() const { return IterPrev(*this); } - - /** \brief The iterator is simply a pointer. */ - T *ptr; - - /* For testing endpoints. */ - T *ptrBeg, *ptrEnd; - }; - - /** \brief Return first element. */ - IterFirst first() { return IterFirst( *this ); } - - /** \brief Return last element. */ - IterLast last() { return IterLast( *this ); } - -protected: - void makeRawSpaceFor(long pos, long len); - - void upResize(long len); - void downResize(long len); -}; - -/* Init a vector iterator with just a vector. */ -template Vector::Iter::Iter( const Vector &v ) -{ - if ( v.tabLen == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = v.data; - ptrBeg = v.data-1; - ptrEnd = v.data+v.tabLen; - } -} - -/* Init a vector iterator with the first of a vector. */ -template Vector::Iter::Iter( - const IterFirst &vf ) -{ - if ( vf.v.tabLen == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vf.v.data; - ptrBeg = vf.v.data-1; - ptrEnd = vf.v.data+vf.v.tabLen; - } -} - -/* Init a vector iterator with the last of a vector. */ -template Vector::Iter::Iter( - const IterLast &vl ) -{ - if ( vl.v.tabLen == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vl.v.data+vl.v.tabLen-1; - ptrBeg = vl.v.data-1; - ptrEnd = vl.v.data+vl.v.tabLen; - } -} - -/* Init a vector iterator with the next of some other iterator. */ -template Vector::Iter::Iter( - const IterNext &vn ) -: - ptr(vn.i.ptr+1), - ptrBeg(vn.i.ptrBeg), - ptrEnd(vn.i.ptrEnd) -{ -} - -/* Init a vector iterator with the prev of some other iterator. */ -template Vector::Iter::Iter( - const IterPrev &vp ) -: - ptr(vp.i.ptr-1), - ptrBeg(vp.i.ptrBeg), - ptrEnd(vp.i.ptrEnd) -{ -} - -/* Set a vector iterator with some vector. */ -template typename Vector::Iter & - Vector::Iter::operator=( const Vector &v ) -{ - if ( v.tabLen == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = v.data; - ptrBeg = v.data-1; - ptrEnd = v.data+v.tabLen; - } - return *this; -} - -/* Set a vector iterator with the first element in a vector. */ -template typename Vector::Iter & - Vector::Iter::operator=( const IterFirst &vf ) -{ - if ( vf.v.tabLen == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vf.v.data; - ptrBeg = vf.v.data-1; - ptrEnd = vf.v.data+vf.v.tabLen; - } - return *this; -} - -/* Set a vector iterator with the last element in a vector. */ -template typename Vector::Iter & - Vector::Iter::operator=( const IterLast &vl ) -{ - if ( vl.v.tabLen == 0 ) - ptr = ptrBeg = ptrEnd = 0; - else { - ptr = vl.v.data+vl.v.tabLen-1; - ptrBeg = vl.v.data-1; - ptrEnd = vl.v.data+vl.v.tabLen; - } - return *this; -} - -/* Set a vector iterator with the next of some other iterator. */ -template typename Vector::Iter & - Vector::Iter::operator=( const IterNext &vn ) -{ - ptr = vn.i.ptr+1; - ptrBeg = vn.i.ptrBeg; - ptrEnd = vn.i.ptrEnd; - return *this; -} - -/* Set a vector iterator with the prev of some other iterator. */ -template typename Vector::Iter & - Vector::Iter::operator=( const IterPrev &vp ) -{ - ptr = vp.i.ptr-1; - ptrBeg = vp.i.ptrBeg; - ptrEnd = vp.i.ptrEnd; - return *this; -} - -/** - * \brief Forget all elements in the vector. - * - * The contents of the vector are reset to null without without the space - * being freed. - */ -template void Vector:: - abandon() -{ - BaseTable::data = 0; - BaseTable::tabLen = 0; - BaseTable::allocLen = 0; -} - -/** - * \brief Transfer the contents of another vector into this vector. - * - * The dynamic array of the other vector is moved into this vector by - * reference. If this vector is non-empty then its contents are first deleted. - * Afterward the other vector will be empty. - */ -template void Vector:: - transfer( Vector &v ) -{ - empty(); - - BaseTable::data = v.data; - BaseTable::tabLen = v.tabLen; - BaseTable::allocLen = v.allocLen; - - v.abandon(); -} - -/** - * \brief Deep copy another vector into this vector. - * - * Copies the entire contents of the other vector into this vector. Any - * existing contents are first deleted. Equivalent to setAs. - * - * \returns A reference to this. - */ -template Vector &Vector:: - operator=( const Vector &v ) -{ - setAs(v.data, v.tabLen); - return *this; -} - -/* Up resize the data for len elements using Resize::upResize to tell us the - * new tabLen. Reads and writes allocLen. Does not read or write tabLen. */ -template void Vector:: - upResize(long len) -{ - /* Ask the resizer what the new tabLen will be. */ - long newLen = Resize::upResize(BaseTable::allocLen, len); - - /* Did the data grow? */ - if ( newLen > BaseTable::allocLen ) { - BaseTable::allocLen = newLen; - if ( BaseTable::data != 0 ) { - /* Table exists already, resize it up. */ - BaseTable::data = (T*) realloc( BaseTable::data, sizeof(T) * newLen ); - if ( BaseTable::data == 0 ) - throw std::bad_alloc(); - } - else { - /* Create the data. */ - BaseTable::data = (T*) malloc( sizeof(T) * newLen ); - if ( BaseTable::data == 0 ) - throw std::bad_alloc(); - } - } -} - -/* Down resize the data for len elements using Resize::downResize to determine - * the new tabLen. Reads and writes allocLen. Does not read or write tabLen. */ -template void Vector:: - downResize(long len) -{ - /* Ask the resizer what the new tabLen will be. */ - long newLen = Resize::downResize( BaseTable::allocLen, len ); - - /* Did the data shrink? */ - if ( newLen < BaseTable::allocLen ) { - BaseTable::allocLen = newLen; - if ( newLen == 0 ) { - /* Simply free the data. */ - free( BaseTable::data ); - BaseTable::data = 0; - } - else { - /* Not shrinking to size zero, realloc it to the smaller size. */ - BaseTable::data = (T*) realloc( BaseTable::data, sizeof(T) * newLen ); - if ( BaseTable::data == 0 ) - throw std::bad_alloc(); - } - } -} - -/** - * \brief Perform a deep copy of the vector. - * - * The contents of the other vector are copied into this vector. This vector - * gets the same allocation size as the other vector. All items are copied - * using the element's copy constructor. - */ -template Vector:: - Vector(const Vector &v) -{ - BaseTable::tabLen = v.tabLen; - BaseTable::allocLen = v.allocLen; - - if ( BaseTable::allocLen > 0 ) { - /* Allocate needed space. */ - BaseTable::data = (T*) malloc(sizeof(T) * BaseTable::allocLen); - if ( BaseTable::data == 0 ) - throw std::bad_alloc(); - - /* If there are any items in the src data, copy them in. */ - T *dst = BaseTable::data, *src = v.data; - for (long pos = 0; pos < BaseTable::tabLen; pos++, dst++, src++ ) - new(dst) T(*src); - } - else { - /* Nothing allocated. */ - BaseTable::data = 0; - } -} - -/** \fn Vector::~Vector() - * \brief Free all memory used by the vector. - * - * The vector is reset to zero elements. Destructors are called on all - * elements in the vector. The space allocated for the vector is freed. - */ - - -/** - * \brief Free all memory used by the vector. - * - * The vector is reset to zero elements. Destructors are called on all - * elements in the vector. The space allocated for the vector is freed. - */ -template void Vector:: - empty() -{ - if ( BaseTable::data != 0 ) { - /* Call All destructors. */ - T *pos = BaseTable::data; - for ( long i = 0; i < BaseTable::tabLen; pos++, i++ ) - pos->~T(); - - /* Free the data space. */ - free( BaseTable::data ); - BaseTable::data = 0; - BaseTable::tabLen = BaseTable::allocLen = 0; - } -} - -/** - * \brief Set the contents of the vector to be len elements exactly. - * - * The vector becomes len elements in length. Destructors are called on any - * existing elements in the vector. Copy constructors are used to place the - * new elements in the vector. - */ -template void Vector:: - setAs(const T *val, long len) -{ - /* Call All destructors. */ - long i; - T *pos = BaseTable::data; - for ( i = 0; i < BaseTable::tabLen; pos++, i++ ) - pos->~T(); - - /* Adjust the allocated length. */ - if ( len < BaseTable::tabLen ) - downResize( len ); - else if ( len > BaseTable::tabLen ) - upResize( len ); - - /* Set the new data length to exactly len. */ - BaseTable::tabLen = len; - - /* Copy data in. */ - T *dst = BaseTable::data; - const T *src = val; - for ( i = 0; i < len; i++, dst++, src++ ) - new(dst) T(*src); -} - -/** - * \brief Set the vector to len copies of item. - * - * The vector becomes len elements in length. Destructors are called on any - * existing elements in the vector. The element's copy constructor is used to - * copy the item into the vector. - */ -template void Vector:: - setAsDup(const T &item, long len) -{ - /* Call All destructors. */ - T *pos = BaseTable::data; - for ( long i = 0; i < BaseTable::tabLen; pos++, i++ ) - pos->~T(); - - /* Adjust the allocated length. */ - if ( len < BaseTable::tabLen ) - downResize( len ); - else if ( len > BaseTable::tabLen ) - upResize( len ); - - /* Set the new data length to exactly len. */ - BaseTable::tabLen = len; - - /* Copy item in one spot at a time. */ - T *dst = BaseTable::data; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(item); -} - -/** - * \brief Set the vector to exactly len new items. - * - * The vector becomes len elements in length. Destructors are called on any - * existing elements in the vector. Default constructors are used to init the - * new items. - */ -template void Vector:: - setAsNew(long len) -{ - /* Call All destructors. */ - T *pos = BaseTable::data; - for ( long i = 0; i < BaseTable::tabLen; pos++, i++ ) - pos->~T(); - - /* Adjust the allocated length. */ - if ( len < BaseTable::tabLen ) - downResize( len ); - else if ( len > BaseTable::tabLen ) - upResize( len ); - - /* Set the new data length to exactly len. */ - BaseTable::tabLen = len; - - /* Create items using default constructor. */ - T *dst = BaseTable::data; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(); -} - - -/** - * \brief Replace len elements at position pos. - * - * If there are existing elements at the positions to be replaced, then - * destructors are called before the space is used. Copy constructors are used - * to place the elements into the vector. It is allowable for the pos and - * length to specify a replacement that overwrites existing elements and - * creates new ones. If pos is greater than the length of the vector then - * undefined behaviour results. If pos is negative, then it is treated as an - * offset relative to the length of the vector. - */ -template void Vector:: - replace(long pos, const T *val, long len) -{ - long endPos, i; - T *item; - - /* If we are given a negative position to replace at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* The end is the one past the last item that we want - * to write to. */ - endPos = pos + len; - - /* Make sure we have enough space. */ - if ( endPos > BaseTable::tabLen ) { - upResize( endPos ); - - /* Delete any objects we need to delete. */ - item = BaseTable::data + pos; - for ( i = pos; i < BaseTable::tabLen; i++, item++ ) - item->~T(); - - /* We are extending the vector, set the new data length. */ - BaseTable::tabLen = endPos; - } - else { - /* Delete any objects we need to delete. */ - item = BaseTable::data + pos; - for ( i = pos; i < endPos; i++, item++ ) - item->~T(); - } - - /* Copy data in using copy constructor. */ - T *dst = BaseTable::data + pos; - const T *src = val; - for ( i = 0; i < len; i++, dst++, src++ ) - new(dst) T(*src); -} - -/** - * \brief Replace at position pos with len copies of an item. - * - * If there are existing elements at the positions to be replaced, then - * destructors are called before the space is used. The copy constructor is - * used to place the element into this vector. It is allowable for the pos and - * length to specify a replacement that overwrites existing elements and - * creates new ones. If pos is greater than the length of the vector then - * undefined behaviour results. If pos is negative, then it is treated as an - * offset relative to the length of the vector. - */ -template void Vector:: - replaceDup(long pos, const T &val, long len) -{ - long endPos, i; - T *item; - - /* If we are given a negative position to replace at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* The end is the one past the last item that we want - * to write to. */ - endPos = pos + len; - - /* Make sure we have enough space. */ - if ( endPos > BaseTable::tabLen ) { - upResize( endPos ); - - /* Delete any objects we need to delete. */ - item = BaseTable::data + pos; - for ( i = pos; i < BaseTable::tabLen; i++, item++ ) - item->~T(); - - /* We are extending the vector, set the new data length. */ - BaseTable::tabLen = endPos; - } - else { - /* Delete any objects we need to delete. */ - item = BaseTable::data + pos; - for ( i = pos; i < endPos; i++, item++ ) - item->~T(); - } - - /* Copy data in using copy constructor. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(val); -} - -/** - * \brief Replace at position pos with len new elements. - * - * If there are existing elements at the positions to be replaced, then - * destructors are called before the space is used. The default constructor is - * used to initialize the new elements. It is allowable for the pos and length - * to specify a replacement that overwrites existing elements and creates new - * ones. If pos is greater than the length of the vector then undefined - * behaviour results. If pos is negative, then it is treated as an offset - * relative to the length of the vector. - */ -template void Vector:: - replaceNew(long pos, long len) -{ - long endPos, i; - T *item; - - /* If we are given a negative position to replace at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* The end is the one past the last item that we want - * to write to. */ - endPos = pos + len; - - /* Make sure we have enough space. */ - if ( endPos > BaseTable::tabLen ) { - upResize( endPos ); - - /* Delete any objects we need to delete. */ - item = BaseTable::data + pos; - for ( i = pos; i < BaseTable::tabLen; i++, item++ ) - item->~T(); - - /* We are extending the vector, set the new data length. */ - BaseTable::tabLen = endPos; - } - else { - /* Delete any objects we need to delete. */ - item = BaseTable::data + pos; - for ( i = pos; i < endPos; i++, item++ ) - item->~T(); - } - - /* Copy data in using copy constructor. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(); -} - -/** - * \brief Remove len elements at position pos. - * - * Destructor is called on all elements removed. Elements to the right of pos - * are shifted len spaces to the left to take up the free space. If pos is - * greater than or equal to the length of the vector then undefined behavior - * results. If pos is negative then it is treated as an offset relative to the - * length of the vector. - */ -template void Vector:: - remove(long pos, long len) -{ - long newLen, lenToSlideOver, endPos; - T *dst, *item; - - /* If we are given a negative position to remove at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* The first position after the last item deleted. */ - endPos = pos + len; - - /* The new data length. */ - newLen = BaseTable::tabLen - len; - - /* The place in the data we are deleting at. */ - dst = BaseTable::data + pos; - - /* Call Destructors. */ - item = dst; - for ( long i = 0; i < len; i += 1, item += 1 ) - item->~T(); - - /* Shift data over if necessary. */ - lenToSlideOver = BaseTable::tabLen - endPos; - if ( len > 0 && lenToSlideOver > 0 ) - memmove(dst, dst + len, sizeof(T)*lenToSlideOver); - - /* Shrink the data if necessary. */ - downResize( newLen ); - - /* Set the new data length. */ - BaseTable::tabLen = newLen; -} - -/** - * \brief Insert len elements at position pos. - * - * Elements in the vector from pos onward are shifted len spaces to the right. - * The copy constructor is used to place the elements into this vector. If pos - * is greater than the length of the vector then undefined behaviour results. - * If pos is negative then it is treated as an offset relative to the length - * of the vector. - */ -template void Vector:: - insert(long pos, const T *val, long len) -{ - /* If we are given a negative position to insert at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* Calculate the new length. */ - long newLen = BaseTable::tabLen + len; - - /* Up resize, we are growing. */ - upResize( newLen ); - - /* Shift over data at insert spot if needed. */ - if ( len > 0 && pos < BaseTable::tabLen ) { - memmove(BaseTable::data + pos + len, BaseTable::data + pos, - sizeof(T)*(BaseTable::tabLen-pos)); - } - - /* Copy data in element by element. */ - T *dst = BaseTable::data + pos; - const T *src = val; - for ( long i = 0; i < len; i++, dst++, src++ ) - new(dst) T(*src); - - /* Set the new length. */ - BaseTable::tabLen = newLen; -} - -/** - * \brief Insert len copies of item at position pos. - * - * Elements in the vector from pos onward are shifted len spaces to the right. - * The copy constructor is used to place the element into this vector. If pos - * is greater than the length of the vector then undefined behaviour results. - * If pos is negative then it is treated as an offset relative to the length - * of the vector. - */ -template void Vector:: - insertDup(long pos, const T &item, long len) -{ - /* If we are given a negative position to insert at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* Calculate the new length. */ - long newLen = BaseTable::tabLen + len; - - /* Up resize, we are growing. */ - upResize( newLen ); - - /* Shift over data at insert spot if needed. */ - if ( len > 0 && pos < BaseTable::tabLen ) { - memmove(BaseTable::data + pos + len, BaseTable::data + pos, - sizeof(T)*(BaseTable::tabLen-pos)); - } - - /* Copy the data item in one at a time. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(item); - - /* Set the new length. */ - BaseTable::tabLen = newLen; -} - -/** - * \brief Insert len new elements using the default constructor. - * - * Elements in the vector from pos onward are shifted len spaces to the right. - * Default constructors are used to init the new elements. If pos is off the - * end of the vector then undefined behaviour results. If pos is negative then - * it is treated as an offset relative to the length of the vector. - */ -template void Vector:: - insertNew(long pos, long len) -{ - /* If we are given a negative position to insert at then - * treat it as a position relative to the length. */ - if ( pos < 0 ) - pos = BaseTable::tabLen + pos; - - /* Calculate the new length. */ - long newLen = BaseTable::tabLen + len; - - /* Up resize, we are growing. */ - upResize( newLen ); - - /* Shift over data at insert spot if needed. */ - if ( len > 0 && pos < BaseTable::tabLen ) { - memmove(BaseTable::data + pos + len, BaseTable::data + pos, - sizeof(T)*(BaseTable::tabLen-pos)); - } - - /* Init new data with default constructors. */ - T *dst = BaseTable::data + pos; - for ( long i = 0; i < len; i++, dst++ ) - new(dst) T(); - - /* Set the new length. */ - BaseTable::tabLen = newLen; -} - -/* Makes space for len items, Does not init the items in any way. If pos is - * greater than the length of the vector then undefined behaviour results. - * Updates the length of the vector. */ -template void Vector:: - makeRawSpaceFor(long pos, long len) -{ - /* Calculate the new length. */ - long newLen = BaseTable::tabLen + len; - - /* Up resize, we are growing. */ - upResize( newLen ); - - /* Shift over data at insert spot if needed. */ - if ( len > 0 && pos < BaseTable::tabLen ) { - memmove(BaseTable::data + pos + len, BaseTable::data + pos, - sizeof(T)*(BaseTable::tabLen-pos)); - } - - /* Save the new length. */ - BaseTable::tabLen = newLen; -} - -#ifdef AAPL_NAMESPACE -} -#endif - -#endif /* _AAPL_VECTOR_H */ diff --git a/aapl/version.mk b/aapl/version.mk deleted file mode 100644 index fa74770a..00000000 --- a/aapl/version.mk +++ /dev/null @@ -1,2 +0,0 @@ -VERSION = 2.14 -PUBDATE = March 2006 diff --git a/cgil/.gitignore b/cgil/.gitignore deleted file mode 100644 index 9ee64540..00000000 --- a/cgil/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Makefile.in -/Makefile diff --git a/cgil/Makefile.am b/cgil/Makefile.am deleted file mode 100644 index 57d12e80..00000000 --- a/cgil/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ - -data_DATA = $(CGIL_FILES) - -CGIL_FILES = ril.lm rlhc-main.lm \ - rlhc-c.lm rlhc-csharp.lm rlhc-go.lm rlhc-js.lm rlhc-ruby.lm \ - rlhc-crack.lm rlhc-d.lm rlhc-java.lm rlhc-julia.lm rlhc-ocaml.lm rlhc-rust.lm - -EXTRA_DIST = $(CGIL_FILES) - diff --git a/cgil/ragel.lm b/cgil/ragel.lm deleted file mode 100644 index 1325bbb3..00000000 --- a/cgil/ragel.lm +++ /dev/null @@ -1,1027 +0,0 @@ - -global GblActionParams: bool = false -global GblMachineMap: map = new map() -global GblCurMachine: machine -global GblTargetMachine: str = "" -global GblSearchMachine: str = "" -global GblWantSection: bool = false -global GblIncludeDepth: int = 0 -global GblImport: bool = false -global GblFileName: str = "" -global GblIncludePaths: list = new list() - -struct saved_globals - FileName: str - TargetMachine: str - SearchMachine: str - WantSection: bool - IncludeDepth: int - Import: bool -end - -global GblSavedGlobals: list = new list() - -void saveGlobals() -{ - new SG: saved_globals() - - SG->FileName = GblFileName - SG->TargetMachine = GblTargetMachine - SG->SearchMachine = GblSearchMachine - SG->WantSection = GblWantSection - SG->Import = GblImport - SG->IncludeDepth = GblIncludeDepth - - GblSavedGlobals->push_tail( SG ) -} - -void restoreGlobals() -{ - SG: saved_globals = GblSavedGlobals->pop_tail() - - GblFileName = SG->FileName - GblTargetMachine = SG->TargetMachine - GblSearchMachine = SG->SearchMachine - GblWantSection = SG->WantSection - GblImport = SG->Import - GblIncludeDepth = SG->IncludeDepth -} - -struct include_history_item - FileName: str - SectionName: str -end - -bool isDuplicateInclude( From: machine, FileName: str, SectionName: str ) -{ - for Item: include_history_item in From->IncludeHistory { - if Item->FileName == FileName && Item->SectionName == SectionName { - return true - } - } - return false -} - -void addIncludeItem( From: machine, FileName: str, SectionName: str ) -{ - new Item: include_history_item() - Item->FileName = FileName - Item->SectionName = SectionName - From->IncludeHistory->push_tail( Item ) -} - -struct machine - Name: str - ActionParams: map - IncludeHistory: list -end - - -rl ident - /( alpha | '_' ) ( alpha | digit | '_' )*/ - -rl number - / digit+ / - -rl hex_number - / '0x' [0-9a-fA-F]+ / - -rl hex_char - / '0x' [0-9a-fA-F]{2} / - -rl NL / '\n' / - -rl c_comment - / '/*' ( any | NL )* :>> '*/' / - -rl cpp_comment - / '//' [^\n]* NL / - -rl ruby_comment - / '#' [^\n]* NL / - -rl s_literal - / "'" ([^'\\\n] | '\\' (any | NL))* "'" / - -rl d_literal - / '"' ([^"\\] | NL | '\\' (any | NL))* '"' / - -rl host_re_literal - / '/' ([^/\\] | NL | '\\' (any | NL))* '/' / - -# -# Consuming ragel defintions without parsing. Used for included sections we -# don't want and for import (TODO). -# -namespace consume - lex - token h_word / [a-zA-Z_][a-zA-Z0-9_]* / - - token h_open /'{'/ - token h_close /'}'/ - - token h_number /digit+/ - token h_hex_number /'0x' [0-9a-fA-F]+/ - - token h_comment - / c_comment | cpp_comment / - - token h_string - / s_literal | d_literal / - - token h_whitespace - / ( [ \t] | NL )+ / - - token h_any / any / - end - - def host_tok - [h_word] - | [h_number] - | [h_hex_number] - | [h_comment] - | [h_string] - | [h_whitespace] - | [h_open host_tok* h_close] - | [h_any] - - lex - ignore /[\t\n ]+/ - ignore /'#' any* :> '\n'/ - - literal `}%% - - token word / [a-zA-Z_][a-zA-Z0-9_]* / - token uint / number / - token hex / hex_number / - - token string / - '"' ( [^"\\] | '\\' any )* '"' 'i'? | - "'" ( [^'\\] | '\\' any )* "'" 'i'? | - "[" ( [^\]\\] | '\\' any )* "]" 'i'? #| - #"/" ( [^\/\\] | '\\' any )* "/" 'i'? - / - - - token open /'{'/ -ni - token close ni- /'}'/ - token c_any / any / - end - - - # Garbling up a machine, no interpretation - def tok - [word] - | [uint] - | [hex] - | [string] - | [open host_tok* h_close] - | [c_any] -end - -# State reference. -namespace state_ref - lex - ignore /[\t\n ]+/ - literal `:: `; `) - token word /[a-zA-Z_][a-zA-Z0-9_]*/ - end - - def state_ref - [opt_name_sep state_ref_names] :Ref - - def opt_name_sep - [`::] :ColonColon - | [] :Empty - - # List of names separated by :: - def state_ref_names - [state_ref_names `:: word] :Rec - | [word] :Base -end - -namespace inline - - def inline_expr - [expr_item_list] :List - - def expr_item_list - [expr_item_list expr_item] :Rec - | [] :Empty - - def expr_item - [expr_any] :ExprAny - | [expr_symbol] :ExprSymbol - | [expr_interpret] :ExprInterpret - - def expr_any - [whitespace] :WS - | [comment] :Comment - | [string] :String - | [number] :Number - | [hex_number] :Hex - | [ident] :Ident - | [c_any] :Any - - def expr_symbol - [`,] :Comma | [`(] :Open | [`)] :Close | [`*] :Star | [`::] :DoubleColon - - def expr_interpret - [`fpc] :Fpc - | [`fc] :Fc - | [`fcurs] :Fcurs - | [`ftargs] :Ftargs - | [`fentry `( state_ref::state_ref state_ref::`)] :Fentry - | [var_ref] :VarRef - - def inline_block - [block_item_list] :List - - def block_item_list - [block_item block_item_list] :Rec - | [] :Base - - def block_item - [expr_any] :ExprAny - | [block_symbol] :BlockSymbol - | [block_interpret] :BlockInterpret - | [`{ inline_block `}] :RecBlock - - def block_symbol - [`,] :B1 | [`;] :B2 | [`(] :B3 | [`)] :B4 | [`*] :B5 | [`::] :B6 - - def block_interpret - [expr_interpret] :ExprInterpret - | [`fhold whitespace? `;] :Fhold - | [`fgoto whitespace? `* inline_expr `;] :FgotoExpr - | [`fnext whitespace? `* inline_expr `;] :FnextExpr - | [`fcall whitespace? `* inline_expr `;] :FcallExpr - | [`fncall whitespace? `* inline_expr `;] :FncallExpr - | [`fexec inline_expr `;] :Fexec - | [`fgoto state_ref::state_ref state_ref::`;] :FgotoSr - | [`fnext state_ref::state_ref state_ref::`;] :FnextSr - | [`fcall state_ref::state_ref state_ref::`;] :FcallSr - | [`fncall state_ref::state_ref state_ref::`;] :FncallSr - | [`fret `;] :Fret - | [`fnret `;] :Fnret - | [`fbreak `;] :Fbreak - | [`fnbreak `;] :Fnbreak -end - -namespace ragel - lex - literal `}%% -ni - - ignore /[\t\n ]+/ - ignore /'#' any* :> '\n'/ - - literal `^ `| `- `, `: `! `? `. - literal `( `) `{ -ni ni- `} `* `& `+ - - literal `-- `:> `:>> `<: `-> `** - - literal `|* `*| `=> - - literal `@ `> `< `% `$ - literal `from `to `eof `lerr `err - literal `when `inwhen `outwhen `>? `$? `%? - - literal `:= `|= `= `; `.. `../i `:: - - literal `>~ `$~ `%~ `<~ `@~ `<>~ - literal `>* `$* `%* `<* `@* `<>* - literal `>/ `$/ `%/ `/ - literal `>! `$! `%! `! - literal `>^ `$^ `%^ `<^ `@^ `<>^ - literal `<> - - literal `%%--{ -ni `%%++{ -ni - - token include_tok - /'include'/ - { - # Take off the include token. - input->pull( match_length ) - - # Parse the include details, up to the the ';' and stop there. - parse_stop Spec: include_spec(input)[] - - Fn: str - Machine: str - if Spec.string - Fn = $Spec.string - if Spec.word - Machine = $Spec.word - - Stream: stream = ragelInclude( Fn, Machine ) - - if Stream { - input->push( "}--%%" ) - input->push_stream( Stream ) - input->push( "%%--{" ) - } - } - - token import_tok - /'import'/ - { - # Take off the include token. - input->pull( match_length ) - - # Parse the import details, up to the the ';' and stop there. - parse_stop Spec: import_spec(input)[] - - Fn: str - if Spec.string - Fn = $Spec.string - - Stream: stream = ragelImport( Fn ) - - if Stream { - input->push( "}++%%" ) - input->push_stream( Stream ) - input->push( "%%++{" ) - } - } - - - literal `machine `action `variable `alphtype - `access `write `getkey `export `prepush - `postpop `nfaprepush `nfapostpop - - literal `:nfa `:nfa_greedy `:nfa_lazy `:nfa_wrap - `:nfa_wrap_greedy `:nfa_wrap_lazy - `:cond `:condplus `:condstar `): - - token string / - '"' ( [^"\\] | '\\' any )* '"' 'i'? | - "'" ( [^'\\] | '\\' any )* "'" 'i'? - / - - token lex_regex_open /'/'/ -ni - token lex_sqopen_pos /'['/ -ni - token lex_sqopen_neg /'[^'/ -ni - - token word / [a-zA-Z_][a-zA-Z0-9_]* / - token uint / number / - token hex / hex_number / - end - - def include_spec - [word `;] - | [string `;] - | [word string `;] - - def import_spec - [string `;] - - lex - token re_dot / '.' / - token re_star / '*' / - token re_char / ^( '\\' | '.' | '*' | '[' | '/' ) | '\\' . any / - token re_close / '/' 'i'? / - token re_sqopen_pos /'['/ - token re_sqopen_neg /'[^'/ - end - - lex - token re_or_dash / '-' / - token re_or_char / ^( '\\' | '-' | ']' ) | '\\' . any / - token re_or_sqclose / ']' / - end - - # Not cannot start with '{', terminated by ';', rewritten into { inline_expr } - token _inline_expr_reparse - /[^{;] [^;]* ';'/ { - R: str = input->pull( match_length - 1 ) - input->pull( 1 ) - input->push( "}" ) - input->push( R ) - input->push( "{" ) - } - - token variable_name /ident/ - - # This region is for deciding if we want to parse a ragel section, or if we - # want to consume it without interpreting. Consuming is for included - # sections we don't want and all sections in an imported file. - lex - token ign_select /''/ - { - if GblWantSection - input->push( make_token( typeid, '' ) ) - else - input->push( make_token( typeid, '' ) ) - } - - token ign_want // - token ign_ignore // - end - - # - # Machine name word. We inspect it to determine if we are interested in the - # section. - # - lex - token mn_word / [a-zA-Z_][a-zA-Z0-9_]* / - { - S: str = input->pull(match_length) - IgnWord: mn_word = make_token( typeid, S ) - input->push( IgnWord ) - - if ( GblImport ) - GblWantSection = false - else if ( GblSearchMachine != "" ) { - if ( S != GblSearchMachine ) - GblWantSection = false - else - GblWantSection = true - } - else { - GblWantSection = true - } - - Name: str = S #$lhs.mn_word - if GblTargetMachine != "" - Name = GblTargetMachine - - Machine: machine = GblMachineMap->find( Name ) - - if !Machine - { - Machine = new machine() - Machine->Name = Name - Machine->ActionParams = new map() - Machine->IncludeHistory = new list() - GblMachineMap->insert( Machine->Name, Machine ) - } - - GblCurMachine = Machine - - # print "want section: [GblWantSection] - } - end - - - def inline_expr_reparse - [_inline_expr_reparse] :Reparse - | [action_expr] :ActionExpr - - def join - [join `, expression] :Rec - | [expression] :Base - - def expression - [expr_left expression_op_list] :Expression - - def expression_op_list - [expression_op expression_op_list] :Op - | [] :Empty - - def expression_op - [`| term] :Or - | [`& term] :And - | [`- term] :Sub - | [`-- term] :Ssub - - def expr_left - [term] :Term - - def term - [term_left term_op_list_short] :Term - - def term_left - [factor_label] :FactorLabel - - # This list is done manually to get shortest match. - def term_op_list_short - [] :Empty - | [term_op term_op_list_short] :Terms - - def term_op - [factor_label] :None - | [`. factor_label] :Dot - | [`:> factor_label] :ColonLt - | [`:>> factor_label] :ColonLtLt - | [`<: factor_label] :GtColon - - def factor_label - [word `: factor_label] :Label - | [factor_ep] :Ep - - def factor_ep - [factor_aug `-> epsilon_target] :Epsilon - | [factor_aug] :Base - - def epsilon_target - [epsilon_target `:: word] :Rec - | [word] :Base - - def action_expr - [`{ CInlineExpr: inline::inline_expr inline::`}] :ActionExpr - - def action_block - [`{ CInlineBlock: inline::inline_block inline::`}] :ActionBlock - - def action_arg_list - [action_arg_list `, action_ref] :Rec - | [action_ref] :Base - - def opt_action_arg_list - [action_arg_list] :List - | [] :Empty - - def named_action_ref - [word] :Plain - { - if ( GblCurMachine->ActionParams->find( $lhs.word ) ) - reject - } - | [word `( opt_action_arg_list `)] :Args - { - if ( ! GblCurMachine->ActionParams->find( $lhs.word ) ) - reject - } - - def action_ref - [named_action_ref] :NamedRef - | [`( named_action_ref `)] :ParenNamed - | [action_block] :Block - - def priority_name - [word] :Word - - def error_name - [word] :Word - - def priority_aug - [uint] :NoSign - | [`+ uint] :Plus - | [`- uint] :Minus - - def aug_base - [`@] :Finish | [`>] :Enter | [`%] :Leave | [`$] :All - - def aug_cond - [`>?] :Start1 | [`$?] :All1 | [`%?] :Leave1 - | [`> `when] :Start2 | [`$ `when] :All2 | [`% `when] :Leave2 - | [`inwhen] :Start3 | [`when] :All3 | [`outwhen] :Leave3 - - def aug_to_state - [`>~] :Start1 | [`<~] :NotStart1 | [`$~] :All1 - | [`%~] :Final1 | [`@~] :NotFinal1 | [`<>~] :Middle1 - | [`> `to] :Start2 | [`< `to] :NotStart2 | [`$ `to] :All2 - | [`% `to] :Final2 | [`@ `to] :NotFinal2 | [`<> `to] :Middle2 - - def aug_from_state - [`>*] :Start1 | [`<*] :NotStart1 | [`$*] :All1 - | [`%*] :Final1 | [`@*] :NotFinal1 | [`<>*] :Middle1 - | [`> `from] :Start2 | [`< `from] :NotStart2 | [`$ `from] :All2 - | [`% `from] :Final2 | [`@ `from] :NotFinal2 | [`<> `from] :Middle2 - - def aug_eof - [`>/] :Start1 | [`/] :Middle1 - | [`> `eof] :Start2 | [`< `eof] :NotStart2 | [`$ `eof] :All2 - | [`% `eof] :Final2 | [`@ `eof] :NotFinal2 | [`<> `eof] :Middle2 - - def aug_gbl_error - [`>!] :Start1 | [`!] :Middle1 - | [`> `err] :Start2 | [`< `err] :NotStart2 | [`$ `err] :All2 - | [`% `err] :Final2 | [`@ `err] :NotFinal2 | [`<> `err] :Middle2 - - def aug_local_error - [`>^] :Start1 | [`<^] :NotStart1 | [`$^] :All1 - | [`%^] :Final1 | [`@^] :NotFinal1 | [`<>^] :Middle1 - | [`> `lerr] :Start2 | [`< `lerr] :NotStart2 | [`$ `lerr] :All2 - | [`% `lerr] :Final2 | [`@ `lerr] :NotFinal2 | [`<> `lerr] :Middle2 - - def factor_aug - [factor_aug aug_base action_ref] :ActionRef - | [factor_aug aug_base priority_aug] :PriorEmbed - | [factor_aug aug_base `( priority_name `, priority_aug `)] :NamedPriorEmbed - | [factor_aug aug_cond action_ref] :CondEmbed - | [factor_aug aug_cond `! action_ref] :NegCondEmbed - | [factor_aug aug_to_state action_ref] :ToStateAction - | [factor_aug aug_from_state action_ref] :FromStateAction - | [factor_aug aug_eof action_ref] :EofAction - | [factor_aug aug_gbl_error action_ref] :GblErrorAction - | [factor_aug aug_local_error action_ref] :LocalErrorDef - | [factor_aug aug_local_error `( error_name `, action_ref `)] :LocalErrorName - | [factor_rep] :Base - - def factor_rep - [factor_neg factor_rep_op_list] :Op - - def factor_rep_op_list - [factor_rep_op factor_rep_op_list] :Rec - | [] :Base - - def factor_rep_op - [`*] :Star - | [`**] :StarStar - | [`?] :Optional - | [`+] :Plus - | [`{ factor_rep_num `}] :ExactRep - | [`{ `, factor_rep_num `}] :MaxRep - | [`{ factor_rep_num `, `}] :MinRep - | [`{ LowRep: factor_rep_num `, HighRep: factor_rep_num `}] :RangeRep - - def factor_rep_num - [uint] :RepNum - - def factor_neg - [`! factor_neg] :Bang - | [`^ factor_neg] :Caret - | [factor] :Base - - def opt_max_arg - [`, action_ref] :Action - | [] :Empty - - def nfastar - [`:nfa] :Default - | [`:nfa_lazy] :Lazy - | [`:nfa_greedy] :Greedy - - def nfawrap - [`:nfa_wrap] :Default - | [`:nfa_wrap_lazy] :Lazy - | [`:nfa_wrap_greedy] :Greedy - - def colon_cond - [`:cond] :Cond - | [`:condstar] :CondStar - | [`:condplus] :CondPlus - - def factor - [alphabet_num] :AlphabetNum - | [word] :Word - | [string] :String - | [lex_sqopen_pos reg_or_data re_or_sqclose] :PosOrBlock - | [lex_sqopen_neg reg_or_data re_or_sqclose] :NegOrBlock - | [lex_regex_open regex re_close] :Regex - | [RL1: range_lit `.. RL2: range_lit] :Range - | [RL1: range_lit `../i RL2: range_lit] :RangeIndep - | [nfastar `( expression `, - Push: action_ref `, Pop: action_ref `, Init: action_ref `, Stay: action_ref `, - Repeat: action_ref `, Exit: action_ref `):] :Nfa - | [nfawrap `( expression `, - Push: action_ref `, Pop: action_ref `, Init: action_ref `, Stay: action_ref `, - Exit: action_ref `):] :NfaWrap - | [colon_cond `( expression `, - Init: action_ref `, Inc: action_ref `, Min: action_ref OptMax: opt_max_arg `):] :Cond - | [`( join `)] :Join - - def regex - [reg_item_rep_list] :List - - def reg_item_rep_list - [reg_item_rep_list reg_item_rep] :Rec - | [] :Base - - def reg_item_rep - [reg_item re_star] :Star - | [reg_item] :Base - - def reg_item - [re_sqopen_pos reg_or_data re_or_sqclose] :PosOrBlock - | [re_sqopen_neg reg_or_data re_or_sqclose] :NegOrBlock - | [re_dot] :Dot - | [re_char] :Char - - def reg_or_data - [reg_or_data reg_or_char] :Data - | [] :Base - - def reg_or_char - [re_or_char] :Char - | [Low: re_or_char re_or_dash High: re_or_char] :Range - - def range_lit - [string] :String - | [alphabet_num] :AN - - def alphabet_num - [uint] :Uint - | [`- uint] :Neg - | [hex] :Hex - - def lm_act - [`=> action_ref] :ActionRef - | [action_block] :ActionBlock - - def opt_lm_act - [lm_act] :Act - | [] :Empty - - def lm_stmt - [join opt_lm_act `;] :LmStmt commit - | [assignment] :Assignment - | [action_spec] :ActionSpec - - def lm_stmt_list - [lm_stmt_list lm_stmt] :Rec - | [lm_stmt] :Base - - def lm - [join] :Join - | [`|* lm_stmt_list `*|] :Lm - | [`:nfa `|* lm_stmt_list `*|] :LmNfa - - # - # Actions - # - def action_param - [word] :Word - - def action_param_list - [action_param_list `, action_param] :Rec - | [action_param] :Base - - def opt_action_param_list - [action_param_list] :List - | [] :Empty - - def action_params - [`( opt_action_param_list `)] :List - { - GblActionParams = true - } - - def action_spec - [`action word action_params action_block] :ActionSpecParams commit - { - # Track that this action has params so we can parse appropriately - # when reducing. - GblCurMachine->ActionParams->insert( $lhs.word, $lhs.word ) - - # Reset after parsing the block. - GblActionParams = false - } - | [`action word action_block] :ActionSpec commit - { - GblActionParams = false - } - - def def_name - [word] :Word - - # - # Machine Instantiations. - # - def assignment - [opt_export def_name `= join `;] :Assignment commit - - def instantiation - [opt_export def_name `:= lm `;] :Instantiation commit - - def nfa_expr - [nfa_expr `| term] :Union - | [term] :Base - - def nfa_round_spec - [Depth: uint `, Group: uint] :Spec - - def nfa_round_list - [nfa_round_list `, nfa_round_spec] :Recurse - | [nfa_round_spec] :Base - - def nfa_rounds - [`( nfa_round_list `)] :Rounds - - def nfa_union - [def_name `|= nfa_rounds nfa_expr `;] :NfaUnion commit - - def alphtype_type - [W1: word] :One - | [W1: word W2: word] :Two - - def opt_export - [`export] :Export - | [] :Base - - def write_arg - [word] :Word - - def machine_name - [`machine mn_word `;] :MachineName - - def open_inc - [`%%--{] :OpenInc - - def close_inc - [host::close_inc] :CloseInc - - def include_statement - [open_inc host::section* close_inc] :IncPost commit - - def open_imp - [`%%++{] :OpenImp - - def close_imp - [host::close_imp] :CloseImp - - def import_statement - [open_imp host::section* close_imp] :ImpPost commit - - def statement - [assignment] :Assignment - | [instantiation] :Instantiation - | [nfa_union] :NfaUnion - | [action_spec] :ActionSpec - | [`prepush action_block] :PrePush commit - | [`postpop action_block] :PostPop commit - | [`variable variable_name inline_expr_reparse] :Variable commit - | [`alphtype alphtype_type `;] :AlphType commit - | [`access inline_expr_reparse] :Access commit - | [`write Cmd: word ArgList: write_arg* `;] :Write commit - | [`getkey inline_expr_reparse] :GetKey commit - | [import_statement] :Import commit - | [include_statement] :Include commit - | [`nfaprepush action_block] :NfaPrePush commit - | [`nfapostpop action_block] :NfaPostPop commit - - def opt_machine_name - [machine_name] :MachineName - | [] :Empty - - def ragel_start - [opt_machine_name ign_want statement*] - | [opt_machine_name ign_ignore consume::tok*] -end - -str prepareLitString( Str: str ) -{ - # TODO: escape sequences - return suffix( prefix( Str, Str.length - 1 ), 1 ) -} - -bool isAbsolutePath( Path: str ) -{ - # TODO: implement this - return false -} - -namespace path - lex - token slash /'/'/ - token chars /[^\/]+/ - end - - def path - [Abs: slash? DirList: dir* File: chars] - - def dir - [chars slash] - - dir *concat_dir( Dir1: dir*, Dir2: dir* ) - { - for D: dir* in Dir1 { - if match D [] { - D = Dir2 - break - } - } - return Dir1 - } -end - -namespace host - token bom / 0xEF 0xBB 0xBF / - - def opt_bom - [bom] :Bom - | [] - - def section - [`%%{ ragel::opt_machine_name ragel::ign_want ragel::statement* ragel::`}%%] :MultiLine - | [`%%{ ragel::opt_machine_name ragel::ign_ignore consume::tok* consume::`}%%] :Consume - | [tok] :Token -end - -def start - [host::opt_bom SectionList: host::section*] - -list makeIncludePathChecks( CurFileName: str, IncFileName: str ) -{ - new L: list() - - parse CurPath: path::path[ CurFileName ] - parse IncPath: path::path[ IncFileName ] - - if match IncPath.Abs [slash] { - # Included file is absolute - L->push_tail( IncFileName ) - } - else { - # First add the location of current file - if match CurPath.DirList [] - L->push_tail( IncFileName ) - else { - # Current file path + Include Path + Include File - cons NewPath: path::path [ - CurPath.Abs - path::concat_dir( CurPath.DirList, IncPath.DirList ) - IncPath.File - ] - - L->push_tail( $NewPath ) - } - - # Next add include file locations. - for Path: str in GblIncludePaths { - parse IncPath: path::path[ CurFileName ] - L->push_tail( "[Path]/[IncFileName]" ) - } - } - return L -} - -stream ragelInclude( IncFileName: str, Machine: str ) -{ - if IncFileName - IncFileName = prepareLitString( IncFileName ) - - Checks: list - if IncFileName - Checks = makeIncludePathChecks( GblFileName, IncFileName ) - else { - Checks = new list() - Checks->push_tail( GblFileName ) - - } - - Stream: stream - OpenedName: str - for P: str in Checks { - Stream = open( P, "r" ) - if Stream { - OpenedName = P - break - } - } - - if !Stream { - print "error: could not open [IncFileName] - return nil - } - - # Default to the current machine if none is specified. - if !Machine - Machine = GblCurMachine->Name - - if isDuplicateInclude( GblCurMachine, IncFileName, Machine ) - return nil - - addIncludeItem( GblCurMachine, IncFileName, Machine ) - - saveGlobals() - - GblIncludeDepth = GblIncludeDepth + 1 - GblFileName = OpenedName - - # Set up the search and target machine names. Search is the machine we want - # to include and target is the machine we include to. - GblSearchMachine = Machine - GblTargetMachine = GblCurMachine->Name - - return Stream -} - -stream ragelImport( IncFileName: str ) -{ - if IncFileName - IncFileName = prepareLitString( IncFileName ) - - Checks: list - if IncFileName - Checks = makeIncludePathChecks( GblFileName, IncFileName ) - else { - Checks = new list() - Checks->push_tail( GblFileName ) - } - - Stream: stream - OpenedName: str - for P: str in Checks { - Stream = open( P, "r" ) - if Stream { - OpenedName = P - break - } - } - - if !Stream { - print "error: could not open [IncFileName] - return nil - } - - saveGlobals() - - GblFileName = OpenedName - GblImport = true - - return Stream -} diff --git a/cgil/ril.lm b/cgil/ril.lm deleted file mode 100644 index cde6ce93..00000000 --- a/cgil/ril.lm +++ /dev/null @@ -1,284 +0,0 @@ -namespace host - lex - rl NL / '\n' / - - token escape - / '@' any / - - literal `={ `}= `${ `}$ `@{ `}@ - - token host_any / any / - end - - def tok - [`${ StmtList: stmt* `}$] :Stmt - | [`={ Expr: expr `}=] :Expr - | [escape] :Escape - | [host_any] :Any -end - -lex - rl NL / '\n' / - - rl s_literal - / "'" ([^'\\\n] | '\\' (any | NL))* "'" / - - rl d_literal - / '"' ([^"\\] | NL | '\\' (any | NL))* '"' / - - rl c_comment - / '/*' ( any | NL )* :>> '*/' / - - rl cpp_comment - / '//' [^\n]* NL / - - literal `array `value `TRUE `FALSE - `while `switch `case - `if `else `offset `index - `goto `deref `entry `label `default - `host `cast `match `pat - - literal `uint `const - `s8 `s16 `s32 `s64 - `s128 `nil `export - `fallthrough `u `c `break `continue - - token ident - /( alpha | '_' ) ( alpha | digit | '_' )*/ - - token uint - / digit+ / - - token hex_number - / '0x' [0-9a-fA-F]+ / - - ignore - / c_comment | cpp_comment / - - token string - / s_literal | d_literal / - - ignore / ( [ \t] | NL )+ / - - literal `$ `{ `} `= `[ `] - `- `, `. `; `( `) `: - `? `* `+ `> `< `& - `~ `! `!= `== `<< `>> - `+= `&& `|| `<= `>= - `@ `-= `-> `={ `${ `@{ -end - -def embedded_host - [`host `( string `, uint `) `={ TL: host::tok* host::`}=] :Expr -| [`host `( string `, uint `) `${ TL: host::tok* host::`}$] :Stmt -| [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] :Bare - -def type - [ident] :Ident -| [ident ident] :Ident2 -| [`uint] :Uint -| [`s8] :S8 -| [`s16] :S16 -| [`s32] :S32 -| [`s64] :S64 -| [`s128] :S128 - -def expr_factor - [embedded_host] :EmbeddedHost -| [ident] :Ident -| [ident `[ expr `]] :ArraySub -| [ident `[ expr `] `. Field: ident] :ArraySubField -| [`offset `( ident `, expr `)] :Offset -| [`deref `( ident `, expr `)] :Deref -| [number] :Number -| [`TRUE] :True -| [`FALSE] :False -| [`nil] :Nil -| [hex_number] :HexNumber -| [string] :String -| [embedded_host `-> expr_factor] :Access -| [`( expr `)] :Paren -| [`cast `( type `) expr_factor] :Cast - -def lvalue - [embedded_host] -| [ident] -| [ident `[ expr `]] -| [ident `[ expr `] `. ident] -| [embedded_host `-> lvalue] - -def expr_factor_op - [`! expr_factor_op] -| [`~ expr_factor_op] -| [expr_factor] - -def expr_bitwise - [expr_bitwise `& expr_factor_op] -| [expr_factor_op] - -def expr_mult - [expr_mult `* expr_bitwise] -| [expr_bitwise] - -def add_op - [`+] | [`-] - -def expr_add - [expr_add add_op expr_mult] -| [expr_mult] - -def shift_op - [`<<] | [`>>] - -def expr_shift - [expr_shift shift_op expr_add] -| [expr_add] - -def test_op - [`<] | [`>] | [`<=] | [`>=] | - [`==] | [`!=] | [`&&] | [`||] - -def expr_test - [expr_test test_op expr_shift] -| [expr_shift] - -def expr - [expr_test] - -def sint - [uint] -| [`- uint] - -def number - [`u `( uint `)] :Unsigned -| [`c `( uint `)] :Char -| [sint] :Number - -def comma_num - [`, number] - -def num_list - [number comma_num*] -| [] - -def static_array - [`array type ident `( number `, number `) `= `{ num_list `} `;] - -def static_value - [`value type ident `= number `;] - -def break_label - [ident `: `:] - -def while_stmt - [break_label? `while `( expr `) stmt] - -def else_if_clause - [`else `if `( expr `) stmt] - -def else_clause - [`else stmt] - -def if_stmt [ - `if `( expr `) stmt - else_if_clause* else_clause? -] - -def match_stmt - [`match `( E: expr `) `{ P: pat_block* D: default_block? `}] - -def pat_block - [`pat expr `{ stmt* `}] - -def switch_stmt - [`switch `( expr `) `{ stmt* `}] - -def case_block - [`case expr `{ stmt* `}] - -def default_block - [`default `{ stmt* `}] - -def case_label - [`case expr `:] - -def goto_label - [ident `:] - -def opt_init - [`= expr] -| [] - -def opt_ptr - [`*] -| [] - -def opt_const - [`const] -| [] - -def declaration - [opt_const type ident opt_init `;] - -def index_stmt - [`index type ident opt_init`;] - -def export_stmt - [`export type ident number `;] - -def goto_stmt - Id: int - [`goto ident `;] - -def fallthrough - [`fallthrough `;] - -def break_stmt - [`break ident? `;] - -def continue_stmt - [`continue ident? `;] - -def block - [`{ StmtList: stmt* `}] - -def expr_stmt - [expr `;] - -def assign_op - [`=] | [`+=] | [`-=] - -def assign_stmt - [LValue: lvalue assign_op expr `;] - -def stmt - [embedded_host] -| [static_array] -| [static_value] -| [declaration] -| [index_stmt] -| [export_stmt] -| [assign_stmt] -| [expr_stmt] -| [while_stmt] -| [if_stmt] -| [match_stmt] -| [switch_stmt] -| [case_block] -| [default_block] -| [case_label] -| [goto_label] -| [goto_stmt] -| [fallthrough] -| [break_stmt] -| [continue_stmt] -| [block] - -token bom / 0xEF 0xBB 0xBF / - -def opt_bom - [bom] :Bom -| [] - -def start - [opt_bom stmt*] diff --git a/cgil/rlhc-c.lm b/cgil/rlhc-c.lm deleted file mode 100644 index dfc24d1d..00000000 --- a/cgil/rlhc-c.lm +++ /dev/null @@ -1,462 +0,0 @@ -include 'ril.lm' - -namespace c_out - - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def c_out - [_IN_ _EX_ item*] -end - -namespace c_gen - global _: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case Stmt { - << "{ - " [stmt_list( StmtList )] - "} - } - case Expr { - << "([expr( Expr )])" - } - case Escape { - Str: str = $escape - << "[Str.suffix( 1 )]" - } - default { - << [Tok] - } - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case Expr - { - << "([tok_list( TL )])" - } - case Stmt - { - << "{ - " [tok_list( TL )] - "} - } - case Bare - { - << [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - << [embedded_host( EH )] - } - case Paren - { - << "([expr( expr )])" - } - case ArraySub - { - << "[ident]\[[expr( expr )]\]" - } - case ArraySubField - { - << "[ident]\[[expr( expr )]\].[Field]" - } - case Offset - { - << "[ident] + [expr( expr )]" - } - case Deref - { - << "(*( [expr(expr)] )) - } - case [`TRUE] - { - << "1" - } - case [`FALSE] - { - << "1" - } - case [N: `nil] - { - << "0" - } - case [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast `( T: type `) F: expr_factor] - { - << "( [type( T )] ) [expr_factor( F )]" - } - default { - # Catches cases not specified - << [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - << [embedded_host( EH )] - } - case [ident O: `[ TL: expr C: `]] - { - << [ident O expr( TL ) C] - } - case [I: ident `[ E: expr `] `. F: ident] - { - << "[I]\[[ expr( E )]\].[F] - } - case [E1: embedded_host `-> E2: lvalue] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - lvalue( E2 ) - } - default { - # Catches cases not specified - << [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - << ['!' expr_factor_op( _expr_factor_op )] - } - case [T: `~ expr_factor_op] - { - << ['~' expr_factor_op( _expr_factor_op )] - } - case [expr_factor] - { - << [expr_factor( expr_factor )] - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - << [expr_bitwise( _expr_bitwise ) A expr_factor_op( expr_factor_op )] - } - case [expr_factor_op] - { - << [expr_factor_op( expr_factor_op )] - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - << [expr_mult( _expr_mult ) T expr_bitwise( expr_bitwise )] - } - case [expr_bitwise] - { - << [expr_bitwise( expr_bitwise )] - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - << [expr_add( _expr_add ) Op expr_mult( expr_mult )] - } - case [expr_mult] - { - << [expr_mult( expr_mult )] - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - << [expr_shift( _expr_shift ) Op expr_add( expr_add )] - } - case [expr_add] - { - << [expr_add( expr_add )] - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - << [expr_test( _expr_test ) Op expr_shift( expr_shift )] - } - case [expr_shift] - { - << [expr_shift( expr_shift )] - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case S8 - << ['signed char '] - case S16 - << ['short '] - case S32 - << ['int '] - case S64 - << ['long '] - case S128 - << ['long long '] - case "uint" - << ['unsigned int '] - default - << [Type] - } - - void number( Number: number ) - { - switch Number - case Unsigned - << "[uint]u" - default - << [Number] - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - << "[number( Number )], " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - << [embedded_host( EH )] - } - case [A: static_array] { - << "static const [type(A.type)] " - "[A.ident] \[\] = { [num_list(A.num_list)] }; - } - case [V: static_value] { - << "static const [V.type] [V.ident] = [V.number]; - } - case [ - `if `( IfExpr: expr `) - IfStmt: stmt - ElseIfClauseList: else_if_clause* - ElseClauseOpt: else_clause? - ] { - << "if ( [expr(IfExpr)] ) - << " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - [`else `if `( ElseIfExpr: expr `) ElseIfStmt: stmt] - - << "else if ( [expr(ElseIfExpr)] ) - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - << "else - << " [stmt(ElseStmt)] - } - } - case [`while `( WhileExpr: expr `) WhileStmt: stmt] { - << "while ( [expr(WhileExpr)] ) - " [stmt(WhileStmt)] - } - case [M: match_stmt] { - << "switch ( [expr(M.E)] ) { - - for PB: pat_block in repeat( M.P ) { - << "case [expr( PB.expr )]: - "[stmt_list( PB._repeat_stmt )] - "break; - } - - if match M.D [D: default_block] { - << "default: - "[stmt_list( D._repeat_stmt )] - "break; - } - - << "} - } - case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { - << "switch ( [expr(SwitchExpr)] ) { - " [stmt_list(StmtList)] - "} - } - case [ES: expr_stmt] { - << "[expr(ES.expr)]; - } - case [B: block] { - << "{ - " [stmt_list(B.StmtList)] - "} - } - case [ - OptConst: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - << "[OptConst] [type(Type)] [Ident]" - - if match OptInit [`= Init: expr] { - << " = [expr(Init)] - } - - << "; - } - case [Export: export_stmt] - { - << "#define [Export.ident] [number(Export.number)] - } - case [fallthrough] - { - # Nothing needed here. - # C falls through by default. - } - case [Index: index_stmt] - { - << "const [type(Index.type)] *[Index.ident] - - if match Index.opt_init [E: `= expr] { - << [E expr(Index.opt_init.expr)] - } - - << "; - } - case [CB: case_block] - { - << "case [expr( CB.expr )]: - "[stmt_list( CB._repeat_stmt )] - "break; - } - case [DB: default_block] - { - << "default: - "[stmt_list( DB._repeat_stmt )] - "break; - } - case [CL: case_label] - { - << "case [expr( CL.expr )]: - } - case [AS: assign_stmt] - { - << "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - << [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - _ = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - CO: c_out::c_out = _->finish() - - if CO { - send Output - [CO] - } - else { - send stderr - "failed to parse output: [_->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - c_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-crack.lm b/cgil/rlhc-crack.lm deleted file mode 100644 index bebe7cd5..00000000 --- a/cgil/rlhc-crack.lm +++ /dev/null @@ -1,536 +0,0 @@ -include 'ril.lm' - -namespace crack_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" | - "`" ( [^`\\] | '\\' any ) * "`" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def crack_out - [_IN_ _EX_ item*] -end - -namespace crack_gen - - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] { - send Parser - "if ( 1 ) { - " [stmt_list( StmtList )] - "} - } - case [host::`={ Expr: expr host::`}=] { - send Parser - "([expr( Expr )])" - } - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser - [Tok] - } - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "if ( 1 ) { - " [tok_list( TL )] - "} - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [`( E: expr `)] - { - send Parser - "([expr(E)])" - } - case [I: ident `[ E: expr `]] - { - send Parser - "[I]\[[expr( E )]\]" - } - case [`offset `( ident `, expr `)] - { - send Parser - [expr( ExprFactor.expr )] - } - case [`deref `( ident `, expr `)] - { - send Parser - [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] - } - case [`TRUE] - { - send Parser "1" - } - case [`FALSE] - { - send Parser "1" - } - case [N: `nil] - { - send Parser "0" - } - case [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast `( T: type `) F: expr_factor] - { - send Parser - "[type( T )] ( [expr_factor( F )] )" - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case [E1: embedded_host `-> E2: lvalue] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - lvalue( E2 ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['int16 '] - case "s16" - send Parser ['int16 '] - case "s32" - send Parser ['int32 '] - case "s64" - send Parser ['int64 '] - case "s128" - send Parser ['long long '] - case "uint" - send Parser ['uint32 '] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]u" - case [`c `( uint `) ] - send Parser "[Number.uint]" - default - send Parser [Number] - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )], " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [Array: static_array] { - send Parser - "const array\[[type(Array.type)]\] " - "[Array.ident] = \[ [num_list(Array.num_list)] \]; - } - case [Value: static_value] { - send Parser - "const [Value.type] [Value.ident] = [Value.number]; - } - case [ - `if `( IfExpr: expr `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) - " [flow_stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "else if ( [expr(ElseIfExpr)] ) - " [flow_stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [flow_stmt(ElseStmt)] - } - } - case [`while `( WhileExpr: expr `) WhileStmt: stmt] { - send Parser - "while ( [expr(WhileExpr)] ) - " [flow_stmt(WhileStmt)] - } - case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { - - require StmtList - [`case E1: expr `{ Inner: stmt* `} Rest: stmt*] - - send Parser - "if ( [expr(SwitchExpr)] == [expr(E1)] ) { - " [stmt_list(Inner)] - "} - - for S: stmt in repeat(Rest) { - switch S - case [`case E1: expr `{ Inner: stmt* `}] - { - send Parser - "else if ( [expr(SwitchExpr)] == [expr(E1)] ) { - " [stmt_list(Inner)] - "} - } - case - [`default `{ Inner: stmt* `}] - { - send Parser - "else { - " [stmt_list(Inner)] - "} - } - } - - send Parser - "; - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [`{ TL: stmt* `}] { - send Parser - "if ( 1 ) { - " [stmt_list(TL)] - "} - } - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - [TypeList type(Type) Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser - [Semi] - } - case [Export: export_stmt] - { - send Parser - "#define [Export.ident] [number(Export.number)] - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "int [Index.ident] - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - else { - send Parser - " = 0" - } - - send Parser "; - } - case [case_block] - { - send Parser - "case [expr( Stmt.case_block.expr )]: - "[stmt_list( Stmt.case_block._repeat_stmt )] - "break; - } - case [default_block] - { - send Parser - "default: - "[stmt_list( Stmt.default_block._repeat_stmt )] - "break; - } - case [case_label] - { - send Parser - "case [expr( Stmt.case_label.expr )]: - } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void flow_stmt( Stmt: stmt ) - { - switch Stmt - case [`{ TL: stmt* `}] { - send Parser - "{ - " [stmt_list(TL)] - "} - } - default { - stmt( Stmt ) - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - CO: crack_out::crack_out = Parser->finish() - - if CO { - send Output - [CO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - crack_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-csharp.lm b/cgil/rlhc-csharp.lm deleted file mode 100644 index 078157c5..00000000 --- a/cgil/rlhc-csharp.lm +++ /dev/null @@ -1,480 +0,0 @@ -include 'ril.lm' - -namespace csharp_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def csharp_out - [_IN_ _EX_ item*] -end - -namespace csharp_gen - - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] - send Parser "{[stmt_list( StmtList )]}" - case [host::`={ Expr: expr host::`}=] - send Parser "([expr( Expr )])" - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser [Tok] - } - } - } - - void embedded_host( EH: embedded_host ) - { - switch EH - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "{[tok_list( TL )]} - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case - [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [O:`( TL: expr C: `)] - { - send Parser - [O expr(TL) C] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case ['offset' '(' ident ',' expr ')'] - { - send Parser - [expr( ExprFactor.expr )] - } - case ['deref' '(' ident ',' expr ')'] - { - send Parser - [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] - } - case [T: `TRUE] - { - T.data = 'true' - send Parser [T] - } - case [F: `FALSE] - { - F.data = 'false' - send Parser [F] - } - case [N: `nil] - { - N.data = '0' - send Parser [N] - } - case [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast Open: `( Type: type Close: `) expr_factor] - { - send Parser [Open] - type( Type ) - send Parser [Close] - expr_factor( ExprFactor._expr_factor ) - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case [E1: embedded_host `-> E2: lvalue] - { - embedded_host( E1 ) - lvalue( E2 ) - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['sbyte '] - case "s16" - send Parser ['short '] - case "s32" - send Parser ['int '] - case "s64" - send Parser ['long '] - case "s128" - send Parser ['long long '] - case "uint" - send Parser ['uint '] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`c `( uint `) ] { - Str: str = $Number.uint - send Parser "'\\u[sprintf( "%04x", Str.atoi() )]'" - } - case [`u `( uint `) ] { - send Parser [$Number.uint] - } - default { - send Parser [$Number.sint] - } - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )], " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - send Parser - "static readonly [type(A.type)] \[\]" - "[A.ident] = { [num_list( A.num_list )] }; - } - case [V: static_value] { - send Parser - "static readonly [V.type] [V.ident] = [V.number]; - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) - " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "else if ( [expr(ElseIfExpr)] ) - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [stmt(ElseStmt)] - } - } - case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { - send Parser - "while ( [expr(WhileExpr)] ) - " [stmt(WhileStmt)] - } - case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { - send Parser - "switch ( [expr(SwitchExpr)] ) { - " [stmt_list(StmtList)] - "} - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - [L stmt_list(TL) R] - } - case [ - TypeList: opt_const Type: type Ident: ident - OptInit: opt_init Semi: `; - ] - { - send Parser - [TypeList type(Type) Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser - [Semi] - } - case [Export: export_stmt] - { - send Parser - "#define [Export.ident] [Export.number] - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "int [Index.ident]" - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - - send Parser ";" - } - case [case_block] - { - send Parser - "case [expr( Stmt.case_block.expr )]: - "[stmt_list( Stmt.case_block._repeat_stmt )] - "break; - } - case [default_block] - { - send Parser - "default: - "[stmt_list( Stmt.default_block._repeat_stmt )] - "break; - } - case [case_label] - { - send Parser - "case [expr( Stmt.case_label.expr )]: - } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - CSO: csharp_out::csharp_out = Parser->finish() - - if CSO { - send Output - [CSO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - csharp_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-d.lm b/cgil/rlhc-d.lm deleted file mode 100644 index 2a047e68..00000000 --- a/cgil/rlhc-d.lm +++ /dev/null @@ -1,511 +0,0 @@ -include 'ril.lm' - -namespace d_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def d_out - [_IN_ _EX_ item*] -end - -namespace d_gen - - global Parser: parser - - global HasDefault: list = new list() - - void pushHasDef( H: int ) - { - HasDefault->push( H ) - } - - int popHasDef() - { - return HasDefault->pop() - } - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] - send Parser "{[stmt_list( StmtList )]}" - case [host::`={ Expr: expr host::`}=] - send Parser "([expr( Expr )])" - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser [Tok] - } - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "{[tok_list( TL )]} - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host(EH)] - } - case - [O:`( TL: expr C: `)] - { - send Parser - [O expr( TL ) C] - } - case - [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr(TL) C] - } - case - [`offset `( ident `, expr `)] - { - send Parser - "& [ExprFactor.ident] \[ [expr(ExprFactor.expr)] \] - } - case - [`deref `( ident `, expr `)] - { - send Parser - "(*( [expr(ExprFactor.expr)] )) - } - case - [T: `TRUE] - { - T.data = '1' - send Parser [T] - } - case - [F: `FALSE] - { - F.data = '0' - send Parser [F] - } - case - [N: `nil] - { - N.data = 'null' - send Parser [N] - } - case - [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast Open: `( Type: type Close: `) expr_factor] - { - send Parser ['cast' Open] - type( Type ) - send Parser [Close] - expr_factor( ExprFactor._expr_factor ) - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host(EH)] - } - case - [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr(TL) C] - } - case [E1: embedded_host `-> E2: lvalue] - { - embedded_host( E1 ) - lvalue( E2 ) - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['byte '] - case "s16" - send Parser ['short '] - case "s32" - send Parser ['int '] - case "s64" - send Parser ['long '] - case "s128" - send Parser ['long long '] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]u" - default - send Parser [Number] - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )], " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host(EH)] - } - case [A: static_array] { - send Parser - "static const [type(A.type)]\[\] " - "[A.ident] = \[ [num_list( A.num_list )] \]; - } - case [V: static_value] { - send Parser - "static const [V.type] [V.ident] = [V.number]; - } - case [ - `if `( IfExpr: expr `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) - " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - [`else `if `( ElseIfExpr: expr `) ElseIfStmt: stmt] - - send Parser - "else if ( [expr(ElseIfExpr)] ) - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [stmt(ElseStmt)] - } - } - case [`while `( WhileExpr: expr `) WhileStmt: stmt] { - send Parser - "while ( [expr(WhileExpr)] ) - " [stmt(WhileStmt)] - } - case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { - pushHasDef( 0 ) - - send Parser - "switch ( [expr(SwitchExpr)] ) { - " [stmt_list(StmtList)] - - if ( popHasDef() == 0 ) { - send Parser - " default: break; - } - - send Parser - "} - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr( ExprExpr ) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - [L stmt_list( TL ) R] - } - case [ - OptConst: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - if match OptConst ['const'] { - send Parser - "const( [type( Type )] ) - } - else { - type( Type ) - } - - send Parser [Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser [Semi] - } - case [case_block] - { - send Parser - "case [expr( Stmt.case_block.expr )]: - "[stmt_list( Stmt.case_block._repeat_stmt )] - "break; - } - case [default_block] - { - send Parser - "default: - "[stmt_list( Stmt.default_block._repeat_stmt )] - "break; - - popHasDef() - pushHasDef( 1 ) - } - case [case_label] - { - send Parser - "case [expr( Stmt.case_label.expr )]: - } - case [export_stmt] - { - send Parser - "static const [type(Stmt.export_stmt.type)] " - "[Stmt.export_stmt.ident] = [number(Stmt.export_stmt.number)]; - } - case ['fallthrough' ';'] - { - send Parser "goto case;" - } - case [Index: index_stmt] - { - send Parser - "const([type(Index.type)]) *[Index.ident]" - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - - send Parser "; - } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - DO: d_out::d_out = Parser->finish() - - if DO { - send Output - [DO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - d_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-go.lm b/cgil/rlhc-go.lm deleted file mode 100644 index b8353d10..00000000 --- a/cgil/rlhc-go.lm +++ /dev/null @@ -1,454 +0,0 @@ -include 'ril.lm' - -namespace out_go - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def out_go - [_IN_ _EX_ item*] -end - - - -namespace go_gen - - global _: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case Stmt { - << "{ - " [stmt_list( StmtList )] - "} - } - case Expr { - << "([expr( Expr )])" - } - case Escape { - Str: str = $Tok - << "[Str.suffix( 1 )]" - } - default { - << [Tok] - } - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case Expr - { - << ['(' tok_list( TL ) ')'] - } - case Stmt - { - << ['{' tok_list( TL ) '}\n'] - } - case Bare - { - << [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case EmbeddedHost - { - << [embedded_host(embedded_host)] - } - case Paren - { - << ['( ' expr(expr) ' )'] - } - case ArraySub - { - << [ident '[ ' expr( expr ) ' ]'] - } - case Offset - { - << "int([expr(expr )]) - } - case Deref - { - << [ident '[ ' expr( expr ) ' ]' ] - } - case True - { - << "true" - } - case False - { - << "false" - } - case Nil - { - << "0" - } - case Access - { - embedded_host(embedded_host) - expr_factor(_expr_factor) - } - case Cast - { - << [type(type) '( ' expr_factor(_expr_factor) ' )' ] - } - default { - # Catches cases not specified - << [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - << [embedded_host(EH)] - } - case [ident `[ TL: expr `]] - { - << [ident '[' expr( TL ) ']'] - } - case [E1: embedded_host `-> E2: lvalue] - { - embedded_host( E1 ) - lvalue( E2 ) - } - default { - # Catches cases not specified - << [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - << ['! ' expr_factor_op( _expr_factor_op )] - } - case [T: `~ expr_factor_op] - { - << ['^ ' expr_factor_op( _expr_factor_op )] - } - case [expr_factor] - { - << [expr_factor( ExprFactorOp.expr_factor )] - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - << [expr_bitwise( _expr_bitwise ) ' & ' expr_factor_op( expr_factor_op )] - } - case [expr_factor_op] - { - << [expr_factor_op( ExprBitwise.expr_factor_op )] - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - << [expr_mult( _expr_mult ) ' * ' expr_bitwise( expr_bitwise )] - } - case [expr_bitwise] - { - << [expr_bitwise( expr_bitwise )] - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - << [expr_add( _expr_add ) ' ' Op ' ' expr_mult( expr_mult )] - } - case [expr_mult] - { - << [expr_mult( ExprAdd.expr_mult )] - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - << [expr_shift( _expr_shift ) ' ' Op ' ' expr_add( expr_add )] - } - case [expr_add] - { - << [expr_add( expr_add )] - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - << [expr_test( _expr_test ) ' ' Op ' ' expr_shift( expr_shift )] - } - case [expr_shift] - { - << [expr_shift( ExprTest.expr_shift )] - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case S8 - << ['int8'] - case S16 - << ['int16'] - case S32 - << ['int32'] - case S64 - << ['int64'] - case S128 - << ['long long'] - default - << [Type] - } - - void number( Number: number ) - { - switch Number - case Unsigned - << [uint] - default - << [Number] - } - - void num_list( NumList: num_list ) - { - number( NumList.number ) - for CommaNum: comma_num in NumList { - << [', ' number( CommaNum.number )] - } - } - - # Go must have {} around if and for statements. We strip any blocks from - # these statments and force our own. - void strip_block_stmt( Stmt: stmt ) - { - if match Stmt [`{ StmtList: stmt* `}] - stmt_list(StmtList) - else - stmt( Stmt ) - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - << [embedded_host(EH)] - } - case [A: static_array] { - << "var [A.ident] = \[\] " - "[type(A.type)] { [num_list(A.num_list)] } - } - case [V: static_value] { - << "var [V.ident] [type(V.type)] = [V.number] - "var _ = [V.ident] - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ] { - # if-statements with only the if clause can go out as an if. - << "if [expr(IfExpr)] { - " [strip_block_stmt(IfStmt)] - "} - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - # If the if-statement has more than just an if clause it goes out as a switch. - << "if [expr( IfExpr )] { - " [strip_block_stmt( IfStmt )] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - << "} else if [expr(ElseIfExpr)] { - " [strip_block_stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - << "} else { - " [strip_block_stmt(ElseStmt)] - } - - << "} - } - case ["while ( TRUE )" WhileStmt: stmt] { - << "for { - " [strip_block_stmt(WhileStmt)] - "} - } - case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { - << "for [expr(WhileExpr)] { - " [strip_block_stmt(WhileStmt)] - "} - } - case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { - << "switch [expr(SwitchExpr)] { - " [stmt_list(StmtList)] - "} - } - case [ExprExpr: expr `;] { - << [expr(ExprExpr) ';'] - } - case [B: block] { - << "{ - " [stmt_list(B.StmtList)] - "} - } - case [D: declaration] - { - << "var [D.ident] [type(D.type)]" - - if match D.opt_init ['=' Expr: expr] { - << " = [expr(Expr)]" - } - - << ['\n'] - } - case [ES: export_stmt] - { - << "#define [ES.ident] [number(ES.number)] - } - case [fallthrough] - { - << "fallthrough - } - case [Index: index_stmt] - { - << "var [Index.ident] int" - - if match Index.opt_init ['=' Expr: expr] { - << " = [expr(Expr)]" - } - - << ['\n'] - } - case [CB: case_block] - { - << "case [expr( CB.expr )]: - " [stmt_list( CB._repeat_stmt )] - } - case [DB: default_block] - { - << "default: - " [stmt_list( DB._repeat_stmt )] - } - case [CL: case_label] - { - << "case [expr( CL.expr )]: - } - case [AS: assign_stmt] - { - << "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - << "[Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - _ = new parser() - Input: _input = _->gets() - Input->auto_trim(true) - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - GO: out_go::out_go = _->finish() - - if GO { - send Output - [GO] - } - else { - send stderr - "failed to parse output: [_->error] - } - - } -end - -void trans( Output: stream, Start: start ) -{ - go_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-java.lm b/cgil/rlhc-java.lm deleted file mode 100644 index a458369f..00000000 --- a/cgil/rlhc-java.lm +++ /dev/null @@ -1,504 +0,0 @@ -include 'ril.lm' - -namespace java_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def java_out - [_IN_ _EX_ item*] -end - -namespace java_gen - - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] { - send Parser - "{ - " [stmt_list( StmtList )] - "} - } - case [host::`={ Expr: expr host::`}=] - send Parser "([expr( Expr )])" - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser [Tok] - } - } - } - - void embedded_host( EH: embedded_host ) - { - switch EH - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "{ - " [tok_list( TL )] - "} - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case - [O:`( TL: expr C: `)] - { - send Parser - [O expr(TL) C] - } - case - [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case - ['offset' '(' ident ',' expr ')'] - { - send Parser - [expr( ExprFactor.expr )] - } - case - ['deref' '(' ident ',' expr ')'] - { - send Parser - [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] - } - case - [T: `TRUE] - { - T.data = 'true' - send Parser [T] - } - case - [F: `FALSE] - { - F.data = 'false' - send Parser [F] - } - case - [N: `nil] - { - N.data = '0' - send Parser [N] - } - case - [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast Open: `( Type: type Close: `) expr_factor] - { - send Parser [Open] - type( Type ) - send Parser [Close] - expr_factor( ExprFactor._expr_factor ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case - [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case [E1: embedded_host `-> E2: lvalue] - { - embedded_host( E1 ) - lvalue( E2 ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['byte '] - case "s16" - send Parser ['short '] - case "s32" - send Parser ['int '] - case "s64" - send Parser ['long '] - case "s128" - send Parser ['long long '] - case "uint" - send Parser ['int '] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]" - default - send Parser [Number] - } - - void java_num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )], " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - send Parser - "private static [type(A.type)] " - "[A.ident] \[\] = { [java_num_list(A.num_list)] }; - } - case [V: static_value] { - send Parser - "private static [V.type] [V.ident] = [V.number]; - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) - " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "else if ( [expr(ElseIfExpr)] ) - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [stmt(ElseStmt)] - } - } - case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { - send Parser - "while ( [expr(WhileExpr)] ) - " [stmt(WhileStmt)] - } - case [BL: break_label? 'while' '(' WhileExpr: expr ')' '{' StmtList: stmt* '}' ] { - if match BL [bl: break_label] - send Parser "[bl.ident]: " - - send Parser - "while ( [expr(WhileExpr)] ) { - " [stmt_list(StmtList)] - "} - } - case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { - send Parser - "switch ( [expr(SwitchExpr)] ) { - " [stmt_list(StmtList)] - "} - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - "{ - " [stmt_list(TL)] - "} - } - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - [TypeList type(Type) Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser - [Semi] - } - case [Export: export_stmt] - { - send Parser - "static final char [Export.ident] = [number(Export.number)]; - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "int [Index.ident]" - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - send Parser "; - - } - case [case_block] - { - send Parser - "case [expr( Stmt.case_block.expr )]: - "[stmt_list( Stmt.case_block._repeat_stmt )] - "break; - } - case [default_block] - { - send Parser - "default: - "[stmt_list( Stmt.default_block._repeat_stmt )] - "break; - } - case [goto_label] { } - case [G: goto_stmt] { } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - JO: java_out::java_out = Parser->finish() - - if JO { - send Output - [JO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - java_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-js.lm b/cgil/rlhc-js.lm deleted file mode 100644 index 29a61346..00000000 --- a/cgil/rlhc-js.lm +++ /dev/null @@ -1,504 +0,0 @@ -include 'ril.lm' - -namespace js_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def js_out - [_IN_ _EX_ item*] -end - -namespace js_gen - - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] { - send Parser - "{ - " [stmt_list( StmtList )] - "} - } - case [host::`={ Expr: expr host::`}=] { - send Parser - "([expr( Expr )])" - } - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser - [Tok] - } - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "{ - " [tok_list( TL )] - "} - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [O:`( TL: expr C: `)] - { - send Parser - [O expr(TL) C] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case ['offset' '(' ident ',' expr ')'] - { - send Parser - [expr( ExprFactor.expr )] - } - case ['deref' '(' ident ',' expr ')'] - { - send Parser [ExprFactor.ident] - if $ExprFactor.ident == 'data' - send Parser ['.charCodeAt(' expr( ExprFactor.expr ) ')'] - else - send Parser ['[' expr( ExprFactor.expr ) ']'] - } - case [T: `TRUE] - { - T.data = 'true' - send Parser [T] - } - case [F: `FALSE] - { - F.data = 'false' - send Parser [F] - } - case [N: `nil] - { - N.data = '-1' - send Parser [N] - } - case [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast Open: `( type Close: `) expr_factor] - { - expr_factor( ExprFactor._expr_factor ) - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case [I: ident `[ E: expr `] `. F: ident] - { - send Parser - "[I]\[[ expr( E )]\].[F] - } - case [E1: embedded_host `-> E2: lvalue] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - lvalue( E2 ) - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test_op( Op: test_op ) - { - switch Op - case [ `== ] - send Parser '===' - case [ `!= ] - send Parser '!==' - default - send Parser [Op] - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - expr_test_op( ExprTest.test_op ) - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser [Number.uint] - default - send Parser [Number] - } - - void type( Type: type ) - { - switch Type - case 'u8' - send Parser 'Uint8' - case 'u16' - send Parser 'Uint16' - case 'u32' - send Parser 'Uint32' - case 's8' - send Parser 'Int8' - case 's16' - send Parser 'Int16' - case 's32' - send Parser 'Int32' - default - send Parser 'Float64' - } - - void num_list( NumList: num_list ) - { - number( NumList.number ) - for CommaNum: comma_num in NumList { - send Parser [', '] - number( CommaNum.number ) - } - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - send Parser - "var [A.ident] = new [type(A.type)]Array(\[ [num_list(A.num_list)]\]); - } - case [V: static_value] { - send Parser - "var [V.ident] = [V.number]; - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) - " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "else if ( [expr(ElseIfExpr)] ) - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [stmt(ElseStmt)] - } - } - case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { - send Parser - "while ( [expr(WhileExpr)] ) - " [stmt(WhileStmt)] - } - case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { - send Parser - "switch ( [expr(SwitchExpr)] ) { - " [stmt_list(StmtList)] - "} - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - "{ - " [stmt_list(TL)] - "} - } - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - "var [Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser - [Semi] - } - case [Export: export_stmt] - { - send Parser - "var [Export.ident] = [number(Export.number)]; - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "var [Index.ident]" - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - - send Parser - "; - } - case [case_block] - { - send Parser - "case [expr( Stmt.case_block.expr )]: - "[stmt_list( Stmt.case_block._repeat_stmt )] - "break; - } - case [default_block] - { - send Parser - "default: - "[stmt_list( Stmt.default_block._repeat_stmt )] - "break; - } - case [goto_label] { } - case [G: goto_stmt] { } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - send Parser - "'use strict'; - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - CO: js_out::js_out = Parser->finish() - - if CO { - send Output - [CO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - js_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-julia.lm b/cgil/rlhc-julia.lm deleted file mode 100644 index 72108994..00000000 --- a/cgil/rlhc-julia.lm +++ /dev/null @@ -1,561 +0,0 @@ -include 'ril.lm' - -namespace julia_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - literal `function `end `while `if `else `elseif - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def kw - [`function _IN_] - | [`while _IN_] - | [`if _IN_] - | [_EX_ `elseif _IN_] - | [_EX_ `else _IN_] - | [_EX_ `end] - - def item - [comment] - | [kw] - | [id] - | [number] - | [symbol] - | [string] - | [`{ _IN_ item* _EX_ `} ] - - def julia_out - [_IN_ _EX_ item*] -end - -namespace julia_gen - - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] { - send Parser - "begin - " [stmt_list( StmtList )] - "end - } - case [host::`={ Expr: expr host::`}=] { - send Parser - "([expr( Expr )])" - } - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser - [Tok] - } - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - [tok_list( TL )] - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [`( E: expr `)] - { - send Parser - "([expr(E)])" - } - case [I: ident `[ E: expr `]] - { - send Parser - "[I]\[1+([expr( E )])\]" - } - case [`offset `( ident `, expr `)] - { - send Parser - [expr( ExprFactor.expr )] - } - case [`deref `( I: ident `, E: expr `)] - { - send Parser - "[I]\[1+([ expr( E ) ])\]" - } - case [`TRUE] - { - send Parser "true" - } - case [`FALSE] - { - send Parser "false" - } - case [N: `nil] - { - send Parser "0" - } - case [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast `( T: type `) F: expr_factor] - { - send Parser - "convert([type( T )], [expr_factor( F )] )" - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[1+(' expr(E) ')]'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [I: ident `[ E: expr `]] - { - send Parser - "[I]\[1+([expr( E )])\]" - } - case [E1: embedded_host `-> E2: lvalue] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - lvalue( E2 ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[1+(' expr(E) ')]'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['Int8'] - case "u8" - send Parser ['UInt8'] - case "s16" - send Parser ['Int16'] - case "s32" - send Parser ['Int32'] - case "s64" - send Parser ['Int64'] - case "s128" - send Parser ['Int128'] - case "uint" - send Parser ['UInt'] - case "int" - send Parser ['Int'] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]u" - default - send Parser [Number] - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )], " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - send Parser - "const [A.ident] = [type(A.type)]\[[num_list(A.num_list)]\] - } - case [V: static_value] { - send Parser - "const [V.ident] = [V.number] - } - # case [declaration] - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - [Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - else { - send Parser - "= 0 - } - - send Parser - [Semi] - } - case [ - `if `( IfExpr: expr `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if [expr(IfExpr)] - " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - [`else `if `( ElseIfExpr: expr `) ElseIfStmt: stmt] - - send Parser - "elseif [expr(ElseIfExpr)] - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [stmt(ElseStmt)] - } - - send Parser - "end - } - case [`while `( WhileExpr: expr `) WhileStmt: stmt] { - send Parser - "while [expr(WhileExpr)] - " [stmt(WhileStmt)] - "end - } - case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { - - require StmtList - [`case E1: expr `{ Inner: stmt* `} Rest: stmt*] - - send Parser - "if [expr(SwitchExpr)] == [expr(E1)] - " [stmt_list(Inner)] - - for S: stmt in repeat(Rest) { - switch S - case [`case E1: expr `{ Inner: stmt* `}] - { - send Parser - "elseif [expr(SwitchExpr)] == [expr(E1)] - " [stmt_list(Inner)] - } - case - [`default `{ Inner: stmt* `}] - { - send Parser - "else - " [stmt_list(Inner)] - } - } - - send Parser - "end - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr)] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - [stmt_list(TL)] - } - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - [TypeList type(Type) Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser "; - } - case [Export: export_stmt] - { - send Parser - "#define [Export.ident] [number(Export.number)] - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "[Index.ident]" - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - else { - send Parser " = 0 " - - } - - send Parser "; - } - case [case_block] - { - send Parser - "@case [expr( Stmt.case_block.expr )] begin - "[stmt_list( Stmt.case_block._repeat_stmt )] - "end - } - case [default_block] - { - send Parser - "default: - "[stmt_list( Stmt.default_block._repeat_stmt )] - "break; - } - case [case_label] - { - send Parser - "@case [expr( Stmt.case_label.expr )] - } - case [L: goto_label] - { - send Parser - "@label [L.ident] - } - case [G: goto_stmt] - { - send Parser - "@goto [G.ident] - } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)] - } - default { - # catches unspecified cases - send Parser - "[Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - CO: julia_out::julia_out = Parser->finish() - - if CO { - send Output - [CO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - julia_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-main.lm b/cgil/rlhc-main.lm deleted file mode 100644 index f9abea7e..00000000 --- a/cgil/rlhc-main.lm +++ /dev/null @@ -1,19 +0,0 @@ -InputFile: str = argv->pop() -OutputFile: str = argv->pop() - -# -# Open input and parse -# -Input: stream = open( InputFile, "r" ) -parse Start: start[ Input ] -if ( !Start ) { - print( error, '\n' ) - exit(1) -} - -# -# Translate -# -Output: stream = open( OutputFile, "w" ) -trans( Output, Start ) - diff --git a/cgil/rlhc-ocaml.lm b/cgil/rlhc-ocaml.lm deleted file mode 100644 index f68b61be..00000000 --- a/cgil/rlhc-ocaml.lm +++ /dev/null @@ -1,609 +0,0 @@ -include 'ril.lm' - -namespace ocaml_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '(*' any* :>> '*)' - / - - literal `begin `end `{ `} - - token id - /[a-zA-Z_][a-zA-Z_0-9]* "'"? / - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' | '`' / - - token string / - '"' ( [^"\\\n] | '\\' any ) * '"' | - "'" ( [^'\\\n] | '\\' any ) * "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`begin _IN_ item* _EX_ `end ] - | [`{ _IN_ item* _EX_ `} ] - - def ocaml_out - [_IN_ _EX_ item*] -end - -namespace ml_gen - - global StaticVarMap: map = new map() - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] - send Parser - "begin - "[stmt_list( StmtList )] - "end; - case [host::`={ Expr: expr host::`}=] - send Parser "([expr( Expr )])" - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser [Tok] - } - } - } - - void embedded_host( EH: embedded_host ) - { - switch EH - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "begin - "[tok_list( TL )] - "end; - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [`( TL: expr `)] - { - send Parser - "( [ expr(TL) ] )" - } - case [I: ident `[ TL: expr `]] - { - if ( StaticVarMap->find( $I ) || $I == 'stack' ) { - send Parser - "[ ExprFactor.ident ].([ expr( TL ) ])" - } - else { - send Parser - "[ ExprFactor.ident ].\[[ expr( TL ) ]\]" - } - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '.(' expr(E) ')'] - } - case ['offset' '(' ident ',' expr ')'] - { - send Parser - [expr( ExprFactor.expr )] - } - case ['deref' '(' I: ident ',' Expr: expr ')'] - { - if ( $I == 'data' ) - send Parser 'Char.code ' - - if ( StaticVarMap->find( $I ) ) { - send Parser - "[I].( [ expr( Expr ) ] )" - } - else { - send Parser - "[I].\[[ expr( Expr ) ]\]" - } - } - case [T: `TRUE] - { - T.data = 'true' - send Parser [T] - } - case [F: `FALSE] - { - F.data = 'false' - send Parser [F] - } - case [N: `nil] - { - N.data = '0' - send Parser [N] - } - case [Number: number] - { - number( Number ) - } - case [I: ident] { - if ( StaticVarMap->find( $I ) ) { - send Parser - [^I] - } - else { - send Parser - "[^I].contents" - } - } - case [E1: embedded_host `-> E2: expr_factor] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast `( type `) expr_factor] - { - send Parser - [expr_factor( ExprFactor._expr_factor )] - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case [E1: embedded_host `-> E2: lvalue] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - lvalue( E2 ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '.(' E ')'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser " lnot " - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [L: expr_bitwise `& R: expr_factor_op] - { - send Parser - "[expr_bitwise( L )] land [expr_factor_op( R )]" - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - switch Op - case [`<<] - send Parser " lsl " - default - send Parser " asr " - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - switch Op - case [`==] - send Parser "= " - default - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['char '] - case "s16" - send Parser ['short '] - case "s32" - send Parser ['int '] - case "s64" - send Parser ['long '] - case "s128" - send Parser ['long long '] - case "uint" - send Parser ['int '] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]u" - default - send Parser [Number] - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )]; " - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - StaticVarMap->insert( $A.ident, ' ' ) - send Parser - "let [A.ident] : int array = \[| - " [num_list(A.num_list)] - "|\] - } - case [V: static_value] { - StaticVarMap->insert( $V.ident, ' ' ) - send Parser - "let [V.ident] : [V.type] = [V.number] - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if [expr(IfExpr)] then - "begin - " [stmt(IfStmt)] - "end - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "else if [expr(ElseIfExpr)] then - "begin - " [stmt(ElseIfStmt)] - "end - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - "begin - " [stmt(ElseStmt)] - "end - } - - send Parser - ";" - } - case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { - send Parser - "while [expr(WhileExpr)] do - " [stmt(WhileStmt)] - "done; - } - case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { - require StmtList - [`case E1: expr `{ Inner: stmt* `} Rest: stmt*] - - send Parser - "if [expr(SwitchExpr)] = [expr(E1)] then - "begin - " [stmt_list(Inner)] - "end - - for S: stmt in repeat(Rest) { - switch S - case [`case E1: expr `{ Inner: stmt* `}] - { - send Parser - "else if [expr(SwitchExpr)] = [expr(E1)] then - "begin - " [stmt_list(Inner)] - "end - } - case - [`default `{ Inner: stmt* `}] - { - send Parser - "else - "begin - " [stmt_list(Inner)] - "end - } - } - - send Parser - "; - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - "begin - "[stmt_list(TL)] - "end; - } - case [D: declaration] { - send Parser - "let [D.ident] : [type(D.type)] ref " - - switch D.opt_init - case [E: `= expr] { - send Parser - "= ref ( [expr(D.opt_init.expr)] )" - } - default { - send Parser - "= ref 0" - } - - send Parser - " in - } - case [Export: export_stmt] - { - send Parser - "#define [Export.ident] [number(Export.number)] - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "let [Index.ident] : int ref " - - switch Index.opt_init - case [E: `= expr] { - send Parser - "= ref ( [expr(Index.opt_init.expr)] )" - } - default { - send Parser - "= ref 0" - } - - send Parser - " in - } - case [case_block] - { - send Parser - "| [expr( Stmt.case_block.expr )] -> - "begin - "[stmt_list( Stmt.case_block._repeat_stmt )] - "end; - } - case [default_block] - { - send Parser - "| _ -> - "[stmt_list( Stmt.default_block._repeat_stmt )] - } - case [case_label] - { - send Parser - "case [expr( Stmt.case_label.expr )]: - } - case [AS: assign_stmt] - { - switch AS.assign_op - case [`=] - { - switch AS.LValue - case "stack\[[expr]\]" { - send Parser - "Array.set stack top.contents [expr(AS.expr)]; - } - case "nfa_bp\[[expr]\].state" { - send Parser - "Array.set nfa_bp_state nfa_len.contents [expr(AS.expr)]; - } - case "nfa_bp\[[expr]\].p" { - send Parser - "Array.set nfa_bp_p nfa_len.contents [expr(AS.expr)]; - } - default { - send Parser - "[lvalue(AS.LValue)] := [expr(AS.expr)]; - } - } - case [`+=] - { - parse RhsAsFactor: expr_factor [$AS.LValue] - send Parser - "[lvalue(AS.LValue)] := [expr_factor(RhsAsFactor)] + [expr(AS.expr)]; - } - case [`-=] - { - parse RhsAsFactor: expr_factor [$AS.LValue] - send Parser - "[lvalue(AS.LValue)] := [expr_factor(RhsAsFactor)] - [expr(AS.expr)]; - } - default { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - MO: ocaml_out::ocaml_out = Parser->finish() - - if MO { - send Output - [MO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } - -end - -void trans( Output: stream, Start: start ) -{ - ml_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-ruby.lm b/cgil/rlhc-ruby.lm deleted file mode 100644 index 87119465..00000000 --- a/cgil/rlhc-ruby.lm +++ /dev/null @@ -1,527 +0,0 @@ -include 'ril.lm' - -namespace ruby_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '#' any* :> '\n' - / - - literal `def `class `begin `end `while `if - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) * "'" | - "/" ( [^/\\] | '\\' any ) * "/" - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' | '{' | '}' | '\\' / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [`begin _IN_] - | [`class _IN_] - | [`while _IN_] - | [`if _IN_] - | [`def _IN_] - | [_EX_ `end] - - def ruby_out - [_IN_ _EX_ item*] -end - -global Parser: parser - -void tok_list( TL: host::tok* ) -{ - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] - { - send Parser - "begin - "[stmt_list( StmtList )] - "end - } - case [host::`={ Expr: expr host::`}=] - expr( Expr ) - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default { - send Parser [Tok] - } - } -} - -void embedded_host( EH: embedded_host ) -{ - switch EH - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "begin - " [tok_list( TL )] - "end - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } -} - -void expr_factor( ExprFactor: expr_factor ) -{ - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [O:`( TL: expr C: `)] - { - send Parser - [O expr(TL) C] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case ['offset' '(' ident ',' expr ')'] - { - send Parser - [expr( ExprFactor.expr )] - } - case ['deref' '(' ident ',' expr ')'] - { - send Parser - [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] - if $ExprFactor.ident == 'data' - send Parser '.ord' - } - case [T: `TRUE] - { - T.data = 'true' - send Parser [T] - } - case [F: `FALSE] - { - F.data = 'false' - send Parser [F] - } - case [N: `nil] - { - N.data = '0' - send Parser [N] - } - case [Number: number] - { - ruby_number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast Open: `( Type: type Close: `) expr_factor] - { - #send Parser [Open] - #type( Type ) - #send Parser [Close] - expr_factor( ExprFactor._expr_factor ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } -} - -void lvalue( ExprFactor: lvalue ) -{ - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [ident O: `[ TL: expr C: `]] - { - send Parser - [ExprFactor.ident O expr( TL ) C] - } - case [E1: embedded_host `-> E2: lvalue] - { - embedded_host( E1 ) - lvalue( E2 ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' E ']'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } -} - -void expr_factor_op( ExprFactorOp: expr_factor_op ) -{ - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [T: `~ expr_factor_op] - { - send Parser [T] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } -} - -void expr_bitwise( ExprBitwise: expr_bitwise ) -{ - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } -} - -void expr_mult( ExprMult: expr_mult ) -{ - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } -} - -void expr_add( ExprAdd: expr_add ) -{ - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } -} - -void expr_shift( ExprShift: expr_shift ) -{ - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } -} - -void expr_test( ExprTest: expr_test ) -{ - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } -} - -void expr( Expr: expr ) -{ - expr_test( Expr.expr_test ) -} - -void type( Type: type ) -{ - switch Type - case "s8" - send Parser ['byte '] - case "s16" - send Parser ['short '] - case "s32" - send Parser ['int '] - case "s64" - send Parser ['long '] - case "s128" - send Parser ['long long '] - case "uint" - send Parser ['int '] - default - send Parser [Type] -} - -void ruby_number( Number: number ) -{ - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]" - default - send Parser [Number] -} - -void ruby_num_list( NumList: num_list ) -{ - for Number: number in NumList - send Parser "[ruby_number( Number )], " -} - -void stmt( Stmt: stmt ) -{ - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - send Parser - "class << self - " attr_accessor :[ A.ident ] - " private :[ A.ident ], :[ A.ident ]= - "end - "self.[ A.ident ] = \[ - " [ruby_num_list( A.num_list )] - "\] - " - } - case [V: static_value] { - send Parser - "class << self - " attr_accessor :[ V.ident ] - "end - "self.[ V.ident ] = [ V.number ]; - " - } - case [ - 'if' O: `( IfExpr: expr C: `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) - " [stmt(IfStmt)] - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "elsif ( [expr(ElseIfExpr)] ) - " [stmt(ElseIfStmt)] - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else - " [stmt(ElseStmt)] - } - send Parser - "end - } - case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { - send Parser - "while ( [expr(WhileExpr)] ) - " [stmt(WhileStmt)] - "end - } - case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { - send Parser - "case [expr(SwitchExpr)] - "when -2 then - "begin - " [stmt_list(StmtList)] - "end - "end - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - "begin - "[stmt_list(TL)] - "end - } - # [declaration] - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - [Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - else { - send Parser - "= 0 - } - - send Parser - [Semi] - } - case [Export: export_stmt] - { - send Parser - "class << self - " attr_accessor :[ Export.ident ] - "end - "self.[ Export.ident ] = [ ruby_number(Export.number) ]; - " - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [Index: index_stmt] - { - send Parser - "[Index.ident]" - - if match Index.opt_init [E: `= expr] { - send Parser - [E expr(Index.opt_init.expr)] - } - else { - send Parser - "= 0 - } - - send Parser "; - } - case [case_block] - { - send Parser - "end - "when [expr( Stmt.case_block.expr )] then - "begin - "[stmt_list( Stmt.case_block._repeat_stmt )] - } - case [default_block] - { - send Parser - "end - "else - "begin - "[stmt_list( Stmt.default_block._repeat_stmt )] - } - case [goto_label] {} - case [goto_stmt] {} - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - case [continue_stmt] - { - send Parser - "next; - } - default { - # catches unspecified cases - send Parser [Stmt] - } -} - -void stmt_list( StmtList: stmt* ) -{ - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) -} - -void ruby_trans( Output: stream, Start: start ) -{ - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - RO: ruby_out::ruby_out = Parser->finish() - - if RO { - send Output - [RO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } -} - -void trans( Output: stream, Start: start ) -{ - ruby_trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/cgil/rlhc-rust.lm b/cgil/rlhc-rust.lm deleted file mode 100644 index 03f8b688..00000000 --- a/cgil/rlhc-rust.lm +++ /dev/null @@ -1,534 +0,0 @@ -include 'ril.lm' - -namespace rust_out - token _IN_ /''/ - token _EX_ /''/ - - lex - token comment / - '//' any* :> '\n' | - '/*' any* :>> '*/' - / - - token id - /[a-zA-Z_][a-zA-Z_0-9]*/ - - token number / - [0-9]+ - / - - token symbol / - '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | - '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | - '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | - '~' / - - literal `{ `} - - token lifetime / "'" id / - - token string / - '"' ( [^"\\] | '\\' any ) * '"' | - "'" ( [^'\\] | '\\' any ) "'" - / - - ignore - /[ \t\v\r\n]+/ - end - - def item - [comment] - | [id] - | [number] - | [symbol] - | [string] - | [lifetime] - | [`{ _IN_ item* _EX_ `} ] - - def rust_out - [_IN_ _EX_ item*] -end - -namespace rust_gen - - global Parser: parser - - void tok_list( TL: host::tok* ) - { - for Tok: host::tok in repeat(TL) { - switch Tok - case [host::`${ StmtList: stmt* host::`}$] - send Parser "{[stmt_list( StmtList )]}" - case [host::`={ Expr: expr host::`}=] - send Parser "([expr( Expr )])" - case [E: escape] { - Str: str = $E - send Parser - "[Str.suffix( 1 )]" - } - default - send Parser [Tok] - } - } - - void embedded_host( EmbeddedHost: embedded_host ) - { - switch EmbeddedHost - case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] - { - send Parser - "([tok_list( TL )])" - } - case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] - { - send Parser - "{[tok_list( TL )]} - } - case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] - { - send Parser - [tok_list( TL )] - } - } - - void expr_factor( ExprFactor: expr_factor ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [O:`( TL: expr C: `)] - { - send Parser - [O expr(TL) C] - } - case [I: ident `[ E: expr `]] - { - send Parser - "[I]\[([expr( E )]) as usize\]" - } - case ['offset' '(' ident ',' expr ')'] - { - send Parser - "( [expr( ExprFactor.expr )] ) as i32" - } - case ['deref' '(' I: ident ',' E: expr ')'] - { - send Parser - "[I]\[([expr( E )]) as usize\] - } - case [T: `TRUE] - { - T.data = 'true' - send Parser [T] - } - case [F: `FALSE] - { - F.data = 'false' - send Parser [F] - } - case [N: `nil] - { - N.data = '0' - send Parser [N] - } - case [Number: number] - { - number( Number ) - } - case [E1: embedded_host `-> E2: expr_factor] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - expr_factor( E2 ) - } - case [`cast `( T: type `) E: expr_factor] - { - send Parser - "( [expr_factor( E )] ) as [type(T)]" - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' expr(E) ' as usize]'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void lvalue( ExprFactor: lvalue ) - { - switch ExprFactor - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [I: ident O: `[ E: expr C: `]] - { - send Parser - "[I]\[([expr( E )]) as usize\] - } - case [E1: embedded_host `-> E2: lvalue] - { - # The accessor operator is contained wihtin the lhs. - embedded_host( E1 ) - lvalue( E2 ) - } - case [I: ident `[ E: expr `] `. F: ident] { - send Parser - [^I '_' ^F '[' expr(E) ' as usize]'] - } - default { - # Catches cases not specified - send Parser [ExprFactor] - } - } - - void expr_factor_op( ExprFactorOp: expr_factor_op ) - { - switch ExprFactorOp - case [B: `! expr_factor_op] - { - send Parser [B] - expr_factor_op( ExprFactorOp._expr_factor_op ) - } - case [`~ EFO: expr_factor_op] - { - send Parser - "![expr_factor_op( EFO )] - } - case [expr_factor] - { - expr_factor( ExprFactorOp.expr_factor ) - } - } - - void expr_bitwise( ExprBitwise: expr_bitwise ) - { - switch ExprBitwise - case [expr_bitwise A: `& expr_factor_op] - { - expr_bitwise( ExprBitwise._expr_bitwise ) - send Parser [A] - expr_factor_op( ExprBitwise.expr_factor_op ) - } - case [expr_factor_op] - { - expr_factor_op( ExprBitwise.expr_factor_op ) - } - } - - void expr_mult( ExprMult: expr_mult ) - { - switch ExprMult - case [expr_mult T: `* expr_bitwise] - { - expr_mult( ExprMult._expr_mult ) - send Parser [T] - expr_bitwise( ExprMult.expr_bitwise ) - } - case [expr_bitwise] - { - expr_bitwise( ExprMult.expr_bitwise ) - } - } - - void expr_add( ExprAdd: expr_add ) - { - switch ExprAdd - case [expr_add Op: add_op expr_mult] - { - expr_add( ExprAdd._expr_add ) - send Parser [Op] - expr_mult( ExprAdd.expr_mult ) - } - case [expr_mult] - { - expr_mult( ExprAdd.expr_mult ) - } - } - - void expr_shift( ExprShift: expr_shift ) - { - switch ExprShift - case [expr_shift Op: shift_op expr_add] - { - expr_shift( ExprShift._expr_shift ) - send Parser [Op] - expr_add( ExprShift.expr_add ) - } - case [expr_add] - { - expr_add( ExprShift.expr_add ) - } - } - - void expr_test( ExprTest: expr_test ) - { - switch ExprTest - case [expr_test Op: test_op expr_shift] - { - expr_test( ExprTest._expr_test ) - send Parser [Op] - expr_shift( ExprTest.expr_shift ) - } - case [expr_shift] - { - expr_shift( ExprTest.expr_shift ) - } - } - - void expr( Expr: expr ) - { - expr_test( Expr.expr_test ) - } - - void type( Type: type ) - { - switch Type - case "s8" - send Parser ['i8 '] - case "s16" - send Parser ['i16 '] - case "s32" - send Parser ['i32 '] - case "s64" - send Parser ['i64 '] - case "s128" - send Parser ['i128'] - case "int" - send Parser ['i32'] - case "uint" - send Parser ['u32'] - default - send Parser [Type] - } - - void number( Number: number ) - { - switch Number - case [`u `( uint `) ] - send Parser "[Number.uint]" - default - send Parser [Number] - } - - void num_list( NumList: num_list ) - { - for Number: number in NumList - send Parser "[number( Number )], " - send Parser "0" - } - - void stmt( Stmt: stmt ) - { - switch Stmt - case [EH: embedded_host] - { - send Parser - [embedded_host( EH )] - } - case [A: static_array] { - Length: int = 1 - for Number: number in A.num_list - Length = Length + 1 - - send Parser - "static [A.ident]: \[[type(A.type)]; [Length]\] = \[ [num_list(A.num_list)] \]; - } - case [V: static_value] { - send Parser - "static [V.ident]: i32 = [V.number]; - } - case [D: declaration] { - send Parser - "let mut [D.ident] " - - switch D.opt_init - case [E: `= expr] { - send Parser - "= [expr(D.opt_init.expr)]; - } - default { - send Parser - "= 0; - } - } - case [Index: index_stmt] - { - send Parser - "let mut [Index.ident] :i32" - - switch Index.opt_init - case [E: `= expr] { - send Parser - "= [expr(Index.opt_init.expr)]; - } - default { - send Parser - "= 0; - } - } - case [ - 'if' `( IfExpr: expr `) IfStmt: stmt - ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? - ] { - send Parser - "if ( [expr(IfExpr)] ) { - " [stmt(IfStmt)] - "} - - for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { - match ElseIfClause - ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] - - send Parser - "else if ( [expr(ElseIfExpr)] ) { - " [stmt(ElseIfStmt)] - "} - } - - if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { - send Parser - "else { - " [stmt(ElseStmt)] - "} - } - } - case [`continue CL: ident `;] { - send Parser - "continue '[CL]; - } - case [`break BL: ident `;] { - send Parser - "break '[BL]; - } - case [BL: break_label? 'while' '(' WhileExpr: expr ')' '{' StmtList: stmt* '}' ] { - if match BL [bl: break_label] - send Parser "'[bl.ident]: " - - send Parser - "while ( [expr(WhileExpr)] ) { - " [stmt_list(StmtList)] - "} - } - case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { - send Parser - "match ( [expr(SwitchExpr)] ) { - " [stmt_list(StmtList)] - - NeedDef: bool = true - for Stmt: stmt in repeat(StmtList) { - if match Stmt [default_block] - NeedDef = false - } - - if NeedDef { - send Parser - " _ => {} - } - - send Parser - "} - } - case [ExprExpr: expr Semi: `;] { - send Parser - [expr(ExprExpr) Semi] - } - case [L: `{ TL: stmt* R: `}] { - send Parser - [L stmt_list(TL) R] - } - case [ - TypeList: opt_const Type: type - Ident: ident OptInit: opt_init Semi: `; - ] - { - send Parser - [TypeList type(Type) Ident] - - if match OptInit [E: `= expr] { - send Parser - [E expr(OptInit.expr)] - } - - send Parser - [Semi] - } - case [Export: export_stmt] - { - send Parser - "#define [Export.ident] [number(Export.number)] - } - case ['fallthrough' ';'] - { - # Nothing needed here. - } - case [case_block] - { - send Parser - "[expr( Stmt.case_block.expr )] => { - "[stmt_list( Stmt.case_block._repeat_stmt )] - "} - } - case [default_block] - { - send Parser - "_ => { - "[stmt_list( Stmt.default_block._repeat_stmt )] - "} - } - case [case_label] - { - send Parser - "case [expr( Stmt.case_label.expr )]: - } - case [AS: assign_stmt] - { - send Parser - "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; - } - default { - # catches unspecified cases - send Parser [Stmt] - } - } - - void stmt_list( StmtList: stmt* ) - { - for Stmt: stmt in repeat( StmtList ) - stmt( Stmt ) - } - - void trans( Output: stream, Start: start ) - { - Parser = new parser() - - if ( Start.opt_bom.bom ) - send Output [Start.opt_bom.bom] - - stmt_list( Start._repeat_stmt ) - - CO: rust_out::rust_out = Parser->finish() - - if CO { - send Output - [CO] - } - else { - send stderr - "failed to parse output: [Parser->error] - } - } -end - -void trans( Output: stream, Start: start ) -{ - rust_gen::trans( Output, Start ) -} - -include 'rlhc-main.lm' diff --git a/configure.ac b/configure.ac index a9b80e08..e3af1e4e 100644 --- a/configure.ac +++ b/configure.ac @@ -189,14 +189,14 @@ AC_ARG_WITH(subject, SUBJ_RAGEL_JULIA_BIN="$withval/bin/ragel-julia" ], [ - SUBJ_AAPL_CPPFLAGS='-I$(abs_top_builddir)/aapl' + SUBJ_AAPL_CPPFLAGS='-I$(abs_top_builddir)/src/aapl' SUBJ_COLM_BIN='$(abs_top_builddir)/src/colm' SUBJ_COLM_CPPFLAGS='-I$(abs_top_builddir)/src/include' SUBJ_COLM_LDFLAGS='-L$(abs_top_builddir)/src/.libs -Wl,-rpath,${abs_top_builddir}/src/.libs' SUBJ_RAGEL_BIN='$(abs_top_builddir)/ragel/ragel' - SUBJ_RAGEL_CPPFLAGS='-I$(abs_top_builddir)/ragel/include -I$(abs_top_builddir)/aapl' + SUBJ_RAGEL_CPPFLAGS='-I$(abs_top_builddir)/ragel/include -I$(abs_top_builddir)/src/aapl' SUBJ_RAGEL_LDFLAGS='-L$(abs_top_builddir)/ragel/.libs -Wl,-rpath,$(abs_top_builddir)/ragel/.libs' SUBJ_RAGEL_LM='$(abs_top_builddir)/ragel' @@ -414,10 +414,10 @@ AC_SUBST(SED_SUBST) AC_CONFIG_HEADERS([src/config.h src/defs.h]) AC_OUTPUT([ Makefile - aapl/Makefile - libfsm/Makefile + src/aapl/Makefile + src/libfsm/Makefile + src/cgil/Makefile src/Makefile - cgil/Makefile test/Makefile test/aapl.d/Makefile test/colm.d/Makefile diff --git a/libfsm/.exrc b/libfsm/.exrc deleted file mode 100644 index 412b360f..00000000 --- a/libfsm/.exrc +++ /dev/null @@ -1,28 +0,0 @@ -if &cp | set nocp | endif -let s:cpo_save=&cpo -set cpo&vim -map j -map  k -map Q gq -nmap gx NetrwBrowseX -nnoremap NetrwBrowseX :call netrw#NetrwBrowseX(expand(""),0) -let &cpo=s:cpo_save -unlet s:cpo_save -set autoindent -set autowriteall -set backspace=2 -set fileencodings=ucs-bom,utf-8,default,latin1 -set helplang=en -set incsearch -set nojoinspaces -set makeprg=make\ -j4 -set printoptions=paper:letter -set ruler -set runtimepath=~/.vim,/var/lib/vim/addons,/usr/share/vim/vimfiles,/usr/share/vim/vim74,/usr/share/vim/vimfiles/after,/var/lib/vim/addons/after,~/.vim/after -set showcmd -set showmatch -set suffixes=.bak,~,.swp,.o,.info,.aux,.log,.dvi,.bbl,.blg,.brf,.cb,.ind,.idx,.ilg,.inx,.out,.toc -set viminfo='20,\"50 -set visualbell -set nowritebackup -" vim: set ft=vim : diff --git a/libfsm/.gitignore b/libfsm/.gitignore deleted file mode 100644 index 78db4968..00000000 --- a/libfsm/.gitignore +++ /dev/null @@ -1,57 +0,0 @@ -/tags -/Makefile -/Makefile.in -/rlscan.cc -/rlparse.cc -/rlparse.h -/version.h -/config.h -/config.h.in -/config.h.in~ -/ragel -/ragel.exe -/.deps -/stamp-h1 -/rlhc -/rlhc.c - -/*.lo - -# Parsing -/parse.c -/rlreduce.cc -/ldparse.c -/ldreduce.cc - -# Common testing file. -/tmp.rl -/tmp.c -/tmp.cc -/tmp.d -/tmp.go -/tmp.ps -/tmp.ml -/tmp.cmi -/tmp.cmx -/tmp.rs -/tmp.crk -/tmp.jl -/tmp -/input - -# The ragel frontend doesn't support OCaml lexical rules yet, so a util is -# needed. -/util.ml -/util.cmi -/util.cmx - -/libragel.a -/libragel.la -/libfsm.a -/libfsm.la -/.libs - -/CMakeFiles -/cmake_install.cmake - -/*.pack diff --git a/libfsm/CMakeLists.txt b/libfsm/CMakeLists.txt deleted file mode 100644 index 3e797981..00000000 --- a/libfsm/CMakeLists.txt +++ /dev/null @@ -1,154 +0,0 @@ -# Package name -set(_PACKAGE_NAME ragel) - -# Read project configuration from ../configure.ac file -file(STRINGS ../configure.ac _PROJECT_CONFIGS - REGEX "(RAGEL_VERSION=)|(RAGEL_PUBDATE=)") -foreach(_PROJECT_CONFIG ${_PROJECT_CONFIGS}) - if(_PROJECT_CONFIG MATCHES "RAGEL_VERSION=\"([^\"]+)") - string(STRIP ${CMAKE_MATCH_1} RAGEL_VERSION) - endif() - if(_PROJECT_CONFIG MATCHES "RAGEL_PUBDATE=\"([^\"]+)") - string(STRIP ${CMAKE_MATCH_1} RAGEL_PUBDATE) - endif() -endforeach() - -## Generate headers -configure_file(version.h.cmake.in version.h @ONLY) -configure_file(ragel-config.cmake.in - "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config.cmake" @ONLY) - -# Runtime headers -set(RUNTIME_HDR - action.h fsmgraph.h ragel.h common.h - gendata.h redfsm.h dot.h) - -# Other CMake modules -include(GNUInstallDirs) - -# libfsm -add_library(libfsm - buffer.h codegen.h - actloop.h actexp.h - tables.h - binary.h bingoto.h binbreak.h binvar.h - flat.h flatgoto.h flatbreak.h flatvar.h - switch.h switchgoto.h switchbreak.h switchvar.h - goto.h gotoloop.h gotoexp.h - ipgoto.h asm.h - idbase.cc fsmstate.cc fsmbase.cc fsmattach.cc fsmmin.cc fsmgraph.cc - fsmap.cc fsmcond.cc fsmnfa.cc common.cc redfsm.cc gendata.cc - allocgen.cc codegen.cc - actexp.cc binvar.cc - tables.cc tabgoto.cc tabbreak.cc tabvar.cc - binary.cc bingoto.cc binbreak.cc actloop.cc - flat.cc flatgoto.cc flatbreak.cc flatvar.cc - switch.cc switchgoto.cc switchbreak.cc switchvar.cc - goto.cc gotoloop.cc gotoexp.cc ipgoto.cc - dot.cc asm.cc) - -target_include_directories(libfsm - PUBLIC - $ - $ - $ - $) - -set_target_properties(libfsm PROPERTIES - OUTPUT_NAME fsm) - -# libragel -add_library(libragel - # dist - parsedata.h parsetree.h inputdata.h pcheck.h reducer.h rlscan.h load.h - parsetree.cc longest.cc parsedata.cc inputdata.cc load.cc reducer.cc) - -if(BUILD_STANDALONE) - # libragel acts as an intermediate library so we can apply - # flags we want to apply to all ragel targets to libragel - # and they'll automatically propogate. This is a best effort - # to get `-static` placed sooner in the link line where it - # matters at least. - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_link_libraries(libragel PUBLIC -static) - else() - message(FATAL_ERROR "Unsupported toolset for standalone build.") - endif() -endif() - -target_link_libraries(libragel PRIVATE colm::libcolm) - -target_include_directories(libragel - PUBLIC - $ - $ - $ - $ - $ - $) - -set_target_properties(libragel PROPERTIES - OUTPUT_NAME ragel) - -set_property(TARGET libragel APPEND PROPERTY - COMPILE_DEFINITIONS BINDIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - -# ragel program - -set(RAGEL_LM - rlparse.lm - ragel.lm - rlreduce.lm) - -add_custom_command(OUTPUT - "${CMAKE_CURRENT_BINARY_DIR}/parse.c" - "${CMAKE_CURRENT_BINARY_DIR}/rlreduce.cc" - DEPENDS ${RAGEL_LM} #$(COLM_BINDEP) - COMMAND colm::colm - ARGS -c -b rlparseC - -o "${CMAKE_CURRENT_BINARY_DIR}/parse.c" - -m "${CMAKE_CURRENT_BINARY_DIR}/rlreduce.cc" - rlparse.lm - WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") - -add_executable(ragel - main.cc - "${CMAKE_CURRENT_BINARY_DIR}/parse.c" - "${CMAKE_CURRENT_BINARY_DIR}/rlreduce.cc") - -target_link_libraries(ragel libragel libfsm) - -foreach(_SUBDIR host-ruby host-asm host-julia host-ocaml host-c host-d - host-csharp host-go host-java host-rust host-crack host-js) - add_subdirectory(${_SUBDIR}) -endforeach() - -if(${PROJECT_NAME}_MAKE_INSTALL) - if(NOT DEFINED CMAKE_INSTALL_CMAKEDIR) - set(CMAKE_INSTALL_CMAKEDIR - "${CMAKE_INSTALL_LIBDIR}/cmake/${_PACKAGE_NAME}" - CACHE STRING "CMake packages") - endif() - install(FILES ${RUNTIME_HDR} - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ragel") - install(TARGETS libfsm libragel ragel - EXPORT ${_PACKAGE_NAME}-targets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") - install(EXPORT ${_PACKAGE_NAME}-targets - NAMESPACE ${_PACKAGE_NAME}:: - DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") - export(EXPORT ${_PACKAGE_NAME}-targets - NAMESPACE ${_PACKAGE_NAME}:: - FILE "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-targets.cmake") - include(CMakePackageConfigHelpers) - write_basic_package_version_file( - "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config-version.cmake" - VERSION ${RAGEL_VERSION} - COMPATIBILITY AnyNewerVersion) - install(FILES - "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config.cmake" - "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config-version.cmake" - DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") -endif() diff --git a/libfsm/Makefile.am b/libfsm/Makefile.am deleted file mode 100644 index d1e8de9d..00000000 --- a/libfsm/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -# libfsm contains only the FSM construction code and the backend code -# generators for C, asm and cgil (Code Gen Intermediate Language) . It is -# useful for building state machine code generators in programs not connected -# to the ragel language. -lib_LTLIBRARIES = libfsm.la - -libfsminclude_HEADERS = \ - action.h fsmgraph.h common.h \ - gendata.h redfsm.h dot.h asm.h ragel.h - -# nodist_pkginclude_HEADERS = config.h - -# -# libfsm: state machine construction and direct code generation. -# -libfsm_la_CPPFLAGS = -I$(top_srcdir)/aapl - -dist_libfsm_la_SOURCES = \ - parsedata.h idbase.h codegen.h \ - actloop.h actexp.h \ - tables.h \ - binary.h bingoto.h binbreak.h binvar.h \ - flat.h flatgoto.h flatbreak.h flatvar.h \ - switch.h switchgoto.h switchbreak.h switchvar.h \ - goto.h gotoloop.h gotoexp.h \ - ipgoto.h asm.h \ - idbase.cc fsmstate.cc fsmbase.cc fsmattach.cc fsmmin.cc fsmgraph.cc \ - fsmap.cc fsmcond.cc fsmnfa.cc common.cc redfsm.cc gendata.cc \ - allocgen.cc codegen.cc \ - actexp.cc binvar.cc \ - tables.cc tabgoto.cc tabbreak.cc tabvar.cc \ - binary.cc bingoto.cc binbreak.cc actloop.cc \ - flat.cc flatgoto.cc flatbreak.cc flatvar.cc \ - switch.cc switchgoto.cc switchbreak.cc switchvar.cc \ - goto.cc gotoloop.cc gotoexp.cc ipgoto.cc \ - dot.cc asm.cc - -libfsm_la_LDFLAGS = -no-undefined - -if LINKER_NO_UNDEFINED -libfsm_la_LDFLAGS += -Wl,--no-undefined -endif - diff --git a/libfsm/actexp.cc b/libfsm/actexp.cc deleted file mode 100644 index 771d4623..00000000 --- a/libfsm/actexp.cc +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "actexp.h" -#include "redfsm.h" -#include "gendata.h" - -void ActExp::FROM_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->fromStateAction != 0 ) - act = state->fromStateAction->actListId + 1; - fromStateActions.value( act ); -} - -void ActExp::COND_ACTION( RedCondPair *cond ) -{ - int action = 0; - if ( cond->action != 0 ) - action = cond->action->actListId + 1; - condActions.value( action ); -} - -void ActExp::TO_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->toStateAction != 0 ) - act = state->toStateAction->actListId + 1; - toStateActions.value( act ); -} - -void ActExp::EOF_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->eofAction != 0 ) - act = state->eofAction->actListId + 1; - eofActions.value( act ); -} - -void ActExp::NFA_PUSH_ACTION( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->push != 0 ) - act = targ->push->actListId+1; - nfaPushActions.value( act ); -} - -void ActExp::NFA_POP_TEST( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->popTest != 0 ) - act = targ->popTest->actListId+1; - nfaPopTrans.value( act ); -} - - -/* Write out the function switch. This switch is keyed on the values - * of the func index. */ -std::ostream &ActExp::FROM_STATE_ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numFromStateRefs > 0 ) { - /* Write the entry label. */ - out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( 0, false, false ) ); - out << "\n\t"; - } - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -/* Write out the function switch. This switch is keyed on the values - * of the func index. */ -std::ostream &ActExp::ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numTransRefs > 0 ) { - /* Write the entry label. */ - out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( 0, false, false ) ); - out << "\n\t"; - } - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -/* Write out the function switch. This switch is keyed on the values - * of the func index. */ -std::ostream &ActExp::TO_STATE_ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numToStateRefs > 0 ) { - /* Write the entry label. */ - out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( 0, false, false ) ); - out << "\n\t"; - } - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &ActExp::EOF_ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numEofRefs > 0 ) { - /* Write the entry label. */ - out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( 0, true, false ) ); - out << "\n\t"; - } - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - - -void ActExp::FROM_STATE_ACTIONS() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " switch ( " << ARR_REF( fromStateActions ) << "[" << vCS() << "] ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - "\n"; - } -} - -void ActExp::REG_ACTIONS( std::string cond ) -{ - out << - " switch ( " << ARR_REF( condActions ) << "[" << cond << "] ) {\n"; - ACTION_SWITCH() << - " }\n" - "\n"; -} -void ActExp::TO_STATE_ACTIONS() -{ - if ( redFsm->anyToStateActions() ) { - out << - " switch ( " << ARR_REF( toStateActions ) << "[" << vCS() << "] ) {\n"; - TO_STATE_ACTION_SWITCH() << - " }\n" - "\n"; - } -} - - -void ActExp::EOF_ACTIONS() -{ - if ( redFsm->anyEofActions() ) { - out << - " switch ( " << ARR_REF( eofActions ) << "[" << vCS() << "] ) {\n"; - EOF_ACTION_SWITCH() << - " }\n"; - } -} - -void ActExp::NFA_FROM_STATE_ACTION_EXEC() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " switch ( " << ARR_REF( fromStateActions ) << "[nfa_bp[nfa_len].state] ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - "\n"; - } -} - diff --git a/libfsm/actexp.h b/libfsm/actexp.h deleted file mode 100644 index 49165755..00000000 --- a/libfsm/actexp.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _BINEXP_H -#define _BINEXP_H - -#include "binary.h" - -struct RedStateAp; -struct RedCondPair; - -class ActExp - : public virtual Tables -{ -public: - ActExp( const CodeGenArgs &args ) - : - Tables( args ) - {} - - virtual void FROM_STATE_ACTION( RedStateAp *state ); - virtual void COND_ACTION( RedCondPair *cond ); - virtual void TO_STATE_ACTION( RedStateAp *state ); - virtual void EOF_ACTION( RedStateAp *state ); - - virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); - virtual void NFA_POP_TEST( RedNfaTarg *targ ); - - virtual std::ostream &FROM_STATE_ACTION_SWITCH(); - virtual std::ostream &ACTION_SWITCH(); - virtual std::ostream &TO_STATE_ACTION_SWITCH(); - virtual std::ostream &EOF_ACTION_SWITCH(); - - virtual void TO_STATE_ACTIONS(); - virtual void REG_ACTIONS( std::string cond ); - virtual void FROM_STATE_ACTIONS(); - virtual void EOF_ACTIONS(); - - virtual void NFA_FROM_STATE_ACTION_EXEC(); -}; - -#endif - diff --git a/libfsm/action.h b/libfsm/action.h deleted file mode 100644 index 5c79befe..00000000 --- a/libfsm/action.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _ACTION_H -#define _ACTION_H - -#include "fsmgraph.h" - -struct NameInst; -struct NameRef; -struct FsmLongestMatch; -struct InlineList; -struct FsmLongestMatchPart; -struct Action; -struct CondSpace; - -/* - * Inline code tree - */ -struct InlineItem -{ - enum Type - { - Text, Goto, Call, Ncall, Next, GotoExpr, CallExpr, NcallExpr, NextExpr, Ret, Nret, - PChar, Char, Hold, Curs, Targs, Entry, Exec, Break, Nbreak, - LmSwitch, LmSetActId, LmSetTokEnd, LmOnLast, LmOnNext, LmOnLagBehind, - LmInitAct, LmInitTokStart, LmSetTokStart, LmNfaOnNext, LmNfaOnLast, LmNfaOnEof, Stmt, Subst, - NfaWrapAction, NfaWrapConds - }; - - InlineItem( const InputLoc &loc, std::string data, Type type ) : - loc(loc), data(data), nameRef(0), children(0), type(type) { } - - InlineItem( const InputLoc &loc, NameRef *nameRef, Type type ) : - loc(loc), nameRef(nameRef), children(0), type(type) { } - - InlineItem( const InputLoc &loc, FsmLongestMatch *longestMatch, - FsmLongestMatchPart *longestMatchPart, Type type ) : loc(loc), - nameRef(0), children(0), longestMatch(longestMatch), - longestMatchPart(longestMatchPart), type(type) { } - - InlineItem( const InputLoc &loc, NameInst *nameTarg, Type type ) : - loc(loc), nameRef(0), nameTarg(nameTarg), children(0), - type(type) { } - - InlineItem( const InputLoc &loc, Type type ) : - loc(loc), nameRef(0), children(0), type(type) { } - - InlineItem( const InputLoc &loc, Action *wrappedAction, Type type ) - : - loc(loc), nameRef(0), children(0), longestMatch(0), - longestMatchPart(0), wrappedAction(wrappedAction), type(type) - {} - - InlineItem( const InputLoc &loc, CondSpace *condSpace, - const CondKeySet &condKeySet, Type type ) - : - loc(loc), nameRef(0), children(0), longestMatch(0), - longestMatchPart(0), wrappedAction(0), condSpace(condSpace), - condKeySet(condKeySet), type(type) - {} - - ~InlineItem(); - - InputLoc loc; - std::string data; - NameRef *nameRef; - NameInst *nameTarg; - InlineList *children; - FsmLongestMatch *longestMatch; - FsmLongestMatchPart *longestMatchPart; - long substPos; - Action *wrappedAction; - CondSpace *condSpace; - CondKeySet condKeySet; - Type type; - - InlineItem *prev, *next; -}; - -/* Normally this would be atypedef, but that would entail including DList from - * ptreetypes, which should be just typedef forwards. */ -struct InlineList : public DList { }; - -struct InlineBlock -{ - InlineBlock( const InputLoc &loc, InlineList *inlineList ) - : loc(loc), inlineList(inlineList) {} - - ~InlineBlock() - { - inlineList->empty(); - delete inlineList; - } - - InputLoc loc; - InlineList *inlineList; -}; - -#endif diff --git a/libfsm/actloop.cc b/libfsm/actloop.cc deleted file mode 100644 index 675e78fa..00000000 --- a/libfsm/actloop.cc +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "actloop.h" -#include "redfsm.h" -#include "gendata.h" - -void ActLoop::FROM_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->fromStateAction != 0 ) - act = state->fromStateAction->location+1; - fromStateActions.value( act ); -} - -void ActLoop::COND_ACTION( RedCondPair *cond ) -{ - int act = 0; - if ( cond->action != 0 ) - act = cond->action->location+1; - condActions.value( act ); -} - -void ActLoop::TO_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->toStateAction != 0 ) - act = state->toStateAction->location+1; - toStateActions.value( act ); -} - -void ActLoop::EOF_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->eofAction != 0 ) - act = state->eofAction->location+1; - eofActions.value( act ); -} - -void ActLoop::NFA_PUSH_ACTION( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->push != 0 ) - act = targ->push->actListId+1; - nfaPushActions.value( act ); -} - -void ActLoop::NFA_POP_TEST( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->popTest != 0 ) - act = targ->popTest->actListId+1; - nfaPopTrans.value( act ); -} - -std::ostream &ActLoop::FROM_STATE_ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numFromStateRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; - ACTION( out, act, IlOpts( 0, false, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &ActLoop::ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numTransRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; - ACTION( out, act, IlOpts( 0, false, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &ActLoop::TO_STATE_ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numToStateRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; - ACTION( out, act, IlOpts( 0, false, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - - -std::ostream &ActLoop::EOF_ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numEofRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; - ACTION( out, act, IlOpts( 0, true, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - - - -void ActLoop::FROM_STATE_ACTIONS() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( fromStateActions ) + "[" + vCS() + "]" ) << ";\n" - " " << nacts << " = " << CAST(UINT()) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - " " << nacts << " -= 1;\n" - " " << acts << " += 1;\n" - " }\n" - "\n"; - } -} - -void ActLoop::REG_ACTIONS( std::string cond ) -{ - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( condActions ) + "[" + cond + "]" ) << ";\n" - " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " )\n" - " {\n"; - ACTION_SWITCH() << - " }\n" - " " << nacts << " -= 1;\n" - " " << acts << " += 1;\n" - " }\n" - "\n"; -} - -void ActLoop::TO_STATE_ACTIONS() -{ - if ( redFsm->anyToStateActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( toStateActions ) + "[" + vCS() + "]" ) << ";\n" - " " << nacts << " = " << CAST(UINT()) << DEREF( ARR_REF( actions ), acts.ref() ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), acts.ref() ) << " ) {\n"; - TO_STATE_ACTION_SWITCH() << - " }\n" - " " << nacts << " -= 1;\n" - " " << acts << " += 1;\n" - " }\n" - "\n"; - } -} - -void ActLoop::EOF_ACTIONS() -{ - if ( redFsm->anyEofActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( eofActions ) + "[" + vCS() + "]" ) << ";\n" - " " << nacts << " = " << CAST(UINT()) << DEREF( ARR_REF( actions ), acts.ref() ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), acts.ref() ) << " ) {\n"; - EOF_ACTION_SWITCH() << - " }\n" - " " << nacts << " -= 1;\n" - " " << acts << " += 1;\n" - " }\n"; - } -} - -void ActLoop::NFA_FROM_STATE_ACTION_EXEC() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( fromStateActions ) + "[nfa_bp[nfa_len].state]" ) << ";\n" - " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), acts.ref() ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), acts.ref() ) << " ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - " " << nacts << " -= 1;\n" - " " << acts << " += 1;\n" - " }\n" - "\n"; - } -} - diff --git a/libfsm/actloop.h b/libfsm/actloop.h deleted file mode 100644 index 238ba72a..00000000 --- a/libfsm/actloop.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _BINLOOP_H -#define _BINLOOP_H - -#include "binary.h" -#include "tables.h" - -struct RedStateAp; -struct RedCondPair; - -class ActLoop - : public virtual Tables -{ -public: - ActLoop( const CodeGenArgs &args ) - : - Tables( args ) - {} - - virtual void FROM_STATE_ACTION( RedStateAp *state ); - virtual void COND_ACTION( RedCondPair *cond ); - virtual void TO_STATE_ACTION( RedStateAp *state ); - virtual void EOF_ACTION( RedStateAp *state ); - - virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); - virtual void NFA_POP_TEST( RedNfaTarg *targ ); - - virtual std::ostream &FROM_STATE_ACTION_SWITCH(); - virtual std::ostream &ACTION_SWITCH(); - virtual std::ostream &TO_STATE_ACTION_SWITCH(); - virtual std::ostream &EOF_ACTION_SWITCH(); - - virtual void FROM_STATE_ACTIONS(); - virtual void REG_ACTIONS( std::string cond ); - virtual void TO_STATE_ACTIONS(); - virtual void EOF_ACTIONS(); - - virtual void NFA_FROM_STATE_ACTION_EXEC(); -}; - - -#endif diff --git a/libfsm/allocgen.cc b/libfsm/allocgen.cc deleted file mode 100644 index 02278bfa..00000000 --- a/libfsm/allocgen.cc +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2005-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "parsedata.h" -#include "fsmgraph.h" -#include "gendata.h" - -/* - * Code generators. - */ -#include "bingoto.h" -#include "binbreak.h" -#include "binvar.h" -#include "flatgoto.h" -#include "flatbreak.h" -#include "flatvar.h" -#include "switchgoto.h" -#include "switchbreak.h" -#include "switchvar.h" -#include "gotoloop.h" -#include "gotoexp.h" -#include "ipgoto.h" -#include "asm.h" - -CodeGenData *makeCodeGenAsm( const HostLang *hostLang, const CodeGenArgs &args ) -{ - return new AsmCodeGen( args ); -} - -/* Invoked by the parser when a ragel definition is opened. */ -CodeGenData *makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ) -{ - FsmGbl *id = args.id; - CodeGenData *codeGen = 0; - BackendFeature feature = hostLang->feature; - if ( args.forceVar ) - feature = VarFeature; - - switch ( args.codeStyle ) { - case GenBinaryLoop: - if ( feature == GotoFeature ) - codeGen = new BinGotoLoop( args ); - else if ( feature == BreakFeature ) - codeGen = new BinBreakLoop( args ); - else - codeGen = new BinVarLoop( args ); - break; - - case GenBinaryExp: - if ( feature == GotoFeature ) - codeGen = new BinGotoExp( args ); - else if ( feature == BreakFeature ) - codeGen = new BinBreakExp( args ); - else - codeGen = new BinVarExp( args ); - break; - - case GenFlatLoop: - if ( feature == GotoFeature ) - codeGen = new FlatGotoLoop( args ); - else if ( feature == BreakFeature ) - codeGen = new FlatBreakLoop( args ); - else - codeGen = new FlatVarLoop( args ); - break; - - case GenFlatExp: - if ( feature == GotoFeature ) - codeGen = new FlatGotoExp( args ); - else if ( feature == BreakFeature ) - codeGen = new FlatBreakExp( args ); - else - codeGen = new FlatVarExp( args ); - break; - case GenSwitchLoop: - if ( feature == GotoFeature ) - codeGen = new SwitchGotoLoop( args ); - else if ( feature == BreakFeature ) - codeGen = new SwitchBreakLoop( args ); - else - codeGen = new SwitchVarLoop( args ); - break; - - case GenSwitchExp: - if ( feature == GotoFeature ) - codeGen = new SwitchGotoExp( args ); - else if ( feature == BreakFeature ) - codeGen = new SwitchBreakExp( args ); - else - codeGen = new SwitchVarExp( args ); - break; - - - case GenGotoLoop: - if ( feature == GotoFeature ) - codeGen = new GotoLoop(args); - else - id->error() << "unsupported lang/style combination" << endp; - break; - case GenGotoExp: - if ( feature == GotoFeature ) - codeGen = new GotoExp(args); - else - id->error() << "unsupported lang/style combination" << endp; - break; - - case GenIpGoto: - if ( feature == GotoFeature ) - codeGen = new IpGoto(args); - else - id->error() << "unsupported lang/style combination" << endp; - break; - } - - return codeGen; -} diff --git a/libfsm/asm.cc b/libfsm/asm.cc deleted file mode 100644 index ecfe1c0f..00000000 --- a/libfsm/asm.cc +++ /dev/null @@ -1,2046 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "asm.h" -#include "redfsm.h" -#include "gendata.h" -#include "bstmap.h" -#include "ragel.h" -#include "redfsm.h" -#include "bstmap.h" -#include "gendata.h" -#include "parsedata.h" -#include - -using std::ostream; -using std::ostringstream; -using std::string; -using std::endl; -using std::istream; -using std::ifstream; -using std::ostream; -using std::ios; -using std::cin; -using std::endl; - -extern int numSplitPartitions; -bool printStatistics = false; - -/* Enables transition logging in the form that score-based state sorting can - * processes. This bit of code is intended to increase locality and reduce - * cache misses. Gains are minimal, 1-2%. */ -// #define LOG_TRANS 1 - -void asmLineDirective( ostream &out, const char *fileName, int line ) -{ - /* Write the preprocessor line info for to the input file. */ - out << "#line " << line << " \""; - for ( const char *pc = fileName; *pc != 0; pc++ ) { - if ( *pc == '\\' ) - out << "\\\\"; - else - out << *pc; - } - out << '"'; - - out << '\n'; -} - -/* Init code gen with in parameters. */ -AsmCodeGen::AsmCodeGen( const CodeGenArgs &args ) -: - CodeGenData( args ), - nextLmSwitchLabel( 1 ), - stackCS( false ) -{ -} - -void AsmCodeGen::genAnalysis() -{ - /* For directly executable machines there is no required state - * ordering. Choose a depth-first ordering to increase the - * potential for fall-throughs. */ - redFsm->depthFirstOrdering(); - - /* Choose default transitions and make the flat transitions by character class. */ - redFsm->chooseDefaultSpan(); - redFsm->makeFlatClass(); - - /* If any errors have occured in the input file then don't write anything. */ - if ( red->id->errorCount > 0 ) - return; - - redFsm->setInTrans(); - - /* Anlayze Machine will find the final action reference counts, among other - * things. We will use these in reporting the usage of fsm directives in - * action code. */ - red->analyzeMachine(); -} - -/* Write out the fsm name. */ -string AsmCodeGen::FSM_NAME() -{ - return fsmName; -} - -/* Emit the offset of the start state as a decimal integer. */ -string AsmCodeGen::START_STATE_ID() -{ - ostringstream ret; - ret << redFsm->startState->id; - return ret.str(); -}; - -string AsmCodeGen::ACCESS() -{ - ostringstream ret; - if ( red->accessExpr != 0 ) - INLINE_LIST( ret, red->accessExpr, 0, false, false ); - return ret.str(); -} - - -string AsmCodeGen::P() -{ - ostringstream ret; - if ( red->pExpr == 0 ) - ret << "%r12"; - else { - INLINE_LIST( ret, red->pExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::PE() -{ - ostringstream ret; - if ( red->peExpr == 0 ) - ret << "%r13"; - else { - INLINE_LIST( ret, red->peExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::vCS() -{ - ostringstream ret; - if ( red->csExpr == 0 ) { - if ( stackCS ) - ret << "-48(%rbp)"; - else - ret << "%r11"; - } - else { - INLINE_LIST( ret, red->csExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::TOP() -{ - ostringstream ret; - if ( red->topExpr == 0 ) - ret << "-64(%rbp)"; - else { - ret << "("; - INLINE_LIST( ret, red->topExpr, 0, false, false ); - ret << ")"; - } - return ret.str(); -} - -string AsmCodeGen::NFA_STACK() -{ - return string( "-80(%rbp)" ); -} - -string AsmCodeGen::NFA_TOP() -{ - return string( "-88(%rbp)" ); -} - -string AsmCodeGen::NFA_SZ() -{ - return string( "-96(%rbp)" ); -} - -string AsmCodeGen::STACK() -{ - ostringstream ret; - if ( red->stackExpr == 0 ) - ret << "-56(%rbp)"; - else { - ret << "("; - INLINE_LIST( ret, red->stackExpr, 0, false, false ); - ret << ")"; - } - return ret.str(); -} - -string AsmCodeGen::vEOF() -{ - ostringstream ret; - if ( red->eofExpr == 0 ) - ret << "-8(%rbp)"; - else { - INLINE_LIST( ret, red->eofExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::TOKSTART() -{ - ostringstream ret; - if ( red->tokstartExpr == 0 ) - ret << "-16(%rbp)"; - else { - INLINE_LIST( ret, red->tokstartExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::TOKEND() -{ - ostringstream ret; - if ( red->tokendExpr == 0 ) - ret << "-24(%rbp)"; - else { - INLINE_LIST( ret, red->tokendExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::ACT() -{ - ostringstream ret; - if ( red->actExpr == 0 ) - ret << "-32(%rbp)"; - else { - INLINE_LIST( ret, red->actExpr, 0, false, false ); - } - return ret.str(); -} - -string AsmCodeGen::NBREAK() -{ - return string("-33(%rbp)"); -} - -string AsmCodeGen::GET_KEY() -{ - ostringstream ret; - if ( red->getKeyExpr != 0 ) { - /* Emit the user supplied method of retrieving the key. */ - ret << "("; - INLINE_LIST( ret, red->getKeyExpr, 0, false, false ); - ret << ")"; - } - else { - /* Expression for retrieving the key, use simple dereference. */ - ret << "(" << P() << ")"; - } - return ret.str(); -} - -string AsmCodeGen::COND_KEY( CondKey key ) -{ - ostringstream ret; - ret << "$" << key.getVal(); - return ret.str(); -} - - -/* Write out a key from the fsm code gen. Depends on wether or not the key is - * signed. */ -string AsmCodeGen::KEY( Key key ) -{ - ostringstream ret; - ret << "$" << key.getVal(); - return ret.str(); -} - -bool AsmCodeGen::isAlphTypeSigned() -{ - return keyOps->isSigned; -} - -void AsmCodeGen::EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) -{ - /* The parser gives fexec two children. The double brackets are for D - * code. If the inline list is a single word it will get interpreted as a - * C-style cast by the D compiler. */ - - ret << - " subq $1, "; - INLINE_LIST( ret, item->children, targState, inFinish, false ); - ret << - "\n" - " movq "; - INLINE_LIST( ret, item->children, targState, inFinish, false ); - ret << ", " << P() << "\n"; -} - -void AsmCodeGen::LM_SWITCH( ostream &ret, GenInlineItem *item, - int targState, int inFinish, bool csForced ) -{ - long done = nextLmSwitchLabel++; - - ret << - " movq " << ACT() << ", %rax\n"; - - for ( GenInlineList::Iter lma = *item->children; lma.lte(); lma++ ) { - long l = nextLmSwitchLabel++; - - /* Write the case label, the action and the case break. */ - if ( lma->lmId < 0 ) { - } - else { - ret << - " cmpq $" << lma->lmId << ", %rax\n" - " jne " << LABEL( "lm_switch_next", l ) << "\n"; - } - - INLINE_LIST( ret, lma->children, targState, inFinish, csForced ); - - ret << - " jmp " << LABEL( "lm_done", done ) << "\n" - "" << LABEL( "lm_switch_next", l ) << ":\n"; - } - - ret << - "" << LABEL( "lm_done", done ) << ":\n"; -} - -void AsmCodeGen::SET_ACT( ostream &ret, GenInlineItem *item ) -{ - ret << - " movq $" << item->lmId << ", " << ACT() << "\n"; -} - -void AsmCodeGen::SET_TOKEND( ostream &ret, GenInlineItem *item ) -{ - /* Sets tokend, there may be an offset. */ - ret << - " movq " << P() << ", %rax\n"; - - if ( item->offset != 0 ) { - out << - " addq $" << item->offset << ", %rax\n"; - } - - out << - " movq %rax, " << TOKEND() << "\n"; -} - -void AsmCodeGen::GET_TOKEND( ostream &ret, GenInlineItem *item ) -{ - ret << - " movq " << TOKEND() << ", " << "%rax\n"; -} - -void AsmCodeGen::INIT_TOKSTART( ostream &ret, GenInlineItem *item ) -{ - ret << - " movq $0, " << TOKSTART() << "\n"; -} - -void AsmCodeGen::INIT_ACT( ostream &ret, GenInlineItem *item ) -{ - ret << - " movq $0, " << ACT() << "\n"; -} - -void AsmCodeGen::SET_TOKSTART( ostream &ret, GenInlineItem *item ) -{ - ret << - " movq " << P() << ", " << TOKSTART() << "\n"; -} - -void AsmCodeGen::HOST_STMT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - } -} - -void AsmCodeGen::HOST_EXPR( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - } -} - -void AsmCodeGen::HOST_TEXT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - } -} - -void AsmCodeGen::GEN_STMT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - } -} - -void AsmCodeGen::GEN_EXPR( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - } -} - -void AsmCodeGen::LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) -{ - /* The parser gives fexec two children. The double brackets are for D code. - * If the inline list is a single word it will get interpreted as a C-style - * cast by the D compiler. This should be in the D code generator. */ - INLINE_LIST( ret, item->children, targState, inFinish, false ); - - ret << - " movq %rax, " << P() << "\n" - " subq $1, " << P() << "\n"; -} - -void AsmCodeGen::NBREAK( ostream &ret, int targState, bool csForced ) -{ - outLabelUsed = true; - ret << - " addq $1, " << P() << "\n"; - - if ( !csForced ) { - ret << - " movq $" << targState << ", " << vCS() << "\n"; - } - - ret << - " movb $1, " << NBREAK() << "\n" - " jmp " << LABEL( "pop" ) << "\n"; -} - -/* Write out an inline tree structure. Walks the list and possibly calls out - * to virtual functions than handle language specific items in the tree. */ -void AsmCodeGen::INLINE_LIST( ostream &ret, GenInlineList *inlineList, - int targState, bool inFinish, bool csForced ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - switch ( item->type ) { - case GenInlineItem::Text: - ret << item->data; - break; - case GenInlineItem::Goto: - GOTO( ret, item->targState->id, inFinish ); - break; - case GenInlineItem::Call: - CALL( ret, item->targState->id, targState, inFinish ); - break; - case GenInlineItem::Next: - NEXT( ret, item->targState->id, inFinish ); - break; - case GenInlineItem::Ret: - RET( ret, inFinish ); - break; - case GenInlineItem::PChar: - ret << P(); - break; - case GenInlineItem::Char: - ret << GET_KEY(); - break; - case GenInlineItem::Hold: - ret << - " subq $1, " << P() << "\n"; - break; - case GenInlineItem::Exec: - EXEC( ret, item, targState, inFinish ); - break; - case GenInlineItem::Curs: - CURS( ret, inFinish ); - break; - case GenInlineItem::Targs: - TARGS( ret, inFinish, targState ); - break; - case GenInlineItem::Entry: - ret << item->targState->id; - break; - case GenInlineItem::GotoExpr: - GOTO_EXPR( ret, item, inFinish ); - break; - case GenInlineItem::CallExpr: - CALL_EXPR( ret, item, targState, inFinish ); - break; - case GenInlineItem::NextExpr: - NEXT_EXPR( ret, item, inFinish ); - break; - case GenInlineItem::LmSwitch: - LM_SWITCH( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::LmSetActId: - SET_ACT( ret, item ); - break; - case GenInlineItem::LmSetTokEnd: - SET_TOKEND( ret, item ); - break; - case GenInlineItem::LmGetTokEnd: - GET_TOKEND( ret, item ); - break; - case GenInlineItem::LmInitTokStart: - INIT_TOKSTART( ret, item ); - break; - case GenInlineItem::LmInitAct: - INIT_ACT( ret, item ); - break; - case GenInlineItem::LmSetTokStart: - SET_TOKSTART( ret, item ); - break; - case GenInlineItem::Break: - BREAK( ret, targState, csForced ); - break; - /* Stubbed. */ - case GenInlineItem::Ncall: - NCALL( ret, item->targState->id, targState, inFinish ); - break; - case GenInlineItem::NcallExpr: - NCALL_EXPR( ret, item, targState, inFinish ); - break; - case GenInlineItem::Nret: - NRET( ret, inFinish ); - break; - case GenInlineItem::Nbreak: - NBREAK( ret, targState, csForced ); - break; - case GenInlineItem::LmCase: - break; - - case GenInlineItem::LmExec: - LM_EXEC( ret, item, targState, inFinish ); - break; - - case GenInlineItem::LmHold: - ret << - " subq $1, " << P() << "\n"; - break; - case GenInlineItem::NfaClear: - ret << - " movq $0, " << NFA_TOP() << "\n"; - break; - - case GenInlineItem::HostStmt: - HOST_STMT( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::HostExpr: - HOST_EXPR( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::HostText: - HOST_TEXT( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::GenStmt: - GEN_STMT( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::GenExpr: - GEN_EXPR( ret, item, targState, inFinish, csForced ); - break; - /* Handled at the top level. */ - case GenInlineItem::NfaWrapAction: - case GenInlineItem::NfaWrapConds: - break; - } - } -} -/* Write out paths in line directives. Escapes any special characters. */ -string AsmCodeGen::LDIR_PATH( char *path ) -{ - ostringstream ret; - for ( char *pc = path; *pc != 0; pc++ ) { - if ( *pc == '\\' ) - ret << "\\\\"; - else - ret << *pc; - } - return ret.str(); -} - -void AsmCodeGen::ACTION( ostream &ret, GenAction *action, int targState, - bool inFinish, bool csForced ) -{ - /* Write the preprocessor line info for going into the source file. */ - asmLineDirective( ret, action->loc.fileName, action->loc.line ); - - /* Write the block and close it off. */ - INLINE_LIST( ret, action->inlineList, targState, inFinish, csForced ); -} - -void AsmCodeGen::CONDITION( ostream &ret, GenAction *condition ) -{ - ret << "\n"; - asmLineDirective( ret, condition->loc.fileName, condition->loc.line ); - INLINE_LIST( ret, condition->inlineList, 0, false, false ); -} - -bool singleItem( GenAction *action, GenInlineItem::Type type ) -{ - return action->inlineList->length() == 1 && - action->inlineList->head->type == type; -} - -void AsmCodeGen::NFA_CONDITION( ostream &ret, GenAction *condition, bool last ) -{ - if ( singleItem( condition, GenInlineItem::NfaWrapAction ) ) - { - GenAction *action = condition->inlineList->head->wrappedAction; - ACTION( out, action, 0, false, false ); - } - else if ( singleItem( condition, GenInlineItem::NfaWrapConds ) ) - { - GenCondSpace *condSpace = condition->inlineList->head->condSpace; - const CondKeySet &condKeySet = condition->inlineList->head->condKeySet; - - out << " movq $0, %r9\n"; - for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - out << - " pushq %r9\n"; - - CONDITION( out, *csi ); - out << - "\n" - " test %eax, %eax\n" - " setne %cl\n" - " movsbq %cl, %rcx\n" - " salq $" << csi.pos() << ", %rcx\n" - " popq %r9\n" - " addq %rcx, %r9\n"; - } - - for ( int c = 0; c < condKeySet.length(); c++ ) { - CondKey key = condKeySet[c]; - out << - " cmpq " << COND_KEY( key ) << ", %r9\n" - " je 102f\n"; - } - - out << - " jmp " << LABEL( "pop_fail" ) << "\n" - "102:\n"; - } - else { - CONDITION( ret, condition ); - out << - " test %eax, %eax\n" - " jz " << LABEL( "pop_fail" ) << "\n"; - } -} - -string AsmCodeGen::ERROR_STATE() -{ - ostringstream ret; - if ( redFsm->errState != 0 ) - ret << redFsm->errState->id; - else - ret << "-1"; - return ret.str(); -} - -string AsmCodeGen::FIRST_FINAL_STATE() -{ - ostringstream ret; - if ( redFsm->firstFinState != 0 ) - ret << redFsm->firstFinState->id; - else - ret << redFsm->nextStateId; - return ret.str(); -} - -void AsmCodeGen::writeInit() -{ - if ( !noCS ) { - /* Don't use vCS here. vCS may assumes CS needs to be on the stack. - * Just use the interface register. */ - out << - " movq $" << redFsm->startState->id << ", %r11\n"; - } - - if ( redFsm->anyNfaStates() ) { - out << - " movq $0, " << NFA_TOP() << "\n"; - } - - /* If there are any calls, then the stack top needs initialization. */ - if ( redFsm->anyActionCalls() || redFsm->anyActionRets() ) { - out << - " movq $0, " << TOP() << "\n"; - } - - if ( red->hasLongestMatch ) { - out << - " movq $0, " << TOKSTART() << "\n" - " movq $0, " << TOKEND() << "\n" - " movq $0, " << ACT() << "\n"; - } -} - -string AsmCodeGen::DATA_PREFIX() -{ - if ( !noPrefix ) - return FSM_NAME() + "_"; - return ""; -} - -/* Emit the alphabet data type. */ -string AsmCodeGen::ALPH_TYPE() -{ - string ret = alphType->data1; - if ( alphType->data2 != 0 ) { - ret += " "; - ret += + alphType->data2; - } - return ret; -} - -void AsmCodeGen::STATIC_CONST_INT( const string &name, const string &value ) -{ - out << - " .align 8\n" - " .type " << name << ", @object\n" - " .size " << name << ", 8\n" << - name << ":\n" - " .long " << value << "\n"; -} - -void AsmCodeGen::STATE_IDS() -{ - if ( redFsm->startState != 0 ) - STATIC_CONST_INT( START(), START_STATE_ID() ); - - if ( !noFinal ) - STATIC_CONST_INT( FIRST_FINAL(), FIRST_FINAL_STATE() ); - - if ( !noError ) - STATIC_CONST_INT( ERROR(), ERROR_STATE() ); - - out << "\n"; - - if ( red->entryPointNames.length() > 0 ) { - for ( EntryNameVect::Iter en = red->entryPointNames; en.lte(); en++ ) { - ostringstream ret; - ret << redFsm->startState->id; - - STATIC_CONST_INT( string( DATA_PREFIX() + "en_" + *en ), - ret.str() ); - } - out << "\n"; - } -} - -void AsmCodeGen::writeStart() -{ - out << START_STATE_ID(); -} - -void AsmCodeGen::writeFirstFinal() -{ - out << FIRST_FINAL_STATE(); -} - -void AsmCodeGen::writeError() -{ - out << ERROR_STATE(); -} - -string AsmCodeGen::PTR_CONST() -{ - return "const "; -} - -string AsmCodeGen::PTR_CONST_END() -{ - return ""; -} - -std::ostream &AsmCodeGen::OPEN_ARRAY( string type, string name ) -{ - out << "static const " << type << " " << name << "[] = {\n"; - return out; -} - -std::ostream &AsmCodeGen::CLOSE_ARRAY() -{ - return out << "};\n"; -} - -std::ostream &AsmCodeGen::STATIC_VAR( string type, string name ) -{ - out << "static const " << type << " " << name; - return out; -} - -string AsmCodeGen::UINT( ) -{ - return "unsigned int"; -} - -string AsmCodeGen::ARR_OFF( string ptr, string offset ) -{ - return ptr + " + " + offset; -} - -string AsmCodeGen::CAST( string type ) -{ - return "(" + type + ")"; -} - -string AsmCodeGen::NULL_ITEM() -{ - return "0"; -} - -string AsmCodeGen::POINTER() -{ - return " *"; -} - -std::ostream &AsmCodeGen::SWITCH_DEFAULT() -{ - return out; -} - -string AsmCodeGen::CTRL_FLOW() -{ - return ""; -} - -void AsmCodeGen::writeExports() -{ - if ( red->exportList.length() > 0 ) { - for ( ExportList::Iter ex = red->exportList; ex.lte(); ex++ ) { - out << "#define " << DATA_PREFIX() << "ex_" << ex->name << " " << - KEY(ex->key) << "\n"; - } - out << "\n"; - } -} - -string AsmCodeGen::LABEL( const char *type, long i ) -{ - std::stringstream s; - s << ".L" << red->machineId << "_" << type << "_" << i; - return s.str(); -} - -string AsmCodeGen::LABEL( const char *name ) -{ - std::stringstream s; - s << ".L" << red->machineId << "_" << name; - return s.str(); -} - -void AsmCodeGen::emitSingleIfElseIf( RedStateAp *state ) -{ - /* Load up the singles. */ - int numSingles = state->outSingle.length(); - RedTransEl *data = state->outSingle.data; - - /* Write out the single indices. */ - for ( int j = 0; j < numSingles; j++ ) { - out << - " cmpb " << KEY( data[j].lowKey ) << ", %r10b\n" - " je " << TRANS_GOTO_TARG( data[j].value ) << "\n"; - } -} - -void AsmCodeGen::emitSingleJumpTable( RedStateAp *state, string def ) -{ - int numSingles = state->outSingle.length(); - RedTransEl *data = state->outSingle.data; - - long long low = data[0].lowKey.getVal(); - long long high = data[numSingles-1].lowKey.getVal(); - - if ( def.size() == 0 ) - def = LABEL( "sjf", state->id ); - - out << - " movzbq %r10b, %rax\n" - " subq $" << low << ", %rax\n" - " cmpq $" << (high - low) << ", %rax\n" - " ja " << def << "\n" - " leaq " << LABEL( "sjt", state->id ) << "(%rip), %rcx\n" - " movslq (%rcx,%rax,4), %rdx\n" - " addq %rcx, %rdx\n" - " jmp *%rdx\n" - " .section .rodata\n" - " .align 4\n" - << LABEL( "sjt", state->id ) << ":\n"; - - for ( long long j = 0; j < numSingles; j++ ) { - /* Fill in gap between prev and this. */ - if ( j > 0 ) { - long long span = keyOps->span( data[j-1].lowKey, data[j].lowKey ) - 2; - for ( long long k = 0; k < span; k++ ) { - out << " .long " << def << " - " << - LABEL( "sjt", state->id ) << "\n"; - } - } - - out << " .long " << TRANS_GOTO_TARG( data[j].value ) << " - " << - LABEL( "sjt", state->id ) << "\n"; - } - - out << - " .text\n" - "" << LABEL( "sjf", state->id ) << ":\n"; -} - - -void AsmCodeGen::emitRangeBSearch( RedStateAp *state, int low, int high ) -{ - static int nl = 1; - - /* Get the mid position, staying on the lower end of the range. */ - int mid = (low + high) >> 1; - RedTransEl *data = state->outRange.data; - - /* Determine if we need to look higher or lower. */ - bool anyLower = mid > low; - bool anyHigher = mid < high; - - /* Determine if the keys at mid are the limits of the alphabet. */ - bool limitLow = keyOps->eq( data[mid].lowKey, keyOps->minKey ); - bool limitHigh = keyOps->eq( data[mid].highKey, keyOps->maxKey ); - -// string nf = TRANS_GOTO_TARG( state->defTrans ); - - /* For some reason the hop is faster and results in smaller code. Not sure - * why. */ - string nf = LABEL( "nf", state->id ); - - if ( anyLower && anyHigher ) { - int l1 = nl++; - string targ = TRANS_GOTO_TARG( data[mid].value ); - - /* Can go lower and higher than mid. */ - out << - " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" - " jge " << LABEL( "nl", l1 ) << "\n"; - - - emitRangeBSearch( state, low, mid-1 ); - - out << - LABEL( "nl", l1 ) << ":\n"; - - if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { - out << - " cmpb " << KEY ( data[mid].highKey ) << ", %r10b\n"; - } - - out << - " jle " << targ << "\n"; - - emitRangeBSearch( state, mid+1, high ); - } - else if ( anyLower && !anyHigher ) { - - string targ; - if ( limitHigh ) - targ = TRANS_GOTO_TARG( data[mid].value ); - else - targ = LABEL( "nl", nl++ ); - - /* Can go lower than mid but not higher. */ - - out << - " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" - " jge " << targ << "\n"; - - emitRangeBSearch( state, low, mid-1 ); - - /* If the higher is the highest in the alphabet then there is no sense - * testing it. */ - if ( !limitHigh ) { - - out << - targ << ":\n"; - - if ( ! keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { - out << - " cmpb " << KEY ( data[mid].highKey ) << ", %r10b\n"; - } - - out << - " jg " << nf << "\n"; - - TRANS_GOTO( data[mid].value ); - } - } - else if ( !anyLower && anyHigher ) { - string targ; - if ( limitLow ) - targ = TRANS_GOTO_TARG( data[mid].value ); - else - targ = LABEL( "nl", nl++ ); - - /* Can go higher than mid but not lower. */ - - out << - " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n" - " jle " << targ << "\n"; - - emitRangeBSearch( state, mid+1, high ); - - /* If the lower end is the lowest in the alphabet then there is no - * sense testing it. */ - if ( !limitLow ) { - - out << - targ << ":\n"; - - if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { - out << - " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"; - } - - out << - " jl " << nf << "\n"; - - TRANS_GOTO( data[mid].value ); - } - } - else { - /* Cannot go higher or lower than mid. It's mid or bust. What - * tests to do depends on limits of alphabet. */ - if ( !limitLow && !limitHigh ) { - - if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { - out << - " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" - " jl " << nf << "\n" - " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n" - " jg " << nf << "\n"; - } - else { - out << - " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" - " jne " << nf << "\n"; - } - - TRANS_GOTO( data[mid].value ); - } - else if ( limitLow && !limitHigh ) { - - out << - " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n" - " jg " << nf << "\n"; - - TRANS_GOTO( data[mid].value ); - } - else if ( !limitLow && limitHigh ) { - - out << - " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" - " jl " << nf << "\n"; - - TRANS_GOTO( data[mid].value ); - } - else { - /* Both high and low are at the limit. No tests to do. */ - TRANS_GOTO( data[mid].value ); - } - } -} - -void AsmCodeGen::emitCharClassIfElseIf( RedStateAp *st ) -{ - long long span = st->high - st->low + 1; - for ( long long pos = 0; pos < span; pos++ ) { - out << - " cmpb " << KEY( st->low + pos ) << ", %r10b\n" - " je " << TRANS_GOTO_TARG( st->transList[pos] ) << "\n"; - } -} - -void AsmCodeGen::emitCharClassJumpTable( RedStateAp *st, string def ) -{ - long long low = st->low; - long long high = st->high; - - if ( def.size() == 0 ) - def = LABEL( "ccf", st->id ); - - out << - " movzbq %r10b, %rax\n" - " subq $" << low << ", %rax\n" - " cmpq $" << (high - low) << ", %rax\n" - " ja " << def << "\n" - " leaq " << LABEL( "cct", st->id ) << "(%rip), %rcx\n" - " movslq (%rcx,%rax,4), %rdx\n" - " addq %rcx, %rdx\n" - " jmp *%rdx\n" - " .section .rodata\n" - " .align 4\n" - << LABEL( "cct", st->id ) << ":\n"; - - long long span = st->high - st->low + 1; - for ( long long pos = 0; pos < span; pos++ ) { - out << " .long " << TRANS_GOTO_TARG( st->transList[pos] ) << " - " << - LABEL( "cct", st->id ) << "\n"; - } - - out << - " .text\n" - "" << LABEL( "ccf", st->id ) << ":\n"; -} - -void AsmCodeGen::NFA_PUSH( RedStateAp *st ) -{ - if ( st->nfaTargs != 0 && st->nfaTargs->length() > 0 ) { - if ( red->nfaPrePushExpr != 0 ) { - out << " movq $" << st->nfaTargs->length() << ", %rdi\n"; - INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); - } - - for ( RedNfaTargs::Iter t = *st->nfaTargs; t.lte(); t++ ) { - out << - " movq " << NFA_STACK() << ", %rax\n" - " movq " << NFA_TOP() << ", %rcx\n" - " imulq $24, %rcx\n" - " movq $" << t->state->id << ", 0(%rax,%rcx,)\n" - " movq " << P() << ", 8(%rax,%rcx,)\n"; - - out << - " # pop action id " << t->id << "\n" - " movq $" << t->id << ", 16(%rax,%rcx,)\n"; - - if ( t->push ) { - for ( GenActionTable::Iter item = t->push->key; item.lte(); item++ ) { - ACTION( out, item->value, st->id, false, - t->push->anyNextStmt() ); - out << "\n"; - } - } - - out << - " movq " << NFA_TOP() << ", %rcx\n" - " addq $1, %rcx\n" - " movq %rcx, " << NFA_TOP() << "\n"; - } - } -} - -void AsmCodeGen::STATE_GOTOS() -{ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Writing code above state gotos. */ - IN_TRANS_ACTIONS( st ); - - if ( st->labelNeeded ) - out << LABEL( "st", st->id ) << ":\n"; - - - /* need to do this if the transition is an eof transition, or if the action - * contains fexec. Otherwise, no need. */ - if ( eof ) { - out << - " cmpq " << P() << ", " << vEOF() << "\n"; - - if ( st->isFinal ) - out << " je " << LABEL( "out", st->id ) << "\n"; - else - out << " je " << LABEL( "pop", st->id ) << "\n"; - } - - if ( st->toStateAction != 0 ) { - /* Remember that we wrote an action. Write every action in the list. */ - for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) { - ACTION( out, item->value, st->id, false, - st->toStateAction->anyNextStmt() ); - out << "\n"; - } - } - - if ( st == redFsm->errState ) { - out << LABEL( "en", st->id ) << ":\n"; - - /* Break out here. */ - outLabelUsed = true; - - out << - " movq $" << st->id << ", " << vCS() << "\n" - " jmp " << LABEL( "pop" ) << "\n"; - } - else { - /* Advance and test buffer pos. */ - if ( st->labelNeeded ) { - out << - " addq $1, " << P() << "\n"; - - } - - /* This is the entry label for starting a run. */ - out << LABEL( "en", st->id ) << ":\n"; - - if ( !noEnd ) { - if ( eof ) { - out << - " cmpq " << P() << ", " << PE() << "\n" - " jne " << LABEL( "nope", st->id ) << "\n" << - " cmpq " << P() << ", " << vEOF() << "\n" - " jne " << LABEL( "out", st->id ) << "\n" << - LABEL( "nope", st->id ) << ":\n"; - } - else { - out << - " cmpq " << P() << ", " << PE() << "\n" - " je " << LABEL( "out", st->id ) << "\n"; - } - } - - NFA_PUSH( st ); - - if ( st->fromStateAction != 0 ) { - /* Remember that we wrote an action. Write every action in the list. */ - for ( GenActionTable::Iter item = st->fromStateAction->key; - item.lte(); item++ ) - { - ACTION( out, item->value, st->id, false, - st->fromStateAction->anyNextStmt() ); - out << "\n"; - } - } - - if ( !noEnd && eof ) { - out << - " cmpq " << P() << ", " << vEOF() << "\n" - " jne " << LABEL( "neofd", st->id ) << "\n"; - - if ( st->eofTrans != 0 ) - TRANS_GOTO( st->eofTrans ); - else { - if ( st->isFinal || !redFsm->anyNfaStates() ) - out << "jmp " << LABEL( "out", st->id ) << "\n"; - else - out << "jmp " << LABEL( "pop", st->id ) << "\n"; - } - - out << - " jmp " << LABEL( "deofd", st->id ) << "\n"; - - out << LABEL( "neofd", st->id ) << ":\n"; - } - - /* Record the prev state if necessary. */ - if ( st->anyRegCurStateRef() ) { - out << - " movq $" << st->id << ", -72(%rbp)\n"; - } - - -#ifdef LOG_TRANS - out << - " movzbl (" << P() << "), %r10d\n" - " movq $" << machineId << ", %rdi\n" - " movq $" << st->id << ", %rsi\n" - " movslq %r10d, %rdx\n" - " call " << LABEL( "log_trans" ) << "\n" - ; -#endif - - /* Load *p. */ - if ( st->transList != 0 ) { - long lowKey = redFsm->lowKey.getVal(); - long highKey = redFsm->highKey.getVal(); - - out << - " movzbl (" << P() << "), %r10d\n" - " cmpl $" << lowKey << ", %r10d\n" - " jl " << LABEL( "nf", st->id ) << "\n" - " cmpl $" << highKey << ", %r10d\n" - " jg " << LABEL( "nf", st->id ) << "\n" - " subl " << KEY( lowKey ) << ", %r10d\n" - " leaq " << LABEL( "char_class" ) << "(%rip), %rcx\n" - " movslq %r10d, %rax\n" - " movb (%rcx, %rax), %r10b\n" - ; - - - long len = ( st->high - st->low + 1 ); - - if ( len < 8 ) - emitCharClassIfElseIf( st ); - else { - string def; - if ( st->outRange.length() == 0 ) - def = TRANS_GOTO_TARG( st->defTrans ); - emitCharClassJumpTable( st, def ); - } - } - - /* Write the default transition. */ - out << LABEL( "nf", st->id ) << ":\n"; - TRANS_GOTO( st->defTrans ); - - if ( !noEnd && eof ) { - out << LABEL( "deofd", st->id) << ":\n"; - } - } - } -} - -unsigned int AsmCodeGen::TO_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->toStateAction != 0 ) - act = state->toStateAction->location+1; - return act; -} - -unsigned int AsmCodeGen::FROM_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->fromStateAction != 0 ) - act = state->fromStateAction->location+1; - return act; -} - -unsigned int AsmCodeGen::EOF_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->eofAction != 0 ) - act = state->eofAction->location+1; - return act; -} - -bool AsmCodeGen::useAgainLabel() -{ - return redFsm->anyActionRets() || - redFsm->anyActionByValControl() || - redFsm->anyRegNextStmt(); -} - -void AsmCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish ) -{ - ret << - " jmp " << LABEL( "st", gotoDest ) << "\n"; -} - -void AsmCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - if ( red->prePushExpr != 0 ) - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - - ret << - " movq " << STACK() << ", %rax\n" - " movq " << TOP() << ", %rcx\n" - " movq $" << targState << ", (%rax, %rcx, 8)\n" - " addq $1, %rcx\n" - " movq %rcx, " << TOP() << "\n" - ; - - ret << - " jmp " << LABEL( "st", callDest ) << "\n"; - ; -} - -void AsmCodeGen::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - if ( red->prePushExpr != 0 ) - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - - ret << - "\n" - " movq "; - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << ", %rdx\n" - "\n" - " movq " << STACK() << ", %rax\n" - " movq " << TOP() << ", %rcx\n" - " movq $" << targState << ", (%rax, %rcx, 8)\n" - " addq $1, %rcx\n" - " movq %rcx, " << TOP() << "\n" - " movq %rdx, " << vCS() << "\n" - ; - - ret << - " jmp " << LABEL( "again" ) << "\n"; -} - -void AsmCodeGen::RET( ostream &ret, bool inFinish ) -{ - ret << - " movq " << STACK() << ", %rax\n" - " movq " << TOP() << ", %rcx\n" - " subq $1, %rcx\n" - " movq (%rax, %rcx, 8), %rax\n" - " movq %rax, " << vCS() << "\n" - " movq %rcx, " << TOP() << "\n"; - - if ( red->postPopExpr != 0 ) - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - - ret << - " jmp " << LABEL("again") << "\n"; -} - -void AsmCodeGen::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << " movq "; - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << ", " << vCS() << "\n"; - - ret << - " jmp " << LABEL("again") << "\n"; -} - -void AsmCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish ) -{ - ret << - " movq $" << nextDest << ", " << vCS() << "\n"; -} - -void AsmCodeGen::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << " movq "; - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << ", " << vCS() << "\n"; -} - -void AsmCodeGen::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - if ( red->prePushExpr != 0 ) - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - - ret << - " movq " << STACK() << ", %rax\n" - " movq " << TOP() << ", %rcx\n" - " movq $" << targState << ", (%rax, %rcx, 8)\n" - " addq $1, %rcx\n" - " movq %rcx, " << TOP() << "\n" - " movq $" << callDest << ", " << vCS() << "\n"; -} - -void AsmCodeGen::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, - int targState, bool inFinish ) -{ - if ( red->prePushExpr != 0 ) - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - - ret << - "\n" - " movq "; - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << ", %rdx\n" - "\n" - " movq " << STACK() << ", %rax\n" - " movq " << TOP() << ", %rcx\n" - " movq $" << targState << ", (%rax, %rcx, 8)\n" - " addq $1, %rcx\n" - " movq %rcx, " << TOP() << "\n" - " movq %rdx, " << vCS() << "\n"; -} - -void AsmCodeGen::NRET( ostream &ret, bool inFinish ) -{ - ret << - " movq " << STACK() << ", %rax\n" - " movq " << TOP() << ", %rcx\n" - " subq $1, %rcx\n" - " movq (%rax, %rcx, 8), %rax\n" - " movq %rax, " << vCS() << "\n" - " movq %rcx, " << TOP() << "\n"; - - if ( red->postPopExpr != 0 ) - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); -} - -void AsmCodeGen::CURS( ostream &ret, bool inFinish ) -{ - ret << - " movq -72(%rbp), %rax\n"; -} - -void AsmCodeGen::TARGS( ostream &ret, bool inFinish, int targState ) -{ - ret << - " movq $" << targState << ", %rax\n"; -} - -void AsmCodeGen::BREAK( ostream &ret, int targState, bool csForced ) -{ - outLabelUsed = true; - ret << "{" << P() << "++; "; - if ( !csForced ) - ret << vCS() << " = " << targState << "; "; - ret << CTRL_FLOW() << "goto _out;}"; -} - -bool AsmCodeGen::IN_TRANS_ACTIONS( RedStateAp *state ) -{ - bool anyWritten = false; - - /* Emit any transitions that have actions and that go to this state. */ - for ( int it = 0; it < state->numInCondTests; it++ ) { - /* Write the label for the transition so it can be jumped to. */ - RedTransAp *trans = state->inCondTests[it]; - out << LABEL( "ctr", trans->id ) << ":\n"; - - if ( trans->condSpace->condSet.length() == 1 ) { - RedCondPair *tp, *fp; - if ( trans->numConds() == 1 ) { - /* The single condition is either false or true, errCond is the - * opposite. */ - if ( trans->outCondKey(0) == 0 ) { - fp = trans->outCond(0); - tp = trans->errCond(); - } - else { - tp = trans->outCond(0); - fp = trans->errCond(); - } - } - else { - /* Full list, goes false, then true. */ - fp = trans->outCond(0); - tp = trans->outCond(1); - } - - GenCondSet::Iter csi = trans->condSpace->condSet; - CONDITION( out, *csi ); - - out << - " test %eax, %eax\n" - " je " << TRANS_GOTO_TARG( fp ) << "\n" - " jmp " << TRANS_GOTO_TARG( tp ) << "\n"; - } - else { - out << " movq $0, %r9\n"; - - for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { - out << - " pushq %r9\n"; - - CONDITION( out, *csi ); - out << - "\n" - " test %eax, %eax\n" - " setne %cl\n" - " movsbq %cl, %rcx\n" - " salq $" << csi.pos() << ", %rcx\n" - " popq %r9\n" - " addq %rcx, %r9\n"; - } - - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - RedCondPair *pair = trans->outCond( c ); - out << - " cmpq " << COND_KEY( key ) << ", %r9\n" - " je " << TRANS_GOTO_TARG( pair ) << "\n"; - - } - - RedCondPair *err = trans->errCond(); - if ( err != 0 ) { - out << - " jmp " << TRANS_GOTO_TARG( err ) << "\n"; - } - } - } - - /* Emit any transitions that have actions and that go to this state. */ - for ( int it = 0; it < state->numInConds; it++ ) { - RedCondPair *pair = state->inConds[it]; - if ( pair->action != 0 /* && pair->labelNeeded */ ) { - /* Remember that we wrote an action so we know to write the - * line directive for going back to the output. */ - anyWritten = true; - - /* Write the label for the transition so it can be jumped to. */ - out << LABEL( "tr", pair->id ) << ":\n"; - - /* If the action contains a next, then we must preload the current - * state since the action may or may not set it. */ - if ( pair->action->anyNextStmt() ) { - out << - " movq $" << pair->targ->id << ", " << vCS() << "\n"; - } - - if ( redFsm->anyRegNbreak() ) { - out << - " movb $0, " << NBREAK() << "\n"; - } - - /* Write each action in the list. */ - for ( GenActionTable::Iter item = pair->action->key; item.lte(); item++ ) { - ACTION( out, item->value, pair->targ->id, false, - pair->action->anyNextStmt() ); - out << "\n"; - } - - if ( redFsm->anyRegNbreak() ) { - out << - " cmpb $0, " << NBREAK() << "\n" - " jne " << LABEL( "pop" ) << "\n"; - outLabelUsed = true; - } - - - /* If the action contains a next then we need to reload, otherwise - * jump directly to the target state. */ - if ( pair->action->anyNextStmt() ) - out << " jmp " << LABEL( "again" ) << "\n"; - else - out << " jmp " << LABEL( "st", pair->targ->id ) << "\n"; - } - } - - return anyWritten; -} - -std::string AsmCodeGen::TRANS_GOTO_TARG( RedCondPair *pair ) -{ - std::stringstream s; - if ( pair->action != 0 ) { - /* Go to the transition which will go to the state. */ - s << LABEL( "tr", pair->id ); - } - else { - /* Go directly to the target state. */ - s << LABEL( "st", pair->targ->id ); - } - return s.str(); -} - -std::string AsmCodeGen::TRANS_GOTO_TARG( RedTransAp *trans ) -{ - if ( trans->condSpace != 0 ) { - /* Need to jump to the trans since there are conditions. */ - return LABEL( "ctr", trans->id ); - } - else { - return TRANS_GOTO_TARG( &trans->p ); - } -} - -/* Emit the goto to take for a given transition. */ -std::ostream &AsmCodeGen::TRANS_GOTO( RedTransAp *trans ) -{ - out << " jmp " << TRANS_GOTO_TARG( trans ) << "\n"; - return out; -} - -std::ostream &AsmCodeGen::EXIT_STATES() -{ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - out << - LABEL( "out", st->id ) << ":\n" - " movq $" << st->id << ", " << vCS() << "\n" - " jmp " << LABEL( "out" ) << "\n"; - - out << - LABEL( "pop", st->id ) << ":\n" - " movq $" << st->id << ", " << vCS() << "\n" - " jmp " << LABEL( "pop" ) << "\n"; - } - return out; -} - -std::ostream &AsmCodeGen::AGAIN_CASES() -{ - /* Jump into the machine based on the current state. */ - out << - " leaq " << LABEL( "again_jmp" ) << "(%rip), %rcx\n"; - - if ( stackCS ) { - out << - " movq " << vCS() << ", %r11\n"; - } - - out << - " movq (%rcx,%r11,8), %rcx\n" - " jmp *%rcx\n" - " .section .rodata\n" - " .align 8\n" - << LABEL( "again_jmp" ) << ":\n"; - - for ( int stId = 0; stId < redFsm->stateList.length(); stId++ ) { - out << - " .quad " << LABEL( "st", stId ) << "\n"; - } - - out << - " .text\n"; - - return out; -} - -std::ostream &AsmCodeGen::ENTRY_CASES() -{ - out << - " movq (%rcx,%r11,8), %rcx\n" - " jmp *%rcx\n" - " .section .rodata\n" - " .align 8\n" - << LABEL( "entry_jmp" ) << ":\n"; - - for ( int stId = 0; stId < redFsm->stateList.length(); stId++ ) { - out << - " .quad " << LABEL( "en", stId ) << "\n"; - } - - out << - " .text\n"; - return out; -} - - -std::ostream &AsmCodeGen::FINISH_CASES() -{ - /* The current state is in %rax. */ - /*long done = */ nextLmSwitchLabel++; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - out << - " cmpq $" << st->id << ", %rax\n" - " jne " << LABEL( "fc", st->id ) << "\n"; - - if ( st->fromStateAction != 0 ) { - /* Remember that we wrote an action. Write every action in the list. */ - for ( GenActionTable::Iter item = st->fromStateAction->key; - item.lte(); item++ ) - { - ACTION( out, item->value, st->id, false, - st->fromStateAction->anyNextStmt() ); - out << "\n"; - } - } - - out << - " jmp " << TRANS_GOTO_TARG( st->eofTrans ) << "\n" << - LABEL( "fc", st->id ) << ":\n"; - } - } - - return out; -} - -void AsmCodeGen::setLabelsNeeded( GenInlineList *inlineList ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - switch ( item->type ) { - case GenInlineItem::Goto: case GenInlineItem::Call: { - /* Mark the target as needing a label. */ - item->targState->labelNeeded = true; - break; - } - default: break; - } - - if ( item->children != 0 ) - setLabelsNeeded( item->children ); - } -} - -void AsmCodeGen::setLabelsNeeded( RedCondPair *pair ) -{ - /* If there is no action with a next statement, then the label will be - * needed. */ - if ( pair->action == 0 || !pair->action->anyNextStmt() ) - pair->targ->labelNeeded = true; - - /* Need labels for states that have goto or calls in action code - * invoked on characters (ie, not from out action code). */ - if ( pair->action != 0 ) { - /* Loop the actions. */ - for ( GenActionTable::Iter act = pair->action->key; act.lte(); act++ ) { - /* Get the action and walk it's tree. */ - setLabelsNeeded( act->value->inlineList ); - } - } -} - -/* Set up labelNeeded flag for each state. */ -void AsmCodeGen::setLabelsNeeded() -{ - /* If we use the _again label, then we the _again switch, which uses all - * labels. */ - if ( useAgainLabel() ) { - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - st->labelNeeded = true; - } - else { - /* Do not use all labels by default, init all labelNeeded vars to false. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - st->labelNeeded = false; - - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - if ( trans->condSpace == 0 ) - setLabelsNeeded( &trans->p ); - } - - for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) - setLabelsNeeded( &cond->p ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofAction != 0 ) { - for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) - setLabelsNeeded( item->value->inlineList ); - } - } - } - - if ( !noEnd ) { - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - st->outNeeded = st->labelNeeded; - } -} - -void AsmCodeGen::writeData() -{ - STATE_IDS(); - - long long maxSpan = keyOps->span( redFsm->lowKey, redFsm->highKey ); - - out << - " .type " << LABEL( "char_class" ) << ", @object\n" << - LABEL( "char_class" ) << ":\n"; - - for ( long long pos = 0; pos < maxSpan; pos++ ) { - out << - " .byte " << redFsm->classMap[pos] << "\n"; - } - -#ifdef LOG_TRANS - out << - LABEL( "fmt_log_trans" ) << ":\n" - " .string \"%i %i %i\\n\"\n"; -#endif -} - -void AsmCodeGen::setNfaIds() -{ - long nextId = 1; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) { - targ->id = nextId; - nextId += 1; - } - } - } -} - -void AsmCodeGen::writeExec() -{ - /* Must set labels immediately before writing because we may depend on the - * noend write option. */ - setLabelsNeeded(); - testEofUsed = false; - outLabelUsed = false; - - setNfaIds(); - - /* If there are eof actions then we need to run code after exporting the - * final state to vCS. Since the interface register is calee-save, we need - * it to live on the stack. */ - stackCS = redFsm->anyEofActivity(); - - /* - * This code needs 88 bytes of stack (offset 0 from %rbp). - * - * cv : %r9 -- caller-save, used internally, condition char, undefined in - * conditions and actions, can use - * - * pc : %r10b -- caller-save, used internally, undefined in conditions - * actions, can use - * - * cs : %r11 -- caller-save, written by write init, read and - * written by exec, undefined in conditions and actions - * - * p : %r12 -- callee-save, interface, persistent - * - * pe : %r13 -- callee-save, interface, persistent - * - * eof: -8(%rbp) - * - * ts: -16(%rbp) - * - * te: -24(%rbp) - * - * act: -32(%rbp) - * - * _nbreak: -40(%rbp) - * - * stackCS: -48(%rbp) - * - * stack: -56(%rbp) - * top: -64(%rbp) - * - * _ps: -72(%rbp) - * - * nfa_stack -80(%rbp) - * nfa_top -88(%rbp) - * nfa_sz -96(%rbp) - */ - - if ( redFsm->anyRegCurStateRef() ) { - out << - " movq $0, -72(%rbp)\n"; - } - - if ( stackCS ) { - /* Only need a persistent cs in the case of eof actions when exiting the - * block. Where CS lives is a matter of performance though, so we should - * only do this if necessary. */ - out << - " movq %r11, " << vCS() << "\n"; - } - - if ( useAgainLabel() ) { - out << - " jmp " << LABEL( "resume" ) << "\n" - << LABEL( "again" ) << ":\n"; - - AGAIN_CASES(); - } - - if ( useAgainLabel() || redFsm->anyNfaStates() ) - out << LABEL( "resume" ) << ":\n"; - - /* Jump into the machine based on the current state. */ - out << - " leaq " << LABEL( "entry_jmp" ) << "(%rip), %rcx\n"; - - if ( stackCS ) { - out << - " movq " << vCS() << ", %r11\n"; - } - - ENTRY_CASES(); - - STATE_GOTOS(); - - EXIT_STATES(); - - out << LABEL( "pop" ) << ":\n"; - - if ( redFsm->anyNfaStates() ) { - out << - " movq " << NFA_TOP() << ", %rcx\n" - " cmpq $0, %rcx\n" - " je " << LABEL( "nfa_stack_empty" ) << "\n" - " movq " << NFA_TOP() << ", %rcx\n" - " subq $1, %rcx\n" - " movq %rcx, " << NFA_TOP() << "\n" - " movq " << NFA_STACK() << ", %rax\n" - " imulq $24, %rcx\n" - " movq 0(%rax,%rcx,), %r11\n" - " movq 8(%rax,%rcx,), " << P() << "\n" - " movq %r11, " << vCS() << "\n" - ; - - if ( redFsm->bAnyNfaPops ) { - out << - " movq %r11, %r14\n" - " movq 16(%rax,%rcx,), %rax\n"; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) { - - /* Write the entry label. */ - out << - " # pop action select\n" - " cmp $" << targ->id << ", %rax\n" - " jne 100f\n"; - - if ( targ->popTest != 0 ) { - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = targ->popTest->key; item.lte(); item++ ) - NFA_CONDITION( out, item->value, item.last() ); - } - - out << - " jmp 101f\n" - "100:\n"; - - } - } - } - - out << - "101:\n" - " movq %r14, %r11\n"; - } - - out << - " jmp " << LABEL( "resume" ) << "\n" << - LABEL( "pop_fail" ) << ":\n" - " movq $" << ERROR_STATE() << ", " << vCS() << "\n" - " jmp " << LABEL( "resume" ) << "\n" << - LABEL( "nfa_stack_empty" ) << ":\n"; - } - - if ( stackCS ) { - out << - " movq " << vCS() << ", %r11\n"; - } - - out << - "# WRITE EXEC END\n"; - - out << LABEL( "out" ) << ":\n"; - - if ( stackCS ) { - out << - " movq " << vCS() << ", %r11\n"; - } - -#ifdef LOG_TRANS - out << - " jmp " << LABEL( "skip" ) << "\n" << - LABEL( "log_trans" ) << ":\n" - " movq %rdx, %rcx\n" - " movq %rsi, %rdx\n" - " movq %rdi, %rsi\n" - " movq " << LABEL( "fmt_log_trans" ) << "@GOTPCREL(%rip), %rdi\n" - " movq $0, %rax\n" - " call printf@PLT\n" - " ret\n" << - LABEL( "skip" ) << ":\n" - "\n"; -#endif -} diff --git a/libfsm/asm.h b/libfsm/asm.h deleted file mode 100644 index 2506e582..00000000 --- a/libfsm/asm.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _ASM_H -#define _ASM_H - -#include -#include -#include -#include - -#include "common.h" -#include "gendata.h" -#include "ragel.h" - -using std::string; -using std::ostream; - -/* Integer array line length. */ -#define IALL_INTEGRAL 8 -#define IALL_STRING 128 - -/* Forwards. */ -struct RedFsmAp; -struct RedStateAp; -struct CodeGenData; -struct GenAction; -struct NameInst; -struct GenInlineItem; -struct GenInlineList; -struct RedAction; -struct FsmLongestMatch; -struct FsmLongestMatchPart; -class AsmCodeGen; -struct RedTransAp; -struct RedStateAp; -struct GenStateCond; - -string itoa( int i ); - -/* - * class AsmCodeGen - */ -class AsmCodeGen : public CodeGenData -{ -public: - AsmCodeGen( const CodeGenArgs &args ); - virtual ~AsmCodeGen() {} - - virtual void writeInit(); - virtual void writeStart(); - virtual void writeFirstFinal(); - virtual void writeError(); - - virtual void statsSummary() {} - virtual void genAnalysis(); - -protected: - string FSM_NAME(); - string START_STATE_ID(); - string KEY( Key key ); - string COND_KEY( CondKey key ); - string LDIR_PATH( char *path ); - virtual void ACTION( ostream &ret, GenAction *action, int targState, - bool inFinish, bool csForced ); - void CONDITION( ostream &ret, GenAction *condition ); - void NFA_CONDITION( ostream &ret, GenAction *condition, bool last ); - string ALPH_TYPE(); - - bool isAlphTypeSigned(); - - string GET_KEY(); - - string P(); - string PE(); - string vEOF(); - string NBREAK(); - - string ACCESS(); - string vCS(); - string STACK(); - string TOP(); - string TOKSTART(); - string TOKEND(); - string ACT(); - - string NFA_STACK(); - string NFA_TOP(); - string NFA_SZ(); - - string DATA_PREFIX(); - string PM() { return "_" + DATA_PREFIX() + "partition_map"; } - string C() { return "_" + DATA_PREFIX() + "cond_spaces"; } - string CK() { return "_" + DATA_PREFIX() + "cond_keys"; } - string K() { return "_" + DATA_PREFIX() + "trans_keys"; } - string I() { return "_" + DATA_PREFIX() + "indices"; } - string CO() { return "_" + DATA_PREFIX() + "cond_offsets"; } - string KO() { return "_" + DATA_PREFIX() + "key_offsets"; } - string IO() { return "_" + DATA_PREFIX() + "index_offsets"; } - string CL() { return "_" + DATA_PREFIX() + "cond_lengths"; } - string SL() { return "_" + DATA_PREFIX() + "single_lengths"; } - string RL() { return "_" + DATA_PREFIX() + "range_lengths"; } - string A() { return "_" + DATA_PREFIX() + "actions"; } - string TA() { return "_" + DATA_PREFIX() + "trans_actions"; } - string TT() { return "_" + DATA_PREFIX() + "trans_targs"; } - string TSA() { return "_" + DATA_PREFIX() + "to_state_actions"; } - string FSA() { return "_" + DATA_PREFIX() + "from_state_actions"; } - string EA() { return "_" + DATA_PREFIX() + "eof_actions"; } - string ET() { return "_" + DATA_PREFIX() + "eof_trans"; } - string SP() { return "_" + DATA_PREFIX() + "key_spans"; } - string CSP() { return "_" + DATA_PREFIX() + "cond_key_spans"; } - string START() { return DATA_PREFIX() + "start"; } - string ERROR() { return DATA_PREFIX() + "error"; } - string FIRST_FINAL() { return DATA_PREFIX() + "first_final"; } - string CTXDATA() { return DATA_PREFIX() + "ctxdata"; } - - string LABEL( const char *type, long i ); - string LABEL( const char *name ); - - void INLINE_LIST( ostream &ret, GenInlineList *inlineList, - int targState, bool inFinish, bool csForced ); - void EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); - void LM_SWITCH( ostream &ret, GenInlineItem *item, int targState, - int inFinish, bool csForced ); - void SET_ACT( ostream &ret, GenInlineItem *item ); - void INIT_TOKSTART( ostream &ret, GenInlineItem *item ); - void INIT_ACT( ostream &ret, GenInlineItem *item ); - void SET_TOKSTART( ostream &ret, GenInlineItem *item ); - void SET_TOKEND( ostream &ret, GenInlineItem *item ); - void GET_TOKEND( ostream &ret, GenInlineItem *item ); - void STATIC_CONST_INT( const string &name, const string &val ); - void STATE_IDS(); - - string ERROR_STATE(); - string FIRST_FINAL_STATE(); - - bool outLabelUsed; - bool testEofUsed; - bool againLabelUsed; - long nextLmSwitchLabel; - bool stackCS; - - void NBREAK( ostream &ret, int targState, bool csForced ); - void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void NRET( ostream &ret, bool inFinish ); - - void HOST_STMT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ); - void HOST_EXPR( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ); - void HOST_TEXT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ); - void GEN_STMT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ); - void GEN_EXPR( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ); - -public: - - virtual string NULL_ITEM(); - virtual string POINTER(); - virtual ostream &SWITCH_DEFAULT(); - virtual ostream &OPEN_ARRAY( string type, string name ); - virtual ostream &CLOSE_ARRAY(); - virtual ostream &STATIC_VAR( string type, string name ); - virtual string ARR_OFF( string ptr, string offset ); - virtual string CAST( string type ); - virtual string UINT(); - virtual string PTR_CONST(); - virtual string PTR_CONST_END(); - virtual string CTRL_FLOW(); - - virtual void writeExports(); - - unsigned int TO_STATE_ACTION( RedStateAp *state ); - unsigned int FROM_STATE_ACTION( RedStateAp *state ); - unsigned int EOF_ACTION( RedStateAp *state ); - - void COND_TRANSLATE( GenStateCond *stateCond ); - void STATE_CONDS( RedStateAp *state, bool genDefault ); - - std::ostream &EXIT_STATES(); - std::string TRANS_GOTO_TARG( RedTransAp *trans ); - std::string TRANS_GOTO_TARG( RedCondPair *pair ); - std::ostream &TRANS_GOTO( RedTransAp *trans ); - std::ostream &AGAIN_CASES(); - std::ostream &FINISH_CASES(); - std::ostream &ENTRY_CASES(); - - void GOTO( ostream &ret, int gotoDest, bool inFinish ); - void CALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NEXT( ostream &ret, int nextDest, bool inFinish ); - void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void RET( ostream &ret, bool inFinish ); - void CURS( ostream &ret, bool inFinish ); - void TARGS( ostream &ret, bool inFinish, int targState ); - void BREAK( ostream &ret, int targState, bool csForced ); - void LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); - - virtual void writeData(); - virtual void writeExec(); - - bool useAgainLabel(); - - void NFA_PUSH( RedStateAp *state ); - bool IN_TRANS_ACTIONS( RedStateAp *state ); - void STATE_GOTOS(); - - void emitSingleIfElseIf( RedStateAp *state ); - void emitSingleJumpTable( RedStateAp *state, std::string def ); - void emitRangeBSearch( RedStateAp *state, int low, int high ); - void emitCharClassIfElseIf( RedStateAp *state ); - void emitCharClassJumpTable( RedStateAp *state, std::string def ); - - /* Set up labelNeeded flag for each state. */ - void setLabelsNeeded( RedCondPair *pair ); - void setLabelsNeeded( GenInlineList *inlineList ); - void setLabelsNeeded(); - - void setNfaIds(); - - void genOutputLineDirective( ostream &out ) {} - void genLineDirective( ostream &out, int line, const char *file ) {} -}; - -#endif diff --git a/libfsm/binary.cc b/libfsm/binary.cc deleted file mode 100644 index 39b58a47..00000000 --- a/libfsm/binary.cc +++ /dev/null @@ -1,819 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "binary.h" -#include "redfsm.h" -#include "gendata.h" - -#include - -void Binary::genAnalysis() -{ - redFsm->sortByStateId(); - - /* Choose default transitions and the single transition. */ - redFsm->chooseDefaultSpan(); - - /* Choose the singles. */ - redFsm->moveSelectTransToSingle(); - - if ( redFsm->errState != 0 ) - redFsm->getErrorCond(); - - /* If any errors have occured in the input file then don't write anything. */ - if ( red->id->errorCount > 0 ) - return; - - /* Anlayze Machine will find the final action reference counts, among other - * things. We will use these in reporting the usage of fsm directives in - * action code. */ - red->analyzeMachine(); - - setKeyType(); - - /* Run the analysis pass over the table data. */ - setTableState( TableArray::AnalyzePass ); - tableDataPass(); - - /* Switch the tables over to the code gen mode. */ - setTableState( TableArray::GeneratePass ); -} - - -void Binary::tableDataPass() -{ - if ( type == Loop ) - taActions(); - - taKeyOffsets(); - taSingleLens(); - taRangeLens(); - taIndexOffsets(); - taIndices(); - - taTransCondSpacesWi(); - taTransOffsetsWi(); - taTransLengthsWi(); - - taTransCondSpaces(); - taTransOffsets(); - taTransLengths(); - - taCondTargs(); - taCondActions(); - - taToStateActions(); - taFromStateActions(); - taEofActions(); - taEofConds(); - taEofTrans(); - - taKeys(); - taCondKeys(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); -} - -void Binary::writeData() -{ - if ( type == Loop ) { - /* If there are any transtion functions then output the array. If there - * are none, don't bother emitting an empty array that won't be used. */ - if ( redFsm->anyActions() ) - taActions(); - } - - taKeyOffsets(); - taKeys(); - taSingleLens(); - taRangeLens(); - taIndexOffsets(); - - taTransCondSpaces(); - taTransOffsets(); - taTransLengths(); - - taCondKeys(); - taCondTargs(); - taCondActions(); - - if ( redFsm->anyToStateActions() ) - taToStateActions(); - - if ( redFsm->anyFromStateActions() ) - taFromStateActions(); - - if ( redFsm->anyEofActions() ) - taEofActions(); - - taEofConds(); - - if ( redFsm->anyEofTrans() ) - taEofTrans(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); - - STATE_IDS(); -} - - -void Binary::setKeyType() -{ - transKeys.setType( ALPH_TYPE(), alphType->size, alphType->isChar ); - transKeys.isSigned = keyOps->isSigned; -} - -void Binary::setTableState( TableArray::State state ) -{ - for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { - TableArray *tableArray = *i; - tableArray->setState( state ); - } -} - -void Binary::taKeyOffsets() -{ - keyOffsets.start(); - - int curKeyOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - keyOffsets.value( curKeyOffset ); - curKeyOffset += st->outSingle.length() + st->outRange.length()*2; - } - - keyOffsets.finish(); -} - - -void Binary::taSingleLens() -{ - singleLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - singleLens.value( st->outSingle.length() ); - - singleLens.finish(); -} - - -void Binary::taRangeLens() -{ - rangeLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - rangeLens.value( st->outRange.length() ); - - rangeLens.finish(); -} - -void Binary::taIndexOffsets() -{ - indexOffsets.start(); - - int curIndOffset = 0; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Write the index offset. */ - indexOffsets.value( curIndOffset ); - - /* Move the index offset ahead. */ - curIndOffset += st->outSingle.length() + st->outRange.length(); - if ( st->defTrans != 0 ) - curIndOffset += 1; - } - - indexOffsets.finish(); -} - -void Binary::taToStateActions() -{ - toStateActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - TO_STATE_ACTION(st); - - toStateActions.finish(); -} - -void Binary::taFromStateActions() -{ - fromStateActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - FROM_STATE_ACTION(st); - - fromStateActions.finish(); -} - -void Binary::taEofActions() -{ - eofActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - EOF_ACTION( st ); - - eofActions.finish(); -} - -void Binary::taEofConds() -{ - /* - * EOF Cond Spaces - */ - eofCondSpaces.start(); - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->outCondSpace != 0 ) - eofCondSpaces.value( st->outCondSpace->condSpaceId ); - else - eofCondSpaces.value( -1 ); - } - eofCondSpaces.finish(); - - /* - * EOF Cond Key Indixes - */ - eofCondKeyOffs.start(); - - int curOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long off = 0; - if ( st->outCondSpace != 0 ) { - off = curOffset; - curOffset += st->outCondKeys.length(); - } - eofCondKeyOffs.value( off ); - } - - eofCondKeyOffs.finish(); - - /* - * EOF Cond Key Lengths. - */ - eofCondKeyLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long len = 0; - if ( st->outCondSpace != 0 ) - len = st->outCondKeys.length(); - eofCondKeyLens.value( len ); - } - - eofCondKeyLens.finish(); - - /* - * EOF Cond Keys - */ - eofCondKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->outCondSpace != 0 ) { - for ( int c = 0; c < st->outCondKeys.length(); c++ ) { - CondKey key = st->outCondKeys[c]; - eofCondKeys.value( key.getVal() ); - } - } - } - - eofCondKeys.finish(); -} - -void Binary::taEofTrans() -{ - eofTrans.start(); - - /* Need to compute transition positions. */ - int totalTrans = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - totalTrans += st->outSingle.length(); - totalTrans += st->outRange.length(); - if ( st->defTrans != 0 ) - totalTrans += 1; - } - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long trans = 0; - if ( st->eofTrans != 0 ) { - trans = totalTrans + 1; - totalTrans += 1; - } - - eofTrans.value( trans ); - } - - eofTrans.finish(); -} - -void Binary::taKeys() -{ - transKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Loop the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - transKeys.value( stel->lowKey.getVal() ); - } - - /* Loop the state's transitions. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - /* Lower key. */ - transKeys.value( rtel->lowKey.getVal() ); - - /* Upper key. */ - transKeys.value( rtel->highKey.getVal() ); - } - } - - transKeys.finish(); -} - -void Binary::taIndices() -{ - indices.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) - indices.value( stel->value->id ); - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) - indices.value( rtel->value->id ); - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) - indices.value( st->defTrans->id ); - } - - indices.finish(); -} - -void Binary::taTransCondSpaces() -{ - transCondSpaces.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - } - - transCondSpaces.finish(); -} - -void Binary::taTransOffsets() -{ - transOffsets.start(); - - int curOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - } - - errCondOffset = curOffset; - - transOffsets.finish(); -} - -void Binary::taTransLengths() -{ - transLengths.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - transLengths.value( trans->numConds() ); - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - transLengths.value( trans->numConds() ); - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - transLengths.value( trans->numConds() ); - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - transLengths.value( trans->numConds() ); - } - } - - transLengths.finish(); -} - -void Binary::taTransCondSpacesWi() -{ - transCondSpacesWi.start(); - - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - /* Cond Space id. */ - if ( trans->condSpace != 0 ) - transCondSpacesWi.value( trans->condSpace->condSpaceId ); - else - transCondSpacesWi.value( -1 ); - } - - transCondSpacesWi.finish(); -} - -void Binary::taTransOffsetsWi() -{ - transOffsetsWi.start(); - - int curOffset = 0; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - transOffsetsWi.value( curOffset ); - - TransApSet::Iter next = trans; - next.increment(); - - curOffset += trans->numConds(); - } - - transOffsetsWi.finish(); -} - -void Binary::taTransLengthsWi() -{ - transLengthsWi.start(); - - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - transLengthsWi.value( trans->numConds() ); - - TransApSet::Iter next = trans; - next.increment(); - } - - transLengthsWi.finish(); -} - -void Binary::taCondKeys() -{ - condKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - } - - condKeys.finish(); -} - -void Binary::taCondTargs() -{ - condTargs.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - } - - if ( redFsm->errCond != 0 ) { - RedCondPair *cond = &redFsm->errCond->p; - condTargs.value( cond->targ->id ); - } - - condTargs.finish(); -} - -void Binary::taCondActions() -{ - condActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - COND_ACTION( cond ); - } - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - COND_ACTION( cond ); - } - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - COND_ACTION( cond ); - } - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - COND_ACTION( cond ); - } - } - } - - if ( redFsm->errCond != 0 ) { - RedCondPair *cond = &redFsm->errCond->p; - COND_ACTION( cond ); - } - - condActions.finish(); -} - -void Binary::taNfaTargs() -{ - nfaTargs.start(); - - /* Offset of zero means no NFA targs, put a filler there. */ - nfaTargs.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaTargs.value( st->nfaTargs->length() ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - nfaTargs.value( targ->state->id ); - } - } - - nfaTargs.finish(); -} - -/* These need to mirror nfa targs. */ -void Binary::taNfaPushActions() -{ - nfaPushActions.start(); - - nfaPushActions.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaPushActions.value( 0 ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_PUSH_ACTION( targ ); - } - } - - nfaPushActions.finish(); -} - -void Binary::taNfaPopTrans() -{ - nfaPopTrans.start(); - - nfaPopTrans.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - - nfaPopTrans.value( 0 ); - - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_POP_TEST( targ ); - } - } - - nfaPopTrans.finish(); -} - -void Binary::taNfaOffsets() -{ - nfaOffsets.start(); - - /* Offset of zero means no NFA targs, real targs start at 1. */ - long offset = 1; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs == 0 ) { - nfaOffsets.value( 0 ); - } - else { - nfaOffsets.value( offset ); - offset += 1 + st->nfaTargs->length(); - } - } - - nfaOffsets.finish(); -} - - -/* Write out the array of actions. */ -std::ostream &Binary::ACTIONS_ARRAY() -{ - out << "\t0, "; - int totalActions = 1; - for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { - /* Write out the length, which will never be the last character. */ - out << act->key.length() << ", "; - /* Put in a line break every 8 */ - if ( totalActions++ % 8 == 7 ) - out << "\n\t"; - - for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) { - out << item->value->actionId; - if ( ! (act.last() && item.last()) ) - out << ", "; - - /* Put in a line break every 8 */ - if ( totalActions++ % 8 == 7 ) - out << "\n\t"; - } - } - out << "\n"; - return out; -} - -void Binary::taActions() -{ - actions.start(); - - /* Put "no-action" at the beginning. */ - actions.value( 0 ); - - for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { - /* Write out the length, which will never be the last character. */ - actions.value( act->key.length() ); - - for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) - actions.value( item->value->actionId ); - } - - actions.finish(); -} - - - - diff --git a/libfsm/binary.h b/libfsm/binary.h deleted file mode 100644 index d947483d..00000000 --- a/libfsm/binary.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _C_BINARY_H -#define _C_BINARY_H - -#include -#include "codegen.h" -#include "tables.h" - -/* Forwards. */ -struct CodeGenData; -struct NameInst; -struct RedTransAp; -struct RedStateAp; - -class Binary - : public virtual Tables -{ -protected: - enum Type { - Loop = 1, Exp - }; - -public: - Binary( const CodeGenArgs &args, Type type ) - : - Tables( args ), - type(type) - {} - -protected: - Type type; - - std::ostream &COND_KEYS_v1(); - std::ostream &COND_SPACES_v1(); - std::ostream &INDICES(); - std::ostream &INDEX_OFFSETS(); - std::ostream &SINGLE_LENS(); - std::ostream &RANGE_LENS(); - std::ostream &TRANS_TARGS_WI(); - std::ostream &ACTIONS_ARRAY(); - - void taKeyOffsets(); - void taSingleLens(); - void taRangeLens(); - void taIndexOffsets(); - void taIndices(); - void taTransCondSpacesWi(); - void taTransOffsetsWi(); - void taTransLengthsWi(); - void taTransCondSpaces(); - void taTransOffsets(); - void taTransLengths(); - void taCondTargs(); - void taCondActions(); - void taToStateActions(); - void taFromStateActions(); - void taEofTrans(); - void taEofConds(); - void taEofActions(); - void taKeys(); - void taActions(); - void taCondKeys(); - void taNfaTargs(); - void taNfaOffsets(); - void taNfaPushActions(); - void taNfaPopTrans(); - - void setKeyType(); - - void setTableState( TableArray::State ); - - virtual void writeData(); - virtual void tableDataPass(); - virtual void genAnalysis(); -}; - -#endif diff --git a/libfsm/binbreak.cc b/libfsm/binbreak.cc deleted file mode 100644 index 18b71542..00000000 --- a/libfsm/binbreak.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "binbreak.h" - -void BinBreak::LOCATE_TRANS() -{ - out << - " " << keys << " = " << OFFSET( ARR_REF( transKeys ), ARR_REF( keyOffsets ) + "[" + vCS() + "]" ) << ";\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexOffsets ) << "[" << vCS() << "];\n" - "\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( singleLens ) << "[" << vCS() << "];\n" - " " << have << " = 0;\n" - " if ( " << klen << " > 0 ) {\n" - " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" - " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + " << klen << " - 1;\n" - " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << keys << " += " << klen << ";\n" - " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << have << " = 1;\n" - " " << trans << " += " << CAST( UINT() ) << "(_mid - " << keys << ");\n" - " break;\n" - " }\n" - " }\n" - " }\n" - "\n" - " " << klen << " = " << CAST("int") << ARR_REF( rangeLens ) << "[" << vCS() << "];\n" - " if ( " << have << " == 0 && " << klen << " > 0 ) {\n" - " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" - " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + (" << klen << "<<1) - 2;\n" - " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n" - " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _upper = _mid - 2;\n" - " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid + 1" ) << " )\n" - " _lower = _mid + 2;\n" - " else {\n" - " " << trans << " += " << CAST( UINT() ) << "((_mid - " << keys << ")>>1);\n" - " break;\n" - " }\n" - " }\n" - " }\n" - "\n"; -} - -void BinBreak::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - std::stringstream success, error; - - out << - " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - if ( red->condSpaceList.length() > 0 ) - COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); - - success << - cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; - - error << - cond << " = " << errCondOffset << ";\n"; - - out << - " {\n" - " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" - " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" - " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << error.str() << "\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << success.str() << "\n" - " break;\n" - " }\n" - " }\n" - " }\n" - ; - } - - out << EMIT_LABEL( _match_cond ); -} - diff --git a/libfsm/binbreak.h b/libfsm/binbreak.h deleted file mode 100644 index 1b48ab24..00000000 --- a/libfsm/binbreak.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_BINBREAK_H -#define RAGEL_BINBREAK_H - -#include "binary.h" -#include "actloop.h" -#include "actexp.h" - -struct BinBreak -: - public Binary, public TabBreak -{ - BinBreak( const CodeGenArgs &args, Binary::Type type ) - : - Tables( args ), - Binary( args, type ), - TabBreak( args ) - {} - - void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class BinBreakLoop - : public BinBreak, public ActLoop -{ -public: - BinBreakLoop( const CodeGenArgs &args ) - : - Tables( args ), - BinBreak( args, Loop ), - ActLoop( args ) - {} -}; - - -class BinBreakExp - : public BinBreak, public ActExp -{ -public: - BinBreakExp( const CodeGenArgs &args ) - : - Tables( args ), - BinBreak( args, Exp ), - ActExp( args ) - {} -}; - - -#endif diff --git a/libfsm/bingoto.cc b/libfsm/bingoto.cc deleted file mode 100644 index 1f4a818d..00000000 --- a/libfsm/bingoto.cc +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "bingoto.h" - -void BinGoto::LOCATE_TRANS() -{ - out << - " " << keys << " = " << OFFSET( ARR_REF( transKeys ), ARR_REF( keyOffsets ) + "[" + vCS() + "]" ) << ";\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexOffsets ) << "[" << vCS() << "];\n" - "\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( singleLens ) << "[" << vCS() << "];\n" - " if ( " << klen << " > 0 ) {\n" - " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" - " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + " << klen << " - 1;\n" - " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << keys << " += " << klen << ";\n" - " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << trans << " += " << CAST( UINT() ) << "(_mid - " << keys << ");\n" - " goto " << _match << ";\n" - " }\n" - " }\n" - " }\n" - "\n" - " " << klen << " = " << CAST("int") << ARR_REF( rangeLens ) << "[" << vCS() << "];\n" - " if ( " << klen << " > 0 ) {\n" - " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" - " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + (" << klen << "<<1) - 2;\n" - " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n" - " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _upper = _mid - 2;\n" - " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid + 1" ) << " )\n" - " _lower = _mid + 2;\n" - " else {\n" - " " << trans << " += " << CAST( UINT() ) << "((_mid - " << keys << ")>>1);\n" - " break;\n" - " }\n" - " }\n" - " }\n" - "\n"; - - out << EMIT_LABEL( _match ); -} - - -void BinGoto::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - std::stringstream success, error; - - out << - " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - if ( red->condSpaceList.length() > 0 ) - COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); - - success << - cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; - - error << - cond << " = " << errCondOffset << ";\n"; - - out << - " {\n" - " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" - " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" - " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << error.str() << "\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << success.str() << "\n" - " break;\n" - " }\n" - " }\n" - " }\n" - ; - } -} - diff --git a/libfsm/bingoto.h b/libfsm/bingoto.h deleted file mode 100644 index 18fa8397..00000000 --- a/libfsm/bingoto.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_BINGOTO_H -#define RAGEL_BINGOTO_H - -#include "binary.h" -#include "actloop.h" -#include "actexp.h" - -struct BinGoto -: - public Binary, public TabGoto -{ - BinGoto( const CodeGenArgs &args, Binary::Type type ) - : - Tables( args ), - Binary( args, type ), - TabGoto( args ) - {} - - void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class BinGotoLoop - : public BinGoto, public ActLoop -{ -public: - BinGotoLoop( const CodeGenArgs &args ) - : - Tables( args ), - BinGoto( args, Loop ), - ActLoop( args ) - {} -}; - - -class BinGotoExp - : public BinGoto, public ActExp -{ -public: - BinGotoExp( const CodeGenArgs &args ) - : - Tables( args ), - BinGoto( args, Exp ), - ActExp( args ) - {} -}; - - -#endif diff --git a/libfsm/binvar.cc b/libfsm/binvar.cc deleted file mode 100644 index 27e40c03..00000000 --- a/libfsm/binvar.cc +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "binvar.h" -#include "parsedata.h" - -void BinVar::LOCATE_TRANS() -{ - out << - " " << keys << " = " << OFFSET( ARR_REF( transKeys ), ARR_REF( keyOffsets ) + "[" + vCS() + "]" ) << ";\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexOffsets ) << "[" << vCS() << "];\n" - "\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( singleLens ) << "[" << vCS() << "];\n" - " " << have << " = 0;\n" - " if ( " << klen << " > 0 ) {\n" - " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" - " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + " << klen << " - 1;\n" - " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" - " _bsc = 1;\n" - " while ( _bsc == 1 ) {\n" - " if ( _upper < _lower ) {\n" - " " << keys << " += " << klen << ";\n" - " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" - " _bsc = 0;\n" - " }\n" - " else {\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << have << " = 1;\n" - " " << trans << " += " << CAST( UINT() ) << "(_mid - " << keys << ");\n" - " _bsc = 0;\n" - " }\n" - " }\n" - " }\n" - " }\n" - "\n" - " " << klen << " = " << CAST("int") << ARR_REF( rangeLens ) << "[" << vCS() << "];\n" - " if ( " << have << " == 0 && " << klen << " > 0 ) {\n" - " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" - " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + (" << klen << "<<1) - 2;\n" - " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" - " _bsc = 1;\n" - " while ( _bsc == 1 ) {\n" - " if ( _upper < _lower ) {\n" - " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" - " _bsc = 0;\n" - " }\n" - " else {\n" - " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n" - " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" - " _upper = _mid - 2;\n" - " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid + 1" ) << " )\n" - " _lower = _mid + 2;\n" - " else {\n" - " " << trans << " += " << CAST( UINT() ) << "((_mid - " << keys << ")>>1);\n" - " _bsc = 0;\n" - " }\n" - " }\n" - " }\n" - " }\n" - "\n"; -} - -void BinVar::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - std::stringstream success, error; - - out << - " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - if ( red->condSpaceList.length() > 0 ) - COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); - - success << - cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; - - error << - cond << " = " << errCondOffset << ";\n"; - - out << - " {\n" - " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" - " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" - " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" - " _bsc = 1;\n" - " while ( _bsc == 1 ) {\n" - " if ( _upper < _lower ) {\n" - " " << error.str() << "\n" - " _bsc = 0;\n" - " }\n" - " else {\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << success.str() << "\n" - " _bsc = 0;\n" - " }\n" - " }\n" - " }\n" - " }\n" - ; - } -} - diff --git a/libfsm/binvar.h b/libfsm/binvar.h deleted file mode 100644 index cbbcef79..00000000 --- a/libfsm/binvar.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_BINVAR_H -#define RAGEL_BINVAR_H - -#include "binary.h" -#include "actloop.h" -#include "actexp.h" - -struct BinVar -: - public Binary, public TabVar -{ - BinVar( const CodeGenArgs &args, Binary::Type type ) - : - Tables( args ), - Binary( args, type ), - TabVar( args ) - {} - - void VAR_COND_BIN_SEARCH( Variable &var, TableArray &keys, std::string ok, std::string error ); - - void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class BinVarLoop - : public BinVar, public ActLoop -{ -public: - BinVarLoop( const CodeGenArgs &args ) - : - Tables( args ), - BinVar( args, Loop ), - ActLoop( args ) - {} -}; - -class BinVarExp -: - public BinVar, public ActExp -{ -public: - BinVarExp( const CodeGenArgs &args ) - : - Tables( args ), - BinVar( args, Exp ), - ActExp( args ) - {} -}; - -#endif diff --git a/libfsm/codegen.cc b/libfsm/codegen.cc deleted file mode 100644 index db8cc60a..00000000 --- a/libfsm/codegen.cc +++ /dev/null @@ -1,1202 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "codegen.h" -#include "ragel.h" -#include "redfsm.h" -#include "gendata.h" -#include "parsedata.h" -#include -#include -#include -#include - - -using std::ostream; -using std::ostringstream; -using std::string; -using std::endl; -using std::istream; -using std::ifstream; -using std::ostream; -using std::ios; -using std::cin; -using std::endl; - -std::ostream &operator<<( std::ostream &out, Variable &v ) -{ - out << v.name; - v.isReferenced = true; - return out; -} - -std::ostream &operator<<( std::ostream &out, GotoLabel &l ) -{ - out << l.name; - l.isReferenced = true; - return out; -} - -TableArray::TableArray( const char *name, CodeGen &codeGen ) -: - state(InitialState), - name(name), - width(0), - isSigned(true), - isChar(false), - stringTables( codeGen.stringTables ), - iall( codeGen.stringTables ? IALL_STRING : IALL_INTEGRAL ), - values(0), - - /* - * Use zero for min and max because - * we we null terminate every array. - */ - min(0), - max(0), - - codeGen(codeGen), - out(codeGen.out), - ln(0), - isReferenced(false), - started(false) -{ - codeGen.arrayVector.append( this ); -} - -std::string TableArray::ref() -{ - isReferenced = true; - return string("_") + codeGen.DATA_PREFIX() + name; -} - -long long TableArray::size() -{ - return width * values; -} - -void TableArray::startAnalyze() -{ -} - -void TableArray::valueAnalyze( long long v ) -{ - values += 1; - if ( v < min ) - min = v; - if ( v > max ) - max = v; -} - -void TableArray::finishAnalyze() -{ - if ( codeGen.backend == Direct ) { - /* Calculate the type if it is not already set. */ - if ( type.empty() ) { - if ( min >= S8BIT_MIN && max <= S8BIT_MAX ) { - type = "signed char"; - width = sizeof(char); - } - else if ( min >= S16BIT_MIN && max <= S16BIT_MAX ) { - type = "short"; - width = sizeof(short); - } - else if ( min >= S32BIT_MIN && max <= S32BIT_MAX ) { - type = "int"; - width = sizeof(int); - } - else if ( min >= S64BIT_MAX && max <= S64BIT_MAX ) { - type = "long"; - width = sizeof(long); - } - else { - type = "long long"; - width = sizeof(long long); - } - } - } - else { - /* Calculate the type if it is not already set. */ - if ( type.empty() ) { - if ( min >= S8BIT_MIN && max <= S8BIT_MAX ) { - type = "s8"; - width = sizeof(char); - } - else if ( min >= S16BIT_MIN && max <= S16BIT_MAX ) { - type = "s16"; - width = sizeof(short); - } - else if ( min >= S32BIT_MIN && max <= S32BIT_MAX ) { - type = "s32"; - width = sizeof(int); - } - else if ( min >= S64BIT_MAX && max <= S64BIT_MAX ) { - type = "s64"; - width = sizeof(long); - } - else { - type = "s128"; - width = sizeof(long long); - } - } - } -} - -void TableArray::startGenerate() -{ - if ( codeGen.backend == Direct ) { - if ( stringTables ) { - out << "static const char S_" << codeGen.DATA_PREFIX() << name << - "[] __attribute__((aligned (16))) = \n\t\""; - } - else { - out << "static const " << type << " " << - "_" << codeGen.DATA_PREFIX() << name << - "[] = {\n\t"; - } - } - else { - out << "array " << type << " " << - "_" << codeGen.DATA_PREFIX() << name << - "( " << min << ", " << max << " ) = { "; - } -} - -void TableArray::stringGenerate( long long value ) -{ - char c; - short h; - int i; -#if SIZEOF_INT != SIZEOF_LONG - long l; -#endif - unsigned char *p = 0; - int n = 0; - switch ( width ) { - case sizeof( char ): - c = value; - p = (unsigned char *)&c; - n = sizeof(char); - break; - case sizeof( short ): - h = value; - p = (unsigned char *)&h; - n = sizeof(short); - break; - case sizeof( int ): - i = value; - p = (unsigned char *)&i; - n = sizeof(int); - break; -#if SIZEOF_INT != SIZEOF_LONG - case sizeof( long ): - l = value; - p = (unsigned char *)&l; - n = sizeof(long); - break; -#endif - } - - std::ios_base::fmtflags prevFlags = out.flags( std::ios::hex ); - int prevFill = out.fill( '0' ); - - while ( n-- > 0 ) { - out << '\\'; - out << 'x'; - out << std::setw(2) << (unsigned int) *p++; - } - - out.flags( prevFlags ); - out.fill( prevFill ); -} - -void TableArray::valueGenerate( long long v ) -{ - if ( codeGen.backend == Direct ) { - if ( stringTables ) { - stringGenerate( v ); - - if ( ++ln % iall == 0 ) { - out << "\"\n\t\""; - ln = 0; - } - } - else { - if ( isChar ) - out << "c(" << v << ")"; - else if ( !isSigned ) - out << v << "u"; - else - out << v; - - if ( ( ++ln % iall ) == 0 ) { - out << ",\n\t"; - ln = 0; - } - else { - out << ", "; - } - } - } - else { - if ( isChar ) - out << "c(" << v << ")"; - else if ( !isSigned ) - out << "u(" << v << ")"; - else - out << v; - out << ", "; - } -} - -void TableArray::finishGenerate() -{ - if ( codeGen.backend == Direct ) { - if ( stringTables ) { - out << "\";\nconst " << type << " *_" << codeGen.DATA_PREFIX() << name << - " = (const " << type << "*) S_" << codeGen.DATA_PREFIX() << name << ";\n\n"; - - } - else { - if ( isChar ) - out << "c(0)\n};\n\n"; - else if ( !isSigned ) - out << "0u\n};\n\n"; - else - out << "0\n};\n\n"; - } - } - else { - if ( isChar ) - out << "c(0) };\n\n"; - else if ( !isSigned ) - out << "u(0) };\n\n"; - else - out << "0 };\n\n"; - } - - if ( codeGen.red->id->printStatistics ) { - codeGen.red->id->stats() << name << "\t" << values << "\t" << - size() << "\t" << endl; - } - - codeGen.tableData += size(); -} - -void TableArray::start() -{ - assert( !started ); - started = true; - switch ( state ) { - case InitialState: - break; - case AnalyzePass: - startAnalyze(); - break; - case GeneratePass: - if ( isReferenced ) - startGenerate(); - break; - } -} - -void TableArray::value( long long v ) -{ - assert( started ); - switch ( state ) { - case InitialState: - break; - case AnalyzePass: - valueAnalyze( v ); - break; - case GeneratePass: - if ( isReferenced ) - valueGenerate( v ); - break; - } -} - -void TableArray::finish() -{ - assert( started ); - started = false; - switch ( state ) { - case InitialState: - break; - case AnalyzePass: - finishAnalyze(); - break; - case GeneratePass: - if ( isReferenced ) - finishGenerate(); - break; - } -} - -/* Init code gen with in parameters. */ -CodeGen::CodeGen( const CodeGenArgs &args ) -: - CodeGenData( args ), - cpc( "_cpc" ), - pop_test( "_pop_test" ), - new_recs( "new_recs" ), - alt( "_alt" ), - tableData( 0 ), - backend( args.id->hostLang->backend ), - stringTables( args.id->stringTables ), - - nfaTargs( "nfa_targs", *this ), - nfaOffsets( "nfa_offsets", *this ), - nfaPushActions( "nfa_push_actions", *this ), - nfaPopTrans( "nfa_pop_trans", *this ) -{ -} - -void CodeGen::statsSummary() -{ - if ( red->id->printStatistics ) - red->id->stats() << "table-data\t\t" << tableData << endl << endl; -} - - -string CodeGen::CAST( string type ) -{ - if ( backend == Direct ) - return "(" + type + ")"; - else - return "cast(" + type + ")"; -} - -/* Write out the fsm name. */ -string CodeGen::FSM_NAME() -{ - return fsmName; -} - -/* Emit the offset of the start state as a decimal integer. */ -string CodeGen::START_STATE_ID() -{ - ostringstream ret; - ret << redFsm->startState->id; - return ret.str(); -}; - - -string CodeGen::ACCESS() -{ - ostringstream ret; - if ( red->accessExpr != 0 ) { - ret << OPEN_HOST_PLAIN(); - INLINE_LIST( ret, red->accessExpr, 0, false, false ); - ret << CLOSE_HOST_PLAIN(); - ret << ACCESS_OPER(); - } - return ret.str(); -} - - -string CodeGen::P() -{ - ostringstream ret; - if ( red->pExpr == 0 ) - ret << "p"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->pExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::PE() -{ - ostringstream ret; - if ( red->peExpr == 0 ) - ret << "pe"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->peExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::vEOF() -{ - ostringstream ret; - if ( red->eofExpr == 0 ) - ret << "eof"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->eofExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::vCS() -{ - ostringstream ret; - if ( red->csExpr == 0 ) - ret << ACCESS() << "cs"; - else { - /* Emit the user supplied method of retrieving the key. */ - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->csExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::TOP() -{ - ostringstream ret; - if ( red->topExpr == 0 ) - ret << ACCESS() + "top"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->topExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::STACK() -{ - ostringstream ret; - if ( red->stackExpr == 0 ) - ret << ACCESS() + "stack"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->stackExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::ACT() -{ - ostringstream ret; - if ( red->actExpr == 0 ) - ret << ACCESS() + "act"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->actExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::TOKSTART() -{ - ostringstream ret; - if ( red->tokstartExpr == 0 ) - ret << ACCESS() + "ts"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->tokstartExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::TOKEND() -{ - ostringstream ret; - if ( red->tokendExpr == 0 ) - ret << ACCESS() + "te"; - else { - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->tokendExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - return ret.str(); -} - -string CodeGen::GET_KEY() -{ - ostringstream ret; - if ( red->getKeyExpr != 0 ) { - /* Emit the user supplied method of retrieving the key. */ - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, red->getKeyExpr, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - } - else { - /* Expression for retrieving the key, use simple dereference. */ - ret << "( " << DEREF( "data", P() ) << ")"; - } - return ret.str(); -} - -/* Write out a key from the fsm code gen. Depends on wether or not the key is - * signed. */ -string CodeGen::KEY( Key key ) -{ - if ( backend == Direct ) { - ostringstream ret; - if ( alphType->isChar ) - ret << "c(" << (unsigned long) key.getVal() << ")"; - else if ( keyOps->isSigned || !keyOps->explicitUnsigned ) - ret << key.getVal(); - else - ret << (unsigned long) key.getVal() << "u"; - return ret.str(); - } - else { - ostringstream ret; - if ( alphType->isChar ) - ret << "c(" << (unsigned long) key.getVal() << ")"; - else if ( keyOps->isSigned || !keyOps->explicitUnsigned ) - ret << key.getVal(); - else - ret << "u(" << (unsigned long) key.getVal() << ")"; - return ret.str(); - } -} - -bool CodeGen::isAlphTypeSigned() -{ - return keyOps->isSigned; -} - -void CodeGen::DECLARE( std::string type, Variable &var, std::string init ) -{ - if ( var.isReferenced ) - out << type << " " << var.name << init << ";\n"; -} - -void CodeGen::EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) -{ - /* The parser gives fexec two children. The double brackets are for D - * code. If the inline list is a single word it will get interpreted as a - * C-style cast by the D compiler. */ - ret << OPEN_GEN_BLOCK() << P() << " = (("; - INLINE_LIST( ret, item->children, targState, inFinish, false ); - ret << "))-1;" << CLOSE_GEN_BLOCK() << "\n"; -} - -void CodeGen::LM_SWITCH( ostream &ret, GenInlineItem *item, - int targState, int inFinish, bool csForced ) -{ - ret << - OPEN_GEN_BLOCK() << "switch( " << ACT() << " ) {\n"; - - for ( GenInlineList::Iter lma = *item->children; lma.lte(); lma++ ) { - /* Write the case label, the action and the case break. */ - if ( lma->lmId < 0 ) - ret << " " << DEFAULT() << " {\n"; - else - ret << " " << CASE( STR(lma->lmId) ) << " {\n"; - - /* Write the block and close it off. */ - INLINE_LIST( ret, lma->children, targState, inFinish, csForced ); - - ret << CEND() << "\n}\n"; - } - - ret << - " }" << CLOSE_GEN_BLOCK() << "\n" - "\t"; -} - -void CodeGen::LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) -{ - /* The parser gives fexec two children. The double brackets are for D - * code. If the inline list is a single word it will get interpreted as a - * C-style cast by the D compiler. This should be in the D code generator. */ - ret << P() << " = (("; - INLINE_LIST( ret, item->children, targState, inFinish, false ); - ret << "))-1;\n"; -} - -void CodeGen::SET_ACT( ostream &ret, GenInlineItem *item ) -{ - ret << ACT() << " = " << item->lmId << ";"; -} - -void CodeGen::SET_TOKEND( ostream &ret, GenInlineItem *item ) -{ - /* The tokend action sets tokend. */ - ret << TOKEND() << " = " << P(); - if ( item->offset != 0 ) - out << "+" << item->offset; - out << ";"; -} - -void CodeGen::GET_TOKEND( ostream &ret, GenInlineItem *item ) -{ - ret << TOKEND(); -} - -void CodeGen::INIT_TOKSTART( ostream &ret, GenInlineItem *item ) -{ - ret << TOKSTART() << " = " << NIL() << ";"; -} - -void CodeGen::INIT_ACT( ostream &ret, GenInlineItem *item ) -{ - ret << ACT() << " = 0;"; -} - -void CodeGen::SET_TOKSTART( ostream &ret, GenInlineItem *item ) -{ - ret << TOKSTART() << " = " << P() << ";"; -} - -void CodeGen::HOST_STMT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - ret << OPEN_HOST_BLOCK( item->loc.fileName, item->loc.line ); - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - ret << CLOSE_HOST_BLOCK(); - } -} - -#if 0 -void CodeGen::LM_CASE( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - } -} -#endif - -void CodeGen::HOST_EXPR( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - ret << CLOSE_HOST_EXPR(); - } -} - -void CodeGen::HOST_TEXT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - ret << OPEN_HOST_PLAIN(); - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - ret << CLOSE_HOST_PLAIN(); - } -} - -void CodeGen::GEN_STMT( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - ret << OPEN_GEN_BLOCK(); - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - ret << CLOSE_GEN_BLOCK(); - } -} - -void CodeGen::GEN_EXPR( ostream &ret, GenInlineItem *item, - int targState, bool inFinish, bool csForced ) -{ - if ( item->children->length() > 0 ) { - /* Write the block and close it off. */ - ret << OPEN_GEN_EXPR(); - INLINE_LIST( ret, item->children, targState, inFinish, csForced ); - ret << CLOSE_GEN_EXPR(); - } -} - -void CodeGen::INLINE_EXPR( ostream &ret, GenInlineList *inlineList ) -{ - ret << OPEN_HOST_EXPR(); - INLINE_LIST( ret, inlineList, 0, false, false ); - ret << CLOSE_HOST_EXPR(); -} - -void CodeGen::INLINE_BLOCK( ostream &ret, GenInlineExpr *inlineExpr ) -{ - out << OPEN_HOST_BLOCK( inlineExpr ); - INLINE_LIST( out, inlineExpr->inlineList, 0, false, false ); - out << CLOSE_HOST_BLOCK(); -} - -void CodeGen::INLINE_PLAIN( ostream &ret, GenInlineExpr *inlineExpr ) -{ - -} - -/* Write out an inline tree structure. Walks the list and possibly calls out - * to virtual functions than handle language specific items in the tree. */ -void CodeGen::INLINE_LIST( ostream &ret, GenInlineList *inlineList, - int targState, bool inFinish, bool csForced ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - switch ( item->type ) { - case GenInlineItem::Text: - if ( backend == Direct ) - ret << item->data; - else - translatedHostData( ret, item->data ); - break; - case GenInlineItem::Goto: - GOTO( ret, item->targState->id, inFinish ); - break; - case GenInlineItem::Call: - CALL( ret, item->targState->id, targState, inFinish ); - break; - case GenInlineItem::Ncall: - NCALL( ret, item->targState->id, targState, inFinish ); - break; - case GenInlineItem::Next: - NEXT( ret, item->targState->id, inFinish ); - break; - case GenInlineItem::Ret: - RET( ret, inFinish ); - break; - case GenInlineItem::Nret: - NRET( ret, inFinish ); - break; - case GenInlineItem::PChar: - ret << P(); - break; - case GenInlineItem::Char: - ret << OPEN_GEN_EXPR() << GET_KEY() << CLOSE_GEN_EXPR(); - break; - case GenInlineItem::Hold: - ret << OPEN_GEN_BLOCK() << P() << " = " << P() << " - 1; " << CLOSE_GEN_BLOCK(); - break; - case GenInlineItem::LmHold: - ret << P() << " = " << P() << " - 1;"; - break; - case GenInlineItem::NfaClear: - ret << "nfa_len = 0; "; - break; - case GenInlineItem::Exec: - EXEC( ret, item, targState, inFinish ); - break; - case GenInlineItem::Curs: - CURS( ret, inFinish ); - break; - case GenInlineItem::Targs: - TARGS( ret, inFinish, targState ); - break; - case GenInlineItem::Entry: - ret << item->targState->id; - break; - case GenInlineItem::GotoExpr: - GOTO_EXPR( ret, item, inFinish ); - break; - case GenInlineItem::CallExpr: - CALL_EXPR( ret, item, targState, inFinish ); - break; - case GenInlineItem::NcallExpr: - NCALL_EXPR( ret, item, targState, inFinish ); - break; - case GenInlineItem::NextExpr: - NEXT_EXPR( ret, item, inFinish ); - break; - case GenInlineItem::LmSwitch: - LM_SWITCH( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::LmExec: - LM_EXEC( ret, item, targState, inFinish ); - break; - case GenInlineItem::LmCase: - /* Not encountered here, in the lm switch. */ - break; - case GenInlineItem::LmSetActId: - SET_ACT( ret, item ); - break; - case GenInlineItem::LmSetTokEnd: - SET_TOKEND( ret, item ); - break; - case GenInlineItem::LmGetTokEnd: - GET_TOKEND( ret, item ); - break; - case GenInlineItem::LmInitTokStart: - INIT_TOKSTART( ret, item ); - break; - case GenInlineItem::LmInitAct: - INIT_ACT( ret, item ); - break; - case GenInlineItem::LmSetTokStart: - SET_TOKSTART( ret, item ); - break; - case GenInlineItem::Break: - BREAK( ret, targState, csForced ); - break; - case GenInlineItem::Nbreak: - NBREAK( ret, targState, csForced ); - break; - case GenInlineItem::HostStmt: - HOST_STMT( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::HostExpr: - HOST_EXPR( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::HostText: - HOST_TEXT( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::GenStmt: - GEN_STMT( ret, item, targState, inFinish, csForced ); - break; - case GenInlineItem::GenExpr: - GEN_EXPR( ret, item, targState, inFinish, csForced ); - break; - /* These should not be encountered. We handle these Nfa wraps at the top level. */ - case GenInlineItem::NfaWrapAction: - case GenInlineItem::NfaWrapConds: - break; - } - } -} - -/* Write out paths in line directives. Escapes any special characters. */ -string CodeGen::LDIR_PATH( char *path ) -{ - ostringstream ret; - for ( char *pc = path; *pc != 0; pc++ ) { - if ( *pc == '\\' ) - ret << "\\\\"; - else - ret << *pc; - } - return ret.str(); -} - -void CodeGen::ACTION( ostream &ret, GenAction *action, IlOpts opts ) -{ - ret << '\t'; - ret << OPEN_HOST_BLOCK( action->loc.fileName, action->loc.line ); - INLINE_LIST( ret, action->inlineList, opts.targState, opts.inFinish, opts.csForced ); - ret << CLOSE_HOST_BLOCK(); - ret << "\n"; - genOutputLineDirective( ret ); -} - -void CodeGen::CONDITION( ostream &ret, GenAction *condition ) -{ - ret << OPEN_HOST_EXPR( condition->loc.fileName, condition->loc.line ); - INLINE_LIST( ret, condition->inlineList, 0, false, false ); - ret << CLOSE_HOST_EXPR(); - ret << "\n"; - genOutputLineDirective( ret ); -} - -void CodeGen::NFA_CONDITION( ostream &ret, GenAction *condition, bool last ) -{ - if ( condition->inlineList->length() == 1 && - condition->inlineList->head->type == - GenInlineItem::NfaWrapAction ) - { - GenAction *action = condition->inlineList->head->wrappedAction; - ACTION( out, action, IlOpts( 0, false, false ) ); - } - else if ( condition->inlineList->length() == 1 && - condition->inlineList->head->type == - GenInlineItem::NfaWrapConds ) - { - ret << - " " << cpc << " = 0;\n"; - - GenCondSpace *condSpace = condition->inlineList->head->condSpace; - for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - ret << - " if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - ret << " ) " << cpc << " += " << condValOffset << ";\n"; - } - - const CondKeySet &keys = condition->inlineList->head->condKeySet; - if ( keys.length() > 0 ) { - ret << pop_test << " = "; - for ( CondKeySet::Iter cki = keys; cki.lte(); cki++ ) { - ret << "" << cpc << " == " << *cki; - if ( !cki.last() ) - ret << " || "; - } - ret << ";\n"; - } - else { - ret << pop_test << " = 0;\n"; - } - - if ( !last ) { - ret << - "if ( !" << pop_test << " )\n" - " break;\n"; - } - } - else { - ret << pop_test << " = "; - CONDITION( ret, condition ); - ret << ";\n"; - - if ( !last ) { - ret << - "if ( !" << pop_test << " )\n" - " break;\n"; - } - } -} - -void CodeGen::NFA_POP_TEST_EXEC() -{ - out << - " " << pop_test << " = 1;\n" - " switch ( nfa_bp[nfa_len].popTrans ) {\n"; - - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; - redAct.lte(); redAct++ ) - { - if ( redAct->numNfaPopTestRefs > 0 ) { - /* Write the entry label. */ - out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) - NFA_CONDITION( out, item->value, item.last() ); - - out << CEND() << "\n}\n"; - } - } - - out << - " }\n" - "\n"; -} - - -string CodeGen::ERROR_STATE() -{ - ostringstream ret; - if ( redFsm->errState != 0 ) - ret << redFsm->errState->id; - else - ret << "-1"; - return ret.str(); -} - -string CodeGen::FIRST_FINAL_STATE() -{ - ostringstream ret; - if ( redFsm->firstFinState != 0 ) - ret << redFsm->firstFinState->id; - else - ret << redFsm->nextStateId; - return ret.str(); -} - -void CodeGen::writeInit() -{ - out << " {\n"; - - if ( !noCS ) - out << "\t" << vCS() << " = " << CAST("int") << START() << ";\n"; - - if ( redFsm->anyNfaStates() ) - out << "\t" << "nfa_len = 0;\n"; - - /* If there are any calls, then the stack top needs initialization. */ - if ( redFsm->anyActionCalls() || redFsm->anyActionNcalls() || - redFsm->anyActionRets() || redFsm->anyActionNrets() ) - { - out << "\t" << TOP() << " = 0;\n"; - } - - if ( red->hasLongestMatch ) { - out << - " " << TOKSTART() << " = " << NIL() << ";\n" - " " << TOKEND() << " = " << NIL() << ";\n"; - - if ( redFsm->usingAct() ) { - out << - " " << ACT() << " = 0;\n"; - } - } - out << " }\n"; -} - -string CodeGen::DATA_PREFIX() -{ - if ( !noPrefix ) - return FSM_NAME() + "_"; - return ""; -} - -/* Emit the alphabet data type. */ -string CodeGen::ALPH_TYPE() -{ - string ret = alphType->data1; - if ( alphType->data2 != 0 ) { - ret += " "; - ret += + alphType->data2; - } - return ret; -} - -void CodeGen::VALUE( string type, string name, string value ) -{ - if ( backend == Direct ) - out << "static const " << type << " " << name << " = " << value << ";\n"; - else - out << "value " << type << " " << name << " = " << value << ";\n"; -} - -string CodeGen::STR( int v ) -{ - ostringstream s; - s << v; - return s.str(); -} - -void CodeGen::STATE_IDS() -{ - if ( redFsm->startState != 0 ) - VALUE( "int", START(), START_STATE_ID() ); - - if ( !noFinal ) - VALUE( "int", FIRST_FINAL(), FIRST_FINAL_STATE() ); - - if ( !noError ) - VALUE( "int", ERROR(), ERROR_STATE() ); - - out << "\n"; - - if ( red->entryPointNames.length() > 0 ) { - for ( EntryNameVect::Iter en = red->entryPointNames; en.lte(); en++ ) { - string name = DATA_PREFIX() + "en_" + *en; - VALUE( "int", name, STR( red->entryPointIds[en.pos()] ) ); - } - out << "\n"; - } -} - -void CodeGen::writeStart() -{ - out << START_STATE_ID(); -} - -void CodeGen::writeFirstFinal() -{ - out << FIRST_FINAL_STATE(); -} - -void CodeGen::writeError() -{ - out << ERROR_STATE(); -} - -void CodeGen::writeExports() -{ - if ( red->exportList.length() > 0 ) { - for ( ExportList::Iter ex = red->exportList; ex.lte(); ex++ ) { - out << EXPORT( ALPH_TYPE(), - DATA_PREFIX() + "ex_" + ex->name, KEY(ex->key) ) << "\n"; - } - out << "\n"; - } -} - -void CodeGen::NFA_PUSH( std::string state ) -{ - if ( redFsm->anyNfaStates() ) { - out << - " if ( " << ARR_REF( nfaOffsets ) << "[" << state << "] != 0 ) {\n" - " " << alt << " = 0; \n" - " " << new_recs << " = " << CAST("int") << ARR_REF( nfaTargs ) << "[" << CAST("int") << - ARR_REF( nfaOffsets ) << "[" << state << "]];\n"; - - if ( red->nfaPrePushExpr != 0 ) { - out << OPEN_HOST_BLOCK( red->nfaPrePushExpr ); - INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); - out << CLOSE_HOST_BLOCK(); - out << "\n"; - genOutputLineDirective( out ); - } - - out << - " while ( " << alt << " < " << new_recs << " ) { \n"; - - - out << - " nfa_bp[nfa_len].state = " << CAST("int") << ARR_REF( nfaTargs ) << "[" << CAST("int") << - ARR_REF( nfaOffsets ) << "[" << state << "] + 1 + " << alt << "];\n" - " nfa_bp[nfa_len].p = " << P() << ";\n"; - - if ( redFsm->bAnyNfaPops ) { - out << - " nfa_bp[nfa_len].popTrans = " << ARR_REF( nfaPopTrans ) << "[" << CAST("long") << - ARR_REF( nfaOffsets ) << "[" << state << "] + 1 + " << alt << "];\n" - "\n" - ; - } - - if ( redFsm->bAnyNfaPushes ) { - out << - " switch ( " << ARR_REF( nfaPushActions ) << "[" << CAST("int") << - ARR_REF( nfaOffsets ) << "[" << state << "] + 1 + " << alt << "] ) {\n"; - - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; - redAct.lte(); redAct++ ) - { - if ( redAct->numNfaPushRefs > 0 ) { - /* Write the entry label. */ - out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) - ACTION( out, item->value, IlOpts( 0, false, false ) ); - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - out << - " }\n"; - } - - - out << - " nfa_len += 1;\n" - " " << alt << " += 1;\n" - " }\n" - " }\n" - ; - } -} - -void CodeGen::NFA_POST_POP() -{ - if ( red->nfaPostPopExpr != 0 ) { - out << OPEN_HOST_BLOCK( red->nfaPostPopExpr ); - INLINE_LIST( out, red->nfaPostPopExpr->inlineList, 0, false, false ); - out << CLOSE_HOST_BLOCK(); - } -} diff --git a/libfsm/codegen.h b/libfsm/codegen.h deleted file mode 100644 index dcc24e3b..00000000 --- a/libfsm/codegen.h +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _C_CODEGEN_H -#define _C_CODEGEN_H - -#include -#include -#include -#include "common.h" -#include "gendata.h" -#include "vector.h" -#include "idbase.h" - -using std::string; -using std::ostream; - -/* Integer array line length. */ -//#define IALL 8 - -#define IALL_INTEGRAL 8 -#define IALL_STRING 128 - - -/* Forwards. */ -struct RedFsmAp; -struct RedStateAp; -struct CodeGenData; -struct GenAction; -struct NameInst; -struct GenInlineItem; -struct GenInlineList; -struct RedAction; -struct FsmLongestMatch; -struct LongestMatchPart; - -string itoa( int i ); - -struct Variable -{ - Variable( const char *name ) : name(name), isReferenced(false) {} - - const std::string ref() { isReferenced = true; return name; } - - const char *name; - bool isReferenced; -}; - -struct GotoLabel -{ - GotoLabel( const char *name ) : name(name), isReferenced(false) {} - - const std::string ref() { isReferenced = true; return name; } - - const char *name; - bool isReferenced; -}; - -std::ostream &operator<<( std::ostream &out, GotoLabel &l ); -std::ostream &operator<<( std::ostream &out, Variable &v ); - -struct TableArray; -typedef Vector ArrayVector; -class CodeGen; - -struct TableArray -{ - enum State { - InitialState = 1, - AnalyzePass, - GeneratePass - }; - - TableArray( const char *name, CodeGen &codeGen ); - - void start(); - void startAnalyze(); - void startGenerate(); - - void setType( std::string type, int width, bool isChar ) - { - this->type = type; - this->width = width; - this->isChar = isChar; - } - - std::string ref(); - - void value( long long v ); - - void valueAnalyze( long long v ); - void valueGenerate( long long v ); - void stringGenerate( long long value ); - - void finish(); - void finishAnalyze(); - void finishGenerate(); - - void setState( TableArray::State state ) - { this->state = state; } - - long long size(); - - State state; - const char *name; - std::string type; - int width; - bool isSigned; - bool isChar; - bool stringTables; - int iall; - long long values; - long long min; - long long max; - CodeGen &codeGen; - std::ostream &out; - int ln; - bool isReferenced; - bool started; -}; - -struct IlOpts -{ - IlOpts( int targState, bool inFinish, bool csForced ) - : targState(targState), inFinish(inFinish), csForced(csForced) {} - - int targState; - bool inFinish; - bool csForced; -}; - - -/* - * class CodeGen - */ -class CodeGen : public CodeGenData -{ -public: - CodeGen( const CodeGenArgs &args ); - - virtual ~CodeGen() {} - - virtual void writeInit(); - virtual void writeStart(); - virtual void writeFirstFinal(); - virtual void writeError(); - virtual void statsSummary(); - -protected: - friend struct TableArray; - typedef Vector ArrayVector; - ArrayVector arrayVector; - - Variable cpc; - Variable pop_test; - Variable new_recs; - Variable alt; - - string FSM_NAME(); - string START_STATE_ID(); - void taActions(); - string KEY( Key key ); - string LDIR_PATH( char *path ); - - void ACTION( ostream &ret, GenAction *action, IlOpts opts ); - void NFA_CONDITION( ostream &ret, GenAction *condition, bool last ); - void NFA_POP_TEST_EXEC(); - void CONDITION( ostream &ret, GenAction *condition ); - string ALPH_TYPE(); - - bool isAlphTypeSigned(); - long long tableData; - RagelBackend backend; - bool stringTables; - BackendFeature backendFeature; - - TableArray nfaTargs; - TableArray nfaOffsets; - TableArray nfaPushActions; - TableArray nfaPopTrans; - - virtual string GET_KEY(); - - string P(); - string PE(); - string vEOF(); - - string ACCESS(); - string vCS(); - string STACK(); - string TOP(); - string TOKSTART(); - string TOKEND(); - string ACT(); - - string DATA_PREFIX(); - string START() { return DATA_PREFIX() + "start"; } - string ERROR() { return DATA_PREFIX() + "error"; } - string FIRST_FINAL() { return DATA_PREFIX() + "first_final"; } - - /* Declare a variable only if referenced. */ - void DECLARE( std::string type, Variable &var, std::string init = "" ); - - string CAST( string type ); - - string ARR_TYPE( const TableArray &ta ) - { return ta.type; } - - string ARR_REF( TableArray &ta ) - { return ta.ref(); } - - void INLINE_EXPR( ostream &ret, GenInlineList *inlineList ); - void INLINE_BLOCK( ostream &ret, GenInlineExpr *inlineExpr ); - void INLINE_PLAIN( ostream &ret, GenInlineExpr *inlineExpr ); - - void INLINE_LIST( ostream &ret, GenInlineList *inlineList, - int targState, bool inFinish, bool csForced ); - virtual void GOTO( ostream &ret, int gotoDest, bool inFinish ) = 0; - virtual void CALL( ostream &ret, int callDest, int targState, bool inFinish ) = 0; - virtual void NCALL( ostream &ret, int callDest, int targState, bool inFinish ) = 0; - virtual void NEXT( ostream &ret, int nextDest, bool inFinish ) = 0; - virtual void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) = 0; - virtual void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) = 0; - virtual void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, - int targState, bool inFinish ) = 0; - virtual void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, - int targState, bool inFinish ) = 0; - virtual void RET( ostream &ret, bool inFinish ) = 0; - virtual void NRET( ostream &ret, bool inFinish ) = 0; - virtual void BREAK( ostream &ret, int targState, bool csForced ) = 0; - virtual void NBREAK( ostream &ret, int targState, bool csForced ) = 0; - virtual void CURS( ostream &ret, bool inFinish ) = 0; - virtual void TARGS( ostream &ret, bool inFinish, int targState ) = 0; - void EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); - void LM_SWITCH( ostream &ret, GenInlineItem *item, int targState, - int inFinish, bool csForced ); - void LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); - void SET_ACT( ostream &ret, GenInlineItem *item ); - void INIT_TOKSTART( ostream &ret, GenInlineItem *item ); - void INIT_ACT( ostream &ret, GenInlineItem *item ); - void SET_TOKSTART( ostream &ret, GenInlineItem *item ); - void SET_TOKEND( ostream &ret, GenInlineItem *item ); - void GET_TOKEND( ostream &ret, GenInlineItem *item ); - - void HOST_STMT( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); - void HOST_EXPR( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); - void HOST_TEXT( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); - void GEN_STMT( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); - void GEN_EXPR( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); - - void STATE_IDS(); - - string ERROR_STATE(); - string FIRST_FINAL_STATE(); - - string STR( int v ); - - void VALUE( string type, string name, string value ); - - string ACCESS_OPER() - { return backend == Direct ? "" : " -> "; } - - string OPEN_HOST_EXPR() - { return backend == Direct ? "(" : "host( \"-\", 1 ) ={"; } - - string OPEN_HOST_EXPR( string fileName, int line ) - { - return backend == Direct ? "(" : "host( \"" + fileName + "\", " + STR(line) + " ) ={"; - } - - string CLOSE_HOST_EXPR() - { return backend == Direct ? ")" : "}="; } - - string OPEN_HOST_BLOCK( string fileName, int line ) - { - if ( backend == Direct ) { - std::stringstream ss; - ss << "{\n" ; - (*genLineDirective)( ss, lineDirectives, line, fileName.c_str() ); - return ss.str(); - } - else { - return "host( \"" + fileName + "\", " + STR(line) + " ) ${"; - } - } - - string OPEN_HOST_BLOCK( GenInlineExpr *inlineExpr ) - { - return OPEN_HOST_BLOCK( inlineExpr->loc.fileName, inlineExpr->loc.line ); - } - - string CLOSE_HOST_BLOCK() - { return backend == Direct ? "}\n" : "}$"; } - - string OPEN_HOST_PLAIN() - { return backend == Direct ? "" : "host( \"-\", 1 ) @{"; } - - string CLOSE_HOST_PLAIN() - { return backend == Direct ? "" : "}@"; } - - string OPEN_GEN_EXPR() - { return backend == Direct ? "(" : "={"; } - - string CLOSE_GEN_EXPR() - { return backend == Direct ? ")" : "}="; } - - string OPEN_GEN_BLOCK() - { return backend == Direct ? "{" : "${"; } - - string CLOSE_GEN_BLOCK() - { return backend == Direct ? "}" : "}$"; } - - string OPEN_GEN_PLAIN() - { return backend == Direct ? "" : "@{"; } - - string CLOSE_GEN_PLAIN() - { return backend == Direct ? "" : "}@"; } - - string INT() - { return "int"; } - - string UINT() - { return backend == Direct ? "unsigned int" : "uint"; } - - string INDEX( string type, string name ) - { - if ( backend == Direct ) - return "const " + type + " *" + name; - else - return "index " + type + " " + name; - } - - string INDEX( string type ) - { - if ( backend == Direct ) - return "const " + type + " *"; - else - return "index " + type + " "; - } - - string LABEL( string name ) - { - return name + ": "; - } - - string EMIT_LABEL( GotoLabel label ) - { - if ( label.isReferenced ) - return std::string(label.name) + ": {}\n"; - else - return ""; - } - - string OFFSET( string arr, string off ) - { - if ( backend == Direct ) - return "( " + arr + " + (" + off + "))"; - else - return "offset( " + arr + ", " + off + " )"; - } - - string TRUE() - { - if ( backend == Direct ) - return "1"; - else - return "TRUE"; - } - - string DEREF( string arr, string off ) - { - if ( backend == Direct ) - return "(*( " + off + "))"; - else - return "deref( " + arr + ", " + off + " )"; - } - - string CASE( string val ) - { - if ( backend == Direct ) - return "case " + val + ": "; - else - return "case " + val; - } - - string DEFAULT() - { - if ( backend == Direct ) - return "default:"; - else - return "default"; - } - - string CEND( ) - { - if ( backend == Direct ) - return " break; "; - else - return " "; - } - - string FALLTHROUGH() - { - if ( backend == Direct ) - return " "; - else - return "fallthrough;"; - } - - string NIL() - { - if ( backend == Direct ) - return "0"; - else - return "nil"; - } - - string EXPORT( string type, string name, string value ) - { - if ( backend == Direct ) - return "#define " + name + " " + value; - else - return "export " + type + " " + name + " " + value + ";"; - } - - void NFA_POST_POP(); - virtual void NFA_PUSH( std::string ); - virtual void NFA_POP() = 0; - virtual void LOCATE_TRANS() {} - virtual void LOCATE_COND() {} - virtual void EOF_TRANS() {} - - - virtual void COND_EXEC( std::string expr ) {} - virtual void COND_BIN_SEARCH( Variable &var, TableArray &keys, std::string ok, std::string error ) {} - -public: - virtual void writeExports(); -}; - -#endif diff --git a/libfsm/common.cc b/libfsm/common.cc deleted file mode 100644 index 9ebbd905..00000000 --- a/libfsm/common.cc +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2006-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "common.h" -#include "stdlib.h" -#include -#include -#include "ragel.h" - -/* - * C - */ - -const char *defaultOutFnC( const char *inputFileName ) -{ - const char *ext = findFileExtension( inputFileName ); - if ( ext != 0 && strcmp( ext, ".rh" ) == 0 ) - return fileNameFromStem( inputFileName, ".h" ); - else - return fileNameFromStem( inputFileName, ".c" ); -} - -HostType hostTypesC[] = -{ - { "char", 0, "char", true, true, false, CHAR_MIN, CHAR_MAX, 0, 0, sizeof(char) }, - { "signed", "char", "char", true, true, false, CHAR_MIN, CHAR_MAX, 0, 0, sizeof(char) }, - { "unsigned", "char", "uchar", false, true, false, 0, 0, 0, UCHAR_MAX, sizeof(unsigned char) }, - { "short", 0, "short", true, true, false, SHRT_MIN, SHRT_MAX, 0, 0, sizeof(short) }, - { "signed", "short", "short", true, true, false, SHRT_MIN, SHRT_MAX, 0, 0, sizeof(short) }, - { "unsigned", "short", "ushort", false, true, false, 0, 0, 0, USHRT_MAX, sizeof(unsigned short) }, - { "int", 0, "int", true, true, false, INT_MIN, INT_MAX, 0, 0, sizeof(int) }, - { "signed", "int", "int", true, true, false, INT_MIN, INT_MAX, 0, 0, sizeof(int) }, - { "unsigned", "int", "uint", false, true, false, 0, 0, 0, UINT_MAX, sizeof(unsigned int) }, - { "long", 0, "long", true, true, false, LONG_MIN, LONG_MAX, 0, 0, sizeof(long) }, - { "signed", "long", "long", true, true, false, LONG_MIN, LONG_MAX, 0, 0, sizeof(long) }, - { "unsigned", "long", "ulong", false, true, false, 0, 0, 0, ULONG_MAX, sizeof(unsigned long) }, -}; - -const HostLang hostLangC = { - hostTypesC, - 12, - 0, - true, - false, /* loopLabels */ - Direct, - GotoFeature, - &makeCodeGen, - &defaultOutFnC, - &genLineDirectiveC -}; - -/* - * ASM - */ -const char *defaultOutFnAsm( const char *inputFileName ) -{ - return fileNameFromStem( inputFileName, ".s" ); -} - -HostType hostTypesAsm[] = -{ - { "char", 0, "char", true, true, false, CHAR_MIN, CHAR_MAX, 0, 0, sizeof(char) }, - { "unsigned", "char", "uchar", false, true, false, 0, 0, 0, UCHAR_MAX, sizeof(unsigned char) }, - { "short", 0, "short", true, true, false, SHRT_MIN, SHRT_MAX, 0, 0, sizeof(short) }, - { "unsigned", "short", "ushort", false, true, false, 0, 0, 0, USHRT_MAX, sizeof(unsigned short) }, - { "int", 0, "int", true, true, false, INT_MIN, INT_MAX, 0, 0, sizeof(int) }, - { "unsigned", "int", "uint", false, true, false, 0, 0, 0, UINT_MAX, sizeof(unsigned int) }, - { "long", 0, "long", true, true, false, LONG_MIN, LONG_MAX, 0, 0, sizeof(long) }, - { "unsigned", "long", "ulong", false, true, false, 0, 0, 0, ULONG_MAX, sizeof(unsigned long) }, -}; - -const HostLang hostLangAsm = { - hostTypesAsm, - 8, - 0, - true, - false, /* loopLabels */ - Direct, - GotoFeature, - &makeCodeGenAsm, - &defaultOutFnC, - &genLineDirectiveAsm -}; - -HostType *findAlphType( const HostLang *hostLang, const char *s1 ) -{ - for ( int i = 0; i < hostLang->numHostTypes; i++ ) { - if ( strcmp( s1, hostLang->hostTypes[i].data1 ) == 0 && - hostLang->hostTypes[i].data2 == 0 ) - { - return hostLang->hostTypes + i; - } - } - - return 0; -} - -HostType *findAlphType( const HostLang *hostLang, const char *s1, const char *s2 ) -{ - for ( int i = 0; i < hostLang->numHostTypes; i++ ) { - if ( strcmp( s1, hostLang->hostTypes[i].data1 ) == 0 && - hostLang->hostTypes[i].data2 != 0 && - strcmp( s2, hostLang->hostTypes[i].data2 ) == 0 ) - { - return hostLang->hostTypes + i; - } - } - - return 0; -} - -HostType *findAlphTypeInternal( const HostLang *hostLang, const char *s1 ) -{ - for ( int i = 0; i < hostLang->numHostTypes; i++ ) { - if ( strcmp( s1, hostLang->hostTypes[i].internalName ) == 0 ) - return hostLang->hostTypes + i; - } - - return 0; -} - -std::streamsize output_filter::countAndWrite( const char *s, std::streamsize n ) -{ - for ( int i = 0; i < n; i++ ) { - switch ( s[i] ) { - case '\n': - line += 1; - break; - case '{': - /* If we detec an open block then eliminate the single-indent - * addition, which is to account for single statements. */ - singleIndent = false; - level += 1; - break; - case '}': - level -= 1; - break; - } - } - - return std::filebuf::xsputn( s, n ); -} - -bool openSingleIndent( const char *s, int n ) -{ - if ( n >= 3 && memcmp( s, "if ", 3 ) == 0 ) - return true; - - if ( n >= 8 && memcmp( s, "else if ", 8 ) == 0 ) - return true; - - if ( n >= 5 && memcmp( s, "else\n", 4 ) == 0 ) - return true; - - return false; -} - -/* Counts newlines before sending sync. */ -int output_filter::sync( ) -{ - line += 1; - return std::filebuf::sync(); -} - -/* Counts newlines before sending data out to file. */ -std::streamsize output_filter::xsputn( const char *s, std::streamsize n ) -{ - std::streamsize ret = n; - int l; - -restart: - if ( indent ) { - /* Consume mode Looking for the first non-whitespace. */ - while ( n > 0 && ( *s == ' ' || *s == '\t' ) ) { - s += 1; - n -= 1; - } - - if ( n > 0 ) { - int tabs = level + ( singleIndent ? 1 : 0 ); - - if ( *s == '}' ) { - /* If the next char is de-dent, then reduce the tabs. This is - * not a stream state change. The level reduction will be - * computed in write. */ - tabs -= 1; - } - - /* Note that the count and write will eliminate this if it detects - * an open block. */ - if ( openSingleIndent( s, n ) ) - singleIndent = true; - else - singleIndent = false; - - if ( *s != '#' ) { - /* Found some data, print the indentation and turn off indentation - * mode. */ - for ( l = 0; l < tabs; l++ ) - countAndWrite( "\t", 1 ); - } - - - indent = 0; - - goto restart; - } - } - else { - char *nl; - if ( (nl = (char*)memchr( s, '\n', n )) ) { - /* Print up to and including the newline. */ - int wl = nl - s + 1; - countAndWrite( s, wl ); - - /* Go into consume state. If we see more non-indentation chars we - * will generate the appropriate indentation level. */ - s += wl; - n -= wl; - indent = true; - goto restart; - } - else { - /* Indentation off, or no indent trigger (newline). */ - countAndWrite( s, n ); - } - } - - // What to do here? - return ret; -} - -/* Scans a string looking for the file extension. If there is a file - * extension then pointer returned points to inside the string - * passed in. Otherwise returns null. */ -const char *findFileExtension( const char *stemFile ) -{ - const char *ppos = stemFile + strlen(stemFile) - 1; - - /* Scan backwards from the end looking for the first dot. - * If we encounter a '/' before the first dot, then stop the scan. */ - while ( 1 ) { - /* If we found a dot or got to the beginning of the string then - * we are done. */ - if ( ppos == stemFile || *ppos == '.' ) - break; - - /* If we hit a / then there is no extension. Done. */ - if ( *ppos == '/' ) { - ppos = stemFile; - break; - } - ppos--; - } - - /* If we got to the front of the string then bail we - * did not find an extension */ - if ( ppos == stemFile ) - ppos = 0; - - return ppos; -} - -/* Make a file name from a stem. Removes the old filename suffix and - * replaces it with a new one. Returns a newed up string. */ -const char *fileNameFromStem( const char *stemFile, const char *suffix ) -{ - long len = strlen( stemFile ); - assert( len > 0 ); - - /* Get the extension. */ - const char *ppos = findFileExtension( stemFile ); - - /* If an extension was found, then shorten what we think the len is. */ - if ( ppos != 0 ) - len = ppos - stemFile; - - /* Make the return string from the stem and the suffix. */ - char *retVal = new char[ len + strlen( suffix ) + 1 ]; - strncpy( retVal, stemFile, len ); - strcpy( retVal + len, suffix ); - - return retVal; -} - -exit_object endp; - -void operator<<( std::ostream &out, exit_object & ) -{ - out << std::endl; - throw AbortCompile( 1 ); -} - -void genLineDirectiveC( std::ostream &out, bool lineDirectives, int line, const char *fileName ) -{ - if ( !lineDirectives ) - out << "/* "; - - out << "#line " << line << " \""; - for ( const char *pc = fileName; *pc != 0; pc++ ) { - if ( *pc == '\\' ) - out << "\\\\"; - else if ( *pc == '"' ) - out << "\\\""; - else - out << *pc; - } - out << '"'; - - if ( !lineDirectives ) - out << " */"; - - out << '\n'; -} - -void genLineDirectiveAsm( std::ostream &out, bool lineDirectives, int line, const char *fileName ) -{ - out << "/* #line " << line << " \""; - for ( const char *pc = fileName; *pc != 0; pc++ ) { - if ( *pc == '\\' ) - out << "\\\\"; - else if ( *pc == '"' ) - out << "\\\""; - else - out << *pc; - } - out << '"'; - out << " */\n"; -} - -void genLineDirectiveTrans( std::ostream &out, bool lineDirectives, int line, const char *fileName ) -{ -} diff --git a/libfsm/common.h b/libfsm/common.h deleted file mode 100644 index 142eb735..00000000 --- a/libfsm/common.h +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _COMMON_H -#define _COMMON_H - -#include -#include -#include -#include "dlist.h" - -struct colm_location; - -struct InputData; -struct CodeGenData; -struct HostLang; -struct CodeGenArgs; - -enum RagelBackend -{ - Direct, - Translated -}; - -enum BackendFeature -{ - GotoFeature, - BreakFeature, - VarFeature -}; - -#define S8BIT_MIN -128 -#define S8BIT_MAX 127 - -#define U8BIT_MIN 0 -#define U8BIT_MAX 255 - -#define S16BIT_MIN -32768 -#define S16BIT_MAX 32767 - -#define U16BIT_MIN 0 -#define U16BIT_MAX 65535 - -#define S31BIT_MIN -1073741824l -#define S31BIT_MAX 1073741823l - -#define S32BIT_MIN -2147483648l -#define S32BIT_MAX 2147483647l - -#define U32BIT_MIN 0 -#define U32BIT_MAX 4294967295l - -#define S64BIT_MIN (-9223372036854775807LL - 1LL) -#define S64BIT_MAX 9223372036854775807LL - -#define U64BIT_MIN 0 -#define U64BIT_MAX 18446744073709551615ULL - -struct ParserLoc -{ - const char *fileName; - int line; - int col; -}; - -/* Location in an input file. */ -struct InputLoc -{ - InputLoc( colm_location *pcloc ); - - InputLoc() : fileName(0), line(-1), col(-1) {} - - InputLoc( const ParserLoc loc ) - { - fileName = loc.fileName; - line = loc.line; - col = loc.col; - - if ( fileName == 0 ) - fileName = "-"; - if ( line == 0 ) - line = 1; - } - - InputLoc( const InputLoc &loc ) - { - fileName = loc.fileName; - line = loc.line; - col = loc.col; - - if ( fileName == 0 ) - fileName = "-"; - if ( line == 0 ) - line = 1; - } - - InputLoc( const char *fileName, int line, int col ) - : fileName(fileName), line(line), col(col) {} - - const char *fileName; - int line; - int col; -}; - -extern InputLoc internal; - -typedef unsigned long long Size; - -struct Key -{ -private: - long key; - -public: - friend struct KeyOps; - - Key( ) {} - Key( const Key &key ) : key(key.key) {} - Key( long key ) : key(key) {} - - /* Returns the value used to represent the key. This value must be - * interpreted based on signedness. */ - long getVal() const { return key; }; - - bool isUpper() const { return ( 'A' <= key && key <= 'Z' ); } - bool isLower() const { return ( 'a' <= key && key <= 'z' ); } - bool isPrintable() const - { - return ( 7 <= key && key <= 13 ) || ( 32 <= key && key < 127 ); - } - - Key toUpper() const - { return Key( 'A' + ( key - 'a' ) ); } - Key toLower() const - { return Key( 'a' + ( key - 'A' ) ); } -}; - -struct CondKey -{ -private: - long key; - -public: - friend inline bool operator<( const CondKey key1, const CondKey key2 ); - friend inline bool operator>( const CondKey key1, const CondKey key2 ); - friend inline bool operator==( const CondKey key1, const CondKey key2 ); - friend inline CondKey operator+( const CondKey key1, const CondKey key2 ); - friend inline CondKey operator-( const CondKey key1, const CondKey key2 ); - - friend struct KeyOps; - - CondKey( ) {} - CondKey( const CondKey &key ) : key(key.key) {} - CondKey( long key ) : key(key) {} - - /* Returns the value used to represent the key. This value must be - * interpreted based on signedness. */ - long getVal() const { return key; }; - - bool isUpper() const { return ( 'A' <= key && key <= 'Z' ); } - bool isLower() const { return ( 'a' <= key && key <= 'z' ); } - bool isPrintable() const - { - return ( 7 <= key && key <= 13 ) || ( 32 <= key && key < 127 ); - } - - CondKey toUpper() const - { return CondKey( 'A' + ( key - 'a' ) ); } - CondKey toLower() const - { return CondKey( 'a' + ( key - 'A' ) ); } - - /* Decrement. Needed only for ranges. */ - inline void decrement(); - inline void increment(); -}; - -inline CondKey operator+(const CondKey key1, const CondKey key2) -{ - return CondKey( key1.key + key2.key ); -} - -inline CondKey operator-(const CondKey key1, const CondKey key2) -{ - return CondKey( key1.key - key2.key ); -} - -struct HostType -{ - const char *data1; - const char *data2; - const char *internalName; - bool isSigned; - bool isOrd; - bool isChar; - long long sMinVal; - long long sMaxVal; - unsigned long long uMinVal; - unsigned long long uMaxVal; - unsigned int size; -}; - -typedef void (*GenLineDirectiveT)( std::ostream &out, bool nld, int line, const char *file ); -typedef const char *(*DefaultOutFnT)( const char *inputFileName ); -typedef CodeGenData *(*MakeCodeGenT)( const HostLang *hostLang, const CodeGenArgs &args ); - -struct HostLang -{ - HostType *hostTypes; - int numHostTypes; - int defaultAlphType; - bool explicitUnsigned; - bool loopLabels; - - RagelBackend backend; - BackendFeature feature; - - MakeCodeGenT makeCodeGen; - DefaultOutFnT defaultOutFn; - GenLineDirectiveT genLineDirective; -}; - -void genLineDirectiveC( std::ostream &out, bool nld, int line, const char *file ); -void genLineDirectiveAsm( std::ostream &out, bool nld, int line, const char *file ); -void genLineDirectiveTrans( std::ostream &out, bool nld, int line, const char *file ); - -extern const HostLang hostLangC; -extern const HostLang hostLangAsm; - -HostType *findAlphType( const HostLang *hostLang, const char *s1 ); -HostType *findAlphType( const HostLang *hostLang, const char *s1, const char *s2 ); -HostType *findAlphTypeInternal( const HostLang *hostLang, const char *s1 ); - -const char *defaultOutFnC( const char *inputFileName ); -extern HostType hostTypesC[]; - -/* An abstraction of the key operators that manages key operations such as - * comparison and increment according the signedness of the key. */ -struct KeyOps -{ - /* Defaults to C "char" type: Signed 8 bit. */ - KeyOps() - : - isSigned(true), - explicitUnsigned(true), - minKey(CHAR_MIN), - maxKey(CHAR_MAX) - {} - - bool isSigned; - bool explicitUnsigned; - Key minKey, maxKey; - - void setAlphType( const HostLang *hostLang, const HostType *alphType ) - { - isSigned = alphType->isSigned; - explicitUnsigned = hostLang->explicitUnsigned; - - if ( isSigned ) { - minKey = (long) alphType->sMinVal; - maxKey = (long) alphType->sMaxVal; - } - else { - minKey = (long) alphType->uMinVal; - maxKey = (long) alphType->uMaxVal; - } - } - - /* Compute the distance between two keys. */ - Size span( Key key1, Key key2 ) - { - return isSigned ? - (unsigned long long)( - (long long)key2.key - - (long long)key1.key + 1) : - (unsigned long long)( - (unsigned long)key2.key) - - (unsigned long long)((unsigned long)key1.key) + 1; - } - - Size alphSize() - { return span( minKey, maxKey ); } - - inline bool lt( const Key key1, const Key key2 ) - { - return this->isSigned ? key1.key < key2.key : - (unsigned long)key1.key < (unsigned long)key2.key; - } - - inline bool le( const Key key1, const Key key2 ) - { - return this->isSigned ? key1.key <= key2.key : - (unsigned long)key1.key <= (unsigned long)key2.key; - } - - inline bool gt( const Key key1, const Key key2 ) - { - return this->isSigned ? key1.key > key2.key : - (unsigned long)key1.key > (unsigned long)key2.key; - } - - inline bool ge( const Key key1, const Key key2 ) - { - return this->isSigned ? key1.key >= key2.key : - (unsigned long)key1.key >= (unsigned long)key2.key; - } - - inline bool eq( const Key key1, const Key key2 ) - { - return key1.key == key2.key; - } - - inline bool ne( const Key key1, const Key key2 ) - { - return key1.key != key2.key; - } - - inline Key add(const Key key1, const Key key2) - { - /* FIXME: must be made aware of isSigned. */ - return Key( key1.key + key2.key ); - } - - inline Key sub(const Key key1, const Key key2) - { - /* FIXME: must be made aware of isSigned. */ - return Key( key1.key - key2.key ); - } - - /* Decrement. Needed only for ranges. */ - inline void decrement( Key &key ) - { - key.key = this->isSigned ? key.key - 1 : ((unsigned long)key.key)-1; - } - - /* Increment. Needed only for ranges. */ - inline void increment( Key &key ) - { - key.key = this->isSigned ? key.key+1 : ((unsigned long)key.key)+1; - } - - /* Returns the key casted to a long long. This form of the key does not - * require any signedness interpretation. */ - inline long long getLongLong( const Key &key ) - { - return this->isSigned ? (long long)key.key : (long long)(unsigned long)key.key; - } -}; - -/* CondKey */ - -inline bool operator<( const CondKey key1, const CondKey key2 ) -{ - return key1.key < key2.key; -} - -inline bool operator>( const CondKey key1, const CondKey key2 ) -{ - return key1.key > key2.key; -} - -inline bool operator==( const CondKey key1, const CondKey key2 ) -{ - return key1.key == key2.key; -} - -/* Increment. Needed only for ranges. */ -inline void CondKey::increment() -{ - key = key + 1; -} - - -/* Filter on the output stream that keeps track of the number of lines - * output. */ -class output_filter -: - public std::filebuf -{ -public: - output_filter( const char *fileName ) - : - fileName(fileName), - line(1), - level(0), - indent(false), - singleIndent(false) - {} - - virtual int sync(); - virtual std::streamsize xsputn( const char* s, std::streamsize n ); - - std::streamsize countAndWrite( const char* s, std::streamsize n ); - - const char *fileName; - int line; - int level; - bool indent; - bool singleIndent; -}; - -class nullbuf -: - public std::streambuf -{ -public: - virtual std::streamsize xsputn( const char * s, std::streamsize n ) - { return n; } - - virtual int overflow( int c ) - { return 1; } -}; - -class cfilebuf : public std::streambuf -{ -public: - cfilebuf( char *fileName, FILE* file ) : fileName(fileName), file(file) { } - char *fileName; - FILE *file; - - int sync() - { - fflush( file ); - return 0; - } - - int overflow( int c ) - { - if ( c != EOF ) - fputc( c, file ); - return 0; - } - - std::streamsize xsputn( const char* s, std::streamsize n ) - { - std::streamsize written = fwrite( s, 1, n, file ); - return written; - } -}; - -class costream : public std::ostream -{ -public: - costream( cfilebuf *b ) : - std::ostream(b), b(b) {} - - ~costream() - { delete b; } - - void fclose() - { ::fclose( b->file ); } - - cfilebuf *b; -}; - - -const char *findFileExtension( const char *stemFile ); -const char *fileNameFromStem( const char *stemFile, const char *suffix ); - -struct Export -{ - Export( std::string name, Key key ) - : name(name), key(key) {} - - std::string name; - Key key; - - Export *prev, *next; -}; - -typedef DList ExportList; - -struct exit_object { }; -extern exit_object endp; -void operator<<( std::ostream &out, exit_object & ); - -enum RagelFrontend -{ - KelbtBased, - ReduceBased -}; - -CodeGenData *makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ); -CodeGenData *makeCodeGenAsm( const HostLang *hostLang, const CodeGenArgs &args ); - -#endif diff --git a/libfsm/config.h.cmake.in b/libfsm/config.h.cmake.in deleted file mode 100644 index ad4bf494..00000000 --- a/libfsm/config.h.cmake.in +++ /dev/null @@ -1,13 +0,0 @@ -/* config.h Generated from config.h.cmake.in by cmake */ - -#ifndef _COLM_CONFIG_H -#define _COLM_CONFIG_H - -#cmakedefine DEBUG 1 - -#cmakedefine HAVE_SYS_WAIT_H 1 - -#cmakedefine SIZEOF_INT @SIZEOF_INT@ -#cmakedefine SIZEOF_LONG @SIZEOF_LONG@ - -#endif /* _COLM_CONFIG_H */ diff --git a/libfsm/dot.cc b/libfsm/dot.cc deleted file mode 100644 index f41ebc27..00000000 --- a/libfsm/dot.cc +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "dot.h" -#include "gendata.h" -#include "parsedata.h" - -using std::istream; -using std::ifstream; -using std::ostream; -using std::ios; -using std::cin; -using std::endl; - -void GraphvizDotGen::key( Key key ) -{ - if ( id->displayPrintables && key.isPrintable() ) { - // Output values as characters, ensuring we escape the quote (") character - char cVal = (char) key.getVal(); - switch ( cVal ) { - case '"': case '\\': - out << "'\\" << cVal << "'"; - break; - case '\a': - out << "'\\\\a'"; - break; - case '\b': - out << "'\\\\b'"; - break; - case '\t': - out << "'\\\\t'"; - break; - case '\n': - out << "'\\\\n'"; - break; - case '\v': - out << "'\\\\v'"; - break; - case '\f': - out << "'\\\\f'"; - break; - case '\r': - out << "'\\\\r'"; - break; - case ' ': - out << "SP"; - break; - default: - out << "'" << cVal << "'"; - break; - } - } - else { - if ( keyOps->isSigned ) - out << key.getVal(); - else - out << (unsigned long) key.getVal(); - } -} - -void GraphvizDotGen::condSpec( CondSpace *condSpace, long condVals ) -{ - if ( condSpace != 0 ) { - out << "("; - for ( CondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - bool set = condVals & (1 << csi.pos()); - if ( !set ) - out << "!"; - (*csi)->actionName( out ); - if ( !csi.last() ) - out << ", "; - } - out << ")"; - } -} - -void GraphvizDotGen::onChar( Key lowKey, Key highKey, CondSpace *condSpace, long condVals ) -{ - /* Output the key. Possibly a range. */ - key( lowKey ); - if ( keyOps->ne( highKey, lowKey ) ) { - out << ".."; - key( highKey ); - } - - condSpec( condSpace, condVals ); -} - - -void GraphvizDotGen::fromStateAction( StateAp *fromState ) -{ - int n = 0; - ActionTable *actionTables[3] = { 0, 0, 0 }; - - if ( fromState->fromStateActionTable.length() != 0 ) - actionTables[n++] = &fromState->fromStateActionTable; - - - /* Loop the existing actions and write out what's there. */ - for ( int a = 0; a < n; a++ ) { - for ( ActionTable::Iter actIt = actionTables[a]->first(); actIt.lte(); actIt++ ) { - Action *action = actIt->value; - action->actionName( out ); - if ( a < n-1 || !actIt.last() ) - out << ", "; - } - } - - if ( n > 0 ) - out << " / "; -} - -void GraphvizDotGen::transAction( StateAp *fromState, TransData *trans ) -{ - int n = 0; - ActionTable *actionTables[3] = { 0, 0, 0 }; - - if ( trans->actionTable.length() != 0 ) - actionTables[n++] = &trans->actionTable; - if ( trans->toState != 0 && trans->toState->toStateActionTable.length() != 0 ) - actionTables[n++] = &trans->toState->toStateActionTable; - - if ( n > 0 ) - out << " / "; - - /* Loop the existing actions and write out what's there. */ - for ( int a = 0; a < n; a++ ) { - for ( ActionTable::Iter actIt = actionTables[a]->first(); actIt.lte(); actIt++ ) { - Action *action = actIt->value; - action->actionName( out ); - if ( a < n-1 || !actIt.last() ) - out << ", "; - } - } -} - -void GraphvizDotGen::action( ActionTable *actionTable ) -{ - /* The action. */ - out << " / "; - for ( ActionTable::Iter actIt = actionTable->first(); actIt.lte(); actIt++ ) { - Action *action = actIt->value; - action->actionName( out ); - if ( !actIt.last() ) - out << ", "; - } -} - -void GraphvizDotGen::transList( StateAp *state ) -{ - /* Build the set of unique transitions out of this state. */ - RedTransSet stTransSet; - for ( TransList::Iter tel = state->outList; tel.lte(); tel++ ) { - if ( tel->plain() ) { - TransDataAp *tdap = tel->tdap(); - - /* Write out the from and to states. */ - out << "\t" << state->alg.stateNum << " -> "; - - if ( tdap->toState == 0 ) - out << "err_" << state->alg.stateNum; - else - out << tdap->toState->alg.stateNum; - - /* Begin the label. */ - out << " [ label = \""; - - fromStateAction( state ); - - onChar( tel->lowKey, tel->highKey, 0, 0 ); - - /* Write the action and close the transition. */ - transAction( state, tdap ); - - out << "\" ];\n"; - } - else { - for ( CondList::Iter ctel = tel->tcap()->condList; ctel.lte(); ctel++ ) { - /* Write out the from and to states. */ - out << "\t" << state->alg.stateNum << " -> "; - - if ( ctel->toState == 0 ) - out << "err_" << state->alg.stateNum; - else - out << ctel->toState->alg.stateNum; - - /* Begin the label. */ - out << " [ label = \""; - - fromStateAction( state ); - - onChar( tel->lowKey, tel->highKey, tel->condSpace, ctel->key.getVal() ); - - /* Write the action and close the transition. */ - transAction( state, ctel ); - out << "\" ];\n"; - } - } - } - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter nfa = *state->nfaOut; nfa.lte(); nfa++ ) { - out << "\t" << state->alg.stateNum << - " -> " << nfa->toState->alg.stateNum << - " [ label = \"EP," << nfa->order << " "; - - fromStateAction( state ); - -// if ( nfa->popTest.length() > 0 || -// nfa->popAction.length() > 0 || -// nfa->popCondKeys.length() > 0 ) -// { -// out << " / "; -// } - - if ( nfa->popCondKeys.length() > 0 ) { - for ( CondKeySet::Iter key = nfa->popCondKeys; key.lte(); key++ ) { - out << "("; - long condVals = *key; - for ( CondSet::Iter csi = nfa->popCondSpace->condSet; csi.lte(); csi++ ) { - bool set = condVals & (1 << csi.pos()); - if ( !set ) - out << "!"; - (*csi)->actionName( out ); - if ( !csi.last() ) - out << ", "; - } - out << ") "; - } - } - - if ( nfa->popAction.length() > 0 ) { - for ( ActionTable::Iter pa = nfa->popAction; pa.lte(); pa++ ) { - pa->value->actionName( out ); - if ( !pa.last() ) - out << ","; - } - } - - if ( nfa->popTest.length() > 0 ) { - for ( ActionTable::Iter pt = nfa->popTest; pt.lte(); pt++ ) { - pt->value->actionName( out ); - if ( !pt.last() ) - out << ","; - } - } - - out << "\" ];"; - } - } -} - -bool GraphvizDotGen::makeNameInst( std::string &res, NameInst *nameInst ) -{ - bool written = false; - if ( nameInst->parent != 0 ) - written = makeNameInst( res, nameInst->parent ); - - if ( !nameInst->name.empty() ) { - if ( written ) - res += '_'; - res += nameInst->name; - written = true; - } - - return written; -} - -void GraphvizDotGen::write( ) -{ - out << - "digraph " << fsmName << " {\n" - " rankdir=LR;\n"; - - /* Define the psuedo states. Transitions will be done after the states - * have been defined as either final or not final. */ - out << " node [ shape = point ];\n"; - - if ( fsm->startState != 0 ) - out << " ENTRY;\n"; - - /* Psuedo states for entry points in the entry map. */ - for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { - StateAp *state = en->value; - out << " en_" << state->alg.stateNum << ";\n"; - } - - /* Psuedo states for final states with eof actions. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - //if ( st->eofTrans != 0 && st->eofTrans->action != 0 ) - // out << " eof_" << st->id << ";\n"; - if ( st->eofActionTable.length() > 0 ) - out << " eof_" << st->alg.stateNum << ";\n"; - } - - out << " node [ shape = circle, height = 0.2 ];\n"; - - /* Psuedo states for states whose default actions go to error. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - bool needsErr = false; - for ( TransList::Iter tel = st->outList; tel.lte(); tel++ ) { - if ( tel->plain() ) { - if ( tel->tdap()->toState == 0 ) { - needsErr = true; - break; - } - } - else { - for ( CondList::Iter ctel = tel->tcap()->condList; ctel.lte(); ctel++ ) { - if ( ctel->toState == 0 ) { - needsErr = true; - break; - } - } - } - } - - if ( needsErr ) - out << " err_" << st->alg.stateNum << " [ label=\"\"];\n"; - } - - /* Attributes common to all nodes, plus double circle for final states. */ - out << " node [ fixedsize = true, height = 0.65, shape = doublecircle ];\n"; - - /* List Final states. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - if ( st->isFinState() ) - out << " " << st->alg.stateNum << ";\n"; - } - - /* List transitions. */ - out << " node [ shape = circle ];\n"; - - /* Walk the states. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) - transList( st ); - - /* Transitions into the start state. */ - if ( fsm->startState != 0 ) - out << " ENTRY -> " << fsm->startState->alg.stateNum << " [ label = \"IN\" ];\n"; - - for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { - NameInst *nameInst = fsmCtx->nameIndex[en->key]; - std::string name; - makeNameInst( name, nameInst ); - StateAp *state = en->value; - out << " en_" << state->alg.stateNum << - " -> " << state->alg.stateNum << - " [ label = \"" << name << "\" ];\n"; - } - - /* Out action transitions. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - if ( st->eofActionTable.length() != 0 ) { - out << " " << st->alg.stateNum << " -> eof_" << - st->alg.stateNum << " [ label = \"EOF"; - - for ( CondKeySet::Iter i = st->outCondKeys; i.lte(); i++ ) { - if ( i.pos() > 0 ) - out << "|"; - condSpec( st->outCondSpace, *i ); - } - - action( &st->eofActionTable ); - out << "\" ];\n"; - } - } - - out << - "}\n"; -} - diff --git a/libfsm/dot.h b/libfsm/dot.h deleted file mode 100644 index 13f53532..00000000 --- a/libfsm/dot.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _GVDOTGEN_H -#define _GVDOTGEN_H - -#include -#include "gendata.h" - - -class GraphvizDotGen : public RedBase -{ -public: - GraphvizDotGen( FsmGbl *id, FsmCtx *fsmCtx, FsmAp *fsm, - std::string fsmName, int machineId, std::ostream &out ) - : - RedBase(id, fsmCtx, fsm, fsmName, machineId), - out(out) - {} - - bool makeNameInst( std::string &res, NameInst *nameInst ); - void action( ActionTable *actionTable ); - void transAction( StateAp *fromState, TransData *trans ); - void key( Key key ); - void condSpec( CondSpace *condSpace, long condVals ); - void onChar( Key lowKey, Key highKey, CondSpace *condSpace, long condVals ); - void transList( StateAp *state ); - void write(); - void fromStateAction( StateAp *fromState ); - - ostream &out; -}; - -#endif diff --git a/libfsm/flat.cc b/libfsm/flat.cc deleted file mode 100644 index 8cda30db..00000000 --- a/libfsm/flat.cc +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright 2004-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "flat.h" -#include "redfsm.h" -#include "gendata.h" - -void Flat::genAnalysis() -{ - redFsm->sortByStateId(); - - /* Choose default transitions and the single transition. */ - redFsm->chooseDefaultSpan(); - - /* Do flat expand. */ - redFsm->makeFlatClass(); - - /* If any errors have occured in the input file then don't write anything. */ - if ( red->id->errorCount > 0 ) - return; - - /* Anlayze Machine will find the final action reference counts, among other - * things. We will use these in reporting the usage of fsm directives in - * action code. */ - red->analyzeMachine(); - - setKeyType(); - - /* Run the analysis pass over the table data. */ - setTableState( TableArray::AnalyzePass ); - tableDataPass(); - - /* Switch the tables over to the code gen mode. */ - setTableState( TableArray::GeneratePass ); -} - -void Flat::tableDataPass() -{ - if ( type == Flat::Loop ) { - if ( redFsm->anyActions() ) - taActions(); - } - - taKeys(); - taCharClass(); - taFlatIndexOffset(); - - taIndices(); - taIndexDefaults(); - taTransCondSpaces(); - - if ( red->condSpaceList.length() > 0 ) - taTransOffsets(); - - taCondTargs(); - taCondActions(); - - taToStateActions(); - taFromStateActions(); - taEofConds(); - taEofActions(); - taEofTrans(); - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); -} - -void Flat::writeData() -{ - if ( type == Flat::Loop ) { - /* If there are any transtion functions then output the array. If there - * are none, don't bother emitting an empty array that won't be used. */ - if ( redFsm->anyActions() ) - taActions(); - } - - taKeys(); - taCharClass(); - taFlatIndexOffset(); - - taIndices(); - taIndexDefaults(); - taTransCondSpaces(); - if ( red->condSpaceList.length() > 0 ) - taTransOffsets(); - taCondTargs(); - taCondActions(); - - if ( redFsm->anyToStateActions() ) - taToStateActions(); - - if ( redFsm->anyFromStateActions() ) - taFromStateActions(); - - taEofConds(); - - if ( redFsm->anyEofActions() ) - taEofActions(); - - if ( redFsm->anyEofTrans() ) - taEofTrans(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); - - STATE_IDS(); -} - - -void Flat::setKeyType() -{ - transKeys.setType( ALPH_TYPE(), alphType->size, alphType->isChar ); - transKeys.isSigned = keyOps->isSigned; -} - -void Flat::setTableState( TableArray::State state ) -{ - for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { - TableArray *tableArray = *i; - tableArray->setState( state ); - } -} - -void Flat::taFlatIndexOffset() -{ - flatIndexOffset.start(); - - int curIndOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Write the index offset. */ - flatIndexOffset.value( curIndOffset ); - - /* Move the index offset ahead. */ - if ( st->transList != 0 ) - curIndOffset += ( st->high - st->low + 1 ); - } - - flatIndexOffset.finish(); -} - -void Flat::taCharClass() -{ - charClass.start(); - - if ( redFsm->classMap != 0 ) { - long long maxSpan = keyOps->span( redFsm->lowKey, redFsm->highKey ); - - for ( long long pos = 0; pos < maxSpan; pos++ ) - charClass.value( redFsm->classMap[pos] ); - } - - charClass.finish(); -} - -void Flat::taToStateActions() -{ - toStateActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Write any eof action. */ - TO_STATE_ACTION(st); - } - - toStateActions.finish(); -} - -void Flat::taFromStateActions() -{ - fromStateActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Write any eof action. */ - FROM_STATE_ACTION( st ); - } - - fromStateActions.finish(); -} - -void Flat::taEofActions() -{ - eofActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Write any eof action. */ - EOF_ACTION( st ); - } - - eofActions.finish(); -} - -void Flat::taEofConds() -{ - /* - * EOF Cond Spaces - */ - eofCondSpaces.start(); - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->outCondSpace != 0 ) - eofCondSpaces.value( st->outCondSpace->condSpaceId ); - else - eofCondSpaces.value( -1 ); - } - eofCondSpaces.finish(); - - /* - * EOF Cond Key Indixes - */ - eofCondKeyOffs.start(); - - int curOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long off = 0; - if ( st->outCondSpace != 0 ) { - off = curOffset; - curOffset += st->outCondKeys.length(); - } - eofCondKeyOffs.value( off ); - } - - eofCondKeyOffs.finish(); - - /* - * EOF Cond Key Lengths. - */ - eofCondKeyLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long len = 0; - if ( st->outCondSpace != 0 ) - len = st->outCondKeys.length(); - eofCondKeyLens.value( len ); - } - - eofCondKeyLens.finish(); - - /* - * EOF Cond Keys - */ - eofCondKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->outCondSpace != 0 ) { - for ( int c = 0; c < st->outCondKeys.length(); c++ ) { - CondKey key = st->outCondKeys[c]; - eofCondKeys.value( key.getVal() ); - } - } - } - - eofCondKeys.finish(); -} - -void Flat::taEofTrans() -{ - /* Transitions must be written ordered by their id. */ - RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) - transPtrs[trans->id] = trans; - - long *transPos = new long[redFsm->transSet.length()]; - for ( int t = 0; t < redFsm->transSet.length(); t++ ) { - /* Save the position. Needed for eofTargs. */ - RedTransAp *trans = transPtrs[t]; - transPos[trans->id] = t; - } - - eofTrans.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long trans = 0; - - if ( st->eofTrans != 0 ) - trans = transPos[st->eofTrans->id] + 1; - - eofTrans.value( trans ); - } - - eofTrans.finish(); - - delete[] transPtrs; - delete[] transPos; -} - -void Flat::taKeys() -{ - transKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->transList ) { - /* Emit just low key and high key. */ - transKeys.value( st->low ); - transKeys.value( st->high ); - } - else { - /* Emit an impossible range so the driver fails the lookup. */ - transKeys.value( 1 ); - transKeys.value( 0 ); - } - } - - transKeys.finish(); -} - -void Flat::taIndices() -{ - indices.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->transList != 0 ) { - long long span = st->high - st->low + 1; - for ( long long pos = 0; pos < span; pos++ ) - indices.value( st->transList[pos]->id ); - } - } - - indices.finish(); -} - -void Flat::taIndexDefaults() -{ - indexDefaults.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) - indexDefaults.value( st->defTrans->id ); - else - indexDefaults.value( 0 ); - } - - indexDefaults.finish(); -} - - -void Flat::taTransCondSpaces() -{ - transCondSpaces.start(); - - /* Transitions must be written ordered by their id. */ - RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - transPtrs[trans->id] = trans; - } - - /* Keep a count of the num of items in the array written. */ - for ( int t = 0; t < redFsm->transSet.length(); t++ ) { - /* Save the position. Needed for eofTargs. */ - RedTransAp *trans = transPtrs[t]; - - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - delete[] transPtrs; - - transCondSpaces.finish(); -} - -void Flat::taTransOffsets() -{ - transOffsets.start(); - - /* Transitions must be written ordered by their id. */ - RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) - transPtrs[trans->id] = trans; - - /* Keep a count of the num of items in the array written. */ - int curOffset = 0; - for ( int t = 0; t < redFsm->transSet.length(); t++ ) { - /* Save the position. Needed for eofTargs. */ - RedTransAp *trans = transPtrs[t]; - - transOffsets.value( curOffset ); - - curOffset += trans->condFullSize(); - } - - delete[] transPtrs; - - transOffsets.finish(); -} - -void Flat::taCondTargs() -{ - condTargs.start(); - - /* Transitions must be written ordered by their id. */ - RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) - transPtrs[trans->id] = trans; - - /* Keep a count of the num of items in the array written. */ - for ( int t = 0; t < redFsm->transSet.length(); t++ ) { - /* Save the position. Needed for eofTargs. */ - RedTransAp *trans = transPtrs[t]; - - long fullSize = trans->condFullSize(); - RedCondPair **fullPairs = new RedCondPair*[fullSize]; - for ( long k = 0; k < fullSize; k++ ) - fullPairs[k] = trans->errCond(); - - for ( int c = 0; c < trans->numConds(); c++ ) - fullPairs[trans->outCondKey( c ).getVal()] = trans->outCond( c ); - - for ( int k = 0; k < fullSize; k++ ) { - RedCondPair *cond = fullPairs[k]; - condTargs.value( cond->targ->id ); - } - - delete[] fullPairs; - } - delete[] transPtrs; - - condTargs.finish(); -} - -void Flat::taCondActions() -{ - condActions.start(); - - /* Transitions must be written ordered by their id. */ - RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) - transPtrs[trans->id] = trans; - - /* Keep a count of the num of items in the array written. */ - for ( int t = 0; t < redFsm->transSet.length(); t++ ) { - /* Save the position. Needed for eofTargs. */ - RedTransAp *trans = transPtrs[t]; - - long fullSize = trans->condFullSize(); - RedCondPair **fullPairs = new RedCondPair*[fullSize]; - for ( long k = 0; k < fullSize; k++ ) - fullPairs[k] = trans->errCond(); - - for ( int c = 0; c < trans->numConds(); c++ ) - fullPairs[trans->outCondKey( c ).getVal()] = trans->outCond( c ); - - for ( int k = 0; k < fullSize; k++ ) { - RedCondPair *cond = fullPairs[k]; - COND_ACTION( cond ); - } - delete[] fullPairs; - } - delete[] transPtrs; - - condActions.finish(); -} - -/* Write out the array of actions. */ -void Flat::taActions() -{ - actions.start(); - - /* Add in the the empty actions array. */ - actions.value( 0 ); - - for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { - /* Length first. */ - actions.value( act->key.length() ); - - for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) - actions.value( item->value->actionId ); - } - - actions.finish(); -} - -void Flat::taNfaTargs() -{ - nfaTargs.start(); - - /* Offset of zero means no NFA targs, put a filler there. */ - nfaTargs.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaTargs.value( st->nfaTargs->length() ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - nfaTargs.value( targ->state->id ); - } - } - - nfaTargs.finish(); -} - -/* These need to mirror nfa targs. */ -void Flat::taNfaPushActions() -{ - nfaPushActions.start(); - - nfaPushActions.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaPushActions.value( 0 ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_PUSH_ACTION( targ ); - } - } - - nfaPushActions.finish(); -} - -void Flat::taNfaPopTrans() -{ - nfaPopTrans.start(); - - nfaPopTrans.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - - nfaPopTrans.value( 0 ); - - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_POP_TEST( targ ); - } - } - - nfaPopTrans.finish(); -} - - -void Flat::taNfaOffsets() -{ - nfaOffsets.start(); - - /* Offset of zero means no NFA targs, real targs start at 1. */ - long offset = 1; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs == 0 ) { - nfaOffsets.value( 0 ); - } - else { - nfaOffsets.value( offset ); - offset += 1 + st->nfaTargs->length(); - } - } - - nfaOffsets.finish(); -} - - - - - - - - diff --git a/libfsm/flat.h b/libfsm/flat.h deleted file mode 100644 index 1e54f5ab..00000000 --- a/libfsm/flat.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2004-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _C_FLAT_H -#define _C_FLAT_H - -#include -#include "codegen.h" -#include "tables.h" - -/* Forwards. */ -struct CodeGenData; -struct NameInst; -struct RedTransAp; -struct RedStateAp; - -class Flat - : public virtual Tables -{ -protected: - enum Type { - Loop = 1, Exp - }; - -public: - Flat( const CodeGenArgs &args, Type type ) - : - Tables( args ), - type(type) - {} - - virtual ~Flat() { } - -protected: - Type type; - - void taKeys(); - void taKeySpans(); - void taCharClass(); - void taActions(); - void taFlatIndexOffset(); - void taIndices(); - void taIndexDefaults(); - void taTransCondSpaces(); - void taTransOffsets(); - void taCondTargs(); - void taCondActions(); - void taToStateActions(); - void taFromStateActions(); - void taEofActions(); - void taEofTrans(); - void taEofConds(); - void taNfaTargs(); - void taNfaOffsets(); - void taNfaPushActions(); - void taNfaPopTrans(); - - void setKeyType(); - - std::ostream &INDICES(); - std::ostream &TRANS_COND_SPACES(); - std::ostream &TRANS_OFFSETS(); - std::ostream &TRANS_LENGTHS(); - std::ostream &COND_KEYS(); - std::ostream &COND_TARGS(); - std::ostream &COND_ACTIONS(); - - virtual void setTableState( TableArray::State ); - - virtual void genAnalysis(); - virtual void tableDataPass(); - virtual void writeData(); -}; - -#endif diff --git a/libfsm/flatbreak.cc b/libfsm/flatbreak.cc deleted file mode 100644 index 08342625..00000000 --- a/libfsm/flatbreak.cc +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "flatbreak.h" - -void FlatBreak::LOCATE_TRANS() -{ - if ( redFsm->classMap == 0 ) { - out << - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n"; - } - else { - long lowKey = redFsm->lowKey.getVal(); - long highKey = redFsm->highKey.getVal(); - - bool limitLow = keyOps->eq( lowKey, keyOps->minKey ); - bool limitHigh = keyOps->eq( highKey, keyOps->maxKey ); - - out << - " " << keys << " = " << OFFSET( ARR_REF( transKeys ), "(" + vCS() + "<<1)" ) << ";\n" - " " << inds << " = " << OFFSET( ARR_REF( indices ), - ARR_REF( flatIndexOffset ) + "[" + vCS() + "]" ) << ";\n" - "\n"; - - if ( !limitLow || !limitHigh ) { - out << " if ( "; - - if ( !limitHigh ) - out << GET_KEY() << " <= " << highKey; - - if ( !limitHigh && !limitLow ) - out << " && "; - - if ( !limitLow ) - out << GET_KEY() << " >= " << lowKey; - - out << " ) {\n"; - } - - out << - " " << ic << " = " << CAST("int") << ARR_REF( charClass ) << "[" << CAST("int") << GET_KEY() << - " - " << lowKey << "];\n" - " if ( " << ic << " <= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "+1" ) << " && " << - "" << ic << " >= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "" ) << " )\n" - " " << trans << " = " << CAST(UINT()) << DEREF( ARR_REF( indices ), - inds.ref() + " + " + CAST("int") + "( " + ic.ref() + " - " + CAST("int") + - DEREF( ARR_REF( transKeys ), keys.ref() + "" ) + " ) " ) << "; \n" - " else\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << - "[" << vCS() << "]" << ";\n"; - - if ( !limitLow || !limitHigh ) { - out << - " }\n" - " else {\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n" - " }\n" - "\n"; - } - } - - -} - -void FlatBreak::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - out << - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - out << - " switch ( " << ARR_REF( transCondSpaces ) << "[" << trans << "] ) {\n" - "\n"; - - for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { - GenCondSpace *condSpace = csi; - if ( condSpace->numTransRefs > 0 ) { - out << " " << CASE( STR(condSpace->condSpaceId) ) << " {\n"; - for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - out << "if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - out << " ) " << cpc << " += " << condValOffset << ";\n"; - } - - out << - " " << CEND() << "\n}\n"; - } - } - - out << - " }\n" - " " << cond << " += " << CAST( UINT() ) << "" << cpc << ";\n"; - } -} diff --git a/libfsm/flatbreak.h b/libfsm/flatbreak.h deleted file mode 100644 index 23400000..00000000 --- a/libfsm/flatbreak.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_FLATBREAK_H -#define RAGEL_FLATBREAK_H - -#include "flat.h" -#include "actloop.h" -#include "actexp.h" - -struct FlatBreak -: - public Flat, public TabBreak -{ - FlatBreak( const CodeGenArgs &args, Flat::Type type ) - : - Tables( args ), - Flat( args, type ), - TabBreak( args ) - {} - - void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class FlatBreakLoop - : public FlatBreak, public ActLoop -{ -public: - FlatBreakLoop( const CodeGenArgs &args ) - : - Tables( args ), - FlatBreak( args, Flat::Loop ), - ActLoop( args ) - {} -}; - -/* - * FlatBreakExp - */ -class FlatBreakExp - : public FlatBreak, public ActExp -{ -public: - FlatBreakExp( const CodeGenArgs &args ) - : - Tables( args ), - FlatBreak( args, Flat::Exp ), - ActExp( args ) - {} -}; - -#endif diff --git a/libfsm/flatgoto.cc b/libfsm/flatgoto.cc deleted file mode 100644 index c3206191..00000000 --- a/libfsm/flatgoto.cc +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "flatgoto.h" - -void FlatGoto::LOCATE_TRANS() -{ - if ( redFsm->classMap == 0 ) { - out << - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n"; - } - else { - long lowKey = redFsm->lowKey.getVal(); - long highKey = redFsm->highKey.getVal(); - - bool limitLow = keyOps->eq( lowKey, keyOps->minKey ); - bool limitHigh = keyOps->eq( highKey, keyOps->maxKey ); - - out << - " " << keys << " = " << OFFSET( ARR_REF( transKeys ), "(" + vCS() + "<<1)" ) << ";\n" - " " << inds << " = " << OFFSET( ARR_REF( indices ), - ARR_REF( flatIndexOffset ) + "[" + vCS() + "]" ) << ";\n" - "\n"; - - if ( !limitLow || !limitHigh ) { - out << " if ( "; - - if ( !limitHigh ) - out << GET_KEY() << " <= " << highKey; - - if ( !limitHigh && !limitLow ) - out << " && "; - - if ( !limitLow ) - out << GET_KEY() << " >= " << lowKey; - - out << " ) {\n"; - } - - out << - " " << ic << " = " << CAST("int") << ARR_REF( charClass ) << "[" << CAST("int") << GET_KEY() << - " - " << lowKey << "];\n" - " if ( " << ic << " <= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "+1" ) << " && " << - "" << ic << " >= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "" ) << " )\n" - " " << trans << " = " << CAST(UINT()) << DEREF( ARR_REF( indices ), - inds.ref() + " + " + CAST("int") + "( " + ic.ref() + " - " + CAST("int") + - DEREF( ARR_REF( transKeys ), keys.ref() + "" ) + " ) " ) << "; \n" - " else\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << - "[" << vCS() << "]" << ";\n"; - - if ( !limitLow || !limitHigh ) { - out << - " }\n" - " else {\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n" - " }\n" - "\n"; - } - } - -} - - -void FlatGoto::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - out << - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - out << - " switch ( " << ARR_REF( transCondSpaces ) << "[" << trans << "] ) {\n" - "\n"; - - for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { - GenCondSpace *condSpace = csi; - if ( condSpace->numTransRefs > 0 ) { - out << " " << CASE( STR(condSpace->condSpaceId) ) << " {\n"; - for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - out << "if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - out << " ) " << cpc << " += " << condValOffset << ";\n"; - } - - out << - " " << CEND() << "\n}\n"; - } - } - - out << - " }\n" - " " << cond << " += " << CAST( UINT() ) << "" << cpc << ";\n"; - } -} diff --git a/libfsm/flatgoto.h b/libfsm/flatgoto.h deleted file mode 100644 index e21b6cd9..00000000 --- a/libfsm/flatgoto.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_FLATGOTO_H -#define RAGEL_FLATGOTO_H - -#include "flat.h" -#include "actloop.h" -#include "actexp.h" - -struct FlatGoto -: - public Flat, public TabGoto -{ - FlatGoto( const CodeGenArgs &args, Flat::Type type ) - : - Tables( args ), - Flat( args, type ), - TabGoto( args ) - {} - - void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class FlatGotoLoop - : public FlatGoto, public ActLoop -{ -public: - FlatGotoLoop( const CodeGenArgs &args ) - : - Tables( args ), - FlatGoto( args, Flat::Loop ), - ActLoop( args ) - {} -}; - -/* - * FlatGotoExp - */ -class FlatGotoExp - : public FlatGoto, public ActExp -{ -public: - FlatGotoExp( const CodeGenArgs &args ) - : - Tables( args ), - FlatGoto( args, Flat::Exp ), - ActExp( args ) - {} -}; - -#endif diff --git a/libfsm/flatvar.cc b/libfsm/flatvar.cc deleted file mode 100644 index 40902940..00000000 --- a/libfsm/flatvar.cc +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "flatvar.h" -#include "parsedata.h" - -void FlatVar::LOCATE_TRANS() -{ - if ( redFsm->classMap == 0 ) { - out << - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n"; - } - else { - long lowKey = redFsm->lowKey.getVal(); - long highKey = redFsm->highKey.getVal(); - - bool limitLow = keyOps->eq( lowKey, keyOps->minKey ); - bool limitHigh = keyOps->eq( highKey, keyOps->maxKey ); - - out << - " " << keys << " = " << OFFSET( ARR_REF( transKeys ), "(" + vCS() + "<<1)" ) << ";\n" - " " << inds << " = " << OFFSET( ARR_REF( indices ), - ARR_REF( flatIndexOffset ) + "[" + vCS() + "]" ) << ";\n" - "\n"; - - if ( !limitLow || !limitHigh ) { - out << " if ( "; - - if ( !limitHigh ) - out << GET_KEY() << " <= " << highKey; - - if ( !limitHigh && !limitLow ) - out << " && "; - - if ( !limitLow ) - out << GET_KEY() << " >= " << lowKey; - - out << " ) {\n"; - } - - out << - " " << ic << " = " << CAST("int") << ARR_REF( charClass ) << "[" << CAST("int") << GET_KEY() << - " - " << lowKey << "];\n" - " if ( " << ic << " <= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "+1" ) << " && " << - "" << ic << " >= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "" ) << " )\n" - " " << trans << " = " << CAST(UINT()) << DEREF( ARR_REF( indices ), - inds.ref() + " + " + CAST("int") + "( " + ic.ref() + " - " + CAST("int") + - DEREF( ARR_REF( transKeys ), keys.ref() + "" ) + " ) " ) << "; \n" - " else\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << - "[" << vCS() << "]" << ";\n"; - - if ( !limitLow || !limitHigh ) { - out << - " }\n" - " else {\n" - " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n" - " }\n" - "\n"; - } - } -} - -void FlatVar::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - out << - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - out << - " switch ( " << ARR_REF( transCondSpaces ) << "[" << trans << "] ) {\n" - "\n"; - - for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { - GenCondSpace *condSpace = csi; - if ( condSpace->numTransRefs > 0 ) { - out << " " << CASE( STR(condSpace->condSpaceId) ) << " {\n"; - for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - out << "if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - out << " ) " << cpc << " += " << condValOffset << ";\n"; - } - - out << - " " << CEND() << "\n}\n"; - } - } - - out << - " }\n" - " " << cond << " += " << CAST( UINT() ) << "" << cpc << ";\n"; - } -} diff --git a/libfsm/flatvar.h b/libfsm/flatvar.h deleted file mode 100644 index 9cd80eab..00000000 --- a/libfsm/flatvar.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_FLATVAR_H -#define RAGEL_FLATVAR_H - -#include "flat.h" -#include "actloop.h" -#include "actexp.h" - -struct FlatVar -: - public Flat, public TabVar -{ - FlatVar( const CodeGenArgs &args, Flat::Type type ) - : - Tables( args ), - Flat( args, type ), - TabVar( args ) - {} - - void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class FlatVarLoop - : public FlatVar, public ActLoop -{ -public: - FlatVarLoop( const CodeGenArgs &args ) - : - Tables( args ), - FlatVar( args, Flat::Loop ), - ActLoop( args ) - {} -}; - -class FlatVarExp -: - public FlatVar, public ActExp -{ -public: - FlatVarExp( const CodeGenArgs &args ) - : - Tables( args ), - FlatVar( args, Flat::Exp ), - ActExp( args ) - {} -}; - -#endif diff --git a/libfsm/fsmap.cc b/libfsm/fsmap.cc deleted file mode 100644 index 98863f52..00000000 --- a/libfsm/fsmap.cc +++ /dev/null @@ -1,1200 +0,0 @@ -/* - * Copyright 2002-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "fsmgraph.h" -#include -using std::endl; - -/* Insert an action into an action table. */ -void ActionTable::setAction( int ordering, Action *action ) -{ - /* Multi-insert in case specific instances of an action appear in a - * transition more than once. */ - insertMulti( ordering, action ); -} - -/* Set all the action from another action table in this table. */ -void ActionTable::setActions( const ActionTable &other ) -{ - for ( ActionTable::Iter action = other; action.lte(); action++ ) - insertMulti( action->key, action->value ); -} - -void ActionTable::setActions( int *orderings, Action **actions, int nActs ) -{ - for ( int a = 0; a < nActs; a++ ) - insertMulti( orderings[a], actions[a] ); -} - -bool ActionTable::hasAction( Action *action ) -{ - for ( int a = 0; a < length(); a++ ) { - if ( data[a].value == action ) - return true; - } - return false; -} - -/* Insert an action into an action table. */ -void LmActionTable::setAction( int ordering, FsmLongestMatchPart *action ) -{ - /* Multi-insert in case specific instances of an action appear in a - * transition more than once. */ - insertMulti( ordering, action ); -} - -/* Set all the action from another action table in this table. */ -void LmActionTable::setActions( const LmActionTable &other ) -{ - for ( LmActionTable::Iter action = other; action.lte(); action++ ) - insertMulti( action->key, action->value ); -} - -void ErrActionTable::setAction( int ordering, Action *action, int transferPoint ) -{ - insertMulti( ErrActionTableEl( action, ordering, transferPoint ) ); -} - -void ErrActionTable::setActions( const ErrActionTable &other ) -{ - for ( ErrActionTable::Iter act = other; act.lte(); act++ ) - insertMulti( ErrActionTableEl( act->action, act->ordering, act->transferPoint ) ); -} - -/* Insert a priority into this priority table. Looks out for priorities on - * duplicate keys. */ -void PriorTable::setPrior( int ordering, PriorDesc *desc ) -{ - PriorEl *lastHit = 0; - PriorEl *insed = insert( PriorEl(ordering, desc), &lastHit ); - if ( insed == 0 ) { - /* This already has a priority on the same key as desc. Overwrite the - * priority if the ordering is larger (later in time). */ - if ( ordering >= lastHit->ordering ) - *lastHit = PriorEl( ordering, desc ); - } -} - -/* Set all the priorities from a priorTable in this table. */ -void PriorTable::setPriors( const PriorTable &other ) -{ - /* Loop src priorities once to overwrite duplicates. */ - PriorTable::Iter priorIt = other; - for ( ; priorIt.lte(); priorIt++ ) - setPrior( priorIt->ordering, priorIt->desc ); -} - -/* Set the priority of starting transitions. Isolates the start state so it has - * no other entry points, then sets the priorities of all the transitions out - * of the start state. If the start state is final, then the outPrior of the - * start state is also set. The idea is that a machine that accepts the null - * string can still specify the starting trans prior for when it accepts the - * null word. */ -void FsmAp::startFsmPrior( int ordering, PriorDesc *prior ) -{ - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - /* Walk all transitions out of the start state. */ - for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - trans->tdap()->priorTable.setPrior( ordering, prior ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - cond->priorTable.setPrior( ordering, prior ); - } - } - } - - if ( startState->nfaOut != 0 ) { - for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) - na->priorTable.setPrior( ordering, prior ); - } - - /* If the new start state is final then set the out priority. This follows - * the same convention as setting start action in the out action table of - * a final start state. */ - if ( startState->stateBits & STB_ISFINAL ) - startState->outPriorTable.setPrior( ordering, prior ); - - /* Start fsm priorities are a special case that may require - * minimization afterwards. */ - afterOpMinimize( true ); -} - -/* Set the priority of all transitions in a graph. Walks all transition lists - * and all def transitions. */ -void FsmAp::allTransPrior( int ordering, PriorDesc *prior ) -{ - /* Walk the list of all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Walk the out list of the state. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - trans->tdap()->priorTable.setPrior( ordering, prior ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - cond->priorTable.setPrior( ordering, prior ); - } - } - } - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter na = *state->nfaOut; na.lte(); na++ ) - na->priorTable.setPrior( ordering, prior ); - } - } -} - -/* Set the priority of all transitions that go into a final state. Note that if - * any entry states are final, we will not be setting the priority of any - * transitions that may go into those states in the future. The graph does not - * support pending in transitions in the same way pending out transitions are - * supported. */ -void FsmAp::finishFsmPrior( int ordering, PriorDesc *prior ) -{ - /* Walk all final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { - /* Walk all in transitions of the final state. */ - for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) - t->priorTable.setPrior( ordering, prior ); - for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) - t->priorTable.setPrior( ordering, prior ); - - if ( (*state)->nfaIn != 0 ) { - for ( NfaInList::Iter na = *(*state)->nfaIn; na.lte(); na++ ) - na->priorTable.setPrior( ordering, prior ); - } - } -} - -/* Set the priority of any future out transitions that may be made going out of - * this state machine. */ -void FsmAp::leaveFsmPrior( int ordering, PriorDesc *prior ) -{ - /* Set priority in all final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - (*state)->outPriorTable.setPrior( ordering, prior ); -} - - -/* Set actions to execute on starting transitions. Isolates the start state - * so it has no other entry points, then adds to the transition functions - * of all the transitions out of the start state. If the start state is final, - * then the func is also added to the start state's out func list. The idea is - * that a machine that accepts the null string can execute a start func when it - * matches the null word, which can only be done when leaving the start/final - * state. */ -void FsmAp::startFsmAction( int ordering, Action *action ) -{ - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - /* Walk the start state's transitions, setting functions. */ - for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - trans->tdap()->actionTable.setAction( ordering, action ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - cond->actionTable.setAction( ordering, action ); - } - } - } - - /* If start state is final then add the action to the out action table. - * This means that when the null string is accepted the start action will - * not be bypassed. */ - if ( startState->stateBits & STB_ISFINAL ) - startState->outActionTable.setAction( ordering, action ); - - if ( startState->nfaOut != 0 ) { - for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) { - - StateAp *state = na->toState; - - /* Walk the start state's transitions, setting functions. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - trans->tdap()->actionTable.setAction( ordering, action ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - cond->actionTable.setAction( ordering, action ); - } - } - } - - /* If start state is final then add the action to the out action table. - * This means that when the null string is accepted the start action will - * not be bypassed. */ - if ( state->stateBits & STB_ISFINAL ) - state->outActionTable.setAction( ordering, action ); - - } - } - - afterOpMinimize( true ); -} - -/* Set functions to execute on all transitions. Walks the out lists of all - * states. */ -void FsmAp::allTransAction( int ordering, Action *action ) -{ - /* Walk all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Walk the out list of the state. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - trans->tdap()->actionTable.setAction( ordering, action ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - cond->actionTable.setAction( ordering, action ); - } - } - } - } -} - -/* Specify functions to execute upon entering final states. If the start state - * is final we can't really specify a function to execute upon entering that - * final state the first time. So function really means whenever entering a - * final state from within the same fsm. */ -void FsmAp::finishFsmAction( int ordering, Action *action ) -{ - /* Walk all final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { - /* Walk the final state's in list. */ - for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) - t->actionTable.setAction( ordering, action ); - for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) - t->actionTable.setAction( ordering, action ); - } -} - -/* Add functions to any future out transitions that may be made going out of - * this state machine. */ -void FsmAp::leaveFsmAction( int ordering, Action *action ) -{ - /* Insert the action in the outActionTable of all final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - (*state)->outActionTable.setAction( ordering, action ); -} - -/* Add functions to the longest match action table for constructing scanners. */ -void FsmAp::longMatchAction( int ordering, FsmLongestMatchPart *lmPart ) -{ - /* Walk all final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { - /* Walk the final state's in list. */ - for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) - t->lmActionTable.setAction( ordering, lmPart ); - for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) - t->lmActionTable.setAction( ordering, lmPart ); - } -} - -void FsmAp::fillGaps( StateAp *state ) -{ - /* - * First pass fills in the the caps between transitions. - */ - if ( state->outList.length() == 0 ) { - /* Add the range on the lower and upper bound. */ - attachNewTrans( state, 0, ctx->keyOps->minKey, ctx->keyOps->maxKey ); - } - else { - TransList srcList; - srcList.transfer( state->outList ); - - /* Check for a gap at the beginning. */ - TransList::Iter trans = srcList, next; - if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) { - /* Make the high key and append. */ - Key highKey = trans->lowKey; - ctx->keyOps->decrement( highKey ); - - attachNewTrans( state, 0, ctx->keyOps->minKey, highKey ); - } - - /* Write the transition. */ - next = trans.next(); - state->outList.append( trans ); - - /* Keep the last high end. */ - Key lastHigh = trans->highKey; - - /* Loop each source range. */ - for ( trans = next; trans.lte(); trans = next ) { - /* Make the next key following the last range. */ - Key nextKey = lastHigh; - ctx->keyOps->increment( nextKey ); - - /* Check for a gap from last up to here. */ - if ( ctx->keyOps->lt( nextKey, trans->lowKey ) ) { - /* Make the high end of the range that fills the gap. */ - Key highKey = trans->lowKey; - ctx->keyOps->decrement( highKey ); - - attachNewTrans( state, 0, nextKey, highKey ); - } - - /* Reduce the transition. If it reduced to anything then add it. */ - next = trans.next(); - state->outList.append( trans ); - - /* Keep the last high end. */ - lastHigh = trans->highKey; - } - - /* Now check for a gap on the end to fill. */ - if ( ctx->keyOps->lt( lastHigh, ctx->keyOps->maxKey ) ) { - /* Get a copy of the default. */ - ctx->keyOps->increment( lastHigh ); - - attachNewTrans( state, 0, lastHigh, ctx->keyOps->maxKey ); - } - } - - /* - * Second pass fills in gaps in condition lists. - */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) - continue; - - CondList srcList; - srcList.transfer( trans->tcap()->condList ); - - CondList::Iter cond = srcList, next; - - /* Check for gap at the beginning. */ - if ( cond->key > 0 ) { - for ( CondKey key = 0; key < cond->key; key.increment() ) - attachNewCond( trans, state, 0, key ); - } - - next = cond.next(); - trans->tcap()->condList.append( cond ); - - CondKey lastKey = cond->key; - - for ( cond = next; cond.lte(); cond = next ) { - /* Make the next key following the last range. */ - CondKey nextKey = lastKey; - nextKey.increment(); - - /* Check for a gap from last up to here. */ - if ( nextKey < cond->key ) { - for ( CondKey key = nextKey; key < cond->key; key.increment() ) - attachNewCond( trans, state, 0, key ); - } - - next = cond.next(); - trans->tcap()->condList.append( cond ); - - lastKey = cond->key; - } - - CondKey high = (trans->condSpace == 0) ? - 0 : (1 << trans->condSpace->condSet.length()); - - /* Now check for a gap on the end to fill. */ - if ( lastKey < high ) { - /* Get a copy of the default. */ - lastKey.increment(); - - for ( CondKey key = lastKey; key < high; key.increment() ) - attachNewCond( trans, state, 0, key ); - } - } -} - -void FsmAp::setErrorActions( StateAp *state, const ActionTable &other ) -{ - /* Fill any gaps in the out list with an error transition. */ - fillGaps( state ); - - /* Set error transitions in the transitions that go to error. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState == 0 ) - trans->tdap()->actionTable.setActions( other ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState == 0 ) - cond->actionTable.setActions( other ); - } - } - } -} - -void FsmAp::setErrorAction( StateAp *state, int ordering, Action *action ) -{ - /* Fill any gaps in the out list with an error transition. */ - fillGaps( state ); - - /* Set error transitions in the transitions that go to error. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState == 0 ) - trans->tdap()->actionTable.setAction( ordering, action ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState == 0 ) - cond->actionTable.setAction( ordering, action ); - } - } - } -} - - -/* Give a target state for error transitions. */ -void FsmAp::setErrorTarget( StateAp *state, StateAp *target, int *orderings, - Action **actions, int nActs ) -{ - /* Fill any gaps in the out list with an error transition. */ - fillGaps( state ); - - /* Set error target in the transitions that go to error. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState == 0 ) { - /* The trans goes to error, redirect it. */ - redirectErrorTrans( trans->tdap()->fromState, target, trans->tdap() ); - trans->tdap()->actionTable.setActions( orderings, actions, nActs ); - } - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState == 0 ) { - /* The trans goes to error, redirect it. */ - redirectErrorTrans( cond->fromState, target, cond ); - cond->actionTable.setActions( orderings, actions, nActs ); - } - } - } - } -} - -void FsmAp::transferOutActions( StateAp *state ) -{ - for ( ActionTable::Iter act = state->outActionTable; act.lte(); act++ ) - state->eofActionTable.setAction( act->key, act->value ); - state->outActionTable.empty(); -} - -void FsmAp::transferErrorActions( StateAp *state, int transferPoint ) -{ - for ( int i = 0; i < state->errActionTable.length(); ) { - ErrActionTableEl *act = state->errActionTable.data + i; - if ( act->transferPoint == transferPoint ) { - /* Transfer the error action and remove it. */ - setErrorAction( state, act->ordering, act->action ); - if ( ! state->isFinState() ) - state->eofActionTable.setAction( act->ordering, act->action ); - state->errActionTable.vremove( i ); - } - else { - /* Not transfering and deleting, skip over the item. */ - i += 1; - } - } -} - -/* Set error actions in the start state. */ -void FsmAp::startErrorAction( int ordering, Action *action, int transferPoint ) -{ - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - /* Add the actions. */ - startState->errActionTable.setAction( ordering, action, transferPoint ); - - afterOpMinimize( true ); -} - -/* Set error actions in all states where there is a transition out. */ -void FsmAp::allErrorAction( int ordering, Action *action, int transferPoint ) -{ - /* Insert actions in the error action table of all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) - state->errActionTable.setAction( ordering, action, transferPoint ); -} - -/* Set error actions in final states. */ -void FsmAp::finalErrorAction( int ordering, Action *action, int transferPoint ) -{ - /* Add the action to the error table of final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - (*state)->errActionTable.setAction( ordering, action, transferPoint ); -} - -void FsmAp::notStartErrorAction( int ordering, Action *action, int transferPoint ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState ) - state->errActionTable.setAction( ordering, action, transferPoint ); - } -} - -void FsmAp::notFinalErrorAction( int ordering, Action *action, int transferPoint ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( ! state->isFinState() ) - state->errActionTable.setAction( ordering, action, transferPoint ); - } -} - -/* Set error actions in the states that have transitions into a final state. */ -void FsmAp::middleErrorAction( int ordering, Action *action, int transferPoint ) -{ - /* Isolate the start state in case it is reachable from in inside the - * machine, in which case we don't want it set. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState && ! state->isFinState() ) - state->errActionTable.setAction( ordering, action, transferPoint ); - } -} - -/* Set EOF actions in the start state. */ -void FsmAp::startEOFAction( int ordering, Action *action ) -{ - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - /* Add the actions. */ - startState->eofActionTable.setAction( ordering, action ); - - afterOpMinimize( true ); -} - -/* Set EOF actions in all states where there is a transition out. */ -void FsmAp::allEOFAction( int ordering, Action *action ) -{ - /* Insert actions in the EOF action table of all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) - state->eofActionTable.setAction( ordering, action ); -} - -/* Set EOF actions in final states. */ -void FsmAp::finalEOFAction( int ordering, Action *action ) -{ - /* Add the action to the error table of final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - (*state)->eofActionTable.setAction( ordering, action ); -} - -void FsmAp::notStartEOFAction( int ordering, Action *action ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState ) - state->eofActionTable.setAction( ordering, action ); - } -} - -void FsmAp::notFinalEOFAction( int ordering, Action *action ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( ! state->isFinState() ) - state->eofActionTable.setAction( ordering, action ); - } -} - -/* Set EOF actions in the states that have transitions into a final state. */ -void FsmAp::middleEOFAction( int ordering, Action *action ) -{ - /* Set the actions in all states that are not the start state and not final. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState && ! state->isFinState() ) - state->eofActionTable.setAction( ordering, action ); - } -} - -/* - * Set To State Actions. - */ - -/* Set to state actions in the start state. */ -void FsmAp::startToStateAction( int ordering, Action *action ) -{ - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - startState->toStateActionTable.setAction( ordering, action ); - - afterOpMinimize( true ); -} - -/* Set to state actions in all states. */ -void FsmAp::allToStateAction( int ordering, Action *action ) -{ - /* Insert the action on all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) - state->toStateActionTable.setAction( ordering, action ); -} - -/* Set to state actions in final states. */ -void FsmAp::finalToStateAction( int ordering, Action *action ) -{ - /* Add the action to the error table of final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - (*state)->toStateActionTable.setAction( ordering, action ); -} - -void FsmAp::notStartToStateAction( int ordering, Action *action ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState ) - state->toStateActionTable.setAction( ordering, action ); - } -} - -void FsmAp::notFinalToStateAction( int ordering, Action *action ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( ! state->isFinState() ) - state->toStateActionTable.setAction( ordering, action ); - } -} - -/* Set to state actions in states that are not final and not the start state. */ -void FsmAp::middleToStateAction( int ordering, Action *action ) -{ - /* Set the action in all states that are not the start state and not final. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState && ! state->isFinState() ) - state->toStateActionTable.setAction( ordering, action ); - } -} - -/* - * Set From State Actions. - */ - -void FsmAp::startFromStateAction( int ordering, Action *action ) -{ - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - startState->fromStateActionTable.setAction( ordering, action ); - - afterOpMinimize( true ); -} - -void FsmAp::allFromStateAction( int ordering, Action *action ) -{ - /* Insert the action on all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) - state->fromStateActionTable.setAction( ordering, action ); -} - -void FsmAp::finalFromStateAction( int ordering, Action *action ) -{ - /* Add the action to the error table of final states. */ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - (*state)->fromStateActionTable.setAction( ordering, action ); -} - -void FsmAp::notStartFromStateAction( int ordering, Action *action ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState ) - state->fromStateActionTable.setAction( ordering, action ); - } -} - -void FsmAp::notFinalFromStateAction( int ordering, Action *action ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( ! state->isFinState() ) - state->fromStateActionTable.setAction( ordering, action ); - } -} - -void FsmAp::middleFromStateAction( int ordering, Action *action ) -{ - /* Set the action in all states that are not the start state and not final. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - if ( state != startState && ! state->isFinState() ) - state->fromStateActionTable.setAction( ordering, action ); - } -} - -/* Shift the function ordering of the start transitions to start - * at fromOrder and increase in units of 1. Useful before staring. - * Returns the maximum number of order numbers used. */ -int FsmAp::shiftStartActionOrder( int fromOrder ) -{ - int maxUsed = 0; - - /* Walk the start state's transitions, shifting function ordering. */ - for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - int curFromOrder = fromOrder; - ActionTable::Iter action = trans->tdap()->actionTable; - for ( ; action.lte(); action++ ) - action->key = curFromOrder++; - - /* Keep track of the max number of orders used. */ - if ( curFromOrder - fromOrder > maxUsed ) - maxUsed = curFromOrder - fromOrder; - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - /* Walk the function data for the transition and set the keys to - * increasing values starting at fromOrder. */ - int curFromOrder = fromOrder; - ActionTable::Iter action = cond->actionTable; - for ( ; action.lte(); action++ ) - action->key = curFromOrder++; - - /* Keep track of the max number of orders used. */ - if ( curFromOrder - fromOrder > maxUsed ) - maxUsed = curFromOrder - fromOrder; - } - } - } - - return maxUsed; -} - -/* Remove all priorities. */ -void FsmAp::clearAllPriorities() -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Clear out priority data. */ - state->outPriorTable.empty(); - - /* Clear transition data from the out transitions. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) - trans->tdap()->priorTable.empty(); - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) - cond->priorTable.empty(); - } - } - - if ( state->nfaIn != 0 ) { - for ( NfaInList::Iter na = *state->nfaIn; na.lte(); na++ ) - na->priorTable.empty(); - } - } -} - -/* Zeros out the function ordering keys. This may be called before minimization - * when it is known that no more fsm operations are going to be done. This - * will achieve greater reduction as states will not be separated on the basis - * of function ordering. */ -void FsmAp::nullActionKeys( ) -{ - /* For each state... */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Walk the transitions for the state. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - /* Walk the action table for the transition. */ - for ( ActionTable::Iter action = trans->tdap()->actionTable; - action.lte(); action++ ) - action->key = 0; - - /* Walk the action table for the transition. */ - for ( LmActionTable::Iter action = trans->tdap()->lmActionTable; - action.lte(); action++ ) - action->key = 0; - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - /* Walk the action table for the transition. */ - for ( ActionTable::Iter action = cond->actionTable; - action.lte(); action++ ) - action->key = 0; - - /* Walk the action table for the transition. */ - for ( LmActionTable::Iter action = cond->lmActionTable; - action.lte(); action++ ) - action->key = 0; - } - } - } - - /* Null the action keys of the to state action table. */ - for ( ActionTable::Iter action = state->toStateActionTable; - action.lte(); action++ ) - action->key = 0; - - /* Null the action keys of the from state action table. */ - for ( ActionTable::Iter action = state->fromStateActionTable; - action.lte(); action++ ) - action->key = 0; - - /* Null the action keys of the out transtions. */ - for ( ActionTable::Iter action = state->outActionTable; - action.lte(); action++ ) - action->key = 0; - - /* Null the action keys of the error action table. */ - for ( ErrActionTable::Iter action = state->errActionTable; - action.lte(); action++ ) - action->ordering = 0; - - /* Null the action keys eof action table. */ - for ( ActionTable::Iter action = state->eofActionTable; - action.lte(); action++ ) - action->key = 0; - } -} - -/* Walk the list of states and verify that non final states do not have out - * data, that all stateBits are cleared, and that there are no states with - * zero foreign in transitions. */ -void FsmAp::verifyStates() -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Non final states should not have leaving data. */ - if ( ! (state->stateBits & STB_ISFINAL) ) { - assert( state->outActionTable.length() == 0 ); - assert( state->outCondSpace == 0 ); - assert( state->outCondKeys.length() == 0 ); - assert( state->outPriorTable.length() == 0 ); - } - - /* Data used in algorithms should be cleared. */ - assert( (state->stateBits & STB_BOTH) == 0 ); - assert( state->foreignInTrans > 0 ); - } -} - -/* Compare two transitions according to their relative priority. Since the - * base transition has no priority associated with it, the default is to - * return equal. */ -int FsmAp::comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 ) -{ - /* Looking for differing priorities on same keys. Need to concurrently - * scan the priority lists. */ - PriorTable::Iter pd1 = priorTable1; - PriorTable::Iter pd2 = priorTable2; - while ( pd1.lte() && pd2.lte() ) { - /* Check keys. */ - if ( pd1->desc->key < pd2->desc->key ) - pd1.increment(); - else if ( pd1->desc->key > pd2->desc->key ) - pd2.increment(); - /* Keys are the same, check priorities. */ - else if ( pd1->desc->priority < pd2->desc->priority ) { - if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { - if ( ! priorInteraction ) { - priorInteraction = true; - guardId = pd1->desc->guardId; - } - } - return -1; - } - else if ( pd1->desc->priority > pd2->desc->priority ) { - if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { - if ( ! priorInteraction ) { - priorInteraction = true; - guardId = pd1->desc->guardId; - } - } - return 1; - } - else { - /* Keys and priorities are equal, advance both. */ - pd1.increment(); - pd2.increment(); - } - } - - /* No differing priorities on the same key. */ - return 0; -} - -int FsmAp::compareCondListBitElim( const CondList &condList1, const CondList &condList2 ) -{ - typedef ValPairIter< PiList > ValPairIterPiListCondAp; - ValPairIterPiListCondAp outPair( condList1, condList2 ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - case ValPairIterPiListCondAp::RangeInS1: { - int compareRes = FsmAp::compareCondBitElimPtr( outPair.s1Tel.trans, 0 ); - if ( compareRes != 0 ) - return compareRes; - break; - } - case ValPairIterPiListCondAp::RangeInS2: { - int compareRes = FsmAp::compareCondBitElimPtr( 0, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - } - case ValPairIterPiListCondAp::RangeOverlap: { - int compareRes = FsmAp::compareCondBitElimPtr( - outPair.s1Tel.trans, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - }} - } - return 0; -} - -/* Compares two transitions according to priority and functions. Pointers - * should not be null. Does not consider to state or from state. Compare two - * transitions according to the data contained in the transitions. Data means - * any properties added to user transitions that may differentiate them. Since - * the base transition has no data, the default is to return equal. */ -int FsmAp::compareTransData( TransAp *trans1, TransAp *trans2 ) -{ - if ( trans1->condSpace < trans2->condSpace ) - return -1; - else if ( trans2->condSpace < trans1->condSpace ) - return 1; - - if ( trans1->plain() ) { - int compareRes = FsmAp::compareCondDataPtr( trans1->tdap(), trans2->tdap() ); - if ( compareRes != 0 ) - return compareRes; - } - else { - typedef ValPairIter< PiList > ValPairIterPiListCondAp; - ValPairIterPiListCondAp outPair( trans1->tcap()->condList, - trans2->tcap()->condList ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - case ValPairIterPiListCondAp::RangeInS1: { - int compareRes = FsmAp::compareCondDataPtr( outPair.s1Tel.trans, 0 ); - if ( compareRes != 0 ) - return compareRes; - break; - } - case ValPairIterPiListCondAp::RangeInS2: { - int compareRes = FsmAp::compareCondDataPtr( 0, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - } - case ValPairIterPiListCondAp::RangeOverlap: { - int compareRes = FsmAp::compareCondDataPtr( - outPair.s1Tel.trans, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - }} - } - } - return 0; -} - -/* Compares two transitions according to priority and functions. Pointers - * should not be null. Does not consider to state or from state. Compare two - * transitions according to the data contained in the transitions. Data means - * any properties added to user transitions that may differentiate them. Since - * the base transition has no data, the default is to return equal. */ -template< class Trans > int FsmAp::compareCondData( Trans *trans1, Trans *trans2 ) -{ - /* Compare the prior table. */ - int cmpRes = CmpPriorTable::compare( trans1->priorTable, - trans2->priorTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Compare longest match action tables. */ - cmpRes = CmpLmActionTable::compare(trans1->lmActionTable, - trans2->lmActionTable); - if ( cmpRes != 0 ) - return cmpRes; - - /* Compare action tables. */ - return CmpActionTable::compare(trans1->actionTable, - trans2->actionTable); -} - -/* Compares two transitions according to priority and functions. Pointers - * should not be null. Does not consider to state or from state. Compare two - * transitions according to the data contained in the transitions. Data means - * any properties added to user transitions that may differentiate them. Since - * the base transition has no data, the default is to return equal. */ -template< class Trans > int FsmAp::compareCondBitElim( Trans *trans1, Trans *trans2 ) -{ - if ( trans1->toState < trans2->toState ) - return -1; - else if ( trans1->toState > trans2->toState ) - return 1; - - /* Compare the prior table. */ - int cmpRes = CmpPriorTable::compare( trans1->priorTable, - trans2->priorTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Compare longest match action tables. */ - cmpRes = CmpLmActionTable::compare(trans1->lmActionTable, - trans2->lmActionTable); - if ( cmpRes != 0 ) - return cmpRes; - - /* Compare action tables. */ - return CmpActionTable::compare(trans1->actionTable, - trans2->actionTable); -} - -/* Compare the properties of states that are embedded by users. Compares out - * priorities, out transitions, to, from, out, error and eof action tables. */ -int FsmAp::compareStateData( const StateAp *state1, const StateAp *state2 ) -{ - /* Compare the out priority table. */ - int cmpRes = CmpPriorTable:: - compare( state1->outPriorTable, state2->outPriorTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Test to state action tables. */ - cmpRes = CmpActionTable::compare( state1->toStateActionTable, - state2->toStateActionTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Test from state action tables. */ - cmpRes = CmpActionTable::compare( state1->fromStateActionTable, - state2->fromStateActionTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Test out action tables. */ - cmpRes = CmpActionTable::compare( state1->outActionTable, - state2->outActionTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Out condition space and set of vals. */ - if ( state1->outCondSpace < state2->outCondSpace ) - return -1; - else if ( state1->outCondSpace > state2->outCondSpace ) - return 1; - - cmpRes = CmpTable::compare( state1->outCondKeys, - state2->outCondKeys ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Test out error action tables. */ - cmpRes = CmpErrActionTable::compare( state1->errActionTable, - state2->errActionTable ); - if ( cmpRes != 0 ) - return cmpRes; - - /* Test eof action tables. */ - cmpRes = CmpActionTable::compare( state1->eofActionTable, - state2->eofActionTable ); - if ( cmpRes != 0 ) - return cmpRes; - - return CmpTable::compare( - state1->lmNfaParts, state2->lmNfaParts ); -} - - -/* Invoked when a state looses its final state status and the leaving - * transition embedding data should be deleted. */ -void FsmAp::clearOutData( StateAp *state ) -{ - /* Kill the out actions and priorities. */ - state->outCondSpace = 0; - state->outCondKeys.empty(); - state->outActionTable.empty(); - state->outPriorTable.empty(); -} - -bool FsmAp::hasOutData( StateAp *state ) -{ - return ( state->outActionTable.length() > 0 || - state->outCondSpace != 0 || - state->outCondKeys.length() > 0 || - state->outPriorTable.length() > 0 || - state->outCondSpace != 0 ); -} - -/* - * Setting Conditions. - */ - -FsmRes FsmAp::startFsmCondition( Action *condAction, bool sense ) -{ - CondSet set; - CondKeySet vals; - set.insert( condAction ); - vals.append( sense ? 1 : 0 ); - - /* Make sure the start state has no other entry points. */ - isolateStartState( this ); - - FsmRes res = embedCondition( this, startState, set, vals ); - if ( !res.success() ) - return res; - - if ( startState->nfaOut != 0 ) { - /* Only one level. */ - for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) { - res = embedCondition( this, startState, set, vals ); - if ( !res.success() ) - return res; - } - } - - afterOpMinimize( true ); - - return FsmRes( FsmRes::Fsm(), this ); -} - -void FsmAp::allTransCondition( Action *condAction, bool sense ) -{ - CondSet set; - CondKeySet vals; - set.insert( condAction ); - vals.append( sense ? 1 : 0 ); - - for ( StateList::Iter state = stateList; state.lte(); state++ ) - embedCondition( this, state, set, vals ); -} - -void FsmAp::leaveFsmCondition( Action *condAction, bool sense ) -{ - for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) - addOutCondition( *state, condAction, sense ); -} diff --git a/libfsm/fsmattach.cc b/libfsm/fsmattach.cc deleted file mode 100644 index 5e7e5e7c..00000000 --- a/libfsm/fsmattach.cc +++ /dev/null @@ -1,857 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include "fsmgraph.h" - -#include -using namespace std; - -void FsmAp::attachStateDict( StateAp *from, StateAp *to ) -{ - if ( to->stateDictIn == 0 ) - to->stateDictIn = new StateSet; - - bool inserted = to->stateDictIn->insert( from ); - assert( inserted ); - - if ( from != to ) { - if ( misfitAccounting ) { - if ( to->foreignInTrans == 0 ) - stateList.append( misfitList.detach( to ) ); - } - - to->foreignInTrans += 1; - } -} - -void FsmAp::detachStateDict( StateAp *from, StateAp *to ) -{ - bool removed = to->stateDictIn->remove( from ); - assert( removed ); - - to->foreignInTrans -= 1; - - if ( from != to ) { - if ( misfitAccounting ) { - if ( to->foreignInTrans == 0 ) - misfitList.append( stateList.detach( to ) ); - } - } -} - -void FsmAp::attachToNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ) -{ - if ( to->nfaIn == 0 ) - to->nfaIn = new NfaInList; - - nfaTrans->fromState = from; - nfaTrans->toState = to; - - attachToInList( from, to, to->nfaIn->head, nfaTrans ); -} - -void FsmAp::detachFromNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ) -{ - nfaTrans->fromState = 0; - nfaTrans->toState = 0; - - detachFromInList( from, to, to->nfaIn->head, nfaTrans ); -} - -template< class Head > void FsmAp::attachToInList( StateAp *from, - StateAp *to, Head *&head, Head *trans ) -{ - trans->ilnext = head; - trans->ilprev = 0; - - /* If in trans list is not empty, set the head->prev to trans. */ - if ( head != 0 ) - head->ilprev = trans; - - /* Now insert ourselves at the front of the list. */ - head = trans; - - /* Keep track of foreign transitions for from and to. */ - if ( from != to ) { - if ( misfitAccounting ) { - /* If the number of foreign in transitions is about to go up to 1 then - * move it from the misfit list to the main list. */ - if ( to->foreignInTrans == 0 ) - stateList.append( misfitList.detach( to ) ); - } - - to->foreignInTrans += 1; - } -}; - -/* Detach a transition from an inlist. The head of the inlist must be supplied. */ -template< class Head > void FsmAp::detachFromInList( StateAp *from, StateAp *to, - Head *&head, Head *trans ) -{ - if ( trans->ilprev == 0 ) - head = trans->ilnext; - else - trans->ilprev->ilnext = trans->ilnext; - - if ( trans->ilnext != 0 ) - trans->ilnext->ilprev = trans->ilprev; - - /* Keep track of foreign transitions for from and to. */ - if ( from != to ) { - to->foreignInTrans -= 1; - - if ( misfitAccounting ) { - /* If the number of foreign in transitions goes down to 0 then move it - * from the main list to the misfit list. */ - if ( to->foreignInTrans == 0 ) - misfitList.append( stateList.detach( to ) ); - } - } -} - -CondAp *FsmAp::attachNewCond( TransAp *trans, StateAp *from, StateAp *to, CondKey onChar ) -{ - /* Sub-transition for conditions. */ - CondAp *condAp = new CondAp( trans ); - condAp->key = onChar; - trans->tcap()->condList.append( condAp ); - - condAp->fromState = from; - condAp->toState = to; - - /* Attach in list. */ - if ( to != 0 ) - attachToInList( from, to, to->inCond.head, condAp ); - - return condAp; -} - -TransAp *FsmAp::attachNewTrans( StateAp *from, StateAp *to, Key lowKey, Key highKey ) -{ - /* Make the new transition. */ - TransDataAp *retVal = new TransDataAp(); - - /* Make the entry in the out list for the transitions. */ - from->outList.append( retVal ); - - /* Set the the keys of the new trans. */ - retVal->lowKey = lowKey; - retVal->highKey = highKey; - - retVal->fromState = from; - retVal->toState = to; - - /* Attach in list. */ - if ( to != 0 ) - attachToInList( from, to, to->inTrans.head, retVal ); - - return retVal; -} - -/* Attach for range lists or for the default transition. This attach should - * be used when a transition already is allocated and must be attached to a - * target state. Does not handle adding the transition into the out list. */ -void FsmAp::attachTrans( StateAp *from, StateAp *to, TransDataAp *trans ) -{ - assert( trans->fromState == 0 && trans->toState == 0 ); - - trans->fromState = from; - trans->toState = to; - - if ( to != 0 ) { - /* For now always attache the one and only condList element. */ - attachToInList( from, to, to->inTrans.head, trans ); - } -} - -void FsmAp::attachTrans( StateAp *from, StateAp *to, CondAp *trans ) -{ - assert( trans->fromState == 0 && trans->toState == 0 ); - - trans->fromState = from; - trans->toState = to; - - if ( to != 0 ) { - /* For now always attache the one and only condList element. */ - attachToInList( from, to, to->inCond.head, trans ); - } -} - -/* Redirect a transition away from error and towards some state. This is just - * like attachTrans except it requires fromState to be set and does not touch - * it. */ -void FsmAp::redirectErrorTrans( StateAp *from, StateAp *to, TransDataAp *trans ) -{ - assert( trans->fromState != 0 && trans->toState == 0 ); - trans->toState = to; - - if ( to != 0 ) { - /* Attach using the inList pointer as the head pointer. */ - attachToInList( from, to, to->inTrans.head, trans ); - } -} - -void FsmAp::redirectErrorTrans( StateAp *from, StateAp *to, CondAp *trans ) -{ - assert( trans->fromState != 0 && trans->toState == 0 ); - trans->toState = to; - - if ( to != 0 ) { - /* Attach using the inList pointer as the head pointer. */ - attachToInList( from, to, to->inCond.head, trans ); - } -} - -/* Detach for out/in lists or for default transition. */ -void FsmAp::detachTrans( StateAp *from, StateAp *to, TransDataAp *trans ) -{ - assert( trans->fromState == from && trans->toState == to ); - - trans->fromState = 0; - trans->toState = 0; - - if ( to != 0 ) { - detachFromInList( from, to, to->inTrans.head, trans ); - } -} - -void FsmAp::detachTrans( StateAp *from, StateAp *to, CondAp *trans ) -{ - assert( trans->fromState == from && trans->toState == to ); - - trans->fromState = 0; - trans->toState = 0; - - if ( to != 0 ) { - detachFromInList( from, to, to->inCond.head, trans ); - } -} - - -/* Detach a state from the graph. Detaches and deletes transitions in and out - * of the state. Empties inList and outList. Removes the state from the final - * state set. A detached state becomes useless and should be deleted. */ -void FsmAp::detachState( StateAp *state ) -{ - while ( state->inTrans.head != 0 ) { - /* Get pointers to the trans and the state. */ - TransDataAp *trans = state->inTrans.head; - - StateAp *fromState = trans->fromState; - - /* Detach the transitions from the source state. */ - detachTrans( fromState, state, trans ); - fromState->outList.detach( trans ); - delete trans->tdap(); - } - - /* Detach the in transitions from the inList list of transitions. */ - while ( state->inCond.head != 0 ) { - /* Get pointers to the trans and the state. */ - CondAp *condAp = state->inCond.head; - TransAp *trans = condAp->transAp; - - StateAp *fromState = condAp->fromState; - - /* Detach the transitions from the source state. */ - detachTrans( fromState, state, condAp ); - - trans->tcap()->condList.detach( condAp ); - delete condAp; - - if ( trans->tcap()->condList.length() == 0 ) { - /* Ok to delete the transition. */ - fromState->outList.detach( trans ); - delete trans->tcap(); - } - } - - /* Remove the entry points in on the machine. */ - while ( state->entryIds.length() > 0 ) - unsetEntry( state->entryIds[0], state ); - - /* Detach out range transitions. */ - for ( TransList::Iter trans = state->outList; trans.lte(); ) { - TransList::Iter next = trans.next(); - if ( trans->plain() ) { - detachTrans( state, trans->tdap()->toState, trans->tdap() ); - delete trans->tdap(); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); ) { - CondList::Iter next = cond.next(); - detachTrans( state, cond->toState, cond ); - delete cond; - cond = next; - } - trans->tcap()->condList.abandon(); - delete trans->tcap(); - } - trans = next; - } - - /* Delete all of the out range pointers. */ - state->outList.abandon(); - - /* Unset final stateness before detaching from graph. */ - if ( state->stateBits & STB_ISFINAL ) - finStateSet.remove( state ); - - if ( state->nfaIn != 0 ) { - while ( state->nfaIn->head != 0 ) { - NfaTrans *trans = state->nfaIn->head; - StateAp *fromState = trans->fromState; - - detachFromNfa( fromState, state, trans ); - fromState->nfaOut->detach( trans ); - delete trans; - } - delete state->nfaIn; - state->nfaIn = 0; - } - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter t = *state->nfaOut; t.lte(); ) { - NfaTransList::Iter next = t.next(); - detachFromNfa( t->fromState, t->toState, t ); - state->nfaOut->detach( t ); - delete t; - t = next; - } - state->nfaOut->abandon(); - delete state->nfaOut; - state->nfaOut = 0; - } - - if ( state->stateDictIn != 0 ) { - for ( StateSet::Iter s = *state->stateDictIn; s.lte(); s++ ) { - bool removed = (*s)->stateDictEl->stateSet.remove( state ); - assert( removed ); - } - - delete state->stateDictIn; - state->stateDictIn = 0; - } - - if ( state->stateDictEl != 0 ) { - for ( StateSet::Iter s = state->stateDictEl->stateSet; s.lte(); s++ ) - detachStateDict( state, *s ); - - stateDict.detach( state->stateDictEl ); - delete state->stateDictEl; - state->stateDictEl = 0; - - nfaList.detach( state ); - } -} - -TransDataAp *FsmAp::dupTransData( StateAp *from, TransDataAp *srcTrans ) -{ - /* Make a new transition. */ - TransDataAp *newTrans = new TransDataAp(); - newTrans->condSpace = srcTrans->condSpace; - - attachTrans( from, srcTrans->tdap()->toState, newTrans ); - addInTrans( newTrans, srcTrans->tdap() ); - - return newTrans; -} - - -/* Duplicate a transition. Makes a new transition that is attached to the same - * dest as srcTrans. The new transition has functions and priority taken from - * srcTrans. Used for merging a transition in to a free spot. The trans can - * just be dropped in. It does not conflict with an existing trans and need - * not be crossed. Returns the new transition. */ -TransAp *FsmAp::dupTrans( StateAp *from, TransAp *srcTrans ) -{ - if ( srcTrans->plain() ) { - /* Make a new transition. */ - TransDataAp *newTrans = new TransDataAp(); - newTrans->condSpace = srcTrans->condSpace; - - attachTrans( from, srcTrans->tdap()->toState, newTrans ); - addInTrans( newTrans, srcTrans->tdap() ); - - return newTrans; - } - else { - /* Make a new transition. */ - TransAp *newTrans = new TransCondAp(); - newTrans->condSpace = srcTrans->condSpace; - - for ( CondList::Iter sc = srcTrans->tcap()->condList; sc.lte(); sc++ ) { - /* Sub-transition for conditions. */ - CondAp *newCond = new CondAp( newTrans ); - newCond->key = sc->key; - newTrans->tcap()->condList.append( newCond ); - - /* We can attach the transition, one does not exist. */ - attachTrans( from, sc->toState, newCond ); - - /* Call the user callback to add in the original source transition. */ - addInTrans( newCond, sc.ptr ); - } - - return newTrans; - } -} - -/* Duplicate a transition. Makes a new transition that is attached to the same - * dest as srcTrans. The new transition has functions and priority taken from - * srcTrans. Used for merging a transition in to a free spot. The trans can - * just be dropped in. It does not conflict with an existing trans and need - * not be crossed. Returns the new transition. */ -CondAp *FsmAp::dupCondTrans( StateAp *from, TransAp *destParent, CondAp *srcTrans ) -{ - /* Sub-transition for conditions. */ - CondAp *newCond = new CondAp( destParent ); - - /* We can attach the transition, one does not exist. */ - attachTrans( from, srcTrans->toState, newCond ); - - /* Call the user callback to add in the original source transition. */ - addInTrans( newCond, srcTrans ); - - return newCond; -} - -/* In crossing, src trans and dest trans both go to existing states. Make one - * state from the sets of states that src and dest trans go to. */ -template< class Trans > Trans *FsmAp::fsmAttachStates( StateAp *from, - Trans *destTrans, Trans *srcTrans ) -{ - /* The priorities are equal. We must merge the transitions. Does the - * existing trans go to the state we are to attach to? ie, are we to - * simply double up the transition? */ - StateAp *toState = srcTrans->toState; - StateAp *existingState = destTrans->toState; - - if ( existingState == toState ) { - /* The transition is a double up to the same state. Copy the src - * trans into itself. We don't need to merge in the from out trans - * data, that was done already. */ - addInTrans( destTrans, srcTrans ); - } - else { - /* The trans is not a double up. Dest trans cannot be the same as src - * trans. Set up the state set. */ - StateSet stateSet; - - /* We go to all the states the existing trans goes to, plus... */ - if ( existingState->stateDictEl == 0 ) - stateSet.insert( existingState ); - else - stateSet.insert( existingState->stateDictEl->stateSet ); - - /* ... all the states that we have been told to go to. */ - if ( toState->stateDictEl == 0 ) - stateSet.insert( toState ); - else - stateSet.insert( toState->stateDictEl->stateSet ); - - /* Look for the state. If it is not there already, make it. */ - StateDictEl *lastFound; - if ( stateDict.insert( stateSet, &lastFound ) ) { - /* Make a new state representing the combination of states in - * stateSet. It gets added to the fill list. This means that we - * need to fill in it's transitions sometime in the future. We - * don't do that now (ie, do not recurse). */ - StateAp *combinState = addState(); - - /* Link up the dict element and the state. */ - lastFound->targState = combinState; - combinState->stateDictEl = lastFound; - - /* Setup the in links. */ - for ( StateSet::Iter s = stateSet; s.lte(); s++ ) - attachStateDict( combinState, *s ); - - /* Add to the fill list. */ - nfaList.append( combinState ); - } - - /* Get the state insertted/deleted. */ - StateAp *targ = lastFound->targState; - - /* Detach the state from existing state. */ - detachTrans( from, existingState, destTrans ); - - /* Re-attach to the new target. */ - attachTrans( from, targ, destTrans ); - - /* Add in src trans to the existing transition that we redirected to - * the new state. We don't need to merge in the from out trans data, - * that was done already. */ - addInTrans( destTrans, srcTrans ); - } - - return destTrans; -} - -/* Two transitions are to be crossed, handle the possibility of either going - * to the error state. */ -template < class Trans > Trans *FsmAp::mergeTrans( StateAp *from, - Trans *destTrans, Trans *srcTrans ) -{ - Trans *retTrans = 0; - if ( destTrans->toState == 0 && srcTrans->toState == 0 ) { - /* Error added into error. */ - addInTrans( destTrans, srcTrans ); - retTrans = destTrans; - } - else if ( destTrans->toState == 0 && srcTrans->toState != 0 ) { - /* Non error added into error we need to detach and reattach, */ - detachTrans( from, destTrans->toState, destTrans ); - attachTrans( from, srcTrans->toState, destTrans ); - addInTrans( destTrans, srcTrans ); - retTrans = destTrans; - } - else if ( srcTrans->toState == 0 ) { - /* Dest goes somewhere but src doesn't, just add it it in. */ - addInTrans( destTrans, srcTrans ); - retTrans = destTrans; - } - else { - /* Both go somewhere, run the actual cross. */ - retTrans = fsmAttachStates( from, destTrans, srcTrans ); - } - - return retTrans; -} - -/* Find the trans with the higher priority. If src is lower priority then dest then - * src is ignored. If src is higher priority than dest, then src overwrites dest. If - * the priorities are equal, then they are merged. */ -CondAp *FsmAp::crossCondTransitions( StateAp *from, TransAp *destParent, - CondAp *destTrans, CondAp *srcTrans ) -{ - CondAp *retTrans; - - /* Compare the priority of the dest and src transitions. */ - int compareRes = comparePrior( destTrans->priorTable, srcTrans->priorTable ); - if ( compareRes < 0 ) { - /* Src trans has a higher priority than dest, src overwrites dest. - * Detach dest and return a copy of src. */ - detachTrans( from, destTrans->toState, destTrans ); - delete destTrans; - retTrans = dupCondTrans( from, destParent, srcTrans ); - } - else if ( compareRes > 0 ) { - /* The dest trans has a higher priority, use dest. */ - retTrans = destTrans; - } - else { - /* Src trans and dest trans have the same priority, they must be merged. */ - retTrans = mergeTrans( from, destTrans, srcTrans ); - } - - /* Return the transition that resulted from the cross. */ - return retTrans; -} - -TransAp *FsmAp::copyTransForExpansion( StateAp *from, TransAp *srcTrans ) -{ - /* This is the dup without the attach. */ - TransCondAp *newTrans = new TransCondAp(); - newTrans->condSpace = srcTrans->condSpace; - - if ( srcTrans->plain() ) { - TransDataAp *srcData = srcTrans->tdap(); - CondAp *newCond = new CondAp( newTrans ); - newCond->key = 0; - - attachTrans( srcData->fromState, srcData->toState, newCond ); - - /* Call the user callback to add in the original source transition. */ - //addInTrans( newCond, srcData ); - - /* Not a copy of ourself, get the functions and priorities. */ - newCond->lmActionTable.setActions( srcData->lmActionTable ); - newCond->actionTable.setActions( srcData->actionTable ); - newCond->priorTable.setPriors( srcData->priorTable ); - - newTrans->condList.append( newCond ); - } - else { - for ( CondList::Iter sc = srcTrans->tcap()->condList; sc.lte(); sc++ ) { - /* Sub-transition for conditions. */ - CondAp *newCond = new CondAp( newTrans ); - newCond->key = sc->key; - - attachTrans( sc->fromState, sc->toState, newCond ); - - /* Call the user callback to add in the original source transition. */ - addInTrans( newCond, sc.ptr ); - - newTrans->condList.append( newCond ); - } - } - - /* Set up the transition's keys and append to the dest list. */ - newTrans->lowKey = srcTrans->lowKey; - newTrans->highKey = srcTrans->highKey; - - return newTrans; -} - -void FsmAp::freeEffectiveTrans( TransAp *trans ) -{ - for ( CondList::Iter sc = trans->tcap()->condList; sc.lte(); ) { - CondList::Iter next = sc.next(); - detachTrans( sc->fromState, sc->toState, sc ); - delete sc; - sc = next; - } - trans->tcap()->condList.abandon(); - delete trans->tcap(); -} - -TransDataAp *FsmAp::crossTransitionsBothPlain( StateAp *from, - TransDataAp *destTrans, TransDataAp *srcTrans ) -{ - /* Neither have cond space and no expansion took place. Cross them. */ - TransDataAp *retTrans; - - /* Compare the priority of the dest and src transitions. */ - int compareRes = comparePrior( destTrans->priorTable, srcTrans->priorTable ); - if ( compareRes < 0 ) { - /* Src trans has a higher priority than dest, src overwrites dest. - * Detach dest and return a copy of src. */ - detachTrans( from, destTrans->toState, destTrans ); - delete destTrans; - retTrans = dupTransData( from, srcTrans ); - } - else if ( compareRes > 0 ) { - /* The dest trans has a higher priority, use dest. */ - retTrans = destTrans; - } - else { - /* Src trans and dest trans have the same priority, they must be merged. */ - retTrans = mergeTrans( from, destTrans, srcTrans ); - } - - /* Return the transition that resulted from the cross. */ - return retTrans; -} - -/* Find the trans with the higher priority. If src is lower priority then dest then - * src is ignored. If src is higher priority than dest, then src overwrites dest. If - * the priorities are equal, then they are merged. */ -TransAp *FsmAp::crossTransitions( StateAp *from, - TransAp *destTrans, TransAp *srcTrans ) -{ - if ( destTrans->plain() && srcTrans->plain() ) { - /* Return the transition that resulted from the cross. */ - return crossTransitionsBothPlain( from, - destTrans->tdap(), srcTrans->tdap() ); - } - else { - /* At least one is non-empty. Target is non-empty. Need to work in - * condition spaced. */ - CondSpace *mergedSpace = expandCondSpace( destTrans, srcTrans ); - - /* If the dest state cond space does not equal the merged, we have to - * rewrite it. If the src state cond space does not equal, we have to - * copy it. */ - - TransAp *effSrcTrans = srcTrans; - - if ( srcTrans->condSpace != mergedSpace ) { - effSrcTrans = copyTransForExpansion( from, srcTrans ); - CondSpace *orig = effSrcTrans->condSpace; - effSrcTrans->condSpace = mergedSpace; - expandConds( from, effSrcTrans, orig, mergedSpace ); - } - - if ( destTrans->condSpace != mergedSpace ) { - /* Make the transition into a conds transition. If dest is a plain - * transition, we have to replace it with a conds transition. */ - if ( destTrans->plain() ) - destTrans = convertToCondAp( from, destTrans->tdap() ); - - /* Now expand the dest. */ - CondSpace *orig = destTrans->condSpace; - destTrans->condSpace = mergedSpace; - expandConds( from, destTrans, orig, mergedSpace ); - } - - /* The destination list. */ - CondList destList; - - /* Set up an iterator to stop at breaks. */ - typedef ValPairIter< PiList > ValPairIterPiListCondAp; - ValPairIterPiListCondAp outPair( destTrans->tcap()->condList, - effSrcTrans->tcap()->condList ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - case ValPairIterPiListCondAp::RangeInS1: { - /* The pair iter is the authority on the keys. It may have needed - * to break the dest range. */ - CondAp *destCond = outPair.s1Tel.trans; - destCond->key = outPair.s1Tel.key; - destList.append( destCond ); - break; - } - case ValPairIterPiListCondAp::RangeInS2: { - /* Src range may get crossed with dest's default transition. */ - CondAp *newCond = dupCondTrans( from, destTrans, outPair.s2Tel.trans ); - - /* Set up the transition's keys and append to the dest list. */ - newCond->key = outPair.s2Tel.key; - destList.append( newCond ); - break; - } - case ValPairIterPiListCondAp::RangeOverlap: { - /* Exact overlap, cross them. */ - CondAp *newTrans = crossCondTransitions( from, destTrans, - outPair.s1Tel.trans, outPair.s2Tel.trans ); - - /* Set up the transition's keys and append to the dest list. */ - newTrans->key = outPair.s1Tel.key; - destList.append( newTrans ); - break; - }} - } - - /* Abandon the old outList and transfer destList into it. */ - destTrans->tcap()->condList.transfer( destList ); - - /* Delete the duplicate. Don't detach anything. */ - if ( srcTrans != effSrcTrans ) - freeEffectiveTrans( effSrcTrans ); - - return destTrans; - } -} - -/* Copy the transitions in srcList to the outlist of dest. The srcList should - * not be the outList of dest, otherwise you would be copying the contents of - * srcList into itself as it's iterated: bad news. */ -void FsmAp::outTransCopy( StateAp *dest, TransAp *srcList ) -{ - /* The destination list. */ - TransList destList; - - /* Set up an iterator to stop at breaks. */ - typedef RangePairIter< PiList > RangePairIterPiListTransAp; - RangePairIterPiListTransAp outPair( ctx, dest->outList, srcList ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - case RangePairIterPiListTransAp::RangeInS1: { - /* The pair iter is the authority on the keys. It may have needed - * to break the dest range. */ - TransAp *destTrans = outPair.s1Tel.trans; - destTrans->lowKey = outPair.s1Tel.lowKey; - destTrans->highKey = outPair.s1Tel.highKey; - destList.append( destTrans ); - break; - } - case RangePairIterPiListTransAp::RangeInS2: { - /* Src range may get crossed with dest's default transition. */ - TransAp *newTrans = dupTrans( dest, outPair.s2Tel.trans ); - - /* Set up the transition's keys and append to the dest list. */ - newTrans->lowKey = outPair.s2Tel.lowKey; - newTrans->highKey = outPair.s2Tel.highKey; - destList.append( newTrans ); - break; - } - case RangePairIterPiListTransAp::RangeOverlap: { - /* Exact overlap, cross them. */ - TransAp *newTrans = crossTransitions( dest, - outPair.s1Tel.trans, outPair.s2Tel.trans ); - - /* Set up the transition's keys and append to the dest list. */ - newTrans->lowKey = outPair.s1Tel.lowKey; - newTrans->highKey = outPair.s1Tel.highKey; - destList.append( newTrans ); - break; - } - case RangePairIterPiListTransAp::BreakS1: { - /* Since we are always writing to the dest trans, the dest needs - * to be copied when it is broken. The copy goes into the first - * half of the break to "break it off". */ - outPair.s1Tel.trans = dupTrans( dest, outPair.s1Tel.trans ); - break; - } - case RangePairIterPiListTransAp::BreakS2: - break; - } - } - - /* Abandon the old outList and transfer destList into it. */ - dest->outList.transfer( destList ); -} - -/* Move all the transitions that go into src so that they go into dest. */ -void FsmAp::moveInwardTrans( StateAp *dest, StateAp *src ) -{ - /* Do not try to move in trans to and from the same state. */ - assert( dest != src ); - - /* If src is the start state, dest becomes the start state. */ - if ( src == startState ) { - unsetStartState(); - setStartState( dest ); - } - - /* For each entry point into, create an entry point into dest, when the - * state is detached, the entry points to src will be removed. */ - for ( EntryIdSet::Iter enId = src->entryIds; enId.lte(); enId++ ) - changeEntry( *enId, dest, src ); - - /* Move the transitions in inList. */ - while ( src->inTrans.head != 0 ) { - /* Get trans and from state. */ - TransDataAp *trans = src->inTrans.head; - StateAp *fromState = trans->fromState; - - /* Detach from src, reattach to dest. */ - detachTrans( fromState, src, trans ); - attachTrans( fromState, dest, trans ); - } - - /* Move the transitions in inList. */ - while ( src->inCond.head != 0 ) { - /* Get trans and from state. */ - CondAp *trans = src->inCond.head; - StateAp *fromState = trans->fromState; - - /* Detach from src, reattach to dest. */ - detachTrans( fromState, src, trans ); - attachTrans( fromState, dest, trans ); - } - - /* Move inward nfa links. */ - if ( src->nfaIn != 0 ) { - while ( src->nfaIn->head != 0 ) { - NfaTrans *trans = src->nfaIn->head; - StateAp *fromState = trans->fromState; - - detachFromNfa( fromState, src, trans ); - attachToNfa( fromState, dest, trans ); - } - } -} diff --git a/libfsm/fsmbase.cc b/libfsm/fsmbase.cc deleted file mode 100644 index cc2c8757..00000000 --- a/libfsm/fsmbase.cc +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "fsmgraph.h" -#include "parsedata.h" -#include "action.h" - -#include -#include -#include - -FsmCtx::FsmCtx( FsmGbl *fsmGbl ) -: - minimizeLevel(fsmGbl->minimizeLevel), - minimizeOpt(fsmGbl->minimizeOpt), - - /* No limit. */ - stateLimit(STATE_UNLIMITED), - - printStatistics(fsmGbl->printStatistics), - - checkPriorInteraction(fsmGbl->checkPriorInteraction), - - unionOp(false), - - condsCheckDepth(0), - - curActionOrd(0), - curPriorOrd(0), - - nextPriorKey(0), - nextCondId(0), - - fsmGbl(fsmGbl), - generatingSectionSubset(false), - lmRequiresErrorState(false), - nameIndex(0), - - getKeyExpr(0), - accessExpr(0), - prePushExpr(0), - postPopExpr(0), - nfaPrePushExpr(0), - nfaPostPopExpr(0), - pExpr(0), - peExpr(0), - eofExpr(0), - csExpr(0), - topExpr(0), - stackExpr(0), - actExpr(0), - tokstartExpr(0), - tokendExpr(0), - dataExpr(0) -{ - keyOps = new KeyOps; - condData = new CondData; -} - -FsmCtx::~FsmCtx() -{ - delete keyOps; - delete condData; - priorDescList.empty(); - - actionList.empty(); - - if ( getKeyExpr != 0 ) - delete getKeyExpr; - if ( accessExpr != 0 ) - delete accessExpr; - if ( prePushExpr != 0 ) - delete prePushExpr; - if ( postPopExpr != 0 ) - delete postPopExpr; - if ( nfaPrePushExpr != 0 ) - delete nfaPrePushExpr; - if ( nfaPostPopExpr != 0 ) - delete nfaPostPopExpr; - if ( pExpr != 0 ) - delete pExpr; - if ( peExpr != 0 ) - delete peExpr; - if ( eofExpr != 0 ) - delete eofExpr; - if ( csExpr != 0 ) - delete csExpr; - if ( topExpr != 0 ) - delete topExpr; - if ( stackExpr != 0 ) - delete stackExpr; - if ( actExpr != 0 ) - delete actExpr; - if ( tokstartExpr != 0 ) - delete tokstartExpr; - if ( tokendExpr != 0 ) - delete tokendExpr; - if ( dataExpr != 0 ) - delete dataExpr; -} - -/* Graph constructor. */ -FsmAp::FsmAp( FsmCtx *ctx ) -: - ctx( ctx ), - - priorInteraction(false), - - /* No start state. */ - startState(0), - errState(0), - - /* Misfit accounting is a switch, turned on only at specific times. It - * controls what happens when states have no way in from the outside - * world.. */ - misfitAccounting(false) -{ -} - -/* Copy all graph data including transitions. */ -FsmAp::FsmAp( const FsmAp &graph ) -: - ctx( graph.ctx ), - - priorInteraction(false), - - /* Lists start empty. Will be filled by copy. */ - stateList(), - misfitList(), - - /* Copy in the entry points, - * pointers will be resolved later. */ - entryPoints(graph.entryPoints), - startState(graph.startState), - errState(0), - - /* Will be filled by copy. */ - finStateSet(), - - /* Misfit accounting is only on during merging. */ - misfitAccounting(false) -{ - /* Create the states and record their map in the original state. */ - StateList::Iter origState = graph.stateList; - for ( ; origState.lte(); origState++ ) { - /* Make the new state. */ - StateAp *newState = new StateAp( *origState ); - - /* Add the state to the list. */ - stateList.append( newState ); - - /* Set the mapsTo item of the old state. */ - origState->alg.stateMap = newState; - } - - /* Derefernce all the state maps. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - /* The points to the original in the src machine. The taget's duplicate - * is in the statemap. */ - StateAp *toState = trans->tdap()->toState != 0 ? - trans->tdap()->toState->alg.stateMap : 0; - - /* Attach The transition to the duplicate. */ - trans->tdap()->toState = 0; - attachTrans( state, toState, trans->tdap() ); - - } - else { - for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { - /* The points to the original in the src machine. The taget's duplicate - * is in the statemap. */ - StateAp *toState = cti->toState != 0 ? cti->toState->alg.stateMap : 0; - - /* Attach The transition to the duplicate. */ - cti->toState = 0; - attachTrans( state, toState, cti ); - } - } - } - - /* Fix the eofTarg, if set. */ - if ( state->eofTarget != 0 ) - state->eofTarget = state->eofTarget->alg.stateMap; - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter n = *state->nfaOut; n.lte(); n++ ) { - StateAp *targ = n->toState->alg.stateMap; - n->toState = 0; - attachToNfa( state, targ, n ); - } - } - } - - /* Fix the state pointers in the entry points array. */ - EntryMapEl *eel = entryPoints.data; - for ( int e = 0; e < entryPoints.length(); e++, eel++ ) { - /* Get the duplicate of the state. */ - eel->value = eel->value->alg.stateMap; - - /* Foreign in transitions must be built up when duping machines so - * increment it here. */ - eel->value->foreignInTrans += 1; - } - - /* Fix the start state pointer and the new start state's count of in - * transiions. */ - startState = startState->alg.stateMap; - startState->foreignInTrans += 1; - - /* Build the final state set. */ - StateSet::Iter st = graph.finStateSet; - for ( ; st.lte(); st++ ) - finStateSet.insert((*st)->alg.stateMap); -} - -/* Deletes all transition data then deletes each state. */ -FsmAp::~FsmAp() -{ - /* Delete all the transitions. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Iterate the out transitions, deleting them. */ - for ( TransList::Iter n, t = state->outList; t.lte(); ) { - n = t.next(); - if ( t->plain() ) - delete t->tdap(); - else - delete t->tcap(); - t = n; - } - state->outList.abandon(); - - if ( state->nfaIn != 0 ) { - delete state->nfaIn; - state->nfaIn = 0; - } - - if ( state->nfaOut != 0 ) { - state->nfaOut->empty(); - delete state->nfaOut; - state->nfaOut = 0; - } - } - - /* Delete all the states. */ - stateList.empty(); -} - -/* Set a state final. The state has its isFinState set to true and the state - * is added to the finStateSet. */ -void FsmAp::setFinState( StateAp *state ) -{ - /* Is it already a fin state. */ - if ( state->stateBits & STB_ISFINAL ) - return; - - state->stateBits |= STB_ISFINAL; - finStateSet.insert( state ); -} - -/* Set a state non-final. The has its isFinState flag set false and the state - * is removed from the final state set. */ -void FsmAp::unsetFinState( StateAp *state ) -{ - /* Is it already a non-final state? */ - if ( ! (state->stateBits & STB_ISFINAL) ) - return; - - /* When a state looses its final state status it must relinquish all the - * properties that are allowed only for final states. */ - clearOutData( state ); - - state->stateBits &= ~ STB_ISFINAL; - finStateSet.remove( state ); -} - -/* Set and unset a state as the start state. */ -void FsmAp::setStartState( StateAp *state ) -{ - /* Sould change from unset to set. */ - assert( startState == 0 ); - startState = state; - - if ( misfitAccounting ) { - /* If the number of foreign in transitions is about to go up to 1 then - * take it off the misfit list and put it on the head list. */ - if ( state->foreignInTrans == 0 ) - stateList.append( misfitList.detach( state ) ); - } - - /* Up the foreign in transitions to the state. */ - state->foreignInTrans += 1; -} - -void FsmAp::unsetStartState() -{ - /* Should change from set to unset. */ - assert( startState != 0 ); - - /* Decrement the entry's count of foreign entries. */ - startState->foreignInTrans -= 1; - - if ( misfitAccounting ) { - /* If the number of foreign in transitions just went down to 0 then take - * it off the main list and put it on the misfit list. */ - if ( startState->foreignInTrans == 0 ) - misfitList.append( stateList.detach( startState ) ); - } - - startState = 0; -} - -/* Associate an id with a state. Makes the state a named entry point. Has no - * effect if the entry point is already mapped to the state. */ -void FsmAp::setEntry( int id, StateAp *state ) -{ - /* Insert the id into the state. If the state is already labelled with id, - * nothing to do. */ - if ( state->entryIds.insert( id ) ) { - /* Insert the entry and assert that it succeeds. */ - entryPoints.insertMulti( id, state ); - - if ( misfitAccounting ) { - /* If the number of foreign in transitions is about to go up to 1 then - * take it off the misfit list and put it on the head list. */ - if ( state->foreignInTrans == 0 ) - stateList.append( misfitList.detach( state ) ); - } - - /* Up the foreign in transitions to the state. */ - state->foreignInTrans += 1; - } -} - -/* Remove the association of an id with a state. The state looses it's entry - * point status. Assumes that the id is indeed mapped to state. */ -void FsmAp::unsetEntry( int id, StateAp *state ) -{ - /* Find the entry point in on id. */ - EntryMapEl *enLow = 0, *enHigh = 0; - entryPoints.findMulti( id, enLow, enHigh ); - while ( enLow->value != state ) - enLow += 1; - - /* Remove the record from the map. */ - entryPoints.remove( enLow ); - - /* Remove the state's sense of the link. */ - state->entryIds.remove( id ); - state->foreignInTrans -= 1; - if ( misfitAccounting ) { - /* If the number of foreign in transitions just went down to 0 then take - * it off the main list and put it on the misfit list. */ - if ( state->foreignInTrans == 0 ) - misfitList.append( stateList.detach( state ) ); - } -} - -/* Remove all association of an id with states. Assumes that the id is indeed - * mapped to a state. */ -void FsmAp::unsetEntry( int id ) -{ - /* Find the entry point in on id. */ - EntryMapEl *enLow = 0, *enHigh = 0; - entryPoints.findMulti( id, enLow, enHigh ); - for ( EntryMapEl *mel = enLow; mel <= enHigh; mel++ ) { - /* Remove the state's sense of the link. */ - mel->value->entryIds.remove( id ); - mel->value->foreignInTrans -= 1; - if ( misfitAccounting ) { - /* If the number of foreign in transitions just went down to 0 - * then take it off the main list and put it on the misfit list. */ - if ( mel->value->foreignInTrans == 0 ) - misfitList.append( stateList.detach( mel->value ) ); - } - } - - /* Remove the records from the entry points map. */ - entryPoints.removeMulti( enLow, enHigh ); -} - - -void FsmAp::changeEntry( int id, StateAp *to, StateAp *from ) -{ - /* Find the entry in the entry map. */ - EntryMapEl *enLow = 0, *enHigh = 0; - entryPoints.findMulti( id, enLow, enHigh ); - while ( enLow->value != from ) - enLow += 1; - - /* Change it to the new target. */ - enLow->value = to; - - /* Remove from's sense of the link. */ - from->entryIds.remove( id ); - from->foreignInTrans -= 1; - if ( misfitAccounting ) { - /* If the number of foreign in transitions just went down to 0 then take - * it off the main list and put it on the misfit list. */ - if ( from->foreignInTrans == 0 ) - misfitList.append( stateList.detach( from ) ); - } - - /* Add to's sense of the link. */ - if ( to->entryIds.insert( id ) != 0 ) { - if ( misfitAccounting ) { - /* If the number of foreign in transitions is about to go up to 1 then - * take it off the misfit list and put it on the head list. */ - if ( to->foreignInTrans == 0 ) - stateList.append( misfitList.detach( to ) ); - } - - /* Up the foreign in transitions to the state. */ - to->foreignInTrans += 1; - } -} - - -/* Clear all entry points from a machine. */ -void FsmAp::unsetAllEntryPoints() -{ - for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) { - /* Kill all the state's entry points at once. */ - if ( en->value->entryIds.length() > 0 ) { - en->value->foreignInTrans -= en->value->entryIds.length(); - - if ( misfitAccounting ) { - /* If the number of foreign in transitions just went down to 0 - * then take it off the main list and put it on the misfit - * list. */ - if ( en->value->foreignInTrans == 0 ) - misfitList.append( stateList.detach( en->value ) ); - } - - /* Clear the set of ids out all at once. */ - en->value->entryIds.empty(); - } - } - - /* Now clear out the entry map all at once. */ - entryPoints.empty(); -} - -/* Assigning an epsilon transition into final states. */ -void FsmAp::epsilonTrans( int id ) -{ - for ( StateSet::Iter fs = finStateSet; fs.lte(); fs++ ) - (*fs)->epsilonTrans.append( id ); -} - -/* Mark all states reachable from state. Traverses transitions forward. Used - * for removing states that have no path into them. */ -void FsmAp::markReachableFromHere( StateAp *state ) -{ - /* Base case: return; */ - if ( state->stateBits & STB_ISMARKED ) - return; - - /* Set this state as processed. We are going to visit all states that this - * state has a transition to. */ - state->stateBits |= STB_ISMARKED; - - /* Recurse on all out transitions. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - markReachableFromHere( trans->tdap()->toState ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - markReachableFromHere( cond->toState ); - } - } - } - - /* Recurse on all states that compose us. */ - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter st = *state->nfaOut; st.lte(); st++ ) - markReachableFromHere( st->toState ); - } - - if ( state->stateDictEl != 0 ) { - for ( StateSet::Iter ss = state->stateDictEl->stateSet; ss.lte(); ss++ ) - markReachableFromHere( *ss ); - } -} - -/* Any transitions to another state? */ -bool FsmAp::anyRegularTransitions( StateAp *state ) -{ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - StateAp *toState = trans->tdap()->toState; - if ( toState != 0 ) - return true; - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - StateAp *toState = cond->toState; - if ( toState != 0 ) - return true; - } - } - } - return false; -} - -void FsmAp::markReachableFromHereStopFinal( StateAp *state ) -{ - /* Base case: return; */ - if ( state->stateBits & STB_ISMARKED ) - return; - - /* Set this state as processed. We are going to visit all states that this - * state has a transition to. */ - state->stateBits |= STB_ISMARKED; - - /* Recurse on all out transitions. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - StateAp *toState = trans->tdap()->toState; - if ( toState != 0 && !toState->isFinState() ) - markReachableFromHereStopFinal( toState ); - - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - StateAp *toState = cond->toState; - if ( toState != 0 && !toState->isFinState() ) - markReachableFromHereStopFinal( toState ); - } - } - } - - /* Recurse on all states that compose us. */ - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter st = *state->nfaOut; st.lte(); st++ ) - markReachableFromHereStopFinal( st->toState ); - } - - if ( state->stateDictEl != 0 ) { - for ( StateSet::Iter ss = state->stateDictEl->stateSet; ss.lte(); ss++ ) - markReachableFromHereStopFinal( *ss ); - } -} - -/* Mark all states reachable from state. Traverse transitions backwards. Used - * for removing dead end paths in graphs. */ -void FsmAp::markReachableFromHereReverse( StateAp *state ) -{ - /* Base case: return; */ - if ( state->stateBits & STB_ISMARKED ) - return; - - /* Set this state as processed. We are going to visit all states with - * transitions into this state. */ - state->stateBits |= STB_ISMARKED; - - /* Recurse on all items in transitions. */ - for ( TransInList::Iter t = state->inTrans; t.lte(); t++ ) - markReachableFromHereReverse( t->fromState ); - for ( CondInList::Iter t = state->inCond; t.lte(); t++ ) - markReachableFromHereReverse( t->fromState ); -} - -/* Determine if there are any entry points into a start state other than the - * start state. Setting starting transitions requires that the start state be - * isolated. In most cases a start state will already be isolated. */ -bool FsmAp::isStartStateIsolated() -{ - /* If there are any in transitions then the state is not isolated. */ - if ( startState->inTrans.head != 0 ) - return false; - if ( startState->inCond.head != 0 ) - return false; - - /* If there are any entry points then isolated. */ - if ( startState->entryIds.length() > 0 ) - return false; - - return true; -} - -/* Bring in other's entry points. Assumes others states are going to be - * copied into this machine. */ -void FsmAp::copyInEntryPoints( FsmAp *other ) -{ - /* Use insert multi because names are not unique. */ - for ( EntryMap::Iter en = other->entryPoints; en.lte(); en++ ) - entryPoints.insertMulti( en->key, en->value ); -} - - -void FsmAp::unsetAllFinStates() -{ - for ( StateSet::Iter st = finStateSet; st.lte(); st++ ) - (*st)->stateBits &= ~ STB_ISFINAL; - finStateSet.empty(); -} - -void FsmAp::setFinBits( int finStateBits ) -{ - for ( int s = 0; s < finStateSet.length(); s++ ) - finStateSet.data[s]->stateBits |= finStateBits; -} - -void FsmAp::unsetFinBits( int finStateBits ) -{ - for ( int s = 0; s < finStateSet.length(); s++ ) - finStateSet.data[s]->stateBits &= ~ finStateBits; -} - - -/* Tests the integrity of the transition lists and the fromStates. */ -void FsmAp::verifyIntegrity() -{ - int count = 0; - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Walk the out transitions and assert fromState is correct. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - assert( trans->tdap()->fromState == state ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - assert( cond->fromState == state ); - } - } - } - - /* Walk the inlist and assert toState is correct. */ - for ( TransInList::Iter t = state->inTrans; t.lte(); t++ ) { - assert( t->toState == state ); - } - for ( CondInList::Iter t = state->inCond; t.lte(); t++ ) { - assert( t->toState == state ); - } - - count += 1; - } - - assert( stateList.length() == count ); -} - -void FsmAp::verifyReachability() -{ - /* Mark all the states that can be reached - * through the set of entry points. */ - markReachableFromHere( startState ); - for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) - markReachableFromHere( en->value ); - - /* Check that everything got marked. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - /* Assert it got marked and then clear the mark. */ - assert( st->stateBits & STB_ISMARKED ); - st->stateBits &= ~ STB_ISMARKED; - } -} - -void FsmAp::verifyNoDeadEndStates() -{ - /* Mark all states that have paths to the final states. */ - for ( StateSet::Iter pst = finStateSet; pst.lte(); pst++ ) - markReachableFromHereReverse( *pst ); - - /* Start state gets honorary marking. Must be done AFTER recursive call. */ - startState->stateBits |= STB_ISMARKED; - - /* Make sure everything got marked. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - /* Assert the state got marked and unmark it. */ - assert( st->stateBits & STB_ISMARKED ); - st->stateBits &= ~ STB_ISMARKED; - } -} - -void FsmAp::depthFirstOrdering( StateAp *state ) -{ - /* Nothing to do if the state is already on the list. */ - if ( state->stateBits & STB_ONLIST ) - return; - - /* Doing depth first, put state on the list. */ - state->stateBits |= STB_ONLIST; - stateList.append( state ); - - /* Recurse on everything ranges. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) - depthFirstOrdering( trans->tdap()->toState ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) - depthFirstOrdering( cond->toState ); - } - } - } - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter s = *state->nfaOut; s.lte(); s++ ) - depthFirstOrdering( s->toState ); - } -} - -/* Ordering states by transition connections. */ -void FsmAp::depthFirstOrdering() -{ - /* Init on state list flags. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) - st->stateBits &= ~STB_ONLIST; - - /* Clear out the state list, we will rebuild it. */ - int stateListLen = stateList.length(); - stateList.abandon(); - - /* Add back to the state list from the start state and all other entry - * points. */ - if ( errState != 0 ) - depthFirstOrdering( errState ); - - depthFirstOrdering( startState ); - for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) - depthFirstOrdering( en->value ); - - /* Make sure we put everything back on. */ - assert( stateListLen == stateList.length() ); -} - -/* Stable sort the states by final state status. */ -void FsmAp::sortStatesByFinal() -{ - /* Move forward through the list and move final states onto the end. */ - StateAp *state = 0; - StateAp *next = stateList.head; - StateAp *last = stateList.tail; - while ( state != last ) { - /* Move forward and load up the next. */ - state = next; - next = state->next; - - /* Throw to the end? */ - if ( state->isFinState() ) { - stateList.detach( state ); - stateList.append( state ); - } - } -} - -void FsmAp::setStateNumbers( int base ) -{ - for ( StateList::Iter state = stateList; state.lte(); state++ ) - state->alg.stateNum = base++; -} - -bool FsmAp::checkErrTrans( StateAp *state, CondAp *trans ) -{ - /* Might go directly to error state. */ - if ( trans->toState == 0 ) - return true; - - return false; -} - -bool FsmAp::checkErrTrans( StateAp *state, TransAp *trans ) -{ - /* - * Look for a gap between this transition and the previous. - */ - if ( trans->prev == 0 ) { - /* If this is the first transition. */ - if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) - return true; - } - else { - /* Not the first transition. Compare against the prev. */ - TransAp *prev = trans->prev; - Key nextKey = prev->highKey; - ctx->keyOps->increment( nextKey ); - if ( ctx->keyOps->lt( nextKey, trans->lowKey ) ) - return true; - } - - if ( trans->plain() ) { - if ( trans->tdap()->toState == 0 ) - return true; - } - else { - /* Check for gaps in the condition list. */ - if ( trans->tcap()->condList.length() < trans->condFullSize() ) - return true; - - /* Check all destinations. */ - for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { - if ( checkErrTrans( state, cti ) ) - return true; - } - } - - return false; -} - -bool FsmAp::checkErrTransFinish( StateAp *state ) -{ - /* Check if there are any ranges already. */ - if ( state->outList.length() == 0 ) - return true; - else { - /* Get the last and check for a gap on the end. */ - TransAp *last = state->outList.tail; - if ( ctx->keyOps->lt( last->highKey, ctx->keyOps->maxKey ) ) - return true; - } - return 0; -} - -bool FsmAp::hasErrorTrans() -{ - bool result; - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - for ( TransList::Iter tr = st->outList; tr.lte(); tr++ ) { - result = checkErrTrans( st, tr ); - if ( result ) - return true; - } - result = checkErrTransFinish( st ); - if ( result ) - return true; - } - return false; -} diff --git a/libfsm/fsmcond.cc b/libfsm/fsmcond.cc deleted file mode 100644 index b2339c12..00000000 --- a/libfsm/fsmcond.cc +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Setting conditions and merging states with conditions are similar activities - * when expressed in code. The critical difference is that a merge is a union - * of multiple paths. We have to take both paths. Setting a condition, however, - * is a restriction. We have to expand the transition to follow both values of - * the condition, then remove the one that is not set. - */ - -#include "fsmgraph.h" -#include "mergesort.h" -#include "parsedata.h" - -#include -#include - -long TransAp::condFullSize() - { return condSpace == 0 ? 1 : condSpace->fullSize(); } - -void FsmAp::expandCondKeys( CondKeySet &condKeys, CondSpace *fromSpace, - CondSpace *mergedSpace ) -{ - CondSet fromCS, mergedCS; - - if ( fromSpace != 0 ) - fromCS.insert( fromSpace->condSet ); - - if ( mergedSpace != 0 ) - mergedCS.insert( mergedSpace->condSet ); - - /* Need to transform condition element to the merged set. */ - for ( int cti = 0; cti < condKeys.length(); cti++ ) { - long origVal = condKeys[cti]; - long newVal = 0; - - /* Iterate the bit positions in the from set. */ - for ( CondSet::Iter csi = fromCS; csi.lte(); csi++ ) { - /* If set, find it in the merged set and flip the bit to 1. */ - if ( origVal & (1 << csi.pos()) ) { - /* The condition is set. Find the bit position in the merged - * set. */ - Action **cim = mergedCS.find( *csi ); - long bitPos = (cim - mergedCS.data); - newVal |= 1 << bitPos; - } - } - - if ( origVal != newVal ) - condKeys[cti] = newVal; - } - - /* Need to double up the whole transition list for each condition test in - * merged that is not in from. The one we add has the bit in question set. - * */ - for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { - Action **cim = fromCS.find( *csi ); - if ( cim == 0 ) { - CondKeySet newItems; - newItems.append( condKeys ); - for ( int cti = 0; cti < condKeys.length(); cti++ ) { - int key = condKeys[cti] | (1 << csi.pos()); - newItems.insert( key ); - } - - condKeys.setAs( newItems ); - } - } -} - -void FsmAp::expandConds( StateAp *fromState, TransAp *trans, - CondSpace *fromSpace, CondSpace *mergedSpace ) -{ - CondSet fromCS, mergedCS; - - if ( fromSpace != 0 ) - fromCS.insert( fromSpace->condSet ); - - if ( mergedSpace != 0 ) - mergedCS.insert( mergedSpace->condSet ); - - /* Need to transform condition element to the merged set. */ - for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { - long origVal = cti->key.getVal(); - long newVal = 0; - - /* Iterate the bit positions in the from set. */ - for ( CondSet::Iter csi = fromCS; csi.lte(); csi++ ) { - /* If set, find it in the merged set and flip the bit to 1. */ - if ( origVal & (1 << csi.pos()) ) { - /* The condition is set. Find the bit position in the merged - * set. */ - Action **cim = mergedCS.find( *csi ); - long bitPos = (cim - mergedCS.data); - newVal |= 1 << bitPos; - } - } - - if ( origVal != newVal ) - cti->key = newVal; - } - - /* Need to double up the whole transition list for each condition test in - * merged that is not in from. The one we add has the bit in question set. - * */ - for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { - Action **cim = fromCS.find( *csi ); - if ( cim == 0 ) { - CondList newItems; - for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { - /* Sub-transition for conditions. */ - CondAp *cond = new CondAp( trans ); - - /* Attach only if our caller wants the expanded transitions - * attached. */ - attachTrans( fromState, cti->toState, cond ); - - /* Call the user callback to add in the original source transition. */ - addInTrans( cond, cti.ptr ); - - cond->key = cti->key.getVal() | (1 << csi.pos()); - - newItems.append( cond ); - } - - /* Merge newItems in. Both the condList and newItems are sorted. Make - * a sorted list out of them. */ - CondAp *dest = trans->tcap()->condList.head; - while ( dest != 0 && newItems.head != 0 ) { - if ( newItems.head->key.getVal() > dest->key.getVal() ) { - dest = dest->next; - } - else { - /* Pop the item for insertion. */ - CondAp *ins = newItems.detachFirst(); - trans->tcap()->condList.addBefore( dest, ins ); - } - } - - /* Append the rest of the items. */ - trans->tcap()->condList.append( newItems ); - } - } -} - -CondSpace *FsmAp::expandCondSpace( TransAp *destTrans, TransAp *srcTrans ) -{ - CondSet destCS, srcCS; - CondSet mergedCS; - - if ( destTrans->condSpace != 0 ) - destCS.insert( destTrans->condSpace->condSet ); - - if ( srcTrans->condSpace != 0 ) - srcCS.insert( srcTrans->condSpace->condSet ); - - mergedCS.insert( destCS ); - mergedCS.insert( srcCS ); - - return addCondSpace( mergedCS ); -} - -StateAp *FsmAp::copyStateForExpansion( StateAp *srcState ) -{ - StateAp *newState = new StateAp(); - newState->outCondSpace = srcState->outCondSpace; - newState->outCondKeys = srcState->outCondKeys; - return newState; -} - -void FsmAp::mergeOutConds( StateAp *destState, StateAp *srcState, bool leaving ) -{ - if ( destState == srcState ) - return; - - bool bothFinal = destState->isFinState() && srcState->isFinState(); - bool unionOp = !leaving; - - CondSet destCS, srcCS; - CondSet mergedCS; - - if ( destState->outCondSpace != 0 ) - destCS.insert( destState->outCondSpace->condSet ); - - if ( srcState->outCondSpace != 0 ) - srcCS.insert( srcState->outCondSpace->condSet ); - - mergedCS.insert( destCS ); - mergedCS.insert( srcCS ); - - if ( mergedCS.length() > 0 ) { - CondSpace *mergedSpace = addCondSpace( mergedCS ); - - CondSpace *srcSpace = srcState->outCondSpace; - CondKeySet srcKeys = srcState->outCondKeys; - - if ( srcSpace != mergedSpace ) { - /* Prep the key list with zero item if necessary. */ - if ( srcSpace == 0 ) - srcKeys.append( 0 ); - - expandCondKeys( srcKeys, srcSpace, mergedSpace ); - } - - if ( destState->outCondSpace != mergedSpace ) { - /* Prep the key list with zero item if necessary. */ - if ( destState->outCondSpace == 0 ) - destState->outCondKeys.append( 0 ); - - /* Now expand the dest. */ - expandCondKeys( destState->outCondKeys, destState->outCondSpace, mergedSpace ); - } - - destState->outCondSpace = mergedSpace; - - if ( unionOp && bothFinal ) { - /* Keys can come from either. */ - for ( CondKeySet::Iter c = srcKeys; c.lte(); c++ ) - destState->outCondKeys.insert( *c ); - } - else { - /* Keys need to be in both sets. */ - for ( long c = 0; c < destState->outCondKeys.length(); ) { - if ( !srcKeys.find( destState->outCondKeys[c] ) ) - destState->outCondKeys.CondKeyVect::remove( c, 1 ); - else - c++; - } - } - } -} - -CondSpace *FsmAp::addCondSpace( const CondSet &condSet ) -{ - CondSpace *condSpace = ctx->condData->condSpaceMap.find( condSet ); - if ( condSpace == 0 ) { - condSpace = new CondSpace( condSet ); - ctx->condData->condSpaceMap.insert( condSpace ); - } - return condSpace; -} - -TransDataAp *FsmAp::convertToTransAp( StateAp *from, CondAp *cond ) -{ - TransDataAp *newTrans = new TransDataAp(); - newTrans->lowKey = cond->transAp->lowKey; - newTrans->highKey = cond->transAp->highKey; - - newTrans->lmActionTable.setActions( cond->lmActionTable ); - newTrans->actionTable.setActions( cond->actionTable ); - newTrans->priorTable.setPriors( cond->priorTable ); - - attachTrans( from, cond->toState, newTrans ); - - /* Detach in list. */ - detachTrans( from, cond->toState, cond ); - delete cond->transAp; - delete cond; - - return newTrans; -} - -TransCondAp *FsmAp::convertToCondAp( StateAp *from, TransDataAp *trans ) -{ - TransCondAp *newTrans = new TransCondAp(); - newTrans->lowKey = trans->lowKey; - newTrans->highKey = trans->highKey; - newTrans->condSpace = trans->condSpace; - - CondAp *newCond = new CondAp( newTrans ); - newCond->key = 0; - newTrans->condList.append( newCond ); - - newCond->lmActionTable.setActions( trans->lmActionTable ); - newCond->actionTable.setActions( trans->actionTable ); - newCond->priorTable.setPriors( trans->priorTable ); - - attachTrans( from, trans->toState, newCond ); - - /* Detach in list. */ - detachTrans( from, trans->toState, trans ); - delete trans; - - return newTrans; -} - -void FsmAp::convertToCondAp( StateAp *state ) -{ - /* First replace TransDataAp with cond versions. */ - TransList destList; - for ( TransList::Iter tr = state->outList; tr.lte(); ) { - TransList::Iter next = tr.next(); - if ( tr->plain() ) { - TransCondAp *newTrans = convertToCondAp( state, tr->tdap() ); - destList.append( newTrans ); - } - else { - destList.append( tr ); - } - - tr = next; - } - - state->outList.abandon(); - state->outList.transfer( destList ); -} - -void FsmAp::doEmbedCondition( StateAp *state, - const CondSet &set, const CondKeySet &vals ) -{ - convertToCondAp( state ); - - for ( TransList::Iter tr = state->outList; tr.lte(); tr++ ) { - - /* The source (being embedded). */ - CondSpace *srcSpace = addCondSpace( set ); - CondKeySet srcVals = vals; - - /* Extract cond key set from the condition list. We will use this to - * compute the intersection of the cond keys. */ - CondSpace *trSpace = tr->condSpace; - CondKeySet trVals; - if ( tr->condSpace == 0 ) - trVals.append( 0 ); - else { - for ( CondList::Iter cti = tr->tcap()->condList; cti.lte(); cti++ ) { - long key = cti->key.getVal(); - trVals.append( key ); - } - } - - /* Construct merged. */ - CondSet mergedCS; - if ( tr->condSpace != 0 ) - mergedCS.insert( tr->condSpace->condSet ); - mergedCS.insert( set ); - - CondSpace *mergedSpace = addCondSpace( mergedCS ); - - if ( srcSpace != mergedSpace ) { - /* Prep the key list with zero item if necessary. */ - if ( srcSpace == 0 ) - srcVals.append( 0 ); - - expandCondKeys( srcVals, srcSpace, mergedSpace ); - } - - if ( trSpace != mergedSpace ) { - /* Don't need to prep the key list with zero item, will be there - * (see above). */ - expandCondKeys( trVals, trSpace, mergedSpace ); - } - - /* Implement AND, in two parts. */ - CondKeySet newItems; - for ( CondKeySet::Iter c = srcVals; c.lte(); c++ ) { - if ( trVals.find( *c ) ) - newItems.insert( *c ); - } - - for ( CondKeySet::Iter c = trVals; c.lte(); c++ ) { - if ( srcVals.find( *c ) ) - newItems.insert( *c ); - } - - /* Expand the transitions, then we remove anything not in the computed - * list of keys. This approach allows us to embed combinations of - * senses, rather than cond-sense pairs. Necessary for out conditions. */ - CondSpace *orig = tr->condSpace; - tr->condSpace = mergedSpace; - expandConds( state, tr, orig, mergedSpace ); - - /* After expansion, remove anything not in newItems. */ - for ( CondList::Iter cti = tr->tcap()->condList; cti.lte(); ) { - long key = cti->key.getVal(); - - if ( !newItems.find( key ) ) { - /* Delete. */ - CondList::Iter next = cti.next(); - - CondAp *cond = cti; - detachTrans( state, cond->toState, cond ); - tr->tcap()->condList.detach( cond ); - delete cond; - - cti = next; - } - else { - /* Leave alone. */ - cti++; - } - } - } -} - -FsmRes FsmAp::embedCondition( FsmAp *fsm, StateAp *state, const CondSet &set, const CondKeySet &vals ) -{ - /* Turn on misfit accounting to possibly catch the old start state. */ - fsm->setMisfitAccounting( true ); - - /* Worker. */ - fsm->doEmbedCondition( state, set, vals ); - - /* Fill in any states that were newed up as combinations of others. */ - FsmRes res = fillInStates( fsm ); - if ( !res.success() ) - return res; - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - return res; -} - -void FsmAp::addOutCondition( StateAp *state, Action *condAction, bool sense ) -{ - CondSet origCS; - if ( state->outCondSpace != 0 ) - origCS.insert( state->outCondSpace->condSet ); - - CondSet mergedCS; - mergedCS.insert( origCS ); - - bool added = mergedCS.insert( condAction ); - if ( !added ) { - - /* Already exists in the cond set. For every transition, if the - * sense is identical to what we are embedding, leave it alone. If - * the sense is opposite, delete it. */ - - /* Find the position. */ - long pos = 0; - for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { - if ( *csi == condAction ) - pos = csi.pos(); - } - - for ( int cti = 0; cti < state->outCondKeys.length(); ) { - long key = state->outCondKeys[cti]; - - bool set = ( key & ( 1 << pos ) ) != 0; - if ( sense xor set ) { - /* Delete. */ - state->outCondKeys.CondKeyVect::remove( cti, 1 ); - } - else { - /* Leave alone. */ - cti++; - } - } - } - else { - /* Does not exist in the cond set. We will add it. */ - - if ( state->outCondSpace == 0 ) { - /* Note that unlike transitions, we start here with an empty key - * list. Add the item */ - state->outCondKeys.append( 0 ); - } - - /* Allocate a cond space for the merged set. */ - CondSpace *mergedCondSpace = addCondSpace( mergedCS ); - state->outCondSpace = mergedCondSpace; - - /* FIXME: assumes one item always. */ - - /* Translate original condition values, making space for the new bit - * (possibly) introduced by the condition embedding. */ - for ( int cti = 0; cti < state->outCondKeys.length(); cti++ ) { - long origVal = state->outCondKeys[cti]; - long newVal = 0; - - /* For every set bit in the orig, find it's position in the merged - * and set the bit appropriately. */ - for ( CondSet::Iter csi = origCS; csi.lte(); csi++ ) { - /* If set, find it in the merged set and flip the bit to 1. If - * not set, there is nothing to do (convenient eh?) */ - if ( origVal & (1 << csi.pos()) ) { - /* The condition is set. Find the bit position in the - * merged set. */ - Action **cim = mergedCS.find( *csi ); - long bitPos = (cim - mergedCS.data); - newVal |= 1 << bitPos; - } - } - - if ( origVal != newVal ) - state->outCondKeys[cti] = newVal; - - /* Now set the new bit appropriately. Since it defaults to zero we - * only take action if sense is positive. */ - if ( sense ) { - Action **cim = mergedCS.find( condAction ); - int pos = cim - mergedCS.data; - state->outCondKeys[cti] = state->outCondKeys[cti] | (1 << pos); - } - } - } -} diff --git a/libfsm/fsmgraph.cc b/libfsm/fsmgraph.cc deleted file mode 100644 index 819bfa96..00000000 --- a/libfsm/fsmgraph.cc +++ /dev/null @@ -1,1948 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "fsmgraph.h" -#include "mergesort.h" -#include "action.h" - -using std::endl; - -Action::~Action() -{ - /* If we were created by substitution of another action then we don't own the inline list. */ - if ( substOf == 0 && inlineList != 0 ) { - inlineList->empty(); - delete inlineList; - inlineList = 0; - } -} - -InlineItem::~InlineItem() -{ - if ( children != 0 ) { - children->empty(); - delete children; - } -} - -/* Make a new state. The new state will be put on the graph's - * list of state. The new state can be created final or non final. */ -StateAp *FsmAp::addState() -{ - /* Make the new state to return. */ - StateAp *state = new StateAp(); - - if ( misfitAccounting ) { - /* Create the new state on the misfit list. All states are created - * with no foreign in transitions. */ - misfitList.append( state ); - } - else { - /* Create the new state. */ - stateList.append( state ); - } - - return state; -} - -/* Construct an FSM that is the concatenation of an array of characters. A new - * machine will be made that has len+1 states with one transition between each - * state for each integer in str. IsSigned determines if the integers are to - * be considered as signed or unsigned ints. */ -FsmAp *FsmAp::concatFsm( FsmCtx *ctx, Key *str, int len ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Make the first state and set it as the start state. */ - StateAp *last = fsm->addState(); - fsm->setStartState( last ); - - /* Attach subsequent states. */ - for ( int i = 0; i < len; i++ ) { - StateAp *newState = fsm->addState(); - fsm->attachNewTrans( last, newState, str[i], str[i] ); - last = newState; - } - - /* Make the last state the final state. */ - fsm->setFinState( last ); - - return fsm; -} - -/* Case insensitive version of concatFsm. */ -FsmAp *FsmAp::concatFsmCI( FsmCtx *ctx, Key *str, int len ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Make the first state and set it as the start state. */ - StateAp *last = fsm->addState(); - fsm->setStartState( last ); - - /* Attach subsequent states. */ - for ( int i = 0; i < len; i++ ) { - StateAp *newState = fsm->addState(); - - KeySet keySet( ctx->keyOps ); - if ( str[i].isLower() ) - keySet.insert( str[i].toUpper() ); - if ( str[i].isUpper() ) - keySet.insert( str[i].toLower() ); - keySet.insert( str[i] ); - - for ( int i = 0; i < keySet.length(); i++ ) - fsm->attachNewTrans( last, newState, keySet[i], keySet[i] ); - - last = newState; - } - - /* Make the last state the final state. */ - fsm->setFinState( last ); - - return fsm; -} - - -/* Construct a machine that matches one character. A new machine will be made - * that has two states with a single transition between the states. */ -FsmAp *FsmAp::concatFsm( FsmCtx *ctx, Key chr ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Two states first start, second final. */ - fsm->setStartState( fsm->addState() ); - - StateAp *end = fsm->addState(); - fsm->setFinState( end ); - - /* Attach on the character. */ - fsm->attachNewTrans( fsm->startState, end, chr, chr ); - - return fsm; -} - -/* Case insensitive version of single-char concat FSM. */ -FsmAp *FsmAp::concatFsmCI( FsmCtx *ctx, Key chr ) -{ - return concatFsmCI( ctx, &chr, 1 ); -} - - -/* Construct a machine that matches any character in set. A new machine will - * be made that has two states and len transitions between the them. The set - * should be ordered correctly accroding to KeyOps and should not contain - * any duplicates. */ -FsmAp *FsmAp::orFsm( FsmCtx *ctx, Key *set, int len ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Two states first start, second final. */ - fsm->setStartState( fsm->addState() ); - - StateAp *end = fsm->addState(); - fsm->setFinState( end ); - - for ( int i = 1; i < len; i++ ) - assert( ctx->keyOps->lt( set[i-1], set[i] ) ); - - /* Attach on all the integers in the given string of ints. */ - for ( int i = 0; i < len; i++ ) - fsm->attachNewTrans( fsm->startState, end, set[i], set[i] ); - - return fsm; -} - -FsmAp *FsmAp::dotFsm( FsmCtx *ctx ) -{ - FsmAp *retFsm = FsmAp::rangeFsm( ctx, - ctx->keyOps->minKey, ctx->keyOps->maxKey ); - return retFsm; -} - -FsmAp *FsmAp::dotStarFsm( FsmCtx *ctx ) -{ - FsmAp *retFsm = FsmAp::rangeStarFsm( ctx, - ctx->keyOps->minKey, ctx->keyOps->maxKey ); - return retFsm; -} - -/* Construct a machine that matches a range of characters. A new machine will - * be made with two states and a range transition between them. The range will - * match any characters from low to high inclusive. Low should be less than or - * equal to high otherwise undefined behaviour results. IsSigned determines - * if the integers are to be considered as signed or unsigned ints. */ -FsmAp *FsmAp::rangeFsm( FsmCtx *ctx, Key low, Key high ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Two states first start, second final. */ - fsm->setStartState( fsm->addState() ); - - StateAp *end = fsm->addState(); - fsm->setFinState( end ); - - /* Attach using the range of characters. */ - fsm->attachNewTrans( fsm->startState, end, low, high ); - - return fsm; -} - -FsmAp *FsmAp::notRangeFsm( FsmCtx *ctx, Key low, Key high ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Two states first start, second final. */ - fsm->setStartState( fsm->addState() ); - - StateAp *end = fsm->addState(); - fsm->setFinState( end ); - - /* Attach using the range of characters. */ - if ( ctx->keyOps->lt( ctx->keyOps->minKey, low ) ) { - ctx->keyOps->decrement( low ); - fsm->attachNewTrans( fsm->startState, end, ctx->keyOps->minKey, low ); - } - - if ( ctx->keyOps->lt( high, ctx->keyOps->maxKey ) ) { - ctx->keyOps->increment( high ); - fsm->attachNewTrans( fsm->startState, end, high, ctx->keyOps->maxKey ); - } - - return fsm; -} - - -FsmAp *FsmAp::rangeFsmCI( FsmCtx *ctx, Key lowKey, Key highKey ) -{ - FsmAp *retFsm = rangeFsm( ctx, lowKey, highKey ); - - /* Union the portion that covers alphas. */ - if ( lowKey.getVal() <= 'z' ) { - int low, high; - if ( lowKey.getVal() <= 'a' ) - low = 'a'; - else - low = lowKey.getVal(); - - if ( highKey.getVal() >= 'a' ) { - if ( highKey.getVal() >= 'z' ) - high = 'z'; - else - high = highKey.getVal(); - - /* Add in upper(low) .. upper(high) */ - - FsmAp *addFsm = FsmAp::rangeFsm( ctx, toupper(low), toupper(high) ); - FsmRes res = FsmAp::unionOp( retFsm, addFsm ); - retFsm = res.fsm; - } - } - - if ( lowKey.getVal() <= 'Z' ) { - int low, high; - if ( lowKey.getVal() <= 'A' ) - low = 'A'; - else - low = lowKey.getVal(); - - if ( highKey.getVal() >= 'A' ) { - if ( highKey.getVal() >= 'Z' ) - high = 'Z'; - else - high = highKey.getVal(); - - /* Add in lower(low) .. lower(high) */ - FsmAp *addFsm = FsmAp::rangeFsm( ctx, tolower(low), tolower(high) ); - FsmRes res = FsmAp::unionOp( retFsm, addFsm ); - retFsm = res.fsm; - } - } - - return retFsm; -} - -/* Construct a machine that a repeated range of characters. */ -FsmAp *FsmAp::rangeStarFsm( FsmCtx *ctx, Key low, Key high ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* One state which is final and is the start state. */ - fsm->setStartState( fsm->addState() ); - fsm->setFinState( fsm->startState ); - - /* Attach start to start using range of characters. */ - fsm->attachNewTrans( fsm->startState, fsm->startState, low, high ); - - return fsm; -} - -/* Construct a machine that matches the empty string. A new machine will be - * made with only one state. The new state will be both a start and final - * state. IsSigned determines if the machine has a signed or unsigned - * alphabet. Fsm operations must be done on machines with the same alphabet - * signedness. */ -FsmAp *FsmAp::lambdaFsm( FsmCtx *ctx ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Give it one state with no transitions making it - * the start state and final state. */ - fsm->setStartState( fsm->addState() ); - fsm->setFinState( fsm->startState ); - - return fsm; -} - -/* Construct a machine that matches nothing at all. A new machine will be - * made with only one state. It will not be final. */ -FsmAp *FsmAp::emptyFsm( FsmCtx *ctx ) -{ - FsmAp *fsm = new FsmAp( ctx ); - - /* Give it one state with no transitions making it - * the start state and final state. */ - fsm->setStartState( fsm->addState() ); - - return fsm; -} - -void FsmAp::transferOutData( StateAp *destState, StateAp *srcState ) -{ - for ( TransList::Iter trans = destState->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) { - /* Get the actions data from the outActionTable. */ - trans->tdap()->actionTable.setActions( srcState->outActionTable ); - - /* Get the priorities from the outPriorTable. */ - trans->tdap()->priorTable.setPriors( srcState->outPriorTable ); - } - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) { - /* Get the actions data from the outActionTable. */ - cond->actionTable.setActions( srcState->outActionTable ); - - /* Get the priorities from the outPriorTable. */ - cond->priorTable.setPriors( srcState->outPriorTable ); - } - } - } - } - - if ( destState->nfaOut != 0 ) { - for ( NfaTransList::Iter na = *destState->nfaOut; na.lte(); na++ ) - transferOutToNfaTrans( na, srcState ); - } -} - -/* Union worker used by union, set diff (subtract) and intersection. */ -FsmRes FsmAp::doUnion( FsmAp *fsm, FsmAp *other ) -{ - /* Build a state set consisting of both start states */ - StateSet startStateSet; - startStateSet.insert( fsm->startState ); - startStateSet.insert( other->startState ); - - /* Both of the original start states loose their start state status. */ - fsm->unsetStartState(); - other->unsetStartState(); - - /* Bring in the rest of other's entry points. */ - fsm->copyInEntryPoints( other ); - other->entryPoints.empty(); - - /* Merge the lists. This will move all the states from other - * into this. No states will be deleted. */ - fsm->stateList.append( other->stateList ); - fsm->misfitList.append( other->misfitList ); - - /* Move the final set data from other into this. */ - fsm->finStateSet.insert(other->finStateSet); - other->finStateSet.empty(); - - /* Since other's list is empty, we can delete the fsm without - * affecting any states. */ - delete other; - - /* Create a new start state. */ - fsm->setStartState( fsm->addState() ); - - /* Merge the start states. */ - fsm->mergeStateList( fsm->startState, startStateSet.data, startStateSet.length() ); - - /* Fill in any new states made from merging. */ - return fillInStates( fsm ); -} - -bool FsmAp::inEptVect( EptVect *eptVect, StateAp *state ) -{ - if ( eptVect != 0 ) { - /* Vect is there, walk it looking for state. */ - for ( int i = 0; i < eptVect->length(); i++ ) { - if ( eptVect->data[i].targ == state ) - return true; - } - } - return false; -} - -/* Fill epsilon vectors in a root state from a given starting point. Epmploys - * a depth first search through the graph of epsilon transitions. */ -void FsmAp::epsilonFillEptVectFrom( StateAp *root, StateAp *from, bool parentLeaving ) -{ - /* Walk the epsilon transitions out of the state. */ - for ( EpsilonTrans::Iter ep = from->epsilonTrans; ep.lte(); ep++ ) { - /* Find the entry point, if the it does not resove, ignore it. */ - EntryMapEl *enLow, *enHigh; - if ( entryPoints.findMulti( *ep, enLow, enHigh ) ) { - /* Loop the targets. */ - for ( EntryMapEl *en = enLow; en <= enHigh; en++ ) { - /* Do not add the root or states already in eptVect. */ - StateAp *targ = en->value; - if ( targ != from && !inEptVect(root->eptVect, targ) ) { - /* Maybe need to create the eptVect. */ - if ( root->eptVect == 0 ) - root->eptVect = new EptVect(); - - /* If moving to a different graph or if any parent is - * leaving then we are leaving. */ - bool leaving = parentLeaving || - root->owningGraph != targ->owningGraph; - - /* All ok, add the target epsilon and recurse. */ - root->eptVect->append( EptVectEl(targ, leaving) ); - epsilonFillEptVectFrom( root, targ, leaving ); - } - } - } - } -} - -void FsmAp::shadowReadWriteStates() -{ - /* Init isolatedShadow algorithm data. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) - st->isolatedShadow = 0; - - /* Any states that may be both read from and written to must - * be shadowed. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - /* Find such states by looping through stateVect lists, which give us - * the states that will be read from. May cause us to visit the states - * that we are interested in more than once. */ - if ( st->eptVect != 0 ) { - /* For all states that will be read from. */ - for ( EptVect::Iter ept = *st->eptVect; ept.lte(); ept++ ) { - /* Check for read and write to the same state. */ - StateAp *targ = ept->targ; - if ( targ->eptVect != 0 ) { - /* State is to be written to, if the shadow is not already - * there, create it. */ - if ( targ->isolatedShadow == 0 ) { - StateAp *shadow = addState(); - mergeStates( shadow, targ ); - targ->isolatedShadow = shadow; - } - - /* Write shadow into the state vector so that it is the - * state that the epsilon transition will read from. */ - ept->targ = targ->isolatedShadow; - } - } - } - } -} - -void FsmAp::resolveEpsilonTrans() -{ - /* Walk the state list and invoke recursive worker on each state. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) - epsilonFillEptVectFrom( st, st, false ); - - /* Prevent reading from and writing to of the same state. */ - shadowReadWriteStates(); - - /* For all states that have epsilon transitions out, draw the transitions, - * clear the epsilon transitions. */ - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - /* If there is a state vector, then create the pre-merge state. */ - if ( st->eptVect != 0 ) { - /* Merge all the epsilon targets into the state. */ - for ( EptVect::Iter ept = *st->eptVect; ept.lte(); ept++ ) { - if ( ept->leaving ) - mergeStatesLeaving( st, ept->targ ); - else - mergeStates( st, ept->targ ); - } - - /* Clean up the target list. */ - delete st->eptVect; - st->eptVect = 0; - } - - /* Clear the epsilon transitions vector. */ - st->epsilonTrans.empty(); - } -} - -FsmRes FsmAp::applyNfaTrans( FsmAp *fsm, StateAp *fromState, StateAp *toState, NfaTrans *nfaTrans ) -{ - fsm->setMisfitAccounting( true ); - - fsm->mergeStates( fromState, toState, false ); - - /* Epsilons can caused merges which leave behind unreachable states. */ - FsmRes res = FsmAp::fillInStates( fsm ); - if ( !res.success() ) - return res; - - /* Can nuke the epsilon transition that we will never - * follow. */ - fsm->detachFromNfa( fromState, toState, nfaTrans ); - fromState->nfaOut->detach( nfaTrans ); - delete nfaTrans; - - if ( fromState->nfaOut->length() == 0 ) { - delete fromState->nfaOut; - fromState->nfaOut = 0; - } - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - return FsmRes( FsmRes::Fsm(), fsm ); -} - -void FsmAp::globOp( FsmAp **others, int numOthers ) -{ - for ( int m = 0; m < numOthers; m++ ) { - assert( ctx == others[m]->ctx ); - } - - /* All other machines loose start states status. */ - for ( int m = 0; m < numOthers; m++ ) - others[m]->unsetStartState(); - - /* Bring the other machines into this. */ - for ( int m = 0; m < numOthers; m++ ) { - /* Bring in the rest of other's entry points. */ - copyInEntryPoints( others[m] ); - others[m]->entryPoints.empty(); - - /* Merge the lists. This will move all the states from other into - * this. No states will be deleted. */ - stateList.append( others[m]->stateList ); - assert( others[m]->misfitList.length() == 0 ); - - /* Move the final set data from other into this. */ - finStateSet.insert( others[m]->finStateSet ); - others[m]->finStateSet.empty(); - - /* Since other's list is empty, we can delete the fsm without - * affecting any states. */ - delete others[m]; - } -} - -/* Used near the end of an fsm construction. Any labels that are still around - * are referenced only by gotos and calls and they need to be made into - * deterministic entry points. */ -void FsmAp::deterministicEntry() -{ - /* States may loose their entry points, turn on misfit accounting. */ - setMisfitAccounting( true ); - - /* Get a copy of the entry map then clear all the entry points. As we - * iterate the old entry map finding duplicates we will add the entry - * points for the new states that we create. */ - EntryMap prevEntry = entryPoints; - unsetAllEntryPoints(); - - for ( int enId = 0; enId < prevEntry.length(); ) { - /* Count the number of states on this entry key. */ - int highId = enId; - while ( highId < prevEntry.length() && prevEntry[enId].key == prevEntry[highId].key ) - highId += 1; - - int numIds = highId - enId; - if ( numIds == 1 ) { - /* Only a single entry point, just set the entry. */ - setEntry( prevEntry[enId].key, prevEntry[enId].value ); - } - else { - /* Multiple entry points, need to create a new state and merge in - * all the targets of entry points. */ - StateAp *newEntry = addState(); - for ( int en = enId; en < highId; en++ ) - mergeStates( newEntry, prevEntry[en].value ); - - /* Add the new state as the single entry point. */ - setEntry( prevEntry[enId].key, newEntry ); - } - - enId += numIds; - } - - /* The old start state may be unreachable. Remove the misfits and turn off - * misfit accounting. */ - removeMisfits(); - setMisfitAccounting( false ); -} - -/* Unset any final states that are no longer to be final due to final bits. */ -void FsmAp::unsetKilledFinals() -{ - /* Duplicate the final state set before we begin modifying it. */ - StateSet fin( finStateSet ); - - for ( int s = 0; s < fin.length(); s++ ) { - /* Check for killing bit. */ - StateAp *state = fin.data[s]; - if ( state->stateBits & STB_GRAPH1 ) { - /* One final state is a killer, set to non-final. */ - unsetFinState( state ); - } - - /* Clear all killing bits. Non final states should never have had those - * state bits set in the first place. */ - state->stateBits &= ~STB_GRAPH1; - } -} - -/* Unset any final states that are no longer to be final due to final bits. */ -void FsmAp::unsetIncompleteFinals() -{ - /* Duplicate the final state set before we begin modifying it. */ - StateSet fin( finStateSet ); - - for ( int s = 0; s < fin.length(); s++ ) { - /* Check for one set but not the other. */ - StateAp *state = fin.data[s]; - if ( state->stateBits & STB_BOTH && - (state->stateBits & STB_BOTH) != STB_BOTH ) - { - /* One state wants the other but it is not there. */ - unsetFinState( state ); - } - - /* Clear wanting bits. Non final states should never have had those - * state bits set in the first place. */ - state->stateBits &= ~STB_BOTH; - } -} - -/* Kleene star operator. Makes this machine the kleene star of itself. Any - * transitions made going out of the machine and back into itself will be - * notified that they are leaving transitions by having the leavingFromState - * callback invoked. */ -FsmRes FsmAp::starOp( FsmAp *fsm ) -{ - /* The start func orders need to be shifted before doing the star. */ - fsm->ctx->curActionOrd += fsm->shiftStartActionOrder( fsm->ctx->curActionOrd ); - - /* Turn on misfit accounting to possibly catch the old start state. */ - fsm->setMisfitAccounting( true ); - - /* Create the new new start state. It will be set final after the merging - * of the final states with the start state is complete. */ - StateAp *prevStartState = fsm->startState; - fsm->unsetStartState(); - fsm->setStartState( fsm->addState() ); - - /* Merge the new start state with the old one to isolate it. */ - fsm->mergeStates( fsm->startState, prevStartState ); - - if ( !fsm->startState->isFinState() ) { - /* Common case, safe to merge. */ - for ( StateSet::Iter st = fsm->finStateSet; st.lte(); st++ ) - fsm->mergeStatesLeaving( *st, fsm->startState ); - } - else { - /* Merge the start state into all final states. Except the start state on - * the first pass. If the start state is set final we will be doubling up - * its transitions, which will get transfered to any final states that - * follow it in the final state set. This will be determined by the order - * of items in the final state set. To prevent this we just merge with the - * start on a second pass. */ - StateSet origFin = fsm->finStateSet; - for ( StateSet::Iter st = origFin; st.lte(); st++ ) { - if ( *st != fsm->startState ) - fsm->mergeStatesLeaving( *st, fsm->startState ); - } - - /* Now it is safe to merge the start state with itself (provided it - * is set final). */ - if ( fsm->startState->isFinState() ) - fsm->mergeStatesLeaving( fsm->startState, fsm->startState ); - } - - /* Now ensure the new start state is a final state. */ - fsm->setFinState( fsm->startState ); - - /* Fill in any states that were newed up as combinations of others. */ - FsmRes res = FsmAp::fillInStates( fsm ); - if ( !res.success() ) - return res; - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - fsm->afterOpMinimize(); - - return res; -} - -FsmRes FsmAp::plusOp( FsmAp *fsm ) -{ - /* Need a duplicate for the star end. */ - FsmAp *factorDup = new FsmAp( *fsm ); - - /* Star the duplicate. */ - FsmRes res1 = FsmAp::starOp( factorDup ); - if ( !res1.success() ) - return res1; - - FsmRes res2 = FsmAp::concatOp( fsm, res1.fsm ); - if ( !res2.success() ) - return res2; - - return res2; -} - -FsmRes FsmAp::questionOp( FsmAp *fsm ) -{ - /* Make the null fsm. */ - FsmAp *nu = FsmAp::lambdaFsm( fsm->ctx ); - - /* Perform the question operator. */ - FsmRes res = FsmAp::unionOp( fsm, nu ); - if ( !res.success() ) - return res; - - return res; -} - -FsmRes FsmAp::exactRepeatOp( FsmAp *fsm, int times ) -{ - /* Zero repetitions produces lambda machine. */ - if ( times == 0 ) { - FsmCtx *fsmCtx = fsm->ctx; - delete fsm; - return FsmRes( FsmRes::Fsm(), FsmAp::lambdaFsm( fsmCtx ) ); - } - - /* The start func orders need to be shifted before doing the - * repetition. */ - fsm->ctx->curActionOrd += fsm->shiftStartActionOrder( fsm->ctx->curActionOrd ); - - /* A repeat of one does absolutely nothing. */ - if ( times == 1 ) - return FsmRes( FsmRes::Fsm(), fsm ); - - /* Make a machine to make copies from. */ - FsmAp *copyFrom = new FsmAp( *fsm ); - - /* Concatentate duplicates onto the end up until before the last. */ - for ( int i = 1; i < times-1; i++ ) { - FsmAp *dup = new FsmAp( *copyFrom ); - FsmRes res = concatOp( fsm, dup ); - if ( !res.success() ) { - delete copyFrom; - return res; - } - } - - /* Now use the copyFrom on the end. */ - FsmRes res = concatOp( fsm, copyFrom ); - if ( !res.success()) - return res; - - res.fsm->afterOpMinimize(); - - return res; -} - -FsmRes FsmAp::maxRepeatOp( FsmAp *fsm, int times ) -{ - /* Zero repetitions produces lambda machine. */ - if ( times == 0 ) { - FsmCtx *fsmCtx = fsm->ctx; - delete fsm; - return FsmRes( FsmRes::Fsm(), FsmAp::lambdaFsm( fsmCtx ) ); - } - - fsm->ctx->curActionOrd += fsm->shiftStartActionOrder( fsm->ctx->curActionOrd ); - - /* A repeat of one optional merely allows zero string. */ - if ( times == 1 ) { - isolateStartState( fsm ); - fsm->setFinState( fsm->startState ); - return FsmRes( FsmRes::Fsm(), fsm ); - } - - /* Make a machine to make copies from. */ - FsmAp *copyFrom = new FsmAp( *fsm ); - - /* The state set used in the from end of the concatentation. Starts with - * the initial final state set, then after each concatenation, gets set to - * the the final states that come from the the duplicate. */ - StateSet lastFinSet( fsm->finStateSet ); - - /* Set the initial state to zero to allow zero copies. */ - isolateStartState( fsm ); - fsm->setFinState( fsm->startState ); - - /* Concatentate duplicates onto the end up until before the last. */ - for ( int i = 1; i < times-1; i++ ) { - /* Make a duplicate for concating and set the fin bits to graph 2 so we - * can pick out it's final states after the optional style concat. */ - FsmAp *dup = new FsmAp( *copyFrom ); - dup->setFinBits( STB_GRAPH2 ); - FsmRes res = concatOp( fsm, dup, false, &lastFinSet, true ); - if ( !res.success() ) { - delete copyFrom; - return res; - } - - /* Clear the last final state set and make the new one by taking only - * the final states that come from graph 2.*/ - lastFinSet.empty(); - for ( int i = 0; i < fsm->finStateSet.length(); i++ ) { - /* If the state came from graph 2, add it to the last set and clear - * the bits. */ - StateAp *fs = fsm->finStateSet[i]; - if ( fs->stateBits & STB_GRAPH2 ) { - lastFinSet.insert( fs ); - fs->stateBits &= ~STB_GRAPH2; - } - } - } - - /* Now use the copyFrom on the end, no bits set, no bits to clear. */ - FsmRes res = concatOp( fsm, copyFrom, false, &lastFinSet, true ); - if ( !res.success() ) - return res; - - res.fsm->afterOpMinimize(); - - return res; -} - -FsmRes FsmAp::minRepeatOp( FsmAp *fsm, int times ) -{ - if ( times == 0 ) { - /* Acts just like a star op on the machine to return. */ - return FsmAp::starOp( fsm ); - } - else { - /* Take a duplicate for the star below. */ - FsmAp *dup = new FsmAp( *fsm ); - - /* Do repetition on the first half. */ - FsmRes exact = FsmAp::exactRepeatOp( fsm, times ); - if ( !exact.success() ) { - delete dup; - return exact; - } - - /* Star the duplicate. */ - FsmRes star = FsmAp::starOp( dup ); - if ( !star.success() ) { - delete exact.fsm; - return star; - } - - /* Tack on the kleene star. */ - return FsmAp::concatOp( exact.fsm, star.fsm ); - } -} - -FsmRes FsmAp::rangeRepeatOp( FsmAp *fsm, int lowerRep, int upperRep ) -{ - if ( lowerRep == 0 && upperRep == 0 ) { - FsmCtx *fsmCtx = fsm->ctx; - delete fsm; - return FsmRes( FsmRes::Fsm(), FsmAp::lambdaFsm( fsmCtx ) ); - } - else if ( lowerRep == 0 ) { - /* Just doing max repetition. Already guarded against n == 0. */ - return FsmAp::maxRepeatOp( fsm, upperRep ); - } - else if ( lowerRep == upperRep ) { - /* Just doing exact repetition. Already guarded against n == 0. */ - return FsmAp::exactRepeatOp( fsm, lowerRep ); - } - else { - /* This is the case that 0 < lowerRep < upperRep. Take a - * duplicate for the optional repeat. */ - FsmAp *dup = new FsmAp( *fsm ); - - /* Do repetition on the first half. */ - FsmRes exact = FsmAp::exactRepeatOp( fsm, lowerRep ); - if ( !exact.success() ) { - delete dup; - return exact; - } - - /* Do optional repetition on the second half. */ - FsmRes optional = FsmAp::maxRepeatOp( dup, upperRep - lowerRep ); - if ( !optional.success() ) { - delete exact.fsm; - return optional; - } - - /* Concat two halves. */ - return FsmAp::concatOp( exact.fsm, optional.fsm ); - } -} - -/* Concatenates other to the end of this machine. Other is deleted. Any - * transitions made leaving this machine and entering into other are notified - * that they are leaving transitions by having the leavingFromState callback - * invoked. Supports specifying the fromStates (istead of first final state - * set). This is useful for a max-repeat schenario, where from states are not - * all of first's final states. Also supports treating the concatentation as - * optional, which leaves the final states of the first machine as final. */ -FsmRes FsmAp::concatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq, StateSet *fromStates, bool optional ) -{ - for ( PriorTable::Iter g = other->startState->guardedInTable; g.lte(); g++ ) { - fsm->allTransPrior( 0, g->desc ); - other->allTransPrior( 0, g->desc->other ); - } - - /* Assert same signedness and return graph concatenation op. */ - assert( fsm->ctx == other->ctx ); - - /* For the merging process. */ - StateSet finStateSetCopy, startStateSet; - - /* Turn on misfit accounting for both graphs. */ - fsm->setMisfitAccounting( true ); - other->setMisfitAccounting( true ); - - /* Get the other's start state. */ - StateAp *otherStartState = other->startState; - - /* Unset other's start state before bringing in the entry points. */ - other->unsetStartState(); - - /* Bring in the rest of other's entry points. */ - fsm->copyInEntryPoints( other ); - other->entryPoints.empty(); - - /* Bring in other's states into our state lists. */ - fsm->stateList.append( other->stateList ); - fsm->misfitList.append( other->misfitList ); - - /* If from states is not set, then get a copy of our final state set before - * we clobber it and use it instead. */ - if ( fromStates == 0 ) { - finStateSetCopy = fsm->finStateSet; - fromStates = &finStateSetCopy; - } - - /* Unset all of our final states and get the final states from other. */ - if ( !optional ) - fsm->unsetAllFinStates(); - fsm->finStateSet.insert( other->finStateSet ); - - /* Since other's lists are empty, we can delete the fsm without - * affecting any states. */ - delete other; - - /* Merge our former final states with the start state of other. */ - for ( int i = 0; i < fromStates->length(); i++ ) { - StateAp *state = fromStates->data[i]; - - /* Merge the former final state with other's start state. */ - fsm->mergeStatesLeaving( state, otherStartState ); - - /* If the former final state was not reset final then we must clear - * the state's out trans data. If it got reset final then it gets to - * keep its out trans data. This must be done before fillInStates gets - * called to prevent the data from being sourced. */ - if ( ! state->isFinState() ) - fsm->clearOutData( state ); - } - - /* Fill in any new states made from merging. */ - FsmRes res = fillInStates( fsm ); - if ( !res.success() ) - return res; - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - res.fsm->afterOpMinimize( lastInSeq ); - - return res; -} - -FsmRes FsmAp::rightStartConcatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) -{ - PriorDesc *priorDesc0 = fsm->ctx->allocPriorDesc(); - PriorDesc *priorDesc1 = fsm->ctx->allocPriorDesc(); - - /* Set up the priority descriptors. The left machine gets the - * lower priority where as the right get the higher start priority. */ - priorDesc0->key = fsm->ctx->nextPriorKey++; - priorDesc0->priority = 0; - fsm->allTransPrior( fsm->ctx->curPriorOrd++, priorDesc0 ); - - /* The start transitions of the right machine gets the higher - * priority. Use the same unique key. */ - priorDesc1->key = priorDesc0->key; - priorDesc1->priority = 1; - other->startFsmPrior( fsm->ctx->curPriorOrd++, priorDesc1 ); - - return concatOp( fsm, other, lastInSeq ); -} - -/* Returns union of fsm and other. Other is deleted. */ -FsmRes FsmAp::unionOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) -{ - assert( fsm->ctx == other->ctx ); - - fsm->ctx->unionOp = true; - - fsm->setFinBits( STB_GRAPH1 ); - other->setFinBits( STB_GRAPH2 ); - - /* Turn on misfit accounting for both graphs. */ - fsm->setMisfitAccounting( true ); - other->setMisfitAccounting( true ); - - /* Call Worker routine. */ - FsmRes res = doUnion( fsm, other ); - if ( !res.success() ) - return res; - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - fsm->ctx->unionOp = false; - fsm->unsetFinBits( STB_BOTH ); - - fsm->afterOpMinimize( lastInSeq ); - - return res; -} - -/* Intersects other with this machine. Other is deleted. */ -FsmRes FsmAp::intersectOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) -{ - assert( fsm->ctx == other->ctx ); - - /* Turn on misfit accounting for both graphs. */ - fsm->setMisfitAccounting( true ); - other->setMisfitAccounting( true ); - - /* Set the fin bits on this and other to want each other. */ - fsm->setFinBits( STB_GRAPH1 ); - other->setFinBits( STB_GRAPH2 ); - - /* Call worker Or routine. */ - FsmRes res = doUnion( fsm, other ); - if ( !res.success() ) - return res; - - /* Unset any final states that are no longer to - * be final due to final bits. */ - fsm->unsetIncompleteFinals(); - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - /* Remove states that have no path to a final state. */ - fsm->removeDeadEndStates(); - - fsm->afterOpMinimize( lastInSeq ); - - return res; -} - -/* Set subtracts other machine from this machine. Other is deleted. */ -FsmRes FsmAp::subtractOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) -{ - assert( fsm->ctx == other->ctx ); - - /* Turn on misfit accounting for both graphs. */ - fsm->setMisfitAccounting( true ); - other->setMisfitAccounting( true ); - - /* Set the fin bits of other to be killers. */ - other->setFinBits( STB_GRAPH1 ); - - /* Call worker Or routine. */ - FsmRes res = doUnion( fsm, other ); - if ( !res.success() ) - return res; - - /* Unset any final states that are no longer to - * be final due to final bits. */ - fsm->unsetKilledFinals(); - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - /* Remove states that have no path to a final state. */ - fsm->removeDeadEndStates(); - - fsm->afterOpMinimize( lastInSeq ); - - return res; -} - -FsmRes FsmAp::epsilonOp( FsmAp *fsm ) -{ - fsm->setMisfitAccounting( true ); - - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) - st->owningGraph = 0; - - /* Perform merges. */ - fsm->resolveEpsilonTrans(); - - /* Epsilons can caused merges which leave behind unreachable states. */ - FsmRes res = FsmAp::fillInStates( fsm ); - if ( !res.success() ) - return res; - - /* Remove the misfits and turn off misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - return res; -} - -/* Make a new maching by joining together a bunch of machines without making - * any transitions between them. A negative finalId results in there being no - * final id. */ -FsmRes FsmAp::joinOp( FsmAp *fsm, int startId, int finalId, FsmAp **others, int numOthers ) -{ - for ( int m = 0; m < numOthers; m++ ) { - assert( fsm->ctx == others[m]->ctx ); - } - - /* Set the owning machines. Start at one. Zero is reserved for the start - * and final states. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) - st->owningGraph = 1; - for ( int m = 0; m < numOthers; m++ ) { - for ( StateList::Iter st = others[m]->stateList; st.lte(); st++ ) - st->owningGraph = 2+m; - } - - /* All machines loose start state status. */ - fsm->unsetStartState(); - for ( int m = 0; m < numOthers; m++ ) - others[m]->unsetStartState(); - - /* Bring the other machines into this. */ - for ( int m = 0; m < numOthers; m++ ) { - /* Bring in the rest of other's entry points. */ - fsm->copyInEntryPoints( others[m] ); - others[m]->entryPoints.empty(); - - /* Merge the lists. This will move all the states from other into - * this. No states will be deleted. */ - fsm->stateList.append( others[m]->stateList ); - assert( others[m]->misfitList.length() == 0 ); - - /* Move the final set data from other into this. */ - fsm->finStateSet.insert( others[m]->finStateSet ); - others[m]->finStateSet.empty(); - - /* Since other's list is empty, we can delete the fsm without - * affecting any states. */ - delete others[m]; - } - - /* Look up the start entry point. */ - EntryMapEl *enLow = 0, *enHigh = 0; - bool findRes = fsm->entryPoints.findMulti( startId, enLow, enHigh ); - if ( ! findRes ) { - /* No start state. Set a default one and proceed with the join. Note - * that the result of the join will be a very uninteresting machine. */ - fsm->setStartState( fsm->addState() ); - } - else { - /* There is at least one start state, create a state that will become - * the new start state. */ - StateAp *newStart = fsm->addState(); - fsm->setStartState( newStart ); - - /* The start state is in an owning machine class all it's own. */ - newStart->owningGraph = 0; - - /* Create the set of states to merge from. */ - StateSet stateSet; - for ( EntryMapEl *en = enLow; en <= enHigh; en++ ) - stateSet.insert( en->value ); - - /* Merge in the set of start states into the new start state. */ - fsm->mergeStateList( newStart, stateSet.data, stateSet.length() ); - } - - /* Take a copy of the final state set, before unsetting them all. This - * will allow us to call clearOutData on the states that don't get - * final state status back back. */ - StateSet finStateSetCopy = fsm->finStateSet; - - /* Now all final states are unset. */ - fsm->unsetAllFinStates(); - - if ( finalId >= 0 ) { - /* Create the implicit final state. */ - StateAp *finState = fsm->addState(); - fsm->setFinState( finState ); - - /* Assign an entry into the final state on the final state entry id. Note - * that there may already be an entry on this id. That's ok. Also set the - * final state owning machine id. It's in a class all it's own. */ - fsm->setEntry( finalId, finState ); - finState->owningGraph = 0; - } - - /* Hand over to workers for resolving epsilon trans. This will merge states - * with the targets of their epsilon transitions. */ - fsm->resolveEpsilonTrans(); - - /* Invoke the relinquish final callback on any states that did not get - * final state status back. */ - for ( StateSet::Iter st = finStateSetCopy; st.lte(); st++ ) { - if ( !((*st)->stateBits & STB_ISFINAL) ) - fsm->clearOutData( *st ); - } - - /* Fill in any new states made from merging. */ - FsmRes res = FsmAp::fillInStates( fsm ); - if ( !res.success() ) - return res; - - /* Joining can be messy. Instead of having misfit accounting on (which is - * tricky here) do a full cleaning. */ - fsm->removeUnreachableStates(); - - return res; -} - -/* Ensure that the start state is free of entry points (aside from the fact - * that it is the start state). If the start state has entry points then Make a - * new start state by merging with the old one. Useful before modifying start - * transitions. If the existing start state has any entry points other than the - * start state entry then modifying its transitions changes more than the start - * transitions. So isolate the start state by separating it out such that it - * only has start stateness as it's entry point. */ -FsmRes FsmAp::isolateStartState( FsmAp *fsm ) -{ - /* Do nothing if the start state is already isolated. */ - if ( fsm->isStartStateIsolated() ) - return FsmRes( FsmRes::Fsm(), fsm ); - - /* Turn on misfit accounting to possibly catch the old start state. */ - fsm->setMisfitAccounting( true ); - - /* This will be the new start state. The existing start - * state is merged with it. */ - StateAp *prevStartState = fsm->startState; - fsm->unsetStartState(); - fsm->setStartState( fsm->addState() ); - - /* Merge the new start state with the old one to isolate it. */ - fsm->mergeStates( fsm->startState, prevStartState ); - - /* Stfil and stateDict will be empty because the merging of the old start - * state into the new one will not have any conflicting transitions. */ - assert( fsm->stateDict.treeSize == 0 ); - assert( fsm->nfaList.length() == 0 ); - - /* The old start state may be unreachable. Remove the misfits and turn off - * misfit accounting. */ - fsm->removeMisfits(); - fsm->setMisfitAccounting( false ); - - return FsmRes( FsmRes::Fsm(), fsm ); -} - -StateAp *FsmAp::dupStartState() -{ - StateAp *dup = addState(); - mergeStates( dup, startState ); - return dup; -} - -/* A state merge which represents the drawing in of leaving transitions. If - * there is any out data then we duplicate the source state, transfer the out - * data, then merge in the state. The new state will be reaped because it will - * not be given any in transitions. */ -void FsmAp::mergeStatesLeaving( StateAp *destState, StateAp *srcState ) -{ - if ( !hasOutData( destState ) ) { - /* Perform the merge, indicating we are leaving, which will affect how - * out conds are merged. */ - mergeStates( destState, srcState, true ); - } - else { - /* Dup the source state. */ - StateAp *ssMutable = addState(); - mergeStates( ssMutable, srcState ); - - /* Do out data transfer (and out condition embedding). */ - transferOutData( ssMutable, destState ); - - if ( destState->outCondSpace != 0 ) { - - doEmbedCondition( ssMutable, destState->outCondSpace->condSet, - destState->outCondKeys ); - } - - /* Now we merge with dest, setting leaving = true. This dictates how - * out conditions should be merged. */ - mergeStates( destState, ssMutable, true ); - } -} - -void FsmAp::checkEpsilonRegularInteraction( const PriorTable &t1, const PriorTable &t2 ) -{ - for ( PriorTable::Iter pd1 = t1; pd1.lte(); pd1++ ) { - for ( PriorTable::Iter pd2 = t2; pd2.lte(); pd2++ ) { - /* Looking for unequal guarded priorities with the same key. */ - if ( pd1->desc->key == pd2->desc->key ) { - if ( pd1->desc->priority < pd2->desc->priority || - pd1->desc->priority > pd2->desc->priority ) - { - if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { - if ( ! priorInteraction ) { - priorInteraction = true; - guardId = pd1->desc->guardId; - } - } - } - } - } - } -} - -void FsmAp::mergeStateProperties( StateAp *destState, StateAp *srcState ) -{ - /* Draw in any properties of srcState into destState. */ - if ( srcState == destState ) { - /* Duplicate the list to protect against write to source. The - * priorities sets are not copied in because that would have no - * effect. */ - destState->epsilonTrans.append( EpsilonTrans( srcState->epsilonTrans ) ); - - /* Get all actions, duplicating to protect against write to source. */ - destState->toStateActionTable.setActions( - ActionTable( srcState->toStateActionTable ) ); - destState->fromStateActionTable.setActions( - ActionTable( srcState->fromStateActionTable ) ); - destState->outActionTable.setActions( ActionTable( srcState->outActionTable ) ); - destState->errActionTable.setActions( ErrActionTable( srcState->errActionTable ) ); - destState->eofActionTable.setActions( ActionTable( srcState->eofActionTable ) ); - - /* Not touching guarded-in table or out conditions. Probably should - * leave some of the above alone as well. */ - } - else { - /* Get the epsilons, out priorities. */ - destState->epsilonTrans.append( srcState->epsilonTrans ); - destState->outPriorTable.setPriors( srcState->outPriorTable ); - - /* Get all actions. */ - destState->toStateActionTable.setActions( srcState->toStateActionTable ); - destState->fromStateActionTable.setActions( srcState->fromStateActionTable ); - destState->outActionTable.setActions( srcState->outActionTable ); - destState->errActionTable.setActions( srcState->errActionTable ); - destState->eofActionTable.setActions( srcState->eofActionTable ); - destState->lmNfaParts.insert( srcState->lmNfaParts ); - destState->guardedInTable.setPriors( srcState->guardedInTable ); - } -} - -void FsmAp::mergeStateBits( StateAp *destState, StateAp *srcState ) -{ - /* Get bits and final state status. Note in the above code we depend on the - * original final state status being present. */ - destState->stateBits |= ( srcState->stateBits & ~STB_ISFINAL ); - if ( srcState->isFinState() ) - setFinState( destState ); -} - -void FsmAp::mergeNfaTransitions( StateAp *destState, StateAp *srcState ) -{ - /* Copy in any NFA transitions. */ - if ( srcState->nfaOut != 0 ) { - if ( destState->nfaOut == 0 ) - destState->nfaOut = new NfaTransList; - - for ( NfaTransList::Iter nt = *srcState->nfaOut; nt.lte(); nt++ ) { - NfaTrans *trans = new NfaTrans( - nt->pushTable, nt->restoreTable, - nt->popFrom, nt->popCondSpace, nt->popCondKeys, - nt->popAction, nt->popTest, nt->order ); - - destState->nfaOut->append( trans ); - attachToNfa( destState, nt->toState, trans ); - } - } -} - -void FsmAp::checkPriorInteractions( StateAp *destState, StateAp *srcState ) -{ - /* Run a check on priority interactions between epsilon transitions and - * regular transitions. This can't be used to affect machine construction, - * only to check for priority guards. */ - if ( destState->nfaOut != 0 ) { - for ( NfaTransList::Iter nt = *destState->nfaOut; nt.lte(); nt++ ) { - for ( TransList::Iter trans = destState->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - checkEpsilonRegularInteraction( - trans->tdap()->priorTable, nt->priorTable ); - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; - cond.lte(); cond++ ) - { - checkEpsilonRegularInteraction( - cond->priorTable, nt->priorTable ); - - } - } - } - } - } -} - -void FsmAp::mergeStates( StateAp *destState, StateAp *srcState, bool leaving ) -{ - /* Transitions. */ - outTransCopy( destState, srcState->outList.head ); - - /* Properties such as out data, to/from actions. */ - mergeStateProperties( destState, srcState ); - - /* Merge out conditions, depends on the operation (leaving or not). */ - mergeOutConds( destState, srcState, leaving ); - - /* State bits, including final state stats. Out conds depnds on this - * happening after. */ - mergeStateBits( destState, srcState ); - - /* Draw in the NFA transitions. */ - mergeNfaTransitions( destState, srcState ); - - /* Hacked in check for priority interactions, allowing detection of some - * bad situations. */ - checkPriorInteractions( destState, srcState ); -} - -void FsmAp::mergeStateList( StateAp *destState, - StateAp **srcStates, int numSrc ) -{ - for ( int s = 0; s < numSrc; s++ ) - mergeStates( destState, srcStates[s] ); -} - -void FsmAp::cleanAbortedFill( StateAp *state ) -{ - /* Iterate the out transitions, deleting them. */ - for ( TransList::Iter n, t = state->outList; t.lte(); ) { - n = t.next(); - if ( t->plain() ) - delete t->tdap(); - else - delete t->tcap(); - t = n; - } - - state->outList.abandon(); - - if ( state->nfaIn != 0 ) { - delete state->nfaIn; - state->nfaIn = 0; - } - - if ( state->nfaOut != 0 ) { - state->nfaOut->empty(); - delete state->nfaOut; - state->nfaOut = 0; - } -} - -void FsmAp::cleanAbortedFill() -{ - while ( nfaList.length() > 0 ) { - StateAp *state = nfaList.head; - - StateSet *stateSet = &state->stateDictEl->stateSet; - //mergeStateList( state, stateSet->data, stateSet->length() ); - - for ( StateSet::Iter s = *stateSet; s.lte(); s++ ) - detachStateDict( state, *s ); - - nfaList.detach( state ); - } - - /* Disassociated state dict elements from states. */ - for ( StateDict::Iter sdi = stateDict; sdi.lte(); sdi++ ) - sdi->targState->stateDictEl = 0; - - /* Delete all the state dict elements. */ - stateDict.empty(); - - /* Delete all the transitions. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) - cleanAbortedFill( state ); - - /* Delete all the states. */ - stateList.empty(); - - /* Delete all the transitions. */ - for ( StateList::Iter state = misfitList; state.lte(); state++ ) - cleanAbortedFill( state ); - - /* Delete all the states. */ - misfitList.empty(); -} - -bool FsmAp::overStateLimit() -{ - if ( ctx->stateLimit > FsmCtx::STATE_UNLIMITED ) { - long states = misfitList.length() + stateList.length(); - if ( states > ctx->stateLimit ) - return true; - } - return false; -} - -bool FsmAp::fillAbort( FsmRes &res, FsmAp *fsm ) -{ - if ( fsm->priorInteraction ) { - fsm->cleanAbortedFill(); - int guardId = fsm->guardId; - delete fsm; - res = FsmRes( FsmRes::PriorInteraction(), guardId ); - return true; - } - - if ( fsm->overStateLimit() ) { - fsm->cleanAbortedFill(); - delete fsm; - res = FsmRes( FsmRes::TooManyStates() ); - return true; - } - - return false; -} - -FsmRes FsmAp::fillInStates( FsmAp *fsm ) -{ - /* Used as return value on success. Filled in with error on abort. */ - FsmRes res( FsmRes::Fsm(), fsm ); - - /* Merge any states that are awaiting merging. This will likey cause other - * states to be added to the NFA list. */ - while ( true ) { - if ( fillAbort( res, fsm ) ) - return res; - - if ( fsm->nfaList.length() == 0 ) - break; - - StateAp *state = fsm->nfaList.head; - - StateSet *stateSet = &state->stateDictEl->stateSet; - fsm->mergeStateList( state, stateSet->data, stateSet->length() ); - - for ( StateSet::Iter s = *stateSet; s.lte(); s++ ) - fsm->detachStateDict( state, *s ); - - fsm->nfaList.detach( state ); - } - - /* The NFA list is empty at this point. There are no state sets we need to - * preserve. */ - - /* Disassociated state dict elements from states. */ - for ( StateDict::Iter sdi = fsm->stateDict; sdi.lte(); sdi++ ) - sdi->targState->stateDictEl = 0; - - /* Delete all the state dict elements. */ - fsm->stateDict.empty(); - - return res; -} - -/* Check if a machine defines a single character. This is useful in validating - * ranges and machines to export. */ -bool FsmAp::checkSingleCharMachine() -{ - /* Must have two states. */ - if ( stateList.length() != 2 ) - return false; - /* The start state cannot be final. */ - if ( startState->isFinState() ) - return false; - /* There should be only one final state. */ - if ( finStateSet.length() != 1 ) - return false; - /* The final state cannot have any transitions out. */ - if ( finStateSet[0]->outList.length() != 0 ) - return false; - /* The start state should have only one transition out. */ - if ( startState->outList.length() != 1 ) - return false; - /* The singe transition out of the start state should not be a range. */ - TransAp *startTrans = startState->outList.head; - if ( ctx->keyOps->ne( startTrans->lowKey, startTrans->highKey ) ) - return false; - return true; -} - -FsmRes FsmAp::condCostFromState( FsmAp *fsm, StateAp *state, long depth ) -{ - /* Nothing to do if the state is already on the list. */ - if ( state->stateBits & STB_ONLIST ) - return FsmRes( FsmRes::Fsm(), fsm ); - - if ( depth > fsm->ctx->condsCheckDepth ) - return FsmRes( FsmRes::Fsm(), fsm ); - - /* Doing depth first, put state on the list. */ - state->stateBits |= STB_ONLIST; - - /* Recurse on everything ranges. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) { - FsmRes res = condCostFromState( fsm, trans->tdap()->toState, depth + 1 ); - if ( !res.success() ) - return res; - } - } - else { - for ( CondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { - if ( (*csi)->costMark ) - return FsmRes( FsmRes::CondCostTooHigh(), (*csi)->costId ); - } - - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) { - FsmRes res = condCostFromState( fsm, cond->toState, depth + 1 ); - if ( !res.success() ) - return res; - } - } - } - } - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter n = *state->nfaOut; n.lte(); n++ ) { - /* We do not increment depth here since this is an epsilon transition. */ - FsmRes res = condCostFromState( fsm, n->toState, depth ); - if ( !res.success() ) - return res; - } - } - - for ( ActionTable::Iter a = state->fromStateActionTable; a.lte(); a++ ) { - if ( a->value->costMark ) - return FsmRes( FsmRes::CondCostTooHigh(), a->value->costId ); - } - - return FsmRes( FsmRes::Fsm(), fsm ); -} - - -/* Returns either success (using supplied fsm), or some error condition. */ -FsmRes FsmAp::condCostSearch( FsmAp *fsm ) -{ - /* Init on state list flags. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) - st->stateBits &= ~STB_ONLIST; - - FsmRes res = condCostFromState( fsm, fsm->startState, 1 ); - if ( !res.success() ) - delete fsm; - return res; -} - -void FsmAp::condCost( Action *action, long repId ) -{ - action->costMark = true; - action->costId = repId; -} - -/* - * This algorithm assigns a price to each state visit, then adds that to a - * running total. Note that we do not guard against multiple visits to a state, - * since we are estimating runtime cost. - * - * We rely on a character histogram and are looking for a probability of being - * in any given state, given that histogram, simple and very effective. - */ -void FsmAp::breadthFromState( double &total, int &minDepth, double *histogram, - FsmAp *fsm, StateAp *state, long depth, int maxDepth, double stateScore ) -{ - if ( depth > maxDepth ) - return; - - /* Recurse on everything ranges. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - - /* Compute target state score. */ - double span = 0; - for ( int i = trans->lowKey.getVal(); i <= trans->highKey.getVal(); i++ ) - span += histogram[i]; - - double targetStateScore = stateScore * ( span ); - - /* Add to the level. */ - total += targetStateScore; - - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 ) { - if ( trans->tdap()->toState->isFinState() && ( minDepth < 0 || depth < minDepth ) ) - minDepth = depth; - - breadthFromState( total, minDepth, histogram, fsm, trans->tdap()->toState, - depth + 1, maxDepth, targetStateScore ); - } - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 ) { - if ( cond->toState->isFinState() && ( minDepth < 0 || depth < minDepth ) ) - minDepth = depth; - - breadthFromState( total, minDepth, histogram, fsm, cond->toState, - depth + 1, maxDepth, targetStateScore ); - } - } - } - } - - if ( state->nfaOut != 0 ) { - for ( NfaTransList::Iter n = *state->nfaOut; n.lte(); n++ ) { - if ( n->toState->isFinState() && ( minDepth < 0 || depth < minDepth ) ) - minDepth = depth; - - /* We do not increment depth here since this is an epsilon transition. */ - breadthFromState( total, minDepth, histogram, fsm, n->toState, depth, maxDepth, stateScore ); - } - } -} - -void FsmAp::breadthFromEntry( double &total, int &minDepth, double *histogram, FsmAp *fsm, StateAp *state ) -{ - long depth = 1; - int maxDepth = 5; - double stateScore = 1.0; - - FsmAp::breadthFromState( total, minDepth, histogram, fsm, state, depth, maxDepth, stateScore ); -} - - -void FsmAp::applyEntryPriorGuard( FsmAp *fsm, long repId ) -{ - PriorDesc *priorDesc0 = fsm->ctx->allocPriorDesc(); - PriorDesc *priorDesc1 = fsm->ctx->allocPriorDesc(); - - priorDesc0->key = fsm->ctx->nextPriorKey; - priorDesc0->priority = 0; - priorDesc0->guarded = true; - priorDesc0->guardId = repId; - priorDesc0->other = priorDesc1; - - priorDesc1->key = fsm->ctx->nextPriorKey; - priorDesc1->priority = 1; - priorDesc1->guarded = true; - priorDesc1->guardId = repId; - priorDesc1->other = priorDesc0; - - /* Roll over for next allocation. */ - fsm->ctx->nextPriorKey += 1; - - /* Only need to set the first. Second is referenced using 'other' field. */ - fsm->startState->guardedInTable.setPrior( 0, priorDesc0 ); -} - -void FsmAp::applyRepeatPriorGuard( FsmAp *fsm, long repId ) -{ - PriorDesc *priorDesc2 = fsm->ctx->allocPriorDesc(); - PriorDesc *priorDesc3 = fsm->ctx->allocPriorDesc(); - - priorDesc2->key = fsm->ctx->nextPriorKey; - priorDesc2->priority = 0; - priorDesc2->guarded = true; - priorDesc2->guardId = repId; - priorDesc2->other = priorDesc3; - - priorDesc3->key = fsm->ctx->nextPriorKey; - priorDesc3->guarded = true; - priorDesc3->priority = 1; - priorDesc3->guardId = repId; - priorDesc3->other = priorDesc2; - - /* Roll over for next allocation. */ - fsm->ctx->nextPriorKey += 1; - - /* Only need to set the first. Second is referenced using 'other' field. */ - fsm->startState->guardedInTable.setPrior( 0, priorDesc2 ); - - fsm->allTransPrior( fsm->ctx->curPriorOrd++, priorDesc3 ); - fsm->leaveFsmPrior( fsm->ctx->curPriorOrd++, priorDesc2 ); -} - -FsmRes FsmAp::condPlus( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ) -{ - condCost( ini, repId ); - condCost( inc, repId ); - condCost( min, repId ); - if ( max != 0 ) - condCost( max, repId ); - - fsm->startFsmAction( 0, inc ); - - if ( max != 0 ) { - FsmRes res = fsm->startFsmCondition( max, true ); - if ( !res.success() ) - return res; - } - - /* Need a duplicated for the star end. */ - FsmAp *dup = new FsmAp( *fsm ); - - applyRepeatPriorGuard( dup, repId ); - - /* Star the duplicate. */ - FsmRes dupStar = FsmAp::starOp( dup ); - if ( !dupStar.success() ) { - delete fsm; - return dupStar; - } - - FsmRes res = FsmAp::concatOp( fsm, dupStar.fsm ); - if ( !res.success() ) - return res; - - /* End plus operation. */ - - res.fsm->leaveFsmCondition( min, true ); - - /* Init action. */ - res.fsm->startFromStateAction( 0, ini ); - - /* Leading priority guard. */ - applyEntryPriorGuard( res.fsm, repId ); - - return res; -} - -FsmRes FsmAp::condStar( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ) -{ - condCost( ini, repId ); - condCost( inc, repId ); - condCost( min, repId ); - if ( max != 0 ) - condCost( max, repId ); - - /* Increment. */ - fsm->startFsmAction( 0, inc ); - - /* Max (optional). */ - if ( max != 0 ) { - FsmRes res = fsm->startFsmCondition( max, true ); - if ( !res.success() ) - return res; - } - - applyRepeatPriorGuard( fsm, repId ); - - /* Star. */ - FsmRes res = FsmAp::starOp( fsm ); - if ( !res.success() ) - return res; - - /* Restrict leaving. */ - res.fsm->leaveFsmCondition( min, true ); - - /* Init action. */ - res.fsm->startFromStateAction( 0, ini ); - - /* Leading priority guard. */ - applyEntryPriorGuard( res.fsm, repId ); - - return res; -} - -/* Remove duplicates of unique actions from an action table. */ -void FsmAp::removeDups( ActionTable &table ) -{ - /* Scan through the table looking for unique actions to - * remove duplicates of. */ - for ( int i = 0; i < table.length(); i++ ) { - /* Remove any duplicates ahead of i. */ - for ( int r = i+1; r < table.length(); ) { - if ( table[r].value == table[i].value ) - table.vremove(r); - else - r += 1; - } - } -} - -/* Remove duplicates from action lists. This operates only on transition and - * eof action lists and so should be called once all actions have been - * transfered to their final resting place. */ -void FsmAp::removeActionDups() -{ - /* Loop all states. */ - for ( StateList::Iter state = stateList; state.lte(); state++ ) { - /* Loop all transitions. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) - removeDups( trans->tdap()->actionTable ); - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) - removeDups( cond->actionTable ); - } - } - removeDups( state->toStateActionTable ); - removeDups( state->fromStateActionTable ); - removeDups( state->eofActionTable ); - } -} - diff --git a/libfsm/fsmgraph.h b/libfsm/fsmgraph.h deleted file mode 100644 index aee6f718..00000000 --- a/libfsm/fsmgraph.h +++ /dev/null @@ -1,2694 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _FSMGRAPH_H -#define _FSMGRAPH_H - -#include "config.h" -#include "ragel.h" -#include "common.h" -#include "vector.h" -#include "bstset.h" -#include "compare.h" -#include "avltree.h" -#include "dlist.h" -#include "dlistmel.h" -#include "bstmap.h" -#include "sbstmap.h" -#include "sbstset.h" -#include "sbsttable.h" -#include "avlset.h" -#include "avlmap.h" - -#include -#include -#include -#include - - -/* Flags that control merging. */ -#define STB_GRAPH1 0x01 -#define STB_GRAPH2 0x02 -#define STB_BOTH 0x03 -#define STB_ISFINAL 0x04 -#define STB_ISMARKED 0x08 -#define STB_ONLIST 0x10 -#define STB_NFA_REP 0x20 - -using std::ostream; - -struct TransAp; -struct StateAp; -struct FsmAp; -struct Action; -struct FsmLongestMatchPart; -struct CondSpace; -struct FsmCtx; -struct InlineBlock; -struct InlineList; - -struct TooManyStates {}; - -struct PriorInteraction -{ - PriorInteraction( long long id ) : id(id) {} - long long id; -}; - -struct NfaRound -{ - NfaRound( long depth, long groups ) - : depth(depth), groups(groups) {} - - long depth; - long groups; -}; - -typedef Vector NfaRoundVect; - -struct CondCostTooHigh -{ - CondCostTooHigh( long long costId ) - : costId(costId) {} - - long long costId; -}; - - -/* State list element for unambiguous access to list element. */ -struct FsmListEl -{ - StateAp *prev, *next; -}; - -/* This is the marked index for a state pair. Used in minimization. It keeps - * track of whether or not the state pair is marked. */ -struct MarkIndex -{ - MarkIndex(int states); - ~MarkIndex(); - - void markPair(int state1, int state2); - bool isPairMarked(int state1, int state2); - -private: - int numStates; - bool *array; -}; - -/* Transistion Action Element. */ -typedef SBstMapEl< int, Action* > ActionTableEl; - -/* Nodes in the tree that use this action. */ -struct NameInst; -struct InlineList; -typedef Vector NameInstVect; - -struct ActionParam -{ - ActionParam( std::string name ) - : name(name) {} - - std::string name; -}; - -typedef Vector ActionParamList; - -typedef Vector ActionArgList; - -struct CmpActionArgList -{ - static inline int compare( const ActionArgList *list1, const ActionArgList *list2 ) - { - return CmpTable::compare( *list1, *list2 ); - } -}; - -typedef BstMap ActionArgListMap; -typedef BstMapEl ActionArgListMapEl; - -/* Element in list of actions. Contains the string for the code to exectute. */ -struct Action -: - public DListEl, - public AvlTreeEl -{ -public: - - Action( const InputLoc &loc, std::string name, InlineList *inlineList, int condId ) - : - loc(loc), - name(name), - inlineList(inlineList), - actionId(-1), - numTransRefs(0), - numToStateRefs(0), - numFromStateRefs(0), - numEofRefs(0), - numCondRefs(0), - numNfaRefs(0), - anyCall(false), - isLmAction(false), - condId(condId), - costMark(false), - costId(0), - paramList(0), - argListMap(0), - substOf(0), - argList(0) - { - } - - ~Action(); - - static Action *cons( const InputLoc &loc, Action *substOf, - ActionArgList *argList, int condId ) - { - Action *action = new Action( loc, std::string(), 0, condId ); - action->substOf = substOf; - action->argList = argList; - action->inlineList = substOf->inlineList; - return action; - } - - /* Key for action dictionary. */ - std::string getKey() const { return name; } - - /* Data collected during parse. */ - InputLoc loc; - std::string name; - InlineList *inlineList; - int actionId; - - void actionName( ostream &out ) - { - if ( name.empty() ) - out << loc.line << ":" << loc.col; - else - out << name; - } - - /* Nodes in the name tree where the action is embedded. This serves as the - * root for name searches. Since actions can be used multiple times we use - * a vector. Name resolver deals with contracts. */ - NameInstVect embedRoots; - - /* Number of references in the final machine. */ - int numRefs() - { - return numTransRefs + numToStateRefs + - numFromStateRefs + numEofRefs + - numNfaRefs; - } - - int numTransRefs; - int numToStateRefs; - int numFromStateRefs; - int numEofRefs; - int numCondRefs; - int numNfaRefs; - bool anyCall; - - bool isLmAction; - int condId; - - bool costMark; - long long costId; - - ActionParamList *paramList; - ActionArgListMap *argListMap; - Action *substOf; - ActionArgList *argList; -}; - -struct CmpCondId -{ - static inline int compare( const Action *cond1, const Action *cond2 ) - { - if ( cond1->condId < cond2->condId ) - return -1; - else if ( cond1->condId > cond2->condId ) - return 1; - return 0; - } -}; - -/* A list of actions. */ -typedef DList ActionList; -typedef AvlTree ActionDict; - -/* Structure for reverse action mapping. */ -struct RevActionMapEl -{ - char *name; - InputLoc location; -}; - - -/* Transition Action Table. */ -struct ActionTable - : public SBstMap< int, Action*, CmpOrd > -{ - void setAction( int ordering, Action *action ); - void setActions( int *orderings, Action **actions, int nActs ); - void setActions( const ActionTable &other ); - - bool hasAction( Action *action ); -}; - -typedef SBstSet< Action*, CmpOrd > ActionSet; -typedef CmpSTable< Action*, CmpOrd > CmpActionSet; - -/* Transistion Action Element. */ -typedef SBstMapEl< int, FsmLongestMatchPart* > LmActionTableEl; - -/* Transition Action Table. */ -struct LmActionTable - : public SBstMap< int, FsmLongestMatchPart*, CmpOrd > -{ - void setAction( int ordering, FsmLongestMatchPart *action ); - void setActions( const LmActionTable &other ); -}; - -/* Compare of a whole action table element (key & value). */ -struct CmpActionTableEl -{ - static int compare( const ActionTableEl &action1, - const ActionTableEl &action2 ) - { - if ( action1.key < action2.key ) - return -1; - else if ( action1.key > action2.key ) - return 1; - else if ( action1.value < action2.value ) - return -1; - else if ( action1.value > action2.value ) - return 1; - return 0; - } -}; - -/* Compare for ActionTable. */ -typedef CmpSTable< ActionTableEl, CmpActionTableEl > CmpActionTable; - -/* Compare of a whole lm action table element (key & value). */ -struct CmpLmActionTableEl -{ - static int compare( const LmActionTableEl &lmAction1, - const LmActionTableEl &lmAction2 ) - { - if ( lmAction1.key < lmAction2.key ) - return -1; - else if ( lmAction1.key > lmAction2.key ) - return 1; - else if ( lmAction1.value < lmAction2.value ) - return -1; - else if ( lmAction1.value > lmAction2.value ) - return 1; - return 0; - } -}; - -/* Compare for ActionTable. */ -typedef CmpSTable< LmActionTableEl, CmpLmActionTableEl > CmpLmActionTable; - -/* Action table element for error action tables. Adds the encoding of transfer - * point. */ -struct ErrActionTableEl -{ - ErrActionTableEl( Action *action, int ordering, int transferPoint ) - : ordering(ordering), action(action), transferPoint(transferPoint) { } - - /* Ordering and id of the action embedding. */ - int ordering; - Action *action; - - /* Id of point of transfere from Error action table to transtions and - * eofActionTable. */ - int transferPoint; - - int getKey() const { return ordering; } -}; - -struct ErrActionTable - : public SBstTable< ErrActionTableEl, int, CmpOrd > -{ - void setAction( int ordering, Action *action, int transferPoint ); - void setActions( const ErrActionTable &other ); -}; - -/* Compare of an error action table element (key & value). */ -struct CmpErrActionTableEl -{ - static int compare( const ErrActionTableEl &action1, - const ErrActionTableEl &action2 ) - { - if ( action1.ordering < action2.ordering ) - return -1; - else if ( action1.ordering > action2.ordering ) - return 1; - else if ( action1.action < action2.action ) - return -1; - else if ( action1.action > action2.action ) - return 1; - else if ( action1.transferPoint < action2.transferPoint ) - return -1; - else if ( action1.transferPoint > action2.transferPoint ) - return 1; - return 0; - } -}; - -/* Compare for ErrActionTable. */ -typedef CmpSTable< ErrActionTableEl, CmpErrActionTableEl > CmpErrActionTable; - - -/* Descibe a priority, shared among PriorEls. - * Has key and whether or not used. */ -struct PriorDesc -{ - PriorDesc() - : - key(0), - priority(0), - guarded(false), - guardId(0), - other(0) - {} - - int key; - int priority; - bool guarded; - long long guardId; - PriorDesc *other; - - PriorDesc *prev, *next; -}; - -typedef DList PriorDescList; - -/* Element in the arrays of priorities for transitions and arrays. Ordering is - * unique among instantiations of machines, desc is shared. */ -struct PriorEl -{ - PriorEl( int ordering, PriorDesc *desc ) - : ordering(ordering), desc(desc) { } - - int ordering; - PriorDesc *desc; -}; - -/* Compare priority elements, which are ordered by the priority descriptor - * key. */ -struct PriorElCmp -{ - static inline int compare( const PriorEl &pel1, const PriorEl &pel2 ) - { - if ( pel1.desc->key < pel2.desc->key ) - return -1; - else if ( pel1.desc->key > pel2.desc->key ) - return 1; - else - return 0; - } -}; - - -/* Priority Table. */ -struct PriorTable - : public SBstSet< PriorEl, PriorElCmp > -{ - void setPrior( int ordering, PriorDesc *desc ); - void setPriors( const PriorTable &other ); -}; - -/* Compare of prior table elements for distinguising state data. */ -struct CmpPriorEl -{ - static inline int compare( const PriorEl &pel1, const PriorEl &pel2 ) - { - if ( pel1.desc < pel2.desc ) - return -1; - else if ( pel1.desc > pel2.desc ) - return 1; - else if ( pel1.ordering < pel2.ordering ) - return -1; - else if ( pel1.ordering > pel2.ordering ) - return 1; - return 0; - } -}; - -/* Compare of PriorTable distinguising state data. Using a compare of the - * pointers is a little more strict than it needs be. It requires that - * prioritiy tables have the exact same set of priority assignment operators - * (from the input lang) to be considered equal. - * - * Really only key-value pairs need be tested and ordering be merged. However - * this would require that in the fuseing of states, priority descriptors be - * chosen for the new fused state based on priority. Since the out transition - * lists and ranges aren't necessarily going to line up, this is more work for - * little gain. Final compression resets all priorities first, so this would - * only be useful for compression at every operator, which is only an - * undocumented test feature. - */ -typedef CmpSTable CmpPriorTable; - -/* Plain action list that imposes no ordering. */ -typedef Vector TransFuncList; - -/* Comparison for TransFuncList. */ -typedef CmpTable< int, CmpOrd > TransFuncListCompare; - -/* In transition list. Like DList except only has head pointers, which is all - * that is required. Insertion and deletion is handled by the graph. This class - * provides the iterator of a single list. */ -template struct InList -{ - InList() : head(0) { } - - Element *head; - - struct Iter - { - /* Default construct. */ - Iter() : ptr(0) { } - - /* Construct, assign from a list. */ - Iter( const InList &il ) : ptr(il.head) { } - Iter &operator=( const InList &dl ) { ptr = dl.head; return *this; } - - /* At the end */ - bool lte() const { return ptr != 0; } - bool end() const { return ptr == 0; } - - /* At the first, last element. */ - bool first() const { return ptr && ptr->ilprev == 0; } - bool last() const { return ptr && ptr->ilnext == 0; } - - /* Cast, dereference, arrow ops. */ - operator Element*() const { return ptr; } - Element &operator *() const { return *ptr; } - Element *operator->() const { return ptr; } - - /* Increment, decrement. */ - inline void operator++(int) { ptr = ptr->ilnext; } - inline void operator--(int) { ptr = ptr->ilprev; } - - /* The iterator is simply a pointer. */ - Element *ptr; - }; -}; - -struct TransData -{ - TransData() - : - fromState(0), toState(0) - {} - - TransData( const TransData &other ) - : - fromState(0), toState(0), - actionTable(other.actionTable), - priorTable(other.priorTable), - lmActionTable(other.lmActionTable) - { - } - - StateAp *fromState; - StateAp *toState; - - /* The function table and priority for the transition. */ - ActionTable actionTable; - PriorTable priorTable; - - LmActionTable lmActionTable; -}; - - -/* The element for the sub-list within a TransAp. These specify the transitions - * and are keyed by the condition expressions. */ -struct CondAp - : public TransData -{ - CondAp( TransAp *transAp ) - : - TransData(), - transAp(transAp), - key(0) - {} - - CondAp( const CondAp &other, TransAp *transAp ) - : - TransData( other ), - transAp(transAp), - key(other.key) - { - } - - /* Owning transition. */ - TransAp *transAp; - - CondKey key; - - /* Pointers for outlist. */ - CondAp *prev, *next; - - /* Pointers for in-list. */ - CondAp *ilprev, *ilnext; -}; - -typedef DList CondList; - -struct TransCondAp; -struct TransDataAp; - -/* Transition class that implements actions and priorities. */ -struct TransAp -{ - TransAp() - : condSpace(0) {} - - TransAp( const TransAp &other ) - : - lowKey(other.lowKey), - highKey(other.highKey), - condSpace(other.condSpace) - { - } - - ~TransAp() - { - // delete condList.head; - // condList.abandon(); - } - - bool plain() const - { return condSpace == 0; } - - TransCondAp *tcap(); - TransDataAp *tdap(); - - long condFullSize(); - - Key lowKey, highKey; - - /* Which conditions are tested on this range. */ - CondSpace *condSpace; - - /* Pointers for outlist. */ - TransAp *prev, *next; -}; - -struct TransCondAp - : public TransAp -{ - TransCondAp() - : - TransAp() - {} - - TransCondAp( const TransCondAp &other ) - : - TransAp( other ), - condList() - {} - - ~TransCondAp() - { - condList.empty(); - } - - /* Cond trans list. Sorted by key value. */ - CondList condList; -}; - -struct TransDataAp - : public TransAp, public TransData -{ - TransDataAp() - : - TransAp(), - TransData() - {} - - TransDataAp( const TransDataAp &other ) - : - TransAp( other ), - TransData( other ) - {} - - /* Pointers for in-list. */ - TransDataAp *ilprev, *ilnext; -}; - -inline TransCondAp *TransAp::tcap() - { return this->condSpace != 0 ? static_cast( this ) : 0; } - -inline TransDataAp *TransAp::tdap() - { return this->condSpace == 0 ? static_cast( this ) : 0; } - -typedef DList TransList; - -/* Need the base vector type for accessing underlying remove function. */ -typedef BstSet CondKeySet; -typedef Vector CondKeyVect; - -/* State class that implements actions and priorities. */ - -struct NfaActions -{ - NfaActions( Action *push, Action *pop, int order ) - : push(push), pop(pop), order(order) {} - - Action *push; - Action *pop; - - int order; - - ActionTable pushTable; - ActionTable popTable; -}; - -struct NfaTrans -{ - NfaTrans( int order ) - : - fromState(0), - toState(0), - order(order), - popCondSpace(0) - { - } - - NfaTrans( const ActionTable &pushTable, - const ActionTable &restoreTable, - const ActionTable &popFrom, - CondSpace *popCondSpace, - const CondKeySet popCondKeys, - const ActionTable &popAction, - const ActionTable &popTable, - int order ) - : - fromState(0), toState(0), - order(order), - pushTable(pushTable), - restoreTable(restoreTable), - popFrom(popFrom), - popCondSpace(popCondSpace), - popCondKeys(popCondKeys), - popAction(popAction), - popTest(popTable) - {} - - NfaTrans( const NfaTrans &other ) - : - fromState(0), toState(0), - order(other.order), - pushTable(other.pushTable), - restoreTable(other.restoreTable), - popCondSpace(other.popCondSpace), - popCondKeys(other.popCondKeys), - popAction(other.popAction), - popTest(other.popTest), - priorTable(other.priorTable) - {} - - - StateAp *fromState; - StateAp *toState; - - int order; - - ActionTable pushTable; - ActionTable restoreTable; - - /* - * 1. Conditions transferred (always tested first) - * 2. Actions transferred - * 3. Pop actions created during epsilon draw. - */ - ActionTable popFrom; - CondSpace *popCondSpace; - CondKeySet popCondKeys; - - ActionTable popAction; - ActionTable popTest; - - PriorTable priorTable; - - NfaTrans *prev, *next; - NfaTrans *ilprev, *ilnext; -}; - - -typedef BstMap NfaStateMap; -typedef BstMapEl NfaStateMapEl; - -typedef DList NfaTransList; -typedef InList NfaInList; - -struct CmpNfaTrans -{ - static int compare( NfaTrans *t1, NfaTrans *t2 ) - { - /* This comparison is too strong. (okay to use something too strong -- - * we just don't find minimal). * */ - if ( t1->toState < t2->toState ) - return -1; - else if ( t1->toState > t2->toState ) - return 1; - else if ( t1->order < t2->order ) - return -1; - else if ( t1->order > t2->order ) - return 1; - else - { - int r = CmpActionTable::compare( t1->pushTable, t2->pushTable ); - if ( r != 0 ) - return r; - - r = CmpActionTable::compare( t1->restoreTable, t2->restoreTable ); - if ( r != 0 ) - return r; - - if ( t1->popCondSpace < t2->popCondSpace ) - return -1; - else if ( t1->popCondSpace > t2->popCondSpace ) - return 1; - - r = CmpTable::compare( t1->popCondKeys, t2->popCondKeys ); - if ( r != 0 ) - return r; - - r = CmpActionTable::compare( t1->popTest, t2->popTest ); - if ( r != 0 ) - return r; - - r = CmpActionTable::compare( t1->popAction, t2->popAction ); - if ( r != 0 ) - return r; - } - - return 0; - } -}; - -struct CmpNfaTransList -{ - static int compare( const NfaTransList &l1, const NfaTransList &l2 ) - { - if ( l1.length() < l2.length() ) - return -1; - else if ( l1.length() > l2.length() ) - return 1; - else { - NfaTransList::Iter i1 = l1; - NfaTransList::Iter i2 = l2; - while ( i1.lte() ) { - int r = CmpNfaTrans::compare( i1, i2 ); - if ( r != 0 ) - return r; - i1++, i2++; - } - } - return 0; - } -}; - -struct CmpNfaStateMapEl -{ - static int compare( const NfaStateMapEl &el1, const NfaStateMapEl &el2 ) - { - if ( el1.key < el2.key ) - return -1; - else if ( el1.key > el2.key ) - return 1; - else if ( el1.value.push < el2.value.push ) - return -1; - else if ( el1.value.push > el2.value.push ) - return 1; - else if ( el1.value.pop < el2.value.pop ) - return -1; - else if ( el1.value.pop > el2.value.pop ) - return 1; - else if ( el1.value.order < el2.value.order ) - return -1; - else if ( el1.value.order > el2.value.order ) - return 1; - return 0; - } -}; - -/* Set of states, list of states. */ -typedef BstSet StateSet; -typedef DList StateList; - -/* A element in a state dict. */ -struct StateDictEl -: - public AvlTreeEl -{ - StateDictEl(const StateSet &stateSet) - : stateSet(stateSet) { } - - const StateSet &getKey() { return stateSet; } - StateSet stateSet; - StateAp *targState; -}; - -/* Dictionary mapping a set of states to a target state. */ -typedef AvlTree< StateDictEl, StateSet, CmpTable > StateDict; - -struct TransEl -{ - /* Constructors. */ - TransEl() { } - TransEl( Key lowKey, Key highKey ) - : lowKey(lowKey), highKey(highKey) { } - TransEl( Key lowKey, Key highKey, TransAp *value ) - : lowKey(lowKey), highKey(highKey), value(value) { } - - Key lowKey, highKey; - TransAp *value; -}; - -struct CmpKey -{ - CmpKey() - : keyOps(0) {} - - KeyOps *keyOps; - - int compare( const Key key1, const Key key2 ) - { - if ( keyOps->lt( key1, key2 ) ) - return -1; - else if ( keyOps->gt( key1, key2 ) ) - return 1; - else - return 0; - } -}; - -/* Vector based set of key items. */ -struct KeySet -: - public BstSet -{ - KeySet( KeyOps *keyOps ) - { - CmpKey::keyOps = keyOps; - } -}; - -struct MinPartition -{ - MinPartition() : active(false) { } - - StateList list; - bool active; - - MinPartition *prev, *next; -}; - -/* Epsilon transition stored in a state. Specifies the target */ -typedef Vector EpsilonTrans; - -/* List of states that are to be drawn into this. */ -struct EptVectEl -{ - EptVectEl( StateAp *targ, bool leaving ) - : targ(targ), leaving(leaving) { } - - StateAp *targ; - bool leaving; -}; -typedef Vector EptVect; - -/* Set of entry ids that go into this state. */ -typedef BstSet EntryIdSet; - -/* Set of longest match items that may be active in a given state. */ -typedef BstSet LmItemSet; - -/* A Conditions which is to be - * transfered on pending out transitions. */ -struct OutCond -{ - OutCond( Action *action, bool sense ) - : action(action), sense(sense) {} - - Action *action; - bool sense; -}; - -struct CmpOutCond -{ - static int compare( const OutCond &outCond1, const OutCond &outCond2 ) - { - if ( outCond1.action < outCond2.action ) - return -1; - else if ( outCond1.action > outCond2.action ) - return 1; - else if ( outCond1.sense < outCond2.sense ) - return -1; - else if ( outCond1.sense > outCond2.sense ) - return 1; - return 0; - } -}; - -/* Conditions. */ -typedef BstSet< Action*, CmpCondId > CondSet; -typedef CmpTable< Action*, CmpCondId > CmpCondSet; - -struct CondSpace - : public AvlTreeEl -{ - CondSpace( const CondSet &condSet ) - : condSet(condSet) {} - - const CondSet &getKey() { return condSet; } - - long fullSize() - { return ( 1 << condSet.length() ); } - - CondSet condSet; - long condSpaceId; -}; - -typedef Vector CondSpaceVect; - -typedef AvlTree CondSpaceMap; - -typedef Vector LongVect; - -struct CondData -{ - CondSpaceMap condSpaceMap; - - ~CondData() - { - condSpaceMap.empty(); - } -}; - -struct FsmGbl -{ - FsmGbl( const HostLang *hostLang ) - : - printStatistics(false), - errorCount(0), - displayPrintables(false), - hostLang(hostLang), - stringTables(false), - checkPriorInteraction(0), - wantDupsRemoved(true), - minimizeLevel(MinimizePartition2), - minimizeOpt(MinimizeMostOps) - {} - - bool printStatistics; - - /* - * Error reporting. - */ - - /* PROGNAME: txt */ - std::ostream &error(); - - /* file:loc: txt */ - std::ostream &error( const InputLoc &loc ); - - /* txt */ - std::ostream &error_plain(); - - /* file:loc: warning: txt */ - std::ostream &warning( const InputLoc &loc ); - - /* Stats reporting. */ - std::ostream &stats(); - - /* Requested info. */ - std::ostream &info(); - - std::stringstream libcerr; - std::stringstream libcout; - - int errorCount; - void abortCompile( int code ); - bool displayPrintables; - - const HostLang *hostLang; - bool stringTables; - bool checkPriorInteraction; - bool wantDupsRemoved; - - MinimizeLevel minimizeLevel; - MinimizeOpt minimizeOpt; -}; - -/* All FSM operations must be between machines that have been created using the - * same context object. */ -struct FsmCtx -{ - FsmCtx( FsmGbl *fsmGbl ); - ~FsmCtx(); - - KeyOps *keyOps; - CondData *condData; - MinimizeLevel minimizeLevel; - MinimizeOpt minimizeOpt; - - static const int STATE_UNLIMITED = 0; - - long stateLimit; - bool printStatistics; - bool checkPriorInteraction; - - bool unionOp; - - long condsCheckDepth; - - /* Counting the action and priority ordering. */ - int curActionOrd; - int curPriorOrd; - - int nextPriorKey; - int nextCondId; - - PriorDesc *allocPriorDesc() - { - PriorDesc *priorDesc = new PriorDesc(); - priorDescList.append( priorDesc ); - return priorDesc; - } - - PriorDescList priorDescList; - - FsmGbl *fsmGbl; - - /* List of actions. Will be pasted into a switch statement. */ - ActionList actionList; - - ExportList exportList; - - bool generatingSectionSubset; - bool lmRequiresErrorState; - - /* Make name ids to name inst pointers. */ - NameInst **nameIndex; - - /* Element type and get key expression. */ - InlineList *getKeyExpr; - InlineList *accessExpr; - - /* Stack management */ - InlineBlock *prePushExpr; - InlineBlock *postPopExpr; - - /* Nfa stack managment. */ - InlineBlock *nfaPrePushExpr; - InlineBlock *nfaPostPopExpr; - - /* Overriding variables. */ - InlineList *pExpr; - InlineList *peExpr; - InlineList *eofExpr; - InlineList *csExpr; - InlineList *topExpr; - InlineList *stackExpr; - InlineList *actExpr; - InlineList *tokstartExpr; - InlineList *tokendExpr; - InlineList *dataExpr; - - Action *newNfaWrapAction( const char *name, InlineList *inlineList, Action *optWrap ); - void createNfaActions( FsmAp *fsm ); - - /* Checking the contents of actions. */ - void checkAction( Action *action ); - void checkInlineList( Action *act, InlineList *inlineList ); - - void analyzeAction( Action *action, InlineList *inlineList ); - void analyzeGraph( FsmAp *graph ); - - void finalizeInstance( FsmAp *graph ); - void prepareReduction( FsmAp *sectionGraph ); -}; - -typedef InList CondInList; -typedef InList TransInList; - -struct NfaStateEl -{ - StateAp *prev, *next; -}; - -typedef DListMel NfaStateList; - -struct StateAp - : public NfaStateEl -{ - StateAp(); - StateAp(const StateAp &other); - ~StateAp(); - - /* Is the state final? */ - bool isFinState() { return stateBits & STB_ISFINAL; } - - /* Out transition list and the pointer for the default out trans. */ - TransList outList; - - /* In transition Lists. */ - TransInList inTrans; - CondInList inCond; - - /* Set only during scanner construction when actions are added. NFA to DFA - * code can ignore this. */ - StateAp *eofTarget; - - /* Entry points into the state. */ - EntryIdSet entryIds; - - /* Epsilon transitions. */ - EpsilonTrans epsilonTrans; - - /* Number of in transitions from states other than ourselves. */ - int foreignInTrans; - - /* Temporary data for various algorithms. */ - union { - /* When duplicating the fsm we need to map each - * state to the new state representing it. */ - StateAp *stateMap; - - /* When minimizing machines by partitioning, this maps to the group - * the state is in. */ - MinPartition *partition; - - /* Identification for printing and stable minimization. */ - int stateNum; - - } alg; - - /* Data used in epsilon operation, maybe fit into alg? */ - StateAp *isolatedShadow; - int owningGraph; - - /* A pointer to a dict element that contains the set of states this state - * represents. This cannot go into alg, because alg.next is used during - * the merging process. */ - StateDictEl *stateDictEl; - StateSet *stateDictIn; - - NfaTransList *nfaOut; - NfaInList *nfaIn; - - /* When drawing epsilon transitions, holds the list of states to merge - * with. */ - EptVect *eptVect; - - /* Bits controlling the behaviour of the state during collapsing to dfa. */ - int stateBits; - - /* State list elements. */ - StateAp *next, *prev; - - /* - * Priority and Action data. - */ - - /* Out priorities transfered to out transitions. */ - PriorTable outPriorTable; - - /* The following two action tables are distinguished by the fact that when - * toState actions are executed immediatly after transition actions of - * incoming transitions and the current character will be the same as the - * one available then. The fromState actions are executed immediately - * before the transition actions of outgoing transitions and the current - * character is same as the one available then. */ - - /* Actions to execute upon entering into a state. */ - ActionTable toStateActionTable; - - /* Actions to execute when going from the state to the transition. */ - ActionTable fromStateActionTable; - - /* Actions to add to any future transitions that leave via this state. */ - ActionTable outActionTable; - - /* Conditions to add to any future transiions that leave via this state. */ - CondSpace *outCondSpace; - CondKeySet outCondKeys; - - /* Error action tables. */ - ErrActionTable errActionTable; - - /* Actions to execute on eof. */ - ActionTable eofActionTable; - - /* Set of longest match items that may be active in this state. */ - LmItemSet lmItemSet; - - PriorTable guardedInTable; - - /* Used by the NFA-based scanner to track the origin of final states. We - * only use it in cases where just one match is possible, starting with the - * final state duplicates that are drawn using NFA transitions. */ - LmItemSet lmNfaParts; -}; - -/* Return and re-entry for the co-routine iterators. This should ALWAYS be - * used inside of a block. */ -#define CO_RETURN(label) \ - itState = label; \ - return; \ - entry##label: {} - -/* Return and re-entry for the co-routine iterators. This should ALWAYS be - * used inside of a block. */ -#define CO_RETURN2(label, uState) \ - itState = label; \ - userState = uState; \ - return; \ - entry##label: {} - -template struct PiList -{ - PiList() - : ptr(0) {} - - PiList( const DList &l ) - : ptr(l.head) {} - - PiList( Item *ptr ) - : ptr(ptr) {} - - operator Item *() const { return ptr; } - Item *operator->() const { return ptr; } - - bool end() { return ptr == 0; } - void clear() { ptr = 0; } - - PiList next() - { return PiList( ptr->next ); } - - Item *ptr; -}; - -template struct PiSingle -{ - PiSingle() - : ptr(0) {} - - PiSingle( Item *ptr ) - : ptr(ptr) {} - - operator Item *() const { return ptr; } - Item *operator->() const { return ptr; } - - bool end() { return ptr == 0; } - void clear() { ptr = 0; } - - /* Next is always nil. */ - PiSingle next() - { return PiSingle( 0 ); } - - Item *ptr; -}; - -template struct PiVector -{ - PiVector() - : ptr(0), length(0) {} - - PiVector( const Vector &v ) - : ptr(v.data), length(v.length()) {} - - PiVector( Item *ptr, long length ) - : ptr(ptr), length(length) {} - - operator Item *() const { return ptr; } - Item *operator->() const { return ptr; } - - bool end() { return length == 0; } - void clear() { ptr = 0; length = 0; } - - PiVector next() - { return PiVector( ptr + 1, length - 1 ); } - - Item *ptr; - long length; -}; - - -template struct ValPairIter -{ - /* Encodes the states that are meaningful to the of caller the iterator. */ - enum UserState - { - RangeInS1, RangeInS2, - RangeOverlap, - }; - - /* Encodes the different states that an fsm iterator can be in. */ - enum IterState { - Begin, - ConsumeS1Range, ConsumeS2Range, - OnlyInS1Range, OnlyInS2Range, - ExactOverlap, End - }; - - ValPairIter( const ItemIter1 &list1, const ItemIter2 &list2 ); - - template struct NextTrans - { - CondKey key; - ItemIter trans; - ItemIter next; - - NextTrans() { key = 0; } - - void load() { - if ( trans.end() ) - next.clear(); - else { - next = trans->next; - key = trans->key; - } - } - - void set( const ItemIter &t ) { - trans = t; - load(); - } - - void increment() { - trans = next; - load(); - } - }; - - /* Query iterator. */ - bool lte() { return itState != End; } - bool end() { return itState == End; } - void operator++(int) { findNext(); } - void operator++() { findNext(); } - - /* Iterator state. */ - ItemIter1 list1; - ItemIter2 list2; - IterState itState; - UserState userState; - - NextTrans s1Tel; - NextTrans s2Tel; - Key bottomLow, bottomHigh; - ItemIter1 *bottomTrans1; - ItemIter2 *bottomTrans2; - -private: - void findNext(); -}; - -/* Init the iterator by advancing to the first item. */ -template - ValPairIter:: - ValPairIter( const ItemIter1 &list1, const ItemIter2 &list2 ) -: - list1(list1), - list2(list2), - itState(Begin) -{ - findNext(); -} - -/* Advance to the next transition. When returns, trans points to the next - * transition, unless there are no more, in which case end() returns true. */ -template - void ValPairIter::findNext() -{ - /* Jump into the iterator routine base on the iterator state. */ - switch ( itState ) { - case Begin: goto entryBegin; - case ConsumeS1Range: goto entryConsumeS1Range; - case ConsumeS2Range: goto entryConsumeS2Range; - case OnlyInS1Range: goto entryOnlyInS1Range; - case OnlyInS2Range: goto entryOnlyInS2Range; - case ExactOverlap: goto entryExactOverlap; - case End: goto entryEnd; - } - -entryBegin: - /* Set up the next structs at the head of the transition lists. */ - s1Tel.set( list1 ); - s2Tel.set( list2 ); - - /* Concurrently scan both out ranges. */ - while ( true ) { - if ( s1Tel.trans.end() ) { - /* We are at the end of state1's ranges. Process the rest of - * state2's ranges. */ - while ( !s2Tel.trans.end() ) { - /* Range is only in s2. */ - CO_RETURN2( ConsumeS2Range, RangeInS2 ); - s2Tel.increment(); - } - break; - } - else if ( s2Tel.trans.end() ) { - /* We are at the end of state2's ranges. Process the rest of - * state1's ranges. */ - while ( !s1Tel.trans.end() ) { - /* Range is only in s1. */ - CO_RETURN2( ConsumeS1Range, RangeInS1 ); - s1Tel.increment(); - } - break; - } - /* Both state1's and state2's transition elements are good. - * The signiture of no overlap is a back key being in front of a - * front key. */ - else if ( s1Tel.key < s2Tel.key ) { - /* A range exists in state1 that does not overlap with state2. */ - CO_RETURN2( OnlyInS1Range, RangeInS1 ); - s1Tel.increment(); - } - else if ( s2Tel.key < s1Tel.key ) { - /* A range exists in state2 that does not overlap with state1. */ - CO_RETURN2( OnlyInS2Range, RangeInS2 ); - s2Tel.increment(); - } - else { - /* There is an exact overlap. */ - CO_RETURN2( ExactOverlap, RangeOverlap ); - - s1Tel.increment(); - s2Tel.increment(); - } - } - - /* Done, go into end state. */ - CO_RETURN( End ); -} - -template struct RangePairIter -{ - /* Encodes the states that are meaningful to the of caller the iterator. */ - enum UserState - { - RangeInS1, RangeInS2, - RangeOverlap, - BreakS1, BreakS2 - }; - - /* Encodes the different states that an fsm iterator can be in. */ - enum IterState { - Begin, - ConsumeS1Range, ConsumeS2Range, - OnlyInS1Range, OnlyInS2Range, - S1SticksOut, S1SticksOutBreak, - S2SticksOut, S2SticksOutBreak, - S1DragsBehind, S1DragsBehindBreak, - S2DragsBehind, S2DragsBehindBreak, - ExactOverlap, End - }; - - RangePairIter( FsmCtx *ctx, const ItemIter1 &list1, const ItemIter2 &list2 ); - - template struct NextTrans - { - Key lowKey, highKey; - ItemIter trans; - ItemIter next; - - NextTrans() - { - highKey = 0; - lowKey = 0; - } - - void load() { - if ( trans.end() ) - next.clear(); - else { - next = trans.next(); - lowKey = trans->lowKey; - highKey = trans->highKey; - } - } - - void set( const ItemIter &t ) { - trans = t; - load(); - } - - void increment() { - trans = next; - load(); - } - }; - - /* Query iterator. */ - bool lte() { return itState != End; } - bool end() { return itState == End; } - void operator++(int) { findNext(); } - void operator++() { findNext(); } - - FsmCtx *ctx; - - /* Iterator state. */ - ItemIter1 list1; - ItemIter2 list2; - IterState itState; - UserState userState; - - NextTrans s1Tel; - NextTrans s2Tel; - Key bottomLow, bottomHigh; - ItemIter1 bottomTrans1; - ItemIter2 bottomTrans2; - -private: - void findNext(); -}; - -/* Init the iterator by advancing to the first item. */ -template RangePairIter:: - RangePairIter( FsmCtx *ctx, const ItemIter1 &list1, const ItemIter2 &list2 ) -: - ctx(ctx), - list1(list1), - list2(list2), - itState(Begin) -{ - bottomLow = 0; - bottomHigh = 0; - findNext(); -} - -/* Advance to the next transition. When returns, trans points to the next - * transition, unless there are no more, in which case end() returns true. */ -template - void RangePairIter::findNext() -{ - /* Jump into the iterator routine base on the iterator state. */ - switch ( itState ) { - case Begin: goto entryBegin; - case ConsumeS1Range: goto entryConsumeS1Range; - case ConsumeS2Range: goto entryConsumeS2Range; - case OnlyInS1Range: goto entryOnlyInS1Range; - case OnlyInS2Range: goto entryOnlyInS2Range; - case S1SticksOut: goto entryS1SticksOut; - case S1SticksOutBreak: goto entryS1SticksOutBreak; - case S2SticksOut: goto entryS2SticksOut; - case S2SticksOutBreak: goto entryS2SticksOutBreak; - case S1DragsBehind: goto entryS1DragsBehind; - case S1DragsBehindBreak: goto entryS1DragsBehindBreak; - case S2DragsBehind: goto entryS2DragsBehind; - case S2DragsBehindBreak: goto entryS2DragsBehindBreak; - case ExactOverlap: goto entryExactOverlap; - case End: goto entryEnd; - } - -entryBegin: - /* Set up the next structs at the head of the transition lists. */ - s1Tel.set( list1 ); - s2Tel.set( list2 ); - - /* Concurrently scan both out ranges. */ - while ( true ) { - if ( s1Tel.trans.end() ) { - /* We are at the end of state1's ranges. Process the rest of - * state2's ranges. */ - while ( !s2Tel.trans.end() ) { - /* Range is only in s2. */ - CO_RETURN2( ConsumeS2Range, RangeInS2 ); - s2Tel.increment(); - } - break; - } - else if ( s2Tel.trans.end() ) { - /* We are at the end of state2's ranges. Process the rest of - * state1's ranges. */ - while ( !s1Tel.trans.end() ) { - /* Range is only in s1. */ - CO_RETURN2( ConsumeS1Range, RangeInS1 ); - s1Tel.increment(); - } - break; - } - /* Both state1's and state2's transition elements are good. - * The signiture of no overlap is a back key being in front of a - * front key. */ - else if ( ctx->keyOps->lt( s1Tel.highKey, s2Tel.lowKey ) ) { - /* A range exists in state1 that does not overlap with state2. */ - CO_RETURN2( OnlyInS1Range, RangeInS1 ); - s1Tel.increment(); - } - else if ( ctx->keyOps->lt( s2Tel.highKey, s1Tel.lowKey ) ) { - /* A range exists in state2 that does not overlap with state1. */ - CO_RETURN2( OnlyInS2Range, RangeInS2 ); - s2Tel.increment(); - } - /* There is overlap, must mix the ranges in some way. */ - else if ( ctx->keyOps->lt( s1Tel.lowKey, s2Tel.lowKey ) ) { - /* Range from state1 sticks out front. Must break it into - * non-overlaping and overlaping segments. */ - bottomLow = s2Tel.lowKey; - bottomHigh = s1Tel.highKey; - s1Tel.highKey = s2Tel.lowKey; - ctx->keyOps->decrement( s1Tel.highKey ); - bottomTrans1 = s1Tel.trans; - - /* Notify the caller that we are breaking s1. This gives them a - * chance to duplicate s1Tel[0,1].value. */ - CO_RETURN2( S1SticksOutBreak, BreakS1 ); - - /* Broken off range is only in s1. */ - CO_RETURN2( S1SticksOut, RangeInS1 ); - - /* Advance over the part sticking out front. */ - s1Tel.lowKey = bottomLow; - s1Tel.highKey = bottomHigh; - s1Tel.trans = bottomTrans1; - } - else if ( ctx->keyOps->lt( s2Tel.lowKey, s1Tel.lowKey ) ) { - /* Range from state2 sticks out front. Must break it into - * non-overlaping and overlaping segments. */ - bottomLow = s1Tel.lowKey; - bottomHigh = s2Tel.highKey; - s2Tel.highKey = s1Tel.lowKey; - ctx->keyOps->decrement( s2Tel.highKey ); - bottomTrans2 = s2Tel.trans; - - /* Notify the caller that we are breaking s2. This gives them a - * chance to duplicate s2Tel[0,1].value. */ - CO_RETURN2( S2SticksOutBreak, BreakS2 ); - - /* Broken off range is only in s2. */ - CO_RETURN2( S2SticksOut, RangeInS2 ); - - /* Advance over the part sticking out front. */ - s2Tel.lowKey = bottomLow; - s2Tel.highKey = bottomHigh; - s2Tel.trans = bottomTrans2; - } - /* Low ends are even. Are the high ends even? */ - else if ( ctx->keyOps->lt( s1Tel.highKey, s2Tel.highKey ) ) { - /* Range from state2 goes longer than the range from state1. We - * must break the range from state2 into an evenly overlaping - * segment. */ - bottomLow = s1Tel.highKey; - ctx->keyOps->increment( bottomLow ); - bottomHigh = s2Tel.highKey; - s2Tel.highKey = s1Tel.highKey; - bottomTrans2 = s2Tel.trans; - - /* Notify the caller that we are breaking s2. This gives them a - * chance to duplicate s2Tel[0,1].value. */ - CO_RETURN2( S2DragsBehindBreak, BreakS2 ); - - /* Breaking s2 produces exact overlap. */ - CO_RETURN2( S2DragsBehind, RangeOverlap ); - - /* Advance over the front we just broke off of range 2. */ - s2Tel.lowKey = bottomLow; - s2Tel.highKey = bottomHigh; - s2Tel.trans = bottomTrans2; - - /* Advance over the entire s1Tel. We have consumed it. */ - s1Tel.increment(); - } - else if ( ctx->keyOps->lt( s2Tel.highKey, s1Tel.highKey ) ) { - /* Range from state1 goes longer than the range from state2. We - * must break the range from state1 into an evenly overlaping - * segment. */ - bottomLow = s2Tel.highKey; - ctx->keyOps->increment( bottomLow ); - bottomHigh = s1Tel.highKey; - s1Tel.highKey = s2Tel.highKey; - bottomTrans1 = s1Tel.trans; - - /* Notify the caller that we are breaking s1. This gives them a - * chance to duplicate s2Tel[0,1].value. */ - CO_RETURN2( S1DragsBehindBreak, BreakS1 ); - - /* Breaking s1 produces exact overlap. */ - CO_RETURN2( S1DragsBehind, RangeOverlap ); - - /* Advance over the front we just broke off of range 1. */ - s1Tel.lowKey = bottomLow; - s1Tel.highKey = bottomHigh; - s1Tel.trans = bottomTrans1; - - /* Advance over the entire s2Tel. We have consumed it. */ - s2Tel.increment(); - } - else { - /* There is an exact overlap. */ - CO_RETURN2( ExactOverlap, RangeOverlap ); - - s1Tel.increment(); - s2Tel.increment(); - } - } - - /* Done, go into end state. */ - CO_RETURN( End ); -} - - -/* Compare lists of epsilon transitions. Entries are name ids of targets. */ -typedef CmpTable< int, CmpOrd > CmpEpsilonTrans; - -/* Compare class for the Approximate minimization. */ -class ApproxCompare -{ -public: - ApproxCompare( FsmCtx *ctx = 0 ) : ctx(ctx) { } - int compare( const StateAp *pState1, const StateAp *pState2 ); - FsmCtx *ctx; -}; - -/* Compare class for the initial partitioning of a partition minimization. */ -class InitPartitionCompare -{ -public: - InitPartitionCompare( FsmCtx *ctx = 0 ) : ctx(ctx) { } - int compare( const StateAp *pState1, const StateAp *pState2 ); - FsmCtx *ctx; -}; - -/* Compare class for the regular partitioning of a partition minimization. */ -class PartitionCompare -{ -public: - PartitionCompare( FsmCtx *ctx = 0 ) : ctx(ctx) { } - int compare( const StateAp *pState1, const StateAp *pState2 ); - FsmCtx *ctx; -}; - -/* Compare class for a minimization that marks pairs. Provides the shouldMark - * routine. */ -class MarkCompare -{ -public: - MarkCompare( FsmCtx *ctx ) : ctx(ctx) { } - bool shouldMark( MarkIndex &markIndex, const StateAp *pState1, - const StateAp *pState2 ); - FsmCtx *ctx; -}; - -/* List of partitions. */ -typedef DList< MinPartition > PartitionList; - -/* List of transtions out of a state. */ -typedef Vector TransListVect; - -/* Entry point map used for keeping track of entry points in a machine. */ -typedef BstSet< int > EntryIdSet; -typedef BstMapEl< int, StateAp* > EntryMapEl; -typedef BstMap< int, StateAp* > EntryMap; -typedef Vector EntryMapBase; - -struct BreadthCost -{ - BreadthCost( std::string name, double cost ) - : name(name), cost(cost) {} - - std::string name; - double cost; -}; - -struct BreadthResult -{ - BreadthResult( double start ) : start(start) {} - - double start; - Vector costs; -}; - -/* Result of an operation. */ -struct FsmRes -{ - struct Fsm {}; - struct TooManyStates {}; - struct PriorInteraction {}; - struct CondCostTooHigh {}; - struct InternalError {}; - - enum Type - { - TypeFsm = 1, - TypeTooManyStates, - TypePriorInteraction, - TypeCondCostTooHigh, - TypeInternalError, - }; - - FsmRes( const Fsm &, FsmAp *fsm ) - : fsm(fsm), type(TypeFsm) {} - - FsmRes( const TooManyStates & ) - : fsm(0), type(TypeTooManyStates) {} - - FsmRes( const PriorInteraction &, long long guardId ) - : fsm(0), type(TypePriorInteraction), id(guardId) {} - - FsmRes( const CondCostTooHigh &, long long costId ) - : fsm(0), type(TypeCondCostTooHigh), id(costId) {} - - FsmRes( const InternalError & ) - : fsm(0), type(TypeInternalError) {} - - bool success() - { return fsm != 0; } - - operator FsmAp*() - { return type == TypeFsm ? fsm : 0; } - FsmAp *operator->() - { return type == TypeFsm ? fsm : 0; } - - FsmAp *fsm; - Type type; - long long id; -}; - -/* Graph class that implements actions and priorities. */ -struct FsmAp -{ - /* Constructors/Destructors. */ - FsmAp( FsmCtx *ctx ); - FsmAp( const FsmAp &graph ); - ~FsmAp(); - - FsmCtx *ctx; - - bool priorInteraction; - int guardId; - - /* The list of states. */ - StateList stateList; - StateList misfitList; - NfaStateList nfaList; - StateDict stateDict; - - /* The map of entry points. */ - EntryMap entryPoints; - - /* The start state. */ - StateAp *startState; - - /* Error state, possibly created only when the final machine has been - * created and the XML machine is about to be written. No transitions - * point to this state. */ - StateAp *errState; - - /* The set of final states. */ - StateSet finStateSet; - - /* Misfit Accounting. Are misfits put on a separate list. */ - bool misfitAccounting; - - /* - * Transition actions and priorities. - */ - - /* Set priorities on transtions. */ - void startFsmPrior( int ordering, PriorDesc *prior ); - void allTransPrior( int ordering, PriorDesc *prior ); - void finishFsmPrior( int ordering, PriorDesc *prior ); - void leaveFsmPrior( int ordering, PriorDesc *prior ); - - /* Action setting support. */ - void transferOutActions( StateAp *state ); - void transferErrorActions( StateAp *state, int transferPoint ); - void setErrorActions( StateAp *state, const ActionTable &other ); - void setErrorAction( StateAp *state, int ordering, Action *action ); - - /* Fill all spaces in a transition list with an error transition. */ - void fillGaps( StateAp *state ); - - /* Similar to setErrorAction, instead gives a state to go to on error. */ - void setErrorTarget( StateAp *state, StateAp *target, int *orderings, - Action **actions, int nActs ); - - /* Set actions to execute. */ - void startFsmAction( int ordering, Action *action ); - void allTransAction( int ordering, Action *action ); - void finishFsmAction( int ordering, Action *action ); - void leaveFsmAction( int ordering, Action *action ); - void longMatchAction( int ordering, FsmLongestMatchPart *lmPart ); - - /* Set conditions. */ - CondSpace *addCondSpace( const CondSet &condSet ); - - void convertToCondAp( StateAp *state ); - -private: - /* Can generate states. */ - void doEmbedCondition( StateAp *state, - const CondSet &set, const CondKeySet &vals ); - - -public: - static FsmRes embedCondition( FsmAp *fsm, StateAp *state, const CondSet &set, - const CondKeySet &vals ); - - FsmRes startFsmCondition( Action *condAction, bool sense ); - void allTransCondition( Action *condAction, bool sense ); - void leaveFsmCondition( Action *condAction, bool sense ); - - /* Set error actions to execute. */ - void startErrorAction( int ordering, Action *action, int transferPoint ); - void allErrorAction( int ordering, Action *action, int transferPoint ); - void finalErrorAction( int ordering, Action *action, int transferPoint ); - void notStartErrorAction( int ordering, Action *action, int transferPoint ); - void notFinalErrorAction( int ordering, Action *action, int transferPoint ); - void middleErrorAction( int ordering, Action *action, int transferPoint ); - - /* Set EOF actions. */ - void startEOFAction( int ordering, Action *action ); - void allEOFAction( int ordering, Action *action ); - void finalEOFAction( int ordering, Action *action ); - void notStartEOFAction( int ordering, Action *action ); - void notFinalEOFAction( int ordering, Action *action ); - void middleEOFAction( int ordering, Action *action ); - - /* Set To State actions. */ - void startToStateAction( int ordering, Action *action ); - void allToStateAction( int ordering, Action *action ); - void finalToStateAction( int ordering, Action *action ); - void notStartToStateAction( int ordering, Action *action ); - void notFinalToStateAction( int ordering, Action *action ); - void middleToStateAction( int ordering, Action *action ); - - /* Set From State actions. */ - void startFromStateAction( int ordering, Action *action ); - void allFromStateAction( int ordering, Action *action ); - void finalFromStateAction( int ordering, Action *action ); - void notStartFromStateAction( int ordering, Action *action ); - void notFinalFromStateAction( int ordering, Action *action ); - void middleFromStateAction( int ordering, Action *action ); - - /* Shift the action ordering of the start transitions to start at - * fromOrder and increase in units of 1. Useful before kleene star - * operation. */ - int shiftStartActionOrder( int fromOrder ); - - /* Clear all priorities from the fsm to so they won't affcet minimization - * of the final fsm. */ - void clearAllPriorities(); - - /* Zero out all the function keys. */ - void nullActionKeys(); - - /* Walk the list of states and verify state properties. */ - void verifyStates(); - - /* Misfit Accounting. Are misfits put on a separate list. */ - void setMisfitAccounting( bool val ) - { misfitAccounting = val; } - - /* Set and Unset a state as final. */ - void setFinState( StateAp *state ); - void unsetFinState( StateAp *state ); - - void setStartState( StateAp *state ); - void unsetStartState( ); - - /* Set and unset a state as an entry point. */ - void setEntry( int id, StateAp *state ); - void changeEntry( int id, StateAp *to, StateAp *from ); - void unsetEntry( int id, StateAp *state ); - void unsetEntry( int id ); - void unsetAllEntryPoints(); - - /* Epsilon transitions. */ - void epsilonTrans( int id ); - - void checkEpsilonRegularInteraction( const PriorTable &t1, const PriorTable &t2 ); - -private: - /* Can generate staes. */ - void shadowReadWriteStates(); - - void afterOpMinimize( bool lastInSeq = true ); - - void removeDups( ActionTable &table ); - -public: - - void removeActionDups(); - - /* - * Basic attaching and detaching. - */ - - /* Common to attaching/detaching list and default. */ - template < class Head > void attachToInList( StateAp *from, - StateAp *to, Head *&head, Head *trans ); - template < class Head > void detachFromInList( StateAp *from, - StateAp *to, Head *&head, Head *trans ); - - void attachToNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ); - void detachFromNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ); - - void attachStateDict( StateAp *from, StateAp *to ); - void detachStateDict( StateAp *from, StateAp *to ); - - /* Attach with a new transition. */ - CondAp *attachNewCond( TransAp *trans, StateAp *from, - StateAp *to, CondKey onChar ); - TransAp *attachNewTrans( StateAp *from, StateAp *to, - Key onChar1, Key onChar2 ); - - /* Attach with an existing transition that already in an out list. */ - void attachTrans( StateAp *from, StateAp *to, TransDataAp *trans ); - void attachTrans( StateAp *from, StateAp *to, CondAp *trans ); - - /* Redirect a transition away from error and towards some state. */ - void redirectErrorTrans( StateAp *from, StateAp *to, TransDataAp *trans ); - void redirectErrorTrans( StateAp *from, StateAp *to, CondAp *trans ); - - /* Detach a transition from a target state. */ - void detachTrans( StateAp *from, StateAp *to, TransDataAp *trans ); - void detachTrans( StateAp *from, StateAp *to, CondAp *trans ); - - /* Detach a state from the graph. */ - void detachState( StateAp *state ); - - /* - * NFA to DFA conversion routines. - */ - - /* Duplicate a transition that will dropin to a free spot. */ - TransDataAp *dupTransData( StateAp *from, TransDataAp *srcTrans ); - TransAp *dupTrans( StateAp *from, TransAp *srcTrans ); - CondAp *dupCondTrans( StateAp *from, TransAp *destParent, CondAp *srcTrans ); - -private: - /* In crossing, two transitions both go to real states. Can generate - * states. */ - template< class Trans > Trans *fsmAttachStates( - StateAp *from, Trans *destTrans, Trans *srcTrans ); - -public: - void expandConds( StateAp *fromState, TransAp *trans, - CondSpace *fromSpace, CondSpace *mergedSpace ); - TransAp *copyTransForExpansion( StateAp *fromState, TransAp *srcTrans ); - StateAp *copyStateForExpansion( StateAp *srcState ); - void freeEffectiveTrans( TransAp *srcTrans ); - -private: - /* Two transitions are to be crossed, handle the possibility of either - * going to the error state. Can generate states. */ - template< class Trans > Trans *mergeTrans( StateAp *from, - Trans *destTrans, Trans *srcTrans ); - -public: - /* Compare deterimne relative priorities of two transition tables. */ - int comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 ); - - void addOutCondition( StateAp *state, Action *condAction, bool sense ); - - void expandCondKeys( CondKeySet &condKeys, CondSpace *fromSpace, - CondSpace *mergedSpace ); - - /* Back to trans ap (minimmization) */ - TransDataAp *convertToTransAp( StateAp *from, CondAp *cond ); - - /* Cross a src transition with one that is already occupying a spot. */ - TransCondAp *convertToCondAp( StateAp *state, TransDataAp *trans ); - CondSpace *expandCondSpace( TransAp *destTrans, TransAp *srcTrans ); - -private: - /* Can generate states. */ - TransAp *crossTransitions( StateAp *from, - TransAp *destTrans, TransAp *srcTrans ); - TransDataAp *crossTransitionsBothPlain( StateAp *from, - TransDataAp *destTrans, TransDataAp *srcTrans ); - CondAp *crossCondTransitions( StateAp *from, - TransAp *destParent, CondAp *destTrans, CondAp *srcTrans ); - -public: - void prepareNfaRound(); - void finalizeNfaRound(); - - void outTransCopy( StateAp *dest, TransAp *srcList ); - void nfaMergeStates( StateAp *destState, StateAp **srcStates, int numSrc ); - void mergeOutConds( StateAp *destState, StateAp *srcState, bool leaving = false ); - void checkPriorInteractions( StateAp *destState, StateAp *srcState ); - void mergeNfaTransitions( StateAp *destState, StateAp *srcState ); - void mergeStateProperties( StateAp *destState, StateAp *srcState ); - void mergeStatesLeaving( StateAp *destState, StateAp *srcState ); - void mergeStateBits( StateAp *destState, StateAp *srcState ); - void mergeStates( StateAp *destState, StateAp *srcState, bool leaving = false ); - - /* Merge a set of states into destState. */ - void mergeStateList( StateAp *destState, StateAp **srcStates, int numSrc ); - - /* Make all states that are combinations of other states and that - * have not yet had their out transitions filled in. This will - * empty out stateDict and stFil. */ - void cleanAbortedFill( StateAp *state ); - void cleanAbortedFill(); - bool overStateLimit(); - void nfaFillInStates(); - - /* - * Transition Comparison. - */ - - template< class Trans > int compareCondBitElim( Trans *trans1, Trans *trans2 ); - template< class Trans > int compareCondBitElimPtr( Trans *trans1, Trans *trans2 ); - int compareCondListBitElim( const CondList &condList1, const CondList &condList2 ); - - /* Compare priority and function table of transitions. */ - static int compareTransData( TransAp *trans1, TransAp *trans2 ); - template< class Trans > static int compareCondData( Trans *trans1, Trans *trans2 ); - - /* Compare transition data. Either of the pointers may be null. */ - static int compareTransDataPtr( TransAp *trans1, TransAp *trans2 ); - template< class Trans > static int compareCondDataPtr( Trans *trans1, Trans *trans2 ); - - /* Compare target state and transition data. Either pointer may be null. */ - static int compareFullPtr( TransAp *trans1, TransAp *trans2 ); - - /* Compare target partitions. Either pointer may be null. */ - static int compareTransPartPtr( TransAp *trans1, TransAp *trans2 ); - template< class Trans > static int compareCondPartPtr( Trans *trans1, Trans *trans2 ); - - static int comparePart( TransAp *trans1, TransAp *trans2 ); - - /* Check marked status of target states. Either pointer may be null. */ - static bool shouldMarkPtr( MarkIndex &markIndex, - TransAp *trans1, TransAp *trans2 ); - - /* - * Callbacks. - */ - - /* Add in the properties of srcTrans into this. */ - template< class Trans > void addInTrans( Trans *destTrans, Trans *srcTrans ); - - /* Compare states on data stored in the states. */ - static int compareStateData( const StateAp *state1, const StateAp *state2 ); - - /* Out transition data. */ - void clearOutData( StateAp *state ); - bool hasOutData( StateAp *state ); - void transferOutData( StateAp *destState, StateAp *srcState ); - - /* - * Allocation. - */ - - /* New up a state and add it to the graph. */ - StateAp *addState(); - - /* - * Building basic machines - */ - - static FsmAp *concatFsm( FsmCtx *ctx, Key c ); - static FsmAp *concatFsmCI( FsmCtx *ctx, Key c ); - static FsmAp *concatFsm( FsmCtx *ctx, Key *str, int len ); - static FsmAp *concatFsmCI( FsmCtx *ctx, Key *str, int len ); - static FsmAp *orFsm( FsmCtx *ctx, Key *set, int len ); - static FsmAp *rangeFsm( FsmCtx *ctx, Key low, Key high ); - static FsmAp *rangeFsmCI( FsmCtx *ctx, Key low, Key high ); - static FsmAp *rangeStarFsm( FsmCtx *ctx, Key low, Key high ); - static FsmAp *emptyFsm( FsmCtx *ctx ); - static FsmAp *lambdaFsm( FsmCtx *ctx ); - static FsmAp *dotFsm( FsmCtx *ctx ); - static FsmAp *dotStarFsm( FsmCtx *ctx ); - static FsmAp *notRangeFsm( FsmCtx *ctx, Key low, Key high ); - - /* - * Fsm operators. - */ - - static FsmRes starOp( FsmAp *fsm ); - static FsmRes plusOp( FsmAp *fsm ); - static FsmRes questionOp( FsmAp *fsm ); - - static FsmRes exactRepeatOp( FsmAp *fsm, int times ); - static FsmRes maxRepeatOp( FsmAp *fsm, int times ); - static FsmRes minRepeatOp( FsmAp *fsm, int times ); - static FsmRes rangeRepeatOp( FsmAp *fsm, int lower, int upper ); - - static FsmRes concatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true, - StateSet *fromStates = 0, bool optional = false ); - static FsmRes unionOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); - static FsmRes intersectOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); - static FsmRes subtractOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); - static FsmRes epsilonOp( FsmAp *fsm ); - static FsmRes joinOp( FsmAp *fsm, int startId, int finalId, FsmAp **others, int numOthers ); - - static FsmRes rightStartConcatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); - - void transferOutToNfaTrans( NfaTrans *trans, StateAp *state ); - - enum NfaRepeatMode { - NfaLegacy = 1, - NfaGreedy, - NfaLazy - }; - - static FsmRes applyNfaTrans( FsmAp *fsm, StateAp *fromState, StateAp *toState, NfaTrans *nfaTrans ); - - /* Results in an NFA. */ - static FsmRes nfaUnionOp( FsmAp *fsm, FsmAp **others, int n, int depth, std::ostream &stats ); - static FsmRes nfaRepeatOp( FsmAp *fsm, Action *push, Action *pop, Action *init, - Action *stay, Action *repeat, Action *exit ); - - static FsmRes nfaRepeatOp2( FsmAp *fsm, Action *push, Action *pop, Action *init, - Action *stay, Action *repeat, Action *exit, NfaRepeatMode mode = NfaGreedy ); - static FsmRes nfaWrap( FsmAp *fsm, Action *push, Action *pop, Action *init, - Action *stay, Action *exit, NfaRepeatMode mode = NfaGreedy ); - - static FsmRes nfaUnion( const NfaRoundVect &roundsList, FsmAp **machines, - int numMachines, std::ostream &stats, bool printStatistics ); - - static FsmRes condPlus( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ); - static FsmRes condStar( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ); - - /* Make a new start state that has no entry points. Will not change the - * meaning of the fsm. */ - static FsmRes isolateStartState( FsmAp *fsm ); - - /* - * Analysis Functions - */ - static FsmRes condCostFromState( FsmAp *fsm, StateAp *state, long depth ); - static FsmRes condCostSearch( FsmAp *fsm ); - static void breadthFromEntry( double &total, int &minDepth, double *histogram, FsmAp *fsm, StateAp *state ); - static void breadthFromState( double &total, int &minDepth, double *histogram, FsmAp *fsm, StateAp *state, - long depth, int maxDepth, double stateScore); - - /* - * Operator workers - */ - void globOp( FsmAp **others, int numOthers ); - void deterministicEntry(); - - /* Determine if there are any entry points into a start state other than - * the start state. */ - bool isStartStateIsolated(); - - /* Make a new start state that has no entry points. Will not change the - * meaning of the fsm. */ - StateAp *dupStartState(); - - /* Workers for resolving epsilon transitions. */ - bool inEptVect( EptVect *eptVect, StateAp *targ ); - void epsilonFillEptVectFrom( StateAp *root, StateAp *from, bool parentLeaving ); - void resolveEpsilonTrans(); - - static bool fillAbort( FsmRes &res, FsmAp *fsm ); - - static FsmRes fillInStates( FsmAp *fsm ); - - /* Workers for concatenation and union. */ - static FsmRes doUnion( FsmAp *fsm, FsmAp *other ); - static FsmRes doConcat( FsmAp *fsm, FsmAp *other, StateSet *fromStates, bool optional ); - - static void condCost( Action *action, long repId ); - static void applyEntryPriorGuard( FsmAp *fsm, long repId ); - static void applyRepeatPriorGuard( FsmAp *fsm, long repId ); - - /* - * Final states - */ - - /* Unset any final states that are no longer to be final - * due to final bits. */ - void unsetIncompleteFinals(); - void unsetKilledFinals(); - - /* Bring in other's entry points. Assumes others states are going to be - * copied into this machine. */ - void copyInEntryPoints( FsmAp *other ); - - /* Ordering states. */ - void depthFirstOrdering( StateAp *state ); - void depthFirstOrdering(); - void sortStatesByFinal(); - - /* Set sqequential state numbers starting at 0. */ - void setStateNumbers( int base ); - - /* Unset all final states. */ - void unsetAllFinStates(); - - /* Set the bits of final states and clear the bits of non final states. */ - void setFinBits( int finStateBits ); - void unsetFinBits( int finStateBits ); - - /* - * Self-consistency checks. - */ - - /* Run a sanity check on the machine. */ - void verifyIntegrity(); - - /* Verify that there are no unreachable states, or dead end states. */ - void verifyReachability(); - void verifyNoDeadEndStates(); - - /* - * Path pruning - */ - - /* Mark all states reachable from state. */ - void markReachableFromHereReverse( StateAp *state ); - - /* Mark all states reachable from state. */ - void markReachableFromHere( StateAp *state ); - void markReachableFromHereStopFinal( StateAp *state ); - - /* Any transitions to another state? */ - bool anyRegularTransitions( StateAp *state ); - - /* Removes states that cannot be reached by any path in the fsm and are - * thus wasted silicon. */ - void removeDeadEndStates(); - - /* Removes states that cannot be reached by any path in the fsm and are - * thus wasted silicon. */ - long removeUnreachableStates(); - - /* Remove error actions from states on which the error transition will - * never be taken. */ - bool outListCovers( StateAp *state ); - bool anyErrorRange( StateAp *state ); - - /* Remove states that are on the misfit list. */ - void removeMisfits(); - - /* - * FSM Minimization - */ - - /* Minimization by partitioning. */ - void minimizePartition1(); - void minimizePartition2(); - - /* Minimize the final state Machine. The result is the minimal fsm. Slow - * but stable, correct minimization. Uses n^2 space (lookout) and average - * n^2 time. Worst case n^3 time, but a that is a very rare case. */ - void minimizeStable(); - - /* Minimize the final state machine. Does not find the minimal fsm, but a - * pretty good approximation. Does not use any extra space. Average n^2 - * time. Worst case n^3 time, but a that is a very rare case. */ - void minimizeApproximate(); - - /* This is the worker for the minimize approximate solution. It merges - * states that have identical out transitions. */ - bool minimizeRound( ); - - /* Given an intial partioning of states, split partitions that have out trans - * to differing partitions. */ - int partitionRound( StateAp **statePtrs, MinPartition *parts, int numParts ); - - /* Split partitions that have a transition to a previously split partition, until - * there are no more partitions to split. */ - int splitCandidates( StateAp **statePtrs, MinPartition *parts, int numParts ); - - /* Fuse together states in the same partition. */ - void fusePartitions( MinPartition *parts, int numParts ); - - /* Mark pairs where out final stateness differs, out trans data differs, - * trans pairs go to a marked pair or trans data differs. Should get - * alot of pairs. */ - void initialMarkRound( MarkIndex &markIndex ); - - /* One marking round on all state pairs. Considers if trans pairs go - * to a marked state only. Returns whether or not a pair was marked. */ - bool markRound( MarkIndex &markIndex ); - - /* Move the in trans into src into dest. */ - void moveInwardTrans(StateAp *dest, StateAp *src); - - /* Make state src and dest the same state. */ - void fuseEquivStates( StateAp *dest, StateAp *src ); - - /* Find any states that didn't get marked by the marking algorithm and - * merge them into the primary states of their equivalence class. */ - void fuseUnmarkedPairs( MarkIndex &markIndex ); - - /* Merge neighboring transitions go to the same state and have the same - * transitions data. */ - void compressTransitions(); - - /* Returns true if there is a transtion (either explicit or by a gap) to - * the error state. */ - bool checkErrTrans( StateAp *state, TransAp *trans ); - bool checkErrTrans( StateAp *state, CondAp *trans ); - bool checkErrTransFinish( StateAp *state ); - bool hasErrorTrans(); - - /* Check if a machine defines a single character. This is useful in - * validating ranges and machines to export. */ - bool checkSingleCharMachine( ); - - bool elimCondBits(); -}; - -/* Callback invoked when another trans (or possibly this) is added into this - * transition during the merging process. Draw in any properties of srcTrans - * into this transition. AddInTrans is called when a new transitions is made - * that will be a duplicate of another transition or a combination of several - * other transitions. AddInTrans will be called for each transition that the - * new transition is to represent. */ -template< class Trans > void FsmAp::addInTrans( Trans *destTrans, Trans *srcTrans ) -{ - /* Protect against adding in from ourselves. */ - if ( srcTrans == destTrans ) { - /* Adding in ourselves, need to make a copy of the source transitions. - * The priorities are not copied in as that would have no effect. */ - destTrans->lmActionTable.setActions( LmActionTable(srcTrans->lmActionTable) ); - destTrans->actionTable.setActions( ActionTable(srcTrans->actionTable) ); - } - else { - /* Not a copy of ourself, get the functions and priorities. */ - destTrans->lmActionTable.setActions( srcTrans->lmActionTable ); - destTrans->actionTable.setActions( srcTrans->actionTable ); - destTrans->priorTable.setPriors( srcTrans->priorTable ); - } -} - -/* Compares two transition pointers according to priority and functions. - * Either pointer may be null. Does not consider to state or from state. */ -template< class Trans > int FsmAp::compareCondDataPtr( Trans *trans1, Trans *trans2 ) -{ - if ( trans1 == 0 && trans2 != 0 ) - return -1; - else if ( trans1 != 0 && trans2 == 0 ) - return 1; - else if ( trans1 != 0 ) { - /* Both of the transition pointers are set. */ - int compareRes = compareCondData( trans1, trans2 ); - if ( compareRes != 0 ) - return compareRes; - } - return 0; -} - -/* Compares two transition pointers according to priority and functions. - * Either pointer may be null. Does not consider to state or from state. */ -template< class Trans > int FsmAp::compareCondBitElimPtr( Trans *trans1, Trans *trans2 ) -{ - if ( trans1 == 0 && trans2 != 0 ) - return -1; - else if ( trans1 != 0 && trans2 == 0 ) - return 1; - else if ( trans1 != 0 ) { - /* Both of the transition pointers are set. */ - int compareRes = compareCondBitElim( trans1, trans2 ); - if ( compareRes != 0 ) - return compareRes; - } - return 0; -} - -struct NameInst; - -/* Tree nodes. */ - -struct NfaUnion; -struct MachineDef; -struct FsmLongestMatch; -struct FsmLongestMatchPart; -struct FsmLmPartList; -struct Range; -struct LengthDef; -struct Action; -struct InlineList; - -/* Reference to a named state. */ -struct NameRef : public Vector {}; -typedef Vector NameRefList; -typedef Vector NameTargList; - -/* - * FsmLongestMatch - * - * Wherever possible the item match will execute on the character. If not - * possible the item match will execute on a lookahead character and either - * hold the current char (if one away) or backup. - * - * How to handle the problem of backing up over a buffer break? - * - * Don't want to use pending out transitions for embedding item match because - * the role of item match action is different: it may sometimes match on the - * final transition, or may match on a lookahead character. - * - * Don't want to invent a new operator just for this. So just trail action - * after machine, this means we can only use literal actions. - * - * The item action may - * - * What states of the machine will be final. The item actions that wrap around - * on the last character will go straight to the start state. - * - * Some transitions will be lookahead transitions, they will hold the current - * character. Crossing them with regular transitions must be restricted - * because it does not make sense. The transition cannot simultaneously hold - * and consume the current character. - */ -struct FsmLongestMatchPart -{ - FsmLongestMatchPart( Action *action, int longestMatchId ) - : - action(action), - longestMatchId(longestMatchId), - inLmSelect(false) - { } - - Action *action; - Action *setActId; - Action *actOnLast; - Action *actOnNext; - Action *actLagBehind; - Action *actNfaOnLast; - Action *actNfaOnNext; - Action *actNfaOnEof; - int longestMatchId; - bool inLmSelect; - FsmLongestMatch *longestMatch; - - FsmLongestMatchPart *prev, *next; -}; - -/* Declare a new type so that ptreetypes.h need not include dlist.h. */ -struct FsmLmPartList - : DList {}; - -struct FsmLongestMatch -{ - /* Construct with a list of joins */ - FsmLongestMatch( FsmLmPartList *longestMatchList ) - : - longestMatchList(longestMatchList), - lmSwitchHandlesError(false) - { - } - - FsmLmPartList *longestMatchList; - bool lmSwitchHandlesError; - - void restart( FsmAp *graph, TransAp *trans ); - void restart( FsmAp *graph, CondAp *cond ); -}; - -struct NameMapVal -{ - Vector vals; -}; - -/* Tree of instantiated names. */ -typedef AvlMapEl NameMapEl; -typedef AvlMap NameMap; -typedef Vector NameVect; - -/* Node in the tree of instantiated names. */ -struct NameInst -{ - NameInst( const InputLoc &loc, NameInst *parent, std::string name, int id, bool isLabel ) : - loc(loc), parent(parent), name(name), id(id), isLabel(isLabel), - isLongestMatch(false), numRefs(0), numUses(0), start(0), final(0) {} - - ~NameInst(); - - InputLoc loc; - - /* Keep parent pointers in the name tree to retrieve - * fully qulified names. */ - NameInst *parent; - - std::string name; - int id; - bool isLabel; - bool isLongestMatch; - - int numRefs; - int numUses; - - /* Names underneath us, excludes anonymous names. */ - NameMap children; - - /* All names underneath us in order of appearance. */ - NameVect childVect; - - /* Join scopes need an implicit "final" target. */ - NameInst *start, *final; - - /* During a fsm generation walk, lists the names that are referenced by - * epsilon operations in the current scope. After the link is made by the - * epsilon reference and the join operation is complete, the label can - * have its refcount decremented. Once there are no more references the - * entry point can be removed from the fsm returned. */ - NameVect referencedNames; - - /* Pointers for the name search queue. */ - NameInst *prev, *next; - - /* Check if this name inst or any name inst below is referenced. */ - bool anyRefsRec(); -}; - -typedef DList NameInstList; - -extern const int ORD_PUSH; -extern const int ORD_RESTORE; -extern const int ORD_COND; -extern const int ORD_COND2; -extern const int ORD_TEST; - -#endif diff --git a/libfsm/fsmmin.cc b/libfsm/fsmmin.cc deleted file mode 100644 index cabe3968..00000000 --- a/libfsm/fsmmin.cc +++ /dev/null @@ -1,934 +0,0 @@ -/* - * Copyright 2002-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "fsmgraph.h" -#include "mergesort.h" - -struct MergeSortInitPartition - : public MergeSort -{ - MergeSortInitPartition( FsmCtx *ctx ) - { - InitPartitionCompare::ctx = ctx; - } -}; - -struct MergeSortPartition - : public MergeSort -{ - MergeSortPartition( FsmCtx *ctx ) - { - PartitionCompare::ctx = ctx; - } -}; - -struct MergeSortApprox - : public MergeSort -{ - MergeSortApprox( FsmCtx *ctx ) - { - ApproxCompare::ctx = ctx; - } -}; - -int FsmAp::partitionRound( StateAp **statePtrs, MinPartition *parts, int numParts ) -{ - /* Need a mergesort object and a single partition compare. */ - MergeSortPartition mergeSort( ctx ); - PartitionCompare partCompare; - - /* For each partition. */ - for ( int p = 0; p < numParts; p++ ) { - /* Fill the pointer array with the states in the partition. */ - StateList::Iter state = parts[p].list; - for ( int s = 0; state.lte(); state++, s++ ) - statePtrs[s] = state; - - /* Sort the states using the partitioning compare. */ - int numStates = parts[p].list.length(); - mergeSort.sort( statePtrs, numStates ); - - /* Assign the states into partitions based on the results of the sort. */ - int destPart = p, firstNewPart = numParts; - for ( int s = 1; s < numStates; s++ ) { - /* If this state differs from the last then move to the next partition. */ - if ( partCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { - /* The new partition is the next avail spot. */ - destPart = numParts; - numParts += 1; - } - - /* If the state is not staying in the first partition, then - * transfer it to its destination partition. */ - if ( destPart != p ) { - StateAp *state = parts[p].list.detach( statePtrs[s] ); - parts[destPart].list.append( state ); - } - } - - /* Fix the partition pointer for all the states that got moved to a new - * partition. This must be done after the states are transfered so the - * result of the sort is not altered. */ - for ( int newPart = firstNewPart; newPart < numParts; newPart++ ) { - StateList::Iter state = parts[newPart].list; - for ( ; state.lte(); state++ ) - state->alg.partition = &parts[newPart]; - } - } - - return numParts; -} - -/** - * \brief Minimize by partitioning version 1. - * - * Repeatedly tries to split partitions until all partitions are unsplittable. - * Produces the most minimal FSM possible. - */ -void FsmAp::minimizePartition1() -{ - /* Need one mergesort object and partition compares. */ - MergeSortInitPartition mergeSort( ctx ); - InitPartitionCompare initPartCompare( ctx ); - - /* Nothing to do if there are no states. */ - if ( stateList.length() == 0 ) - return; - - /* - * First thing is to partition the states by final state status and - * transition functions. This gives us an initial partitioning to work - * with. - */ - - /* Make a array of pointers to states. */ - int numStates = stateList.length(); - StateAp** statePtrs = new StateAp*[numStates]; - - /* Fill up an array of pointers to the states for easy sorting. */ - StateList::Iter state = stateList; - for ( int s = 0; state.lte(); state++, s++ ) - statePtrs[s] = state; - - /* Sort the states using the array of states. */ - mergeSort.sort( statePtrs, numStates ); - - /* An array of lists of states is used to partition the states. */ - MinPartition *parts = new MinPartition[numStates]; - - /* Assign the states into partitions. */ - int destPart = 0; - for ( int s = 0; s < numStates; s++ ) { - /* If this state differs from the last then move to the next partition. */ - if ( s > 0 && initPartCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { - /* Move to the next partition. */ - destPart += 1; - } - - /* Put the state into its partition. */ - statePtrs[s]->alg.partition = &parts[destPart]; - parts[destPart].list.append( statePtrs[s] ); - } - - /* We just moved all the states from the main list into partitions without - * taking them off the main list. So clean up the main list now. */ - stateList.abandon(); - - /* Split partitions. */ - int numParts = destPart + 1; - while ( true ) { - /* Test all partitions for splitting. */ - int newNum = partitionRound( statePtrs, parts, numParts ); - - /* When no partitions can be split, stop. */ - if ( newNum == numParts ) - break; - - numParts = newNum; - } - - /* Fuse states in the same partition. The states will end up back on the - * main list. */ - fusePartitions( parts, numParts ); - - /* Cleanup. */ - delete[] statePtrs; - delete[] parts; -} - -/* Split partitions that need splittting, decide which partitions might need - * to be split as a result, continue until there are no more that might need - * to be split. */ -int FsmAp::splitCandidates( StateAp **statePtrs, MinPartition *parts, int numParts ) -{ - /* Need a mergesort and a partition compare. */ - MergeSortPartition mergeSort( ctx ); - PartitionCompare partCompare( ctx ); - - /* The lists of unsplitable (partList) and splitable partitions. - * Only partitions in the splitable list are check for needing splitting. */ - PartitionList partList, splittable; - - /* Initially, all partitions are born from a split (the initial - * partitioning) and can cause other partitions to be split. So any - * partition with a state with a transition out to another partition is a - * candidate for splitting. This will make every partition except possibly - * partitions of final states split candidates. */ - for ( int p = 0; p < numParts; p++ ) { - /* Assume not active. */ - parts[p].active = false; - - /* Look for a trans out of any state in the partition. */ - for ( StateList::Iter state = parts[p].list; state.lte(); state++ ) { - /* If there is at least one transition out to another state then - * the partition becomes splittable. */ - if ( state->outList.length() > 0 ) { - parts[p].active = true; - break; - } - } - - /* If it was found active then it goes on the splittable list. */ - if ( parts[p].active ) - splittable.append( &parts[p] ); - else - partList.append( &parts[p] ); - } - - /* While there are partitions that are splittable, pull one off and try - * to split it. If it splits, determine which partitions may now be split - * as a result of the newly split partition. */ - while ( splittable.length() > 0 ) { - MinPartition *partition = splittable.detachFirst(); - - /* Fill the pointer array with the states in the partition. */ - StateList::Iter state = partition->list; - for ( int s = 0; state.lte(); state++, s++ ) - statePtrs[s] = state; - - /* Sort the states using the partitioning compare. */ - int numStates = partition->list.length(); - mergeSort.sort( statePtrs, numStates ); - - /* Assign the states into partitions based on the results of the sort. */ - MinPartition *destPart = partition; - int firstNewPart = numParts; - for ( int s = 1; s < numStates; s++ ) { - /* If this state differs from the last then move to the next partition. */ - if ( partCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { - /* The new partition is the next avail spot. */ - destPart = &parts[numParts]; - numParts += 1; - } - - /* If the state is not staying in the first partition, then - * transfer it to its destination partition. */ - if ( destPart != partition ) { - StateAp *state = partition->list.detach( statePtrs[s] ); - destPart->list.append( state ); - } - } - - /* Fix the partition pointer for all the states that got moved to a new - * partition. This must be done after the states are transfered so the - * result of the sort is not altered. */ - int newPart; - for ( newPart = firstNewPart; newPart < numParts; newPart++ ) { - StateList::Iter state = parts[newPart].list; - for ( ; state.lte(); state++ ) - state->alg.partition = &parts[newPart]; - } - - /* Put the partition we just split and any new partitions that came out - * of the split onto the inactive list. */ - partition->active = false; - partList.append( partition ); - for ( newPart = firstNewPart; newPart < numParts; newPart++ ) { - parts[newPart].active = false; - partList.append( &parts[newPart] ); - } - - if ( destPart == partition ) - continue; - - /* Now determine which partitions are splittable as a result of - * splitting partition by walking the in lists of the states in - * partitions that got split. Partition is the faked first item in the - * loop. */ - MinPartition *causalPart = partition; - newPart = firstNewPart - 1; - while ( newPart < numParts ) { - /* Loop all states in the causal partition. */ - StateList::Iter state = causalPart->list; - for ( ; state.lte(); state++ ) { - /* Walk all transition into the state and put the partition - * that the from state is in onto the splittable list. */ - for ( TransInList::Iter t = state->inTrans; t.lte(); t++ ) { - MinPartition *fromPart = t->fromState->alg.partition; - if ( ! fromPart->active ) { - fromPart->active = true; - partList.detach( fromPart ); - splittable.append( fromPart ); - } - } - for ( CondInList::Iter t = state->inCond; t.lte(); t++ ) { - MinPartition *fromPart = t->fromState->alg.partition; - if ( ! fromPart->active ) { - fromPart->active = true; - partList.detach( fromPart ); - splittable.append( fromPart ); - } - } - } - - newPart += 1; - causalPart = &parts[newPart]; - } - } - return numParts; -} - - -/** - * \brief Minimize by partitioning version 2 (best alg). - * - * Repeatedly tries to split partitions that may splittable until there are no - * more partitions that might possibly need splitting. Runs faster than - * version 1. Produces the most minimal fsm possible. - */ -void FsmAp::minimizePartition2() -{ - /* Need a mergesort and an initial partition compare. */ - MergeSortInitPartition mergeSort( ctx ); - InitPartitionCompare initPartCompare( ctx ); - - /* Nothing to do if there are no states. */ - if ( stateList.length() == 0 ) - return; - - /* - * First thing is to partition the states by final state status and - * transition functions. This gives us an initial partitioning to work - * with. - */ - - /* Make a array of pointers to states. */ - int numStates = stateList.length(); - StateAp** statePtrs = new StateAp*[numStates]; - - /* Fill up an array of pointers to the states for easy sorting. */ - StateList::Iter state = stateList; - for ( int s = 0; state.lte(); state++, s++ ) - statePtrs[s] = state; - - /* Sort the states using the array of states. */ - mergeSort.sort( statePtrs, numStates ); - - /* An array of lists of states is used to partition the states. */ - MinPartition *parts = new MinPartition[numStates]; - - /* Assign the states into partitions. */ - int destPart = 0; - for ( int s = 0; s < numStates; s++ ) { - /* If this state differs from the last then move to the next partition. */ - if ( s > 0 && initPartCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { - /* Move to the next partition. */ - destPart += 1; - } - - /* Put the state into its partition. */ - statePtrs[s]->alg.partition = &parts[destPart]; - parts[destPart].list.append( statePtrs[s] ); - } - - /* We just moved all the states from the main list into partitions without - * taking them off the main list. So clean up the main list now. */ - stateList.abandon(); - - /* Split partitions. */ - int numParts = splitCandidates( statePtrs, parts, destPart+1 ); - - /* Fuse states in the same partition. The states will end up back on the - * main list. */ - fusePartitions( parts, numParts ); - - /* Cleanup. */ - delete[] statePtrs; - delete[] parts; -} - -void FsmAp::initialMarkRound( MarkIndex &markIndex ) -{ - /* P and q for walking pairs. */ - StateAp *p = stateList.head, *q; - - /* Need an initial partition compare. */ - InitPartitionCompare initPartCompare( ctx ); - - /* Walk all unordered pairs of (p, q) where p != q. - * The second depth of the walk stops before reaching p. This - * gives us all unordered pairs of states (p, q) where p != q. */ - while ( p != 0 ) { - q = stateList.head; - while ( q != p ) { - /* If the states differ on final state status, out transitions or - * any transition data then they should be separated on the initial - * round. */ - if ( initPartCompare.compare( p, q ) != 0 ) - markIndex.markPair( p->alg.stateNum, q->alg.stateNum ); - - q = q->next; - } - p = p->next; - } -} - -#ifdef TO_UPGRADE_CONDS -bool FsmAp::markRound( MarkIndex &markIndex ) -{ - /* P an q for walking pairs. Take note if any pair gets marked. */ - StateAp *p = stateList.head, *q; - bool pairWasMarked = false; - - /* Need a mark comparison. */ - MarkCompare markCompare( ctx ); - - /* Walk all unordered pairs of (p, q) where p != q. - * The second depth of the walk stops before reaching p. This - * gives us all unordered pairs of states (p, q) where p != q. */ - while ( p != 0 ) { - q = stateList.head; - while ( q != p ) { - /* Should we mark the pair? */ - if ( !markIndex.isPairMarked( p->alg.stateNum, q->alg.stateNum ) ) { - if ( markCompare.shouldMark( markIndex, p, q ) ) { - markIndex.markPair( p->alg.stateNum, q->alg.stateNum ); - pairWasMarked = true; - } - } - q = q->next; - } - p = p->next; - } - - return pairWasMarked; -} -#endif - -#ifdef TO_UPGRADE_CONDS -/** - * \brief Minimize by pair marking. - * - * Decides if each pair of states is distinct or not. Uses O(n^2) memory and - * should only be used on small graphs. Produces the most minmimal FSM - * possible. - */ -void FsmAp::minimizeStable() -{ - /* Set the state numbers. */ - setStateNumbers( 0 ); - - /* This keeps track of which pairs have been marked. */ - MarkIndex markIndex( stateList.length() ); - - /* Mark pairs where final stateness, out trans, or trans data differ. */ - initialMarkRound( markIndex ); - - /* While the last round of marking succeeded in marking a state - * continue to do another round. */ - int modified = markRound( markIndex ); - while (modified) - modified = markRound( markIndex ); - - /* Merge pairs that are unmarked. */ - fuseUnmarkedPairs( markIndex ); -} -#endif - -#ifdef TO_UPGRADE_CONDS -bool FsmAp::minimizeRound() -{ - /* Nothing to do if there are no states. */ - if ( stateList.length() == 0 ) - return false; - - /* Need a mergesort on approx compare and an approx compare. */ - MergeSortApprox mergeSort( ctx ); - ApproxCompare approxCompare( ctx ); - - /* Fill up an array of pointers to the states. */ - StateAp **statePtrs = new StateAp*[stateList.length()]; - StateList::Iter state = stateList; - for ( int s = 0; state.lte(); state++, s++ ) - statePtrs[s] = state; - - bool modified = false; - - /* Sort The list. */ - mergeSort.sort( statePtrs, stateList.length() ); - - /* Walk the list looking for duplicates next to each other, - * merge in any duplicates. */ - StateAp **pLast = statePtrs; - StateAp **pState = statePtrs + 1; - for ( int i = 1; i < stateList.length(); i++, pState++ ) { - if ( approxCompare.compare( *pLast, *pState ) == 0 ) { - /* Last and pState are the same, so fuse together. Move forward - * with pState but not with pLast. If any more are identical, we - * must */ - fuseEquivStates( *pLast, *pState ); - modified = true; - } - else { - /* Last and this are different, do not set to merge them. Move - * pLast to the current (it may be way behind from merging many - * states) and pState forward one to consider the next pair. */ - pLast = pState; - } - } - delete[] statePtrs; - return modified; -} -#endif - -#ifdef TO_UPGRADE_CONDS -/** - * \brief Minmimize by an approximation. - * - * Repeatedly tries to find states with transitions out to the same set of - * states on the same set of keys until no more identical states can be found. - * Does not produce the most minimial FSM possible. - */ -void FsmAp::minimizeApproximate() -{ - /* While the last minimization round succeeded in compacting states, - * continue to try to compact states. */ - while ( true ) { - bool modified = minimizeRound(); - if ( ! modified ) - break; - } -} -#endif - - -/* Remove states that have no path to them from the start state. Recursively - * traverses the graph marking states that have paths into them. Then removes - * all states that did not get marked. */ -long FsmAp::removeUnreachableStates() -{ - long origLen = stateList.length(); - - /* Misfit accounting should be off and there should be no states on the - * misfit list. */ - assert( !misfitAccounting && misfitList.length() == 0 ); - - /* Mark all the states that can be reached - * through the existing set of entry points. */ - markReachableFromHere( startState ); - for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) - markReachableFromHere( en->value ); - - /* Delete all states that are not marked - * and unmark the ones that are marked. */ - StateAp *state = stateList.head; - while ( state ) { - StateAp *next = state->next; - - if ( state->stateBits & STB_ISMARKED ) - state->stateBits &= ~ STB_ISMARKED; - else { - detachState( state ); - stateList.detach( state ); - delete state; - } - - state = next; - } - - return origLen - stateList.length(); -} - -bool FsmAp::outListCovers( StateAp *state ) -{ - /* Must be at least one range to cover. */ - if ( state->outList.length() == 0 ) - return false; - - /* The first must start at the lower bound. */ - TransList::Iter trans = state->outList.first(); - if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) - return false; - - /* Loop starts at second el. */ - trans.increment(); - - /* Loop checks lower against prev upper. */ - for ( ; trans.lte(); trans++ ) { - /* Lower end of the trans must be one greater than the - * previous' high end. */ - Key lowKey = trans->lowKey; - ctx->keyOps->decrement( lowKey ); - if ( ctx->keyOps->lt( trans->prev->highKey, lowKey ) ) - return false; - } - - /* Require that the last range extends to the upper bound. */ - trans = state->outList.last(); - if ( ctx->keyOps->lt( trans->highKey, ctx->keyOps->maxKey ) ) - return false; - - return true; -} - -/* Remove states that that do not lead to a final states. Works recursivly traversing - * the graph in reverse (starting from all final states) and marking seen states. Then - * removes states that did not get marked. */ -void FsmAp::removeDeadEndStates() -{ - /* Misfit accounting should be off and there should be no states on the - * misfit list. */ - assert( !misfitAccounting && misfitList.length() == 0 ); - - /* Mark all states that have paths to the final states. */ - StateAp **st = finStateSet.data; - int nst = finStateSet.length(); - for ( int i = 0; i < nst; i++, st++ ) - markReachableFromHereReverse( *st ); - - /* Start state gets honorary marking. If the machine accepts nothing we - * still want the start state to hang around. This must be done after the - * recursive call on all the final states so that it does not cause the - * start state in transitions to be skipped when the start state is - * visited by the traversal. */ - startState->stateBits |= STB_ISMARKED; - - /* Delete all states that are not marked - * and unmark the ones that are marked. */ - StateAp *state = stateList.head; - while ( state != 0 ) { - StateAp *next = state->next; - - if ( state->stateBits & STB_ISMARKED ) - state->stateBits &= ~ STB_ISMARKED; - else { - detachState( state ); - stateList.detach( state ); - delete state; - } - - state = next; - } -} - -/* Remove states on the misfit list. To work properly misfit accounting should - * be on when this is called. The detaching of a state will likely cause - * another misfit to be collected and it can then be removed. */ -void FsmAp::removeMisfits() -{ - while ( misfitList.length() > 0 ) { - /* Get the first state. */ - StateAp *state = misfitList.head; - - /* Detach and delete. */ - detachState( state ); - - /* The state was previously on the misfit list and detaching can only - * remove in transitions so the state must still be on the misfit - * list. */ - misfitList.detach( state ); - delete state; - } -} - -/* Fuse src into dest because they have been deemed equivalent states. - * Involves moving transitions into src to go into dest and invoking - * callbacks. Src is deleted detached from the graph and deleted. */ -void FsmAp::fuseEquivStates( StateAp *dest, StateAp *src ) -{ - /* This would get ugly. */ - assert( dest != src ); - - /* Cur is a duplicate. We can merge it with trail. */ - moveInwardTrans( dest, src ); - - detachState( src ); - stateList.detach( src ); - delete src; -} - -void FsmAp::fuseUnmarkedPairs( MarkIndex &markIndex ) -{ - StateAp *p = stateList.head, *nextP, *q; - - /* Definition: The primary state of an equivalence class is the first state - * encounterd that belongs to the equivalence class. All equivalence - * classes have primary state including equivalence classes with one state - * in it. */ - - /* For each unmarked pair merge p into q and delete p. q is always the - * primary state of it's equivalence class. We wouldn't have landed on it - * here if it were not, because it would have been deleted. - * - * Proof that q is the primaray state of it's equivalence class: Assume q - * is not the primary state of it's equivalence class, then it would be - * merged into some state that came before it and thus p would be - * equivalent to that state. But q is the first state that p is equivalent - * to so we have a contradiction. */ - - /* Walk all unordered pairs of (p, q) where p != q. - * The second depth of the walk stops before reaching p. This - * gives us all unordered pairs of states (p, q) where p != q. */ - while ( p != 0 ) { - nextP = p->next; - - q = stateList.head; - while ( q != p ) { - /* If one of p or q is a final state then mark. */ - if ( ! markIndex.isPairMarked( p->alg.stateNum, q->alg.stateNum ) ) { - fuseEquivStates( q, p ); - break; - } - q = q->next; - } - p = nextP; - } -} - -void FsmAp::fusePartitions( MinPartition *parts, int numParts ) -{ - /* For each partition, fuse state 2, 3, ... into state 1. */ - for ( int p = 0; p < numParts; p++ ) { - /* Assume that there will always be at least one state. */ - StateAp *first = parts[p].list.head, *toFuse = first->next; - - /* Put the first state back onto the main state list. Don't bother - * removing it from the partition list first. */ - stateList.append( first ); - - /* Fuse the rest of the state into the first. */ - while ( toFuse != 0 ) { - /* Save the next. We will trash it before it is needed. */ - StateAp *next = toFuse->next; - - /* Put the state to be fused in to the first back onto the main - * list before it is fuse. the graph. The state needs to be on - * the main list for the detach from the graph to work. Don't - * bother removing the state from the partition list first. We - * need not maintain it. */ - stateList.append( toFuse ); - - /* Now fuse to the first. */ - fuseEquivStates( first, toFuse ); - - /* Go to the next that we saved before trashing the next pointer. */ - toFuse = next; - } - - /* We transfered the states from the partition list into the main list without - * removing the states from the partition list first. Clean it up. */ - parts[p].list.abandon(); - } -} - -/* Merge neighboring transitions that go to the same state and have the same - * transitions data. */ -void FsmAp::compressTransitions() -{ - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->outList.length() > 1 ) { - for ( TransList::Iter trans = st->outList, next = trans.next(); next.lte(); ) { - Key nextLow = next->lowKey; - ctx->keyOps->decrement( nextLow ); - - /* Require there be no conditions in either of the merge - * candidates. */ - bool merge = false; - TransDataAp *td; - TransDataAp *tn; - - if ( trans->plain() && - next->plain() && - ctx->keyOps->eq( trans->highKey, nextLow ) ) - { - td = trans->tdap(); - tn = next->tdap(); - - /* Check the condition target and action data. */ - if ( td->toState == tn->toState && CmpActionTable::compare( - td->actionTable, tn->actionTable ) == 0 ) - { - merge = true; - } - } - - if ( merge ) { - trans->highKey = next->highKey; - st->outList.detach( tn ); - detachTrans( tn->fromState, tn->toState, tn ); - delete tn; - next = trans.next(); - } - else { - trans.increment(); - next.increment(); - } - } - } - } -} - -bool FsmAp::elimCondBits() -{ - bool modified = false; - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - restart: - for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { - if ( !trans->plain() ) { - CondSpace *cs = trans->condSpace; - - for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ ) { - long bit = 1 << csi.pos(); - - /* Sort into on and off lists. */ - CondList on; - CondList off; - TransCondAp *tcap = trans->tcap(); - while ( tcap->condList.length() > 0 ) { - CondAp *cond = tcap->condList.detachFirst(); - if ( cond->key.getVal() & bit ) { - cond->key = CondKey( cond->key.getVal() & ~bit ); - on.append( cond ); - } - else { - off.append( cond ); - } - } - - bool merge = false; - if ( on.length() > 0 && on.length() == off.length() ) { - /* test if the same */ - int cmpRes = compareCondListBitElim( on, off ); - if ( cmpRes == 0 ) - merge = true; - } - - if ( merge ) { - if ( cs->condSet.length() == 1 ) { - /* clear out the on-list. */ - while ( on.length() > 0 ) { - CondAp *cond = on.detachFirst(); - detachTrans( st, cond->toState, cond ); - } - - /* turn back into a plain transition. */ - CondAp *cond = off.detachFirst(); - TransAp *n = convertToTransAp( st, cond ); - TransAp *before = trans->prev; - st->outList.detach( trans ); - st->outList.addAfter( before, n ); - modified = true; - goto restart; - } - else - { - CondSet newSet = cs->condSet; - newSet.Vector::remove( csi.pos(), 1 ); - trans->condSpace = addCondSpace( newSet ); - - /* clear out the on-list. */ - while ( on.length() > 0 ) { - CondAp *cond = on.detachFirst(); - detachTrans( st, cond->toState, cond ); - } - } - } - - /* Turn back into a single list. */ - while ( on.length() > 0 || off.length() > 0 ) { - if ( on.length() == 0 ) { - while ( off.length() > 0 ) - tcap->condList.append( off.detachFirst() ); - } - else if ( off.length() == 0 ) { - while ( on.length() > 0 ) { - CondAp *cond = on.detachFirst(); - cond->key = CondKey( cond->key.getVal() | bit ); - tcap->condList.append( cond ); - } - } - else { - if ( off.head->key.getVal() < ( on.head->key.getVal() | bit ) ) { - tcap->condList.append( off.detachFirst() ); - } - else { - CondAp *cond = on.detachFirst(); - cond->key = CondKey( cond->key.getVal() | bit ); - tcap->condList.append( cond ); - } - } - } - - if ( merge ) { - modified = true; - goto restart; - } - } - } - } - } - return modified; -} - -/* Perform minimization after an operation according - * to the command line args. */ -void FsmAp::afterOpMinimize( bool lastInSeq ) -{ - /* Switch on the prefered minimization algorithm. */ - if ( ctx->minimizeOpt == MinimizeEveryOp || ( ctx->minimizeOpt == MinimizeMostOps && lastInSeq ) ) { - /* First clean up the graph. FsmAp operations may leave these - * lying around. There should be no dead end states. The subtract - * intersection operators are the only places where they may be - * created and those operators clean them up. */ - removeUnreachableStates(); - - switch ( ctx->minimizeLevel ) { - #ifdef TO_UPGRADE_CONDS - case MinimizeApprox: - minimizeApproximate(); - break; - #endif - case MinimizePartition1: - minimizePartition1(); - break; - case MinimizePartition2: - minimizePartition2(); - break; - #ifdef TO_UPGRADE_CONDS - case MinimizeStable: - minimizeStable(); - break; - #endif - } - } -} - diff --git a/libfsm/fsmnfa.cc b/libfsm/fsmnfa.cc deleted file mode 100644 index cde4f82d..00000000 --- a/libfsm/fsmnfa.cc +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright 2015-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "fsmgraph.h" -#include "mergesort.h" -#include "parsedata.h" - -using std::endl; - -void FsmAp::nfaFillInStates() -{ - long count = nfaList.length(); - - /* Can this lead to too many DFAs? Since the nfa merge is removing misfits, - * it is possible we remove a state that is on the nfa list, but we don't - * adjust count. */ - - /* Merge any states that are awaiting merging. This will likey cause - * other states to be added to the stfil list. */ - while ( nfaList.length() > 0 && count-- ) { - StateAp *state = nfaList.head; - - StateSet *stateSet = &state->stateDictEl->stateSet; - nfaMergeStates( state, stateSet->data, stateSet->length() ); - - for ( StateSet::Iter s = *stateSet; s.lte(); s++ ) - detachStateDict( state, *s ); - - nfaList.detach( state ); - } -} - -void FsmAp::prepareNfaRound() -{ - for ( StateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->nfaOut != 0 && ! (st->stateBits & STB_NFA_REP) ) { - StateSet set; - for ( NfaTransList::Iter to = *st->nfaOut; to.lte(); to++ ) - set.insert( to->toState ); - - st->stateDictEl = new StateDictEl( set ); - st->stateDictEl->targState = st; - stateDict.insert( st->stateDictEl ); - delete st->nfaOut; - st->nfaOut = 0; - - nfaList.append( st ); - } - } -} - -void FsmAp::finalizeNfaRound() -{ - /* For any remaining NFA states, remove from the state dict. We need to - * keep the state sets. */ - for ( NfaStateList::Iter ns = nfaList; ns.lte(); ns++ ) - stateDict.detach( ns->stateDictEl ); - - /* Disassociate non-nfa states from their state dicts. */ - for ( StateDict::Iter sdi = stateDict; sdi.lte(); sdi++ ) - sdi->targState->stateDictEl = 0; - - /* Delete the state dict elements for non-nfa states. */ - stateDict.empty(); - - /* Transfer remaining stateDictEl sets to nfaOut. */ - while ( nfaList.length() > 0 ) { - StateAp *state = nfaList.head; - state->nfaOut = new NfaTransList; - for ( StateSet::Iter ss = state->stateDictEl->stateSet; ss.lte(); ss++ ) { - /* Attach it using the NFA transitions data structure (propigates - * to output). */ - NfaTrans *trans = new NfaTrans( /* 0, 0, */ 1 ); - state->nfaOut->append( trans ); - attachToNfa( state, *ss, trans ); - - detachStateDict( state, *ss ); - } - delete state->stateDictEl; - state->stateDictEl = 0; - nfaList.detach( state ); - } -} - -void FsmAp::nfaMergeStates( StateAp *destState, - StateAp **srcStates, int numSrc ) -{ - for ( int s = 0; s < numSrc; s++ ) { - mergeStates( destState, srcStates[s] ); - - while ( misfitList.length() > 0 ) { - StateAp *state = misfitList.head; - - /* Detach and delete. */ - detachState( state ); - misfitList.detach( state ); - delete state; - } - } -} - - -/* - * WRT action ordering. - * - * All the pop restore actions get an ordering of -2 to cause them to always - * execute first. This is the action that restores the state and we need that - * to happen before any user actions. - */ -const int ORD_PUSH = 0; -const int ORD_RESTORE = -3; -const int ORD_COND = -1; -const int ORD_COND2 = -2; -const int ORD_TEST = 1073741824; - -void FsmAp::transferOutToNfaTrans( NfaTrans *trans, StateAp *state ) -{ - trans->popFrom = state->fromStateActionTable; - trans->popCondSpace = state->outCondSpace; - trans->popCondKeys = state->outCondKeys; - trans->priorTable.setPriors( state->outPriorTable ); - trans->popAction.setActions( state->outActionTable ); -} - -FsmRes FsmAp::nfaWrap( FsmAp *fsm, Action *push, Action *pop, Action *init, - Action *stay, Action *exit, NfaRepeatMode mode ) -{ - /* - * First Concat. - */ - StateSet origFinals = fsm->finStateSet; - - /* Get the orig start state. */ - StateAp *origStartState = fsm->startState; - - /* New start state. */ - StateAp *newStart = fsm->addState(); - - newStart->nfaOut = new NfaTransList; - - const int orderInit = 0; - const int orderStay = mode == NfaGreedy ? 3 : 1; - const int orderExit = mode == NfaGreedy ? 1 : 3; - - NfaTrans *trans; - if ( init ) { - /* Transition into the repetition. Doesn't make much sense to flip this - * statically false, but provided for consistency of interface. Allows - * an init so we can have only local state manipulation. */ - trans = new NfaTrans( orderInit ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, init ); - - newStart->nfaOut->append( trans ); - fsm->attachToNfa( newStart, origStartState, trans ); - } - - StateAp *newFinal = fsm->addState(); - - for ( StateSet::Iter orig = origFinals; orig.lte(); orig++ ) { - /* For every final state, we place a new final state in front of it, - * with an NFA transition to the original. This is the "stay" choice. */ - StateAp *repl = fsm->addState(); - fsm->moveInwardTrans( repl, *orig ); - - repl->nfaOut = new NfaTransList; - - if ( stay != 0 ) { - /* Transition to original final state. Represents staying. */ - trans = new NfaTrans( orderStay ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, stay ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, *orig, trans ); - } - - if ( exit != 0 ) { - /* Transition to thew new final. Represents exiting. */ - trans = new NfaTrans( orderExit ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, exit ); - - fsm->transferOutToNfaTrans( trans, *orig ); - repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, newFinal, trans ); - } - - fsm->unsetFinState( *orig ); - } - - fsm->unsetStartState(); - fsm->setStartState( newStart ); - fsm->setFinState( newFinal ); - - return FsmRes( FsmRes::Fsm(), fsm ); -} - - -FsmRes FsmAp::nfaRepeatOp2( FsmAp *fsm, Action *push, Action *pop, Action *init, - Action *stay, Action *repeat, Action *exit, NfaRepeatMode mode ) -{ - /* - * First Concat. - */ - StateSet origFinals = fsm->finStateSet; - - /* Get the orig start state. */ - StateAp *origStartState = fsm->startState; - StateAp *repStartState = fsm->dupStartState(); - - /* New start state. */ - StateAp *newStart1 = fsm->addState(); - StateAp *newStart2 = fsm->addState(); - - newStart1->nfaOut = new NfaTransList; - newStart2->nfaOut = new NfaTransList; - - const int orderInit = 0; - const int orderStay = mode == NfaGreedy ? 3 : 1; - const int orderRepeat = mode == NfaGreedy ? 2 : 2; - const int orderExit = mode == NfaGreedy ? 1 : 3; - - NfaTrans *trans; - if ( init ) { - /* Transition into the repetition. Doesn't make much sense to flip this - * statically false, but provided for consistency of interface. Allows - * an init so we can have only local state manipulation. */ - trans = new NfaTrans( orderInit ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, init ); - - newStart1->nfaOut->append( trans ); - fsm->attachToNfa( newStart1, newStart2, trans ); - } - - StateAp *newFinal = fsm->addState(); - - if ( exit ) { - trans = new NfaTrans( orderExit ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, exit ); - - newStart2->nfaOut->append( trans ); - fsm->attachToNfa( newStart1, newFinal, trans ); - } - - if ( repeat ) { - trans = new NfaTrans( orderRepeat ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, repeat ); - - newStart2->nfaOut->append( trans ); - fsm->attachToNfa( newStart1, origStartState, trans ); - } - - for ( StateSet::Iter orig = origFinals; orig.lte(); orig++ ) { - /* For every final state, we place a new final state in front of it, - * with an NFA transition to the original. This is the "stay" choice. */ - StateAp *repl = fsm->addState(); - fsm->moveInwardTrans( repl, *orig ); - - repl->nfaOut = new NfaTransList; - - if ( stay != 0 ) { - /* Transition to original final state. Represents staying. */ - trans = new NfaTrans( orderStay ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, stay ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, *orig, trans ); - } - - /* Transition back to the start. Represents repeat. */ - if ( repeat != 0 ) { - trans = new NfaTrans( orderRepeat ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, repeat ); - - fsm->transferOutToNfaTrans( trans, *orig ); - repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, repStartState, trans ); - } - - if ( exit != 0 ) { - /* Transition to thew new final. Represents exiting. */ - trans = new NfaTrans( orderExit ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, exit ); - - fsm->transferOutToNfaTrans( trans, *orig ); - repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, newFinal, trans ); - } - - fsm->unsetFinState( *orig ); - } - - fsm->unsetStartState(); - fsm->setStartState( newStart1 ); - fsm->setFinState( newFinal ); - - return FsmRes( FsmRes::Fsm(), fsm ); -} - - -/* This version contains the init, increment and test in the nfa pop actions. - * This is a compositional operator since it doesn't leave any actions to - * trailing characters, where they may interact with other actions that use the - * same variables. */ -FsmRes FsmAp::nfaRepeatOp( FsmAp *fsm, Action *push, Action *pop, Action *init, - Action *stay, Action *repeat, Action *exit ) -{ - /* - * First Concat. - */ - StateSet origFinals = fsm->finStateSet; - - /* Get the orig start state. */ - StateAp *origStartState = fsm->startState; - StateAp *repStartState = fsm->dupStartState(); - - /* New start state. */ - StateAp *newStart = fsm->addState(); - - newStart->nfaOut = new NfaTransList; - - NfaTrans *trans; - if ( init ) { - /* Transition into the repetition. Doesn't make much sense to flip this - * statically false, but provided for consistency of interface. Allows - * an init so we can have only local state manipulation. */ - trans = new NfaTrans( 1 ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, init ); - - newStart->nfaOut->append( trans ); - fsm->attachToNfa( newStart, origStartState, trans ); - } - - StateAp *newFinal = fsm->addState(); - - for ( StateSet::Iter orig = origFinals; orig.lte(); orig++ ) { - /* For every final state, we place a new final state in front of it, - * with an NFA transition to the original. This is the "stay" choice. */ - StateAp *repl = fsm->addState(); - fsm->moveInwardTrans( repl, *orig ); - - repl->nfaOut = new NfaTransList; - - const int orderStay = 3; - const int orderRepeat = 2; - const int orderExit = 1; - - if ( stay != 0 ) { - /* Transition to original final state. Represents staying. */ - trans = new NfaTrans( orderStay ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, stay ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, *orig, trans ); - } - - /* Transition back to the start. Represents repeat. */ - if ( repeat != 0 ) { - trans = new NfaTrans( orderRepeat ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, repeat ); - - fsm->transferOutToNfaTrans( trans, *orig ); - repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, repStartState, trans ); - } - - if ( exit != 0 ) { - /* Transition to thew new final. Represents exiting. */ - trans = new NfaTrans( orderExit ); - - trans->pushTable.setAction( ORD_PUSH, push ); - trans->restoreTable.setAction( ORD_RESTORE, pop ); - trans->popTest.setAction( ORD_TEST, exit ); - - fsm->transferOutToNfaTrans( trans, *orig ); - repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); - - repl->nfaOut->append( trans ); - fsm->attachToNfa( repl, newFinal, trans ); - } - - fsm->unsetFinState( *orig ); - } - - fsm->unsetStartState(); - fsm->setStartState( newStart ); - fsm->setFinState( newFinal ); - - return FsmRes( FsmRes::Fsm(), fsm ); -} - - -/* Unions others with fsm. Others are deleted. */ -FsmRes FsmAp::nfaUnionOp( FsmAp *fsm, FsmAp **others, int n, int depth, ostream &stats ) -{ - /* Mark existing NFA states as NFA_REP states, which excludes them from the - * prepare NFA round. We must treat them as final NFA states and not try to - * make them deterministic. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - if ( st->nfaOut != 0 ) - st->stateBits |= STB_NFA_REP; - } - - for ( int o = 0; o < n; o++ ) { - for ( StateList::Iter st = others[o]->stateList; st.lte(); st++ ) { - if ( st->nfaOut != 0 ) - st->stateBits |= STB_NFA_REP; - } - } - - for ( int o = 0; o < n; o++ ) - assert( fsm->ctx == others[o]->ctx ); - - /* Not doing misfit accounting here. If we wanted to, it would need to be - * made nfa-compatibile. */ - - /* Build a state set consisting of both start states */ - StateSet startStateSet; - startStateSet.insert( fsm->startState ); - for ( int o = 0; o < n; o++ ) - startStateSet.insert( others[o]->startState ); - - /* Both of the original start states loose their start state status. */ - fsm->unsetStartState(); - for ( int o = 0; o < n; o++ ) - others[o]->unsetStartState(); - - /* Bring in the rest of other's entry points. */ - for ( int o = 0; o < n; o++ ) { - fsm->copyInEntryPoints( others[o] ); - others[o]->entryPoints.empty(); - } - - for ( int o = 0; o < n; o++ ) { - /* Merge the lists. This will move all the states from other - * into this. No states will be deleted. */ - fsm->stateList.append( others[o]->stateList ); - fsm->misfitList.append( others[o]->misfitList ); - // nfaList.append( others[o]->nfaList ); - } - - for ( int o = 0; o < n; o++ ) { - /* Move the final set data from other into this. */ - fsm->finStateSet.insert( others[o]->finStateSet ); - others[o]->finStateSet.empty(); - } - - for ( int o = 0; o < n; o++ ) { - /* Since other's list is empty, we can delete the fsm without - * affecting any states. */ - delete others[o]; - } - - /* Create a new start state. */ - fsm->setStartState( fsm->addState() ); - - if ( depth == 0 ) { - fsm->startState->stateDictEl = new StateDictEl( startStateSet ); - fsm->nfaList.append( fsm->startState ); - - for ( StateSet::Iter s = startStateSet; s.lte(); s++ ) { - NfaTrans *trans = new NfaTrans( /* 0, 0, */ 0 ); - - if ( fsm->startState->nfaOut == 0 ) - fsm->startState->nfaOut = new NfaTransList; - - fsm->startState->nfaOut->append( trans ); - fsm->attachToNfa( fsm->startState, *s, trans ); - } - } - else { - /* Merge the start states. */ - if ( fsm->ctx->printStatistics ) - stats << "nfa-fill-round\t0" << endl; - - fsm->nfaMergeStates( fsm->startState, startStateSet.data, startStateSet.length() ); - - long removed = fsm->removeUnreachableStates(); - if ( fsm->ctx->printStatistics ) - stats << "round-unreach\t" << removed << endl; - - /* Fill in any new states made from merging. */ - for ( long i = 1; i < depth; i++ ) { - if ( fsm->ctx->printStatistics ) - stats << "nfa-fill-round\t" << i << endl; - - if ( fsm->nfaList.length() == 0 ) - break; - - fsm->nfaFillInStates( ); - - long removed = fsm->removeUnreachableStates(); - if ( fsm->ctx->printStatistics ) - stats << "round-unreach\t" << removed << endl; - } - - fsm->finalizeNfaRound(); - - long maxStateSetSize = 0; - long count = 0; - for ( StateList::Iter s = fsm->stateList; s.lte(); s++ ) { - if ( s->nfaOut != 0 && s->nfaOut->length() > 0 ) { - count += 1; - if ( s->nfaOut->length() > maxStateSetSize ) - maxStateSetSize = s->nfaOut->length(); - } - } - - if ( fsm->ctx->printStatistics ) { - stats << "fill-list\t" << count << endl; - stats << "state-dict\t" << fsm->stateDict.length() << endl; - stats << "states\t" << fsm->stateList.length() << endl; - stats << "max-ss\t" << maxStateSetSize << endl; - } - - fsm->removeUnreachableStates(); - - if ( fsm->ctx->printStatistics ) - stats << "post-unreachable\t" << fsm->stateList.length() << endl; - - fsm->minimizePartition2(); - - if ( fsm->ctx->printStatistics ) { - stats << "post-min\t" << fsm->stateList.length() << std::endl; - stats << std::endl; - } - } - - return FsmRes( FsmRes::Fsm(), fsm ); -} - -FsmRes FsmAp::nfaUnion( const NfaRoundVect &roundsList, - FsmAp **machines, int numMachines, - std::ostream &stats, bool printStatistics ) -{ - long sumPlain = 0, sumMin = 0; - for ( int i = 0; i < numMachines; i++ ) { - sumPlain += machines[i]->stateList.length(); - - machines[i]->removeUnreachableStates(); - machines[i]->minimizePartition2(); - - sumMin += machines[i]->stateList.length(); - } - - if ( printStatistics ) { - stats << "sum-plain\t" << sumPlain << endl; - stats << "sum-minimized\t" << sumMin << endl; - } - - /* For each round. */ - for ( NfaRoundVect::Iter r = roundsList; r.lte(); r++ ) { - - if ( printStatistics ) { - stats << "depth\t" << r->depth << endl; - stats << "grouping\t" << r->groups << endl; - } - - int numGroups = 0; - int start = 0; - while ( start < numMachines ) { - /* If nfa-group-max is zero, don't group, put all terms into a single - * n-depth NFA. */ - int amount = r->groups == 0 ? numMachines : r->groups; - if ( ( start + amount ) > numMachines ) - amount = numMachines - start; - - FsmAp **others = machines + start + 1; - FsmRes res = FsmAp::nfaUnionOp( machines[start], others, (amount - 1), r->depth, stats ); - machines[start] = res.fsm; - - start += amount; - numGroups++; - } - - if ( numGroups == 1 ) - break; - - /* Move the group starts into the groups array. */ - FsmAp **groups = new FsmAp*[numGroups]; - int g = 0; - start = 0; - while ( start < numMachines ) { - groups[g] = machines[start]; - start += r->groups == 0 ? numMachines : r->groups; - g++; - } - - delete[] machines; - machines = groups; - numMachines = numGroups; - } - - FsmAp *ret = machines[0]; - return FsmRes( FsmRes::Fsm(), ret ); -} diff --git a/libfsm/fsmstate.cc b/libfsm/fsmstate.cc deleted file mode 100644 index 03a4df34..00000000 --- a/libfsm/fsmstate.cc +++ /dev/null @@ -1,603 +0,0 @@ -/* - * Copyright 2002-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "fsmgraph.h" - -#include -#include -#include - -/* Construct a mark index for a specified number of states. Must new up - * an array that is states^2 in size. */ -MarkIndex::MarkIndex( int states ) : numStates(states) -{ - /* Total pairs is states^2. Actually only use half of these, but we allocate - * them all to make indexing into the array easier. */ - int total = states * states; - - /* New up chars so that individual DListEl constructors are - * not called. Zero out the mem manually. */ - array = new bool[total]; - memset( array, 0, sizeof(bool) * total ); -} - -/* Free the array used to store state pairs. */ -MarkIndex::~MarkIndex() -{ - delete[] array; -} - -/* Mark a pair of states. States are specified by their number. The - * marked states are moved from the unmarked list to the marked list. */ -void MarkIndex::markPair(int state1, int state2) -{ - int pos = ( state1 >= state2 ) ? - ( state1 * numStates ) + state2 : - ( state2 * numStates ) + state1; - - array[pos] = true; -} - -/* Returns true if the pair of states are marked. Returns false otherwise. - * Ordering of states given does not matter. */ -bool MarkIndex::isPairMarked(int state1, int state2) -{ - int pos = ( state1 >= state2 ) ? - ( state1 * numStates ) + state2 : - ( state2 * numStates ) + state1; - - return array[pos]; -} - -/* Create a new fsm state. State has not out transitions or in transitions, not - * out out transition data and not number. */ -StateAp::StateAp() -: - /* No out or in transitions. */ - outList(), - inTrans(), - inCond(), - - /* No EOF target. */ - eofTarget(0), - - /* No entry points, or epsilon trans. */ - entryIds(), - epsilonTrans(), - - /* No transitions in from other states. */ - foreignInTrans(0), - - /* Only used during merging. Normally null. */ - stateDictEl(0), - stateDictIn(0), - - nfaOut(0), - nfaIn(0), - - eptVect(0), - - /* No state identification bits. */ - stateBits(0), - - /* No Priority data. */ - outPriorTable(), - - /* No Action data. */ - toStateActionTable(), - fromStateActionTable(), - outActionTable(), - outCondSpace(0), - outCondKeys(), - errActionTable(), - eofActionTable(), - guardedInTable(), - lmNfaParts() -{ -} - -/* Copy everything except actual the transitions. That is left up to the - * FsmAp copy constructor. */ -StateAp::StateAp(const StateAp &other) -: - /* All lists are cleared. They will be filled in when the - * individual transitions are duplicated and attached. */ - outList(), - inTrans(), - inCond(), - - /* Set this using the original state's eofTarget. It will get mapped back - * to the new machine in the Fsm copy constructor. */ - eofTarget(other.eofTarget), - - /* Duplicate the entry id set and epsilon transitions. These - * are sets of integers and as such need no fixing. */ - entryIds(other.entryIds), - epsilonTrans(other.epsilonTrans), - - /* No transitions in from other states. */ - foreignInTrans(0), - - /* This is only used during merging. Normally null. */ - stateDictEl(0), - stateDictIn(0), - - nfaOut(0), - nfaIn(0), - - eptVect(0), - - /* Fsm state data. */ - stateBits(other.stateBits), - - /* Copy in priority data. */ - outPriorTable(other.outPriorTable), - - /* Copy in action data. */ - toStateActionTable(other.toStateActionTable), - fromStateActionTable(other.fromStateActionTable), - outActionTable(other.outActionTable), - outCondSpace(other.outCondSpace), - outCondKeys(other.outCondKeys), - errActionTable(other.errActionTable), - eofActionTable(other.eofActionTable), - - guardedInTable(other.guardedInTable), - lmNfaParts(other.lmNfaParts) -{ - /* Duplicate all the transitions. */ - for ( TransList::Iter trans = other.outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - /* Duplicate and store the orginal target in the transition. This will - * be corrected once all the states have been created. */ - TransDataAp *newTrans = new TransDataAp( *trans->tdap() ); - assert( trans->tdap()->lmActionTable.length() == 0 ); - newTrans->toState = trans->tdap()->toState; - outList.append( newTrans ); - } - else { - /* Duplicate and store the orginal target in the transition. This will - * be corrected once all the states have been created. */ - TransAp *newTrans = new TransCondAp( *trans->tcap() ); - - for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { - CondAp *newCondTrans = new CondAp( *cti, newTrans ); - newCondTrans->key = cti->key; - - newTrans->tcap()->condList.append( newCondTrans ); - - assert( cti->lmActionTable.length() == 0 ); - - newCondTrans->toState = cti->toState; - } - - outList.append( newTrans ); - } - } - - /* Dup the nfa trans. */ - if ( other.nfaOut != 0 ) { - nfaOut = new NfaTransList; - for ( NfaTransList::Iter trans = *other.nfaOut; trans.lte(); trans++ ) { - NfaTrans *newtrans = new NfaTrans( *trans ); - newtrans->toState = trans->toState; - - nfaOut->append( newtrans ); - } - } -} - -/* If there is a state dict element, then delete it. Everything else is left - * up to the FsmGraph destructor. */ -StateAp::~StateAp() -{ - if ( stateDictEl != 0 ) - delete stateDictEl; - - if ( stateDictIn != 0 ) - delete stateDictIn; - - if ( nfaIn != 0 ) - delete nfaIn; - - if ( nfaOut != 0 ) { - nfaOut->empty(); - delete nfaOut; - } -} - -#ifdef TO_UPGRADE_CONDS -/* Compare two states using pointers to the states. With the approximate - * compare, the idea is that if the compare finds them the same, they can - * immediately be merged. */ -int ApproxCompare::compare( const StateAp *state1, const StateAp *state2 ) -{ - int compareRes; - - /* Test final state status. */ - if ( (state1->stateBits & STB_ISFINAL) && !(state2->stateBits & STB_ISFINAL) ) - return -1; - else if ( !(state1->stateBits & STB_ISFINAL) && (state2->stateBits & STB_ISFINAL) ) - return 1; - - /* Test epsilon transition sets. */ - compareRes = CmpEpsilonTrans::compare( state1->epsilonTrans, - state2->epsilonTrans ); - if ( compareRes != 0 ) - return compareRes; - - /* Compare the out transitions. */ - compareRes = FsmAp::compareStateData( state1, state2 ); - if ( compareRes != 0 ) - return compareRes; - - /* Use a pair iterator to get the transition pairs. */ - RangePairIter outPair( ctx, state1->outList.head, state2->outList.head ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - - case RangePairIter::RangeInS1: - compareRes = FsmAp::compareFullPtr( outPair.s1Tel.trans, 0 ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIter::RangeInS2: - compareRes = FsmAp::compareFullPtr( 0, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIter::RangeOverlap: - compareRes = FsmAp::compareFullPtr( - outPair.s1Tel.trans, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIter::BreakS1: - case RangePairIter::BreakS2: - break; - } - } - - /* Check EOF targets. */ - if ( state1->eofTarget < state2->eofTarget ) - return -1; - else if ( state1->eofTarget > state2->eofTarget ) - return 1; - - if ( state1->guardedIn || !state2->guardedIn ) - return -1; - else if ( !state1->guardedIn || state2->guardedIn ) - return 1; - - /* Got through the entire state comparison, deem them equal. */ - return 0; -} -#endif - - -/* Compare class used in the initial partition. */ -int InitPartitionCompare::compare( const StateAp *state1, const StateAp *state2 ) -{ - int compareRes; - - if ( state1->nfaOut == 0 && state2->nfaOut != 0 ) - return -1; - else if ( state1->nfaOut != 0 && state2->nfaOut == 0 ) - return 1; - else if ( state1->nfaOut != 0 ) { - compareRes = CmpNfaTransList::compare( - *state1->nfaOut, *state2->nfaOut ); - if ( compareRes != 0 ) - return compareRes; - } - - /* Test final state status. */ - if ( (state1->stateBits & STB_ISFINAL) && !(state2->stateBits & STB_ISFINAL) ) - return -1; - else if ( !(state1->stateBits & STB_ISFINAL) && (state2->stateBits & STB_ISFINAL) ) - return 1; - - /* Test epsilon transition sets. */ - compareRes = CmpEpsilonTrans::compare( state1->epsilonTrans, - state2->epsilonTrans ); - if ( compareRes != 0 ) - return compareRes; - - /* Compare the out transitions. */ - compareRes = FsmAp::compareStateData( state1, state2 ); - if ( compareRes != 0 ) - return compareRes; - - /* Use a pair iterator to test the transition pairs. */ - typedef RangePairIter< PiList > RangePairIterPiListTransAp; - RangePairIterPiListTransAp - outPair( ctx, state1->outList, state2->outList ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - - case RangePairIterPiListTransAp::RangeInS1: - compareRes = FsmAp::compareTransDataPtr( outPair.s1Tel.trans, 0 ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIterPiListTransAp::RangeInS2: - compareRes = FsmAp::compareTransDataPtr( 0, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIterPiListTransAp::RangeOverlap: - compareRes = FsmAp::compareTransDataPtr( - outPair.s1Tel.trans, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIterPiListTransAp::BreakS1: - case RangePairIterPiListTransAp::BreakS2: - break; - } - } - - return 0; -} - -/* Compare class for the sort that does the partitioning. */ -int PartitionCompare::compare( const StateAp *state1, const StateAp *state2 ) -{ - int compareRes; - - /* Use a pair iterator to get the transition pairs. */ - typedef RangePairIter< PiList > RangePairIterPiListTransAp; - RangePairIterPiListTransAp outPair( ctx, state1->outList, state2->outList ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - - case RangePairIterPiListTransAp::RangeInS1: - compareRes = FsmAp::compareTransPartPtr( outPair.s1Tel.trans, 0 ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIterPiListTransAp::RangeInS2: - compareRes = FsmAp::compareTransPartPtr( 0, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIterPiListTransAp::RangeOverlap: - compareRes = FsmAp::compareTransPartPtr( - outPair.s1Tel.trans, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - - case RangePairIterPiListTransAp::BreakS1: - case RangePairIterPiListTransAp::BreakS2: - break; - } - } - - /* Test eof targets. */ - if ( state1->eofTarget == 0 && state2->eofTarget != 0 ) - return -1; - else if ( state1->eofTarget != 0 && state2->eofTarget == 0 ) - return 1; - else if ( state1->eofTarget != 0 ) { - /* Both eof targets are set. */ - compareRes = CmpOrd< MinPartition* >::compare( - state1->eofTarget->alg.partition, state2->eofTarget->alg.partition ); - if ( compareRes != 0 ) - return compareRes; - } - - return 0; -} - -#ifdef TO_UPGRADE_CONDS -/* Compare class for the sort that does the partitioning. */ -bool MarkCompare::shouldMark( MarkIndex &markIndex, const StateAp *state1, - const StateAp *state2 ) -{ - /* Use a pair iterator to get the transition pairs. */ - RangePairIter outPair( ctx, state1->outList.head, state2->outList.head ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - - case RangePairIter::RangeInS1: - if ( FsmAp::shouldMarkPtr( markIndex, outPair.s1Tel.trans, 0 ) ) - return true; - break; - - case RangePairIter::RangeInS2: - if ( FsmAp::shouldMarkPtr( markIndex, 0, outPair.s2Tel.trans ) ) - return true; - break; - - case RangePairIter::RangeOverlap: - if ( FsmAp::shouldMarkPtr( markIndex, - outPair.s1Tel.trans, outPair.s2Tel.trans ) ) - return true; - break; - - case RangePairIter::BreakS1: - case RangePairIter::BreakS2: - break; - } - } - - return false; -} -#endif - -/* - * Transition Comparison. - */ - -int FsmAp::comparePart( TransAp *trans1, TransAp *trans2 ) -{ - if ( trans1->plain() ) { - int compareRes = FsmAp::compareCondPartPtr( trans1->tdap(), trans2->tdap() ); - if ( compareRes != 0 ) - return compareRes; - } - else { - /* Use a pair iterator to get the transition pairs. */ - typedef ValPairIter< PiList > ValPairIterPiListCondAp; - ValPairIterPiListCondAp outPair( trans1->tcap()->condList, - trans2->tcap()->condList ); - for ( ; !outPair.end(); outPair++ ) { - switch ( outPair.userState ) { - - case ValPairIterPiListCondAp::RangeInS1: { - int compareRes = FsmAp::compareCondPartPtr( outPair.s1Tel.trans, 0 ); - if ( compareRes != 0 ) - return compareRes; - break; - } - - case ValPairIterPiListCondAp::RangeInS2: { - int compareRes = FsmAp::compareCondPartPtr( 0, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - } - - case ValPairIterPiListCondAp::RangeOverlap: { - int compareRes = FsmAp::compareCondPartPtr( - outPair.s1Tel.trans, outPair.s2Tel.trans ); - if ( compareRes != 0 ) - return compareRes; - break; - }} - } - } - - return 0; -} - -/* Compare target partitions. Either pointer may be null. */ -int FsmAp::compareTransPartPtr( TransAp *trans1, TransAp *trans2 ) -{ - if ( trans1 != 0 ) { - /* If trans1 is set then so should trans2. The initial partitioning - * guarantees this for us. */ - return comparePart( trans1, trans2 ); - } - - return 0; -} - -template< class Trans > int FsmAp::compareCondPartPtr( Trans *trans1, Trans *trans2 ) -{ - if ( trans1 != 0 ) { - /* If trans1 is set then so should trans2. The initial partitioning - * guarantees this for us. */ - if ( trans1->toState == 0 && trans2->toState != 0 ) - return -1; - else if ( trans1->toState != 0 && trans2->toState == 0 ) - return 1; - else if ( trans1->toState != 0 ) { - /* Both of targets are set. */ - return CmpOrd< MinPartition* >::compare( - trans1->toState->alg.partition, trans2->toState->alg.partition ); - } - } - return 0; -} - - -/* Compares two transition pointers according to priority and functions. - * Either pointer may be null. Does not consider to state or from state. */ -int FsmAp::compareTransDataPtr( TransAp *trans1, TransAp *trans2 ) -{ - if ( trans1 == 0 && trans2 != 0 ) - return -1; - else if ( trans1 != 0 && trans2 == 0 ) - return 1; - else if ( trans1 != 0 ) { - /* Both of the transition pointers are set. */ - int compareRes = compareTransData( trans1, trans2 ); - if ( compareRes != 0 ) - return compareRes; - } - return 0; -} - -#ifdef TO_UPGRADE_CONDS -/* Compares two transitions according to target state, priority and functions. - * Does not consider from state. Either of the pointers may be null. */ -int FsmAp::compareFullPtr( TransAp *trans1, TransAp *trans2 ) -{ - /* << "FIXME: " << __PRETTY_FUNCTION__ << std::endl; */ - - if ( (trans1 != 0) ^ (trans2 != 0) ) { - /* Exactly one of the transitions is set. */ - if ( trans1 != 0 ) - return -1; - else - return 1; - } - else if ( trans1 != 0 ) { - /* Both of the transition pointers are set. Test target state, - * priority and funcs. */ - if ( tai(trans1)->tcap()->condList.head->toState < tai(trans2)->tcap()->condList.head->toState ) - return -1; - else if ( tai(trans1)->tcap()->condList.head->toState > tai(trans2)->tcap()->condList.head->toState ) - return 1; - else if ( tai(trans1)->tcap()->condList.head->toState != 0 ) { - /* Test transition data. */ - int compareRes = compareTransData( trans1, trans2 ); - if ( compareRes != 0 ) - return compareRes; - } - } - return 0; -} -#endif - -#ifdef TO_UPGRADE_CONDS -bool FsmAp::shouldMarkPtr( MarkIndex &markIndex, TransAp *trans1, - TransAp *trans2 ) -{ - /* << "FIXME: " << __PRETTY_FUNCTION__ << std::endl; */ - - if ( (trans1 != 0) ^ (trans2 != 0) ) { - /* Exactly one of the transitions is set. The initial mark round - * should rule out this case. */ - assert( false ); - } - else if ( trans1 != 0 ) { - /* Both of the transitions are set. If the target pair is marked, then - * the pair we are considering gets marked. */ - return markIndex.isPairMarked( tai(trans1)->tcap()->condList.head->toState->alg.stateNum, - tai(trans2)->tcap()->condList.head->toState->alg.stateNum ); - } - - /* Neither of the transitiosn are set. */ - return false; -} -#endif diff --git a/libfsm/gendata.cc b/libfsm/gendata.cc deleted file mode 100644 index 4e3253ad..00000000 --- a/libfsm/gendata.cc +++ /dev/null @@ -1,1732 +0,0 @@ -/* - * Copyright 2005-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "gendata.h" -#include "ragel.h" -#include "parsedata.h" -#include "fsmgraph.h" -#include "action.h" - -#include -#include - -string itoa( int i ) -{ - char buf[16]; - sprintf( buf, "%i", i ); - return buf; -} - -void openHostBlock( char opener, InputData *id, ostream &out, const char *fileName, int line ) -{ - out << "host( \""; - for ( const char *pc = fileName; *pc != 0; pc++ ) { - if ( *pc == '\\' ) - out << "\\\\"; - else - out << *pc; - } - out << "\", " << line << " ) " << opener << "{"; -} - -void Reducer::appendTrans( TransListVect &outList, Key lowKey, - Key highKey, TransAp *trans ) -{ - if ( trans->plain() ) { - if ( trans->tdap()->toState != 0 || trans->tdap()->actionTable.length() > 0 ) - outList.append( TransEl( lowKey, highKey, trans ) ); - } - else { - /* Add once if any cond has a to-state or an action table. */ - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->toState != 0 || cond->actionTable.length() > 0 ) { - outList.append( TransEl( lowKey, highKey, trans ) ); - break; - } - } - } -} - -void Reducer::reduceActionTables() -{ - /* Reduce the actions tables to a set. */ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - RedActionTable *actionTable = 0; - - /* Reduce To State Actions. */ - if ( st->toStateActionTable.length() > 0 ) { - if ( actionTableMap.insert( st->toStateActionTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - } - - /* Reduce From State Actions. */ - if ( st->fromStateActionTable.length() > 0 ) { - if ( actionTableMap.insert( st->fromStateActionTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - } - - /* Reduce EOF actions. */ - if ( st->eofActionTable.length() > 0 ) { - if ( actionTableMap.insert( st->eofActionTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - } - - /* Loop the transitions and reduce their actions. */ - for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { - if ( trans->plain() ) { - if ( trans->tdap()->actionTable.length() > 0 ) { - if ( actionTableMap.insert( trans->tdap()->actionTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - } - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - if ( cond->actionTable.length() > 0 ) { - if ( actionTableMap.insert( cond->actionTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - } - } - } - } - - if ( st->nfaOut != 0 ) { - for ( NfaTransList::Iter n = *st->nfaOut; n.lte(); n++ ) { - if ( actionTableMap.insert( n->pushTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - - if ( actionTableMap.insert( n->restoreTable, &actionTable ) ) - actionTable->id = nextActionTableId++; - - if ( actionTableMap.insert( n->popAction, &actionTable ) ) - actionTable->id = nextActionTableId++; - - if ( actionTableMap.insert( n->popTest, &actionTable ) ) - actionTable->id = nextActionTableId++; - } - } - } -} - - -void Reducer::makeText( GenInlineList *outList, InlineItem *item ) -{ - GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), GenInlineItem::Text ); - inlineItem->data = item->data; - - outList->append( inlineItem ); -} - -void Reducer::makeTargetItem( GenInlineList *outList, NameInst *nameTarg, - GenInlineItem::Type type ) -{ - long targetState; - if ( fsmCtx->generatingSectionSubset ) - targetState = -1; - else { - EntryMapEl *targ = fsm->entryPoints.find( nameTarg->id ); - targetState = targ->value->alg.stateNum; - } - - /* Make the item. */ - GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), type ); - inlineItem->targId = targetState; - outList->append( inlineItem ); -} - - -void Reducer::makeSubList( GenInlineList *outList, const InputLoc &loc, - InlineList *inlineList, GenInlineItem::Type type ) -{ - /* Fill the sub list. */ - GenInlineList *subList = new GenInlineList; - makeGenInlineList( subList, inlineList ); - - /* Make the item. */ - GenInlineItem *inlineItem = new GenInlineItem( loc, type ); - inlineItem->children = subList; - outList->append( inlineItem ); -} - -/* Make a sublist item with a given type. */ -void Reducer::makeSubList( GenInlineList *outList, - InlineList *inlineList, GenInlineItem::Type type ) -{ - makeSubList( outList, InputLoc(), inlineList, type ); -} - -void Reducer::makeLmOnLast( GenInlineList *outList, InlineItem *item ) -{ - makeSetTokend( outList, 1 ); - - if ( item->longestMatchPart->action != 0 ) { - Action *action = item->longestMatchPart->action; - makeSubList( outList, action->loc, action->inlineList, - GenInlineItem::HostStmt ); - } -} - -void Reducer::makeLmOnNext( GenInlineList *outList, InlineItem *item ) -{ - makeSetTokend( outList, 0 ); - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmHold ) ); - - if ( item->longestMatchPart->action != 0 ) { - Action *action = item->longestMatchPart->action; - makeSubList( outList, action->loc, action->inlineList, - GenInlineItem::HostStmt ); - } -} - -void Reducer::makeExecGetTokend( GenInlineList *outList ) -{ - /* Make the Exec item. */ - GenInlineItem *execItem = new GenInlineItem( InputLoc(), GenInlineItem::LmExec ); - execItem->children = new GenInlineList; - - /* Make the GetTokEnd */ - GenInlineItem *getTokend = new GenInlineItem( InputLoc(), GenInlineItem::LmGetTokEnd ); - execItem->children->append( getTokend ); - - outList->append( execItem ); -} - -void Reducer::makeLmOnLagBehind( GenInlineList *outList, InlineItem *item ) -{ - /* Jump to the tokend. */ - makeExecGetTokend( outList ); - - if ( item->longestMatchPart->action != 0 ) { - Action *action = item->longestMatchPart->action; - makeSubList( outList, action->loc, action->inlineList, - GenInlineItem::HostStmt ); - } -} - -void Reducer::makeLmSwitch( GenInlineList *outList, InlineItem *item ) -{ - GenInlineItem *lmSwitch = new GenInlineItem( InputLoc(), GenInlineItem::LmSwitch ); - GenInlineList *lmList = lmSwitch->children = new GenInlineList; - FsmLongestMatch *longestMatch = item->longestMatch; - - /* We can't put the here because we may need to handle the error - * case and in that case p should not be changed. Instead use a default - * label in the switch to adjust p when user actions are not set. An id of - * -1 indicates the default. */ - - if ( longestMatch->lmSwitchHandlesError ) { - /* If the switch handles error then we should have also forced the - * error state. */ - assert( fsm->errState != 0 ); - - GenInlineItem *errCase = new GenInlineItem( InputLoc(), GenInlineItem::HostStmt ); - errCase->lmId = 0; - errCase->children = new GenInlineList; - - GenInlineItem *host = new GenInlineItem( item->loc, GenInlineItem::HostStmt ); - host->children = new GenInlineList; - errCase->children->append( host ); - - /* Make the item. This should probably be an LM goto, would eliminate - * need for wrapping in host statement. .*/ - GenInlineItem *gotoItem = new GenInlineItem( InputLoc(), GenInlineItem::Goto ); - gotoItem->targId = fsm->errState->alg.stateNum; - host->children->append( gotoItem ); - - lmList->append( errCase ); - } - - bool needDefault = false; - for ( FsmLmPartList::Iter lmi = *longestMatch->longestMatchList; lmi.lte(); lmi++ ) { - if ( lmi->inLmSelect ) { - if ( lmi->action == 0 ) - needDefault = true; - else { - /* Open the action. Write it with the context that sets up _p - * when doing control flow changes from inside the machine. */ - GenInlineItem *lmCase = new GenInlineItem( InputLoc(), GenInlineItem::LmCase ); - lmCase->lmId = lmi->longestMatchId; - lmCase->children = new GenInlineList; - - makeExecGetTokend( lmCase->children ); - - GenInlineItem *subHost = new GenInlineItem( lmi->action->loc, - GenInlineItem::HostStmt ); - subHost->children = new GenInlineList; - makeGenInlineList( subHost->children, lmi->action->inlineList ); - lmCase->children->append( subHost ); - - lmList->append( lmCase ); - } - } - } - - if ( needDefault ) { - GenInlineItem *defCase = new GenInlineItem( item->loc, GenInlineItem::HostStmt ); - defCase->lmId = -1; - defCase->children = new GenInlineList; - - makeExecGetTokend( defCase->children ); - - lmList->append( defCase ); - } - - outList->append( lmSwitch ); -} - -void Reducer::makeLmNfaOnNext( GenInlineList *outList, InlineItem *item ) -{ - makeSetTokend( outList, 0 ); - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmHold ) ); - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::NfaClear ) ); - - if ( item->longestMatchPart->action != 0 ) { - Action *action = item->longestMatchPart->action; - makeSubList( outList, action->loc, action->inlineList, - GenInlineItem::HostStmt ); - } -} - -void Reducer::makeLmNfaOnEof( GenInlineList *outList, InlineItem *item ) -{ - makeSetTokend( outList, 0 ); - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::NfaClear ) ); - - if ( item->longestMatchPart->action != 0 ) { - Action *action = item->longestMatchPart->action; - makeSubList( outList, action->loc, action->inlineList, - GenInlineItem::HostStmt ); - } -} - -void Reducer::makeLmNfaOnLast( GenInlineList *outList, InlineItem *item ) -{ - makeSetTokend( outList, 1 ); - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::NfaClear ) ); - - if ( item->longestMatchPart->action != 0 ) { - Action *action = item->longestMatchPart->action; - makeSubList( outList, action->loc, action->inlineList, - GenInlineItem::HostStmt ); - } -} - - -void Reducer::makeSetTokend( GenInlineList *outList, long offset ) -{ - GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), GenInlineItem::LmSetTokEnd ); - inlineItem->offset = offset; - outList->append( inlineItem ); -} - -void Reducer::makeSetAct( GenInlineList *outList, long lmId ) -{ - GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), GenInlineItem::LmSetActId ); - inlineItem->lmId = lmId; - outList->append( inlineItem ); -} - -void Reducer::makeGenInlineList( GenInlineList *outList, InlineList *inList ) -{ - for ( InlineList::Iter item = *inList; item.lte(); item++ ) { - switch ( item->type ) { - case InlineItem::Text: - makeText( outList, item ); - break; - case InlineItem::Goto: - makeTargetItem( outList, item->nameTarg, GenInlineItem::Goto ); - break; - case InlineItem::GotoExpr: - makeSubList( outList, item->children, GenInlineItem::GotoExpr ); - break; - case InlineItem::Call: - makeTargetItem( outList, item->nameTarg, GenInlineItem::Call ); - break; - case InlineItem::CallExpr: - makeSubList( outList, item->children, GenInlineItem::CallExpr ); - break; - case InlineItem::Ncall: - makeTargetItem( outList, item->nameTarg, GenInlineItem::Ncall ); - break; - case InlineItem::NcallExpr: - makeSubList( outList, item->children, GenInlineItem::NcallExpr ); - break; - case InlineItem::Next: - makeTargetItem( outList, item->nameTarg, GenInlineItem::Next ); - break; - case InlineItem::NextExpr: - makeSubList( outList, item->children, GenInlineItem::NextExpr ); - break; - case InlineItem::Break: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Break ) ); - break; - case InlineItem::Nbreak: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Nbreak ) ); - break; - case InlineItem::Ret: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Ret ) ); - break; - case InlineItem::Nret: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Nret ) ); - break; - case InlineItem::PChar: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::PChar ) ); - break; - case InlineItem::Char: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Char ) ); - break; - case InlineItem::Curs: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Curs ) ); - break; - case InlineItem::Targs: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Targs ) ); - break; - case InlineItem::Entry: - makeTargetItem( outList, item->nameTarg, GenInlineItem::Entry ); - break; - - case InlineItem::Hold: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Hold ) ); - break; - case InlineItem::Exec: - makeSubList( outList, item->children, GenInlineItem::Exec ); - break; - - case InlineItem::LmSetActId: - makeSetAct( outList, item->longestMatchPart->longestMatchId ); - break; - case InlineItem::LmSetTokEnd: - makeSetTokend( outList, 1 ); - break; - - case InlineItem::LmOnLast: - makeLmOnLast( outList, item ); - break; - case InlineItem::LmOnNext: - makeLmOnNext( outList, item ); - break; - case InlineItem::LmOnLagBehind: - makeLmOnLagBehind( outList, item ); - break; - case InlineItem::LmSwitch: - makeLmSwitch( outList, item ); - break; - - case InlineItem::LmNfaOnLast: - makeLmNfaOnLast( outList, item ); - break; - case InlineItem::LmNfaOnNext: - makeLmNfaOnNext( outList, item ); - break; - case InlineItem::LmNfaOnEof: - makeLmNfaOnEof( outList, item ); - break; - - case InlineItem::LmInitAct: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmInitAct ) ); - break; - case InlineItem::LmInitTokStart: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmInitTokStart ) ); - break; - case InlineItem::LmSetTokStart: - outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmSetTokStart ) ); - hasLongestMatch = true; - break; - case InlineItem::Stmt: - makeSubList( outList, item->children, GenInlineItem::GenStmt ); - break; - case InlineItem::Subst: { - /* Find the subst action. */ - Action *subst = curInlineAction->argList->data[item->substPos]; - makeGenInlineList( outList, subst->inlineList ); - break; - } - case InlineItem::NfaWrapAction: { - GenAction *wrap = allActions + item->wrappedAction->actionId; - GenInlineItem *gii = new GenInlineItem( InputLoc(), - GenInlineItem::NfaWrapAction ); - gii->wrappedAction = wrap; - outList->append( gii ); - break; - } - case InlineItem::NfaWrapConds: { - GenCondSpace *condSpace = allCondSpaces + item->condSpace->condSpaceId; - - GenInlineItem *gii = new GenInlineItem( InputLoc(), - GenInlineItem::NfaWrapConds ); - gii->condSpace = condSpace; - gii->condKeySet = item->condKeySet; - outList->append( gii ); - break; - }} - } -} - -void Reducer::makeExports() -{ - for ( ExportList::Iter exp = fsmCtx->exportList; exp.lte(); exp++ ) - exportList.append( new Export( exp->name, exp->key ) ); -} - -void Reducer::makeAction( Action *action ) -{ - GenInlineList *genList = new GenInlineList; - - curInlineAction = action; - makeGenInlineList( genList, action->inlineList ); - curInlineAction = 0; - - newAction( curAction++, action->name, action->loc, genList ); -} - - -void Reducer::makeActionList() -{ - /* Determine which actions to write. */ - int nextActionId = 0; - for ( ActionList::Iter act = fsmCtx->actionList; act.lte(); act++ ) { - if ( act->numRefs() > 0 || act->numCondRefs > 0 ) - act->actionId = nextActionId++; - } - - /* Write the list. */ - initActionList( nextActionId ); - curAction = 0; - - for ( ActionList::Iter act = fsmCtx->actionList; act.lte(); act++ ) { - if ( act->actionId >= 0 ) - makeAction( act ); - } -} - -void Reducer::makeActionTableList() -{ - /* Must first order the action tables based on their id. */ - int numTables = nextActionTableId; - RedActionTable **tables = new RedActionTable*[numTables]; - for ( ActionTableMap::Iter at = actionTableMap; at.lte(); at++ ) - tables[at->id] = at; - - initActionTableList( numTables ); - curActionTable = 0; - - for ( int t = 0; t < numTables; t++ ) { - long length = tables[t]->key.length(); - - /* Collect the action table. */ - RedAction *redAct = allActionTables + curActionTable; - redAct->actListId = curActionTable; - redAct->key.setAsNew( length ); - - for ( ActionTable::Iter atel = tables[t]->key; atel.lte(); atel++ ) { - redAct->key[atel.pos()].key = 0; - redAct->key[atel.pos()].value = allActions + - atel->value->actionId; - } - - /* Insert into the action table map. */ - redFsm->actionMap.insert( redAct ); - - curActionTable += 1; - } - - delete[] tables; -} - -void Reducer::makeConditions() -{ - if ( fsm->ctx->condData->condSpaceMap.length() > 0 ) { - /* Allocate condition space ids. */ - long nextCondSpaceId = 0; - for ( CondSpaceMap::Iter cs = fsm->ctx->condData->condSpaceMap; cs.lte(); cs++ ) - cs->condSpaceId = nextCondSpaceId++; - - /* Allocate the array of conditions and put them on the list. */ - long length = fsm->ctx->condData->condSpaceMap.length(); - allCondSpaces = new GenCondSpace[length]; - for ( long c = 0; c < length; c++ ) - condSpaceList.append( &allCondSpaces[c] ); - - long curCondSpace = 0; - for ( CondSpaceMap::Iter cs = fsm->ctx->condData->condSpaceMap; cs.lte(); cs++ ) { - /* Transfer the id. */ - allCondSpaces[curCondSpace].condSpaceId = cs->condSpaceId; - - curCondSpace += 1; - } - } - - makeActionList(); - makeActionTableList(); - - if ( fsm->ctx->condData->condSpaceMap.length() > 0 ) { - long curCondSpace = 0; - for ( CondSpaceMap::Iter cs = fsm->ctx->condData->condSpaceMap; cs.lte(); cs++ ) { - for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ ) - condSpaceItem( curCondSpace, (*csi)->actionId ); - curCondSpace += 1; - } - } -} - -bool Reducer::makeNameInst( std::string &res, NameInst *nameInst ) -{ - bool written = false; - if ( nameInst->parent != 0 ) - written = makeNameInst( res, nameInst->parent ); - - if ( !nameInst->name.empty() ) { - if ( written ) - res += '_'; - res += nameInst->name; - written = true; - } - - return written; -} - -void Reducer::makeEntryPoints() -{ - /* List of entry points other than start state. */ - if ( fsm->entryPoints.length() > 0 || fsmCtx->lmRequiresErrorState ) { - if ( fsmCtx->lmRequiresErrorState ) - setForcedErrorState(); - - for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { - /* Get the name instantiation from nameIndex. */ - NameInst *nameInst = fsmCtx->nameIndex[en->key]; - std::string name; - makeNameInst( name, nameInst ); - StateAp *state = en->value; - addEntryPoint( strdup(name.c_str()), state->alg.stateNum ); - } - } -} - -void Reducer::makeStateActions( StateAp *state ) -{ - RedActionTable *toStateActions = 0; - if ( state->toStateActionTable.length() > 0 ) - toStateActions = actionTableMap.find( state->toStateActionTable ); - - RedActionTable *fromStateActions = 0; - if ( state->fromStateActionTable.length() > 0 ) - fromStateActions = actionTableMap.find( state->fromStateActionTable ); - - if ( toStateActions != 0 || fromStateActions != 0 ) { - long to = -1; - if ( toStateActions != 0 ) - to = toStateActions->id; - - long from = -1; - if ( fromStateActions != 0 ) - from = fromStateActions->id; - - setStateActions( curState, to, from, -1 ); - } -} - -void Reducer::makeTrans( Key lowKey, Key highKey, TransAp *trans ) -{ - RedCondEl *outConds; - int numConds; - - assert( ( allStates + curState ) != redFsm->errState ); - - if ( trans->plain() ) { - long targ = -1; - long action = -1; - - /* First reduce the action. */ - RedActionTable *actionTable = 0; - if ( trans->tdap()->actionTable.length() > 0 ) - actionTable = actionTableMap.find( trans->tdap()->actionTable ); - - if ( trans->tdap()->toState != 0 ) - targ = trans->tdap()->toState->alg.stateNum; - - if ( actionTable != 0 ) - action = actionTable->id; - - /* Make the new transitions. */ - RedStateAp *targState = targ >= 0 ? (allStates + targ) : redFsm->getErrorState(); - RedAction *at = action >= 0 ? (allActionTables + action) : 0; - - RedTransAp *trans = redFsm->allocateTrans( targState, at ); - newTrans( allStates + curState, lowKey, highKey, trans ); - } - else { - numConds = trans->tcap()->condList.length(); - outConds = new RedCondEl[numConds]; - int pos = 0; - for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++, pos++ ) { - long targ = -1; - long action = -1; - - /* First reduce the action. */ - RedActionTable *actionTable = 0; - if ( cti->actionTable.length() > 0 ) - actionTable = actionTableMap.find( cti->actionTable ); - - if ( cti->toState != 0 ) - targ = cti->toState->alg.stateNum; - - if ( actionTable != 0 ) - action = actionTable->id; - - /* Make the new transitions. */ - RedStateAp *targState = targ >= 0 ? (allStates + targ) : redFsm->getErrorState(); - RedAction *at = action >= 0 ? (allActionTables + action) : 0; - RedCondAp *cond = redFsm->allocateCond( targState, at ); - - outConds[pos].key = cti->key; - outConds[pos].value = cond; - } - - GenCondSpace *condSpace = allCondSpaces + trans->condSpace->condSpaceId; - - /* If the cond list is not full then we need an error cond. */ - RedCondAp *errCond = 0; - if ( numConds < ( 1 << condSpace->condSet.length() ) ) - errCond = redFsm->getErrorCond(); - - RedTransAp *trans = redFsm->allocateTrans( - condSpace, outConds, numConds, errCond ); - - newTrans( allStates + curState, lowKey, highKey, trans ); - } -} - -void Reducer::makeEofTrans( StateAp *state ) -{ - /* EOF actions go out here only if the state has no eof target. If it has - * an eof target then an eof transition will be used instead. */ - RedActionTable *eofActions = 0; - if ( state->eofActionTable.length() > 0 ) - eofActions = actionTableMap.find( state->eofActionTable ); - - /* Add an EOF transition if we have conditions, a target, or actions, */ - if ( state->outCondSpace != 0 || state->eofTarget != 0 || eofActions != 0 ) - redFsm->bAnyEofActivity = true; - - long targ = state->alg.stateNum; - long action = -1; - - if ( state->eofTarget != 0 ) - targ = state->eofTarget->alg.stateNum; - - if ( eofActions != 0 ) - action = eofActions->id; - - - if ( state->outCondSpace == 0 ) { - // std::cerr << "setEofTrans( " << - // state->alg.stateNum << ", " << targ << ", " << action << " );" << endl; - - setEofTrans( state->alg.stateNum, targ, action ); - } - else { - int numConds = state->outCondKeys.length(); - RedCondEl *outConds = new RedCondEl[numConds]; - for ( int pos = 0; pos < numConds; pos++ ) { - /* Make the new transitions. */ - RedStateAp *targState = targ >= 0 ? (allStates + targ) : redFsm->getErrorState(); - RedAction *at = action >= 0 ? (allActionTables + action) : 0; - RedCondAp *cond = redFsm->allocateCond( targState, at ); - - outConds[pos].key = state->outCondKeys[pos]; - outConds[pos].value = cond; - } - - GenCondSpace *condSpace = allCondSpaces + state->outCondSpace->condSpaceId; - - /* If the cond list is not full then we need an error cond. */ - RedCondAp *errCond = 0; - if ( numConds < ( 1 << condSpace->condSet.length() ) ) - errCond = redFsm->getErrorCond(); - - setEofTrans( state->alg.stateNum, condSpace, outConds, numConds, errCond ); - } -} - - -void Reducer::makeTransList( StateAp *state ) -{ - TransListVect outList; - - /* If there is only are no ranges the task is simple. */ - if ( state->outList.length() > 0 ) { - /* Loop each source range. */ - for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { - /* Reduce the transition. If it reduced to anything then add it. */ - appendTrans( outList, trans->lowKey, trans->highKey, trans ); - } - } - - initTransList( curState, outList.length() ); - - for ( TransListVect::Iter tvi = outList; tvi.lte(); tvi++ ) - makeTrans( tvi->lowKey, tvi->highKey, tvi->value ); - - finishTransList( curState ); -} - -void Reducer::makeStateList() -{ - /* Write the list of states. */ - long length = fsm->stateList.length(); - initStateList( length ); - curState = 0; - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - makeStateActions( st ); - makeEofTrans( st ); - makeTransList( st ); - - long id = st->alg.stateNum; - setId( curState, id ); - - if ( st->isFinState() ) - setFinal( curState ); - - if ( st->nfaOut != 0 ) { - RedStateAp *from = allStates + curState; - from->nfaTargs = new RedNfaTargs; - for ( NfaTransList::Iter targ = *st->nfaOut; targ.lte(); targ++ ) { - RedStateAp *rtarg = allStates + targ->toState->alg.stateNum; - - RedAction *pushRa = 0; - RedAction *popTestRa = 0; - - if ( targ->pushTable.length() > 0 ) { - RedActionTable *pushActions = - actionTableMap.find( targ->pushTable ); - pushRa = allActionTables + pushActions->id; - } - - if ( targ->popTest.length() > 0 ) { - RedActionTable *popActions = - actionTableMap.find( targ->popTest ); - popTestRa = allActionTables + popActions->id; - } - - - from->nfaTargs->append( RedNfaTarg( rtarg, pushRa, - popTestRa, targ->order ) ); - - MergeSort sort; - sort.sort( from->nfaTargs->data, from->nfaTargs->length() ); - } - } - - curState += 1; - } -} - -void Reducer::makeMachine() -{ - createMachine(); - - /* Action tables. */ - reduceActionTables(); - - makeConditions(); - - /* Start State. */ - setStartState( fsm->startState->alg.stateNum ); - - /* Error state. */ - if ( fsm->errState != 0 ) - setErrorState( fsm->errState->alg.stateNum ); - - makeEntryPoints(); - makeStateList(); - - resolveTargetStates(); -} - -void Reducer::make( const HostLang *hostLang, const HostType *alphType ) -{ - /* Alphabet type. */ - setAlphType( hostLang, alphType->internalName ); - - /* Getkey expression. */ - if ( fsmCtx->getKeyExpr != 0 ) { - getKeyExpr = new GenInlineList; - makeGenInlineList( getKeyExpr, fsmCtx->getKeyExpr ); - } - - /* Access expression. */ - if ( fsmCtx->accessExpr != 0 ) { - accessExpr = new GenInlineList; - makeGenInlineList( accessExpr, fsmCtx->accessExpr ); - } - - /* PrePush expression. */ - if ( fsmCtx->prePushExpr != 0 ) { - GenInlineList *il = new GenInlineList; - makeGenInlineList( il, fsmCtx->prePushExpr->inlineList ); - prePushExpr = new GenInlineExpr( fsmCtx->prePushExpr->loc, il ); - } - - /* PostPop expression. */ - if ( fsmCtx->postPopExpr != 0 ) { - GenInlineList *il = new GenInlineList; - makeGenInlineList( il, fsmCtx->postPopExpr->inlineList ); - postPopExpr = new GenInlineExpr( fsmCtx->postPopExpr->loc, il ); - } - - /* PrePush expression. */ - if ( fsmCtx->nfaPrePushExpr != 0 ) { - GenInlineList *il = new GenInlineList; - makeGenInlineList( il, fsmCtx->nfaPrePushExpr->inlineList ); - nfaPrePushExpr = new GenInlineExpr( fsmCtx->nfaPrePushExpr->loc, il ); - } - - /* PostPop expression. */ - if ( fsmCtx->nfaPostPopExpr != 0 ) { - GenInlineList *il = new GenInlineList; - makeGenInlineList( il, fsmCtx->nfaPostPopExpr->inlineList ); - nfaPostPopExpr = new GenInlineExpr( fsmCtx->nfaPostPopExpr->loc, il ); - } - - - /* - * Variable expressions. - */ - - if ( fsmCtx->pExpr != 0 ) { - pExpr = new GenInlineList; - makeGenInlineList( pExpr, fsmCtx->pExpr ); - } - - if ( fsmCtx->peExpr != 0 ) { - peExpr = new GenInlineList; - makeGenInlineList( peExpr, fsmCtx->peExpr ); - } - - if ( fsmCtx->eofExpr != 0 ) { - eofExpr = new GenInlineList; - makeGenInlineList( eofExpr, fsmCtx->eofExpr ); - } - - if ( fsmCtx->csExpr != 0 ) { - csExpr = new GenInlineList; - makeGenInlineList( csExpr, fsmCtx->csExpr ); - } - - if ( fsmCtx->topExpr != 0 ) { - topExpr = new GenInlineList; - makeGenInlineList( topExpr, fsmCtx->topExpr ); - } - - if ( fsmCtx->stackExpr != 0 ) { - stackExpr = new GenInlineList; - makeGenInlineList( stackExpr, fsmCtx->stackExpr ); - } - - if ( fsmCtx->actExpr != 0 ) { - actExpr = new GenInlineList; - makeGenInlineList( actExpr, fsmCtx->actExpr ); - } - - if ( fsmCtx->tokstartExpr != 0 ) { - tokstartExpr = new GenInlineList; - makeGenInlineList( tokstartExpr, fsmCtx->tokstartExpr ); - } - - if ( fsmCtx->tokendExpr != 0 ) { - tokendExpr = new GenInlineList; - makeGenInlineList( tokendExpr, fsmCtx->tokendExpr ); - } - - if ( fsmCtx->dataExpr != 0 ) { - dataExpr = new GenInlineList; - makeGenInlineList( dataExpr, fsmCtx->dataExpr ); - } - - makeExports(); - makeMachine(); - - /* Do this before distributing transitions out to singles and defaults - * makes life easier. */ - redFsm->maxKey = findMaxKey(); - - redFsm->assignActionLocs(); - - /* Find the first final state (The final state with the lowest id). */ - redFsm->findFirstFinState(); -} - -void Reducer::createMachine() -{ - redFsm = new RedFsmAp( fsm->ctx, machineId ); -} - -void Reducer::initActionList( unsigned long length ) -{ - allActions = new GenAction[length]; - for ( unsigned long a = 0; a < length; a++ ) - actionList.append( allActions+a ); -} - -void Reducer::newAction( int anum, std::string name, - const InputLoc &loc, GenInlineList *inlineList ) -{ - allActions[anum].actionId = anum; - allActions[anum].name = name; - allActions[anum].loc = loc; - allActions[anum].inlineList = inlineList; -} - -void Reducer::initActionTableList( unsigned long length ) -{ - allActionTables = new RedAction[length]; -} - -void Reducer::initStateList( unsigned long length ) -{ - redFsm->allStates = allStates = new RedStateAp[length]; - for ( unsigned long s = 0; s < length; s++ ) - redFsm->stateList.append( allStates+s ); - - /* We get the start state as an offset, set the pointer now. */ - if ( startState >= 0 ) - redFsm->startState = allStates + startState; - if ( errState >= 0 ) - redFsm->errState = allStates + errState; - for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ ) - redFsm->entryPoints.insert( allStates + *en ); - - /* The nextStateId is no longer used to assign state ids (they come in set - * from the frontend now), however generation code still depends on it. - * Should eventually remove this variable. */ - redFsm->nextStateId = redFsm->stateList.length(); -} - -void Reducer::setStartState( unsigned long _startState ) -{ - startState = _startState; -} - -void Reducer::setErrorState( unsigned long _errState ) -{ - errState = _errState; -} - -void Reducer::addEntryPoint( char *name, unsigned long entryState ) -{ - entryPointIds.append( entryState ); - entryPointNames.append( name ); -} - -void Reducer::initTransList( int snum, unsigned long length ) -{ - /* Could preallocate the out range to save time growing it. For now do - * nothing. */ -} - -void Reducer::newTrans( RedStateAp *state, Key lowKey, Key highKey, RedTransAp *trans ) -{ - /* Get the current state and range. */ - RedTransList &destRange = state->outRange; - - /* Reduced machines are complete. We need to fill any gaps with the error - * transitions. */ - if ( destRange.length() == 0 ) { - /* Range is currently empty. */ - if ( keyOps->lt( keyOps->minKey, lowKey ) ) { - /* The first range doesn't start at the low end. */ - Key fillHighKey = lowKey; - keyOps->decrement( fillHighKey ); - - /* Create the filler with the state's error transition. */ - RedTransEl newTel( fsm->ctx->keyOps->minKey, fillHighKey, - redFsm->getErrorTrans() ); - destRange.append( newTel ); - } - } - else { - /* The range list is not empty, get the the last range. */ - RedTransEl *last = &destRange[destRange.length()-1]; - Key nextKey = last->highKey; - keyOps->increment( nextKey ); - if ( keyOps->lt( nextKey, lowKey ) ) { - /* There is a gap to fill. Make the high key. */ - Key fillHighKey = lowKey; - keyOps->decrement( fillHighKey ); - - /* Create the filler with the state's error transtion. */ - RedTransEl newTel( nextKey, fillHighKey, redFsm->getErrorTrans() ); - destRange.append( newTel ); - } - } - - /* Filler taken care of. Append the range. */ - destRange.append( RedTransEl( lowKey, highKey, trans ) ); -} - -void Reducer::finishTransList( int snum ) -{ - /* Get the current state and range. */ - RedStateAp *curState = allStates + snum; - RedTransList &destRange = curState->outRange; - - if ( curState == redFsm->errState ) - return; - - /* We may need filler on the end. */ - /* Check if there are any ranges already. */ - if ( destRange.length() == 0 ) { - /* Fill with the whole alphabet. */ - /* Add the range on the lower and upper bound. */ - RedTransEl newTel( fsm->ctx->keyOps->minKey, - fsm->ctx->keyOps->maxKey, redFsm->getErrorTrans() ); - destRange.append( newTel ); - } - else { - /* Get the last and check for a gap on the end. */ - RedTransEl *last = &destRange[destRange.length()-1]; - if ( keyOps->lt( last->highKey, fsm->ctx->keyOps->maxKey ) ) { - /* Make the high key. */ - Key fillLowKey = last->highKey; - keyOps->increment( fillLowKey ); - - /* Create the new range with the error trans and append it. */ - RedTransEl newTel( fillLowKey, fsm->ctx->keyOps->maxKey, - redFsm->getErrorTrans() ); - destRange.append( newTel ); - } - } -} - -void Reducer::setId( int snum, int id ) -{ - RedStateAp *curState = allStates + snum; - curState->id = id; -} - -void Reducer::setFinal( int snum ) -{ - RedStateAp *curState = allStates + snum; - curState->isFinal = true; -} - - -void Reducer::setStateActions( int snum, long toStateAction, - long fromStateAction, long eofAction ) -{ - RedStateAp *curState = allStates + snum; - if ( toStateAction >= 0 ) - curState->toStateAction = allActionTables + toStateAction; - if ( fromStateAction >= 0 ) - curState->fromStateAction = allActionTables + fromStateAction; - if ( eofAction >= 0 ) - curState->eofAction = allActionTables + eofAction; -} - -void Reducer::setEofTrans( int snum, long eofTarget, long actId ) -{ - RedStateAp *curState = allStates + snum; - RedStateAp *targState = allStates + eofTarget; - RedAction *eofAct = actId >= 0 ? allActionTables + actId : 0; - - RedTransAp *trans = redFsm->allocateTrans( targState, eofAct ); - curState->eofTrans = trans; -} - -void Reducer::setEofTrans( int snum, GenCondSpace *condSpace, - RedCondEl *outConds, int numConds, RedCondAp *errCond ) -{ - RedStateAp *curState = allStates + snum; - - RedTransAp *trans = redFsm->allocateTrans( condSpace, outConds, numConds, errCond ); - - curState->eofTrans = trans; -} - -void Reducer::resolveTargetStates( GenInlineList *inlineList ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - switch ( item->type ) { - case GenInlineItem::Goto: case GenInlineItem::Call: - case GenInlineItem::Ncall: case GenInlineItem::Next: - case GenInlineItem::Entry: - item->targState = allStates + item->targId; - break; - default: - break; - } - - if ( item->children != 0 ) - resolveTargetStates( item->children ); - } -} - -void Reducer::resolveTargetStates() -{ - for ( GenActionList::Iter a = actionList; a.lte(); a++ ) - resolveTargetStates( a->inlineList ); - -#if 0 - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofAction != 0 ) { - for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) - setLabelsNeeded( item->value->inlineList ); - } - - if ( st->eofTrans != 0 ) { - long condsFullSize = st->eofTrans->condFullSize(); - for ( int c = 0; c < condsFullSize; c++ ) { - RedCondPair *pair = st->eofTrans->outCond( c ); - setLabelsNeeded( pair ); - } - } -#endif -} - -bool Reducer::setAlphType( const HostLang *hostLang, const char *data ) -{ - HostType *alphType = findAlphTypeInternal( hostLang, data ); - if ( alphType == 0 ) - return false; - - return true; -} - -void Reducer::condSpaceItem( int cnum, long condActionId ) -{ - GenCondSpace *cond = allCondSpaces + cnum; - cond->condSet.append( allActions + condActionId ); -} - -void Reducer::initStateCondList( int snum, ulong length ) -{ - /* Could preallocate these, as we could with transitions. */ -} - -void Reducer::addStateCond( int snum, Key lowKey, Key highKey, long condNum ) -{ -} - -Key Reducer::findMaxKey() -{ - Key maxKey = fsm->ctx->keyOps->maxKey; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - assert( st->outSingle.length() == 0 ); - assert( st->defTrans == 0 ); - - long rangeLen = st->outRange.length(); - if ( rangeLen > 0 ) { - Key highKey = st->outRange[rangeLen-1].highKey; - if ( keyOps->gt( highKey, maxKey ) ) - maxKey = highKey; - } - } - return maxKey; -} - -void Reducer::actionActionRefs( RedAction *action ) -{ - action->numTransRefs += 1; - for ( GenActionTable::Iter item = action->key; item.lte(); item++ ) - item->value->numTransRefs += 1; -} - -void Reducer::transActionRefs( RedTransAp *trans ) -{ - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - if ( cond->action != 0 ) - actionActionRefs( cond->action ); - } - - if ( trans->condSpace != 0 ) - trans->condSpace->numTransRefs += 1; -} - -void Reducer::transListActionRefs( RedTransList &list ) -{ - for ( RedTransList::Iter rtel = list; rtel.lte(); rtel++ ) - transActionRefs( rtel->value ); -} - -void Reducer::findFinalActionRefs() -{ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Rerence count out of single transitions. */ - transListActionRefs( st->outSingle ); - - /* Reference count out of range transitions. */ - transListActionRefs( st->outRange ); - - /* Reference count default transition. */ - if ( st->defTrans != 0 ) - transActionRefs( st->defTrans ); - - /* Reference count EOF transitions. */ - if ( st->eofTrans != 0 ) - transActionRefs( st->eofTrans ); - - /* Reference count to state actions. */ - if ( st->toStateAction != 0 ) { - st->toStateAction->numToStateRefs += 1; - for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) - item->value->numToStateRefs += 1; - } - - /* Reference count from state actions. */ - if ( st->fromStateAction != 0 ) { - st->fromStateAction->numFromStateRefs += 1; - for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ ) - item->value->numFromStateRefs += 1; - } - - /* Reference count EOF actions. */ - if ( st->eofAction != 0 ) { - st->eofAction->numEofRefs += 1; - for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) - item->value->numEofRefs += 1; - } - - if ( st->nfaTargs != 0 ) { - for ( RedNfaTargs::Iter nt = *st->nfaTargs; nt.lte(); nt++ ) { - - if ( nt->push != 0 ) { - nt->push->numNfaPushRefs += 1; - for ( GenActionTable::Iter item = nt->push->key; item.lte(); item++ ) - item->value->numNfaPushRefs += 1; - } - - if ( nt->popTest != 0 ) { - nt->popTest->numNfaPopTestRefs += 1; - for ( GenActionTable::Iter item = nt->popTest->key; item.lte(); item++ ) - item->value->numNfaPopTestRefs += 1; - } - } - } - } -} - -void Reducer::analyzeAction( GenAction *act, GenInlineList *inlineList ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - /* Only consider actions that are referenced. */ - if ( act->numRefs() > 0 ) { - if ( item->type == GenInlineItem::Goto || item->type == GenInlineItem::GotoExpr ) - { - redFsm->bAnyActionGotos = true; - } - else if ( item->type == GenInlineItem::Call || item->type == GenInlineItem::CallExpr ) { - redFsm->bAnyActionCalls = true; - } - else if ( item->type == GenInlineItem::Ncall || item->type == GenInlineItem::NcallExpr ) { - redFsm->bAnyActionCalls = true; - } - else if ( item->type == GenInlineItem::Ret ) - redFsm->bAnyActionRets = true; - else if ( item->type == GenInlineItem::Nret ) - redFsm->bAnyActionNrets = true; - else if ( item->type == GenInlineItem::LmInitAct || - item->type == GenInlineItem::LmSetActId || - item->type == GenInlineItem::LmSwitch ) - { - redFsm->bUsingAct = true; - } - - /* Any by value control in all actions? */ - if ( item->type == GenInlineItem::CallExpr || item->type == GenInlineItem::GotoExpr ) - redFsm->bAnyActionByValControl = true; - } - - /* Check for various things in regular actions. */ - if ( act->numTransRefs > 0 || act->numToStateRefs > 0 || act->numFromStateRefs > 0 ) { - /* Any returns in regular actions? */ - if ( item->type == GenInlineItem::Ret || item->type == GenInlineItem::Nret ) - redFsm->bAnyRegActionRets = true; - - /* Any next statements in the regular actions? */ - if ( item->type == GenInlineItem::Next || item->type == GenInlineItem::NextExpr || - item->type == GenInlineItem::Ncall || item->type == GenInlineItem::NcallExpr || - item->type == GenInlineItem::Nret ) - redFsm->bAnyRegNextStmt = true; - - /* Any by value control in regular actions? */ - if ( item->type == GenInlineItem::CallExpr || item->type == GenInlineItem::GotoExpr ) - redFsm->bAnyRegActionByValControl = true; - - /* Any references to the current state in regular actions? */ - if ( item->type == GenInlineItem::Curs ) - redFsm->bAnyRegCurStateRef = true; - - if ( item->type == GenInlineItem::Break ) - redFsm->bAnyRegBreak = true; - - if ( item->type == GenInlineItem::Nbreak ) - redFsm->bAnyRegNbreak = true; - } - - if ( item->children != 0 ) - analyzeAction( act, item->children ); - } -} - -void Reducer::analyzeActionList( RedAction *redAct, GenInlineList *inlineList ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - /* Any next statements in the action table? */ - if ( item->type == GenInlineItem::Next || item->type == GenInlineItem::NextExpr || - item->type == GenInlineItem::Ncall || item->type == GenInlineItem::NcallExpr || - item->type == GenInlineItem::Nret ) - redAct->bAnyNextStmt = true; - - /* Any references to the current state. */ - if ( item->type == GenInlineItem::Curs ) - redAct->bAnyCurStateRef = true; - - if ( item->type == GenInlineItem::Break ) - redAct->bAnyBreakStmt = true; - - if ( item->type == GenInlineItem::NfaWrapConds ) - item->condSpace->numNfaRefs += 1; - - if ( item->children != 0 ) - analyzeActionList( redAct, item->children ); - } -} - -/* Assign ids to referenced actions. */ -void Reducer::assignActionIds() -{ - int nextActionId = 0; - for ( GenActionList::Iter act = actionList; act.lte(); act++ ) { - /* Only ever interested in referenced actions. */ - if ( act->numRefs() > 0 ) - act->actionId = nextActionId++; - } -} - -void Reducer::setValueLimits() -{ - redFsm->maxSingleLen = 0; - redFsm->maxRangeLen = 0; - redFsm->maxKeyOffset = 0; - redFsm->maxIndexOffset = 0; - redFsm->maxActListId = 0; - redFsm->maxActionLoc = 0; - redFsm->maxActArrItem = 0; - redFsm->maxSpan = 0; - redFsm->maxFlatIndexOffset = 0; - redFsm->maxCondSpaceId = 0; - - /* In both of these cases the 0 index is reserved for no value, so the max - * is one more than it would be if they started at 0. */ - redFsm->maxIndex = redFsm->transSet.length(); - redFsm->maxCond = condSpaceList.length(); - - /* The nextStateId - 1 is the last state id assigned. */ - redFsm->maxState = redFsm->nextStateId - 1; - - for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) { - if ( csi->condSpaceId > redFsm->maxCondSpaceId ) - redFsm->maxCondSpaceId = csi->condSpaceId; - } - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Maximum single length. */ - if ( st->outSingle.length() > redFsm->maxSingleLen ) - redFsm->maxSingleLen = st->outSingle.length(); - - /* Maximum range length. */ - if ( st->outRange.length() > redFsm->maxRangeLen ) - redFsm->maxRangeLen = st->outRange.length(); - - /* The key offset index offset for the state after last is not used, skip it.. */ - if ( ! st.last() ) { - redFsm->maxKeyOffset += st->outSingle.length() + st->outRange.length()*2; - redFsm->maxIndexOffset += st->outSingle.length() + st->outRange.length() + 2; - } - - /* Max key span. */ - if ( st->transList != 0 ) { - unsigned long long span = fsm->ctx->keyOps->span( st->lowKey, st->highKey ); - if ( span > redFsm->maxSpan ) - redFsm->maxSpan = span; - } - - /* Max flat index offset. */ - if ( ! st.last() ) { - if ( st->transList != 0 ) - redFsm->maxFlatIndexOffset += fsm->ctx->keyOps->span( st->lowKey, st->highKey ); - redFsm->maxFlatIndexOffset += 1; - } - } - - for ( GenActionTableMap::Iter at = redFsm->actionMap; at.lte(); at++ ) { - /* Maximum id of action lists. */ - if ( at->actListId+1 > redFsm->maxActListId ) - redFsm->maxActListId = at->actListId+1; - - /* Maximum location of items in action array. */ - if ( at->location+1 > redFsm->maxActionLoc ) - redFsm->maxActionLoc = at->location+1; - - /* Maximum values going into the action array. */ - if ( at->key.length() > redFsm->maxActArrItem ) - redFsm->maxActArrItem = at->key.length(); - for ( GenActionTable::Iter item = at->key; item.lte(); item++ ) { - if ( item->value->actionId > redFsm->maxActArrItem ) - redFsm->maxActArrItem = item->value->actionId; - } - } -} - -/* Gather various info on the machine. */ -void Reducer::analyzeMachine() -{ - /* Find the true count of action references. */ - findFinalActionRefs(); - - /* Check if there are any calls in action code. */ - for ( GenActionList::Iter act = actionList; act.lte(); act++ ) { - /* Record the occurrence of various kinds of actions. */ - if ( act->numToStateRefs > 0 ) - redFsm->bAnyToStateActions = true; - if ( act->numFromStateRefs > 0 ) - redFsm->bAnyFromStateActions = true; - if ( act->numEofRefs > 0 ) - redFsm->bAnyEofActions = true; - if ( act->numTransRefs > 0 ) - redFsm->bAnyRegActions = true; - - if ( act->numNfaPushRefs > 0 ) { - redFsm->bAnyNfaPushPops = true; - redFsm->bAnyNfaPushes = true; - } - - if ( act->numNfaPopActionRefs > 0 ) { - redFsm->bAnyNfaPushPops = true; - redFsm->bAnyNfaPops = true; - } - - if ( act->numNfaPopTestRefs > 0 ) { - redFsm->bAnyNfaPushPops = true; - redFsm->bAnyNfaPops = true; - } - - /* Recurse through the action's parse tree looking for various things. */ - analyzeAction( act, act->inlineList ); - } - - /* Analyze reduced action lists. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - for ( GenActionTable::Iter act = redAct->key; act.lte(); act++ ) - if ( act->value->inlineList != 0 ) - analyzeActionList( redAct, act->value->inlineList ); - } - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) - redFsm->bAnyNfaStates = true; - } - - /* Find states that have transitions with actions that have next - * statements. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Check any actions out of outSinge. */ - for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - if ( cond->action != 0 && cond->action->anyCurStateRef() ) - st->bAnyRegCurStateRef = true; - } - } - - /* Check any actions out of outRange. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - if ( cond->action != 0 && cond->action->anyCurStateRef() ) - st->bAnyRegCurStateRef = true; - } - } - - /* Check any action out of default. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - if ( cond->action != 0 && cond->action->anyCurStateRef() ) - st->bAnyRegCurStateRef = true; - } - } - - if ( st->eofTrans != 0 ) - redFsm->bAnyEofTrans = true; - } - - for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) { - GenCondSpace *condSpace = csi; - - if ( condSpace->numTransRefs > 0 ) - redFsm->bAnyTransCondRefs = true; - - if ( condSpace->numNfaRefs > 0 ) - redFsm->bAnyNfaCondRefs = true; - } - - /* Assign ids to actions that are referenced. */ - assignActionIds(); - - /* Set the maximums of various values used for deciding types. */ - setValueLimits(); -} - -void CodeGenData::genOutputLineDirective( std::ostream &out ) const -{ - std::streambuf *sbuf = out.rdbuf(); - output_filter *filter = dynamic_cast(sbuf); - if ( filter != 0 ) - (*genLineDirective)( out, lineDirectives, filter->line + 1, filter->fileName ); -} - -void CodeGenData::write_option_error( InputLoc &loc, std::string arg ) -{ - red->id->warning(loc) << "unrecognized write option \"" << arg << "\"" << std::endl; -} - -void CodeGenData::writeClear() -{ - clear(); - - /* Delete all the nodes in the action list. Will cause all the - * string data that represents the actions to be deallocated. */ - red->fsm->ctx->actionList.empty(); - - delete red->fsm; - red->fsm = 0; - - // red->pd->graphDict.empty(); - - cleared = true; -} - -void CodeGenData::collectReferences() -{ - /* Do this once only. */ - if ( !referencesCollected ) { - referencesCollected = true; - - /* Nullify the output and execute the write. We use this pass to collect references. */ - nullbuf nb; - std::streambuf *filt = out.rdbuf( &nb ); - writeExec(); - - /* Restore the output for whatever writing comes next. */ - out.rdbuf( filt ); - } -} - -void CodeGenData::writeStatement( InputLoc &loc, int nargs, - std::vector &args, bool generateDot, const HostLang *hostLang ) -{ - /* Start write generation on a fresh line. */ - out << '\n'; - - if ( cleared ) { - red->id->error(loc) << "write statement following a clear is invalid" << std::endl; - return; - } - - genOutputLineDirective( out ); - - if ( args[0] == "data" ) { - for ( int i = 1; i < nargs; i++ ) { - if ( args[i] == "noerror" ) - noError = true; - else if ( args[i] == "noprefix" ) - noPrefix = true; - else if ( args[i] == "nofinal" ) - noFinal = true; - else - write_option_error( loc, args[i] ); - } - - if ( red->id->printStatistics ) { - red->id->stats() << "fsm-name\t" << fsmName << std::endl; - red->id->stats() << "fsm-states\t" << redFsm->stateList.length() << std::endl; - } - - collectReferences(); - writeData(); - statsSummary(); - } - else if ( args[0] == "init" ) { - for ( int i = 1; i < nargs; i++ ) { - if ( args[i] == "nocs" ) - noCS = true; - else - write_option_error( loc, args[i] ); - } - writeInit(); - } - else if ( args[0] == "exec" ) { - for ( int i = 1; i < nargs; i++ ) { - if ( args[i] == "noend" ) - noEnd = true; - else - write_option_error( loc, args[i] ); - } - collectReferences(); - writeExec(); - } - else if ( args[0] == "exports" ) { - for ( int i = 1; i < nargs; i++ ) - write_option_error( loc, args[i] ); - writeExports(); - } - else if ( args[0] == "start" ) { - for ( int i = 1; i < nargs; i++ ) - write_option_error( loc, args[i] ); - writeStart(); - } - else if ( args[0] == "first_final" ) { - for ( int i = 1; i < nargs; i++ ) - write_option_error( loc, args[i] ); - writeFirstFinal(); - } - else if ( args[0] == "error" ) { - for ( int i = 1; i < nargs; i++ ) - write_option_error( loc, args[i] ); - writeError(); - } - else if ( args[0] == "clear" ) { - for ( int i = 1; i < nargs; i++ ) - write_option_error( loc, args[i] ); - writeClear(); - } - else { - /* EMIT An error here. */ - red->id->error(loc) << "unrecognized write command \"" << - args[0] << "\"" << std::endl; - } -} diff --git a/libfsm/gendata.h b/libfsm/gendata.h deleted file mode 100644 index f34f2629..00000000 --- a/libfsm/gendata.h +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright 2005-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _GENDATA_H -#define _GENDATA_H - -#include -#include -#include -#include "config.h" -#include "redfsm.h" -#include "common.h" -#include "fsmgraph.h" - -/* Forwards. */ -struct TransAp; -struct FsmAp; -struct PdBase; -struct InputData; -struct FsmGbl; -struct GenInlineList; -struct InlineItem; - -struct RedActionTable -: - public AvlTreeEl -{ - RedActionTable( const ActionTable &key ) - : - key(key), - id(0) - { } - - const ActionTable &getKey() - { return key; } - - ActionTable key; - int id; -}; - -typedef AvlTree ActionTableMap; - -struct NextRedTrans -{ - Key lowKey, highKey; - TransAp *trans; - TransAp *next; - - void load() { - if ( trans != 0 ) { - next = trans->next; - lowKey = trans->lowKey; - highKey = trans->highKey; - } - } - - NextRedTrans( TransAp *t ) { - trans = t; - load(); - } - - void increment() { - trans = next; - load(); - } -}; - -struct RedBase -{ - RedBase( FsmGbl *id, FsmCtx *fsmCtx, FsmAp *fsm, std::string fsmName, int machineId ) - : - id(id), - fsmCtx(fsmCtx), - fsm(fsm), - fsmName(fsmName), - machineId(machineId), - keyOps(fsm->ctx->keyOps), - nextActionTableId(0) - { - } - - FsmGbl *id; - FsmCtx *fsmCtx; - FsmAp *fsm; - std::string fsmName; - int machineId; - - KeyOps *keyOps; - - ActionTableMap actionTableMap; - int nextActionTableId; -}; - -struct NameInst; -typedef DList GenActionList; - -typedef unsigned long ulong; - -void openHostBlock( char opener, InputData *id, std::ostream &out, const char *fileName, int line ); - -string itoa( int i ); - -struct Reducer - : public RedBase -{ - Reducer( FsmGbl *id, FsmCtx *fsmCtx, FsmAp *fsm, std::string fsmName, int machineId ) - : - RedBase( id, fsmCtx, fsm, fsmName, machineId ), - redFsm(0), - allActions(0), - allActionTables(0), - allConditions(0), - allCondSpaces(0), - allStates(0), - nameIndex(0), - startState(-1), - errState(-1), - getKeyExpr(0), - accessExpr(0), - prePushExpr(0), - postPopExpr(0), - nfaPrePushExpr(0), - nfaPostPopExpr(0), - pExpr(0), - peExpr(0), - eofExpr(0), - csExpr(0), - topExpr(0), - stackExpr(0), - actExpr(0), - tokstartExpr(0), - tokendExpr(0), - dataExpr(0), - hasLongestMatch(false) - { - } - - ~Reducer() - { - if ( redFsm != 0 ) - delete redFsm; - - delete[] allActions; - delete[] allActionTables; - delete[] allConditions; - delete[] allCondSpaces; - - actionTableMap.empty(); - - if ( getKeyExpr != 0 ) - delete getKeyExpr; - if ( accessExpr != 0 ) - delete accessExpr; - if ( prePushExpr != 0 ) - delete prePushExpr; - if ( postPopExpr != 0 ) - delete postPopExpr; - if ( nfaPrePushExpr != 0 ) - delete nfaPrePushExpr; - if ( nfaPostPopExpr != 0 ) - delete nfaPostPopExpr; - if ( pExpr != 0 ) - delete pExpr; - if ( peExpr != 0 ) - delete peExpr; - if ( eofExpr != 0 ) - delete eofExpr; - if ( csExpr != 0 ) - delete csExpr; - if ( topExpr != 0 ) - delete topExpr; - if ( stackExpr != 0 ) - delete stackExpr; - if ( actExpr != 0 ) - delete actExpr; - if ( tokstartExpr != 0 ) - delete tokstartExpr; - if ( tokendExpr != 0 ) - delete tokendExpr; - if ( dataExpr != 0 ) - delete dataExpr; - } - -protected: - /* Collected during parsing. */ - int curAction; - int curActionTable; - int curState; - - void makeKey( GenInlineList *outList, Key key ); - void makeText( GenInlineList *outList, InlineItem *item ); - void makeLmOnLast( GenInlineList *outList, InlineItem *item ); - void makeLmOnNext( GenInlineList *outList, InlineItem *item ); - void makeLmOnLagBehind( GenInlineList *outList, InlineItem *item ); - void makeLmSwitch( GenInlineList *outList, InlineItem *item ); - void makeLmNfaOnLast( GenInlineList *outList, InlineItem *item ); - void makeLmNfaOnNext( GenInlineList *outList, InlineItem *item ); - void makeLmNfaOnEof( GenInlineList *outList, InlineItem *item ); - void makeActionExec( GenInlineList *outList, InlineItem *item ); - void makeSetTokend( GenInlineList *outList, long offset ); - void makeSetAct( GenInlineList *outList, long lmId ); - void makeSubList( GenInlineList *outList, InlineList *inlineList, - GenInlineItem::Type type ); - void makeTargetItem( GenInlineList *outList, NameInst *nameTarg, - GenInlineItem::Type type ); - void makeExecGetTokend( GenInlineList *outList ); - void makeActionList(); - void makeAction( Action *action ); - void makeActionTableList(); - void makeConditions(); - void makeEntryPoints(); - bool makeNameInst( std::string &out, NameInst *nameInst ); - void makeStateList(); - - void makeStateActions( StateAp *state ); - void makeEofTrans( StateAp *state ); - void makeTransList( StateAp *state ); - void makeTrans( Key lowKey, Key highKey, TransAp *trans ); - void newTrans( RedStateAp *state, Key lowKey, Key highKey, RedTransAp *trans ); - - void makeSubList( GenInlineList *outList, const InputLoc &loc, - InlineList *inlineList, GenInlineItem::Type type ); - - void createMachine(); - void initActionList( unsigned long length ); - void newAction( int anum, std::string name, - const InputLoc &loc, GenInlineList *inlineList ); - void initActionTableList( unsigned long length ); - void initStateList( unsigned long length ); - void setStartState( unsigned long startState ); - void setErrorState( unsigned long errState ); - void addEntryPoint( char *name, unsigned long entryState ); - void setId( int snum, int id ); - void setFinal( int snum ); - void initTransList( int snum, unsigned long length ); - - void newTrans( int snum, int tnum, Key lowKey, Key highKey, - GenCondSpace *gcs, RedTransAp *trans ); - - void finishTransList( int snum ); - void setStateActions( int snum, long toStateAction, - long fromStateAction, long eofAction ); - void setEofTrans( int snum, long targ, long eofAction ); - void setEofTrans( int snum, GenCondSpace *condSpace, - RedCondEl *outConds, int numConds, RedCondAp *errCond ); - void setForcedErrorState() - { redFsm->forcedErrorState = true; } - - void condSpaceItem( int cnum, long condActionId ); - void newCondSpace( int cnum, int condSpaceId ); - - void initStateCondList( int snum, ulong length ); - void addStateCond( int snum, Key lowKey, Key highKey, long condNum ); - - - void resolveTargetStates( GenInlineList *inlineList ); - void resolveTargetStates(); - - - /* Gather various info on the machine. */ - void analyzeActionList( RedAction *redAct, GenInlineList *inlineList ); - void analyzeAction( GenAction *act, GenInlineList *inlineList ); - void actionActionRefs( RedAction *action ); - void transListActionRefs( RedTransList &list ); - void transActionRefs( RedTransAp *trans ); - void findFinalActionRefs(); - - void setValueLimits(); - void assignActionIds(); - - - void appendTrans( TransListVect &outList, Key lowKey, Key highKey, TransAp *trans ); - void reduceActionTables(); - -public: - - Key findMaxKey(); - void makeMachine(); - void makeExports(); - void makeGenInlineList( GenInlineList *outList, InlineList *inList ); - bool setAlphType( const HostLang *hostLang, const char *data ); - void analyzeMachine(); - void make( const HostLang *hostLang, const HostType *alphType ); - - /* - * Collecting the machine. - */ - - RedFsmAp *redFsm; - GenAction *allActions; - RedAction *allActionTables; - Condition *allConditions; - GenCondSpace *allCondSpaces; - RedStateAp *allStates; - NameInst **nameIndex; - int startState; - int errState; - GenActionList actionList; - CondSpaceList condSpaceList; - - GenInlineList *getKeyExpr; - GenInlineList *accessExpr; - GenInlineExpr *prePushExpr; - GenInlineExpr *postPopExpr; - - GenInlineExpr *nfaPrePushExpr; - GenInlineExpr *nfaPostPopExpr; - - /* Overriding variables. */ - GenInlineList *pExpr; - GenInlineList *peExpr; - GenInlineList *eofExpr; - GenInlineList *csExpr; - GenInlineList *topExpr; - GenInlineList *stackExpr; - GenInlineList *actExpr; - GenInlineList *tokstartExpr; - GenInlineList *tokendExpr; - GenInlineList *dataExpr; - - EntryIdVect entryPointIds; - EntryNameVect entryPointNames; - bool hasLongestMatch; - ExportList exportList; - Action *curInlineAction; -}; - -struct CodeGenArgs -{ - CodeGenArgs( FsmGbl *id, Reducer *red, HostType *alphType, - int machineId, std::string sourceFileName, - std::string fsmName, std::ostream &out, - CodeStyle codeStyle ) - : - id(id), - red(red), - alphType(alphType), - machineId(machineId), - sourceFileName(sourceFileName), - fsmName(fsmName), - out(out), - codeStyle(codeStyle), - lineDirectives(true), - forceVar(false), - loopLabels(false) - {} - - FsmGbl *id; - Reducer *red; - HostType *alphType; - int machineId; - std::string sourceFileName; - std::string fsmName; - std::ostream &out; - CodeStyle codeStyle; - bool lineDirectives; - GenLineDirectiveT genLineDirective; - bool forceVar; - bool loopLabels; -}; - -struct CodeGenData -{ - CodeGenData( const CodeGenArgs &args ) - : - red(args.red), - redFsm(args.red->redFsm), - sourceFileName(args.sourceFileName), - fsmName(args.fsmName), - keyOps(red->keyOps), - alphType(args.alphType), - out(args.out), - noEnd(false), - noPrefix(false), - noFinal(false), - noError(false), - noCS(false), - lineDirectives(args.lineDirectives), - cleared(false), - referencesCollected(false), - genLineDirective(args.id->hostLang->genLineDirective) - { - } - - /* - * The interface to the code generator. - */ - virtual void genAnalysis() = 0; - - /* These are invoked by writeStatement and are normally what are used to - * implement the code generators. */ - virtual void writeData() {}; - virtual void writeInit() {}; - virtual void writeExec() {}; - virtual void writeExports() {}; - virtual void writeStart() {}; - virtual void writeFirstFinal() {}; - virtual void writeError() {}; - virtual void writeClear(); - - /* Show some stats after a write data. */ - virtual void statsSummary() = 0; - - /* This can also be overridden to modify the processing of write - * statements. */ - virtual void writeStatement( InputLoc &loc, int nargs, - std::vector &args, bool generateDot, const HostLang *hostLang ); - - /********************/ - - virtual ~CodeGenData() - { - } - - void clear() - { - delete red->redFsm; - red->redFsm = 0; - } - - void collectReferences(); - -protected: - - Reducer *red; - RedFsmAp *redFsm; - std::string sourceFileName; - std::string fsmName; - KeyOps *keyOps; - HostType *alphType; - ostream &out; - - /* Write options. */ - bool noEnd; - bool noPrefix; - bool noFinal; - bool noError; - bool noCS; - - void write_option_error( InputLoc &loc, std::string arg ); - - bool lineDirectives; - bool cleared; - - bool referencesCollected; - - void genOutputLineDirective( std::ostream &out ) const; - GenLineDirectiveT genLineDirective; -}; - -/* Selects and constructs the codegen based on the output options. */ -CodeGenData *makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ); -CodeGenData *asm_makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ); - -typedef AvlMap CodeGenMap; -typedef AvlMapEl CodeGenMapEl; - -#endif diff --git a/libfsm/goto.cc b/libfsm/goto.cc deleted file mode 100644 index 610f44d1..00000000 --- a/libfsm/goto.cc +++ /dev/null @@ -1,978 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "goto.h" -#include "redfsm.h" -#include "bstmap.h" -#include "gendata.h" - -#include - -using std::ostringstream; - -IpLabel *Goto::allocateLabels( IpLabel *labels, IpLabel::Type type, int n ) -{ - if ( labels == 0 ) { - labels = new IpLabel[n]; - for ( int id = 0; id < n; id++ ) { - labels[id].type = type; - labels[id].stid = id; - } - } - - return labels; -} - -void Goto::setTableState( TableArray::State state ) -{ - for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { - TableArray *tableArray = *i; - tableArray->setState( state ); - } -} - -/* Emit the goto to take for a given transition. */ -std::ostream &Goto::COND_GOTO( RedCondPair *cond ) -{ - out << "goto " << ctrLabel[cond->id].reference() << ";"; - return out; -} - -/* Emit the goto to take for a given transition. */ -std::ostream &Goto::TRANS_GOTO( RedTransAp *trans ) -{ - if ( trans->condSpace == 0 || trans->condSpace->condSet.length() == 0 ) { - /* Existing. */ - assert( trans->numConds() == 1 ); - RedCondPair *cond = trans->outCond( 0 ); - - /* Go to the transition which will go to the state. */ - out << "goto " << ctrLabel[cond->id].reference() << ";"; - } - else { - out << ck << " = 0;\n"; - for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { - out << "if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - out << " )\n" << ck << " += " << condValOffset << ";\n"; - } - CondKey lower = 0; - CondKey upper = trans->condFullSize() - 1; - COND_B_SEARCH( trans, lower, upper, 0, trans->numConds()-1 ); - - if ( trans->errCond() != 0 ) { - COND_GOTO( trans->errCond() ) << "\n"; - } - } - - return out; -} - -/* Write out the array of actions. */ -void Goto::taActions() -{ - actions.start(); - - actions.value( 0 ); - - for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { - /* Write out the length, which will never be the last character. */ - actions.value( act->key.length() ); - - for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) - actions.value( item->value->actionId ); - } - - actions.finish(); -} - -void Goto::GOTO_HEADER( RedStateAp *state ) -{ - /* Label the state. */ - out << "case " << state->id << ":\n"; -} - - -void Goto::SINGLE_SWITCH( RedStateAp *state ) -{ - /* Load up the singles. */ - int numSingles = state->outSingle.length(); - RedTransEl *data = state->outSingle.data; - - if ( numSingles == 1 ) { - /* If there is a single single key then write it out as an if. */ - out << "if ( " << GET_KEY() << " == " << - KEY(data[0].lowKey) << " ) {\n"; - - /* Virtual function for writing the target of the transition. */ - TRANS_GOTO(data[0].value) << "\n"; - out << "}\n"; - } - else if ( numSingles > 1 ) { - /* Write out single keys in a switch if there is more than one. */ - out << "switch( " << GET_KEY() << " ) {\n"; - - /* Write out the single indices. */ - for ( int j = 0; j < numSingles; j++ ) { - out << "case " << KEY(data[j].lowKey) << ": {\n"; - TRANS_GOTO(data[j].value) << "\n"; - out << "}\n"; - } - - /* Close off the transition switch. */ - out << "}\n"; - } -} - -void Goto::RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ) -{ - /* Get the mid position, staying on the lower end of the range. */ - int mid = (low + high) >> 1; - RedTransEl *data = state->outRange.data; - - /* Determine if we need to look higher or lower. */ - bool anyLower = mid > low; - bool anyHigher = mid < high; - - /* Determine if the keys at mid are the limits of the alphabet. */ - bool limitLow = keyOps->eq( data[mid].lowKey, lower ); - bool limitHigh = keyOps->eq( data[mid].highKey, upper ); - - if ( anyLower && anyHigher ) { - /* Can go lower and higher than mid. */ - out << "if ( " << GET_KEY() << " < " << - KEY(data[mid].lowKey) << " ) {\n"; - RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); - out << "} else if ( " << GET_KEY() << " > " << - KEY(data[mid].highKey) << " ) {\n"; - RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); - out << "} else {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - else if ( anyLower && !anyHigher ) { - /* Can go lower than mid but not higher. */ - out << "if ( " << GET_KEY() << " < " << - KEY(data[mid].lowKey) << " ) {\n"; - RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); - - /* if the higher is the highest in the alphabet then there is no - * sense testing it. */ - if ( limitHigh ) { - out << "} else {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - else { - out << "} else if ( " << GET_KEY() << " <= " << - KEY(data[mid].highKey) << " ) {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - } - else if ( !anyLower && anyHigher ) { - /* Can go higher than mid but not lower. */ - out << "if ( " << GET_KEY() << " > " << - KEY(data[mid].highKey) << " ) {\n"; - RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); - - /* If the lower end is the lowest in the alphabet then there is no - * sense testing it. */ - if ( limitLow ) { - out << "} else {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - else { - out << "} else if ( " << GET_KEY() << " >= " << - KEY(data[mid].lowKey) << " ) {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - } - else { - /* Cannot go higher or lower than mid. It's mid or bust. What - * tests to do depends on limits of alphabet. */ - if ( !limitLow && !limitHigh ) { - out << "if ( " << KEY(data[mid].lowKey) << " <= " << - GET_KEY() << " && " << GET_KEY() << " <= " << - KEY(data[mid].highKey) << " ) {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - else if ( limitLow && !limitHigh ) { - out << "if ( " << GET_KEY() << " <= " << - KEY(data[mid].highKey) << " ) {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - else if ( !limitLow && limitHigh ) { - out << "if ( " << KEY(data[mid].lowKey) << " <= " << - GET_KEY() << " ) {\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - else { - /* Both high and low are at the limit. No tests to do. */ - out << "{\n"; - TRANS_GOTO(data[mid].value) << "\n"; - out << "}\n"; - } - } -} - -/* Write out a key from the fsm code gen. Depends on wether or not the key is - * signed. */ -string Goto::CKEY( CondKey key ) -{ - ostringstream ret; - ret << key.getVal(); - return ret.str(); -} - -void Goto::COND_B_SEARCH( RedTransAp *trans, CondKey lower, - CondKey upper, int low, int high ) -{ - /* Get the mid position, staying on the lower end of the range. */ - int mid = (low + high) >> 1; -// RedCondEl *data = trans->outCond(0); - - /* Determine if we need to look higher or lower. */ - bool anyLower = mid > low; - bool anyHigher = mid < high; - - CondKey midKey = trans->outCondKey( mid ); - RedCondPair *midTrans = trans->outCond( mid ); - - /* Determine if the keys at mid are the limits of the alphabet. */ - bool limitLow = midKey == lower; - bool limitHigh = midKey == upper; - - if ( anyLower && anyHigher ) { - /* Can go lower and higher than mid. */ - out << "if ( " << ck << " < " << - CKEY(midKey) << " ) {\n"; - COND_B_SEARCH( trans, lower, midKey-1, low, mid-1 ); - out << "} else if ( " << ck << " > " << - CKEY(midKey) << " ) {\n"; - COND_B_SEARCH( trans, midKey+1, upper, mid+1, high ); - out << "} else {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - else if ( anyLower && !anyHigher ) { - /* Can go lower than mid but not higher. */ - out << "if ( " << ck << " < " << - CKEY(midKey) << " ) {\n"; - COND_B_SEARCH( trans, lower, midKey-1, low, mid-1); - - /* if the higher is the highest in the alphabet then there is no - * sense testing it. */ - if ( limitHigh ) { - out << "} else {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - else { - out << "} else if ( " << ck << " <= " << - CKEY(midKey) << " ) {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - } - else if ( !anyLower && anyHigher ) { - /* Can go higher than mid but not lower. */ - out << "if ( " << ck << " > " << - CKEY(midKey) << " ) {\n"; - COND_B_SEARCH( trans, midKey+1, upper, mid+1, high ); - - /* If the lower end is the lowest in the alphabet then there is no - * sense testing it. */ - if ( limitLow ) { - out << "} else {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - else { - out << "} else if ( " << ck << " >= " << - CKEY(midKey) << " ) {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - } - else { - /* Cannot go higher or lower than mid. It's mid or bust. What - * tests to do depends on limits of alphabet. */ - if ( !limitLow && !limitHigh ) { - out << "if ( " << ck << " == " << - CKEY(midKey) << " ) {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - else if ( limitLow && !limitHigh ) { - out << "if ( " << ck << " <= " << - CKEY(midKey) << " ) {\n"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - else if ( !limitLow && limitHigh ) { - out << "if ( " << CKEY(midKey) << " <= " << ck << " )\n {"; - COND_GOTO(midTrans) << "\n"; - out << "}\n"; - } - else { - /* Both high and low are at the limit. No tests to do. */ - COND_GOTO(midTrans) << "\n"; - } - } -} - -void Goto::STATE_GOTO_ERROR() -{ - /* Bail out immediately. */ - out << " goto " << _again << ";\n"; -} - -void Goto::FROM_STATE_ACTION_EMIT( RedStateAp *state ) -{ - if ( state->fromStateAction != 0 ) { - /* Write every action in the list. */ - for ( GenActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( state->id, false, - state->fromStateAction->anyNextStmt() ) ); - out << "\n"; - } - } -} - -std::ostream &Goto::STATE_CASES() -{ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Writing code above state gotos. */ - GOTO_HEADER( st ); - - FROM_STATE_ACTION_EMIT( st ); - - if ( !noEnd && eof ) { - out << - "if ( " << P() << " == " << vEOF() << " ) {\n"; - - if ( st->eofTrans != 0 ) - TRANS_GOTO( st->eofTrans ); - - out << - " goto " << _again << ";\n" - "}\n" - "else {\n"; - } - - if ( st == redFsm->errState ) - STATE_GOTO_ERROR(); - else { - /* Try singles. */ - if ( st->outSingle.length() > 0 ) - SINGLE_SWITCH( st ); - - /* Default case is to binary search for the ranges, if that fails then */ - if ( st->outRange.length() > 0 ) { - RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey, - 0, st->outRange.length() - 1 ); - } - - /* Write the default transition. */ - TRANS_GOTO( st->defTrans ) << "\n"; - } - - if ( !noEnd && eof ) { - out << - "}\n"; - } - } - return out; -} - -std::ostream &Goto::TRANSITION( RedCondPair *pair ) -{ - /* Write the label for the transition so it can be jumped to. */ - if ( ctrLabel[pair->id].isReferenced ) - out << "_ctr" << pair->id << ": "; - - /* Destination state. */ - if ( pair->action != 0 && pair->action->anyCurStateRef() ) - out << ps << " = " << vCS() << ";"; - out << vCS() << " = " << pair->targ->id << "; "; - - if ( pair->action != 0 ) { - /* Write out the transition func. */ - out << "goto f" << pair->action->actListId << ";\n"; - } - else { - /* No code to execute, just loop around. */ - out << "goto " << _again << ";\n"; - } - return out; -} - -std::ostream &Goto::TRANSITIONS() -{ - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - if ( trans->condSpace == 0 ) - TRANSITION( &trans->p ); - } - - for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) - TRANSITION( &cond->p ); - - return out; -} - -unsigned int Goto::TO_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->toStateAction != 0 ) - act = state->toStateAction->location+1; - return act; -} - -unsigned int Goto::FROM_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->fromStateAction != 0 ) - act = state->fromStateAction->location+1; - return act; -} - -unsigned int Goto::EOF_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->eofAction != 0 ) - act = state->eofAction->location+1; - return act; -} - -void Goto::taToStateActions() -{ - toStateActions.start(); - - /* Take one off for the psuedo start state. */ - int numStates = redFsm->stateList.length(); - unsigned int *vals = new unsigned int[numStates]; - memset( vals, 0, sizeof(unsigned int)*numStates ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - vals[st->id] = TO_STATE_ACTION(st); - - for ( int st = 0; st < redFsm->nextStateId; st++ ) { - /* Write any eof action. */ - toStateActions.value( vals[st] ); - } - delete[] vals; - - toStateActions.finish(); -} - -void Goto::taFromStateActions() -{ - fromStateActions.start(); - - /* Take one off for the psuedo start state. */ - int numStates = redFsm->stateList.length(); - unsigned int *vals = new unsigned int[numStates]; - memset( vals, 0, sizeof(unsigned int)*numStates ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - vals[st->id] = FROM_STATE_ACTION(st); - - for ( int st = 0; st < redFsm->nextStateId; st++ ) { - /* Write any eof action. */ - fromStateActions.value( vals[st] ); - } - delete[] vals; - - fromStateActions.finish(); -} - -void Goto::taEofActions() -{ - eofActions.start(); - - /* Take one off for the psuedo start state. */ - int numStates = redFsm->stateList.length(); - unsigned int *vals = new unsigned int[numStates]; - memset( vals, 0, sizeof(unsigned int)*numStates ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - vals[st->id] = EOF_ACTION(st); - - for ( int st = 0; st < redFsm->nextStateId; st++ ) { - /* Write any eof action. */ - eofActions.value( vals[st] ); - } - delete[] vals; - - eofActions.finish(); -} - -void Goto::taNfaOffsets() -{ - nfaOffsets.start(); - - /* Offset of zero means no NFA targs, real targs start at 1. */ - long offset = 1; - - /* Take one off for the psuedo start state. */ - int numStates = redFsm->stateList.length(); - unsigned int *vals = new unsigned int[numStates]; - memset( vals, 0, sizeof(unsigned int)*numStates ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs == 0 ) { - vals[st->id] = 0; - //nfaOffsets.value( 0 ); - } - else { - vals[st->id] = offset; - //nfaOffsets.value( offset ); - offset += 1 + st->nfaTargs->length(); - } - } - - for ( int st = 0; st < redFsm->nextStateId; st++ ) - nfaOffsets.value( vals[st] ); - delete[] vals; - - nfaOffsets.finish(); -} - -void Goto::taNfaTargs() -{ - nfaTargs.start(); - - /* Offset of zero means no NFA targs, put a filler there. */ - nfaTargs.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaTargs.value( st->nfaTargs->length() ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - nfaTargs.value( targ->state->id ); - } - } - - nfaTargs.finish(); -} - -/* These need to mirror nfa targs. */ -void Goto::taNfaPushActions() -{ - nfaPushActions.start(); - - nfaPushActions.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaPushActions.value( 0 ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_PUSH_ACTION( targ ); - } - } - - nfaPushActions.finish(); -} - -void Goto::taNfaPopTrans() -{ - nfaPopTrans.start(); - - nfaPopTrans.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaPopTrans.value( 0 ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_POP_TEST( targ ); - } - } - - nfaPopTrans.finish(); -} - -void Goto::EOF_CHECK( ostream &ret ) -{ - ret << - " if ( " << P() << " == " << PE() << " )\n" - " goto " << _test_eof << ";\n"; -} - -void Goto::GOTO( ostream &ret, int gotoDest, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << "; "; - - if ( inFinish && !noEnd ) - EOF_CHECK( ret ); - - ret << "goto " << _again << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void Goto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - if ( inFinish && !noEnd ) - EOF_CHECK( ret ); - - ret << " goto " << _again << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void Goto::CURS( ostream &ret, bool inFinish ) -{ - ret << "(" << ps << ")"; -} - -void Goto::TARGS( ostream &ret, bool inFinish, int targState ) -{ - ret << "(" << vCS() << ")"; -} - -void Goto::NEXT( ostream &ret, int nextDest, bool inFinish ) -{ - ret << vCS() << " = " << nextDest << ";"; -} - -void Goto::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << vCS() << " = ("; - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << ");"; -} - -void Goto::CALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << - TOP() << " += 1;" << vCS() << " = " << - callDest << ";"; - - if ( inFinish && !noEnd ) - EOF_CHECK( ret ); - - ret << " goto " << _again << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void Goto::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << - TOP() << " += 1;" << vCS() << " = " << - callDest << "; " << CLOSE_GEN_BLOCK(); -} - -void Goto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << - vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - if ( inFinish && !noEnd ) - EOF_CHECK( ret ); - - ret << " goto " << _again << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void Goto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << - vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); -} - -void Goto::RET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - if ( inFinish && !noEnd ) - EOF_CHECK( ret ); - - ret << "goto " << _again << ";" << CLOSE_GEN_BLOCK(); -} - -void Goto::NRET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << CLOSE_GEN_BLOCK(); -} - -void Goto::BREAK( ostream &ret, int targState, bool csForced ) -{ - ret << OPEN_GEN_BLOCK() << P() << " += 1; " << "goto " << _out << "; " << CLOSE_GEN_BLOCK(); -} - -void Goto::NBREAK( ostream &ret, int targState, bool csForced ) -{ - ret << OPEN_GEN_BLOCK() << P() << " += 1; " << nbreak << " = 1; " << CLOSE_GEN_BLOCK(); -} - -void Goto::tableDataPass() -{ - if ( type == Loop ) - taActions(); - - taToStateActions(); - taFromStateActions(); - taEofActions(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); -} - -void Goto::genAnalysis() -{ - /* For directly executable machines there is no required state - * ordering. Choose a depth-first ordering to increase the - * potential for fall-throughs. */ - redFsm->depthFirstOrdering(); - - /* Choose default transitions and the single transition. */ - redFsm->chooseDefaultSpan(); - - /* Choose single. */ - redFsm->moveSelectTransToSingle(); - - /* If any errors have occured in the input file then don't write anything. */ - if ( red->id->errorCount > 0 ) - return; - - /* Anlayze Machine will find the final action reference counts, among other - * things. We will use these in reporting the usage of fsm directives in - * action code. */ - red->analyzeMachine(); - - /* Run the analysis pass over the table data. */ - setTableState( TableArray::AnalyzePass ); - tableDataPass(); - - /* Switch the tables over to the code gen mode. */ - setTableState( TableArray::GeneratePass ); -} - -void Goto::writeData() -{ - if ( type == Loop ) { - if ( redFsm->anyActions() ) - taActions(); - } - - if ( redFsm->anyToStateActions() ) - taToStateActions(); - - if ( redFsm->anyFromStateActions() ) - taFromStateActions(); - - if ( redFsm->anyEofActions() ) - taEofActions(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); - - STATE_IDS(); -} - -void Goto::writeExec() -{ - int maxCtrId = redFsm->nextCondId > redFsm->nextTransId ? redFsm->nextCondId : redFsm->nextTransId; - ctrLabel = allocateLabels( ctrLabel, IpLabel::Ctr, maxCtrId ); - - out << "{\n"; - - DECLARE( INT(), cpc ); - DECLARE( INT(), ck ); - DECLARE( INT(), pop_test ); - DECLARE( INT(), nbreak ); - DECLARE( INT(), ps, " = 0" ); - DECLARE( INT(), new_recs ); - DECLARE( INT(), alt ); - DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); - DECLARE( UINT(), nacts ); - - out << "\n"; - - out << EMIT_LABEL( _resume ); - - /* Do we break out on no more input. */ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - if ( !noEnd ) { - if ( eof ) { - out << - " if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" - " goto " << _out << ";\n"; - } - else { - out << - " if ( " << P() << " == " << PE() << " )\n" - " goto " << _out << ";\n"; - } - } - - NFA_PUSH( vCS() ); - - out << - " switch ( " << vCS() << " ) {\n"; - STATE_CASES() << - " }\n" - "\n"; - TRANSITIONS() << - "\n"; - - if ( redFsm->anyRegActions() ) - EXEC_FUNCS() << "\n"; - - out << EMIT_LABEL( _again ); - - if ( !noEnd && eof ) { - out << - " if ( " << P() << " == " << vEOF() << " ) {\n" - " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" - " goto " << _out << ";\n" - " }\n" - " else {\n"; - } - - TO_STATE_ACTIONS(); - - if ( redFsm->errState != 0 ) { - out << - " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; - } - - out << - " " << P() << " += 1;\n" - " goto " << _resume << ";\n"; - - if ( redFsm->errState != 0 ) { - out << - " }\n"; - } - - if ( !noEnd && eof ) { - out << - " }\n"; - } - - if ( redFsm->anyNfaStates() ) { - out << - " if ( nfa_len == 0 )\n" - " goto " << _out << ";\n" - "\n" - " nfa_count += 1;\n" - " nfa_len -= 1;\n" - " " << P() << " = nfa_bp[nfa_len].p;\n" - ; - - if ( redFsm->bAnyNfaPops ) { - NFA_FROM_STATE_ACTION_EXEC(); - - NFA_POP_TEST_EXEC(); - - out << - " if ( " << pop_test << " )\n" - " " << vCS() << " = nfa_bp[nfa_len].state;\n" - " else\n" - " " << vCS() << " = " << ERROR_STATE() << ";\n"; - } - else { - out << - " " << vCS() << " = nfa_bp[nfa_len].state;\n"; - - } - - NFA_POST_POP(); - - out << "goto " << _resume << ";\n"; - } - - out << EMIT_LABEL( _out ); - - out << "}\n"; -} diff --git a/libfsm/goto.h b/libfsm/goto.h deleted file mode 100644 index dcf13448..00000000 --- a/libfsm/goto.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _C_GOTO_H -#define _C_GOTO_H - -#include -#include "codegen.h" - -/* Forwards. */ -struct CodeGenData; -struct NameInst; -struct RedTransAp; -struct RedStateAp; -struct GenStateCond; - -struct IpLabel -{ - IpLabel() - : - type(None), - stid(0), - isReferenced(false) - {} - - enum Type - { - None = 1, - TestEof, - Ctr, - St, - Out, - Pop - }; - - std::string reference() - { - isReferenced = true; - return define(); - } - - std::string define() - { - std::stringstream ss; - switch ( type ) { - case None: break; - case TestEof: - ss << "_test_eof" << stid; - break; - case Ctr: - ss << "_ctr" << stid; - break; - case St: - ss << "_st" << stid; - break; - case Out: - ss << "_out" << stid; - break; - case Pop: - ss << "_pop" << stid; - break; - } - - return ss.str(); - } - - Type type; - int stid; - bool isReferenced; -}; - - -/* - * Goto driven fsm. - */ -class Goto - : public CodeGen -{ -public: - enum Type { - Loop = 1, - Exp, - Ip - }; - - Goto( const CodeGenArgs &args, Type type ) - : - CodeGen( args ), - type(type), - acts( "_acts" ), - nacts( "_nacts" ), - ck( "_ck" ), - nbreak( "_nbreak" ), - ps( "_ps" ), - _out("_out"), - _pop("_pop"), - _again("_again"), - _resume("_resume"), - _test_eof("_test_eof"), - actions( "actions", *this ), - toStateActions( "to_state_actions", *this ), - fromStateActions( "from_state_actions", *this ), - eofActions( "eof_actions", *this ), - ctrLabel(0) - {} - - void tableDataPass(); - virtual void genAnalysis(); - virtual void writeData(); - virtual void writeExec(); - - std::ostream &TRANSITION( RedCondPair *pair ); - - void FROM_STATE_ACTION_EMIT( RedStateAp *state ); - - std::ostream &STATE_CASES(); - std::ostream &TRANSITIONS(); - - Type type; - - Variable acts; - Variable nacts; - Variable ck; - Variable nbreak; - Variable ps; - - GotoLabel _out; - GotoLabel _pop; - GotoLabel _again; - GotoLabel _resume; - GotoLabel _test_eof; - - TableArray actions; - TableArray toStateActions; - TableArray fromStateActions; - TableArray eofActions; - - IpLabel *ctrLabel; - - void taActions(); - void taToStateActions(); - void taFromStateActions(); - void taEofActions(); - void taNfaTargs(); - void taNfaOffsets(); - void taNfaPushActions(); - void taNfaPopTrans(); - - void EOF_CHECK( ostream &ret ); - - void GOTO( ostream &ret, int gotoDest, bool inFinish ); - void CALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NEXT( ostream &ret, int nextDest, bool inFinish ); - void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void CURS( ostream &ret, bool inFinish ); - void TARGS( ostream &ret, bool inFinish, int targState ); - void RET( ostream &ret, bool inFinish ); - void NRET( ostream &ret, bool inFinish ); - void BREAK( ostream &ret, int targState, bool csForced ); - void NBREAK( ostream &ret, int targState, bool csForced ); - - virtual unsigned int TO_STATE_ACTION( RedStateAp *state ); - virtual unsigned int FROM_STATE_ACTION( RedStateAp *state ); - virtual unsigned int EOF_ACTION( RedStateAp *state ); - - virtual std::ostream &EXEC_FUNCS() = 0; - virtual std::ostream &TO_STATE_ACTION_SWITCH() = 0; - virtual std::ostream &FROM_STATE_ACTION_SWITCH() = 0; - virtual std::ostream &EOF_ACTION_SWITCH() = 0; - - std::ostream &ACTIONS_ARRAY(); - - void setTableState( TableArray::State ); - - virtual std::ostream &COND_GOTO( RedCondPair *trans ); - - string CKEY( CondKey key ); - void COND_B_SEARCH( RedTransAp *trans, CondKey lower, CondKey upper, int low, int high); - - virtual std::ostream &TRANS_GOTO( RedTransAp *trans ); - - void SINGLE_SWITCH( RedStateAp *state ); - void RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ); - - /* Called from STATE_GOTOS just before writing the gotos */ - virtual void GOTO_HEADER( RedStateAp *state ); - virtual void STATE_GOTO_ERROR(); - - virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ) = 0; - virtual void NFA_POP_TEST( RedNfaTarg *targ ) {} - virtual void NFA_FROM_STATE_ACTION_EXEC() = 0; - - void NFA_POP() {} - - virtual void FROM_STATE_ACTIONS() = 0; - virtual void TO_STATE_ACTIONS() = 0; - virtual void REG_ACTIONS() = 0; - virtual void EOF_ACTIONS() = 0; - - IpLabel *allocateLabels( IpLabel *labels, IpLabel::Type type, int n ); -}; - -#endif diff --git a/libfsm/gotoexp.cc b/libfsm/gotoexp.cc deleted file mode 100644 index fe695401..00000000 --- a/libfsm/gotoexp.cc +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "gotoexp.h" -#include "redfsm.h" -#include "gendata.h" -#include "bstmap.h" -#include "parsedata.h" - -std::ostream &GotoExp::EXEC_FUNCS() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numTransRefs > 0 ) { - /* We are at the start of a glob, write the case. */ - out << "f" << redAct->actListId << ":\n"; - - if ( redFsm->anyRegNbreak() ) - out << nbreak << " = 0;\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) - ACTION( out, item->value, IlOpts( 0, false, false ) ); - - if ( redFsm->anyRegNbreak() ) { - out << - " if ( " << nbreak << " == 1 )\n" - " goto " << _out << ";\n"; - } - - - out << "goto " << _again << ";\n"; - } - } - return out; -} - -/* Write out the function switch. This switch is keyed on the values - * of the func index. */ -std::ostream &GotoExp::TO_STATE_ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numToStateRefs > 0 ) { - /* Write the entry label. */ - out << "\t" << CASE( STR( redAct->actListId+1 ) ) << "{\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) - ACTION( out, item->value, IlOpts( 0, false, false ) ); - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -/* Write out the function switch. This switch is keyed on the values - * of the func index. */ -std::ostream &GotoExp::FROM_STATE_ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numFromStateRefs > 0 ) { - /* Write the entry label. */ - out << "\t" << CASE( STR( redAct->actListId+1 ) ) << "{\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) - ACTION( out, item->value, IlOpts( 0, false, false ) ); - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &GotoExp::EOF_ACTION_SWITCH() -{ - /* Loop the actions. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numEofRefs > 0 ) { - /* Write the entry label. */ - out << "\t" << CASE( STR( redAct->actListId+1 ) ) << "{\n"; - - /* Write each action in the list of action items. */ - for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) - ACTION( out, item->value, IlOpts( 0, true, false ) ); - - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -unsigned int GotoExp::TO_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->toStateAction != 0 ) - act = state->toStateAction->actListId+1; - return act; -} - -unsigned int GotoExp::FROM_STATE_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->fromStateAction != 0 ) - act = state->fromStateAction->actListId+1; - return act; -} - -unsigned int GotoExp::EOF_ACTION( RedStateAp *state ) -{ - int act = 0; - if ( state->eofAction != 0 ) - act = state->eofAction->actListId+1; - return act; -} - -void GotoExp::NFA_PUSH_ACTION( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->push != 0 ) - act = targ->push->actListId+1; - nfaPushActions.value( act ); -} - -void GotoExp::NFA_POP_TEST( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->popTest != 0 ) - act = targ->popTest->actListId+1; - nfaPopTrans.value( act ); -} - - -void GotoExp::NFA_FROM_STATE_ACTION_EXEC() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " switch ( " << ARR_REF( fromStateActions ) << "[nfa_bp[nfa_len].state] ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - "\n"; - } -} - -void GotoExp::FROM_STATE_ACTIONS() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " switch ( " << ARR_REF( fromStateActions ) << "[" << vCS() << "] ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - "\n"; - } -} - -void GotoExp::TO_STATE_ACTIONS() -{ - if ( redFsm->anyToStateActions() ) { - out << - " switch ( " << ARR_REF( toStateActions ) << "[" << vCS() << "] ) {\n"; - TO_STATE_ACTION_SWITCH() << - " }\n" - "\n"; - } -} - -void GotoExp::REG_ACTIONS() -{ - -} - -void GotoExp::EOF_ACTIONS() -{ - if ( redFsm->anyEofActions() ) { - out << - " switch ( " << ARR_REF( eofActions ) << "[" << vCS() << "] ) {\n"; - EOF_ACTION_SWITCH() << - " }\n"; - } - -} diff --git a/libfsm/gotoexp.h b/libfsm/gotoexp.h deleted file mode 100644 index ddb3f138..00000000 --- a/libfsm/gotoexp.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef SWITCH_GOTO_EXP_H -#define SWITCH_GOTO_EXP_H - -#include -#include "goto.h" - -/* Forwards. */ -struct CodeGenData; - -/* - * class GotoExp - */ -class GotoExp - : public Goto -{ -public: - GotoExp( const CodeGenArgs &args ) - : Goto(args, Exp) {} - - virtual std::ostream &EXEC_FUNCS(); - virtual std::ostream &TO_STATE_ACTION_SWITCH(); - virtual std::ostream &FROM_STATE_ACTION_SWITCH(); - virtual std::ostream &EOF_ACTION_SWITCH(); - - unsigned int TO_STATE_ACTION( RedStateAp *state ); - unsigned int FROM_STATE_ACTION( RedStateAp *state ); - unsigned int EOF_ACTION( RedStateAp *state ); - - virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); - virtual void NFA_POP_TEST( RedNfaTarg *targ ); - virtual void NFA_FROM_STATE_ACTION_EXEC(); - - virtual void FROM_STATE_ACTIONS(); - virtual void TO_STATE_ACTIONS(); - virtual void REG_ACTIONS(); - virtual void EOF_ACTIONS(); -}; - -namespace C -{ - class GotoExp - : - public ::GotoExp - { - public: - GotoExp( const CodeGenArgs &args ) - : ::GotoExp( args ) - {} - }; -} - - -#endif diff --git a/libfsm/gotoloop.cc b/libfsm/gotoloop.cc deleted file mode 100644 index 1db3f2cd..00000000 --- a/libfsm/gotoloop.cc +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "gotoloop.h" -#include "redfsm.h" -#include "bstmap.h" -#include "gendata.h" -#include "parsedata.h" - -std::ostream &GotoLoop::ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numTransRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; - ACTION( out, act, IlOpts( 0, false, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &GotoLoop::EOF_ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numEofRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; - ACTION( out, act, IlOpts( 0, true, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &GotoLoop::FROM_STATE_ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numFromStateRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; - ACTION( out, act, IlOpts( 0, false, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -std::ostream &GotoLoop::TO_STATE_ACTION_SWITCH() -{ - /* Walk the list of functions, printing the cases. */ - for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { - /* Write out referenced actions. */ - if ( act->numToStateRefs > 0 ) { - /* Write the case label, the action and the case break. */ - out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; - ACTION( out, act, IlOpts( 0, false, false ) ); - out << "\n\t" << CEND() << "\n}\n"; - } - } - - return out; -} - -void GotoLoop::NFA_PUSH_ACTION( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->push != 0 ) - act = targ->push->actListId+1; - nfaPushActions.value( act ); -} - -void GotoLoop::NFA_POP_TEST( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->popTest != 0 ) - act = targ->popTest->actListId+1; - nfaPopTrans.value( act ); -} - -std::ostream &GotoLoop::EXEC_FUNCS() -{ - /* Make labels that set acts and jump to execFuncs. Loop func indices. */ - for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { - if ( redAct->numTransRefs > 0 ) { - out << " f" << redAct->actListId << ": " << - "" << acts << " = " << OFFSET( ARR_REF( actions ), itoa( redAct->location+1 ) ) << ";" - " goto execFuncs;\n"; - } - } - - out << - "\n" - "execFuncs:\n"; - - if ( redFsm->anyRegNbreak() ) - out << nbreak << " = 0;\n"; - - out << - " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; - ACTION_SWITCH() << - " }\n" - " " << acts << " += 1;\n" - " " << nacts << " -= 1;\n" - " }\n" - "\n"; - - if ( redFsm->anyRegNbreak() ) { - out << - " if ( " << nbreak << " == 1 )\n" - " goto " << _out << ";\n"; - } - - out << - " goto _again;\n"; - return out; -} - -void GotoLoop::NFA_FROM_STATE_ACTION_EXEC() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( fromStateActions ) + "[nfa_bp[nfa_len].state]" ) << ";\n" - " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" - " " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - " " << nacts << " -= 1;\n" - " " << acts << " += 1;\n" - " }\n" - "\n"; - } -} - -void GotoLoop::FROM_STATE_ACTIONS() -{ - if ( redFsm->anyFromStateActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), - ARR_REF( fromStateActions ) + "[" + vCS() + "]" ) << ";\n" - " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << "; " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; - FROM_STATE_ACTION_SWITCH() << - " }\n" - " " << acts << " += 1;\n" - " " << nacts << " -= 1;\n" - " }\n" - "\n"; - } -} - -void GotoLoop::TO_STATE_ACTIONS() -{ - if ( redFsm->anyToStateActions() ) { - out << - " " << acts << " = " << OFFSET( ARR_REF( actions ), - ARR_REF( toStateActions ) + "[" + vCS() + "]" ) << ";\n" - " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << "; " << acts << " += 1;\n" - " while ( " << nacts << " > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; - TO_STATE_ACTION_SWITCH() << - " }\n" - " " << acts << " += 1;\n" - " " << nacts << " -= 1;\n" - " }\n" - "\n"; - } -} - -void GotoLoop::REG_ACTIONS() -{ -} - -void GotoLoop::EOF_ACTIONS() -{ - if ( redFsm->anyEofActions() ) { - out << - " " << INDEX( ARR_TYPE( actions ), "__acts" ) << ";\n" - " " << UINT() << " __nacts;\n" - " __acts = " << OFFSET( ARR_REF( actions ), - ARR_REF( eofActions ) + "[" + vCS() + "]" ) << ";\n" - " __nacts = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "__acts" ) << "; __acts += 1;\n" - " while ( __nacts > 0 ) {\n" - " switch ( " << DEREF( ARR_REF( actions ), "__acts" ) << " ) {\n"; - EOF_ACTION_SWITCH() << - " }\n" - " __acts += 1;\n" - " __nacts -= 1;\n" - " }\n"; - } -} diff --git a/libfsm/gotoloop.h b/libfsm/gotoloop.h deleted file mode 100644 index 68c43ce2..00000000 --- a/libfsm/gotoloop.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef SWITCH_GOTO_LOOP_H -#define SWITCH_GOTO_LOOP_H - -#include -#include "goto.h" - -/* Forwards. */ -struct CodeGenData; -struct NameInst; -struct RedTransAp; -struct RedStateAp; -struct GenStateCond; - -class GotoLoop - : public Goto -{ -public: - GotoLoop( const CodeGenArgs &args ) - : Goto(args, Loop) {} - - virtual std::ostream &ACTION_SWITCH(); - virtual std::ostream &EXEC_FUNCS(); - virtual std::ostream &TO_STATE_ACTION_SWITCH(); - virtual std::ostream &FROM_STATE_ACTION_SWITCH(); - virtual std::ostream &EOF_ACTION_SWITCH(); - - virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); - virtual void NFA_POP_TEST( RedNfaTarg *targ ); - virtual void NFA_FROM_STATE_ACTION_EXEC(); - - virtual void FROM_STATE_ACTIONS(); - virtual void TO_STATE_ACTIONS(); - virtual void REG_ACTIONS(); - virtual void EOF_ACTIONS(); -}; - -namespace C -{ - class GotoLoop - : - public ::GotoLoop - { - public: - GotoLoop( const CodeGenArgs &args ) - : ::GotoLoop( args ) - {} - }; -} - -#endif diff --git a/libfsm/idbase.cc b/libfsm/idbase.cc deleted file mode 100644 index 38850b56..00000000 --- a/libfsm/idbase.cc +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2008-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "fsmgraph.h" -#include "parsedata.h" -#include "action.h" - -/* Error reporting format. */ -ErrorFormat errorFormat = ErrorFormatGNU; - -void FsmCtx::finalizeInstance( FsmAp *graph ) -{ - /* Resolve any labels that point to multiple states. Any labels that are - * still around are referenced only by gotos and calls and they need to be - * made into deterministic entry points. */ - graph->deterministicEntry(); - - /* - * All state construction is now complete. - */ - - /* Transfer actions from the out action tables to eof action tables. */ - for ( StateSet::Iter state = graph->finStateSet; state.lte(); state++ ) - graph->transferOutActions( *state ); - - /* Transfer global error actions. */ - for ( StateList::Iter state = graph->stateList; state.lte(); state++ ) - graph->transferErrorActions( state, 0 ); - - if ( fsmGbl->wantDupsRemoved ) - graph->removeActionDups(); - - /* Remove unreachable states. There should be no dead end states. The - * subtract and intersection operators are the only places where they may - * be created and those operators clean them up. */ - graph->removeUnreachableStates(); - - /* No more fsm operations are to be done. Action ordering numbers are - * no longer of use and will just hinder minimization. Clear them. */ - graph->nullActionKeys(); - - /* Transition priorities are no longer of use. We can clear them - * because they will just hinder minimization as well. Clear them. */ - graph->clearAllPriorities(); - - if ( graph->ctx->minimizeOpt != MinimizeNone ) { - /* Minimize here even if we minimized at every op. Now that function - * keys have been cleared we may get a more minimal fsm. */ - switch ( graph->ctx->minimizeLevel ) { - #ifdef TO_UPGRADE_CONDS - case MinimizeApprox: - graph->minimizeApproximate(); - break; - #endif - #ifdef TO_UPGRADE_CONDS - case MinimizeStable: - graph->minimizeStable(); - break; - #endif - case MinimizePartition1: - graph->minimizePartition1(); - break; - case MinimizePartition2: - graph->minimizePartition2(); - break; - } - } - - graph->compressTransitions(); - - createNfaActions( graph ); -} - -void FsmCtx::analyzeAction( Action *action, InlineList *inlineList ) -{ - /* FIXME: Actions used as conditions should be very constrained. */ - for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) { - if ( item->type == InlineItem::Call || item->type == InlineItem::CallExpr || - item->type == InlineItem::Ncall || item->type == InlineItem::NcallExpr ) - { - action->anyCall = true; - } - - /* Need to recurse into longest match items. */ - if ( item->type == InlineItem::LmSwitch ) { - FsmLongestMatch *lm = item->longestMatch; - for ( FsmLmPartList::Iter lmi = *lm->longestMatchList; lmi.lte(); lmi++ ) { - if ( lmi->action != 0 ) - analyzeAction( action, lmi->action->inlineList ); - } - } - - if ( item->type == InlineItem::LmOnLast || - item->type == InlineItem::LmOnNext || - item->type == InlineItem::LmOnLagBehind ) - { - FsmLongestMatchPart *lmi = item->longestMatchPart; - if ( lmi->action != 0 ) - analyzeAction( action, lmi->action->inlineList ); - } - - if ( item->children != 0 ) - analyzeAction( action, item->children ); - } -} - -/* Check actions for bad uses of fsm directives. We don't go inside longest - * match items in actions created by ragel, since we just want the user - * actions. */ -void FsmCtx::checkInlineList( Action *act, InlineList *inlineList ) -{ - for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) { - /* EOF checks. */ - if ( act->numEofRefs > 0 ) { - switch ( item->type ) { - /* Currently no checks. */ - default: - break; - } - } - - /* Recurse. */ - if ( item->children != 0 ) - checkInlineList( act, item->children ); - } -} - -void FsmCtx::checkAction( Action *action ) -{ - /* Check for actions with calls that are embedded within a longest match - * machine. */ - if ( !action->isLmAction && action->numRefs() > 0 && action->anyCall ) { - for ( NameInstVect::Iter ar = action->embedRoots; ar.lte(); ar++ ) { - NameInst *check = *ar; - while ( check != 0 ) { - if ( check->isLongestMatch ) { - fsmGbl->error(action->loc) << "within a scanner, fcall and fncall are permitted" - " only in pattern actions" << endl; - break; - } - check = check->parent; - } - } - } - - checkInlineList( action, action->inlineList ); -} - -void FsmCtx::analyzeGraph( FsmAp *graph ) -{ - for ( ActionList::Iter act = actionList; act.lte(); act++ ) - analyzeAction( act, act->inlineList ); - - for ( StateList::Iter st = graph->stateList; st.lte(); st++ ) { - /* The transition list. */ - for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { - //if ( trans->condSpace != 0 ) { - // for ( CondSet::Iter sci = trans->condSpace->condSet; sci.lte(); sci++ ) - // (*sci)->numCondRefs += 1; - //} - - if ( trans->plain() ) { - for ( ActionTable::Iter at = trans->tdap()->actionTable; at.lte(); at++ ) - at->value->numTransRefs += 1; - } - else { - for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { - for ( ActionTable::Iter at = cond->actionTable; at.lte(); at++ ) - at->value->numTransRefs += 1; - } - } - } - - for ( ActionTable::Iter at = st->toStateActionTable; at.lte(); at++ ) - at->value->numToStateRefs += 1; - - for ( ActionTable::Iter at = st->fromStateActionTable; at.lte(); at++ ) - at->value->numFromStateRefs += 1; - - for ( ActionTable::Iter at = st->eofActionTable; at.lte(); at++ ) - at->value->numEofRefs += 1; - - //for ( OutCondSet::Iter oci = st->outCondSet; oci.lte(); oci++ ) - // oci->action->numCondRefs += 1; - - if ( st->nfaOut != 0 ) { - for ( NfaTransList::Iter n = *st->nfaOut; n.lte(); n++ ) { - for ( ActionTable::Iter ati = n->pushTable; ati.lte(); ati++ ) - ati->value->numNfaRefs += 1; - - for ( ActionTable::Iter ati = n->restoreTable; ati.lte(); ati++ ) - ati->value->numNfaRefs += 1; - - for ( ActionTable::Iter ati = n->popAction; ati.lte(); ati++ ) - ati->value->numNfaRefs += 1; - - for ( ActionTable::Iter ati = n->popTest; ati.lte(); ati++ ) - ati->value->numNfaRefs += 1; - } - } - } - - /* Can't count on cond references in transitions, since we don't refcount - * the spaces. FIXME: That would be the proper solution. */ - for ( CondSpaceMap::Iter cs = condData->condSpaceMap; cs.lte(); cs++ ) { - for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ ) - (*csi)->numCondRefs += 1; - } - - /* Checks for bad usage of directives in action code. */ - for ( ActionList::Iter act = actionList; act.lte(); act++ ) - checkAction( act ); -} - -/* This create an action that refs the original embed roots, if the optWrap arg - * is supplied. */ -Action *FsmCtx::newNfaWrapAction( const char *name, InlineList *inlineList, Action *optWrap ) -{ - InputLoc loc; - loc.line = 1; - loc.col = 1; - loc.fileName = "NONE"; - - Action *action = new Action( loc, name, inlineList, nextCondId++ ); - - if ( optWrap != 0 ) - action->embedRoots.append( optWrap->embedRoots ); - - actionList.append( action ); - return action; -} - -void FsmCtx::createNfaActions( FsmAp *fsm ) -{ - for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { - if ( st->nfaOut != 0 ) { - for ( NfaTransList::Iter n = *st->nfaOut; n.lte(); n++ ) { - /* Move pop restore actions into poptest. Wrap to override the - * condition-like testing. */ - for ( ActionTable::Iter ati = n->restoreTable; ati.lte(); ati++ ) { - n->popTest.setAction( ati->key, ati->value ); - } - - /* Move pop actions into pop test. Wrap to override the - * condition-like testing. */ - for ( ActionTable::Iter ati = n->popFrom; ati.lte(); ati++ ) { - - InlineList *il1 = new InlineList; - il1->append( new InlineItem( InputLoc(), - ati->value, InlineItem::NfaWrapAction ) ); - Action *wrap = newNfaWrapAction( "action_wrap", il1, ati->value ); - n->popTest.setAction( ORD_COND2, wrap ); - } - - /* Move condition evaluation into pop test. Wrap with condition - * execution. */ - if ( n->popCondSpace != 0 ) { - InlineList *il1 = new InlineList; - il1->append( new InlineItem( InputLoc(), - n->popCondSpace, n->popCondKeys, - InlineItem::NfaWrapConds ) ); - Action *wrap = newNfaWrapAction( "cond_wrap", il1, 0 ); - n->popTest.setAction( ORD_COND, wrap ); - } - - /* Move pop actions into pop test. Wrap to override the - * condition-like testing. */ - for ( ActionTable::Iter ati = n->popAction; ati.lte(); ati++ ) { - - InlineList *il1 = new InlineList; - il1->append( new InlineItem( InputLoc(), - ati->value, InlineItem::NfaWrapAction ) ); - Action *wrap = newNfaWrapAction( "action_wrap", il1, ati->value ); - n->popTest.setAction( ati->key, wrap ); - } - } - } - } -} - -void FsmCtx::prepareReduction( FsmAp *sectionGraph ) -{ - /* Decide if an error state is necessary. - * 1. There is an error transition - * 2. There is a gap in the transitions - * 3. The longest match operator requires it. */ - if ( lmRequiresErrorState || sectionGraph->hasErrorTrans() ) - sectionGraph->errState = sectionGraph->addState(); - - /* State numbers need to be assigned such that all final states have a - * larger state id number than all non-final states. This enables the - * first_final mechanism to function correctly. We also want states to be - * ordered in a predictable fashion. So we first apply a depth-first - * search, then do a stable sort by final state status, then assign - * numbers. */ - - sectionGraph->depthFirstOrdering(); - sectionGraph->sortStatesByFinal(); - sectionGraph->setStateNumbers( 0 ); -} - - -void translatedHostData( ostream &out, const std::string &data ) -{ - const char *p = data.c_str(); - for ( const char *c = p; *c != 0; ) { - if ( c[0] == '}' && ( c[1] == '@' || c[1] == '$' || c[1] == '=' ) ) { - out << "@}@" << c[1]; - c += 2; - } - else if ( c[0] == '@' ) { - out << "@@"; - c += 1; - } - // Have some escaping issues that these fix, but they lead to other problems. - // Can be reproduced by passing "={}" through ragel and adding --colm-backend - // else if ( c[0] == '=' ) { - // out << "@="; - // c += 1; - //} - // else if ( c[0] == '$' ) { - // out << "@$"; - // c += 1; - //} - else { - out << c[0]; - c += 1; - } - } -} - - -void FsmGbl::abortCompile( int code ) -{ - throw AbortCompile( code ); -} - -/* Print the opening to a warning in the input, then return the error ostream. */ -ostream &FsmGbl::warning( const InputLoc &loc ) -{ - ostream &err = std::cerr; - err << loc << ": warning: "; - return err; -} - -/* Print the opening to a program error, then return the error stream. */ -ostream &FsmGbl::error() -{ - errorCount += 1; - ostream &err = std::cerr; - err << PROGNAME ": "; - return err; -} - -ostream &FsmGbl::error( const InputLoc &loc ) -{ - errorCount += 1; - ostream &err = std::cerr; - err << loc << ": "; - return err; -} - -ostream &FsmGbl::error_plain() -{ - errorCount += 1; - ostream &err = std::cerr; - return err; -} - - -std::ostream &FsmGbl::stats() -{ - return std::cout; -} - -/* Requested info. */ -std::ostream &FsmGbl::info() -{ - return std::cout; -} - -ostream &operator<<( ostream &out, const InputLoc &loc ) -{ - assert( loc.fileName != 0 ); - switch ( errorFormat ) { - case ErrorFormatMSVC: - out << loc.fileName << "(" << loc.line; - if ( loc.col ) - out << "," << loc.col; - out << ")"; - break; - - default: - out << loc.fileName << ":" << loc.line; - if ( loc.col ) - out << ":" << loc.col; - break; - } - return out; -} - diff --git a/libfsm/idbase.h b/libfsm/idbase.h deleted file mode 100644 index a34837a8..00000000 --- a/libfsm/idbase.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _IDBASE_H -#define _IDBASE_H - -void translatedHostData( ostream &out, const std::string &data ); - -#endif - diff --git a/libfsm/ipgoto.cc b/libfsm/ipgoto.cc deleted file mode 100644 index 3718ea91..00000000 --- a/libfsm/ipgoto.cc +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "ipgoto.h" -#include "redfsm.h" -#include "gendata.h" -#include "bstmap.h" -#include "parsedata.h" - -#include - -using std::ostringstream; - -void IpGoto::tableDataPass() -{ - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); -} - -void IpGoto::genAnalysis() -{ - /* For directly executable machines there is no required state - * ordering. Choose a depth-first ordering to increase the - * potential for fall-throughs. */ - redFsm->depthFirstOrdering(); - - /* Choose default transitions and the single transition. */ - redFsm->chooseDefaultSpan(); - - /* Choose single. */ - redFsm->moveSelectTransToSingle(); - - /* If any errors have occured in the input file then don't write anything. */ - if ( red->id->errorCount > 0 ) - return; - - redFsm->setInTrans(); - - /* Anlayze Machine will find the final action reference counts, among other - * things. We will use these in reporting the usage of fsm directives in - * action code. */ - red->analyzeMachine(); - - /* Run the analysis pass over the table data. */ - setTableState( TableArray::AnalyzePass ); - tableDataPass(); - - /* Switch the tables over to the code gen mode. */ - setTableState( TableArray::GeneratePass ); -} - -bool IpGoto::useAgainLabel() -{ - return redFsm->anyActionRets() || - redFsm->anyActionByValControl() || - redFsm->anyRegNextStmt(); -} - -void IpGoto::GOTO( ostream &ret, int gotoDest, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - ret << "goto " << stLabel[gotoDest].reference() << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void IpGoto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - ret << " goto " << _again << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void IpGoto::CALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << targState << - "; " << TOP() << "+= 1; "; - - ret << "goto " << stLabel[callDest].reference() << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void IpGoto::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << targState << - "; " << TOP() << "+= 1; " << vCS() << " = " << callDest << "; " << - CLOSE_GEN_BLOCK(); -} - -void IpGoto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << targState << "; " << TOP() << "+= 1;" << - vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - ret << " goto " << _again << ";"; - - ret << CLOSE_GEN_BLOCK(); -} - -void IpGoto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << targState << "; " << TOP() << "+= 1;" << - vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); -} - -void IpGoto::RET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " - << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << "goto " << _again << ";" << CLOSE_GEN_BLOCK(); -} - -void IpGoto::NRET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " - << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << CLOSE_GEN_BLOCK(); -} - -void IpGoto::NEXT( ostream &ret, int nextDest, bool inFinish ) -{ - ret << vCS() << " = " << nextDest << ";"; -} - -void IpGoto::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << vCS() << " = ("; - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << ");"; -} - -void IpGoto::CURS( ostream &ret, bool inFinish ) -{ - ret << "(" << ps << ")"; -} - -void IpGoto::TARGS( ostream &ret, bool inFinish, int targState ) -{ - ret << targState; -} - -void IpGoto::BREAK( ostream &ret, int targState, bool csForced ) -{ - ret << OPEN_GEN_BLOCK() << P() << "+= 1; "; - if ( !csForced ) - ret << vCS() << " = " << targState << "; "; - ret << "goto " << _out << ";" << CLOSE_GEN_BLOCK(); -} - -void IpGoto::NBREAK( ostream &ret, int targState, bool csForced ) -{ - ret << OPEN_GEN_BLOCK() << P() << "+= 1; "; - if ( !csForced ) - ret << vCS() << " = " << targState << "; "; - ret << nbreak << " = 1;" << CLOSE_GEN_BLOCK(); -} - -void IpGoto::NFA_PUSH_ACTION( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->push != 0 ) - act = targ->push->actListId+1; - nfaPushActions.value( act ); -} - -void IpGoto::NFA_POP_TEST( RedNfaTarg *targ ) -{ - int act = 0; - if ( targ->popTest != 0 ) - act = targ->popTest->actListId+1; - nfaPopTrans.value( act ); -} - - -bool IpGoto::IN_TRANS_ACTIONS( RedStateAp *state ) -{ - bool anyWritten = false; - - /* Emit any transitions that have actions and that go to this state. */ - for ( int it = 0; it < state->numInConds; it++ ) { - RedCondPair *trans = state->inConds[it]; - if ( trans->action != 0 ) { - /* Remember that we wrote an action so we know to write the - * line directive for going back to the output. */ - anyWritten = true; - - /* Write the label for the transition so it can be jumped to. */ - if ( ctrLabel[trans->id].isReferenced ) - out << "_ctr" << trans->id << ":\n"; - - /* If the action contains a next, then we must preload the current - * state since the action may or may not set it. */ - if ( trans->action->anyNextStmt() ) - out << " " << vCS() << " = " << trans->targ->id << ";\n"; - - if ( redFsm->anyRegNbreak() ) - out << nbreak << " = 0;\n"; - - /* Write each action in the list. */ - for ( GenActionTable::Iter item = trans->action->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( trans->targ->id, false, - trans->action->anyNextStmt() ) ); - out << "\n"; - } - - if ( redFsm->anyRegNbreak() ) { - out << - "if ( " << nbreak << " == 1 )\n" - " goto " << _out << ";\n"; - } - - - /* If the action contains a next then we need to reload, otherwise - * jump directly to the target state. */ - if ( trans->action->anyNextStmt() ) - out << "goto " << _again << ";\n"; - else - out << "goto " << stLabel[trans->targ->id].reference() << ";\n"; - } - } - - - return anyWritten; -} - -void IpGoto::GOTO_HEADER( RedStateAp *state ) -{ -} - -void IpGoto::STATE_GOTO_ERROR() -{ -} - - -/* Emit the goto to take for a given transition. */ -std::ostream &IpGoto::TRANS_GOTO( RedTransAp *trans ) -{ - if ( trans->condSpace == 0 || trans->condSpace->condSet.length() == 0 ) { - /* Existing. */ - assert( trans->numConds() == 1 ); - RedCondPair *cond = trans->outCond( 0 ); - if ( cond->action != 0 ) { - /* Go to the transition which will go to the state. */ - out << "goto " << ctrLabel[trans->p.id].reference() << ";"; - } - else { - /* Go directly to the target state. */ - out << "goto " << stLabel[cond->targ->id].reference() << ";"; - } - } - else { - out << ck << " = 0;\n"; - for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { - out << "if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - out << " )\n" << ck << " += " << condValOffset << ";\n"; - } - CondKey lower = 0; - CondKey upper = trans->condFullSize() - 1; - COND_B_SEARCH( trans, lower, upper, 0, trans->numConds() - 1 ); - - if ( trans->errCond() != 0 ) { - COND_GOTO( trans->errCond() ) << "\n"; - } - } - - return out; -} - -/* Emit the goto to take for a given transition. */ -std::ostream &IpGoto::COND_GOTO( RedCondPair *cond ) -{ - /* Existing. */ - if ( cond->action != 0 ) { - /* Go to the transition which will go to the state. */ - out << "goto " << ctrLabel[cond->id].reference() << ";"; - } - else { - /* Go directly to the target state. */ - out << "goto " << stLabel[cond->targ->id].reference() << ";"; - } - - return out; -} - -std::ostream &IpGoto::EXIT_STATES() -{ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( outLabel[st->id].isReferenced ) { - out << outLabel[st->id].define() << ": " << vCS() << " = " << - st->id << "; goto " << _out << "; \n"; - } - if ( popLabel[st->id].isReferenced ) { - out << popLabel[st->id].define() << ": " << vCS() << " = " << - st->id << "; goto " << _pop << "; \n"; - } - } - return out; -} - -std::ostream &IpGoto::AGAIN_CASES() -{ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - out << - "case " << st->id << ": goto " << stLabel[st->id].reference() << ";\n"; - } - return out; -} - -std::ostream &IpGoto::STATE_GOTO_CASES() -{ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - out << "case " << st->id << ":\n"; - out << "goto st_case_" << st->id << ";\n"; - } - return out; -} - -void IpGoto::NFA_PUSH_ST( RedStateAp *state ) -{ - std::stringstream ss; - ss << state->id; - std::string _state = ss.str(); - - if ( redFsm->anyNfaStates() ) { - - if ( state->nfaTargs != 0 ) { - out << - "if ( " << ARR_REF( nfaOffsets ) << "[" << _state << "] != 0 ) {\n"; - - if ( red->nfaPrePushExpr != 0 ) { - out << - new_recs << " = " << state->nfaTargs->length() << ";\n"; - } - - if ( red->nfaPrePushExpr != 0 ) { - out << OPEN_HOST_BLOCK( red->nfaPrePushExpr ); - INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); - out << CLOSE_HOST_BLOCK(); - } - - int alt = 0; - for ( RedNfaTargs::Iter nt = *state->nfaTargs; nt.lte(); nt++ ) { - out << - " nfa_bp[nfa_len].state = " << nt->state->id << ";\n" - " nfa_bp[nfa_len].p = " << P() << ";\n"; - - if ( nt->popTest != 0 ) { - out << - " nfa_bp[nfa_len].popTrans = " << (nt->popTest->actListId+1) << ";\n"; - } - else if ( redFsm->bAnyNfaPops ) { - out << - " nfa_bp[nfa_len].popTrans = 0;\n"; - } - - if ( nt->push != 0 ) { - for ( GenActionTable::Iter item = nt->push->key; item.lte(); item++ ) - ACTION( out, item->value, IlOpts( 0, false, false ) ); - } - - out << - " nfa_len += 1;\n"; - - alt += 1; - } - - out << - "}\n"; - } - } -} - -std::ostream &IpGoto::STATE_GOTOS() -{ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - IN_TRANS_ACTIONS( st ); - - if ( stLabel[st->id].isReferenced ) - out << "_st" << st->id << ":\n"; - - /* Need to do this if the transition is an eof transition, or if - * the action contains fexec. Otherwise, no need. */ - if ( eof ) { - out << - "if ( " << P() << " == " << vEOF() << " )\n"; - - if ( st->isFinal || !redFsm->anyNfaStates() ) - out << "goto " << outLabel[st->id].reference() << ";\n"; - else - out << "goto " << popLabel[st->id].reference() << ";\n"; - } - - if ( st->toStateAction != 0 ) { - /* Write every action in the list. */ - for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( st->id, false, - st->toStateAction->anyNextStmt() ) ); - out << "\n"; - } - } - - if ( st == redFsm->errState ) { - out << "st_case_" << st->id << ":\n"; - - /* Break out here. */ - if ( !redFsm->anyNfaStates() ) - out << "goto " << outLabel[st->id].reference() << ";\n"; - else - out << "goto " << popLabel[st->id].reference() << ";\n"; - } - else { - - /* Advance and test buffer pos. */ - if ( st->labelNeeded ) { - out << - P() << "+= 1;\n"; - } - - /* Give the st a switch case. */ - out << "st_case_" << st->id << ":\n"; - - if ( !noEnd ) { - if ( eof ) { - out << - "if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" - " goto " << outLabel[st->id].reference() << ";\n"; - } - else { - out << - "if ( " << P() << " == " << PE() << " )\n" - " goto " << outLabel[st->id].reference() << ";\n"; - } - } - - - NFA_PUSH_ST( st ); - - if ( st->fromStateAction != 0 ) { - /* Write every action in the list. */ - for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ ) { - ACTION( out, item->value, IlOpts( st->id, false, - st->fromStateAction->anyNextStmt() ) ); - out << "\n"; - } - } - - if ( !noEnd && eof ) { - out << - "if ( " << P() << " == " << vEOF() << " ) {\n"; - - if ( st->eofTrans != 0 ) - TRANS_GOTO( st->eofTrans ); - else { - if ( st->isFinal || !redFsm->anyNfaStates() ) - out << "goto " << outLabel[st->id].reference() << ";\n"; - else - out << "goto " << popLabel[st->id].reference() << ";\n"; - } - - out << - "}\n" - "else {\n"; - } - - /* Record the prev st if necessary. */ - if ( st->anyRegCurStateRef() ) - out << ps << " = " << st->id << ";\n"; - - /* Try singles. */ - if ( st->outSingle.length() > 0 ) - SINGLE_SWITCH( st ); - - /* Default case is to binary search for the ranges, if that fails then */ - if ( st->outRange.length() > 0 ) { - RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey, - 0, st->outRange.length() - 1 ); - } - - /* Write the default transition. */ - TRANS_GOTO( st->defTrans ) << "\n"; - - if ( !noEnd && eof ) { - out << - "}\n"; - } - } - } - return out; -} - -std::ostream &IpGoto::FINISH_CASES() -{ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - out << - "case " << st->id << ":\n"; - - TRANS_GOTO( st->eofTrans ); - } - } - - return out; -} - -void IpGoto::setLabelsNeeded( GenInlineList *inlineList ) -{ - for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { - switch ( item->type ) { - case GenInlineItem::Goto: - case GenInlineItem::Call: - case GenInlineItem::Ncall: { - /* Mark the target as needing a label. */ - item->targState->labelNeeded = true; - break; - } - default: break; - } - - if ( item->children != 0 ) - setLabelsNeeded( item->children ); - } -} - -void IpGoto::setLabelsNeeded( RedCondPair *pair ) -{ - /* If there is no action with a next statement, then the label will be - * needed. */ - if ( pair->action == 0 || !pair->action->anyNextStmt() ) - pair->targ->labelNeeded = true; - - /* Need labels for states that have goto or calls in action code - * invoked on characters (ie, not from out action code). */ - if ( pair->action != 0 ) { - /* Loop the actions. */ - for ( GenActionTable::Iter act = pair->action->key; act.lte(); act++ ) { - /* Get the action and walk it's tree. */ - setLabelsNeeded( act->value->inlineList ); - } - } -} - -/* Set up labelNeeded flag for each state. */ -void IpGoto::setLabelsNeeded() -{ - /* If we use the _again label, then we generate the _again switch, which - * uses all labels. */ - if ( useAgainLabel() ) { - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - st->labelNeeded = true; - } - else { - /* Do not use all labels by default, init all labelNeeded vars to false. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - st->labelNeeded = false; - - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - if ( trans->condSpace == 0 ) - setLabelsNeeded( &trans->p ); - } - - for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) - setLabelsNeeded( &cond->p ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofAction != 0 ) { - for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) - setLabelsNeeded( item->value->inlineList ); - } - } - } -} - -void IpGoto::writeData() -{ - STATE_IDS(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); -} - -void IpGoto::NFA_FROM_STATE_ACTION_EXEC() -{ -// if ( redFsm->anyFromStateActions() ) { -// /* Unimplemented feature. Don't have the from state actions array in -// * this mode. Need to add it, or to alter the NFA pop codegen to be -// * consistent with the mode. */ -// assert( false ); -// } -} - -void IpGoto::writeExec() -{ - int maxCtrId = redFsm->nextCondId > redFsm->nextTransId ? redFsm->nextCondId : redFsm->nextTransId; - - stLabel = allocateLabels( stLabel, IpLabel::St, redFsm->nextStateId ); - ctrLabel = allocateLabels( ctrLabel, IpLabel::Ctr, maxCtrId ); - outLabel = allocateLabels( outLabel, IpLabel::Out, redFsm->nextStateId ); - popLabel = allocateLabels( popLabel, IpLabel::Pop, redFsm->nextStateId ); - - /* Must set labels immediately before writing because we may depend on the - * noend write option. */ - setLabelsNeeded(); - - out << "{\n"; - - DECLARE( INT(), cpc ); - DECLARE( INT(), ck ); - DECLARE( INT(), pop_test ); - DECLARE( INT(), nbreak ); - DECLARE( INT(), ps ); - DECLARE( INT(), new_recs ); - DECLARE( INT(), alt ); - - if ( _again.isReferenced ) { - out << - " goto " << _resume << ";\n" - "\n"; - - out << EMIT_LABEL( _again ); - - out << - " switch ( " << vCS() << " ) {\n"; - AGAIN_CASES() << - " }\n" - "\n"; - - } - - out << EMIT_LABEL( _resume ); - - out << "switch ( " << vCS() << " ) {\n"; - - STATE_GOTO_CASES(); - - out << "}\n"; - - STATE_GOTOS(); - - EXIT_STATES(); - - out << EMIT_LABEL( _pop ); - - if ( redFsm->anyNfaStates() ) { - out << - "if ( nfa_len == 0 )\n" - " goto " << _out << ";\n" - "\n"; - - out << - "nfa_count += 1;\n" - "nfa_len -= 1;\n" << - P() << " = nfa_bp[nfa_len].p;\n"; - - if ( redFsm->bAnyNfaPops ) { - NFA_FROM_STATE_ACTION_EXEC(); - - NFA_POP_TEST_EXEC(); - - out << - "if ( " << pop_test << " )\n" - " " << vCS() << " = nfa_bp[nfa_len].state;\n" - "else\n" - " " << vCS() << " = " << ERROR_STATE() << ";\n"; - } - else { - out << - vCS() << " = nfa_bp[nfa_len].state;\n"; - - } - - NFA_POST_POP(); - - out << "goto " << _resume << ";\n"; - } - - out << EMIT_LABEL( _out ); - - out << - "}\n"; -} diff --git a/libfsm/ipgoto.h b/libfsm/ipgoto.h deleted file mode 100644 index 1ec51bbf..00000000 --- a/libfsm/ipgoto.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef IPGOTO_H -#define IPGOTO_H - -#include -#include "goto.h" - -/* Forwards. */ -struct CodeGenData; - -/* - * class FGotoCodeGen - */ -class IpGoto - : public Goto -{ -public: - IpGoto( const CodeGenArgs &args ) - : - Goto( args, Ip ), - stLabel(0), - ctrLabel(0), - outLabel(0), - popLabel(0) - {} - - std::ostream &EXIT_STATES(); - std::ostream &TRANS_GOTO( RedTransAp *trans ); - std::ostream &COND_GOTO( RedCondPair *trans ); - std::ostream &FINISH_CASES(); - std::ostream &AGAIN_CASES(); - std::ostream &STATE_GOTOS(); - std::ostream &STATE_GOTO_CASES(); - - /* unused. */ - virtual std::ostream &ACTION_SWITCH() { return out; } - virtual std::ostream &EXEC_FUNCS() { return out; } - virtual std::ostream &TO_STATE_ACTION_SWITCH() { return out; } - virtual std::ostream &FROM_STATE_ACTION_SWITCH() { return out; } - virtual std::ostream &EOF_ACTION_SWITCH() { return out; } - - /* Unused */ - virtual void FROM_STATE_ACTIONS() {} - virtual void TO_STATE_ACTIONS() {} - virtual void REG_ACTIONS() {} - virtual void EOF_ACTIONS() {} - - void GOTO( ostream &ret, int gotoDest, bool inFinish ); - void CALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NEXT( ostream &ret, int nextDest, bool inFinish ); - void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void RET( ostream &ret, bool inFinish ); - void NRET( ostream &ret, bool inFinish ); - void CURS( ostream &ret, bool inFinish ); - void TARGS( ostream &ret, bool inFinish, int targState ); - void BREAK( ostream &ret, int targState, bool csForced ); - void NBREAK( ostream &ret, int targState, bool csForced ); - - virtual void genAnalysis(); - virtual void writeData(); - virtual void writeExec(); - -protected: - bool useAgainLabel(); - - /* Called from Goto::STATE_GOTOS just before writing the gotos for - * each state. */ - bool IN_TRANS_ACTIONS( RedStateAp *state ); - void GOTO_HEADER( RedStateAp *state ); - void STATE_GOTO_ERROR(); - - /* Set up labelNeeded flag for each state. */ - void setLabelsNeeded( RedCondPair *pair ); - void setLabelsNeeded( GenInlineList *inlineList ); - void setLabelsNeeded(); - - void NFA_PUSH_ACTION( RedNfaTarg *targ ); - void NFA_POP_TEST( RedNfaTarg *targ ); - virtual void NFA_FROM_STATE_ACTION_EXEC(); - - void NFA_PUSH_ST( RedStateAp *state ); - - void tableDataPass(); - - IpLabel *stLabel; - IpLabel *ctrLabel; - IpLabel *outLabel; - IpLabel *popLabel; -}; - -namespace C -{ - class IpGoto - : - public ::IpGoto - { - public: - IpGoto( const CodeGenArgs &args ) - : ::IpGoto( args ) - {} - }; -} - -#endif diff --git a/libfsm/parsedata.h b/libfsm/parsedata.h deleted file mode 100644 index 4d11b1c7..00000000 --- a/libfsm/parsedata.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _PARSEDATA_H -#define _PARSEDATA_H - - -#endif diff --git a/libfsm/ragel-config.cmake.in b/libfsm/ragel-config.cmake.in deleted file mode 100644 index 8de5d2cb..00000000 --- a/libfsm/ragel-config.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -# @_PACKAGE_NAME@-config.cmake Generated from ragel-config.cmake.in by cmake - -include("${CMAKE_CURRENT_LIST_DIR}/@_PACKAGE_NAME@-targets.cmake") diff --git a/libfsm/ragel.h b/libfsm/ragel.h deleted file mode 100644 index c3fd6f22..00000000 --- a/libfsm/ragel.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _RAGEL_H -#define _RAGEL_H - -#include -#include -#include -#include -#include "vector.h" -#include "config.h" -#include "common.h" - -#define PROGNAME "ragel" - -#define MAIN_MACHINE "main" - -/* Target output style. */ -enum CodeStyle -{ - GenBinaryLoop, - GenBinaryExp, - GenFlatLoop, - GenFlatExp, - GenGotoLoop, - GenGotoExp, - GenSwitchLoop, - GenSwitchExp, - GenIpGoto -}; - -/* To what degree are machine minimized. */ -enum MinimizeLevel { - #ifdef TO_UPGRADE_CONDS - MinimizeApprox, - #endif - #ifdef TO_UPGRADE_CONDS - MinimizeStable, - #endif - MinimizePartition1, - MinimizePartition2 -}; - -enum MinimizeOpt { - MinimizeNone, - MinimizeEnd, - MinimizeMostOps, - MinimizeEveryOp -}; - -/* Target implementation */ -enum RubyImplEnum -{ - MRI, - Rubinius -}; - -/* Error reporting format. */ -enum ErrorFormat { - ErrorFormatGNU, - ErrorFormatMSVC, -}; - -extern ErrorFormat errorFormat; - - -struct colm_location; - -InputLoc makeInputLoc( const char *fileName, int line = 0, int col = 0 ); -InputLoc makeInputLoc( const struct colm_location *loc ); -std::ostream &operator<<( std::ostream &out, const InputLoc &loc ); - -void xmlEscapeHost( std::ostream &out, const char *data, long len ); - - -using std::endl; - -extern const char mainMachine[]; - -struct AbortCompile -{ - AbortCompile( int code ) - : code(code) {} - - int code; -}; - -#endif diff --git a/libfsm/redfsm.cc b/libfsm/redfsm.cc deleted file mode 100644 index 1b83e5b5..00000000 --- a/libfsm/redfsm.cc +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "redfsm.h" -#include "avlmap.h" -#include "mergesort.h" -#include "fsmgraph.h" -#include -#include -#include - -using std::ostringstream; - -GenInlineItem::~GenInlineItem() -{ - if ( children != 0 ) { - children->empty(); - delete children; - } -} - -string GenAction::nameOrLoc() -{ - if ( name.empty() ) { - ostringstream ret; - ret << loc.line << ":" << loc.col; - return ret.str(); - } - else { - return name; - } -} - -RedFsmAp::RedFsmAp( FsmCtx *fsmCtx, int machineId ) -: - keyOps(fsmCtx->keyOps), - fsmCtx(fsmCtx), - machineId(machineId), - forcedErrorState(false), - nextActionId(0), - nextTransId(0), - nextCondId(0), - startState(0), - errState(0), - errTrans(0), - errCond(0), - firstFinState(0), - numFinStates(0), - bAnyToStateActions(false), - bAnyFromStateActions(false), - bAnyRegActions(false), - bAnyEofActions(false), - bAnyEofTrans(false), - bAnyEofActivity(false), - bAnyActionGotos(false), - bAnyActionCalls(false), - bAnyActionNcalls(false), - bAnyActionRets(false), - bAnyActionNrets(false), - bAnyActionByValControl(false), - bAnyRegActionRets(false), - bAnyRegActionByValControl(false), - bAnyRegNextStmt(false), - bAnyRegCurStateRef(false), - bAnyRegBreak(false), - bAnyRegNbreak(false), - bUsingAct(false), - bAnyNfaStates(false), - bAnyNfaPushPops(false), - bAnyNfaPushes(false), - bAnyNfaPops(false), - bAnyTransCondRefs(false), - bAnyNfaCondRefs(false), - nextClass(0), - classMap(0) -{ -} - -RedFsmAp::~RedFsmAp() -{ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - delete[] st->transList; - if ( st->nfaTargs != 0 ) - delete st->nfaTargs; - if ( st->inConds != 0 ) - delete[] st->inConds; - if ( st->inCondTests != 0 ) - delete[] st->inCondTests; - } - - delete[] allStates; - if ( classMap != 0 ) - delete[] classMap; - - for ( TransApSet::Iter ti = transSet; ti.lte(); ti++ ) { - if ( ti->condSpace != 0 ) - delete[] ti->v.outConds; - } - - condSet.empty(); - transSet.empty(); -} - -/* Does the machine have any actions. */ -bool RedFsmAp::anyActions() -{ - return actionMap.length() > 0; -} - -void RedFsmAp::depthFirstOrdering( RedStateAp *state ) -{ - /* Nothing to do if the state is already on the list. */ - if ( state->onStateList ) - return; - - /* Doing depth first, put state on the list. */ - state->onStateList = true; - stateList.append( state ); - - /* At this point transitions should only be in ranges. */ - assert( state->outSingle.length() == 0 ); - assert( state->defTrans == 0 ); - - /* Recurse on everything ranges. */ - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - for ( int c = 0; c < rtel->value->numConds(); c++ ) { - RedCondPair *cond = rtel->value->outCond( c ); - if ( cond->targ != 0 ) - depthFirstOrdering( cond->targ ); - } - } - - if ( state->nfaTargs ) { - for ( RedNfaTargs::Iter s = *state->nfaTargs; s.lte(); s++ ) - depthFirstOrdering( s->state ); - } -} - -/* Ordering states by transition connections. */ -void RedFsmAp::depthFirstOrdering() -{ - /* Init on state list flags. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) - st->onStateList = false; - - /* Clear out the state list, we will rebuild it. */ - int stateListLen = stateList.length(); - stateList.abandon(); - - /* Add back to the state list from the start state and all other entry - * points. */ - if ( startState != 0 ) - depthFirstOrdering( startState ); - for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) - depthFirstOrdering( *en ); - if ( forcedErrorState ) - depthFirstOrdering( errState ); - - /* Make sure we put everything back on. */ - assert( stateListLen == stateList.length() ); -} - -void RedFsmAp::breadthFirstAdd( RedStateAp *state ) -{ - if ( state->onStateList ) - return; - - state->onStateList = true; - stateList.append( state ); -} - -void RedFsmAp::breadthFirstOrdering() -{ - /* Init on state list flags. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) - st->onStateList = false; - - /* Clear out the state list, we will rebuild it. */ - int stateListLen = stateList.length(); - stateList.abandon(); - - if ( startState != 0 ) - breadthFirstAdd( startState ); - - int depth = 0; - int nextLevel = stateList.length(); - int pos = 0; - - /* To implement breadth-first we traverse the current list (assuming a - * start state) and add children. */ - RedStateAp *cur = stateList.head; - while ( cur != 0 ) { - /* Recurse on everything ranges. */ - for ( RedTransList::Iter rtel = cur->outRange; rtel.lte(); rtel++ ) { - for ( int c = 0; c < rtel->value->numConds(); c++ ) { - RedCondPair *cond = rtel->value->outCond( c ); - if ( cond->targ != 0 ) - breadthFirstAdd( cond->targ ); - } - } - - if ( cur->nfaTargs ) { - for ( RedNfaTargs::Iter s = *cur->nfaTargs; s.lte(); s++ ) - breadthFirstAdd( s->state ); - } - - cur = cur->next; - pos += 1; - - if ( pos == nextLevel ) { - depth += 1; - nextLevel = stateList.length(); - } - } - - for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) - depthFirstOrdering( *en ); - if ( forcedErrorState ) - depthFirstOrdering( errState ); - - assert( stateListLen == stateList.length() ); -} - -#ifdef SCORE_ORDERING -void RedFsmAp::readScores() -{ - /* - * Reads processed transitions logged by ASM codegen when LOG_TRANS is - * enabled. Process with: - * - * cat trans-log | sort -n -k 1 -k 2 -k 3 | uniq -c | sort -r -n -k1 -r > scores - */ - FILE *sfn = fopen( "scores", "r" ); - - scores = new long*[nextStateId]; - for ( int i = 0; i < nextStateId; i++ ) { - scores[i] = new long[256]; - memset( scores[i], 0, sizeof(long) * 256 ); - } - - long score, m, state, ch; - while ( true ) { - int n = fscanf( sfn, "%ld %ld %ld %ld\n", &score, &m, &state, &ch ); - if ( n != 4 ) - break; - if ( m == machineId ) - scores[state][ch] = score; - } - fclose( sfn ); - - /* Init on state list flags. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - RedTransList::Iter rtel = st->outRange; - int chi = 0; - while ( rtel.lte() ) { - /* 1. Bring chi up to lower end of out range. */ - while ( chi < rtel->lowKey.getVal() ) { - chi++; - } - - /* 2. While inside lower, add in score. */ - while ( chi <= rtel->highKey.getVal() ) { - rtel->score += scores[st->id][chi]; - chi++; - } - - /* 3. Next range. */ - rtel++; - } - } -} - -/* This second pass will collect any states that didn't make it in the first - * pass. Used for depth-first and breadth-first passes. */ -void RedFsmAp::scoreSecondPass( RedStateAp *state ) -{ - /* Nothing to do if the state is already on the list. */ - if ( state->onListRest ) - return; - - /* Doing depth first, put state on the list. */ - state->onListRest = true; - - if ( !state->onStateList ) { - state->onStateList = true; - stateList.append( state ); - } - - /* At this point transitions should only be in ranges. */ - assert( state->outSingle.length() == 0 ); - assert( state->defTrans == 0 ); - - /* Recurse on everything ranges. */ - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - for ( int c = 0; c < rtel->value->numConds(); c++ ) { - RedCondPair *cond = rtel->value->outCond( c ); - if ( cond->targ != 0 ) - scoreSecondPass( cond->targ ); - } - } - - if ( state->nfaTargs ) { - for ( RedNfaTargs::Iter s = *state->nfaTargs; s.lte(); s++ ) - scoreSecondPass( s->state ); - } -} - -void RedFsmAp::scoreOrderingDepth( RedStateAp *state ) -{ - /* Nothing to do if the state is already on the list. */ - if ( state->onStateList ) - return; - - /* Doing depth first, put state on the list. */ - state->onStateList = true; - stateList.append( state ); - - /* At this point transitions should only be in ranges. */ - assert( state->outSingle.length() == 0 ); - assert( state->defTrans == 0 ); - - /* Recurse on everything ranges. */ - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - if ( rtel->score > 10 ) { - for ( int c = 0; c < rtel->value->numConds(); c++ ) { - RedCondPair *cond = rtel->value->outCond( c ); - if ( cond->targ != 0 ) - scoreOrderingDepth( cond->targ ); - } - } - } -} - -void RedFsmAp::scoreOrderingDepth() -{ - readScores(); - - /* Init on state list flags. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - st->onStateList = false; - st->onListRest = false; - } - - /* Clear out the state list, we will rebuild it. */ - int stateListLen = stateList.length(); - stateList.abandon(); - - scoreOrderingDepth( startState ); - - scoreSecondPass( startState ); - for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) - scoreSecondPass( *en ); - if ( forcedErrorState ) - scoreSecondPass( errState ); - - assert( stateListLen == stateList.length() ); -} - -void RedFsmAp::scoreOrderingBreadth() -{ - readScores(); - - /* Init on state list flags. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - st->onStateList = false; - st->onListRest = false; - } - - /* Clear out the state list, we will rebuild it. */ - int stateListLen = stateList.length(); - stateList.abandon(); - - if ( startState != 0 ) - breadthFirstAdd( startState ); - - int depth = 0; - int nextLevel = stateList.length(); - int pos = 0; - - /* To implement breadth-first we traverse the current list (assuming a - * start state) and add children. */ - RedStateAp *cur = stateList.head; - while ( cur != 0 ) { - /* Recurse on everything ranges. */ - for ( RedTransList::Iter rtel = cur->outRange; rtel.lte(); rtel++ ) { - if ( rtel->score > 100 ) { - for ( int c = 0; c < rtel->value->numConds(); c++ ) { - RedCondPair *cond = rtel->value->outCond( c ); - if ( cond->targ != 0 ) - breadthFirstAdd( cond->targ ); - } - } - } - - cur = cur->next; - pos += 1; - - if ( pos == nextLevel ) { - depth += 1; - nextLevel = stateList.length(); - } - } - - scoreSecondPass( startState ); - for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) - scoreSecondPass( *en ); - if ( forcedErrorState ) - scoreSecondPass( errState ); - - assert( stateListLen == stateList.length() ); -} -#endif - -void RedFsmAp::randomizedOrdering() -{ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) - st->onStateList = false; - - /* Clear out the state list, we will rebuild it. */ - int stateListLen = stateList.length(); - stateList.abandon(); - - srand( time( 0 ) ); - - for ( int i = nextStateId; i > 0; i-- ) { - /* Pick one from 0 ... i (how many are left). */ - int nth = rand() % i; - - /* Go forward through the list adding the nth. Need to scan because - * there are items already added in the list. */ - for ( int j = 0; j < nextStateId; j++ ) { - if ( !allStates[j].onStateList ) { - if ( nth == 0 ) { - /* Add. */ - allStates[j].onStateList = true; - stateList.append( &allStates[j] ); - break; - } - else { - nth -= 1; - } - } - } - } - assert( stateListLen == stateList.length() ); -} - -/* Assign state ids by appearance in the state list. */ -void RedFsmAp::sequentialStateIds() -{ - /* Table based machines depend on the state numbers starting at zero. */ - nextStateId = 0; - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) - st->id = nextStateId++; -} - -/* Stable sort the states by final state status. */ -void RedFsmAp::sortStatesByFinal() -{ - /* Move forward through the list and move final states onto the end. */ - RedStateAp *state = 0; - RedStateAp *next = stateList.head; - RedStateAp *last = stateList.tail; - while ( state != last ) { - /* Move forward and load up the next. */ - state = next; - next = state->next; - - /* Throw to the end? */ - if ( state->isFinal ) { - stateList.detach( state ); - stateList.append( state ); - } - } -} - -/* Assign state ids by final state state status. */ -void RedFsmAp::sortStateIdsByFinal() -{ - /* Table based machines depend on this starting at zero. */ - nextStateId = 0; - - /* First pass to assign non final ids. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( ! st->isFinal ) - st->id = nextStateId++; - } - - /* Second pass to assign final ids. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->isFinal ) - st->id = nextStateId++; - } -} - -struct CmpStateById -{ - static int compare( RedStateAp *st1, RedStateAp *st2 ) - { - if ( st1->id < st2->id ) - return -1; - else if ( st1->id > st2->id ) - return 1; - else - return 0; - } -}; - -void RedFsmAp::sortByStateId() -{ - /* Make the array. */ - int pos = 0; - RedStateAp **ptrList = new RedStateAp*[stateList.length()]; - for ( RedStateList::Iter st = stateList; st.lte(); st++, pos++ ) - ptrList[pos] = st; - - MergeSort mergeSort; - mergeSort.sort( ptrList, stateList.length() ); - - stateList.abandon(); - for ( int st = 0; st < pos; st++ ) - stateList.append( ptrList[st] ); - - delete[] ptrList; -} - -/* Find the final state with the lowest id. */ -void RedFsmAp::findFirstFinState() -{ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->isFinal && (firstFinState == 0 || st->id < firstFinState->id) ) - firstFinState = st; - } -} - -void RedFsmAp::assignActionLocs() -{ - int nextLocation = 0; - for ( GenActionTableMap::Iter act = actionMap; act.lte(); act++ ) { - /* Store the loc, skip over the array and a null terminator. */ - act->location = nextLocation; - nextLocation += act->key.length() + 1; - } -} - -/* Check if we can extend the current range by displacing any ranges - * ahead to the singles. */ -bool RedFsmAp::canExtend( const RedTransList &list, int pos ) -{ - /* Get the transition that we want to extend. */ - RedTransAp *extendTrans = list[pos].value; - - /* Look ahead in the transition list. */ - for ( int next = pos + 1; next < list.length(); pos++, next++ ) { - /* If they are not continuous then cannot extend. */ - Key nextKey = list[next].lowKey; - keyOps->decrement( nextKey ); - if ( keyOps->ne( list[pos].highKey, nextKey ) ) - break; - - /* Check for the extenstion property. */ - if ( extendTrans == list[next].value ) - return true; - - /* If the span of the next element is more than one, then don't keep - * checking, it won't be moved to single. */ - unsigned long long nextSpan = keyOps->span( list[next].lowKey, list[next].highKey ); - if ( nextSpan > 1 ) - break; - } - return false; -} - -/* Move ranges to the singles list if it means we can extend some ranges, or if - * the spans are of length one. */ -void RedFsmAp::moveSelectTransToSingle( RedStateAp *state ) -{ - RedTransList &range = state->outRange; - RedTransList &single = state->outSingle; - for ( int rpos = 0; rpos < range.length(); ) { - /* Check if this is a range we can extend. */ - if ( canExtend( range, rpos ) ) { - /* Transfer singles over. */ - while ( range[rpos].value != range[rpos+1].value ) { - /* Transfer the range to single. */ - single.append( range[rpos+1] ); - range.remove( rpos+1 ); - } - - /* Extend. */ - range[rpos].highKey = range[rpos+1].highKey; - range.remove( rpos+1 ); - } - /* Maybe move it to the singles. */ - else if ( keyOps->span( range[rpos].lowKey, range[rpos].highKey ) == 1 ) { - single.append( range[rpos] ); - range.remove( rpos ); - } - else { - /* Keeping it in the ranges. */ - rpos += 1; - } - } -} - -void RedFsmAp::moveAllTransToSingle( RedStateAp *state ) -{ - RedTransList &range = state->outRange; - RedTransList &single = state->outSingle; - for ( int rpos = 0; rpos < range.length(); rpos++ ) { - - RedTransEl el = range[rpos]; - unsigned long long span = keyOps->span( el.lowKey, el.highKey ); - - Key key = el.lowKey; - for ( unsigned long long pos = 0; pos < span; pos++ ) { - el.lowKey = el.highKey = key; - single.append( el ); - keyOps->increment( key ); - } - } - range.empty(); -} - -/* Look through ranges and choose suitable single character transitions. */ -void RedFsmAp::moveSelectTransToSingle() -{ - /* Loop the states. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - /* Rewrite the transition list taking out the suitable single - * transtions. */ - moveSelectTransToSingle( st ); - } -} - -void RedFsmAp::moveAllTransToSingle() -{ - /* Loop the states. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - /* Rewrite the transition list taking out the suitable single - * transtions. */ - moveAllTransToSingle( st ); - } -} - -void RedFsmAp::makeFlat() -{ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->outRange.length() == 0 ) { - st->lowKey = st->highKey = 0; - st->transList = 0; - } - else { - st->lowKey = st->outRange[0].lowKey; - st->highKey = st->outRange[st->outRange.length()-1].highKey; - unsigned long long span = keyOps->span( st->lowKey, st->highKey ); - st->transList = new RedTransAp*[ span ]; - memset( st->transList, 0, sizeof(RedTransAp*)*span ); - - for ( RedTransList::Iter trans = st->outRange; trans.lte(); trans++ ) { - unsigned long long base, trSpan; - base = keyOps->span( st->lowKey, trans->lowKey )-1; - trSpan = keyOps->span( trans->lowKey, trans->highKey ); - for ( unsigned long long pos = 0; pos < trSpan; pos++ ) - st->transList[base+pos] = trans->value; - } - - /* Fill in the gaps with the default transition. */ - for ( unsigned long long pos = 0; pos < span; pos++ ) { - if ( st->transList[pos] == 0 ) - st->transList[pos] = st->defTrans; - } - } - } -} - -void RedFsmAp::characterClass( EquivList &equiv ) -{ - /* Find the global low and high keys. */ - bool anyTrans = false; - Key lowKey = keyOps->maxKey; - Key highKey = keyOps->minKey; - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->outRange.length() == 0 ) - continue; - - st->lowKey = st->outRange[0].lowKey; - st->highKey = st->outRange[st->outRange.length()-1].highKey; - - if ( keyOps->lt( st->lowKey, lowKey ) ) - lowKey = st->lowKey; - - if ( keyOps->gt( st->highKey, highKey ) ) - highKey = st->highKey; - - anyTrans = true; - } - - if ( ! anyTrans ) { - this->lowKey = lowKey; - this->highKey = highKey; - this->classMap = 0; - this->nextClass = 1; - return; - } - - long long next = 1; - equiv.append( new EquivClass( lowKey, highKey, next++ ) ); - - /* Start with a single equivalence class and break it up using range - * boundaries of each state. This will tell us what the equivalence class - * ranges are. These are the ranges that always go to the same state, - * across all states. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->outRange.length() == 0 ) - continue; - - EquivList newList; - PairKeyMap uniqPairs; - - /* What is the set of unique transitions (*for this state) */ - EquivAlloc uniqTrans; - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - if ( ! uniqTrans.find( rtel->value ) ) - uniqTrans.insert( rtel->value, next++ ); - } - - /* Merge with whole-machine equiv classes. */ - typedef RangePairIter< PiList, PiVector > RangePairIterPiListEquivClassPiVectorRedTransEl; - for ( RangePairIterPiListEquivClassPiVectorRedTransEl - pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ ) - { - switch ( pair.userState ) { - - case RangePairIterPiListEquivClassPiVectorRedTransEl::RangeOverlap: { - /* Look up the char for s2. */ - EquivAllocEl *s2El = uniqTrans.find( pair.s2Tel.trans->value ); - - /* Can't use either equiv classes, find uniques. */ - PairKey pairKey( pair.s1Tel.trans->value, s2El->value ); - PairKeyMapEl *pairEl = uniqPairs.find( pairKey ); - if ( ! pairEl ) - pairEl = uniqPairs.insert( pairKey, next++ ); - - EquivClass *equivClass = new EquivClass( - pair.s1Tel.lowKey, pair.s1Tel.highKey, - pairEl->value ); - newList.append( equivClass ); - break; - } - - case RangePairIterPiListEquivClassPiVectorRedTransEl::RangeInS1: { - EquivClass *equivClass = new EquivClass( - pair.s1Tel.lowKey, pair.s1Tel.highKey, - pair.s1Tel.trans->value ); - newList.append( equivClass ); - break; - } - - case RangePairIterPiListEquivClassPiVectorRedTransEl::RangeInS2: { - /* Look up the char for s2. */ - EquivAllocEl *s2El = uniqTrans.find( pair.s2Tel.trans->value ); - - EquivClass *equivClass = new EquivClass( - pair.s2Tel.lowKey, pair.s2Tel.highKey, - s2El->value ); - newList.append( equivClass ); - break; - } - - case RangePairIterPiListEquivClassPiVectorRedTransEl::BreakS1: - case RangePairIterPiListEquivClassPiVectorRedTransEl::BreakS2: - break; - } - } - - equiv.empty(); - equiv.transfer( newList ); - } - - /* Reduce to sequential. */ - next = 0; - BstMap map; - for ( EquivClass *c = equiv.head; c != 0; c = c->next ) { - BstMapEl *el = map.find( c->value ); - if ( ! el ) - el = map.insert( c->value, next++ ); - c->value = el->value; - } - - /* Build the map and emit arrays from the range-based equiv classes. Will - * likely crash if there are no transitions in the FSM. */ - long long maxSpan = keyOps->span( lowKey, highKey ); - long long *dest = new long long[maxSpan]; - memset( dest, 0, sizeof(long long) * maxSpan ); - - for ( EquivClass *c = equiv.head; c != 0; c = c->next ) { - long long base = keyOps->span( lowKey, c->lowKey ) - 1; - long long span = keyOps->span( c->lowKey, c->highKey ); - for ( long long s = 0; s < span; s++ ) - dest[base + s] = c->value; - } - - this->lowKey = lowKey; - this->highKey = highKey; - this->classMap = dest; - this->nextClass = next; - -} - -void RedFsmAp::makeFlatClass() -{ - EquivList equiv; - characterClass( equiv ); - - /* Expand the transitions. This uses the equivalence classes. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - if ( st->outRange.length() == 0 ) { - st->lowKey = st->highKey = 0; - st->low = st->high = 0; - st->transList = 0; - } - else { - st->lowKey = st->outRange[0].lowKey; - st->highKey = st->outRange[st->outRange.length()-1].highKey; - - /* Compute low and high in class space. Use a pair iter to find all - * the clases. Alleviates the need to iterate the whole input - * alphabet. */ - st->low = nextClass; - st->high = -1; - for ( RangePairIter< PiList, PiVector > - pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ ) - { - if ( pair.userState == RangePairIter, PiVector >::RangeOverlap || - pair.userState == RangePairIter, PiVector >::RangeInS2 ) - { - long long off = keyOps->span( lowKey, pair.s2Tel.lowKey ) - 1; - if ( classMap[off] < st->low ) - st->low = classMap[off]; - if ( classMap[off] > st->high ) - st->high = classMap[off]; - } - } - - long long span = st->high - st->low + 1; - st->transList = new RedTransAp*[ span ]; - memset( st->transList, 0, sizeof(RedTransAp*)*span ); - - for ( RangePairIter< PiList, PiVector > - pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ ) - { - if ( pair.userState == RangePairIter< PiList, PiVector >::RangeOverlap || - pair.userState == RangePairIter< PiList, PiVector >::RangeInS2 ) - { - long long off = keyOps->span( lowKey, pair.s2Tel.lowKey ) - 1; - st->transList[ classMap[off] - st->low ] = pair.s2Tel.trans->value; - } - } - - /* Fill in the gaps with the default transition. */ - for ( long long pos = 0; pos < span; pos++ ) { - if ( st->transList[pos] == 0 ) - st->transList[pos] = st->defTrans; - } - } - } - - equiv.empty(); -} - - -/* A default transition has been picked, move it from the outRange to the - * default pointer. */ -void RedFsmAp::moveToDefault( RedTransAp *defTrans, RedStateAp *state ) -{ - /* Rewrite the outRange, omitting any ranges that use - * the picked default. */ - RedTransList outRange; - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - /* If it does not take the default, copy it over. */ - if ( rtel->value != defTrans ) - outRange.append( *rtel ); - } - - /* Save off the range we just created into the state's range. */ - state->outRange.transfer( outRange ); - - /* Store the default. */ - state->defTrans = defTrans; -} - -bool RedFsmAp::alphabetCovered( RedTransList &outRange ) -{ - /* Cannot cover without any out ranges. */ - if ( outRange.length() == 0 ) - return false; - - /* If the first range doesn't start at the the lower bound then the - * alphabet is not covered. */ - RedTransList::Iter rtel = outRange; - if ( keyOps->lt( keyOps->minKey, rtel->lowKey ) ) - return false; - - /* Check that every range is next to the previous one. */ - rtel.increment(); - for ( ; rtel.lte(); rtel++ ) { - Key highKey = rtel[-1].highKey; - keyOps->increment( highKey ); - if ( keyOps->ne( highKey, rtel->lowKey ) ) - return false; - } - - /* The last must extend to the upper bound. */ - RedTransEl *last = &outRange[outRange.length()-1]; - if ( keyOps->lt( last->highKey, keyOps->maxKey ) ) - return false; - - return true; -} - -RedTransAp *RedFsmAp::chooseDefaultSpan( RedStateAp *state ) -{ - /* Make a set of transitions from the outRange. */ - RedTransSet stateTransSet; - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) - stateTransSet.insert( rtel->value ); - - /* For each transition in the find how many alphabet characters the - * transition spans. */ - unsigned long long *span = new unsigned long long[stateTransSet.length()]; - memset( span, 0, sizeof(unsigned long long) * stateTransSet.length() ); - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - /* Lookup the transition in the set. */ - RedTransAp **inSet = stateTransSet.find( rtel->value ); - int pos = inSet - stateTransSet.data; - span[pos] += keyOps->span( rtel->lowKey, rtel->highKey ); - } - - /* Find the max span, choose it for making the default. */ - RedTransAp *maxTrans = 0; - unsigned long long maxSpan = 0; - for ( RedTransSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) { - if ( span[rtel.pos()] > maxSpan ) { - maxSpan = span[rtel.pos()]; - maxTrans = *rtel; - } - } - - delete[] span; - return maxTrans; -} - -/* Pick default transitions from ranges for the states. */ -void RedFsmAp::chooseDefaultSpan() -{ - /* Loop the states. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - /* Only pick a default transition if the alphabet is covered. This - * avoids any transitions in the out range that go to error and avoids - * the need for an ERR state. */ - if ( alphabetCovered( st->outRange ) ) { - /* Pick a default transition by largest span. */ - RedTransAp *defTrans = chooseDefaultSpan( st ); - - /* Rewrite the transition list taking out the transition we picked - * as the default and store the default. */ - moveToDefault( defTrans, st ); - } - } -} - -RedTransAp *RedFsmAp::chooseDefaultGoto( RedStateAp *state ) -{ - /* Make a set of transitions from the outRange. */ - RedTransSet stateTransSet; - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - for ( int c = 0; c < rtel->value->numConds(); c++ ) { - RedCondPair *cond = rtel->value->outCond(c); - if ( cond->targ == state->next ) - return rtel->value; - } - } - return 0; -} - -void RedFsmAp::chooseDefaultGoto() -{ - /* Loop the states. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - /* Pick a default transition. */ - RedTransAp *defTrans = chooseDefaultGoto( st ); - if ( defTrans == 0 ) - defTrans = chooseDefaultSpan( st ); - - /* Rewrite the transition list taking out the transition we picked - * as the default and store the default. */ - moveToDefault( defTrans, st ); - } -} - -RedTransAp *RedFsmAp::chooseDefaultNumRanges( RedStateAp *state ) -{ - /* Make a set of transitions from the outRange. */ - RedTransSet stateTransSet; - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) - stateTransSet.insert( rtel->value ); - - /* For each transition in the find how many ranges use the transition. */ - int *numRanges = new int[stateTransSet.length()]; - memset( numRanges, 0, sizeof(int) * stateTransSet.length() ); - for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { - /* Lookup the transition in the set. */ - RedTransAp **inSet = stateTransSet.find( rtel->value ); - numRanges[inSet - stateTransSet.data] += 1; - } - - /* Find the max number of ranges. */ - RedTransAp *maxTrans = 0; - int maxNumRanges = 0; - for ( RedTransSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) { - if ( numRanges[rtel.pos()] > maxNumRanges ) { - maxNumRanges = numRanges[rtel.pos()]; - maxTrans = *rtel; - } - } - - delete[] numRanges; - return maxTrans; -} - -void RedFsmAp::chooseDefaultNumRanges() -{ - /* Loop the states. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - /* Pick a default transition. */ - RedTransAp *defTrans = chooseDefaultNumRanges( st ); - - /* Rewrite the transition list taking out the transition we picked - * as the default and store the default. */ - moveToDefault( defTrans, st ); - } -} - -RedCondAp *RedFsmAp::getErrorCond() -{ - return allocateCond( getErrorState(), 0 ); -} - -RedTransAp *RedFsmAp::getErrorTrans() -{ - return allocateTrans( getErrorState(), 0 ); -} - -RedStateAp *RedFsmAp::getErrorState() -{ - /* Something went wrong. An error state is needed but one was not supplied - * by the frontend. */ - assert( errState != 0 ); - return errState; -} - -/* Makes a plain transition. */ -RedTransAp *RedFsmAp::allocateTrans( RedStateAp *targ, RedAction *action ) -{ - /* Create a reduced trans and look for it in the transiton set. */ - RedTransAp redTrans( 0, 0, targ, action ); - RedTransAp *inDict = transSet.find( &redTrans ); - if ( inDict == 0 ) { - inDict = new RedTransAp( nextTransId++, nextCondId++, targ, action ); - transSet.insert( inDict ); - } - return inDict; -} - -/* Makes a cond list transition. */ -RedTransAp *RedFsmAp::allocateTrans( GenCondSpace *condSpace, - RedCondEl *outConds, int numConds, RedCondAp *errCond ) -{ - /* Create a reduced trans and look for it in the transiton set. */ - RedTransAp redTrans( 0, condSpace, outConds, numConds, errCond ); - RedTransAp *inDict = transSet.find( &redTrans ); - if ( inDict == 0 ) { - inDict = new RedTransAp( nextTransId++, condSpace, outConds, numConds, errCond ); - transSet.insert( inDict ); - } - else { - /* Need to free the out cond vector. */ - delete[] outConds; - } - return inDict; -} - -RedCondAp *RedFsmAp::allocateCond( RedStateAp *targ, RedAction *action ) -{ - /* Create a reduced trans and look for it in the transiton set. */ - RedCondAp redCond( targ, action, 0 ); - RedCondAp *inDict = condSet.find( &redCond ); - if ( inDict == 0 ) { - inDict = new RedCondAp( targ, action, nextCondId++ ); - condSet.insert( inDict ); - } - return inDict; -} - -void RedFsmAp::partitionFsm( int nparts ) -{ - /* At this point the states are ordered by a depth-first traversal. We - * will allocate to partitions based on this ordering. */ - this->nParts = nparts; - int partSize = stateList.length() / nparts; - int remainder = stateList.length() % nparts; - int numInPart = partSize; - int partition = 0; - if ( remainder-- > 0 ) - numInPart += 1; - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - st->partition = partition; - - numInPart -= 1; - if ( numInPart == 0 ) { - partition += 1; - numInPart = partSize; - if ( remainder-- > 0 ) - numInPart += 1; - } - } -} - -void RedFsmAp::setInTrans() -{ - /* First pass counts the number of transitions. */ - for ( CondApSet::Iter trans = condSet; trans.lte(); trans++ ) - trans->p.targ->numInConds += 1; - - for ( TransApSet::Iter trans = transSet; trans.lte(); trans++ ) { - if ( trans->condSpace == 0 ) - trans->p.targ->numInConds += 1; - else { - /* We have a placement choice here, but associate it with the - * first. */ - RedCondPair *pair = trans->outCond( 0 ); - pair->targ->numInCondTests += 1; - } - } - - /* Allocate. Reset the counts so we can use them as the current size. */ - for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { - st->inConds = new RedCondPair*[st->numInConds]; - st->numInConds = 0; - - st->inCondTests = new RedTransAp*[st->numInCondTests]; - st->numInCondTests = 0; - } - - /* Fill the arrays. */ - for ( CondApSet::Iter trans = condSet; trans.lte(); trans++ ) { - RedStateAp *targ = trans->p.targ; - targ->inConds[targ->numInConds++] = &trans->p; - } - - for ( TransApSet::Iter trans = transSet; trans.lte(); trans++ ) { - if ( trans->condSpace == 0 ) { - RedStateAp *targ = trans->p.targ; - targ->inConds[targ->numInConds++] = &trans->p; - } - else { - RedCondPair *pair = trans->outCond( 0 ); - RedStateAp *targ = pair->targ; - targ->inCondTests[targ->numInCondTests++] = trans; - } - } -} diff --git a/libfsm/redfsm.h b/libfsm/redfsm.h deleted file mode 100644 index 392b1a9c..00000000 --- a/libfsm/redfsm.h +++ /dev/null @@ -1,889 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _REDFSM_H -#define _REDFSM_H - -#include -#include -#include -#include "config.h" -#include "common.h" -#include "vector.h" -#include "dlist.h" -#include "compare.h" -#include "bstmap.h" -#include "bstset.h" -#include "avlmap.h" -#include "avltree.h" -#include "avlbasic.h" -#include "mergesort.h" -#include "sbstmap.h" -#include "sbstset.h" -#include "sbsttable.h" - -#define TRANS_ERR_TRANS 0 -#define STATE_ERR_STATE 0 -#define FUNC_NO_FUNC 0 - -// #define SCORE_ORDERING 1 - -using std::string; - -struct RedStateAp; -struct GenInlineList; -struct GenAction; -struct FsmCtx; -struct GenCondSpace; -typedef BstSet RedCondKeySet; - -/* - * Inline code tree - */ -struct GenInlineItem -{ - enum Type - { - Text, Goto, Call, Ncall, Next, GotoExpr, CallExpr, - NcallExpr, NextExpr, Ret, Nret, - PChar, Char, Hold, Curs, Targs, Entry, Exec, Break, Nbreak, - LmSwitch, LmExec, LmSetActId, LmSetTokEnd, LmGetTokEnd, - LmInitAct, LmInitTokStart, LmSetTokStart, NfaClear, - HostStmt, HostExpr, HostText, - GenStmt, GenExpr, LmCase, LmHold, - NfaWrapAction, NfaWrapConds - }; - - GenInlineItem( const InputLoc &loc, Type type ) : - loc(loc), targId(0), targState(0), - lmId(0), children(0), offset(0), - wrappedAction(0), type(type) { } - - ~GenInlineItem(); - - InputLoc loc; - std::string data; - int targId; - RedStateAp *targState; - int lmId; - GenInlineList *children; - int offset; - GenAction *wrappedAction; - GenCondSpace *condSpace; - RedCondKeySet condKeySet; - Type type; - - GenInlineItem *prev, *next; -}; - -/* Normally this would be atypedef, but that would entail including DList from - * ptreetypes, which should be just typedef forwards. */ -struct GenInlineList : public DList { }; - -struct GenInlineExpr -{ - GenInlineExpr( const InputLoc &loc, GenInlineList *inlineList ) - : loc(loc), inlineList( inlineList ) {} - - ~GenInlineExpr() - { - if ( inlineList != 0 ) { - inlineList->empty(); - delete inlineList; - } - } - - InputLoc loc; - GenInlineList *inlineList; -}; - -/* Element in list of actions. Contains the string for the code to exectute. */ -struct GenAction -: - public DListEl -{ - GenAction( ) - : - inlineList(0), - actionId(0), - numTransRefs(0), - numToStateRefs(0), - numFromStateRefs(0), - numEofRefs(0), - numNfaPushRefs(0), - numNfaRestoreRefs(0), - numNfaPopActionRefs(0), - numNfaPopTestRefs(0) - { - } - - ~GenAction() - { - if ( inlineList != 0 ) { - inlineList->empty(); - delete inlineList; - } - } - - /* Data collected during parse. */ - InputLoc loc; - std::string name; - GenInlineList *inlineList; - int actionId; - - string nameOrLoc(); - - /* Number of references in the final machine. */ - int numRefs() - { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; } - int numTransRefs; - int numToStateRefs; - int numFromStateRefs; - int numEofRefs; - int numNfaPushRefs; - int numNfaRestoreRefs; - int numNfaPopActionRefs; - int numNfaPopTestRefs; -}; - - -/* Forwards. */ -struct RedStateAp; -struct StateAp; - -/* Transistion GenAction Element. */ -typedef SBstMapEl< int, GenAction* > GenActionTableEl; - -/* Transition GenAction Table. */ -struct GenActionTable - : public SBstMap< int, GenAction*, CmpOrd > -{ - void setAction( int ordering, GenAction *action ); - void setActions( int *orderings, GenAction **actions, int nActs ); - void setActions( const GenActionTable &other ); -}; - -/* Compare of a whole action table element (key & value). */ -struct CmpGenActionTableEl -{ - static int compare( const GenActionTableEl &action1, - const GenActionTableEl &action2 ) - { - if ( action1.key < action2.key ) - return -1; - else if ( action1.key > action2.key ) - return 1; - else if ( action1.value < action2.value ) - return -1; - else if ( action1.value > action2.value ) - return 1; - return 0; - } -}; - -/* Compare for GenActionTable. */ -typedef CmpSTable< GenActionTableEl, CmpGenActionTableEl > CmpGenActionTable; - -/* Set of states. */ -typedef BstSet RedStateSet; -typedef BstSet IntSet; - -/* Reduced action. */ -struct RedAction -: - public AvlTreeEl -{ - RedAction( ) - : - key(), - eofRefs(0), - numTransRefs(0), - numToStateRefs(0), - numFromStateRefs(0), - numEofRefs(0), - numNfaPushRefs(0), - numNfaRestoreRefs(0), - numNfaPopActionRefs(0), - numNfaPopTestRefs(0), - bAnyNextStmt(false), - bAnyCurStateRef(false), - bAnyBreakStmt(false), - bUsingAct(false) - { } - - const GenActionTable &getKey() - { return key; } - - GenActionTable key; - int actListId; - int location; - IntSet *eofRefs; - - /* Number of references in the final machine. */ - int numRefs() - { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; } - int numTransRefs; - int numToStateRefs; - int numFromStateRefs; - int numEofRefs; - int numNfaPushRefs; - int numNfaRestoreRefs; - int numNfaPopActionRefs; - int numNfaPopTestRefs; - - bool anyNextStmt() { return bAnyNextStmt; } - bool anyCurStateRef() { return bAnyCurStateRef; } - bool anyBreakStmt() { return bAnyBreakStmt; } - bool usingAct() { return bUsingAct; } - - bool bAnyNextStmt; - bool bAnyCurStateRef; - bool bAnyBreakStmt; - bool bUsingAct; -}; - -typedef AvlTree GenActionTableMap; - -struct RedCondPair -{ - int id; - RedStateAp *targ; - RedAction *action; -}; - -struct RedCondAp -: - public AvlTreeEl -{ - RedCondAp( RedStateAp *targ, RedAction *action, int id ) - { - p.id = id; - p.targ = targ; - p.action = action; - } - - RedCondPair p; -}; - -struct RedCondEl -{ - CondKey key; - RedCondAp *value; -}; - -struct CmpRedCondEl -{ - static int compare( const RedCondEl &el1, const RedCondEl &el2 ) - { - if ( el1.key < el2.key ) - return -1; - else if ( el1.key > el2.key ) - return 1; - else if ( el1.value < el2.value ) - return -1; - else if ( el1.value > el2.value ) - return 1; - else - return 0; - } -}; - -typedef Vector< GenAction* > GenCondSet; - -struct GenCondSpace -{ - GenCondSpace() - : - numTransRefs(0), - numNfaRefs(0) - {} - - Key baseKey; - GenCondSet condSet; - int condSpaceId; - - long fullSize() - { return ( 1 << condSet.length() ); } - - long numTransRefs; - long numNfaRefs; - - GenCondSpace *next, *prev; -}; - -typedef DList CondSpaceList; - -struct RedCondVect -{ - int numConds; - RedCondEl *outConds; - RedCondAp *errCond; -}; - -/* Reduced transition. */ -struct RedTransAp -: - public AvlTreeEl -{ - RedTransAp( int id, GenCondSpace *condSpace, - RedCondEl *outConds, int numConds, RedCondAp *errCond ) - : - id(id), - condSpace(condSpace) - { - v.outConds = outConds; - v.numConds = numConds; - v.errCond = errCond; - } - - RedTransAp( int id, int condId, RedStateAp *targ, RedAction *action ) - : - id(id), - condSpace(0) - { - p.id = condId; - p.targ = targ; - p.action = action; - } - - long condFullSize() - { - return condSpace == 0 ? 1 : condSpace->fullSize(); - } - - CondKey outCondKey( int off ) - { - return condSpace == 0 ? CondKey(0) : v.outConds[off].key; - } - - RedCondPair *outCond( int off ) - { - return condSpace == 0 ? &p : &v.outConds[off].value->p; - } - - int numConds() - { - return condSpace == 0 ? 1 : v.numConds; - } - - RedCondPair *errCond() - { - return condSpace == 0 ? 0 : ( v.errCond != 0 ? &v.errCond->p : 0 ); - } - - int id; - GenCondSpace *condSpace; - - /* Either a pair or a vector of conds. */ - union - { - RedCondPair p; - RedCondVect v; - }; -}; - -/* Compare of transitions for the final reduction of transitions. Comparison - * is on target and the pointer to the shared action table. It is assumed that - * when this is used the action tables have been reduced. */ -struct CmpRedTransAp -{ - static int compare( const RedTransAp &t1, const RedTransAp &t2 ) - { - if ( t1.condSpace < t2.condSpace ) - return -1; - else if ( t1.condSpace > t2.condSpace ) - return 1; - else { - if ( t1.condSpace == 0 ) { - if ( t1.p.targ < t2.p.targ ) - return -1; - else if ( t1.p.targ > t2.p.targ ) - return 1; - else if ( t1.p.action < t2.p.action ) - return -1; - else if ( t1.p.action > t2.p.action ) - return 1; - else - return 0; - - } - else { - if ( t1.v.numConds < t2.v.numConds ) - return -1; - else if ( t1.v.numConds > t2.v.numConds ) - return 1; - else - { - RedCondEl *i1 = t1.v.outConds, *i2 = t2.v.outConds; - long len = t1.v.numConds, cmpResult; - for ( long pos = 0; pos < len; - pos += 1, i1 += 1, i2 += 1 ) - { - cmpResult = CmpRedCondEl::compare(*i1, *i2); - if ( cmpResult != 0 ) - return cmpResult; - } - return 0; - } - } - } - } -}; - -struct CmpRedCondAp -{ - static int compare( const RedCondAp &t1, const RedCondAp &t2 ) - { - if ( t1.p.targ < t2.p.targ ) - return -1; - else if ( t1.p.targ > t2.p.targ ) - return 1; - else if ( t1.p.action < t2.p.action ) - return -1; - else if ( t1.p.action > t2.p.action ) - return 1; - else - return 0; - } -}; - -typedef AvlBasic TransApSet; -typedef AvlBasic CondApSet; - -/* Element in out range. */ -struct RedTransEl -{ - /* Constructors. */ - RedTransEl( Key lowKey, Key highKey, RedTransAp *value ) - : - lowKey(lowKey), - highKey(highKey), - value(value) -#ifdef SCORE_ORDERING - , score(0) -#endif - { } - - Key lowKey, highKey; - RedTransAp *value; -#ifdef SCORE_ORDERING - long long score; -#endif -}; - -typedef Vector RedTransList; -typedef Vector RedStateVect; - -typedef BstMapEl RedSpanMapEl; -typedef BstMap RedSpanMap; - -/* Compare used by span map sort. Reverse sorts by the span. */ -struct CmpRedSpanMapEl -{ - static int compare( const RedSpanMapEl &smel1, const RedSpanMapEl &smel2 ) - { - if ( smel1.value > smel2.value ) - return -1; - else if ( smel1.value < smel2.value ) - return 1; - else - return 0; - } -}; - -/* Sorting state-span map entries by span. */ -typedef MergeSort RedSpanMapSort; - -/* Set of entry ids that go into this state. */ -typedef Vector EntryIdVect; -typedef Vector EntryNameVect; - -struct Condition -{ - Condition( ) - : key(0), baseKey(0) {} - - Key key; - Key baseKey; - GenCondSet condSet; - - Condition *next, *prev; -}; -typedef DList ConditionList; - -struct GenStateCond -{ - Key lowKey; - Key highKey; - - GenCondSpace *condSpace; - - GenStateCond *prev, *next; -}; -typedef DList GenStateCondList; -typedef Vector StateCondVect; - -struct RedNfaTarg -{ - RedNfaTarg( RedStateAp *state, RedAction *push, - RedAction *popTest, int order ) - : - id(0), - state(state), - push(push), - popTest(popTest), - order(order) - {} - - long id; - RedStateAp *state; - RedAction *push; - RedAction *popTest; - int order; -}; - -struct RedNfaTargCmp -{ - static inline long compare( const RedNfaTarg &k1, const RedNfaTarg &k2 ) - { - if ( k1.order < k2.order ) - return -1; - else if ( k1.order > k2.order ) - return 1; - return 0; - } -}; - -typedef Vector RedNfaTargs; - -/* Reduced state. */ -struct RedStateAp -{ - RedStateAp() - : - defTrans(0), - transList(0), - isFinal(false), - labelNeeded(false), - outNeeded(false), - onStateList(false), - onListRest(false), - toStateAction(0), - fromStateAction(0), - eofAction(0), - eofTrans(0), - id(0), - bAnyRegCurStateRef(false), - partitionBoundary(false), - inConds(0), - numInConds(0), - inCondTests(0), - numInCondTests(0), - nfaTargs(0), - outCondSpace(0) - { } - - /* Transitions out. */ - RedTransList outSingle; - RedTransList outRange; - RedTransAp *defTrans; - - /* For flat keys. */ - Key lowKey, highKey; - RedTransAp **transList; - long long low, high; - - /* The list of states that transitions from this state go to. */ - RedStateVect targStates; - - bool isFinal; - bool labelNeeded; - bool outNeeded; - bool onStateList; - bool onListRest; - RedAction *toStateAction; - RedAction *fromStateAction; - RedAction *eofAction; - RedTransAp *eofTrans; - int id; - - /* Pointers for the list of states. */ - RedStateAp *prev, *next; - - bool anyRegCurStateRef() { return bAnyRegCurStateRef; } - bool bAnyRegCurStateRef; - - int partition; - bool partitionBoundary; - - RedCondPair **inConds; - int numInConds; - - RedTransAp **inCondTests; - int numInCondTests; - - RedNfaTargs *nfaTargs; - GenCondSpace *outCondSpace; - RedCondKeySet outCondKeys; -}; - -/* List of states. */ -typedef DList RedStateList; - -/* Set of reduced transitons. Comparison is by pointer. */ -typedef BstSet< RedTransAp*, CmpOrd > RedTransSet; - -/* Next version of the fsm machine. */ -struct RedFsmAp -{ - RedFsmAp( FsmCtx *fsmCtx, int machineId ); - ~RedFsmAp(); - - KeyOps *keyOps; - FsmCtx *fsmCtx; - int machineId; - - bool forcedErrorState; - - int nextActionId; - int nextTransId; - int nextCondId; - - /* Next State Id doubles as the total number of state ids. */ - int nextStateId; - - TransApSet transSet; - CondApSet condSet; - GenActionTableMap actionMap; - RedStateList stateList; - RedStateSet entryPoints; - RedStateAp *startState; - RedStateAp *errState; - RedTransAp *errTrans; - RedCondAp *errCond; - RedTransAp *errActionTrans; - RedStateAp *firstFinState; - RedStateAp *allStates; - int numFinStates; - int nParts; - - bool bAnyToStateActions; - bool bAnyFromStateActions; - bool bAnyRegActions; - bool bAnyEofActions; - bool bAnyEofTrans; - bool bAnyEofActivity; - bool bAnyActionGotos; - bool bAnyActionCalls; - bool bAnyActionNcalls; - bool bAnyActionRets; - bool bAnyActionNrets; - bool bAnyActionByValControl; - bool bAnyRegActionRets; - bool bAnyRegActionByValControl; - bool bAnyRegNextStmt; - bool bAnyRegCurStateRef; - bool bAnyRegBreak; - bool bAnyRegNbreak; - bool bUsingAct; - bool bAnyNfaStates; - bool bAnyNfaPushPops; - bool bAnyNfaPushes; - bool bAnyNfaPops; - bool bAnyTransCondRefs; - bool bAnyNfaCondRefs; - - int maxState; - int maxSingleLen; - int maxRangeLen; - int maxKeyOffset; - int maxIndexOffset; - int maxIndex; - int maxActListId; - int maxActionLoc; - int maxActArrItem; - unsigned long long maxSpan; - int maxFlatIndexOffset; - Key maxKey; - int maxCondSpaceId; - int maxCond; - - bool anyActions(); - bool anyToStateActions() { return bAnyToStateActions; } - bool anyFromStateActions() { return bAnyFromStateActions; } - bool anyRegActions() { return bAnyRegActions; } - bool anyEofActions() { return bAnyEofActions; } - bool anyEofTrans() { return bAnyEofTrans; } - bool anyEofActivity() { return bAnyEofActivity; } - bool anyActionGotos() { return bAnyActionGotos; } - bool anyActionCalls() { return bAnyActionCalls; } - bool anyActionNcalls() { return bAnyActionNcalls; } - bool anyActionRets() { return bAnyActionRets; } - bool anyActionNrets() { return bAnyActionNrets; } - bool anyActionByValControl() { return bAnyActionByValControl; } - bool anyRegActionRets() { return bAnyRegActionRets; } - bool anyRegActionByValControl() { return bAnyRegActionByValControl; } - bool anyRegNextStmt() { return bAnyRegNextStmt; } - bool anyRegCurStateRef() { return bAnyRegCurStateRef; } - bool anyRegBreak() { return bAnyRegBreak; } - bool usingAct() { return bUsingAct; } - bool anyRegNbreak() { return bAnyRegNbreak; } - bool anyNfaStates() { return bAnyNfaStates; } - - /* Is is it possible to extend a range by bumping ranges that span only - * one character to the singles array. */ - bool canExtend( const RedTransList &list, int pos ); - - /* Pick single transitions from the ranges. */ - void moveSelectTransToSingle( RedStateAp *state ); - void moveAllTransToSingle( RedStateAp *state ); - - void moveSelectTransToSingle(); - void moveAllTransToSingle(); - - void makeFlat(); - - /* State low/high, in key space and class space. */ - Key lowKey; - Key highKey; - long long nextClass; - long long *classMap; - - /* Support structs for equivalence class computation. */ - struct EquivClass - { - EquivClass( Key lowKey, Key highKey, long long value ) - : lowKey(lowKey), highKey(highKey), value(value) {} - - Key lowKey, highKey; - long long value; - EquivClass *prev, *next; - }; - - typedef DList EquivList; - typedef BstMap EquivAlloc; - typedef BstMapEl EquivAllocEl; - - struct PairKey - { - PairKey( long long k1, long long k2 ) - : k1(k1), k2(k2) {} - - long long k1; - long long k2; - }; - - struct PairKeyCmp - { - static inline long compare( const PairKey &k1, const PairKey &k2 ) - { - if ( k1.k1 < k2.k1 ) - return -1; - else if ( k1.k1 > k2.k1 ) - return 1; - if ( k1.k2 < k2.k2 ) - return -1; - else if ( k1.k2 > k2.k2 ) - return 1; - else - return 0; - } - }; - - typedef BstMap< PairKey, long long, PairKeyCmp > PairKeyMap; - typedef BstMapEl< PairKey, long long > PairKeyMapEl; - - void characterClass( EquivList &equiv ); - void makeFlatClass(); - - /* Move a selected transition from ranges to default. */ - void moveToDefault( RedTransAp *defTrans, RedStateAp *state ); - - /* Pick a default transition by largest span. */ - RedTransAp *chooseDefaultSpan( RedStateAp *state ); - void chooseDefaultSpan(); - - /* Pick a default transition by most number of ranges. */ - RedTransAp *chooseDefaultNumRanges( RedStateAp *state ); - void chooseDefaultNumRanges(); - - /* Pick a default transition tailored towards goto driven machine. */ - RedTransAp *chooseDefaultGoto( RedStateAp *state ); - void chooseDefaultGoto(); - - /* Ordering states by transition connections. */ - void optimizeStateOrdering( RedStateAp *state ); - void optimizeStateOrdering(); - - /* Ordering states by transition connections. */ - void depthFirstOrdering( RedStateAp *state ); - void depthFirstOrdering(); - - void breadthFirstAdd( RedStateAp *state ); - void breadthFirstOrdering(); - - void randomizedOrdering(); - -#ifdef SCORE_ORDERING - long **scores; - void scoreSecondPass( RedStateAp *state ); - void scoreOrderingBreadth(); - void readScores(); - void scoreOrderingDepth( RedStateAp *state ); - void scoreOrderingDepth(); -#endif - - /* Set state ids. */ - void sequentialStateIds(); - void sortStateIdsByFinal(); - - /* Arrange states in by final id. This is a stable sort. */ - void sortStatesByFinal(); - - /* Sorting states by id. */ - void sortByStateId(); - - /* Locating the first final state. This is the final state with the lowest - * id. */ - void findFirstFinState(); - - void assignActionLocs(); - - RedCondAp *getErrorCond(); - RedTransAp *getErrorTrans(); - RedStateAp *getErrorState(); - - /* Is every char in the alphabet covered? */ - bool alphabetCovered( RedTransList &outRange ); - - RedTransAp *allocateTrans( RedStateAp *targ, RedAction *action ); - RedTransAp *allocateTrans( GenCondSpace *condSpace, - RedCondEl *outConds, int numConds, RedCondAp *errCond ); - - RedCondAp *allocateCond( RedStateAp *targState, RedAction *actionTable ); - - void partitionFsm( int nParts ); - - void setInTrans(); -}; - -#endif diff --git a/libfsm/switch.cc b/libfsm/switch.cc deleted file mode 100644 index 076f3585..00000000 --- a/libfsm/switch.cc +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ragel.h" -#include "switch.h" -#include "redfsm.h" -#include "gendata.h" - -#include - -std::ostream &Switch::TRANS_GOTO( int off, RedTransAp *trans ) -{ - out << "_trans = " << off << ";\n"; - return out; -} - -void Switch::RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ) -{ - /* Get the mid position, staying on the lower end of the range. */ - int mid = (low + high) >> 1; - RedTransEl *data = state->outRange.data; - - /* Determine if we need to look higher or lower. */ - bool anyLower = mid > low; - bool anyHigher = mid < high; - - /* Determine if the keys at mid are the limits of the alphabet. */ - bool limitLow = keyOps->eq( data[mid].lowKey, lower ); - bool limitHigh = keyOps->eq( data[mid].highKey, upper ); - - if ( anyLower && anyHigher ) { - /* Can go lower and higher than mid. */ - out << "if ( " << GET_KEY() << " < " << - KEY(data[mid].lowKey) << " ) {\n"; - RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); - out << "} else if ( " << GET_KEY() << " > " << - KEY(data[mid].highKey) << " ) {\n"; - RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); - out << "} else {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value ) << "\n"; - out << "}\n"; - } - else if ( anyLower && !anyHigher ) { - /* Can go lower than mid but not higher. */ - out << "if ( " << GET_KEY() << " < " << - KEY(data[mid].lowKey) << " ) {\n"; - RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); - - /* if the higher is the highest in the alphabet then there is no - * sense testing it. */ - if ( limitHigh ) { - out << "} else {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - } - else { - out << "} else if ( " << GET_KEY() << " <= " << - KEY(data[mid].highKey) << " ) {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - - out << "else {\n"; - DEFAULT( state ); - out << "}\n"; - } - } - else if ( !anyLower && anyHigher ) { - /* Can go higher than mid but not lower. */ - out << "if ( " << GET_KEY() << " > " << - KEY(data[mid].highKey) << " ) {\n"; - RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); - - /* If the lower end is the lowest in the alphabet then there is no - * sense testing it. */ - if ( limitLow ) { - out << "} else {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - } - else { - out << "} else if ( " << GET_KEY() << " >= " << - KEY(data[mid].lowKey) << " ) {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - - out << "else {\n"; - DEFAULT( state ); - out << "}\n"; - } - } - else { - /* Cannot go higher or lower than mid. It's mid or bust. What - * tests to do depends on limits of alphabet. */ - if ( !limitLow && !limitHigh ) { - out << "if ( " << KEY(data[mid].lowKey) << " <= " << - GET_KEY() << " && " << GET_KEY() << " <= " << - KEY(data[mid].highKey) << " ) {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - - out << "else {\n"; - DEFAULT( state ); - out << "}\n"; - } - else if ( limitLow && !limitHigh ) { - out << "if ( " << GET_KEY() << " <= " << - KEY(data[mid].highKey) << " ) {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - - out << "else {\n"; - DEFAULT( state ); - out << "}\n"; - } - else if ( !limitLow && limitHigh ) { - out << "if ( " << KEY(data[mid].lowKey) << " <= " << - GET_KEY() << " ) {\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - - out << "else {\n"; - DEFAULT( state ); - out << "}\n"; - } - else { - /* Both high and low are at the limit. No tests to do. */ - out << "{\n"; - TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; - out << "}\n"; - } - } -} - -void Switch::SINGLE_SWITCH( RedStateAp *st ) -{ - /* Load up the singles. */ - int numSingles = st->outSingle.length(); - RedTransEl *data = st->outSingle.data; - - if ( numSingles == 1 ) { - /* If there is a single single key then write it out as an if. */ - out << "\tif ( " << GET_KEY() << " == " << - KEY(data[0].lowKey) << " ) {\n\t\t"; - - /* Virtual function for writing the target of the transition. */ - TRANS_GOTO(transBase, data[0].value) << "\n"; - out << "\t}\n"; - - out << "else {\n"; - NOT_SINGLE( st ); - out << "}\n"; - } - else if ( numSingles > 1 ) { - /* Write out single keys in a switch if there is more than one. */ - out << "\tswitch( " << GET_KEY() << " ) {\n"; - - /* Write out the single indices. */ - for ( int j = 0; j < numSingles; j++ ) { - out << CASE( KEY(data[j].lowKey) ) << " {\n"; - TRANS_GOTO(transBase + j, data[j].value) << "\n"; - out << CEND() << "\n}\n"; - } - - out << CodeGen::DEFAULT() << " {\n"; - NOT_SINGLE( st ); - out << CEND() << "\n}\n"; - - /* Close off the transition switch. */ - out << "\t}\n"; - } -} - -void Switch::DEFAULT( RedStateAp *st ) -{ - if ( st->defTrans != 0 ) { - TRANS_GOTO( transBase + st->outSingle.length() + st->outRange.length(), st->defTrans ) << "\n"; - } -} - -void Switch::NOT_SINGLE( RedStateAp *st ) -{ - if ( st->outRange.length() > 0 ) { - RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey, - 0, st->outRange.length() - 1 ); - } - else { - DEFAULT( st ); - } -} - -void Switch::LOCATE_TRANS() -{ - transBase = 0; - - out << - " switch ( " << vCS() << " ) {\n"; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st == redFsm->errState ) { - out << CASE( STR( st->id ) ) << " {\n"; - out << CEND() << "\n}\n"; - } - else { - /* Label the state. */ - out << CASE( STR( st->id ) ) << " {\n"; - - /* Try singles. */ - if ( st->outSingle.length() > 0 ) { - SINGLE_SWITCH( st ); - } - else { - NOT_SINGLE( st ); - } - - out << CEND() << "\n}\n"; - } - - transBase += st->outSingle.length() + - st->outRange.length() + - ( st->defTrans != 0 ? 1 : 0 ); - } - - out << - " }\n" - "\n"; -} - -void Switch::genAnalysis() -{ - redFsm->sortByStateId(); - - /* Choose default transitions and the single transition. */ - redFsm->chooseDefaultSpan(); - - /* Choose the singles. */ - redFsm->moveSelectTransToSingle(); - - if ( redFsm->errState != 0 ) - redFsm->getErrorCond(); - - /* If any errors have occured in the input file then don't write anything. */ - if ( red->id->errorCount > 0 ) - return; - - /* Anlayze Machine will find the final action reference counts, among other - * things. We will use these in reporting the usage of fsm directives in - * action code. */ - red->analyzeMachine(); - - setKeyType(); - - /* Run the analysis pass over the table data. */ - setTableState( TableArray::AnalyzePass ); - tableDataPass(); - - /* Switch the tables over to the code gen mode. */ - setTableState( TableArray::GeneratePass ); -} - - -void Switch::tableDataPass() -{ - if ( type == Loop ) - taActions(); - - taKeyOffsets(); - taSingleLens(); - taRangeLens(); - taIndexOffsets(); - taIndices(); - - taTransCondSpacesWi(); - taTransOffsetsWi(); - taTransLengthsWi(); - - taTransCondSpaces(); - taTransOffsets(); - taTransLengths(); - - taCondTargs(); - taCondActions(); - - taToStateActions(); - taFromStateActions(); - taEofActions(); - taEofConds(); - taEofTrans(); - - taKeys(); - taCondKeys(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); -} - -void Switch::writeData() -{ - if ( type == Loop ) { - /* If there are any transtion functions then output the array. If there - * are none, don't bother emitting an empty array that won't be used. */ - if ( redFsm->anyActions() ) - taActions(); - } - - taKeyOffsets(); - taKeys(); - taSingleLens(); - taRangeLens(); - taIndexOffsets(); - - taTransCondSpaces(); - taTransOffsets(); - taTransLengths(); - - taCondKeys(); - taCondTargs(); - taCondActions(); - - if ( redFsm->anyToStateActions() ) - taToStateActions(); - - if ( redFsm->anyFromStateActions() ) - taFromStateActions(); - - if ( redFsm->anyEofActions() ) - taEofActions(); - - taEofConds(); - - if ( redFsm->anyEofTrans() ) - taEofTrans(); - - taNfaTargs(); - taNfaOffsets(); - taNfaPushActions(); - taNfaPopTrans(); - - STATE_IDS(); -} - - -void Switch::setKeyType() -{ - transKeys.setType( ALPH_TYPE(), alphType->size, alphType->isChar ); - transKeys.isSigned = keyOps->isSigned; -} - -void Switch::setTableState( TableArray::State state ) -{ - for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { - TableArray *tableArray = *i; - tableArray->setState( state ); - } -} - -void Switch::taKeyOffsets() -{ - keyOffsets.start(); - - int curKeyOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - keyOffsets.value( curKeyOffset ); - curKeyOffset += st->outSingle.length() + st->outRange.length() * 2; - } - - keyOffsets.finish(); -} - - -void Switch::taSingleLens() -{ - singleLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - singleLens.value( st->outSingle.length() ); - - singleLens.finish(); -} - - -void Switch::taRangeLens() -{ - rangeLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - rangeLens.value( st->outRange.length() ); - - rangeLens.finish(); -} - -void Switch::taIndexOffsets() -{ - indexOffsets.start(); - - int curIndOffset = 0; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Write the index offset. */ - indexOffsets.value( curIndOffset ); - - /* Move the index offset ahead. */ - curIndOffset += st->outSingle.length() + st->outRange.length(); - if ( st->defTrans != 0 ) - curIndOffset += 1; - } - - indexOffsets.finish(); -} - -void Switch::taToStateActions() -{ - toStateActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - TO_STATE_ACTION(st); - - toStateActions.finish(); -} - -void Switch::taFromStateActions() -{ - fromStateActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - FROM_STATE_ACTION(st); - - fromStateActions.finish(); -} - -void Switch::taEofActions() -{ - eofActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) - EOF_ACTION( st ); - - eofActions.finish(); -} - -void Switch::taEofConds() -{ - /* - * EOF Cond Spaces - */ - eofCondSpaces.start(); - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->outCondSpace != 0 ) - eofCondSpaces.value( st->outCondSpace->condSpaceId ); - else - eofCondSpaces.value( -1 ); - } - eofCondSpaces.finish(); - - /* - * EOF Cond Key Indixes - */ - eofCondKeyOffs.start(); - - int curOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long off = 0; - if ( st->outCondSpace != 0 ) { - off = curOffset; - curOffset += st->outCondKeys.length(); - } - eofCondKeyOffs.value( off ); - } - - eofCondKeyOffs.finish(); - - /* - * EOF Cond Key Lengths. - */ - eofCondKeyLens.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long len = 0; - if ( st->outCondSpace != 0 ) - len = st->outCondKeys.length(); - eofCondKeyLens.value( len ); - } - - eofCondKeyLens.finish(); - - /* - * EOF Cond Keys - */ - eofCondKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->outCondSpace != 0 ) { - for ( int c = 0; c < st->outCondKeys.length(); c++ ) { - CondKey key = st->outCondKeys[c]; - eofCondKeys.value( key.getVal() ); - } - } - } - - eofCondKeys.finish(); -} - -void Switch::taEofTrans() -{ - eofTrans.start(); - - /* Need to compute transition positions. */ - int totalTrans = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - totalTrans += st->outSingle.length(); - totalTrans += st->outRange.length(); - if ( st->defTrans != 0 ) - totalTrans += 1; - } - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - long trans = 0; - if ( st->eofTrans != 0 ) { - trans = totalTrans + 1; - totalTrans += 1; - } - - eofTrans.value( trans ); - } - - eofTrans.finish(); -} - -void Switch::taKeys() -{ - transKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Loop the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - transKeys.value( stel->lowKey.getVal() ); - } - - /* Loop the state's transitions. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - /* Lower key. */ - transKeys.value( rtel->lowKey.getVal() ); - - /* Upper key. */ - transKeys.value( rtel->highKey.getVal() ); - } - } - - transKeys.finish(); -} - -void Switch::taIndices() -{ - indices.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) - indices.value( stel->value->id ); - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) - indices.value( rtel->value->id ); - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) - indices.value( st->defTrans->id ); - } - - indices.finish(); -} - -void Switch::taTransCondSpaces() -{ - transCondSpaces.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - if ( trans->condSpace != 0 ) - transCondSpaces.value( trans->condSpace->condSpaceId ); - else - transCondSpaces.value( -1 ); - } - } - - transCondSpaces.finish(); -} - -void Switch::taTransOffsets() -{ - transOffsets.start(); - - int curOffset = 0; - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - transOffsets.value( curOffset ); - curOffset += trans->numConds(); - } - } - - errCondOffset = curOffset; - - transOffsets.finish(); -} - -void Switch::taTransLengths() -{ - transLengths.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - transLengths.value( trans->numConds() ); - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - transLengths.value( trans->numConds() ); - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - transLengths.value( trans->numConds() ); - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - transLengths.value( trans->numConds() ); - } - } - - transLengths.finish(); -} - -void Switch::taTransCondSpacesWi() -{ - transCondSpacesWi.start(); - - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - /* Cond Space id. */ - if ( trans->condSpace != 0 ) - transCondSpacesWi.value( trans->condSpace->condSpaceId ); - else - transCondSpacesWi.value( -1 ); - } - - transCondSpacesWi.finish(); -} - -void Switch::taTransOffsetsWi() -{ - transOffsetsWi.start(); - - int curOffset = 0; - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - transOffsetsWi.value( curOffset ); - - TransApSet::Iter next = trans; - next.increment(); - - curOffset += trans->numConds(); - } - - transOffsetsWi.finish(); -} - -void Switch::taTransLengthsWi() -{ - transLengthsWi.start(); - - for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { - transLengthsWi.value( trans->numConds() ); - - TransApSet::Iter next = trans; - next.increment(); - } - - transLengthsWi.finish(); -} - -void Switch::taCondKeys() -{ - condKeys.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - CondKey key = trans->outCondKey( c ); - condKeys.value( key.getVal() ); - } - } - } - - condKeys.finish(); -} - -void Switch::taCondTargs() -{ - condTargs.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - condTargs.value( cond->targ->id ); - } - } - } - - if ( redFsm->errCond != 0 ) { - RedCondPair *cond = &redFsm->errCond->p; - condTargs.value( cond->targ->id ); - } - - condTargs.finish(); -} - -void Switch::taCondActions() -{ - condActions.start(); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - /* Walk the singles. */ - for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { - RedTransAp *trans = stel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - COND_ACTION( cond ); - } - } - - /* Walk the ranges. */ - for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { - RedTransAp *trans = rtel->value; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond( c ); - COND_ACTION( cond ); - } - } - - /* The state's default index goes next. */ - if ( st->defTrans != 0 ) { - RedTransAp *trans = st->defTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - COND_ACTION( cond ); - } - } - } - - /* Add any eof transitions that have not yet been written out above. */ - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->eofTrans != 0 ) { - RedTransAp *trans = st->eofTrans; - for ( int c = 0; c < trans->numConds(); c++ ) { - RedCondPair *cond = trans->outCond(c); - COND_ACTION( cond ); - } - } - } - - if ( redFsm->errCond != 0 ) { - RedCondPair *cond = &redFsm->errCond->p; - COND_ACTION( cond ); - } - - condActions.finish(); -} - -void Switch::taNfaTargs() -{ - nfaTargs.start(); - - /* Offset of zero means no NFA targs, put a filler there. */ - nfaTargs.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaTargs.value( st->nfaTargs->length() ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - nfaTargs.value( targ->state->id ); - } - } - - nfaTargs.finish(); -} - -/* These need to mirror nfa targs. */ -void Switch::taNfaPushActions() -{ - nfaPushActions.start(); - - nfaPushActions.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - nfaPushActions.value( 0 ); - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_PUSH_ACTION( targ ); - } - } - - nfaPushActions.finish(); -} - -void Switch::taNfaPopTrans() -{ - nfaPopTrans.start(); - - nfaPopTrans.value( 0 ); - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs != 0 ) { - - nfaPopTrans.value( 0 ); - - for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) - NFA_POP_TEST( targ ); - } - } - - nfaPopTrans.finish(); -} - -void Switch::taNfaOffsets() -{ - nfaOffsets.start(); - - /* Offset of zero means no NFA targs, real targs start at 1. */ - long offset = 1; - - for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { - if ( st->nfaTargs == 0 ) { - nfaOffsets.value( 0 ); - } - else { - nfaOffsets.value( offset ); - offset += 1 + st->nfaTargs->length(); - } - } - - nfaOffsets.finish(); -} - - -/* Write out the array of actions. */ -std::ostream &Switch::ACTIONS_ARRAY() -{ - out << "\t0, "; - int totalActions = 1; - for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { - /* Write out the length, which will never be the last character. */ - out << act->key.length() << ", "; - /* Put in a line break every 8 */ - if ( totalActions++ % 8 == 7 ) - out << "\n\t"; - - for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) { - out << item->value->actionId; - if ( ! (act.last() && item.last()) ) - out << ", "; - - /* Put in a line break every 8 */ - if ( totalActions++ % 8 == 7 ) - out << "\n\t"; - } - } - out << "\n"; - return out; -} - -void Switch::taActions() -{ - actions.start(); - - /* Put "no-action" at the beginning. */ - actions.value( 0 ); - - for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { - /* Write out the length, which will never be the last character. */ - actions.value( act->key.length() ); - - for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) - actions.value( item->value->actionId ); - } - - actions.finish(); -} - - - - diff --git a/libfsm/switch.h b/libfsm/switch.h deleted file mode 100644 index 7f23778b..00000000 --- a/libfsm/switch.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _C_SWITCH_H -#define _C_SWITCH_H - -#include -#include "codegen.h" -#include "tables.h" - -/* Forwards. */ -struct CodeGenData; -struct NameInst; -struct RedTransAp; -struct RedStateAp; - -class Switch - : public virtual Tables -{ -protected: - enum Type { - Loop = 1, Exp - }; - -public: - Switch( const CodeGenArgs &args, Type type ) - : - Tables( args ), - type(type) - {} - - std::ostream &TRANS_GOTO( int off, RedTransAp *trans ); - void RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ); - void SINGLE_SWITCH( RedStateAp *st ); - void DEFAULT( RedStateAp *st ); - void NOT_SINGLE( RedStateAp *st ); - void LOCATE_TRANS(); - -protected: - Type type; - int transBase; - - std::ostream &COND_KEYS_v1(); - std::ostream &COND_SPACES_v1(); - std::ostream &INDICES(); - std::ostream &INDEX_OFFSETS(); - std::ostream &SINGLE_LENS(); - std::ostream &RANGE_LENS(); - std::ostream &TRANS_TARGS_WI(); - std::ostream &ACTIONS_ARRAY(); - - void taKeyOffsets(); - void taSingleLens(); - void taRangeLens(); - void taIndexOffsets(); - void taIndices(); - void taTransCondSpacesWi(); - void taTransOffsetsWi(); - void taTransLengthsWi(); - void taTransCondSpaces(); - void taTransOffsets(); - void taTransLengths(); - void taCondTargs(); - void taCondActions(); - void taToStateActions(); - void taFromStateActions(); - void taEofTrans(); - void taEofConds(); - void taEofActions(); - void taKeys(); - void taActions(); - void taCondKeys(); - void taNfaTargs(); - void taNfaOffsets(); - void taNfaPushActions(); - void taNfaPopTrans(); - - void setKeyType(); - - void setTableState( TableArray::State ); - - virtual void writeData(); - virtual void tableDataPass(); - virtual void genAnalysis(); -}; - -#endif diff --git a/libfsm/switchbreak.cc b/libfsm/switchbreak.cc deleted file mode 100644 index 567dfbc4..00000000 --- a/libfsm/switchbreak.cc +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "switchbreak.h" - -void SwitchBreak::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - std::stringstream success, error; - - out << - " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - if ( red->condSpaceList.length() > 0 ) - COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); - - success << - cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; - - error << - cond << " = " << errCondOffset << ";\n"; - - out << - " {\n" - " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" - " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" - " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << error.str() << "\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << success.str() << "\n" - " break;\n" - " }\n" - " }\n" - " }\n" - ; - } - - out << EMIT_LABEL( _match_cond ); -} - diff --git a/libfsm/switchbreak.h b/libfsm/switchbreak.h deleted file mode 100644 index fdbac68c..00000000 --- a/libfsm/switchbreak.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_SWITCHBREAK_H -#define RAGEL_SWITCHBREAK_H - -#include "switch.h" -#include "actloop.h" -#include "actexp.h" - -struct SwitchBreak -: - public Switch, public TabBreak -{ - SwitchBreak( const CodeGenArgs &args, Switch::Type type ) - : - Tables( args ), - Switch( args, type ), - TabBreak( args ) - {} - - void LOCATE_COND(); -}; - -class SwitchBreakLoop - : public SwitchBreak, public ActLoop -{ -public: - SwitchBreakLoop( const CodeGenArgs &args ) - : - Tables( args ), - SwitchBreak( args, Loop ), - ActLoop( args ) - {} -}; - - -class SwitchBreakExp - : public SwitchBreak, public ActExp -{ -public: - SwitchBreakExp( const CodeGenArgs &args ) - : - Tables( args ), - SwitchBreak( args, Exp ), - ActExp( args ) - {} -}; - - -#endif diff --git a/libfsm/switchgoto.cc b/libfsm/switchgoto.cc deleted file mode 100644 index 3b293c70..00000000 --- a/libfsm/switchgoto.cc +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2001-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "switchgoto.h" - -void SwitchGoto::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - std::stringstream success, error; - - out << - " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - if ( red->condSpaceList.length() > 0 ) - COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); - - success << - cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; - - error << - cond << " = " << errCondOffset << ";\n"; - - out << - " {\n" - " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" - " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" - " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" - " while ( " << TRUE() << " ) {\n" - " if ( _upper < _lower ) {\n" - " " << error.str() << "\n" - " break;\n" - " }\n" - "\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << success.str() << "\n" - " break;\n" - " }\n" - " }\n" - " }\n" - ; - } -} - diff --git a/libfsm/switchgoto.h b/libfsm/switchgoto.h deleted file mode 100644 index d8207325..00000000 --- a/libfsm/switchgoto.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_SWITCHGOTO_H -#define RAGEL_SWITCHGOTO_H - -#include "switch.h" -#include "actloop.h" -#include "actexp.h" - -struct SwitchGoto -: - public Switch, public TabGoto -{ - SwitchGoto( const CodeGenArgs &args, Switch::Type type ) - : - Tables( args ), - Switch( args, type ), - TabGoto( args ) - {} - - void LOCATE_COND(); -}; - -class SwitchGotoLoop - : public SwitchGoto, public ActLoop -{ -public: - SwitchGotoLoop( const CodeGenArgs &args ) - : - Tables( args ), - SwitchGoto( args, Loop ), - ActLoop( args ) - {} -}; - - -class SwitchGotoExp - : public SwitchGoto, public ActExp -{ -public: - SwitchGotoExp( const CodeGenArgs &args ) - : - Tables( args ), - SwitchGoto( args, Exp ), - ActExp( args ) - {} -}; - - -#endif diff --git a/libfsm/switchvar.cc b/libfsm/switchvar.cc deleted file mode 100644 index 5382bc04..00000000 --- a/libfsm/switchvar.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "switchvar.h" -#include "parsedata.h" - -void SwitchVar::LOCATE_COND() -{ - if ( red->condSpaceList.length() > 0 ) { - std::stringstream success, error; - - out << - " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" - " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" - " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" - "\n"; - - out << - " " << cpc << " = 0;\n"; - - if ( red->condSpaceList.length() > 0 ) - COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); - - success << - cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; - - error << - cond << " = " << errCondOffset << ";\n"; - - out << - " {\n" - " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" - " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" - " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" - " _bsc = 1;\n" - " while ( _bsc == 1 ) {\n" - " if ( _upper < _lower ) {\n" - " " << error.str() << "\n" - " _bsc = 0;\n" - " }\n" - " else {\n" - " _mid = _lower + ((_upper-_lower) >> 1);\n" - " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _upper = _mid - 1;\n" - " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" - " _lower = _mid + 1;\n" - " else {\n" - " " << success.str() << "\n" - " _bsc = 0;\n" - " }\n" - " }\n" - " }\n" - " }\n" - ; - } -} - diff --git a/libfsm/switchvar.h b/libfsm/switchvar.h deleted file mode 100644 index 220963a4..00000000 --- a/libfsm/switchvar.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014-2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef RAGEL_SWITCHVAR_H -#define RAGEL_SWITCHVAR_H - -#include "switch.h" -#include "actloop.h" -#include "actexp.h" - -struct SwitchVar -: - public Switch, public TabVar -{ - SwitchVar( const CodeGenArgs &args, Switch::Type type ) - : - Tables( args ), - Switch( args, type ), - TabVar( args ) - {} - - void VAR_COND_BIN_SEARCH( Variable &var, TableArray &keys, std::string ok, std::string error ); - - //void LOCATE_TRANS(); - void LOCATE_COND(); -}; - -class SwitchVarLoop - : public SwitchVar, public ActLoop -{ -public: - SwitchVarLoop( const CodeGenArgs &args ) - : - Tables( args ), - SwitchVar( args, Loop ), - ActLoop( args ) - {} -}; - -class SwitchVarExp -: - public SwitchVar, public ActExp -{ -public: - SwitchVarExp( const CodeGenArgs &args ) - : - Tables( args ), - SwitchVar( args, Exp ), - ActExp( args ) - {} -}; - -#endif diff --git a/libfsm/tabbreak.cc b/libfsm/tabbreak.cc deleted file mode 100644 index ee82cc0c..00000000 --- a/libfsm/tabbreak.cc +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright 2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "tables.h" -#include "binary.h" -#include "flat.h" - -std::string TabBreak::BREAK( GotoLabel &label ) -{ - string ret = "break"; - if ( loopLabels ) { - ret += " "; - ret += label.ref(); - } - return ret; -} - -std::string TabBreak::CONTINUE( GotoLabel &label ) -{ - string ret = "continue"; - if ( loopLabels ) { - ret += " "; - ret += label.ref(); - } - return ret; -} - -std::string TabBreak::BREAK_LABEL( GotoLabel &label ) -{ - if ( loopLabels ) { - if ( label.isReferenced ) - return std::string(label.name) + "::\n"; - } - return ""; -} - -void TabBreak::CONTROL_JUMP( ostream &ret, bool inFinish ) -{ - ret << "if ( " << TRUE() << " ) break " << _again << ";"; -} - -void TabBreak::GOTO( ostream &ret, int gotoDest, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << ";"; - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabBreak::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabBreak::CALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << - vCS() << "; " << TOP() << " += 1;" << vCS() << " = " << - callDest << ";"; - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabBreak::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << - TOP() << " += 1;" << vCS() << " = " << - callDest << "; " << CLOSE_GEN_BLOCK(); -} - -void TabBreak::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << - vCS() << "; " << TOP() << " += 1;" << vCS() << - " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabBreak::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << vCS() << - " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); -} - -void TabBreak::RET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabBreak::NRET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << CLOSE_GEN_BLOCK(); -} - -void TabBreak::BREAK( ostream &ret, int targState, bool csForced ) -{ - ret << - OPEN_GEN_BLOCK() << - P() << " += 1; " << - "break " << _resume << "; " << - CLOSE_GEN_BLOCK(); -} - -void TabBreak::NBREAK( ostream &ret, int targState, bool csForced ) -{ - ret << - OPEN_GEN_BLOCK() << - P() << " += 1; " << - nbreak << " = 1;" << - CLOSE_GEN_BLOCK(); -} - -void TabBreak::writeExec() -{ - out << - " {\n"; - - DECLARE( INT(), ps ); - DECLARE( INT(), cpc ); - DECLARE( INT(), nbreak ); - DECLARE( INT(), klen ); - DECLARE( INDEX( ARR_TYPE( condKeys ) ), ckeys ); - DECLARE( INDEX( ARR_TYPE( eofCondKeys ) ), cekeys ); - DECLARE( UINT(), trans, " = 0" ); - DECLARE( UINT(), cond, " = 0" ); - DECLARE( INDEX( ALPH_TYPE() ), keys ); - DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); - DECLARE( INDEX( ARR_TYPE( indices ) ), inds ); - DECLARE( UINT(), nacts ); - DECLARE( INT(), have ); - DECLARE( INT(), pop_test ); - DECLARE( INT(), new_recs ); - DECLARE( INT(), alt ); - DECLARE( INT(), ic ); - - out << BREAK_LABEL( _resume ); - - /* Do we break out on no more input. */ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - if ( !noEnd ) { - if ( eof ) { - out << - " while ( " << P() << " != " << PE() << " || " << P() << " == " << vEOF() << " ) {\n"; - } - else { - out << - " while ( " << P() << " != " << PE() << " ) {\n"; - } - } - else { - out << - " while ( " << TRUE() << " ) {\n"; - - } - - NFA_PUSH( vCS() ); - - if ( loopLabels ) { - out << BREAK_LABEL( _again ); - out << "while ( " << TRUE() << " ) {\n"; - } - - FROM_STATE_ACTIONS(); - - if ( !noEnd && eof ) { - out << - "if ( " << P() << " == " << vEOF() << " ) {\n"; - - if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) { - if ( redFsm->anyEofTrans() ) { - out << - " if ( " << ARR_REF( eofTrans ) << "[" << vCS() << "] > 0 ) {\n" - " " << trans << " = " << - CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n" - " }\n"; - } - } - - out << - "}\n" - "else {\n"; - } - - LOCATE_TRANS(); - - if ( !noEnd && eof ) { - out << - "}\n"; - } - - LOCATE_COND(); - - if ( redFsm->anyRegCurStateRef() ) - out << " " << ps << " = " << vCS() << ";\n"; - - string condVar = - red->condSpaceList.length() != 0 ? cond.ref() : trans.ref(); - - out << - " " << vCS() << " = " << CAST(INT()) << ARR_REF( condTargs ) << "[" << condVar << "];\n\n"; - - if ( redFsm->anyRegActions() ) { - out << - " if ( " << ARR_REF( condActions ) << "[" << condVar << "] != 0 ) {\n" - "\n"; - - if ( redFsm->anyRegNbreak() ) - out << " " << nbreak << " = 0;\n"; - - REG_ACTIONS( condVar ); - - if ( redFsm->anyRegNbreak() ) { - out << - " if ( " << nbreak << " == 1 )\n" - " " << BREAK( _resume ) << ";\n"; - } - - out << "}\n"; - } - - - if ( loopLabels ) { - out << BREAK( _again ) << ";\n}\n"; - } - else { - out << "\n" << EMIT_LABEL( _again ); - } - - if ( !noEnd && eof ) { - out << - " if ( " << P() << " == " << vEOF() << " ) {\n" - " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" - " " << BREAK( _resume ) << ";\n" - " }\n" - " else {\n"; - } - - TO_STATE_ACTIONS(); - - if ( redFsm->errState != 0 ) { - out << - " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; - } - - out << - " " << P() << " += 1;\n" - " " << CONTINUE( _resume ) << ";\n"; - - if ( redFsm->errState != 0 ) { - out << - " }\n"; - } - - if ( !noEnd && eof ) { - out << - " }\n"; - } - - if ( redFsm->anyNfaStates() ) { - out << - " if ( nfa_len == 0 )\n" - " " << BREAK ( _resume ) << ";\n" - "\n" - " nfa_count += 1;\n" - " nfa_len -= 1;\n" - " " << P() << " = nfa_bp[nfa_len].p;\n" - ; - - if ( redFsm->bAnyNfaPops ) { - NFA_FROM_STATE_ACTION_EXEC(); - - NFA_POP_TEST_EXEC(); - - out << - " if ( " << pop_test << " )\n" - " " << vCS() << " = nfa_bp[nfa_len].state;\n" - " else\n" - " " << vCS() << " = " << ERROR_STATE() << ";\n"; - } - else { - out << - " " << vCS() << " = nfa_bp[nfa_len].state;\n"; - - } - - NFA_POST_POP(); - } - else { - out << - " " << BREAK( _resume ) << ";\n"; - } - - out << - "}\n"; - - out << EMIT_LABEL( _out ); - - out << " }\n"; -} - diff --git a/libfsm/tabgoto.cc b/libfsm/tabgoto.cc deleted file mode 100644 index ca90cb9d..00000000 --- a/libfsm/tabgoto.cc +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright 2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "tables.h" -#include "binary.h" -#include "flat.h" - -void TabGoto::CONTROL_JUMP( ostream &ret, bool inFinish ) -{ - ret << "goto " << _again << ";"; -} - -void TabGoto::GOTO( ostream &ret, int gotoDest, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << ";"; - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabGoto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabGoto::CALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << - vCS() << "; " << TOP() << " += 1;" << vCS() << " = " << - callDest << ";"; - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabGoto::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << - TOP() << " += 1;" << vCS() << " = " << - callDest << "; " << CLOSE_GEN_BLOCK(); -} - -void TabGoto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << - vCS() << "; " << TOP() << " += 1;" << vCS() << - " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";"; - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabGoto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << vCS() << - " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); -} - -void TabGoto::RET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - CONTROL_JUMP( ret, inFinish ); - ret << CLOSE_GEN_BLOCK(); -} - -void TabGoto::NRET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << CLOSE_GEN_BLOCK(); -} - -void TabGoto::BREAK( ostream &ret, int targState, bool csForced ) -{ - ret << - OPEN_GEN_BLOCK() << - P() << " += 1; " << - "goto " << _out << "; " << - CLOSE_GEN_BLOCK(); -} - -void TabGoto::NBREAK( ostream &ret, int targState, bool csForced ) -{ - ret << - OPEN_GEN_BLOCK() << - P() << " += 1; " << - nbreak << " = 1;" << - CLOSE_GEN_BLOCK(); -} - -void TabGoto::writeExec() -{ - out << - " {\n"; - - DECLARE( INT(), ps ); - DECLARE( INT(), cpc ); - DECLARE( INT(), nbreak ); - DECLARE( INT(), klen ); - DECLARE( INDEX( ARR_TYPE( condKeys ) ), ckeys ); - DECLARE( INDEX( ARR_TYPE( eofCondKeys ) ), cekeys ); - DECLARE( UINT(), trans, " = 0" ); - DECLARE( UINT(), cond, " = 0" ); - DECLARE( INDEX( ALPH_TYPE() ), keys ); - DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); - DECLARE( INDEX( ARR_TYPE( indices ) ), inds ); - DECLARE( UINT(), nacts ); - DECLARE( INT(), pop_test ); - DECLARE( INT(), new_recs ); - DECLARE( INT(), alt ); - DECLARE( INT(), ic ); - - out << EMIT_LABEL( _resume ); - - /* Do we break out on no more input. */ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - if ( !noEnd ) { - if ( eof ) { - out << - " if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" - " goto " << _out << ";\n"; - } - else { - out << - " if ( " << P() << " == " << PE() << " )\n" - " goto " << _out << ";\n"; - } - } - - NFA_PUSH( vCS() ); - - FROM_STATE_ACTIONS(); - - if ( !noEnd && eof ) { - out << - "if ( " << P() << " == " << vEOF() << " ) {\n"; - - if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) { - if ( redFsm->anyEofTrans() ) { - out << - " if ( " << ARR_REF( eofTrans ) << "[" << vCS() << "] > 0 ) {\n" - " " << trans << " = " << - CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n" - " }\n"; - } - } - - out << - "}\n" - "else {\n"; - } - - LOCATE_TRANS(); - - if ( !noEnd && eof ) { - out << - "}\n"; - } - - LOCATE_COND(); - - if ( redFsm->anyRegCurStateRef() ) - out << " " << ps << " = " << vCS() << ";\n"; - - string condVar = - red->condSpaceList.length() != 0 ? cond.ref() : trans.ref(); - - out << - " " << vCS() << " = " << CAST(INT()) << ARR_REF( condTargs ) << "[" << condVar << "];\n\n"; - - if ( redFsm->anyRegActions() ) { - out << - " if ( " << ARR_REF( condActions ) << "[" << condVar << "] != 0 ) {\n" - "\n"; - - if ( redFsm->anyRegNbreak() ) - out << " " << nbreak << " = 0;\n"; - - REG_ACTIONS( condVar ); - - if ( redFsm->anyRegNbreak() ) { - out << - " if ( " << nbreak << " == 1 )\n" - " goto " << _out << ";\n"; - } - - out << "}\n"; - } - - out << "\n" << EMIT_LABEL( _again ); - - if ( !noEnd && eof ) { - out << - " if ( " << P() << " == " << vEOF() << " ) {\n" - " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" - " goto " << _out << ";\n" - " }\n" - " else {\n"; - } - - TO_STATE_ACTIONS(); - - if ( redFsm->errState != 0 ) { - out << - " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; - } - - out << - " " << P() << " += 1;\n" - " goto " << _resume << ";\n"; - - if ( redFsm->errState != 0 ) { - out << - " }\n"; - } - - if ( !noEnd && eof ) { - out << - " }\n"; - } - - if ( redFsm->anyNfaStates() ) { - out << - " if ( nfa_len == 0 )\n" - " goto " << _out << ";\n" - "\n" - " nfa_count += 1;\n" - " nfa_len -= 1;\n" - " " << P() << " = nfa_bp[nfa_len].p;\n" - ; - - if ( redFsm->bAnyNfaPops ) { - NFA_FROM_STATE_ACTION_EXEC(); - - NFA_POP_TEST_EXEC(); - - out << - " if ( " << pop_test << " )\n" - " " << vCS() << " = nfa_bp[nfa_len].state;\n" - " else\n" - " " << vCS() << " = " << ERROR_STATE() << ";\n"; - } - else { - out << - " " << vCS() << " = nfa_bp[nfa_len].state;\n"; - - } - - NFA_POST_POP(); - - out << "goto " << _resume << ";\n"; - } - - out << EMIT_LABEL( _out ); - - out << " }\n"; -} - diff --git a/libfsm/tables.cc b/libfsm/tables.cc deleted file mode 100644 index 40edd93e..00000000 --- a/libfsm/tables.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "tables.h" - -void Tables::CURS( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_EXPR() << ps << CLOSE_GEN_EXPR(); -} - -void Tables::TARGS( ostream &ret, bool inFinish, int targState ) -{ - ret << OPEN_GEN_EXPR() << vCS() << CLOSE_GEN_EXPR(); -} - -void Tables::NEXT( ostream &ret, int nextDest, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << nextDest << ";" << CLOSE_GEN_BLOCK(); -} - -void Tables::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << "" << vCS() << " = " << OPEN_HOST_EXPR(); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";" << CLOSE_GEN_BLOCK(); -} - -void Tables::EOF_TRANS() -{ - out << - "" << trans << " = " << CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n"; - - if ( red->condSpaceList.length() > 0 ) { - out << - "" << cond << " = " << CAST(UINT()) << ARR_REF( transOffsets ) << "[" << trans << "];\n"; - } -} - -void Tables::COND_EXEC( std::string expr ) -{ - out << - " switch ( " << expr << " ) {\n" - "\n"; - - for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { - GenCondSpace *condSpace = csi; - out << " " << CASE( STR( condSpace->condSpaceId ) ) << " {\n"; - for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { - out << "if ( "; - CONDITION( out, *csi ); - Size condValOffset = (1 << csi.pos()); - out << " ) " << cpc << " += " << condValOffset << ";\n"; - } - - out << - " " << CEND() << "\n}\n"; - } - - out << - " }\n"; -} - diff --git a/libfsm/tables.h b/libfsm/tables.h deleted file mode 100644 index 258f869e..00000000 --- a/libfsm/tables.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _TABLES_H -#define _TABLES_H - -#include -#include "codegen.h" - -struct Tables -: - public CodeGen -{ - Tables( const CodeGenArgs &args ) - : - CodeGen( args ), - - pa( "_pa" ), - klen( "_klen" ), - ckeys( "_ckeys" ), - cekeys( "_cekeys" ), - trans( "_trans" ), - cond( "_cond" ), - keys( "_keys" ), - acts( "_acts" ), - nacts( "_nacts" ), - inds( "_inds" ), - - cont( "_cont" ), - nfa_repeat( "_nfa_repeat" ), - nfa_test( "_nfa_test" ), - ps( "_ps" ), - nbreak( "_nbreak" ), - have( "__have" ), - ic( "_ic" ), - - _out("_out"), - _pop("_pop"), - _test_eof( "_test_eof" ), - _resume( "_resume" ), - _match_cond( "_match_cond" ), - _again( "_again" ), - _match( "_match" ), - _eof_goto( "_eof_goto" ), - - actions( "actions", *this ), - transKeys( "trans_keys", *this ), - charClass( "char_class", *this ), - flatIndexOffset( "index_offsets", *this ), - indices( "indices", *this ), - indexDefaults( "index_defaults", *this ), - transCondSpaces( "trans_cond_spaces", *this ), - transOffsets( "trans_offsets", *this ), - condTargs( "cond_targs", *this ), - condActions( "cond_actions", *this ), - toStateActions( "to_state_actions", *this ), - fromStateActions( "from_state_actions", *this ), - eofCondSpaces( "eof_cond_spaces", *this ), - eofCondKeyOffs( "eof_cond_key_offs", *this ), - eofCondKeyLens( "eof_cond_key_lens", *this ), - eofCondKeys( "eof_cond_keys", *this ), - eofActions( "eof_actions", *this ), - eofTrans( "eof_trans", *this ), - - keyOffsets( "key_offsets", *this ), - singleLens( "single_lengths", *this ), - rangeLens( "range_lengths", *this ), - indexOffsets( "index_offsets", *this ), - transCondSpacesWi( "trans_cond_spaces_wi", *this ), - transOffsetsWi( "trans_offsets_wi", *this ), - transLengthsWi( "trans_lengths_wi", *this ), - transLengths( "trans_lengths", *this ), - condKeys( "cond_keys", *this ) - {} - - Variable pa; - Variable klen; - Variable ckeys; - Variable cekeys; - Variable trans; - Variable cond; - Variable keys; - Variable acts; - Variable nacts; - Variable inds; - Variable cont; - Variable nfa_repeat; - Variable nfa_test; - Variable ps; - Variable nbreak; - Variable have; - Variable ic; - - GotoLabel _out; - GotoLabel _pop; - GotoLabel _test_eof; - GotoLabel _resume; - GotoLabel _match_cond; - GotoLabel _again; - GotoLabel _match; - GotoLabel _eof_goto; - - TableArray actions; - TableArray transKeys; - TableArray charClass; - TableArray flatIndexOffset; - TableArray indices; - TableArray indexDefaults; - TableArray transCondSpaces; - TableArray transOffsets; - TableArray condTargs; - TableArray condActions; - TableArray toStateActions; - TableArray fromStateActions; - TableArray eofCondSpaces; - TableArray eofCondKeyOffs; - TableArray eofCondKeyLens; - TableArray eofCondKeys; - TableArray eofActions; - TableArray eofTrans; - - TableArray keyOffsets; - TableArray singleLens; - TableArray rangeLens; - TableArray indexOffsets; - TableArray transCondSpacesWi; - TableArray transOffsetsWi; - TableArray transLengthsWi; - TableArray transLengths; - TableArray condKeys; - - int errCondOffset; - - virtual void TO_STATE_ACTION( RedStateAp *state ) = 0; - virtual void FROM_STATE_ACTION( RedStateAp *state ) = 0; - virtual void EOF_ACTION( RedStateAp *state ) = 0; - virtual void COND_ACTION( RedCondPair *cond ) = 0; - - virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ) = 0; - virtual void NFA_POP_TEST( RedNfaTarg *targ ) = 0; - virtual void NFA_FROM_STATE_ACTION_EXEC() = 0; - - virtual void FROM_STATE_ACTIONS() = 0; - virtual void REG_ACTIONS( std::string cond ) = 0; - virtual void TO_STATE_ACTIONS() = 0; - virtual void EOF_ACTIONS() = 0; - - void CURS( ostream &ret, bool inFinish ); - void TARGS( ostream &ret, bool inFinish, int targState ); - void NEXT( ostream &ret, int nextDest, bool inFinish ); - void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void EOF_TRANS(); - void COND_EXEC( std::string expr ); -}; - -struct TabGoto -: - public virtual Tables -{ - TabGoto( const CodeGenArgs &args ) - : - Tables( args ) - {} - - void CONTROL_JUMP( ostream &ret, bool inFinish ); - - void GOTO( ostream &ret, int gotoDest, bool inFinish ); - void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void CALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); - void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void RET( ostream &ret, bool inFinish ); - void NRET( ostream &ret, bool inFinish ); - void BREAK( ostream &ret, int targState, bool csForced ); - void NBREAK( ostream &ret, int targState, bool csForced ); - - void NFA_POP() {} - - void writeExec(); -}; - -struct TabBreak -: - public virtual Tables -{ - TabBreak( const CodeGenArgs &args ) - : - Tables( args ), - loopLabels( args.loopLabels ) - {} - - void CONTROL_JUMP( ostream &ret, bool inFinish ); - - void GOTO( ostream &ret, int gotoDest, bool inFinish ); - void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void CALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); - void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void RET( ostream &ret, bool inFinish ); - void NRET( ostream &ret, bool inFinish ); - void BREAK( ostream &ret, int targState, bool csForced ); - void NBREAK( ostream &ret, int targState, bool csForced ); - - void NFA_POP() {} - - void writeExec(); - - bool loopLabels; - std::string BREAK( GotoLabel &label ); - std::string CONTINUE( GotoLabel &label ); - std::string BREAK_LABEL( GotoLabel &label ); -}; - -struct TabVar -: - public virtual Tables -{ - TabVar( const CodeGenArgs &args ) - : - Tables( args ) - {} - - void GOTO( ostream &ret, int gotoDest, bool inFinish ); - void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); - void CALL( ostream &ret, int callDest, int targState, bool inFinish ); - void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); - void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); - void RET( ostream &ret, bool inFinish ); - void NRET( ostream &ret, bool inFinish ); - void BREAK( ostream &ret, int targState, bool csForced ); - void NBREAK( ostream &ret, int targState, bool csForced ); - - void NFA_POP() {} - - std::string BREAK( GotoLabel &label ); - std::string CONTINUE( GotoLabel &label ); - std::string BREAK_LABEL( GotoLabel &label ); - - void writeExec(); -}; - - -#endif diff --git a/libfsm/tabvar.cc b/libfsm/tabvar.cc deleted file mode 100644 index 02bd7b55..00000000 --- a/libfsm/tabvar.cc +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2018 Adrian Thurston - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "tables.h" -#include "flatvar.h" -#include "binvar.h" - -std::string TabVar::BREAK( GotoLabel &label ) -{ - return "{ _cont = 0; _again = 0; }"; -} - -std::string TabVar::CONTINUE( GotoLabel &label ) -{ - return "{ _cont = 0; _again = 1; }"; -} - -std::string TabVar::BREAK_LABEL( GotoLabel &label ) -{ - return ""; -} - -void TabVar::GOTO( ostream &ret, int gotoDest, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << ";" << CLOSE_GEN_BLOCK(); -} - -void TabVar::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR( "-", 1 ); - INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";" << CLOSE_GEN_BLOCK(); -} - -void TabVar::CALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - red->id->error() << "cannot use fcall in -B mode" << std::endl; - red->id->abortCompile( 1 ); -} - -void TabVar::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << - vCS() << "; " << TOP() << " += 1;" << vCS() << " = " << - callDest << ";" << CLOSE_GEN_BLOCK(); -} - -void TabVar::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - red->id->error() << "cannot use fcall in -B mode" << std::endl; - red->id->abortCompile( 1 ); -} - -void TabVar::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK(); - - if ( red->prePushExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->prePushExpr ); - INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << STACK() << "[" << TOP() << "] = " << - vCS() << "; " << TOP() << " += 1;" << vCS() << - " = " << OPEN_HOST_EXPR( "-", 1 ); - INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); - ret << CLOSE_HOST_EXPR() << ";" << CLOSE_GEN_BLOCK(); -} - -void TabVar::RET( ostream &ret, bool inFinish ) -{ - red->id->error() << "cannot use fret in -B mode" << std::endl; - red->id->abortCompile( 1 ); -} - -void TabVar::NRET( ostream &ret, bool inFinish ) -{ - ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << - STACK() << "[" << TOP() << "]; "; - - if ( red->postPopExpr != 0 ) { - ret << OPEN_HOST_BLOCK( red->postPopExpr ); - INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); - ret << CLOSE_HOST_BLOCK(); - } - - ret << CLOSE_GEN_BLOCK(); -} - -void TabVar::BREAK( ostream &ret, int targState, bool csForced ) -{ - red->id->error() << "cannot use fbreak in -B mode" << std::endl; - red->id->abortCompile( 1 ); -} - -void TabVar::NBREAK( ostream &ret, int targState, bool csForced ) -{ - ret << - OPEN_GEN_BLOCK() << - P() << "+= 1;\n" << - nbreak << " = 1;" << - CLOSE_GEN_BLOCK(); -} - -void TabVar::writeExec() -{ - out << - "{\n"; - - DECLARE( INT(), ps ); - DECLARE( INT(), cpc ); - DECLARE( INT(), nbreak ); - DECLARE( INT(), klen ); - DECLARE( INDEX( ARR_TYPE( condKeys ) ), ckeys ); - DECLARE( INDEX( ARR_TYPE( eofCondKeys ) ), cekeys ); - DECLARE( UINT(), trans, " = 0" ); - DECLARE( UINT(), cond, " = 0" ); - DECLARE( INDEX( ALPH_TYPE() ), keys ); - DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); - DECLARE( INDEX( ARR_TYPE( indices ) ), inds ); - DECLARE( UINT(), nacts ); - DECLARE( INT(), have ); - DECLARE( INT(), pop_test ); - DECLARE( INT(), new_recs ); - DECLARE( INT(), alt ); - DECLARE( INT(), ic ); - - out << UINT() << " _have = 0;\n"; - out << UINT() << " _cont = 1;\n"; - out << UINT() << " _again = 1;\n"; - out << UINT() << " _bsc = 1;\n"; - - out << BREAK_LABEL( _resume ); - - /* Do we break out on no more input. */ - bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); - if ( !noEnd ) { - if ( eof ) { - out << - " while ( _again == 1 && ( " << P() << " != " << PE() << " || " << P() << " == " << vEOF() << " ) ) {\n"; - } - else { - out << - " while ( _again == 1 && " << P() << " != " << PE() << " ) {\n"; - } - } - else { - out << - " while ( _again == 1 ) {\n"; - - } - - out << "_cont = 1;\n"; - out << "_again = 1;\n"; - - NFA_PUSH( vCS() ); - - FROM_STATE_ACTIONS(); - - if ( !noEnd && eof ) { - out << - "if ( " << P() << " == " << vEOF() << " ) {\n"; - - if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) { - if ( redFsm->anyEofTrans() ) { - out << - " if ( " << ARR_REF( eofTrans ) << "[" << vCS() << "] > 0 ) {\n" - " " << trans << " = " << - CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n" - " }\n"; - } - } - - out << - "}\n" - "else {\n"; - } - - LOCATE_TRANS(); - - if ( !noEnd && eof ) { - out << - "}\n"; - } - - LOCATE_COND(); - - if ( redFsm->anyRegCurStateRef() ) - out << " " << ps << " = " << vCS() << ";\n"; - - string condVar = - red->condSpaceList.length() != 0 ? cond.ref() : trans.ref(); - - out << - " " << vCS() << " = " << CAST(INT()) << ARR_REF( condTargs ) << "[" << condVar << "];\n\n"; - - if ( redFsm->anyRegActions() ) { - out << - " if ( " << ARR_REF( condActions ) << "[" << condVar << "] != 0 ) {\n" - "\n"; - - if ( redFsm->anyRegNbreak() ) - out << " " << nbreak << " = 0;\n"; - - REG_ACTIONS( condVar ); - - if ( redFsm->anyRegNbreak() ) { - out << - " if ( " << nbreak << " == 1 )\n" - " " << BREAK( _resume ) << "\n"; - } - - out << "}\n"; - } - - out << "if ( _cont == 1 ) {\n"; - - out << "\n" << EMIT_LABEL( _again ); - - if ( !noEnd && eof ) { - out << - " if ( " << P() << " == " << vEOF() << " ) {\n" - " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" - " " << BREAK( _resume ) << "\n" - " }\n" - " else {\n"; - } - - TO_STATE_ACTIONS(); - - if ( redFsm->errState != 0 ) { - out << - " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; - } - - out << - " " << P() << " += 1;\n" - " " << CONTINUE( _resume ) << "\n"; - - if ( redFsm->errState != 0 ) { - out << - " }\n"; - } - - if ( !noEnd && eof ) { - out << - " }\n"; - } - - out << "if ( _cont == 1 ) {\n"; - - if ( redFsm->anyNfaStates() ) { - out << - " if ( nfa_len == 0 )\n" - " " << BREAK ( _resume ) << "\n" - "\n"; - - out << "if ( _cont == 1 ) {\n"; - - out << - " nfa_count += 1;\n" - " nfa_len -= 1;\n" - " " << P() << " = nfa_bp[nfa_len].p;\n" - ; - - if ( redFsm->bAnyNfaPops ) { - NFA_FROM_STATE_ACTION_EXEC(); - - NFA_POP_TEST_EXEC(); - - out << - " if ( " << pop_test << " )\n" - " " << vCS() << " = nfa_bp[nfa_len].state;\n" - " else\n" - " " << vCS() << " = " << ERROR_STATE() << ";\n"; - } - else { - out << - " " << vCS() << " = nfa_bp[nfa_len].state;\n"; - - } - - NFA_POST_POP(); - - /* cont */ - out << "}\n"; - } - else { - out << - " " << BREAK( _resume ) << "\n"; - } - - /* cont */ - out << "}}\n"; - - /* P loop. */ - out << "}\n"; - - out << EMIT_LABEL( _out ); - - /* Variable decl. */ - out << "}\n"; -} - diff --git a/src/Makefile.am b/src/Makefile.am index 5a53f040..bc43d75d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -AM_CPPFLAGS = -I$(top_srcdir)/aapl +SUBDIRS = aapl libfsm cgil + +AM_CPPFLAGS = -I$(top_srcdir)/src/aapl AUTOMAKE_OPTIONS = subdir-objects diff --git a/src/aapl/.gitignore b/src/aapl/.gitignore new file mode 100644 index 00000000..b336cc7c --- /dev/null +++ b/src/aapl/.gitignore @@ -0,0 +1,2 @@ +/Makefile +/Makefile.in diff --git a/src/aapl/COPYING b/src/aapl/COPYING new file mode 100644 index 00000000..e246673a --- /dev/null +++ b/src/aapl/COPYING @@ -0,0 +1,20 @@ + +Copyright (c) 2001-2016 Adrian Thurston et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/aapl/ChangeLog b/src/aapl/ChangeLog new file mode 100644 index 00000000..483ad825 --- /dev/null +++ b/src/aapl/ChangeLog @@ -0,0 +1,562 @@ +Aapl 2.14 - March 17, 2006 +========================== + -Added a transfer function to the double list. This function should supersede + the use of shallowCopy + abandon. In the by-value list the destination list + is emptied first. + -Added a transfer function to the Avl tree. + -All double lists and Avl trees now implement deep copies in the + operator=(...) functions and copy constructors. Previously only the by-value + versions of these structures would do deep copies. + -Removed the deep and shallow copy functions from the double lists and Avl + trees. These structures should now be consistent accross all variants except + for the fact that the by-value versions delete elements in destructors and + when overwriting and the others simply abandon their elements (which may not + be allocated on the stack). + +Aapl 2.13 - Jan 25, 2006 +======================== + -The vector and binary search table constructors that set an initial amount + of allocation have been removed. They have been replaced with constructors + that insert initial values, which is a more common task and is a more + intuitive use for contructors. + -Removed the String class. Better to use the stl string class. Aapl::String + does not provide any real advantage over the STL version. Aapl::String has + not been heavily tested and does not have very much functionality. + -Removed the "tiny" versions of templates. These templates had functionality + reduced in the interest of reducing compiled binary size. These templates + did not find any real world use, nor were they heavily tested. + -Removed the "simple" versions of vectors. These should be implemented as + template specializations, not new classes. May be brought back as such in + the future. + -Added vinsert and vremove routines for accessing the insert and remove of + the underlying vector of binary search tables. Previously, these were + accessed by prefixing the insert call with the vector base class name, + however this method is somewhat inconvenient, because it either requires all + the template arguments to be given or an additional typedef to be made. + Prefixing the call with the letter v is simpler. + +Aapl 2.12 - May 15, 2005 +======================== + -Documentation updates to trees, lists, compare classes, binary search tables + and various other places. + -Added iterator documentation and example. + -Added proper includes for all examples, which now all compile. + -Table comparisons now properly inherit the CompareT class, enabling table + comparisons to call non-static element compare classes. + -Removed the Deque structure. This structure was never used, incomplete and + poorly tested. In most problems for which it is a candidate (large + collections of objects in sequential ordering that are not required to be in + contiguous memory), a simple replacement can easily be implemented with a + small amount of code. Rather than be allowed to go unused and unmaintained, + it is removed. + -In the AvlTreeEl structures for Maps and Sets, getKey can be const. + -Removed the non-standard malloc include. + +Aapl 2.11 - May 30, 2004 +======================== + -Moved from ints to longs for most integer values to get 64 bit numbers on 64 + bit machines. Fixes alignment problems on 64 bit machines. + -Added AvliBasic, the linked version of the basic tree where the entire + element is the key. + -Updated documentation. + +Aapl 2.10 -- Feb 5, 2004 +======================== + -Fixes for two-stage lookup. Compiles with gcc-3.4 + Details at http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html#Name%20lookup + -Fixed wrong Vector::Iter::pos() routines. + -FsmGraph code removed from aapl, as an increasing amount of diverse + specialization is required in applications. No longer feels like a general + purpose template. + +Aapl 2.9 -- Dec 8, 2003 +======================= + -Rewrote Deque to look and behave more like the rest of Aapl. Split it into + Deque and DequeSimp. Deque supports complex classes with constructors and + destructors. DequeSimp is for simple classes that can be coppied with + memcpy. Wrote stress tests for both. + -SVector::makeRawSpaceFor fixed to detach from the vector when the current + storage is shared. This affected SBstTable::insert() routines and caused + them to modify shared data, instead of behaving like copy on write as they + should. + -Tiny templates renamed to be consistent with the non-tiny templates. The + tiny versions now only have a T prepended to the name, the second character + is no longer lowercased. This changes Tavl* to TAvl*, TdList* to TDList* and + TsVect* to TSVect*. + -Removed AvlPmap, AvlPset, AvliPmap, and AvliPset. Rationale is that these + are too application specific. They require a pointer as the key (not + anything that dereferences the pointer) and so do not generalize very well. + Due to their lack of generality, they belong in application code. They + allowed a savings in code size when many instantiations were necessary, + however, the savings was only slightly better than TAvlTree. + -Test programs split into stress tests and non stress tests. All stress tests + run indefinitely as long as they don't encounter an error condition, in + which case they terminate by an assertion failure. They can be given a time + limit to run for by specifying the number of seconds on the command line. + -FsmGraph given repetition operators for repeating a machine n times. + -Concatenation operator accepts an optional list of states to draw the epsilon + transition instead of the from graph's final state set. + -Useless shared table header template parameter removed. + +Aapl 2.8.2 -- Aug 18, 2003 +========================== + -AvlSet::Iter and DListVal::Iter data access operators (*, ->) now return the + node in the tree, rather than value. This is to make all iterator behaviour + consistent: return the element. This also allows for O(1) statements like + tree.detach( iter ); + -DList and DListMel and Avl trees that are not sets or maps no longer do a + deep copy in the copy constructor and assignement operator. This is to stay + consistent with the fact that these lists and trees are not responsible for + managing the memory of the elements they contain. + -Table compare split into static and non-static versions. Default is the + static version, which compiles out the 'this' pointer. If an item compare + requires access to class data, the CmpTableNs can be used. + -The user's Fsm class is now required as a template parameter to FsmTrans, + FsmState and FsmGraph. + -All callbacks moved into FsmGraph class to allow the access of user data in + FsmGraph. + -Added callbacks for state creation and destruction to FsmGraph. + -EpsilonOp now takes a list of fsms to draw ops to. Allows the merging in of + several machines using only a single invocation of the NFA to DFA algorithm. + -FsmKeyOps is now expected to be a base class of the derived fsm class (ie a + sibling of FsmGraph). + -In transition lists are encapsulated in a class with an iterator. + -Unused leavingFsm parameter to starOp and concatOp removed. + -Fixed documentation building warnings. + -Documented iterator classes. + +Aapl 2.8.1 -- Jun 11, 2003 +========================== + -All iterator endpoint test functions have been renamed. + Iter::more() -> Iter::lte() + Iter::done() -> Iter::end() + Iter::revMore() -> Iter::gtb() + Iter::revDone() -> Iter::beg() + The rationale is that it is not obvious that more() and done() are direction + specific. It is more clear that lte() (less than end) needs to be changed to + gtb() (greater than beginning) when reversing the direction of a traversal. + -All avl tree element classes have been renamed to make element names + consistent. + AvlNode -> AvlTreeEl + AvliNode -> AvliTreeEl + AvlMapNode -> AvlMapEl + AvliMapNode -> AvliMapEl + AvlSetNode -> AvlSetEl + AvliSetNode -> AvliSetEl + TavlNode -> TavlTreeEl + TavliNode -> TavliTreeEl + TavlMapNode -> TavlMapEl + TavliMapNode -> TavliMapEl + TavlSetNode -> TavlSetEl + TavliSetNode -> TavliSetEl + AvlPmapNode -> AvlPmapEl + AvliPmapNode -> AvliPmapEl + AvlPsetNode -> AvlPsetEl + AvliPsetNode -> AvliPsetEl + FsmSDNode -> StateDictEl + AvlTree::nodeCount -> AvlTree::treeSize + -All binary search table and avl tree classes now inherit from the compare + class. This allows the compare function to be non-static and for it make use + of state that is common to the data structure. + -BstTable(int allocLen) no longer uses Vector::setAsNew and therefore Element + does not require Element() constructor. + +Aapl 2.8.0 -- Jan 5, 2003 +========================= + -Switched to the LGPL. + -Added get() to String class. Returns the data ptr. + -Added operator<<(ostream &, String&). Previously relied on the implicit + cast to char* which did not work all the time. + -Iterators renamed and rewritten. The new style for checking if the iterator + is done is to call more() or done() if moving forwards and revMore() or + revDone() if moving backwards. Iterators now have first() and last() calls + for checking if the iterator is on the first and last element. The next() + and prev() calls now return the next and previous. THEY NO LONGER MOVE THE + ITERATOR FORWARD OR BACKWARDS. Increment() and decrement() are now available + for moving the iterator. + -Various fixes for the intel C++ compiler. + -Shared vector table header renamed from TabHead to STabHead. + -Started Tiny DList, Tiny VectSimp, Tiny SVectSimp, and Tiny AvlTree. These + classes reduce final executable size by reusing core code across + instantiations with different types. They can be used identically to the + non-tiny counterpart if only iterators are used for accesing the structure. + The catch is that the underlying data structures have generic pointers that + are not directly usable without casting. + +Aapl 2.7.0 -- Dec 20, 2002 +========================== + -DoubleList::length -> DoubleList::listLen and added length() function. This + is to keep consistent with vectors and strings. + -Sorting routines now inherit from the compare class and use a non-static + compare routine. This lets the compare have state. This will likely be + extended to all structures that use a compare class. + -Constructor added to string that does sprintf style formatting. + -Routines added to string for setting from a char*, len pair. + -Table class (used by vector) member length changed to tabLen and the + the length() const routine added. This makes SVector a more easy substitute + for Vector. Previously the length member was an int in Vector and a function + call in SVector. Now it is a function in both. + -String::str -> String::data and String::getLen() -> String::length() to make + access of string data consistent with access of table data. + -Binary search tweaked to use a shift in place of a divide. + -FsmGraph code from rlfsm moved back into Aapl and Reglang FSM terminated. + Now that the graph code has been separted from the priority and action code, + the base template is leaner and more generalized. It now fits well in Aapl + as a generic programming construct. Also, rlfsm was not enjoying much + exposure and no sense in letting the fsmgraph code fight for itself. The + relevant fsmgraph changes since the split at 2.5.0 follow: + -Support of arbitrary key types for graph completed. + -Dramatic performance improvements in basic fsm operations. Kleen star, + union, and concatenation do not require a walk of all states and can go very + fast when there is no overlap between the machines. + -Fsm Range transitions implemented. Can now efficiently specify machines that + span very large ranges. + -More efficient partition based fsm minimization implemented. Algorithm is + similar to Hopcroft's minimization algorithm. + -Fsm Graph split into base code that implements FSM operations, NFA-DFA + conversion and minimization and a superclass that implements priority and + action setting. Allows the base code to be easily reused for other + applications that require different state and transition properties. The + superclass is left as user code in Ragel and is not included in Aapl. + -Can now have various classes of named entry points in an fsm graph for + maintaining entry other than the start state. This is useful for making + actions that jump into or call various named locations in the machine. + -Various bugs in fsm graph code fixed. + +Aapl 2.6.0 -- Nov 4, 2002 +========================= + -Added AvlPmap, AvlPset, AvliPmap and AvliPset classes. These are + instantiations of AvlMap and AvlSet with void* types and a small inline + wrapper interface for doing type conversions. If many different maps are + used for storing integers or pointers then these classes can cut down on + code bloat. + -Added AvlTree::remove, which will detach and delete elements from the tree. + -Removed set, unSet and isSet from AvlTree. These functions are redundant and + clutter the interface. + insert/remove/find functionality just for convenience. Prefer to remove them + than to clutter the interface with inconsistently named functions. + -Fixed the return type of BstTable::removeMulti. It should be an int (not + bool) because it returns the number of items removed. + -Added SVector and SVectSimp: implicitly shared copy-on-write vectors. + -Added ResizeCtLin: Identical to ResizeLin, except the step is specified at + compile time using a template argument. + -Removed deepCopy from the classes that by default behave as a deep copy. + Found the duplicate function to make it confusing as to how the structure's + operator= behaves. + -File rename of shrstr to astring, to make it easier to find. + -BsTable renamed to BstTable to stay consistenty named with the other Bst + classes. Also renamed the file from bstable.h to bsttable.h for consistency. + -Compare class routines are now Compare::compare (used to be + Compare::Compare). This is to keep with the lowercase function name + convention. + -Added the insertion of whole elements to BsTable and BstMap (already exists + for BstMap). + -Added the insertion of entire other tables to BsTable, BstMap and BstSet. + -The getKey function for binary search table must now be const as well as the + existing requirement of returning a const object. This was needed for the + insertion of whole elements so the requirement was made for all + insert/remove/find routines in order to stay consistent. + -Removed set, unSet and isSet from BstSet. These functions duplicated the + insert/remove/find functionality just for convenience. Prefer to remove them + than to clutter the interface with inconsistently named functions. + +Aapl 2.5.4 -- Sept 20, 2002 +=========================== + -All of Aapl is now in the Aapl:: namespace, which is disabled by default. It + can be turned on by defining #define AAPL_NAMESPACE. + -Mergesort uses only memcpy to move data around, instead of the inconsistent + use of the = operator. Classes that are sorted have no way of knowing that + they are being moved around in mem. + -Implemented QuickSort, BubbleSort and Insertion Sort. + -QuickSort uses InsertSort when the array being sorted is small. + -MergeSort uses BubbleSort when the array be sorted is small. + -Implemented an implicitly shared string class. + +Aapl 2.5.3 -- Aug 17, 2002 +========================== + -Much work done on the user documentation. + -AvlMap and AvlSet destructors now delete all elements. The idea is that + these classes manage memory on behalf of the user, so cleanup. Also, a deep + copy will cause the existing contents to be deleted. The remaining avl trees + do not assume ownership of element and thus do not delete anything. + +Aapl 2.5.2 -- Aug 14, 2002 +========================== + -Bug fixed in Vector::replace. When overwriting data without overwriting up + to the end of the vector or extending it, the vector would shrink in size. + This is because the tableLength was getting set to the end of the overwrite + region in every case. + -Bug fixed in empty and abandon of Avli* trees. They now clear the list head + and tail pointers. + -shallowCopy, deepCopy and the = operator were added to double list, vector, + and avl tree. The = operator implements the deep copy functionality. + -The Vector class was heavily rewritten. The purpose of the rewrite is to + have a vector that does not require the class that it contains to have a + default constructor. In many cases adding a default constructor violates the + design of a class. A class should not be required to have a default + constructor just because it is going into a vector. To have this feature, + the new vector differs from the old vector in a few ways. If the vector is + instructed to add elements off the end of the vector then undefined + behaviour results. The new vector will not implictly create new items. + Also, new items cannot be added to the list by giving a null data pointer. + Instead insertNew, appendNew, etc. can be used. + -DListVal destructor now deletes all elements. The reasoning is that DListVal + manages elements and so it should clean up after itself. + -remove routines added to double list classes. They are the same as detach + except they also delete elements. + -Default down resize of ResizeRunTime fixed: now is Exponential as is the up + resizing. + +Aapl 2.5.1 -- Aug 9, 2002 +========================= + -Class and function descriptions from doc/*.txt moved to source code in + doxygen format. + -Iterators slimmed down in anticipation of the more coplicated iterator for + avltree. Iterators now provide basic moving forward and backward and looking + at current value. Looking ahead and moving ahead by more than one space is no + longer supported. + -Assignment operator of all iterators now return a reference to this. + -Implemented iterator for avl tree (non Avli* trees). The iterator can be + started on either end and can move forwards or backwards. Does not support + trees larger than 32 element in height (should be sufficient). + +Aapl 2.5.0 -- Jun 22, 2002 +========================== + -Doxygen docs started. + -ExpnResize -> ResizeExpn, ConstResize -> ResizeConst, LinResize -> ResizeLin + Name changes that will keep a lexographically sorted list of classes in good + order. + -StrCmp -> CmpStr, OrdCmp -> CmpOrd, TableCmp -> CmpTable for same reason as + above. + -BstTable fixed so that Resize class is passed to underlying vector. + -Pdp Endianness support removed as it is untested. Don't have a pdp endian + machine on which to test. + -cc file extension changed to cpp for better portability. + + ******** Aapl split out into three libs: Aapl, Autil, Reglang FSM *********** + + Aapl is: A generic programming template library. Aapl is an alternative to + the STL. It currently contains Linked List, Avl Tree, Vector, Binary Search + Table, Double Ended Queue, Merge Sort. + + Autil is: A library of simple utility classes that are easy to come by. + Autil contains paramater parser, byte ordering facilities, character + classes. + + Reglang FSM is: A template library of algorithms and data structures for + compiling state machines from regular languages. + +Aapl 2.4.1 -- May 4, 2002 +========================= + -Fixed automatic dependencies, were not working correctly. + -C++ standards compliant I/O and namespaces. + +Aapl 2.4.0 -- May 3, 2002 +========================= + -Vector now uses malloc, realloc and free so the performance gain of + of using realloc over new, memcpy, delete in resizing can be realized. + -Buffer Renamed to VectSimp. The name buffer is way overused and brings + preconceptions with it. VectSimp now has the same functionality as vector. + It is a 'flavour' of vector. The main difference is that is does not use + copy constructors/destructors. It uses memcpy to put data in and as a result + can go much faster. The idea is that it is used on 'simple data'. + -The type of resizing that vector uses is no longer controlled by including + different files and using different names (ie, VectorC no longer exists). + The old style (last seen in v1.1.2) of using a template parameter has been + resurrected. It was previously removed because it caused very long symbol + names. That problem has been fixed by making the class that goes into that + template parameter not a template. It is a normal class and as such will + cause symbol names to increase a constant amount. The old way was the class + was a template taking type T so that essentially doubled the length of the + symbol. The purpose of this change is to eliminate the many files required + to have all the different resizing types. Any class that inherits from + vector needs to have many flavours in order to support the different + resizing types and that gets ugly. Providing resizing options to the bstable + classes would require 30 files. + -Default step for linear resizing is changed from 10 to 256. + -FsmGraph structure supports Default transitions. Default transitions can be + used in place of newing up a transition for every char in the alphabet in + order to get the 'dot' and 'dot star' fsms. It is incredibly more efficient + and will make large integer alphabets feasible. + -The conversion from FsmGraph to FsmMachine is now moved into a class called + FsmBuild. + -FsmMachine no longer stores transition indices as a linear chunk. Now + stores key, index pairs. This will facilitate using FsmMachine with very + large alphabets because large index tables will not be allocated. when there + is great variation in transition keys. + -FsmMachine stores only offsets, no pointers. + -Aapl assert is removed in favour of using system assert. Can't see a reason + to duplicate system include. + -Lowercased the interface to BsTable. + -Makefiles use automatic dependencies. + -AvlTree interface was lowercased/consolodated. + -Large switches at the top of *common.h files were removed. The defines were + put into the files that include the common files. + -BsTable interface was lowercased/consolodated. + -BstSet replaces VectSet. + -Configure script allows you to specify what objects to build using the + --enable-src option + -AvlTree verification is moved out of the main tree and into the test cases. + +Aapl 2.3.1 -- March 25, 2002 +============================ + -Fixed errors in the event loop with respect to unregistering. + -Added Signal handling to event loop. + -Breaking out of event loop is now much cleaner. + -Added CONTENTS file. + -Removed trivial class File. I think most would rather code this kind + of stuff on their own, or use a more featureful library. + -Removed Data base class. Again, kind of trivial. Better left to the user. + -DList::detach{Front,End} -> DList::detach{First,Last}. This change was + made in order to be more consistent with the iterator convention of + using first, last, sfirst, slast. + -Wrote documentation for double list. + +Aapl 2.3.0 -- March 19, 2002 +============================ + IMPORTANT: This release marks the beginning of a major change in the + naming conventions. Previously, naming conventions had function names + starting with an upper case. The member functions will slowly be changed + to start with a lower case. Some functions names will also be changed + to follow the conventions in doc/conventions.txt. Vector and DList have + already been changed. + + These changes are being made to make aapl more compatible with existing + template libraries and to provide a more consistent interface across + the classes. + + -Vector and double list get iterators. + -Fixed buffer. Restructuring vector broke it. + -Lower cased names in vector. + -Lower cased names in doublelist. + -Vector::Overwrite -> Vector::replace + -Vector::Delete -> Vector::remove + -DList::AddEnd -> DoubleList::append() + -DList::AddFront -> DoubleList::prepend() + -DList::DeleteElements -> DoubleList::empty() + -DList::EmptyList -> DoubleList::abandon() + -Added DListVal, which is the 'by-value-type' double list that does + not require a DListEl class to be defined. + -Began conventions doc. + -Rewrote the connection/selector code. It is now simpler and more generic. + Docs on the way. The code is now in event.{h,cc}. The select loop can + reliably translate asyncronous signals into events in the event loop. + However, code is not yet set up for processing the signal events. The event + loop handles signals using sigsetjmp/siglongjmp and avoides the classic unix + select loop race condition with respect to signals. + +Aapl 2.2.0 -- Feb 25, 2002 +========================== + -Added AvlSet. AvlSet is like AvlMap except you give only a key. It also has + the set interface: Set, UnSet and IsSet that return boolean values and don't + really care to speak about element in a tree. + -Added walkable versions of avl tree. There is a walkable version of all + existing trees. They are AvliTree, AvliMel, AvliMelKey, AvliMap, and AvliSet. + Implemented by having the element inherit from DListEl and the tree inherit + from DList. All operations on the tree remain O(log(N)). Maintaining the list + pointers adds a constant amount of work. Adds 8 bytes to each element (for a + total of 24) and 12 bytes to the tree (for a total of 20). + -Added a few more sections to docs for AvlTree + +Aapl 2.1.0 -- Feb 22, 2002 +========================== + IMPORTANT: VectorC(int) no longer has the same meaning. Previously it set + allocation length with no size. Now it sets size with no allocation so + it is effectively useless as the table cannot grow it's allocation. It + will assert fail. Use VectorC(int, int) instead. See Vector docs. + + -Further split Vector into all possible types of upresizing and down + resizing. Existing Vectors remain unchanged except for constructor of + VectorC. It is now possible to have a vector that up resizes linear and + down exponential or up exponential and no down (constant), etc. Also + added VectorR, which allows for runtime setting of how to resize. + -Implemented a step for the linear vector. Now the following is true: + If the sequence of size requests is linear, the number of resizes is + also linear. + -Wrote docs for Vector. + +Aapl 2.0.0 -- Feb 17, 2002 +========================== + IMPORTANT: The 2.x.x version of Aapl is not backwards compatible with + 1.x.x version. Code that compiles against 1.x.x may not compile against + 2.x.x. The changes mostly amount to two things: + + 1) Making data and function class member naming convetions consistent. All + variables start with a lower case whereas functions, classes, constants + and type names all start with an upper case. + + 2) Shortening the symbols in resulting object code. Aapl classes achieved + great versatility by providing many tempate parameters mostly with + default values. Now this versatility is achieved by splitting the class + up into many different classes, each with thier own 'flavour' of the data + structure. This split is accomplished using preprocessor defines so there + is no code duplication. Classes now have shorter names and only required + template paramaters. This dramatically cuts down on long symbols and + eliminates compiler warnings on platforms that restrict symbol lengths. + + -Made all data member names consistent with general coding style: constant + identifiers start with upper case, variables (members too) start with lower. + -Split up DoubleList into DList and DListMel. Goal is shorter + symbol names by making two specialzed classes instead of one general class. + -Split up AvlTree into 4 classes. Same goal as split of DoubleList. + AvlTree now supports having multiple keys in the same object as well as + multiple tree pointers. This means a single object can be in different + trees that each use different keys. + -General rearranging of code in fsmgraph and changing templates to result + in shorter symbols. Try to keep symbol lengths < 255. + -Split up Vector into 3 classes, one for each table type. + -Split up VectorSet into 3 classes, one for each table type. Also renamed + it to VectSet for conciseness. + -Split up Binary Search table into 6 classes, one for each table type times + regular bst table and bstmap. The bstmap has the template semantics of + the old bstable. BsTable lets you give the whole object you want to put in + the table in the same manner as vector and avltree. + -Both AvlTree and BsTable use GetKey() functions in the element/element types. + Previously they required that the key member be named 'key.' It can now + be named anything, and it must be returned by GetKey. + -Added copy constructor for AvlTree. + -Added Avl test progs for the various flavours. + -Started some real docs. Currently only AvlTree is documented. + +Aapl 1.2.2 -- Feb 2, 2002 +========================= + -Started ChangeLog file. + -Parser Gen is now gone from aapl. It is not generic reusable code. It now + lives in Keller which will be released soon. + +Aapl 1.2.1 -- Jan 29, 2002 +========================== + -Can choose between assertions that dump details of the assertion + failure to the screen then segfault or just segfault using a define. + -Bugfix in avltree insertion. Only showed up when re-using avl element. + The insert routine was not nulling out appropriate pointers when adding + a leaf element. Worked correctly when always newing up element (how avl tree + is mostly used) as nulling out pointers is done by the element constructor. + -File Buffer size up to 1K. Was at 10 bytes for debugging/testing purposes. + -Transition count variables gone from fsm graph. Computing this is silly as + the vectors that implement the out transitions + -Transition priorities routines in FSM Graph no longer operate as a 'delta' + but instead as absolute. + -Function keys are no long a duple, instead just one integer. Using two + integers is overkill and not likely to be used. To acieve the same effect as + a duple, break the bits of the single integer into multiple fields. + -Allow the shifting of the priorities of start transitions to above some + value. Useful before staring machines so that out transitions of a final + state run before the transitions of the start state when going from a final + state back into the machine. + -Allow the clearing of transition functions. + -Cleaned up FsmAttachStates: no longer reusing transitions when replacing, + old trans are deleted and a new one is made. This eliminates the need for + the ResetTransition routine. + -When building machines, use max int and min int as the defaults for highIndex + and lowIndex. + -When building machines, transition 0 is always the error transition, + no longer insert it into the transition list on demand. + -When building machines, compute the global high and low index. Used by ragel. + -Out priorities have a concept of being set vs not being set. + -Concatenation and Star can now behave as if they do not cause leaving the fsm. + Allows the user to turn off picking up of out transitions and out priorities. + +Aapl 1.2 -- Aug 28, 2001 +======================== + -Initial release. diff --git a/src/aapl/Makefile.am b/src/aapl/Makefile.am new file mode 100644 index 00000000..b3cd0c94 --- /dev/null +++ b/src/aapl/Makefile.am @@ -0,0 +1,7 @@ +aaplinclude_HEADERS = \ + avlbasic.h avlimel.h avlmap.h bstcommon.h compare.h insertsort.h \ + sbstset.h avlcommon.h avlimelkey.h avlmel.h bstmap.h dlcommon.h \ + mergesort.h sbsttable.h avlibasic.h avliset.h avlmelkey.h bstset.h \ + dlist.h quicksort.h svector.h avlikeyless.h avlitree.h avlset.h \ + bsttable.h dlistmel.h resize.h table.h avlimap.h avlkeyless.h avltree.h \ + bubblesort.h dlistval.h sbstmap.h vector.h astring.h buffer.h rope.h diff --git a/src/aapl/README b/src/aapl/README new file mode 100644 index 00000000..a2fa5e65 --- /dev/null +++ b/src/aapl/README @@ -0,0 +1,6 @@ +This directory contains the Aapl source distribution. For the +documentation, build scripts, test programs, ChangeLog, etc. get the +aapldev package. + +AaplDev and other information about Aapl is available from +http://www.elude.ca/aapl/ diff --git a/src/aapl/astring.h b/src/aapl/astring.h new file mode 100644 index 00000000..29d876f4 --- /dev/null +++ b/src/aapl/astring.h @@ -0,0 +1,862 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_ASTRING_H +#define _AAPL_ASTRING_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +#ifdef AAPL_DOCUMENTATION + +/** + * \defgroup astring String + * \brief Implicitly shared copy-on-write string. + * + * @{ + */ + +/** + * \class String + * \brief Implicitly shared copy-on-write string. + */ + +/*@}*/ + +class String +{ +public: + /** + * \brief Create a null string. Data points to NULL. + */ + String(); + + /** + * \brief Construct a string from a c-style string. + * + * A new buffer is allocated for the c string. Initially, this string will + * be the only String class referencing the data. + */ + String( const char *s ); + + /** + * \brief Construct a string from a c-style string of specific length. + * + * A new buffer is allocated for the c string. Initially, this string will + * be the only String class referencing the data. + */ + String( const char *s, long len ); + + /** + * \brief Construct a string from another String. + * + * A refernce to the buffer allocated for s is taken. A new buffer is + * not allocated. + */ + String( const String &s ); + + /** + * \brief Construct a string using snprintf. + * + * Requires a maximum length for the resulting string. If the formatting + * (not including trailing null) requires more space than maxLen, the + * result will be truncated to maxLen long. Only the length actually + * written will be used by the new string. This string will be the only + * String class referencing the data. + */ + String( long maxLen, const char *format, ... ) + + /** + * \brief Clean up the string. + * + * If the string is not null, the referenced data is detached. If no other + * string refernces the detached data, it is deleted. + */ + ~String(); + + /** + * \brief Set the string from a c-style string. + * + * If this string is not null, the current buffer is dereferenced and + * possibly deleted. A new buffer is allocated (or possibly the old buffer + * reused) for the string. Initially, this string will be the only String + * class referencing the data. + * + * If s is null, then this string becomes a null ptr. + * + * \returns A reference to this. + */ + String &operator=( const char *s ); + + /** + * \brief Set the string from a c-style of specific length. + * + * If this string is not null, the current buffer is dereferenced and + * possibly deleted. A new buffer is allocated (or possibly the old buffer + * reused) for the string. Initially, this string will be the only String + * class referencing the data. + * + * If s is null, then this string becomes a null ptr. + * + * \returns A reference to this. + */ + void setAs( const char *s, long len ); + + /** + * \brief Set the string from a single char. + * + * The current buffer is dereferenced and possibly deleted. A new buffer + * is allocated (or possibly the old buffer reused) for the string. + * Initially, this string will be the only String class referencing the + * data. + * + * If s is null, then this string becomes a null ptr. + * + * \returns A reference to this. + */ + String &operator=( const char c ); + + + /** + * \brief Set the string from another String. + * + * If this string is not null, the current buffer is dereferenced and + * possibly deleted. A reference to the buffer allocated for s is taken. + * A new buffer is not allocated. + * + * If s is null, then this string becomes a null ptr. + * + * \returns a reference to this. + */ + String &operator=( const String &s ); + + /** + * \brief Append a c string to the end of this string. + * + * If this string shares its allocation with another, a copy is first + * taken. The buffer for this string is grown and s is appended to the + * end. + * + * If s is null nothing happens. + * + * \returns a reference to this. + */ + String &operator+=( const char *s ); + + /** + * \brief Append a c string of specific length to the end of this string. + * + * If this string shares its allocation with another, a copy is first + * taken. The buffer for this string is grown and s is appended to the + * end. + * + * If s is null nothing happens. + * + * \returns a reference to this. + */ + void append( const char *s, long len ); + + /** + * \brief Append a single char to the end of this string. + * + * If this string shares its allocation with another, a copy is first + * taken. The buffer for this string is grown and s is appended to the + * end. + * + * \returns a reference to this. + */ + String &operator+=( const char c ); + + /** + * \brief Append a String to the end of this string. + * + * If this string shares its allocation with another, a copy is first + * taken. The buffer for this string is grown and the data of s is + * appeneded to the end. + * + * If s is null nothing happens. + * + * returns a reference to this. + */ + String &operator+=( const String &s ); + + /** + * \brief Cast to a char star. + * + * \returns the string data. A null string returns 0. + */ + operator char*() const; + + /** + * \brief Get a pointer to the data. + * + * \returns the string Data + */ + char *get() const; + + /** + * \brief Get the length of the string + * + * If the string is null, then undefined behaviour results. + * + * \returns the length of the string. + */ + long length() const; + + /** + * \brief Pointer to the data. + * + * Publically accessible pointer to the data. Immediately in front of the + * string data block is the string header which stores the refcount and + * length. Consequently, care should be taken if modifying this pointer. + */ + char *data; +}; + +/** + * \relates String + * \brief Concatenate a c-style string and a String. + * + * \returns The concatenation of the two strings in a String. + */ +String operator+( const String &s1, const char *s2 ); + +/** + * \relates String + * \brief Concatenate a String and a c-style string. + * + * \returns The concatenation of the two strings in a String. + */ +String operator+( const char *s1, const String &s2 ); + +/** + * \relates String + * \brief Concatenate two String classes. + * + * \returns The concatenation of the two strings in a String. + */ +String operator+( const String &s1, const String &s2 ); + +#endif + +template class StrTmpl +{ +public: + class Fresh {}; + + /* Header located just before string data. Keeps the length and a refcount on + * the data. */ + struct Head + { + long refCount; + long length; + }; + + /** + * \brief Create a null string. + */ + StrTmpl() : data(0) { } + + /* Clean up the string. */ + ~StrTmpl(); + + /* Construct a string from a c-style string. */ + StrTmpl( const char *s ); + + /* Construct a string from a c-style string of specific len. */ + StrTmpl( const char *s, long len ); + + /* Allocate len spaces. */ + StrTmpl( const Fresh &, long len ); + + /* Construct a string from another StrTmpl. */ + StrTmpl( const StrTmpl &s ); + + /* Construct a string from with, sprintf. */ + StrTmpl( long lenGuess, const char *format, ... ); + + /* Set the string from a c-style string. */ + StrTmpl &operator=( const char *s ); + + /* Set the string from a c-style string of specific len. */ + void setAs( const char *s, long len ); + + /* Allocate len spaces. */ + void setAs( const Fresh &, long len ); + + void chop( long len ); + + /* Construct a string from with, sprintf. */ + void setAs( long lenGuess, const char *format, ... ); + + /* Set the string from a single char. */ + StrTmpl &operator=( const char c ); + + /* Set the string from another StrTmpl. */ + StrTmpl &operator=( const StrTmpl &s ); + + /* Append a c string to the end of this string. */ + StrTmpl &operator+=( const char *s ); + + /* Append a c string to the end of this string of specifi len. */ + void append( const char *s, long len ); + + /* Append a single char to the end of this string. */ + StrTmpl &operator+=( const char c ); + + /* Append an StrTmpl to the end of this string. */ + StrTmpl &operator+=( const StrTmpl &s ); + + /* Cast to a char star. */ + operator char*() const { return data; } + + /* Get a pointer to the data. */ + char *get() const { return data; } + + /* Return the length of the string. Must check for null data pointer. */ + long length() const { return data ? (((Head*)data)-1)->length : 0; } + + void empty() { setAs( (const char *)0, 0 ); } + + /** + * \brief Pointer to the data. + */ + char *data; + +protected: + /* Make space for a string of length len to be appended. */ + char *appendSpace( long len ); + void initSpace( long length ); + void setSpace( long length ); + + template friend StrTmpl operator+( + const StrTmpl &s1, const char *s2 ); + template friend StrTmpl operator+( + const char *s1, const StrTmpl &s2 ); + template friend StrTmpl operator+( + const StrTmpl &s1, const StrTmpl &s2 ); + +private: + /* A dummy struct solely to make a constructor that will never be + * ambiguous with the public constructors. */ + struct DisAmbig { }; + StrTmpl( char *data, const DisAmbig & ) : data(data) { } +}; + +/* Free all mem used by the string. */ +template StrTmpl::~StrTmpl() +{ + if ( data != 0 ) { + /* If we are the only ones referencing the string, then delete it. */ + Head *head = ((Head*) data) - 1; + head->refCount -= 1; + if ( head->refCount == 0 ) + free( head ); + } +} + +/* Create from a c-style string. */ +template StrTmpl::StrTmpl( const char *s ) +{ + if ( s == 0 ) + data = 0; + else { + /* Find the length and allocate the space for the shared string. */ + long length = strlen( s ); + + /* Init space for the data. */ + initSpace( length ); + + /* Copy in the data. */ + memcpy( data, s, length+1 ); + } +} + +/* Create from a c-style string. */ +template StrTmpl::StrTmpl( const char *s, long length ) +{ + if ( s == 0 ) + data = 0; + else { + /* Init space for the data. */ + initSpace( length ); + + /* Copy in the data. */ + memcpy( data, s, length ); + data[length] = 0; + } +} + +/* Create from a c-style string. */ +template StrTmpl::StrTmpl( const Fresh &, long length ) +{ + /* Init space for the data. */ + initSpace( length ); + data[length] = 0; +} + +/* Create from another string class. */ +template StrTmpl::StrTmpl( const StrTmpl &s ) +{ + if ( s.data == 0 ) + data = 0; + else { + /* Take a reference to the string. */ + Head *strHead = ((Head*)s.data) - 1; + strHead->refCount += 1; + data = (char*) (strHead+1); + } +} + +/* Construct a string from with, sprintf. */ +template StrTmpl::StrTmpl( long lenGuess, const char *format, ... ) +{ + /* Set the string for len. */ + initSpace( lenGuess ); + + va_list args; + + /* Write to the temporary buffer. */ + va_start( args, format ); + + long written = vsnprintf( data, lenGuess+1, format, args ); + if ( written > lenGuess ) { + setSpace( written ); + written = vsnprintf( data, written+1, format, args ); + } + chop( written ); + + va_end( args ); +} + +/* Construct a string from with, sprintf. */ +template void StrTmpl::setAs( long lenGuess, const char *format, ... ) +{ + /* Set the string for len. */ + setSpace( lenGuess ); + + va_list args; + + /* Write to the temporary buffer. */ + va_start( args, format ); + + long written = vsnprintf( data, lenGuess+1, format, args ); + if ( written > lenGuess ) { + setSpace( written ); + written = vsnprintf( data, written+1, format, args ); + } + chop( written ); + + va_end( args ); +} + +template void StrTmpl::initSpace( long length ) +{ + /* Find the length and allocate the space for the shared string. */ + Head *head = (Head*) malloc( sizeof(Head) + length+1 ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Init the header. */ + head->refCount = 1; + head->length = length; + + /* Save the pointer to the data. */ + data = (char*) (head+1); +} + + +/* Set this string to be the c string exactly. The old string is discarded. + * Returns a reference to this. */ +template StrTmpl &StrTmpl::operator=( const char *s ) +{ + if ( s == 0 ) { + /* Just free the data, we are being set to null. */ + if ( data != 0 ) { + Head *head = ((Head*)data) - 1; + head->refCount -= 1; + if ( head->refCount == 0 ) + free(head); + data = 0; + } + } + else { + /* Find the length of the string we are setting. */ + long length = strlen( s ); + + /* Set the string for len. */ + setSpace( length ); + + /* Copy in the data. */ + memcpy( data, s, length+1 ); + } + return *this; +} + +/* Set this string to be the c string exactly. The old string is discarded. + * Returns a reference to this. */ +template void StrTmpl::setAs( const char *s, long length ) +{ + if ( s == 0 ) { + /* Just free the data, we are being set to null. */ + if ( data != 0 ) { + Head *head = ((Head*)data) - 1; + head->refCount -= 1; + if ( head->refCount == 0 ) + free(head); + data = 0; + } + } + else { + /* Set the string for len. */ + setSpace( length ); + + /* Copy in the data. */ + memcpy( data, s, length ); + data[length] = 0; + } +} + +template void StrTmpl::chop( long length ) +{ + /* Detach from the existing string. */ + Head *head = ((Head*)data) - 1; + assert( head->refCount == 1 ); + assert( length <= head->length ); + head->length = length; + data[length] = 0; +} + +/* Set this string to be the c string exactly. The old string is discarded. + * Returns a reference to this. */ +template void StrTmpl::setAs( const Fresh &, long length ) +{ + setSpace( length ); + data[length] = 0; +} + +/* Set this string to be the single char exactly. The old string is discarded. + * Returns a reference to this. */ +template StrTmpl &StrTmpl::operator=( const char c ) +{ + /* Set to length 1. */ + setSpace( 1 ); + + /* Copy in the data. */ + data[0] = c; + data[1] = 0; + + /* Return ourselves. */ + return *this; +} + +/* Set this string to be the StrTmpl s exactly. The old string is + * discarded. */ +template StrTmpl &StrTmpl::operator=( const StrTmpl &s ) +{ + /* Detach from the existing string. */ + if ( data != 0 ) { + Head *head = ((Head*)data) - 1; + head->refCount -= 1; + if ( head->refCount == 0 ) + free( head ); + } + + if ( s.data != 0 ) { + /* Take a reference to the string. */ + Head *strHead = ((Head*)s.data) - 1; + strHead->refCount += 1; + data = (char*)(strHead+1); + } + else { + /* Setting from a null string, just null our pointer. */ + data = 0; + } + return *this; +} + +/* Prepare the string to be set to something else of the given length. */ +template void StrTmpl::setSpace( long length ) +{ + /* Detach from the existing string. */ + Head *head = ((Head*)data) - 1; + if ( data != 0 && --head->refCount == 0 ) { + /* Resuse the space. */ + head = (Head*) realloc( head, sizeof(Head) + length+1 ); + } + else { + /* Need to make new space, there is no usable old space. */ + head = (Head*) malloc( sizeof(Head) + length+1 ); + } + if ( head == 0 ) + throw std::bad_alloc(); + + /* Init the header. */ + head->refCount = 1; + head->length = length; + + /* Copy in the data and save the pointer to it. */ + data = (char*) (head+1); +} + +/* Append a c-style string to the end of this string. Returns a reference to + * this */ +template StrTmpl &StrTmpl::operator+=( const char *s ) +{ + /* Find the length of the string appended. */ + if ( s != 0 ) { + /* Get the string length and make space on the end. */ + long addedLen = strlen( s ); + char *dest = appendSpace( addedLen ); + + /* Copy the data in. Plus one for the null. */ + memcpy( dest, s, addedLen+1 ); + } + return *this; +} + +/* Append a c-style string of specific length to the end of this string. + * Returns a reference to this */ +template void StrTmpl::append( const char *s, long length ) +{ + /* Find the length of the string appended. */ + if ( s != 0 ) { + /* Make space on the end. */ + char *dest = appendSpace( length ); + + /* Copy the data in. Plus one for the null. */ + memcpy( dest, s, length ); + dest[length] = 0; + } +} + +/* Append a single char to the end of this string. Returns a reference to + * this */ +template StrTmpl &StrTmpl::operator+=( const char c ) +{ + /* Grow on the end. */ + char *dst = appendSpace( 1 ); + + /* Append a single charachter. */ + dst[0] = c; + dst[1] = 0; + return *this; +} + + +/* Append an StrTmpl string to the end of this string. Returns a reference + * to this */ +template StrTmpl &StrTmpl::operator+=( const StrTmpl &s ) +{ + /* Find the length of the string appended. */ + if ( s.data != 0 ) { + /* Find the length to append. */ + long addedLen = (((Head*)s.data) - 1)->length; + + /* Make space on the end to put the string. */ + char *dest = appendSpace( addedLen ); + + /* Append the data, add one for the null. */ + memcpy( dest, s.data, addedLen+1 ); + } + return *this; +} + +/* Make space for a string of length len to be appended. */ +template char *StrTmpl::appendSpace( long len ) +{ + if ( data == 0 ) { + initSpace( len ); + return data; + } + else { + /* Find the length of this and the string appended. */ + Head *head = (((Head*)data) - 1); + long thisLen = head->length; + + if ( head->refCount == 1 ) { + /* No other string is using the space, grow this space. */ + head = (Head*) realloc( head, + sizeof(Head) + thisLen + len + 1 ); + if ( head == 0 ) + throw std::bad_alloc(); + data = (char*) (head+1); + + /* Adjust the length. */ + head->length += len; + } + else { + /* Another string is using this space, make new space. */ + head->refCount -= 1; + Head *newHead = (Head*) malloc( + sizeof(Head) + thisLen + len + 1 ); + if ( newHead == 0 ) + throw std::bad_alloc(); + data = (char*) (newHead+1); + + /* Set the new header and data from this. */ + newHead->refCount = 1; + newHead->length = thisLen + len; + memcpy( data, head+1, thisLen ); + } + + /* Return writing position. */ + return data + thisLen; + } +} + +/* Concatenate a String and a c-style string. */ +template StrTmpl operator+( const StrTmpl &s1, const char *s2 ) +{ + /* Find s2 length and alloc the space for the result. */ + long str1Len = (((typename StrTmpl::Head*)(s1.data)) - 1)->length; + long str2Len = strlen( s2 ); + + typename StrTmpl::Head *head = (typename StrTmpl::Head*) + malloc( sizeof(typename StrTmpl::Head) + str1Len + str2Len + 1 ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Set up the header. */ + head->refCount = 1; + head->length = str1Len + str2Len; + + /* Save the pointer to data and copy the data in. */ + char *data = (char*) (head+1); + memcpy( data, s1.data, str1Len ); + memcpy( data + str1Len, s2, str2Len + 1 ); + return StrTmpl( data, typename StrTmpl::DisAmbig() ); +} + +/* Concatenate a c-style string and a String. */ +template StrTmpl operator+( const char *s1, const StrTmpl &s2 ) +{ + /* Find s2 length and alloc the space for the result. */ + long str1Len = strlen( s1 ); + long str2Len = (((typename StrTmpl::Head*)(s2.data)) - 1)->length; + + typename StrTmpl::Head *head = (typename StrTmpl::Head*) + malloc( sizeof(typename StrTmpl::Head) + str1Len + str2Len + 1 ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Set up the header. */ + head->refCount = 1; + head->length = str1Len + str2Len; + + /* Save the pointer to data and copy the data in. */ + char *data = (char*) (head+1); + memcpy( data, s1, str1Len ); + memcpy( data + str1Len, s2.data, str2Len + 1 ); + return StrTmpl( data, typename StrTmpl::DisAmbig() ); +} + +/* Add two StrTmpl strings. */ +template StrTmpl operator+( const StrTmpl &s1, const StrTmpl &s2 ) +{ + /* Find s2 length and alloc the space for the result. */ + long str1Len = (((typename StrTmpl::Head*)(s1.data)) - 1)->length; + long str2Len = (((typename StrTmpl::Head*)(s2.data)) - 1)->length; + typename StrTmpl::Head *head = (typename StrTmpl::Head*) + malloc( sizeof(typename StrTmpl::Head) + str1Len + str2Len + 1 ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Set up the header. */ + head->refCount = 1; + head->length = str1Len + str2Len; + + /* Save the pointer to data and copy the data in. */ + char *data = (char*) (head+1); + memcpy( data, s1.data, str1Len ); + memcpy( data + str1Len, s2.data, str2Len + 1 ); + return StrTmpl( data, typename StrTmpl::DisAmbig() ); +} + +/* Operator used in case the compiler does not support the conversion. */ +template inline std::ostream &operator<<( std::ostream &o, const StrTmpl &s ) +{ + return o.write( s.data, s.length() ); +} + +typedef StrTmpl String; + +/* + * StringStream for appending to streams with an ostream. + */ +struct StringOutBuf +: + public std::streambuf +{ + StringOutBuf( String &s ) + : + s(s) + { + } + + int_type overflow( int_type c ) + { + if ( c != EOF ) { + char z = c; + s.append( &z, 1 ); + } + return c; + } + + std::streamsize xsputn( const char *data, std::streamsize num ) + { + s.append( data, num ); + return num; + } + + String &s; +}; + +struct StringStream +: + public std::ostream +{ + StringStream( String &s ) + : + std::ostream( 0 ), + buf( s ) + { + rdbuf( &buf ); + } + + StringOutBuf buf; +}; + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_ASTRING_H */ diff --git a/src/aapl/avlbasic.h b/src/aapl/avlbasic.h new file mode 100644 index 00000000..4e31dd97 --- /dev/null +++ b/src/aapl/avlbasic.h @@ -0,0 +1,66 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLBASIC_H +#define _AAPL_AVLBASIC_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlBasic + * \brief AVL Tree in which the entire element structure is the key. + * + * AvlBasic is an AVL tree that does not distinguish between the element that + * it contains and the key. The entire element structure is the key that is + * used to compare the relative ordering of elements. This is similar to the + * BstSet structure. + * + * AvlBasic does not assume ownership of elements in the tree. Items must be + * explicitly de-allocated. + */ + +/*@}*/ + +#define BASE_EL(name) name +#define BASEKEY(name) name +#define AVLMEL_CLASSDEF class Element, class Compare +#define AVLMEL_TEMPDEF class Element, class Compare +#define AVLMEL_TEMPUSE Element, Compare +#define AvlTree AvlBasic +#define AVL_BASIC + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef AVL_BASIC + +#endif /* _AAPL_AVLBASIC_H */ diff --git a/src/aapl/avlcommon.h b/src/aapl/avlcommon.h new file mode 100644 index 00000000..a1f86692 --- /dev/null +++ b/src/aapl/avlcommon.h @@ -0,0 +1,1637 @@ +/* + * Copyright 2001 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This header is not wrapped in ifndef becuase it is not intended to + * be included by the user. */ + +#include + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +#ifdef WALKABLE +/* This is used by AvlTree, AvlMel and AvlMelKey so it + * must be protected by global ifdefs. */ +#ifndef __AAPL_AVLI_EL__ +#define __AAPL_AVLI_EL__ + +/** + * \brief Tree element properties for linked AVL trees. + * + * AvliTreeEl needs to be inherited by classes that intend to be element in an + * AvliTree. + */ +template struct AvliTreeEl +{ + /** + * \brief Tree pointers connecting element in a tree. + */ + SubClassEl *left, *right, *parent; + + /** + * \brief Linked list pointers. + */ + SubClassEl *prev, *next; + + /** + * \brief Height of the tree rooted at this element. + * + * Height is required by the AVL balancing algorithm. + */ + long height; +}; +#endif /* __AAPL_AVLI_EL__ */ + +#else /* not WALKABLE */ + +/* This is used by All the non walkable trees so it must be + * protected by a global ifdef. */ +#ifndef __AAPL_AVL_EL__ +#define __AAPL_AVL_EL__ +/** + * \brief Tree element properties for linked AVL trees. + * + * AvlTreeEl needs to be inherited by classes that intend to be element in an + * AvlTree. + */ +template struct AvlTreeEl +{ + /** + * \brief Tree pointers connecting element in a tree. + */ + SubClassEl *left, *right, *parent; + + /** + * \brief Height of the tree rooted at this element. + * + * Height is required by the AVL balancing algorithm. + */ + long height; +}; +#endif /* __AAPL_AVL_EL__ */ +#endif /* def WALKABLE */ + + +#if defined( AVLTREE_MAP ) + +#ifdef WALKABLE + +/** + * \brief Tree element for AvliMap + * + * Stores the key and value pair. + */ +template struct AvliMapEl : + public AvliTreeEl< AvliMapEl > +{ + AvliMapEl(const Key &key) + : key(key) { } + AvliMapEl(const Key &key, const Value &value) + : key(key), value(value) { } + + const Key &getKey() const { return key; } + + /** \brief The key. */ + Key key; + + /** \brief The value. */ + Value value; +}; +#else /* not WALKABLE */ + +/** + * \brief Tree element for AvlMap + * + * Stores the key and value pair. + */ +template struct AvlMapEl : + public AvlTreeEl< AvlMapEl > +{ + AvlMapEl(const Key &key) + : key(key) { } + AvlMapEl(const Key &key, const Value &value) + : key(key), value(value) { } + + const Key &getKey() const { return key; } + + /** \brief The key. */ + Key key; + + /** \brief The value. */ + Value value; +}; +#endif /* def WALKABLE */ + +#elif defined( AVLTREE_SET ) + +#ifdef WALKABLE +/** + * \brief Tree element for AvliSet + * + * Stores the key. + */ +template struct AvliSetEl : + public AvliTreeEl< AvliSetEl > +{ + AvliSetEl(const Key &key) : key(key) { } + + const Key &getKey() const { return key; } + + /** \brief The key. */ + Key key; +}; +#else /* not WALKABLE */ +/** + * \brief Tree element for AvlSet + * + * Stores the key. + */ +template struct AvlSetEl : + public AvlTreeEl< AvlSetEl > +{ + AvlSetEl(const Key &key) : key(key) { } + + const Key &getKey() const { return key; } + + /** \brief The key. */ + Key key; +}; +#endif /* def WALKABLE */ + +#endif /* AVLTREE_SET */ + +/* Common AvlTree Class */ +template < AVLMEL_CLASSDEF > class AvlTree +#if !defined( AVL_KEYLESS ) && defined ( WALKABLE ) + : public Compare, public BASELIST +#elif !defined( AVL_KEYLESS ) + : public Compare +#elif defined( WALKABLE ) + : public BASELIST +#endif +{ +public: + typedef Element El; + + /** + * \brief Create an empty tree. + */ +#ifdef WALKABLE + AvlTree() : root(0), treeSize(0) { } +#else + AvlTree() : root(0), head(0), tail(0), treeSize(0) { } +#endif + + /** + * \brief Perform a deep copy of the tree. + * + * Each element is duplicated for the new tree. Copy constructors are used + * to create the new elements. + */ + AvlTree(const AvlTree &other); + +#if defined( AVLTREE_MAP ) || defined( AVLTREE_SET ) + /** + * \brief Clear the contents of the tree. + * + * All element are deleted. + */ + ~AvlTree() { empty(); } + + /** + * \brief Perform a deep copy of the tree. + * + * Each element is duplicated for the new tree. Copy constructors are used + * to create the new element. If this tree contains items, they are first + * deleted. + * + * \returns A reference to this. + */ + AvlTree &operator=( const AvlTree &tree ); + + /** + * \brief Transfer the elements of another tree into this. + * + * First deletes all elements in this tree. + */ + void transfer( AvlTree &tree ); +#else + /** + * \brief Abandon all elements in the tree. + * + * Tree elements are not deleted. + */ + ~AvlTree() {} + + /** + * \brief Perform a deep copy of the tree. + * + * Each element is duplicated for the new tree. Copy constructors are used + * to create the new element. If this tree contains items, they are + * abandoned. + * + * \returns A reference to this. + */ + AvlTree &operator=( const AvlTree &tree ); + + /** + * \brief Transfer the elements of another tree into this. + * + * All elements in this tree are abandoned first. + */ + void transfer( AvlTree &tree ); +#endif + +#ifndef AVL_KEYLESS + /* Insert a element into the tree. */ + Element *insert( Element *element, Element **lastFound = 0 ); + +#ifdef AVL_BASIC + /* Find a element in the tree. Returns the element if + * element exists, false otherwise. */ + Element *find( const Element *element ) const; + +#else + Element *insert( const Key &key, Element **lastFound = 0 ); + +#ifdef AVLTREE_MAP + Element *insert( const Key &key, const Value &val, + Element **lastFound = 0 ); +#endif + + /* Find a element in the tree. Returns the element if + * key exists, false otherwise. */ + Element *find( const Key &key ) const; + + /* Detach a element from the tree. */ + Element *detach( const Key &key ); + + /* Detach and delete a element from the tree. */ + bool remove( const Key &key ); +#endif /* AVL_BASIC */ +#endif /* AVL_KEYLESS */ + + /* Detach a element from the tree. */ + Element *detach( Element *element ); + + /* Detach and delete a element from the tree. */ + void remove( Element *element ); + + /* Free all memory used by tree. */ + void empty(); + + /* Abandon all element in the tree. Does not delete element. */ + void abandon(); + + /** Root element of the tree. */ + Element *root; + +#ifndef WALKABLE + Element *head, *tail; +#endif + + /** The number of element in the tree. */ + long treeSize; + + /** \brief Return the number of elements in the tree. */ + long length() const { return treeSize; } + + /** \brief Return the number of elements in the tree. */ + long size() const { return treeSize; } + + /* Various classes for setting the iterator */ + struct Iter; + struct IterFirst { IterFirst( const AvlTree &t ) : t(t) { } const AvlTree &t; }; + struct IterLast { IterLast( const AvlTree &t ) : t(t) { } const AvlTree &t; }; + struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; + struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; + +#ifdef WALKABLE + /** + * \brief Avl Tree Iterator. + * \ingroup iterators + */ + struct Iter + { + /* Default construct. */ + Iter() : ptr(0) { } + + /* Construct from an avl tree and iterator-setting classes. */ + Iter( const AvlTree &t ) : ptr(t.head) { } + Iter( const IterFirst &af ) : ptr(af.t.head) { } + Iter( const IterLast &al ) : ptr(al.t.tail) { } + Iter( const IterNext &an ) : ptr(findNext(an.i.ptr)) { } + Iter( const IterPrev &ap ) : ptr(findPrev(ap.i.ptr)) { } + + /* Assign from a tree and iterator-setting classes. */ + Iter &operator=( const AvlTree &tree ) { ptr = tree.head; return *this; } + Iter &operator=( const IterFirst &af ) { ptr = af.t.head; return *this; } + Iter &operator=( const IterLast &al ) { ptr = al.t.tail; return *this; } + Iter &operator=( const IterNext &an ) { ptr = findNext(an.i.ptr); return *this; } + Iter &operator=( const IterPrev &ap ) { ptr = findPrev(ap.i.ptr); return *this; } + + /** \brief Less than end? */ + bool lte() const { return ptr != 0; } + + /** \brief At end? */ + bool end() const { return ptr == 0; } + + /** \brief Greater than beginning? */ + bool gtb() const { return ptr != 0; } + + /** \brief At beginning? */ + bool beg() const { return ptr == 0; } + + /** \brief At first element? */ + bool first() const { return ptr && ptr->BASE_EL(prev) == 0; } + + /** \brief At last element? */ + bool last() const { return ptr && ptr->BASE_EL(next) == 0; } + + /** \brief Implicit cast to Element*. */ + operator Element*() const { return ptr; } + + /** \brief Dereference operator returns Element&. */ + Element &operator *() const { return *ptr; } + + /** \brief Arrow operator returns Element*. */ + Element *operator->() const { return ptr; } + + /** \brief Move to next item. */ + inline Element *operator++(); + + /** \brief Move to next item. */ + inline Element *operator++(int); + + /** \brief Move to next item. */ + inline Element *increment(); + + /** \brief Move to previous item. */ + inline Element *operator--(); + + /** \brief Move to previous item. */ + inline Element *operator--(int); + + /** \brief Move to previous item. */ + inline Element *decrement(); + + /** \brief Return the next item. Does not modify this. */ + IterNext next() const { return IterNext( *this ); } + + /** \brief Return the previous item. Does not modify this. */ + IterPrev prev() const { return IterPrev( *this ); } + + private: + static Element *findPrev( Element *element ) { return element->BASE_EL(prev); } + static Element *findNext( Element *element ) { return element->BASE_EL(next); } + + public: + + /** \brief The iterator is simply a pointer. */ + Element *ptr; + }; + +#else + + /** + * \brief Avl Tree Iterator. + * \ingroup iterators + */ + struct Iter + { + /* Default construct. */ + Iter() : ptr(0), tree(0) { } + + /* Construct from a tree and iterator-setting classes. */ + Iter( const AvlTree &t ) : ptr(t.head), tree(&t) { } + Iter( const IterFirst &af ) : ptr(af.t.head), tree(&af.t) { } + Iter( const IterLast &al ) : ptr(al.t.tail), tree(&al.t) { } + Iter( const IterNext &an ) : ptr(findNext(an.i.ptr)), tree(an.i.tree) { } + Iter( const IterPrev &ap ) : ptr(findPrev(ap.i.ptr)), tree(ap.i.tree) { } + + /* Assign from a tree and iterator-setting classes. */ + Iter &operator=( const AvlTree &t ) + { ptr = t.head; tree = &t; return *this; } + Iter &operator=( const IterFirst &af ) + { ptr = af.t.head; tree = &af.t; return *this; } + Iter &operator=( const IterLast &al ) + { ptr = al.t.tail; tree = &al.t; return *this; } + Iter &operator=( const IterNext &an ) + { ptr = findNext(an.i.ptr); tree = an.i.tree; return *this; } + Iter &operator=( const IterPrev &ap ) + { ptr = findPrev(ap.i.ptr); tree = ap.i.tree; return *this; } + + /** \brief Less than end? */ + bool lte() const { return ptr != 0; } + + /** \brief At end? */ + bool end() const { return ptr == 0; } + + /** \brief Greater than beginning? */ + bool gtb() const { return ptr != 0; } + + /** \brief At beginning? */ + bool beg() const { return ptr == 0; } + + /** \brief At first element? */ + bool first() const { return ptr && ptr == tree->head; } + + /** \brief At last element? */ + bool last() const { return ptr && ptr == tree->tail; } + + /** \brief Implicit cast to Element*. */ + operator Element*() const { return ptr; } + + /** \brief Dereference operator returns Element&. */ + Element &operator *() const { return *ptr; } + + /** \brief Arrow operator returns Element*. */ + Element *operator->() const { return ptr; } + + /** \brief Move to next item. */ + inline Element *operator++(); + + /** \brief Move to next item. */ + inline Element *operator++(int); + + /** \brief Move to next item. */ + inline Element *increment(); + + /** \brief Move to previous item. */ + inline Element *operator--(); + + /** \brief Move to previous item. */ + inline Element *operator--(int); + + /** \brief Move to previous item. */ + inline Element *decrement(); + + /** \brief Return the next item. Does not modify this. */ + IterNext next() const { return IterNext( *this ); } + + /** \brief Return the previous item. Does not modify this. */ + IterPrev prev() const { return IterPrev( *this ); } + + private: + static Element *findPrev( Element *element ); + static Element *findNext( Element *element ); + + public: + /** \brief The iterator is simply a pointer. */ + Element *ptr; + + /* The list is not walkable so we need to keep a pointerto the tree + * so we can test against head and tail in O(1) time. */ + const AvlTree *tree; + }; +#endif + + /** \brief Return first element. */ + IterFirst first() { return IterFirst( *this ); } + + /** \brief Return last element. */ + IterLast last() { return IterLast( *this ); } + +protected: + /* Recursive worker for the copy constructor. */ + Element *copyBranch( Element *element ); + + /* Recursively delete element in the tree. */ + void deleteChildrenOf(Element *n); + + /* rebalance the tree beginning at the leaf whose + * grandparent is unbalanced. */ + Element *rebalance(Element *start); + + /* Move up the tree from a given element, recalculating the heights. */ + void recalcHeights(Element *start); + + /* Move up the tree and find the first element whose + * grand-parent is unbalanced. */ + Element *findFirstUnbalGP(Element *start); + + /* Move up the tree and find the first element which is unbalanced. */ + Element *findFirstUnbalEl(Element *start); + + /* Replace a element in the tree with another element not in the tree. */ + void replaceEl(Element *element, Element *replacement); + + /* Remove a element from the tree and put another (normally a child of element) + * in its place. */ + void removeEl(Element *element, Element *filler); + + /* Once an insertion point is found at a leaf then do the insert. */ + void attachRebal( Element *element, Element *parentEl, Element *lastLess ); +}; + +/* Copy constructor. New up each item. */ +template AvlTree:: + AvlTree(const AvlTree &other) +#if !defined( AVL_KEYLESS ) && defined ( WALKABLE ) + /* BASELIST should be made empty. The copyBranch function + * will fill in the details for us. */ + : Compare( other ), BASELIST() +#elif !defined( AVL_KEYLESS ) + : Compare( other ) +#elif defined( WALKABLE ) + : BASELIST( ) +#endif +{ + treeSize = other.treeSize; + root = other.root; + +#ifndef WALKABLE + head = 0; + tail = 0; +#endif + + /* If there is a root, copy the tree. */ + if ( other.root != 0 ) + root = copyBranch( other.root ); +} + +#if defined( AVLTREE_MAP ) || defined( AVLTREE_SET ) + +/* Assignment does deep copy. */ +template AvlTree &AvlTree:: + operator=( const AvlTree &other ) +{ + /* Clear the tree first. */ + empty(); + + /* Reset the list pointers, the tree copy will fill in the list for us. */ +#ifdef WALKABLE + BASELIST::abandon(); +#else + head = 0; + tail = 0; +#endif + + /* Copy the entire tree. */ + treeSize = other.treeSize; + root = other.root; + if ( other.root != 0 ) + root = copyBranch( other.root ); + return *this; +} + +template void AvlTree:: + transfer(AvlTree &other) +{ + /* Clear the tree first. */ + empty(); + + treeSize = other.treeSize; + root = other.root; + +#ifdef WALKABLE + BASELIST::head = other.BASELIST::head; + BASELIST::tail = other.BASELIST::tail; + BASELIST::listLen = other.BASELIST::listLen; +#else + head = other.head; + tail = other.tail; +#endif + + other.abandon(); +} + +#else /* ! AVLTREE_MAP && ! AVLTREE_SET */ + +/* Assignment does deep copy. This version does not clear the tree first. */ +template AvlTree &AvlTree:: + operator=( const AvlTree &other ) +{ + /* Reset the list pointers, the tree copy will fill in the list for us. */ +#ifdef WALKABLE + BASELIST::abandon(); +#else + head = 0; + tail = 0; +#endif + + /* Copy the entire tree. */ + treeSize = other.treeSize; + root = other.root; + if ( other.root != 0 ) + root = copyBranch( other.root ); + return *this; +} + +template void AvlTree:: + transfer(AvlTree &other) +{ + treeSize = other.treeSize; + root = other.root; + +#ifdef WALKABLE + BASELIST::head = other.BASELIST::head; + BASELIST::tail = other.BASELIST::tail; + BASELIST::listLen = other.BASELIST::listLen; +#else + head = other.head; + tail = other.tail; +#endif + + other.abandon(); +} + +#endif + +/* + * Iterator operators. + */ + +/* Prefix ++ */ +template Element *AvlTree::Iter:: + operator++() +{ + return ptr = findNext( ptr ); +} + +/* Postfix ++ */ +template Element *AvlTree::Iter:: + operator++(int) +{ + Element *rtn = ptr; + ptr = findNext( ptr ); + return rtn; +} + +/* increment */ +template Element *AvlTree::Iter:: + increment() +{ + return ptr = findNext( ptr ); +} + +/* Prefix -- */ +template Element *AvlTree::Iter:: + operator--() +{ + return ptr = findPrev( ptr ); +} + +/* Postfix -- */ +template Element *AvlTree::Iter:: + operator--(int) +{ + Element *rtn = ptr; + ptr = findPrev( ptr ); + return rtn; +} + +/* decrement */ +template Element *AvlTree::Iter:: + decrement() +{ + return ptr = findPrev( ptr ); +} + +#ifndef WALKABLE + +/* Move ahead one. */ +template Element *AvlTree::Iter:: + findNext( Element *element ) +{ + /* Try to go right once then infinite left. */ + if ( element->BASE_EL(right) != 0 ) { + element = element->BASE_EL(right); + while ( element->BASE_EL(left) != 0 ) + element = element->BASE_EL(left); + } + else { + /* Go up to parent until we were just a left child. */ + while ( true ) { + Element *last = element; + element = element->BASE_EL(parent); + if ( element == 0 || element->BASE_EL(left) == last ) + break; + } + } + return element; +} + +/* Move back one. */ +template Element *AvlTree::Iter:: + findPrev( Element *element ) +{ + /* Try to go left once then infinite right. */ + if ( element->BASE_EL(left) != 0 ) { + element = element->BASE_EL(left); + while ( element->BASE_EL(right) != 0 ) + element = element->BASE_EL(right); + } + else { + /* Go up to parent until we were just a left child. */ + while ( true ) { + Element *last = element; + element = element->BASE_EL(parent); + if ( element == 0 || element->BASE_EL(right) == last ) + break; + } + } + return element; +} + +#endif + + +/* Recursive worker for tree copying. */ +template Element *AvlTree:: + copyBranch( Element *element ) +{ + /* Duplicate element. Either the base element's copy constructor or defaul + * constructor will get called. Both will suffice for initting the + * pointers to null when they need to be. */ + Element *retVal = new Element(*element); + + /* If the left tree is there, copy it. */ + if ( retVal->BASE_EL(left) ) { + retVal->BASE_EL(left) = copyBranch(retVal->BASE_EL(left)); + retVal->BASE_EL(left)->BASE_EL(parent) = retVal; + } + +#ifdef WALKABLE + BASELIST::addAfter( BASELIST::tail, retVal ); +#else + if ( head == 0 ) + head = retVal; + tail = retVal; +#endif + + /* If the right tree is there, copy it. */ + if ( retVal->BASE_EL(right) ) { + retVal->BASE_EL(right) = copyBranch(retVal->BASE_EL(right)); + retVal->BASE_EL(right)->BASE_EL(parent) = retVal; + } + return retVal; +} + +/* Once an insertion position is found, attach a element to the tree. */ +template void AvlTree:: + attachRebal( Element *element, Element *parentEl, Element *lastLess ) +{ + /* Increment the number of element in the tree. */ + treeSize += 1; + + /* Set element's parent. */ + element->BASE_EL(parent) = parentEl; + + /* New element always starts as a leaf with height 1. */ + element->BASE_EL(left) = 0; + element->BASE_EL(right) = 0; + element->BASE_EL(height) = 1; + + /* Are we inserting in the tree somewhere? */ + if ( parentEl != 0 ) { + /* We have a parent so we are somewhere in the tree. If the parent + * equals lastLess, then the last traversal in the insertion went + * left, otherwise it went right. */ + if ( lastLess == parentEl ) { + parentEl->BASE_EL(left) = element; +#ifdef WALKABLE + BASELIST::addBefore( parentEl, element ); +#endif + } + else { + parentEl->BASE_EL(right) = element; +#ifdef WALKABLE + BASELIST::addAfter( parentEl, element ); +#endif + } + +#ifndef WALKABLE + /* Maintain the first and last pointers. */ + if ( head->BASE_EL(left) == element ) + head = element; + + /* Maintain the first and last pointers. */ + if ( tail->BASE_EL(right) == element ) + tail = element; +#endif + } + else { + /* No parent element so we are inserting the root. */ + root = element; +#ifdef WALKABLE + BASELIST::addAfter( BASELIST::tail, element ); +#else + head = tail = element; +#endif + } + + + /* Recalculate the heights. */ + recalcHeights(parentEl); + + /* Find the first unbalance. */ + Element *ub = findFirstUnbalGP(element); + + /* rebalance. */ + if ( ub != 0 ) + { + /* We assert that after this single rotation the + * tree is now properly balanced. */ + rebalance(ub); + } +} + +#ifndef AVL_KEYLESS + +/** + * \brief Insert an existing element into the tree. + * + * If the insert succeeds and lastFound is given then it is set to the element + * inserted. If the insert fails then lastFound is set to the existing element in + * the tree that has the same key as element. If the element's avl pointers are + * already in use then undefined behaviour results. + * + * \returns The element inserted upon success, null upon failure. + */ +template Element *AvlTree:: + insert( Element *element, Element **lastFound ) +{ + long keyRelation; + Element *curEl = root, *parentEl = 0; + Element *lastLess = 0; + + while (true) { + if ( curEl == 0 ) { + /* We are at an external element and did not find the key we were + * looking for. Attach underneath the leaf and rebalance. */ + attachRebal( element, parentEl, lastLess ); + + if ( lastFound != 0 ) + *lastFound = element; + return element; + } + +#ifdef AVL_BASIC + keyRelation = this->compare( *element, *curEl ); +#else + keyRelation = this->compare( element->BASEKEY(getKey()), + curEl->BASEKEY(getKey()) ); +#endif + + /* Do we go left? */ + if ( keyRelation < 0 ) { + parentEl = lastLess = curEl; + curEl = curEl->BASE_EL(left); + } + /* Do we go right? */ + else if ( keyRelation > 0 ) { + parentEl = curEl; + curEl = curEl->BASE_EL(right); + } + /* We have hit the target. */ + else { + if ( lastFound != 0 ) + *lastFound = curEl; + return 0; + } + } +} + +#ifdef AVL_BASIC + +/** + * \brief Find a element in the tree with the given key. + * + * \returns The element if key exists, null if the key does not exist. + */ +template Element *AvlTree:: + find( const Element *element ) const +{ + Element *curEl = root; + long keyRelation; + + while (curEl) { + keyRelation = this->compare( *element, *curEl ); + + /* Do we go left? */ + if ( keyRelation < 0 ) + curEl = curEl->BASE_EL(left); + /* Do we go right? */ + else if ( keyRelation > 0 ) + curEl = curEl->BASE_EL(right); + /* We have hit the target. */ + else { + return curEl; + } + } + return 0; +} + +#else + +/** + * \brief Insert a new element into the tree with given key. + * + * If the key is not already in the tree then a new element is made using the + * Element(const Key &key) constructor and the insert succeeds. If lastFound is + * given then it is set to the element inserted. If the insert fails then + * lastFound is set to the existing element in the tree that has the same key as + * element. + * + * \returns The new element upon success, null upon failure. + */ +template Element *AvlTree:: + insert( const Key &key, Element **lastFound ) +{ + long keyRelation; + Element *curEl = root, *parentEl = 0; + Element *lastLess = 0; + + while (true) { + if ( curEl == 0 ) { + /* We are at an external element and did not find the key we were + * looking for. Create the new element, attach it underneath the leaf + * and rebalance. */ + Element *element = new Element( key ); + attachRebal( element, parentEl, lastLess ); + + if ( lastFound != 0 ) + *lastFound = element; + return element; + } + + keyRelation = this->compare( key, curEl->BASEKEY(getKey()) ); + + /* Do we go left? */ + if ( keyRelation < 0 ) { + parentEl = lastLess = curEl; + curEl = curEl->BASE_EL(left); + } + /* Do we go right? */ + else if ( keyRelation > 0 ) { + parentEl = curEl; + curEl = curEl->BASE_EL(right); + } + /* We have hit the target. */ + else { + if ( lastFound != 0 ) + *lastFound = curEl; + return 0; + } + } +} + +#ifdef AVLTREE_MAP +/** + * \brief Insert a new element into the tree with key and value. + * + * If the key is not already in the tree then a new element is constructed and + * the insert succeeds. If lastFound is given then it is set to the element + * inserted. If the insert fails then lastFound is set to the existing element in + * the tree that has the same key as element. This insert routine is only + * available in AvlMap because it is the only class that knows about a Value + * type. + * + * \returns The new element upon success, null upon failure. + */ +template Element *AvlTree:: + insert( const Key &key, const Value &val, Element **lastFound ) +{ + long keyRelation; + Element *curEl = root, *parentEl = 0; + Element *lastLess = 0; + + while (true) { + if ( curEl == 0 ) { + /* We are at an external element and did not find the key we were + * looking for. Create the new element, attach it underneath the leaf + * and rebalance. */ + Element *element = new Element( key, val ); + attachRebal( element, parentEl, lastLess ); + + if ( lastFound != 0 ) + *lastFound = element; + return element; + } + + keyRelation = this->compare(key, curEl->getKey()); + + /* Do we go left? */ + if ( keyRelation < 0 ) { + parentEl = lastLess = curEl; + curEl = curEl->BASE_EL(left); + } + /* Do we go right? */ + else if ( keyRelation > 0 ) { + parentEl = curEl; + curEl = curEl->BASE_EL(right); + } + /* We have hit the target. */ + else { + if ( lastFound != 0 ) + *lastFound = curEl; + return 0; + } + } +} +#endif /* AVLTREE_MAP */ + + +/** + * \brief Find a element in the tree with the given key. + * + * \returns The element if key exists, null if the key does not exist. + */ +template Element *AvlTree:: + find( const Key &key ) const +{ + Element *curEl = root; + long keyRelation; + + while (curEl) { + keyRelation = this->compare( key, curEl->BASEKEY(getKey()) ); + + /* Do we go left? */ + if ( keyRelation < 0 ) + curEl = curEl->BASE_EL(left); + /* Do we go right? */ + else if ( keyRelation > 0 ) + curEl = curEl->BASE_EL(right); + /* We have hit the target. */ + else { + return curEl; + } + } + return 0; +} + + +/** + * \brief Find a element, then detach it from the tree. + * + * The element is not deleted. + * + * \returns The element detached if the key is found, othewise returns null. + */ +template Element *AvlTree:: + detach(const Key &key) +{ + Element *element = find( key ); + if ( element ) { + detach(element); + } + + return element; +} + +/** + * \brief Find, detach and delete a element from the tree. + * + * \returns True if the element was found and deleted, false otherwise. + */ +template bool AvlTree:: + remove(const Key &key) +{ + /* Assume not found. */ + bool retVal = false; + + /* Look for the key. */ + Element *element = find( key ); + if ( element != 0 ) { + /* If found, detach the element and delete. */ + detach( element ); + delete element; + retVal = true; + } + + return retVal; +} + +#endif /* AVL_BASIC */ +#endif /* AVL_KEYLESS */ + + +/** + * \brief Detach and delete a element from the tree. + * + * If the element is not in the tree then undefined behaviour results. + */ +template void AvlTree:: + remove(Element *element) +{ + /* Detach and delete. */ + detach(element); + delete element; +} + +/** + * \brief Detach a element from the tree. + * + * If the element is not in the tree then undefined behaviour results. + * + * \returns The element given. + */ +template Element *AvlTree:: + detach(Element *element) +{ + Element *replacement, *fixfrom; + long lheight, rheight; + +#ifdef WALKABLE + /* Remove the element from the ordered list. */ + BASELIST::detach( element ); +#endif + + /* Update treeSize. */ + treeSize--; + + /* Find a replacement element. */ + if (element->BASE_EL(right)) + { + /* Find the leftmost element of the right subtree. */ + replacement = element->BASE_EL(right); + while (replacement->BASE_EL(left)) + replacement = replacement->BASE_EL(left); + + /* If replacing the element the with its child then we need to start + * fixing at the replacement, otherwise we start fixing at the + * parent of the replacement. */ + if (replacement->BASE_EL(parent) == element) + fixfrom = replacement; + else + fixfrom = replacement->BASE_EL(parent); + +#ifndef WALKABLE + if ( element == head ) + head = replacement; +#endif + + removeEl(replacement, replacement->BASE_EL(right)); + replaceEl(element, replacement); + } + else if (element->BASE_EL(left)) + { + /* Find the rightmost element of the left subtree. */ + replacement = element->BASE_EL(left); + while (replacement->BASE_EL(right)) + replacement = replacement->BASE_EL(right); + + /* If replacing the element the with its child then we need to start + * fixing at the replacement, otherwise we start fixing at the + * parent of the replacement. */ + if (replacement->BASE_EL(parent) == element) + fixfrom = replacement; + else + fixfrom = replacement->BASE_EL(parent); + +#ifndef WALKABLE + if ( element == tail ) + tail = replacement; +#endif + + removeEl(replacement, replacement->BASE_EL(left)); + replaceEl(element, replacement); + } + else + { + /* We need to start fixing at the parent of the element. */ + fixfrom = element->BASE_EL(parent); + +#ifndef WALKABLE + if ( element == head ) + head = element->BASE_EL(parent); + if ( element == tail ) + tail = element->BASE_EL(parent); +#endif + + /* The element we are deleting is a leaf element. */ + removeEl(element, 0); + } + + /* If fixfrom is null it means we just deleted + * the root of the tree. */ + if ( fixfrom == 0 ) + return element; + + /* Fix the heights after the deletion. */ + recalcHeights(fixfrom); + + /* Fix every unbalanced element going up in the tree. */ + Element *ub = findFirstUnbalEl(fixfrom); + while ( ub ) + { + /* Find the element to rebalance by moving down from the first unbalanced + * element 2 levels in the direction of the greatest heights. On the + * second move down, the heights may be equal ( but not on the first ). + * In which case go in the direction of the first move. */ + lheight = ub->BASE_EL(left) ? ub->BASE_EL(left)->BASE_EL(height) : 0; + rheight = ub->BASE_EL(right) ? ub->BASE_EL(right)->BASE_EL(height) : 0; + assert( lheight != rheight ); + if (rheight > lheight) + { + ub = ub->BASE_EL(right); + lheight = ub->BASE_EL(left) ? + ub->BASE_EL(left)->BASE_EL(height) : 0; + rheight = ub->BASE_EL(right) ? + ub->BASE_EL(right)->BASE_EL(height) : 0; + if (rheight > lheight) + ub = ub->BASE_EL(right); + else if (rheight < lheight) + ub = ub->BASE_EL(left); + else + ub = ub->BASE_EL(right); + } + else + { + ub = ub->BASE_EL(left); + lheight = ub->BASE_EL(left) ? + ub->BASE_EL(left)->BASE_EL(height) : 0; + rheight = ub->BASE_EL(right) ? + ub->BASE_EL(right)->BASE_EL(height) : 0; + if (rheight > lheight) + ub = ub->BASE_EL(right); + else if (rheight < lheight) + ub = ub->BASE_EL(left); + else + ub = ub->BASE_EL(left); + } + + + /* rebalance returns the grandparant of the subtree formed + * by the element that were rebalanced. + * We must continue upward from there rebalancing. */ + fixfrom = rebalance(ub); + + /* Find the next unbalaced element. */ + ub = findFirstUnbalEl(fixfrom); + } + + return element; +} + + +/** + * \brief Empty the tree and delete all the element. + * + * Resets the tree to its initial state. + */ +template void AvlTree::empty() +{ + if ( root ) { + /* Recursively delete from the tree structure. */ + deleteChildrenOf(root); + delete root; + root = 0; + treeSize = 0; + +#ifdef WALKABLE + BASELIST::abandon(); +#else + head = tail = 0; +#endif + } +} + +/** + * \brief Forget all element in the tree. + * + * Does not delete element. Resets the the tree to it's initial state. + */ +template void AvlTree::abandon() +{ + root = 0; + treeSize = 0; + +#ifdef WALKABLE + BASELIST::abandon(); +#else + head = tail = 0; +#endif +} + +/* Recursively delete all the children of a element. */ +template void AvlTree:: + deleteChildrenOf( Element *element ) +{ + /* Recurse left. */ + if (element->BASE_EL(left)) { + deleteChildrenOf(element->BASE_EL(left)); + + /* Delete left element. */ + delete element->BASE_EL(left); + element->BASE_EL(left) = 0; + } + + /* Recurse right. */ + if (element->BASE_EL(right)) { + deleteChildrenOf(element->BASE_EL(right)); + + /* Delete right element. */ + delete element->BASE_EL(right); + element->BASE_EL(left) = 0; + } +} + +/* rebalance from a element whose gradparent is unbalanced. Only + * call on a element that has a grandparent. */ +template Element *AvlTree:: + rebalance(Element *n) +{ + long lheight, rheight; + Element *a, *b, *c; + Element *t1, *t2, *t3, *t4; + + Element *p = n->BASE_EL(parent); /* parent (Non-NUL). L*/ + Element *gp = p->BASE_EL(parent); /* Grand-parent (Non-NULL). */ + Element *ggp = gp->BASE_EL(parent); /* Great grand-parent (may be NULL). */ + + if (gp->BASE_EL(right) == p) + { + /* gp + * \ + * p + */ + if (p->BASE_EL(right) == n) + { + /* gp + * \ + * p + * \ + * n + */ + a = gp; + b = p; + c = n; + t1 = gp->BASE_EL(left); + t2 = p->BASE_EL(left); + t3 = n->BASE_EL(left); + t4 = n->BASE_EL(right); + } + else + { + /* gp + * \ + * p + * / + * n + */ + a = gp; + b = n; + c = p; + t1 = gp->BASE_EL(left); + t2 = n->BASE_EL(left); + t3 = n->BASE_EL(right); + t4 = p->BASE_EL(right); + } + } + else + { + /* gp + * / + * p + */ + if (p->BASE_EL(right) == n) + { + /* gp + * / + * p + * \ + * n + */ + a = p; + b = n; + c = gp; + t1 = p->BASE_EL(left); + t2 = n->BASE_EL(left); + t3 = n->BASE_EL(right); + t4 = gp->BASE_EL(right); + } + else + { + /* gp + * / + * p + * / + * n + */ + a = n; + b = p; + c = gp; + t1 = n->BASE_EL(left); + t2 = n->BASE_EL(right); + t3 = p->BASE_EL(right); + t4 = gp->BASE_EL(right); + } + } + + /* Perform rotation. + */ + + /* Tie b to the great grandparent. */ + if ( ggp == 0 ) + root = b; + else if ( ggp->BASE_EL(left) == gp ) + ggp->BASE_EL(left) = b; + else + ggp->BASE_EL(right) = b; + b->BASE_EL(parent) = ggp; + + /* Tie a as a leftchild of b. */ + b->BASE_EL(left) = a; + a->BASE_EL(parent) = b; + + /* Tie c as a rightchild of b. */ + b->BASE_EL(right) = c; + c->BASE_EL(parent) = b; + + /* Tie t1 as a leftchild of a. */ + a->BASE_EL(left) = t1; + if ( t1 != 0 ) t1->BASE_EL(parent) = a; + + /* Tie t2 as a rightchild of a. */ + a->BASE_EL(right) = t2; + if ( t2 != 0 ) t2->BASE_EL(parent) = a; + + /* Tie t3 as a leftchild of c. */ + c->BASE_EL(left) = t3; + if ( t3 != 0 ) t3->BASE_EL(parent) = c; + + /* Tie t4 as a rightchild of c. */ + c->BASE_EL(right) = t4; + if ( t4 != 0 ) t4->BASE_EL(parent) = c; + + /* The heights are all recalculated manualy and the great + * grand-parent is passed to recalcHeights() to ensure + * the heights are correct up the tree. + * + * Note that recalcHeights() cuts out when it comes across + * a height that hasn't changed. + */ + + /* Fix height of a. */ + lheight = a->BASE_EL(left) ? a->BASE_EL(left)->BASE_EL(height) : 0; + rheight = a->BASE_EL(right) ? a->BASE_EL(right)->BASE_EL(height) : 0; + a->BASE_EL(height) = (lheight > rheight ? lheight : rheight) + 1; + + /* Fix height of c. */ + lheight = c->BASE_EL(left) ? c->BASE_EL(left)->BASE_EL(height) : 0; + rheight = c->BASE_EL(right) ? c->BASE_EL(right)->BASE_EL(height) : 0; + c->BASE_EL(height) = (lheight > rheight ? lheight : rheight) + 1; + + /* Fix height of b. */ + lheight = a->BASE_EL(height); + rheight = c->BASE_EL(height); + b->BASE_EL(height) = (lheight > rheight ? lheight : rheight) + 1; + + /* Fix height of b's parents. */ + recalcHeights(ggp); + return ggp; +} + +/* Recalculates the heights of all the ancestors of element. */ +template void AvlTree:: + recalcHeights(Element *element) +{ + long lheight, rheight, new_height; + while ( element != 0 ) + { + lheight = element->BASE_EL(left) ? element->BASE_EL(left)->BASE_EL(height) : 0; + rheight = element->BASE_EL(right) ? element->BASE_EL(right)->BASE_EL(height) : 0; + + new_height = (lheight > rheight ? lheight : rheight) + 1; + + /* If there is no chage in the height, then there will be no + * change in any of the ancestor's height. We can stop going up. + * If there was a change, continue upward. */ + if (new_height == element->BASE_EL(height)) + return; + else + element->BASE_EL(height) = new_height; + + element = element->BASE_EL(parent); + } +} + +/* Finds the first element whose grandparent is unbalanced. */ +template Element *AvlTree:: + findFirstUnbalGP(Element *element) +{ + long lheight, rheight, balanceProp; + Element *gp; + + if ( element == 0 || element->BASE_EL(parent) == 0 || + element->BASE_EL(parent)->BASE_EL(parent) == 0 ) + return 0; + + /* Don't do anything if we we have no grandparent. */ + gp = element->BASE_EL(parent)->BASE_EL(parent); + while ( gp != 0 ) + { + lheight = gp->BASE_EL(left) ? gp->BASE_EL(left)->BASE_EL(height) : 0; + rheight = gp->BASE_EL(right) ? gp->BASE_EL(right)->BASE_EL(height) : 0; + balanceProp = lheight - rheight; + + if ( balanceProp < -1 || balanceProp > 1 ) + return element; + + element = element->BASE_EL(parent); + gp = gp->BASE_EL(parent); + } + return 0; +} + + +/* Finds the first element that is unbalanced. */ +template Element *AvlTree:: + findFirstUnbalEl(Element *element) +{ + if ( element == 0 ) + return 0; + + while ( element != 0 ) + { + long lheight = element->BASE_EL(left) ? + element->BASE_EL(left)->BASE_EL(height) : 0; + long rheight = element->BASE_EL(right) ? + element->BASE_EL(right)->BASE_EL(height) : 0; + long balanceProp = lheight - rheight; + + if ( balanceProp < -1 || balanceProp > 1 ) + return element; + + element = element->BASE_EL(parent); + } + return 0; +} + +/* Replace a element in the tree with another element not in the tree. */ +template void AvlTree:: + replaceEl(Element *element, Element *replacement) +{ + Element *parent = element->BASE_EL(parent), + *left = element->BASE_EL(left), + *right = element->BASE_EL(right); + + replacement->BASE_EL(left) = left; + if (left) + left->BASE_EL(parent) = replacement; + replacement->BASE_EL(right) = right; + if (right) + right->BASE_EL(parent) = replacement; + + replacement->BASE_EL(parent) = parent; + if (parent) + { + if (parent->BASE_EL(left) == element) + parent->BASE_EL(left) = replacement; + else + parent->BASE_EL(right) = replacement; + } + else + root = replacement; + + replacement->BASE_EL(height) = element->BASE_EL(height); +} + +/* Removes a element from a tree and puts filler in it's place. + * Filler should be null or a child of element. */ +template void AvlTree:: + removeEl(Element *element, Element *filler) +{ + Element *parent = element->BASE_EL(parent); + + if (parent) + { + if (parent->BASE_EL(left) == element) + parent->BASE_EL(left) = filler; + else + parent->BASE_EL(right) = filler; + } + else + root = filler; + + if (filler) + filler->BASE_EL(parent) = parent; + + return; +} + +#ifdef AAPL_NAMESPACE +} +#endif diff --git a/src/aapl/avlibasic.h b/src/aapl/avlibasic.h new file mode 100644 index 00000000..5f9b2c7f --- /dev/null +++ b/src/aapl/avlibasic.h @@ -0,0 +1,68 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLIBASIC_H +#define _AAPL_AVLIBASIC_H + +#include "compare.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliBasic + * \brief Linked AVL Tree in which the entire element structure is the key. + * + * AvliBasic is a linked AVL tree that does not distinguish between the + * element that it contains and the key. The entire element structure is the + * key that is used to compare the relative ordering of elements. This is + * similar to the BstSet structure. + * + * AvliBasic does not assume ownership of elements in the tree. Items must be + * explicitly de-allocated. + */ + +/*@}*/ + +#define BASE_EL(name) name +#define BASEKEY(name) name +#define AVLMEL_CLASSDEF class Element, class Compare +#define AVLMEL_TEMPDEF class Element, class Compare +#define AVLMEL_TEMPUSE Element, Compare +#define AvlTree AvliBasic +#define AVL_BASIC +#define WALKABLE + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef AVL_BASIC +#undef WALKABLE + +#endif /* _AAPL_AVLIBASIC_H */ diff --git a/src/aapl/avlikeyless.h b/src/aapl/avlikeyless.h new file mode 100644 index 00000000..8d90443b --- /dev/null +++ b/src/aapl/avlikeyless.h @@ -0,0 +1,65 @@ +/* + * Copyright 2002, 2003 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLIKEYLESS_H +#define _AAPL_AVLIKEYLESS_H + +#include "compare.h" +#include "dlistmel.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliKeyless + * \brief Linked AVL tree that has no insert/find/remove functions that take a + * key. + * + * AvliKeyless is an implementation of the AVL tree rebalancing functionality + * only. It provides the common code for the tiny AVL tree implementations. + */ + +/*@}*/ + +#define BASE_EL(name) name +#define BASELIST DListMel< Element, AvliTreeEl > +#define AVLMEL_CLASSDEF class Element +#define AVLMEL_TEMPDEF class Element +#define AVLMEL_TEMPUSE Element +#define AvlTree AvliKeyless +#define WALKABLE +#define AVL_KEYLESS + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASELIST +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef WALKABLE +#undef AVL_KEYLESS + +#endif /* _AAPL_AVLIKEYLESS_H */ diff --git a/src/aapl/avlimap.h b/src/aapl/avlimap.h new file mode 100644 index 00000000..f2b1b0c5 --- /dev/null +++ b/src/aapl/avlimap.h @@ -0,0 +1,78 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLIMAP_H +#define _AAPL_AVLIMAP_H + +#include "compare.h" +#include "dlist.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliMap + * \brief Linked key and value oriented AVL tree. + * + * AvliMap stores key and value pairs in elements that managed by the tree. It + * is intendend to be similar to map template found in the STL. AvliMap + * requires that a Key type, a Value type, and a class containing a compare() + * routine for Key be given. Items can be inserted with just a key or with a + * key and value pair. + * + * AvliMap assumes all elements in the tree are allocated on the heap and are + * to be managed by the tree. This means that the class destructor will delete + * the contents of the tree. A deep copy will cause existing elements to be + * deleted first. + * + * \include ex_avlimap.cpp + */ + +/*@}*/ + +#define AVLTREE_MAP +#define BASE_EL(name) name +#define BASEKEY(name) name +#define BASELIST DList< AvliMapEl > +#define AVLMEL_CLASSDEF class Key, class Value, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Key, class Value, class Compare +#define AVLMEL_TEMPUSE Key, Value, Compare +#define AvlTree AvliMap +#define Element AvliMapEl +#define WALKABLE + +#include "avlcommon.h" + +#undef AVLTREE_MAP +#undef BASE_EL +#undef BASEKEY +#undef BASELIST +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef Element +#undef WALKABLE + +#endif /* _AAPL_AVLIMAP_H */ diff --git a/src/aapl/avlimel.h b/src/aapl/avlimel.h new file mode 100644 index 00000000..9a7ff951 --- /dev/null +++ b/src/aapl/avlimel.h @@ -0,0 +1,80 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLIMEL_H +#define _AAPL_AVLIMEL_H + +#include "compare.h" +#include "dlistmel.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliMel + * \brief Linked AVL tree for element appearing in multiple trees. + * + * AvliMel allows for an element to simultaneously be in multiple trees without + * the trees interferring with one another. For each tree that the element is + * to appear in, there must be a distinct set of AVL Tree management data that + * can be unambiguously referenced with some base class name. This name + * is passed to the tree as a template parameter and is used in the tree + * algorithms. + * + * The element must use the same key type and value in each tree that it + * appears in. If distinct keys are required, the AvliMelKey structure is + * available. + * + * AvliMel does not assume ownership of elements in the tree. The destructor + * will not delete the elements. If the user wishes to explicitly deallocate + * all the items in the tree the empty() routine is available. + * + * \include ex_avlimel.cpp + */ + +/*@}*/ + +#define BASE_EL(name) BaseEl::name +#define BASEKEY(name) name +#define BASELIST DListMel< Element, BaseEl > +#define AVLMEL_CLASSDEF class Element, class Key, \ + class BaseEl, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Element, class Key, \ + class BaseEl, class Compare +#define AVLMEL_TEMPUSE Element, Key, BaseEl, Compare +#define AvlTree AvliMel +#define WALKABLE + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef BASELIST +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef WALKABLE + +#endif /* _AAPL_AVLIMEL_H */ diff --git a/src/aapl/avlimelkey.h b/src/aapl/avlimelkey.h new file mode 100644 index 00000000..41d4ae49 --- /dev/null +++ b/src/aapl/avlimelkey.h @@ -0,0 +1,77 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLIMELKEY_H +#define _AAPL_AVLIMELKEY_H + +#include "compare.h" +#include "dlistmel.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliMelKey + * \brief Linked AVL tree for element appearing in multiple trees with different keys. + * + * AvliMelKey is similar to AvliMel, except that an additional template + * parameter, BaseKey, is provided for resolving ambiguous references to + * getKey(). This means that if an element is stored in multiple trees, each + * tree can use a different key for ordering the elements in it. Using + * AvliMelKey an array of data structures can be indexed with an O(log(n)) + * search on two or more of the values contained within it and without + * allocating any additional data. + * + * AvliMelKey does not assume ownership of elements in the tree. The destructor + * will not delete the elements. If the user wishes to explicitly deallocate + * all the items in the tree the empty() routine is available. + * + * \include ex_avlimelkey.cpp + */ + +/*@}*/ + +#define BASE_EL(name) BaseEl::name +#define BASEKEY(name) BaseKey::name +#define BASELIST DListMel< Element, BaseEl > +#define AVLMEL_CLASSDEF class Element, class Key, class BaseEl, \ + class BaseKey, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Element, class Key, class BaseEl, \ + class BaseKey, class Compare +#define AVLMEL_TEMPUSE Element, Key, BaseEl, BaseKey, Compare +#define AvlTree AvliMelKey +#define WALKABLE + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef BASELIST +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef WALKABLE + +#endif /* _AAPL_AVLIMELKEY_H */ diff --git a/src/aapl/avliset.h b/src/aapl/avliset.h new file mode 100644 index 00000000..e309c3ad --- /dev/null +++ b/src/aapl/avliset.h @@ -0,0 +1,76 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLISET_H +#define _AAPL_AVLISET_H + +#include "compare.h" +#include "dlist.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliSet + * \brief Linked Key-only oriented tree. + * + * AvliSet stores only keys in elements that are managed by the tree. AvliSet + * requires that a Key type and a class containing a compare() routine + * for Key be given. Items are inserted with just a key value. + * + * AvliSet assumes all elements in the tree are allocated on the heap and are + * to be managed by the tree. This means that the class destructor will delete + * the contents of the tree. A deep copy will cause existing elements to be + * deleted first. + * + * \include ex_avliset.cpp + */ + +/*@}*/ + +#define AVLTREE_SET +#define BASE_EL(name) name +#define BASEKEY(name) name +#define BASELIST DList< AvliSetEl > +#define AVLMEL_CLASSDEF class Key, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Key, class Compare +#define AVLMEL_TEMPUSE Key, Compare +#define AvlTree AvliSet +#define Element AvliSetEl +#define WALKABLE + +#include "avlcommon.h" + +#undef AVLTREE_SET +#undef BASE_EL +#undef BASEKEY +#undef BASELIST +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef Element +#undef WALKABLE + +#endif /* _AAPL_AVLISET_H */ diff --git a/src/aapl/avlitree.h b/src/aapl/avlitree.h new file mode 100644 index 00000000..54c8f30f --- /dev/null +++ b/src/aapl/avlitree.h @@ -0,0 +1,79 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLITREE_H +#define _AAPL_AVLITREE_H + +#include "compare.h" +#include "dlistmel.h" + +/** + * \addtogroup avlitree + * @{ + */ + +/** + * \class AvliTree + * \brief Linked AVL tree. + * + * AvliTree is the standard linked by-structure AVL tree. To use this + * structure the user must define an element type and give it the necessary + * properties. At the very least it must have a getKey() function that will be + * used to compare the relative ordering of elements and tree management data + * necessary for the AVL algorithm. An element type can acquire the management + * data by inheriting the AvliTreeEl class. + * + * AvliTree does not presume to manage the allocation of elements in the tree. + * The destructor will not delete the items in the tree, instead the elements + * must be explicitly de-allocated by the user if necessary and when it is + * safe to do so. The empty() routine will traverse the tree and delete all + * items. + * + * Since the tree does not manage the elements, it can contain elements that + * are allocated statically or that are part of another data structure. + * + * \include ex_avlitree.cpp + */ + +/*@}*/ + +#define BASE_EL(name) name +#define BASEKEY(name) name +#define BASELIST DListMel< Element, AvliTreeEl > +#define AVLMEL_CLASSDEF class Element, class Key, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Element, class Key, class Compare +#define AVLMEL_TEMPUSE Element, Key, Compare +#define AvlTree AvliTree +#define WALKABLE + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef BASELIST +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef WALKABLE + +#endif /* _AAPL_AVLITREE_H */ diff --git a/src/aapl/avlkeyless.h b/src/aapl/avlkeyless.h new file mode 100644 index 00000000..9ba769c2 --- /dev/null +++ b/src/aapl/avlkeyless.h @@ -0,0 +1,59 @@ +/* + * Copyright 2002, 2003 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLKEYLESS_H +#define _AAPL_AVLKEYLESS_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlKeyless + * \brief AVL tree that has no insert/find/remove functions that take a key. + * + * AvlKeyless is an implementation of the AVL tree rebalancing functionality + * only. It provides the common code for the tiny AVL tree implementations. + */ + +/*@}*/ + +#define BASE_EL(name) name +#define AVLMEL_CLASSDEF class Element +#define AVLMEL_TEMPDEF class Element +#define AVLMEL_TEMPUSE Element +#define AvlTree AvlKeyless +#define AVL_KEYLESS + +#include "avlcommon.h" + +#undef BASE_EL +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef AVL_KEYLESS + +#endif /* _AAPL_AVLKEYLESS_H */ diff --git a/src/aapl/avlmap.h b/src/aapl/avlmap.h new file mode 100644 index 00000000..9b52c8ba --- /dev/null +++ b/src/aapl/avlmap.h @@ -0,0 +1,75 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLMAP_H +#define _AAPL_AVLMAP_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlMap + * \brief Key and value oriented AVL tree. + * + * AvlMap stores key and value pairs in elements that managed by the tree. It + * is intendend to be similar to map template found in the STL. AvlMap + * requires that a Key type, a Value type, and a class containing a compare() + * routine for Key be given. Items can be inserted with just a key or with a + * key and value pair. + * + * AvlMap assumes all elements in the tree are allocated on the heap and are + * to be managed by the tree. This means that the class destructor will delete + * the contents of the tree. A deep copy will cause existing elements to be + * deleted first. + * + * \include ex_avlmap.cpp + */ + +/*@}*/ + +#define AVLTREE_MAP +#define BASE_EL(name) name +#define BASEKEY(name) name +#define AVLMEL_CLASSDEF class Key, class Value, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Key, class Value, class Compare +#define AVLMEL_TEMPUSE Key, Value, Compare +#define AvlTree AvlMap +#define Element AvlMapEl + +#include "avlcommon.h" + +#undef AVLTREE_MAP +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef Element + + + +#endif /* _AAPL_AVLMAP_H */ diff --git a/src/aapl/avlmel.h b/src/aapl/avlmel.h new file mode 100644 index 00000000..fe7671b8 --- /dev/null +++ b/src/aapl/avlmel.h @@ -0,0 +1,75 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLMEL_H +#define _AAPL_AVLMEL_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlMel + * \brief AVL tree for elements appearing in multiple trees. + * + * AvlMel allows for an element to simultaneously be in multiple trees without + * the trees interferring with one another. For each tree that the element is + * to appear in, there must be a distinct set of AVL Tree management data that + * can be unambiguously referenced with some base class name. This name + * is passed to the tree as a template parameter and is used in the tree + * algorithms. + * + * The element must use the same key type and value in each tree that it + * appears in. If distinct keys are required, the AvlMelKey structure is + * available. + * + * AvlMel does not assume ownership of elements in the tree. The destructor + * will not delete the elements. If the user wishes to explicitly deallocate + * all the items in the tree the empty() routine is available. + * + * \include ex_avlmel.cpp + */ + +/*@}*/ + +#define BASE_EL(name) BaseEl::name +#define BASEKEY(name) name +#define AVLMEL_CLASSDEF class Element, class Key, \ + class BaseEl, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Element, class Key, \ + class BaseEl, class Compare +#define AVLMEL_TEMPUSE Element, Key, BaseEl, Compare +#define AvlTree AvlMel + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree + +#endif /* _AAPL_AVLMEL_H */ diff --git a/src/aapl/avlmelkey.h b/src/aapl/avlmelkey.h new file mode 100644 index 00000000..cce7126f --- /dev/null +++ b/src/aapl/avlmelkey.h @@ -0,0 +1,72 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLMELKEY_H +#define _AAPL_AVLMELKEY_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlMelKey + * \brief AVL tree for elements appearing in multiple trees with different keys. + * + * AvlMelKey is similar to AvlMel, except that an additional template + * parameter, BaseKey, is provided for resolving ambiguous references to + * getKey(). This means that if an element is stored in multiple trees, each + * tree can use a different key for ordering the elements in it. Using + * AvlMelKey an array of data structures can be indexed with an O(log(n)) + * search on two or more of the values contained within it and without + * allocating any additional data. + * + * AvlMelKey does not assume ownership of elements in the tree. The destructor + * will not delete the elements. If the user wishes to explicitly deallocate + * all the items in the tree the empty() routine is available. + * + * \include ex_avlmelkey.cpp + */ + +/*@}*/ + +#define BASE_EL(name) BaseEl::name +#define BASEKEY(name) BaseKey::name +#define AVLMEL_CLASSDEF class Element, class Key, class BaseEl, \ + class BaseKey, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Element, class Key, class BaseEl, \ + class BaseKey, class Compare +#define AVLMEL_TEMPUSE Element, Key, BaseEl, BaseKey, Compare +#define AvlTree AvlMelKey + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree + +#endif /* _AAPL_AVLMELKEY_H */ diff --git a/src/aapl/avlset.h b/src/aapl/avlset.h new file mode 100644 index 00000000..97e5c484 --- /dev/null +++ b/src/aapl/avlset.h @@ -0,0 +1,71 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLSET_H +#define _AAPL_AVLSET_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlSet + * \brief Key-only oriented tree. + * + * AvlSet stores only keys in elements that are managed by the tree. AvlSet + * requires that a Key type and a class containing a compare() routine + * for Key be given. Items are inserted with just a key value. + * + * AvlSet assumes all elements in the tree are allocated on the heap and are + * to be managed by the tree. This means that the class destructor will delete + * the contents of the tree. A deep copy will cause existing elements to be + * deleted first. + * + * \include ex_avlset.cpp + */ + +/*@}*/ + +#define AVLTREE_SET +#define BASE_EL(name) name +#define BASEKEY(name) name +#define AVLMEL_CLASSDEF class Key, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Key, class Compare +#define AVLMEL_TEMPUSE Key, Compare +#define AvlTree AvlSet +#define Element AvlSetEl + +#include "avlcommon.h" + +#undef AVLTREE_SET +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree +#undef Element + +#endif /* _AAPL_AVLSET_H */ diff --git a/src/aapl/avltree.h b/src/aapl/avltree.h new file mode 100644 index 00000000..9237810e --- /dev/null +++ b/src/aapl/avltree.h @@ -0,0 +1,74 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_AVLTREE_H +#define _AAPL_AVLTREE_H + +#include "compare.h" + +/** + * \addtogroup avltree + * @{ + */ + +/** + * \class AvlTree + * \brief Basic AVL tree. + * + * AvlTree is the standard by-structure AVL tree. To use this structure the + * user must define an element type and give it the necessary properties. At + * the very least it must have a getKey() function that will be used to + * compare the relative ordering of elements and tree management data + * necessary for the AVL algorithm. An element type can acquire the management + * data by inheriting the AvlTreeEl class. + * + * AvlTree does not presume to manage the allocation of elements in the tree. + * The destructor will not delete the items in the tree, instead the elements + * must be explicitly de-allocated by the user if necessary and when it is + * safe to do so. The empty() routine will traverse the tree and delete all + * items. + * + * Since the tree does not manage the elements, it can contain elements that + * are allocated statically or that are part of another data structure. + * + * \include ex_avltree.cpp + */ + +/*@}*/ + +#define BASE_EL(name) name +#define BASEKEY(name) name +#define AVLMEL_CLASSDEF class Element, class Key, class Compare = CmpOrd +#define AVLMEL_TEMPDEF class Element, class Key, class Compare +#define AVLMEL_TEMPUSE Element, Key, Compare +#define AvlTree AvlTree + +#include "avlcommon.h" + +#undef BASE_EL +#undef BASEKEY +#undef AVLMEL_CLASSDEF +#undef AVLMEL_TEMPDEF +#undef AVLMEL_TEMPUSE +#undef AvlTree + +#endif /* _AAPL_AVLTREE_H */ diff --git a/src/aapl/bstcommon.h b/src/aapl/bstcommon.h new file mode 100644 index 00000000..391b4ff1 --- /dev/null +++ b/src/aapl/bstcommon.h @@ -0,0 +1,815 @@ +/* + * Copyright 2001 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This header is not wrapped in ifndefs because it is + * not intended to be included by users directly. */ + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/* Binary Search Table */ +template < BST_TEMPL_DECLARE > class BstTable : + public Compare, + public Vector< Element, Resize > +{ + typedef Vector BaseVector; + typedef Table BaseTable; + +public: + /** + * \brief Default constructor. + * + * Create an empty binary search table. + */ + BstTable() { } + + /** + * \brief Construct with initial value. + * + * Constructs a binary search table with an initial item. Uses the default + * constructor for initializing Value. + */ + BstTable(const Key &key) + { insert(key); } + +#if defined( BSTMAP ) + /** + * \brief Construct with initial value. + * + * Constructs a binary search table with an initial key/value pair. + */ + BstTable(const Key &key, const Value &val) + { insert(key, val); } +#endif + +#if ! defined( BSTSET ) + /** + * \brief Construct with initial value. + * + * Constructs a binary search table with an initial Element. + */ + BstTable(const Element &el) + { insert(el); } +#endif + + Element *insert(const Key &key, Element **lastFound = 0); + Element *insertMulti(const Key &key); + + bool insert(const BstTable &other); + void insertMulti(const BstTable &other); + +#if defined( BSTMAP ) + Element *insert(const Key &key, const Value &val, + Element **lastFound = 0); + Element *insertMulti(const Key &key, const Value &val ); +#endif + +#if ! defined( BSTSET ) + Element *insert(const Element &el, Element **lastFound = 0); + Element *insertMulti(const Element &el); +#endif + + Element *find(const Key &key, Element **lastFound = 0) const; + bool findMulti( const Key &key, Element *&lower, + Element *&upper ) const; + + bool remove(const Key &key); + bool remove(Element *item); + long removeMulti(const Key &key); + long removeMulti(Element *lower, Element *upper); + + /* The following provide access to the underlying insert and remove + * functions that my be hidden by the BST insert and remove. The insertDup + * and insertNew functions will never be hidden. They are provided for + * consistency. The difference between the non-shared and the shared + * tables is the documentation reference to the invoked function. */ + +#if !defined( SHARED_BST ) + /*@{*/ + + /** \brief Call the insert of the underlying vector. + * + * Provides to access to the vector insert, which may become hidden. Care + * should be taken to ensure that after the insert the ordering of + * elements is preserved. + * Invokes Vector::insert( long pos, const T &val ). + */ + void vinsert(long pos, const Element &val) + { Vector< Element, Resize >::insert( pos, &val, 1 ); } + + /** \brief Call the insert of the underlying vector. + * + * Provides to access to the vector insert, which may become hidden. Care + * should be taken to ensure that after the insert the ordering of + * elements is preserved. + * Invokes Vector::insert( long pos, const T *val, long len ). + */ + void vinsert(long pos, const Element *val, long len) + { Vector< Element, Resize >::insert( pos, val, len ); } + + /** \brief Call the insert of the underlying vector. + * + * Provides to access to the vector insert, which may become hidden. Care + * should be taken to ensure that after the insert the ordering of + * elements is preserved. + * Invokes Vector::insert( long pos, const Vector &v ). + */ + void vinsert(long pos, const BstTable &v) + { Vector< Element, Resize >::insert( pos, v.data, v.tabLen ); } + + /*@}*/ + + /*@{*/ + + /** \brief Call the remove of the underlying vector. + * + * Provides access to the vector remove, which may become hidden. + * Invokes Vector::remove( long pos ). + */ + void vremove(long pos) + { Vector< Element, Resize >::remove( pos, 1 ); } + + /** \brief Call the remove of the underlying vector. + * + * Proves access to the vector remove, which may become hidden. + * Invokes Vector::remove( long pos, long len ). + */ + void vremove(long pos, long len) + { Vector< Element, Resize >::remove( pos, len ); } + + /*@}*/ +#else /* SHARED_BST */ + /*@{*/ + + /** \brief Call the insert of the underlying vector. + * + * Provides to access to the vector insert, which may become hidden. Care + * should be taken to ensure that after the insert the ordering of + * elements is preserved. + * Invokes SVector::insert( long pos, const T &val ). + */ + void vinsert(long pos, const Element &val) + { Vector< Element, Resize >::insert( pos, &val, 1 ); } + + /** \brief Call the insert of the underlying vector. + * + * Provides to access to the vector insert, which may become hidden. Care + * should be taken to ensure that after the insert the ordering of + * elements is preserved. + * Invokes SVector::insert( long pos, const T *val, long len ). + */ + void vinsert(long pos, const Element *val, long len) + { Vector< Element, Resize >::insert( pos, val, len ); } + + /** \brief Call the insert of the underlying vector. + * + * Provides to access to the vector insert, which may become hidden. Care + * should be taken to ensure that after the insert the ordering of + * elements is preserved. + * Invokes SVector::insert( long pos, const SVector &v ). + */ + void vinsert(long pos, const BstTable &v) + { Vector< Element, Resize >::insert( pos, v.data, v.length() ); } + + /*@}*/ + + /*@{*/ + + /** \brief Call the remove of the underlying vector. + * + * Provides access to the vector remove, which may become hidden. + * Invokes SVector::remove( long pos ). + */ + void vremove(long pos) + { Vector< Element, Resize >::remove( pos, 1 ); } + + /** \brief Call the remove of the underlying vector. + * + * Proves access to the vector remove, which may become hidden. + * Invokes SVector::remove( long pos, long len ). + */ + void vremove(long pos, long len) + { Vector< Element, Resize >::remove( pos, len ); } + + /*@}*/ + +#endif /* SHARED_BST */ +}; + + +#if 0 +#if defined( SHARED_BST ) +/** + * \brief Construct a binary search table with an initial amount of + * allocation. + * + * The table is initialized to have room for allocLength elements. The + * table starts empty. + */ +template BstTable:: + BstTable( long allocLen ) +{ + /* Allocate the space if we are given a positive allocLen. */ + if ( allocLen > 0 ) { + /* Allocate the data needed. */ + STabHead *head = (STabHead*) + malloc( sizeof(STabHead) + sizeof(Element) * allocLen ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Set up the header and save the data pointer. */ + head->refCount = 1; + head->allocLen = allocLen; + head->tabLen = 0; + BaseTable::data = (Element*) (head + 1); + } +} +#else +/** + * \brief Construct a binary search table with an initial amount of + * allocation. + * + * The table is initialized to have room for allocLength elements. The + * table starts empty. + */ +template BstTable:: + BstTable( long allocLen ) +{ + /* Allocate the space if we are given a positive allocLen. */ + BaseTable::allocLen = allocLen; + if ( BaseTable::allocLen > 0 ) { + BaseTable::data = (Element*) malloc(sizeof(Element) * BaseTable::allocLen); + if ( BaseTable::data == NULL ) + throw std::bad_alloc(); + } +} + +#endif +#endif + +/** + * \brief Find the element with the given key and remove it. + * + * If multiple elements with the given key exist, then it is unspecified which + * element will be removed. + * + * \returns True if an element is found and consequently removed, false + * otherwise. + */ +template bool BstTable:: + remove(const Key &key) +{ + Element *el = find(key); + if ( el != 0 ) { + Vector< Element >::remove(el - BaseTable::data); + return true; + } + return false; +} + +/** + * \brief Remove the element pointed to by item. + * + * If item does not point to an element in the tree, then undefined behaviour + * results. If item is null, then remove has no effect. + * + * \returns True if item is not null, false otherwise. + */ +template bool BstTable:: + remove( Element *item ) +{ + if ( item != 0 ) { + Vector< Element >::remove(item - BaseTable::data); + return true; + } + return false; +} + +/** + * \brief Find and remove the entire range of elements with the given key. + * + * \returns The number of elements removed. + */ +template long BstTable:: + removeMulti(const Key &key) +{ + Element *low, *high; + if ( findMulti(key, low, high) ) { + /* Get the length of the range. */ + long num = high - low + 1; + Vector< Element >::remove(low - BaseTable::data, num); + return num; + } + + return 0; +} + +template long BstTable:: + removeMulti(Element *lower, Element *upper) +{ + /* Get the length of the range. */ + long num = upper - lower + 1; + Vector< Element >::remove(lower - BaseTable::data, num); + return num; +} + + +/** + * \brief Find a range of elements with the given key. + * + * If any elements with the given key exist then lower and upper are set to + * the low and high ends of the continous range of elements with the key. + * Lower and upper will point to the first and last elements with the key. + * + * \returns True if any elements are found, false otherwise. + */ +template bool BstTable:: + findMulti(const Key &key, Element *&low, Element *&high ) const +{ + const Element *lower, *mid, *upper; + long keyRelation; + const long tblLen = BaseTable::length(); + + if ( BaseTable::data == 0 ) + return false; + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the fd in the array. */ + return false; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = this->compare(key, GET_KEY(*mid)); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + Element *lowEnd = BaseTable::data - 1; + Element *highEnd = BaseTable::data + tblLen; + + lower = mid - 1; + while ( lower != lowEnd && + this->compare(key, GET_KEY(*lower)) == 0 ) + lower--; + + upper = mid + 1; + while ( upper != highEnd && + this->compare(key, GET_KEY(*upper)) == 0 ) + upper++; + + low = (Element*)lower + 1; + high = (Element*)upper - 1; + return true; + } + } +} + +/** + * \brief Find an element with the given key. + * + * If the find succeeds then lastFound is set to the element found. If the + * find fails then lastFound is set the location where the key would be + * inserted. If there is more than one element in the tree with the given key, + * then it is unspecified which element is returned as the match. + * + * \returns The element found on success, null on failure. + */ +template Element *BstTable:: + find( const Key &key, Element **lastFound ) const +{ + const Element *lower, *mid, *upper; + long keyRelation; + const long tblLen = BaseTable::length(); + + if ( BaseTable::data == 0 ) + return 0; + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the key. Last found gets the insert location. */ + if ( lastFound != 0 ) + *lastFound = (Element*)lower; + return 0; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = this->compare(key, GET_KEY(*mid)); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + /* Key is found. Last found gets the found record. */ + if ( lastFound != 0 ) + *lastFound = (Element*)mid; + return (Element*)mid; + } + } +} + +template Element *BstTable:: + insert(const Key &key, Element **lastFound) +{ + const Element *lower, *mid, *upper; + long keyRelation, insertPos; + const long tblLen = BaseTable::length(); + + if ( tblLen == 0 ) { + /* If the table is empty then go straight to insert. */ + lower = BaseTable::data; + goto insert; + } + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the key in the array. + * Place to insert at is lower. */ + goto insert; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = this->compare(key, GET_KEY(*mid)); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + if ( lastFound != 0 ) + *lastFound = (Element*)mid; + return 0; + } + } + +insert: + /* Get the insert pos. */ + insertPos = lower - BaseTable::data; + + /* Do the insert. After makeRawSpaceFor, lower pointer is no good. */ + BaseVector::makeRawSpaceFor(insertPos, 1); + new(BaseTable::data + insertPos) Element(key); + + /* Set lastFound */ + if ( lastFound != 0 ) + *lastFound = BaseTable::data + insertPos; + return BaseTable::data + insertPos; +} + + +template Element *BstTable:: + insertMulti(const Key &key) +{ + const Element *lower, *mid, *upper; + long keyRelation, insertPos; + const long tblLen = BaseTable::length(); + + if ( tblLen == 0 ) { + /* If the table is empty then go straight to insert. */ + lower = BaseTable::data; + goto insert; + } + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the key in the array. + * Place to insert at is lower. */ + goto insert; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = this->compare(key, GET_KEY(*mid)); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + lower = mid; + goto insert; + } + } + +insert: + /* Get the insert pos. */ + insertPos = lower - BaseTable::data; + + /* Do the insert. */ + BaseVector::makeRawSpaceFor(insertPos, 1); + new(BaseTable::data + insertPos) Element(key); + + /* Return the element inserted. */ + return BaseTable::data + insertPos; +} + +/** + * \brief Insert each element from other. + * + * Always attempts to insert all elements even if the insert of some item from + * other fails. + * + * \returns True if all items inserted successfully, false if any insert + * failed. + */ +template bool BstTable:: + insert(const BstTable &other) +{ + bool allSuccess = true; + long otherLen = other.length(); + for ( long i = 0; i < otherLen; i++ ) { + Element *el = insert( other.data[i] ); + if ( el == 0 ) + allSuccess = false; + } + return allSuccess; +} + +/** + * \brief Insert each element from other even if the elements exist already. + * + * No individual insertMulti can fail. + */ +template void BstTable:: + insertMulti(const BstTable &other) +{ + long otherLen = other.length(); + for ( long i = 0; i < otherLen; i++ ) + insertMulti( other.data[i] ); +} + +#if ! defined( BSTSET ) + +/** + * \brief Insert the given element. + * + * If the key in the given element does not already exist in the table then a + * new element is inserted. They element copy constructor is used to place the + * element into the table. If lastFound is given, it is set to the new element + * created. If the insert fails then lastFound is set to the existing element + * of the same key. + * + * \returns The new element created upon success, null upon failure. + */ +template Element *BstTable:: + insert(const Element &el, Element **lastFound ) +{ + const Element *lower, *mid, *upper; + long keyRelation, insertPos; + const long tblLen = BaseTable::length(); + + if ( tblLen == 0 ) { + /* If the table is empty then go straight to insert. */ + lower = BaseTable::data; + goto insert; + } + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the key in the array. + * Place to insert at is lower. */ + goto insert; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = this->compare(GET_KEY(el), GET_KEY(*mid)); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + if ( lastFound != 0 ) + *lastFound = (Element*)mid; + return 0; + } + } + +insert: + /* Get the insert pos. */ + insertPos = lower - BaseTable::data; + + /* Do the insert. After makeRawSpaceFor, lower pointer is no good. */ + BaseVector::makeRawSpaceFor(insertPos, 1); + new(BaseTable::data + insertPos) Element(el); + + /* Set lastFound */ + if ( lastFound != 0 ) + *lastFound = BaseTable::data + insertPos; + return BaseTable::data + insertPos; +} + +/** + * \brief Insert the given element even if it exists already. + * + * If the key in the given element exists already then the new element is + * placed next to some other element of the same key. InsertMulti cannot fail. + * The element copy constructor is used to place the element in the table. + * + * \returns The new element created. + */ +template Element *BstTable:: + insertMulti(const Element &el) +{ + const Element *lower, *mid, *upper; + long keyRelation, insertPos; + const long tblLen = BaseTable::length(); + + if ( tblLen == 0 ) { + /* If the table is empty then go straight to insert. */ + lower = BaseTable::data; + goto insert; + } + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the fd in the array. + * Place to insert at is lower. */ + goto insert; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = this->compare(GET_KEY(el), GET_KEY(*mid)); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + lower = mid; + goto insert; + } + } + +insert: + /* Get the insert pos. */ + insertPos = lower - BaseTable::data; + + /* Do the insert. */ + BaseVector::makeRawSpaceFor(insertPos, 1); + new(BaseTable::data + insertPos) Element(el); + + /* Return the element inserted. */ + return BaseTable::data + insertPos; +} +#endif + + +#if defined( BSTMAP ) + +/** + * \brief Insert the given key-value pair. + * + * If the given key does not already exist in the table then the key-value + * pair is inserted. Copy constructors are used to place the pair in the + * table. If lastFound is given, it is set to the new entry created. If the + * insert fails then lastFound is set to the existing pair of the same key. + * + * \returns The new element created upon success, null upon failure. + */ +template Element *BstTable:: + insert(const Key &key, const Value &val, Element **lastFound) +{ + const Element *lower, *mid, *upper; + long keyRelation, insertPos; + const long tblLen = BaseTable::length(); + + if ( tblLen == 0 ) { + /* If the table is empty then go straight to insert. */ + lower = BaseTable::data; + goto insert; + } + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the fd in the array. + * Place to insert at is lower. */ + goto insert; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = Compare::compare(key, mid->key); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + if ( lastFound != NULL ) + *lastFound = (Element*)mid; + return 0; + } + } + +insert: + /* Get the insert pos. */ + insertPos = lower - BaseTable::data; + + /* Do the insert. */ + BaseVector::makeRawSpaceFor(insertPos, 1); + new(BaseTable::data + insertPos) Element(key, val); + + /* Set lastFound */ + if ( lastFound != NULL ) + *lastFound = BaseTable::data + insertPos; + return BaseTable::data + insertPos; +} + + +/** + * \brief Insert the given key-value pair even if the key exists already. + * + * If the key exists already then the key-value pair is placed next to some + * other pair of the same key. InsertMulti cannot fail. Copy constructors are + * used to place the pair in the table. + * + * \returns The new element created. + */ +template Element *BstTable:: + insertMulti(const Key &key, const Value &val) +{ + const Element *lower, *mid, *upper; + long keyRelation, insertPos; + const long tblLen = BaseTable::length(); + + if ( tblLen == 0 ) { + /* If the table is empty then go straight to insert. */ + lower = BaseTable::data; + goto insert; + } + + lower = BaseTable::data; + upper = BaseTable::data + tblLen - 1; + while ( true ) { + if ( upper < lower ) { + /* Did not find the key in the array. + * Place to insert at is lower. */ + goto insert; + } + + mid = lower + ((upper-lower)>>1); + keyRelation = Compare::compare(key, mid->key); + + if ( keyRelation < 0 ) + upper = mid - 1; + else if ( keyRelation > 0 ) + lower = mid + 1; + else { + lower = mid; + goto insert; + } + } + +insert: + /* Get the insert pos. */ + insertPos = lower - BaseTable::data; + + /* Do the insert. */ + BaseVector::makeRawSpaceFor(insertPos, 1); + new(BaseTable::data + insertPos) Element(key, val); + + /* Return the element inserted. */ + return BaseTable::data + insertPos; +} + +#endif + +#ifdef AAPL_NAMESPACE +} +#endif diff --git a/src/aapl/bstmap.h b/src/aapl/bstmap.h new file mode 100644 index 00000000..4a5f5310 --- /dev/null +++ b/src/aapl/bstmap.h @@ -0,0 +1,114 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_BSTMAP_H +#define _AAPL_BSTMAP_H + +#include "compare.h" +#include "vector.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \brief Element for BstMap. + * + * Stores the key and value pair. + */ +template struct BstMapEl +{ + BstMapEl() {} + BstMapEl(const Key &key) : key(key) {} + BstMapEl(const Key &key, const Value &val) : key(key), value(val) {} + + /** \brief The key */ + Key key; + + /** \brief The value. */ + Value value; +}; + +#ifdef AAPL_NAMESPACE +} +#endif + +/** + * \addtogroup bst + * @{ + */ + +/** + * \class BstMap + * \brief Binary search table for key and value pairs. + * + * BstMap stores key and value pairs in each element. The key and value can be + * any type. A compare class for the key must be supplied. + */ + +/*@}*/ + +#define BST_TEMPL_DECLARE class Key, class Value, \ + class Compare = CmpOrd, class Resize = ResizeExpn +#define BST_TEMPL_DEF class Key, class Value, class Compare, class Resize +#define BST_TEMPL_USE Key, Value, Compare, Resize +#define GET_KEY(el) ((el).key) +#define BstTable BstMap +#define Element BstMapEl +#define BSTMAP + +#include "bstcommon.h" + +#undef BST_TEMPL_DECLARE +#undef BST_TEMPL_DEF +#undef BST_TEMPL_USE +#undef GET_KEY +#undef BstTable +#undef Element +#undef BSTMAP + +/** + * \fn BstMap::insert(const Key &key, BstMapEl **lastFound) + * \brief Insert the given key. + * + * If the given key does not already exist in the table then a new element + * having key is inserted. They key copy constructor and value default + * constructor are used to place the pair in the table. If lastFound is given, + * it is set to the new entry created. If the insert fails then lastFound is + * set to the existing pair of the same key. + * + * \returns The new element created upon success, null upon failure. + */ + +/** + * \fn BstMap::insertMulti(const Key &key) + * \brief Insert the given key even if it exists already. + * + * If the key exists already then the new element having key is placed next + * to some other pair of the same key. InsertMulti cannot fail. The key copy + * constructor and the value default constructor are used to place the pair in + * the table. + * + * \returns The new element created. + */ + +#endif /* _AAPL_BSTMAP_H */ diff --git a/src/aapl/bstset.h b/src/aapl/bstset.h new file mode 100644 index 00000000..14e0375a --- /dev/null +++ b/src/aapl/bstset.h @@ -0,0 +1,87 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_BSTSET_H +#define _AAPL_BSTSET_H + +/** + * \addtogroup bst + * @{ + */ + +/** + * \class BstSet + * \brief Binary search table for types that are the key. + * + * BstSet is suitable for types that comprise the entire key. Rather than look + * into the element to retrieve the key, the element is the key. A class that + * contains a comparison routine for the key must be given. + */ + +/*@}*/ + +#include "compare.h" +#include "vector.h" + +#define BST_TEMPL_DECLARE class Key, class Compare = CmpOrd, \ + class Resize = ResizeExpn +#define BST_TEMPL_DEF class Key, class Compare, class Resize +#define BST_TEMPL_USE Key, Compare, Resize +#define GET_KEY(el) (el) +#define BstTable BstSet +#define Element Key +#define BSTSET + +#include "bstcommon.h" + +#undef BST_TEMPL_DECLARE +#undef BST_TEMPL_DEF +#undef BST_TEMPL_USE +#undef GET_KEY +#undef BstTable +#undef Element +#undef BSTSET + +/** + * \fn BstSet::insert(const Key &key, Key **lastFound) + * \brief Insert the given key. + * + * If the given key does not already exist in the table then it is inserted. + * The key's copy constructor is used to place the item in the table. If + * lastFound is given, it is set to the new entry created. If the insert fails + * then lastFound is set to the existing key of the same value. + * + * \returns The new element created upon success, null upon failure. + */ + +/** + * \fn BstSet::insertMulti(const Key &key) + * \brief Insert the given key even if it exists already. + * + * If the key exists already then it is placed next to some other key of the + * same value. InsertMulti cannot fail. The key's copy constructor is used to + * place the item in the table. + * + * \returns The new element created. + */ + +#endif /* _AAPL_BSTSET_H */ diff --git a/src/aapl/bsttable.h b/src/aapl/bsttable.h new file mode 100644 index 00000000..cb9a056a --- /dev/null +++ b/src/aapl/bsttable.h @@ -0,0 +1,85 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_BSTTABLE_H +#define _AAPL_BSTTABLE_H + +#include "compare.h" +#include "vector.h" + +/** + * \addtogroup bst + * @{ + */ + +/** + * \class BstTable + * \brief Binary search table for structures that contain a key. + * + * This is the basic binary search table. It can be used to contain a + * structure that has a key and possibly some data. The key should be a member + * of the element class and accessible with getKey(). A class containing the + * compare routine must be supplied. + */ + +/*@}*/ + +#define BST_TEMPL_DECLARE class Element, class Key, \ + class Compare = CmpOrd, class Resize = ResizeExpn +#define BST_TEMPL_DEF class Element, class Key, class Compare, class Resize +#define BST_TEMPL_USE Element, Key, Compare, Resize +#define GET_KEY(el) ((el).getKey()) +#define BSTTABLE + +#include "bstcommon.h" + +#undef BST_TEMPL_DECLARE +#undef BST_TEMPL_DEF +#undef BST_TEMPL_USE +#undef GET_KEY +#undef BSTTABLE + +/** + * \fn BstTable::insert(const Key &key, Element **lastFound) + * \brief Insert a new element with the given key. + * + * If the given key does not already exist in the table a new element is + * inserted with the given key. A constructor taking only const Key& is used + * to initialize the new element. If lastFound is given, it is set to the new + * element created. If the insert fails then lastFound is set to the existing + * element with the same key. + * + * \returns The new element created upon success, null upon failure. + */ + +/** + * \fn BstTable::insertMulti(const Key &key) + * \brief Insert a new element even if the key exists already. + * + * If the key exists already then the new element is placed next to some + * element with the same key. InsertMulti cannot fail. A constructor taking + * only const Key& is used to initialize the new element. + * + * \returns The new element created. + */ + +#endif /* _AAPL_BSTTABLE_H */ diff --git a/src/aapl/bubblesort.h b/src/aapl/bubblesort.h new file mode 100644 index 00000000..42482991 --- /dev/null +++ b/src/aapl/bubblesort.h @@ -0,0 +1,95 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_BUBBLESORT_H +#define _AAPL_BUBBLESORT_H + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup sort + * @{ + */ + +/** + * \class BubbleSort + * \brief Bubble sort an array of data. + * + * BubbleSort can be used to sort any array of objects of type T provided a + * compare class is given. BubbleSort is in-place. It does not require any + * temporary storage. + * + * Objects are not made aware that they are being moved around in memory. + * Assignment operators, constructors and destructors are never invoked by the + * sort. + * + * BubbleSort runs in O(n^2) time. It is most useful when sorting arrays that + * are nearly sorted. It is best when neighbouring pairs are out of place. + * BubbleSort is a stable sort, meaning that objects with the same key have + * their relative ordering preserved. + */ + +/*@}*/ + +/* BubbleSort. */ +template class BubbleSort + : public Compare +{ +public: + /* Sorting interface routine. */ + void sort(T *data, long len); +}; + + +/** + * \brief Bubble sort an array of data. + */ +template void BubbleSort:: + sort(T *data, long len) +{ + bool changed = true; + for ( long pass = 1; changed && pass < len; pass ++ ) { + changed = false; + for ( long i = 0; i < len-pass; i++ ) { + /* Do we swap pos with the next one? */ + if ( this->compare( data[i], data[i+1] ) > 0 ) { + char tmp[sizeof(T)]; + + /* Swap the two items. */ + memcpy( tmp, data+i, sizeof(T) ); + memcpy( data+i, data+i+1, sizeof(T) ); + memcpy( data+i+1, tmp, sizeof(T) ); + + /* Note that we made a change. */ + changed = true; + } + } + } +} + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_BUBBLESORT_H */ diff --git a/src/aapl/buffer.h b/src/aapl/buffer.h new file mode 100644 index 00000000..2aef08ca --- /dev/null +++ b/src/aapl/buffer.h @@ -0,0 +1,60 @@ +/* + * Copyright 2003 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_BUFFER_H +#define _AAPL_BUFFER_H + +/* Buffer is an automatically grown buffer for collecting tokens. Always reuses + * space. Never down resizes. Clear is a cheap operation. */ +struct Buffer +{ + static const int BUFFER_INITIAL_SIZE = 4096; + + Buffer() + { + data = (char*) malloc( BUFFER_INITIAL_SIZE ); + allocated = BUFFER_INITIAL_SIZE; + length = 0; + } + + ~Buffer() + { + free(data); + } + + void append( char p ) + { + if ( length == allocated ) { + allocated *= 2; + data = (char*) realloc( data, allocated ); + } + data[length++] = p; + } + + void clear() { length = 0; } + + char *data; + int allocated; + int length; +}; + +#endif diff --git a/src/aapl/compare.h b/src/aapl/compare.h new file mode 100644 index 00000000..bbfa2136 --- /dev/null +++ b/src/aapl/compare.h @@ -0,0 +1,269 @@ +/* + * Copyright 2001 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_COMPARE_H +#define _AAPL_COMPARE_H + +#include +#include +#include "table.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \defgroup compare Compare + * \brief Basic compare clases. + * + * Compare classes are used by data structures that need to know the relative + * ordering of elemets. To become a compare class, a class must imlement a + * routine long compare(const T &key1, const T &key2) that behaves just like + * strcmp. + * + * Compare classes are passed to the template data structure as a template + * parameter and are inherited. In most cases the compare routine will base + * the key comparision only on the two keys and the compare routine can + * therefore be static. Though sometimes it is useful to include data in the + * compare class and use this data in the comparison. For example the compare + * class may contain a pointer to some other data structure to which the + * comparison is delegated. + * + * @{ + */ + +/** + * \brief Compare two null terminated character sequences. + * + * This comparision class is a wrapper for strcmp. + */ +struct CmpStr +{ + /** + * \brief Compare two null terminated string types. + */ + static inline long compare(const char *k1, const char *k2) + { return strcmp(k1, k2); } +}; + +struct CmpString +{ + static inline long compare(const std::string &k1, const std::string &k2) + { return k1.compare( k2 ); } +}; + + +/** + * \brief Compare a type for which < and > are implemented. + * + * CmpOrd is suitable for simple types such as integers and pointers that by + * default have the less-than and greater-than operators defined. + */ +template struct CmpOrd +{ + /** + * \brief Compare two ordinal types. + * + * This compare routine copies its arguements in by value. + */ + static inline long compare(const T k1, const T k2) + { + if (k1 < k2) + return -1; + else if (k1 > k2) + return 1; + else + return 0; + } +}; + +/** + * \brief Compare two tables of type T + * + * Table comparison is useful for keying a data structure on a vector or + * binary search table. T is the element type stored in the table. + * CompareT is the comparison structure used to compare the individual values + * in the table. + */ +template < class T, class CompareT = CmpOrd > struct CmpTable + : public CompareT +{ + /** + * \brief Compare two tables storing type T. + */ + static inline long compare(const Table &t1, const Table &t2) + { + if ( t1.tabLen < t2.tabLen ) + return -1; + else if ( t1.tabLen > t2.tabLen ) + return 1; + else + { + T *i1 = t1.data, *i2 = t2.data; + long len = t1.tabLen, cmpResult; + for ( long pos = 0; pos < len; + pos += 1, i1 += 1, i2 += 1 ) + { + cmpResult = CompareT::compare(*i1, *i2); + if ( cmpResult != 0 ) + return cmpResult; + } + return 0; + } + } +}; + +/** + * \brief Compare two tables of type T -- non-static version. + * + * CmpTableNs is identical to CmpTable, however the compare routine is + * non-static. If the CompareT class contains a non-static compare, then this + * version must be used because a static member cannot invoke a non-static + * member. + * + * Table comparison is useful for keying a data structure on a vector or binary + * search table. T is the element type stored in the table. CompareT + * is the comparison structure used to compare the individual values in the + * table. + */ +template < class T, class CompareT = CmpOrd > struct CmpTableNs + : public CompareT +{ + /** + * \brief Compare two tables storing type T. + */ + inline long compare(const Table &t1, const Table &t2) + { + if ( t1.tabLen < t2.tabLen ) + return -1; + else if ( t1.tabLen > t2.tabLen ) + return 1; + else + { + T *i1 = t1.data, *i2 = t2.data; + long len = t1.tabLen, cmpResult; + for ( long pos = 0; pos < len; + pos += 1, i1 += 1, i2 += 1 ) + { + cmpResult = CompareT::compare(*i1, *i2); + if ( cmpResult != 0 ) + return cmpResult; + } + return 0; + } + } +}; + +/** + * \brief Compare two implicitly shared tables of type T + * + * This table comparison is for data structures based on implicitly + * shared tables. + * + * Table comparison is useful for keying a data structure on a vector or + * binary search table. T is the element type stored in the table. + * CompareT is the comparison structure used to compare the individual values + * in the table. + */ +template < class T, class CompareT = CmpOrd > struct CmpSTable : public CompareT +{ + /** + * \brief Compare two tables storing type T. + */ + static inline long compare(const STable &t1, const STable &t2) + { + long t1Length = t1.length(); + long t2Length = t2.length(); + + /* Compare lengths. */ + if ( t1Length < t2Length ) + return -1; + else if ( t1Length > t2Length ) + return 1; + else { + /* Compare the table data. */ + T *i1 = t1.data, *i2 = t2.data; + for ( long pos = 0; pos < t1Length; + pos += 1, i1 += 1, i2 += 1 ) + { + long cmpResult = CompareT::compare(*i1, *i2); + if ( cmpResult != 0 ) + return cmpResult; + } + return 0; + } + } +}; + +/** + * \brief Compare two implicitly shared tables of type T -- non-static + * version. + * + * This is a non-static table comparison for data structures based on + * implicitly shared tables. If the CompareT class contains a non-static + * compare, then this version must be used because a static member cannot + * invoke a non-static member. + * + * Table comparison is useful for keying a data structure on a vector or + * binary search table. T is the element type stored in the table. + * CompareT is the comparison structure used to compare the individual values + * in the table. + */ +template < class T, class CompareT = CmpOrd > struct CmpSTableNs + : public CompareT +{ + /** + * \brief Compare two tables storing type T. + */ + inline long compare(const STable &t1, const STable &t2) + { + long t1Length = t1.length(); + long t2Length = t2.length(); + + /* Compare lengths. */ + if ( t1Length < t2Length ) + return -1; + else if ( t1Length > t2Length ) + return 1; + else { + /* Compare the table data. */ + T *i1 = t1.data, *i2 = t2.data; + for ( long pos = 0; pos < t1Length; + pos += 1, i1 += 1, i2 += 1 ) + { + long cmpResult = CompareT::compare(*i1, *i2); + if ( cmpResult != 0 ) + return cmpResult; + } + return 0; + } + } +}; + + +/*@}*/ + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_COMPARE_H */ diff --git a/src/aapl/dlcommon.h b/src/aapl/dlcommon.h new file mode 100644 index 00000000..92d45259 --- /dev/null +++ b/src/aapl/dlcommon.h @@ -0,0 +1,791 @@ +/* + * Copyright 2001 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This header is not wrapped in ifndef becuase it is not intended to + * be included by the user. */ + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +#if defined( DOUBLELIST_VALUE ) +/** + * \brief Double list element for DListVal. + * + * DListValEl stores the type T of DListVal by value. + */ +template struct DListValEl +{ + /** + * \brief Construct a DListValEl with a given value. + * + * The only constructor available initializes the value element. This + * enforces that DListVal elements are never created without having their + * value intialzed by the user. T's copy constructor is used to copy the + * value in. + */ + DListValEl( const T &val ) : value(val) { } + + /** + * \brief Value stored by the list element. + * + * Value is always copied into new list elements using the copy + * constructor. + */ + T value; + + /** + * \brief List previous pointer. + * + * Points to the previous item in the list. If this is the first item in + * the list, then prev is NULL. If this element is not in a list then + * prev is undefined. + */ + DListValEl *prev; + + /** + * \brief List next pointer. + * + * Points to the next item in the list. If this is the list item in the + * list, then next is NULL. If this element is not in a list then next is + * undefined. + */ + DListValEl *next; +}; +#else + +#ifndef __AAPL_DOUBLE_LIST_EL +#define __AAPL_DOUBLE_LIST_EL +/** + * \brief Double list element properties. + * + * This class can be inherited to make a class suitable to be a double list + * element. It simply provides the next and previous pointers. An alternative + * is to put the next and previous pointers in the class directly. + */ +template struct DListEl +{ + /** + * \brief List previous pointer. + * + * Points to the previous item in the list. If this is the first item in + * the list, then prev is NULL. If this element is not in a list then + * prev is undefined. + */ + Element *prev; + + /** + * \brief List next pointer. + * + * Points to the next item in the list. If this is the list item in the + * list, then next is NULL. If this element is not in a list then next is + * undefined. + */ + Element *next; +}; +#endif /* __AAPL_DOUBLE_LIST_EL */ + +#endif + +/* Doubly Linked List */ +template class DList +{ +public: + /** \brief Initialize an empty list. */ + DList() : head(0), tail(0), listLen(0) {} + + /** + * \brief Perform a deep copy of the list. + * + * The elements of the other list are duplicated and put into this list. + * Elements are copied using the copy constructor. + */ + DList(const DList &other); + +#ifdef DOUBLELIST_VALUE + /** + * \brief Clear the double list contents. + * + * All elements are deleted. + */ + ~DList() { empty(); } + + /** + * \brief Assign another list into this list using a deep copy. + * + * The elements of the other list are duplicated and put into this list. + * Each list item is created using the copy constructor. If this list + * contains any elements before the copy, they are deleted first. + * + * \returns A reference to this. + */ + DList &operator=(const DList &other); + + /** + * \brief Transfer the contents of another list into this list. + * + * The elements of the other list moved in. The other list will be empty + * afterwards. If this list contains any elements before the copy, then + * they are deleted. + */ + void transfer(DList &other); +#else + /** + * \brief Abandon all elements in the list. + * + * List elements are not deleted. + */ + ~DList() {} + + /** + * \brief Perform a deep copy of the list. + * + * The elements of the other list are duplicated and put into this list. + * Each list item is created using the copy constructor. If this list + * contains any elements before the copy, they are abandoned. + * + * \returns A reference to this. + */ + DList &operator=(const DList &other); + + /** + * \brief Transfer the contents of another list into this list. + * + * The elements of the other list moved in. The other list will be empty + * afterwards. If this list contains any elements before the copy, they + * are abandoned. + */ + void transfer(DList &other); +#endif + + +#ifdef DOUBLELIST_VALUE + /** + * \brief Make a new element and prepend it to the front of the list. + * + * The item is copied into the new element using the copy constructor. + * Equivalent to list.addBefore(list.head, item). + */ + void prepend(const T &item); + + /** + * \brief Make a new element and append it to the end of the list. + * + * The item is copied into the new element using the copy constructor. + * Equivalent to list.addAfter(list.tail, item). + */ + void append(const T &item); + + /** + * \brief Make a new element and insert it immediately after an element in + * the list. + * + * The item is copied into the new element using the copy constructor. If + * prev_el is NULL then the new element is prepended to the front of the + * list. If prev_el is not already in the list then undefined behaviour + * results. Equivalent to list.addAfter(prev_el, new DListValEl(item)). + */ + void addAfter(Element *prev_el, const T &item); + + /** + * \brief Make a new element and insert it immediately before an element + * in the list. + * + * The item is copied into the new element using the copy construcotor. If + * next_el is NULL then the new element is appended to the end of the + * list. If next_el is not already in the list then undefined behaviour + * results. Equivalent to list.addBefore(next_el, new DListValEl(item)). + */ + void addBefore(Element *next_el, const T &item); +#endif + + /** + * \brief Prepend a single element to the front of the list. + * + * If new_el is already an element of some list, then undefined behaviour + * results. Equivalent to list.addBefore(list.head, new_el). + */ + void prepend(Element *new_el) { addBefore(head, new_el); } + + /** + * \brief Append a single element to the end of the list. + * + * If new_el is alreay an element of some list, then undefined behaviour + * results. Equivalent to list.addAfter(list.tail, new_el). + */ + void append(Element *new_el) { addAfter(tail, new_el); } + + /** + * \brief Prepend an entire list to the beginning of this list. + * + * All items are moved, not copied. Afterwards, the other list is emtpy. + * All items are prepended at once, so this is an O(1) operation. + * Equivalent to list.addBefore(list.head, dl). + */ + void prepend(DList &dl) { addBefore(head, dl); } + + /** + * \brief Append an entire list to the end of the list. + * + * All items are moved, not copied. Afterwards, the other list is empty. + * All items are appened at once, so this is an O(1) operation. + * Equivalent to list.addAfter(list.tail, dl). + */ + void append(DList &dl) { addAfter(tail, dl); } + + void addAfter(Element *prev_el, Element *new_el); + void addBefore(Element *next_el, Element *new_el); + + void addAfter(Element *prev_el, DList &dl); + void addBefore(Element *next_el, DList &dl); + + /** + * \brief Detach the head of the list + * + * The element detached is not deleted. If there is no head of the list + * (the list is empty) then undefined behaviour results. Equivalent to + * list.detach(list.head). + * + * \returns The element detached. + */ + Element *detachFirst() { return detach(head); } + + /** + * \brief Detach the tail of the list + * + * The element detached is not deleted. If there is no tail of the list + * (the list is empty) then undefined behaviour results. Equivalent to + * list.detach(list.tail). + * + * \returns The element detached. + */ + Element *detachLast() { return detach(tail); } + + /* Detaches an element from the list. Does not free any memory. */ + Element *detach(Element *el); + + /** + * \brief Detach and delete the first element in the list. + * + * If there is no first element (the list is empty) then undefined + * behaviour results. Equivalent to delete list.detach(list.head); + */ + void removeFirst() { delete detach( head ); } + + /** + * \brief Detach and delete the last element in the list. + * + * If there is no last element (the list is emtpy) then undefined + * behaviour results. Equivalent to delete list.detach(list.tail); + */ + void removeLast() { delete detach( tail ); } + + /** + * \brief Detach and delete an element from the list. + * + * If the element is not in the list, then undefined behaviour results. + * Equivalent to delete list.detach(el); + */ + void remove(Element *el) { delete detach( el ); } + + void empty(); + void abandon(); + + /** \brief The number of elements in the list. */ + long length() const { return listLen; } + + /** \brief Head and tail of the linked list. */ + Element *head, *tail; + + /** \brief The number of element in the list. */ + long listLen; + + /* Convenience access. */ + long size() const { return listLen; } + + /* Forward this so a ref can be used. */ + struct Iter; + + /* Class for setting the iterator. */ + struct IterFirst { IterFirst( const DList &l ) : l(l) { } const DList &l; }; + struct IterLast { IterLast( const DList &l ) : l(l) { } const DList &l; }; + struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; + struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; + + /** + * \brief Double List Iterator. + * \ingroup iterators + */ + struct Iter + { + /* Default construct. */ + Iter() : ptr(0) { } + + /* Construct from a double list. */ + Iter( const DList &dl ) : ptr(dl.head) { } + Iter( Element *el ) : ptr(el) { } + Iter( const IterFirst &dlf ) : ptr(dlf.l.head) { } + Iter( const IterLast &dll ) : ptr(dll.l.tail) { } + Iter( const IterNext &dln ) : ptr(dln.i.ptr->BASE_EL(next)) { } + Iter( const IterPrev &dlp ) : ptr(dlp.i.ptr->BASE_EL(prev)) { } + + /* Assign from a double list. */ + Iter &operator=( const DList &dl ) { ptr = dl.head; return *this; } + Iter &operator=( Element *el ) { ptr = el; return *this; } + Iter &operator=( const IterFirst &af ) { ptr = af.l.head; return *this; } + Iter &operator=( const IterLast &al ) { ptr = al.l.tail; return *this; } + Iter &operator=( const IterNext &an ) { ptr = an.i.ptr->BASE_EL(next); return *this; } + Iter &operator=( const IterPrev &ap ) { ptr = ap.i.ptr->BASE_EL(prev); return *this; } + + /** \brief Less than end? */ + bool lte() const { return ptr != 0; } + + /** \brief At end? */ + bool end() const { return ptr == 0; } + + /** \brief Greater than beginning? */ + bool gtb() const { return ptr != 0; } + + /** \brief At beginning? */ + bool beg() const { return ptr == 0; } + + /** \brief At first element? */ + bool first() const { return ptr && ptr->BASE_EL(prev) == 0; } + + /** \brief At last element? */ + bool last() const { return ptr && ptr->BASE_EL(next) == 0; } + + /** \brief Implicit cast to Element*. */ + operator Element*() const { return ptr; } + + /** \brief Dereference operator returns Element&. */ + Element &operator *() const { return *ptr; } + + /** \brief Arrow operator returns Element*. */ + Element *operator->() const { return ptr; } + + /** \brief Move to next item. */ + inline Element *operator++() { return ptr = ptr->BASE_EL(next); } + + /** \brief Move to next item. */ + inline Element *increment() { return ptr = ptr->BASE_EL(next); } + + /** \brief Move to next item. */ + inline Element *operator++(int); + + /** \brief Move to previous item. */ + inline Element *operator--() { return ptr = ptr->BASE_EL(prev); } + + /** \brief Move to previous item. */ + inline Element *decrement() { return ptr = ptr->BASE_EL(prev); } + + /** \brief Move to previous item. */ + inline Element *operator--(int); + + /** \brief Return the next item. Does not modify this. */ + inline IterNext next() const { return IterNext(*this); } + + /** \brief Return the prev item. Does not modify this. */ + inline IterPrev prev() const { return IterPrev(*this); } + + /** \brief The iterator is simply a pointer. */ + Element *ptr; + }; + + /** \brief Return first element. */ + IterFirst first() { return IterFirst(*this); } + + /** \brief Return last element. */ + IterLast last() { return IterLast(*this); } +}; + +/* Copy constructor, does a deep copy of other. */ +template DList:: + DList(const DList &other) : + head(0), tail(0), listLen(0) +{ + Element *el = other.head; + while( el != 0 ) { + append( new Element(*el) ); + el = el->BASE_EL(next); + } +} + +#ifdef DOUBLELIST_VALUE + +/* Assignement operator does deep copy. */ +template DList &DList:: + operator=(const DList &other) +{ + /* Free the old list. The value list assumes items were allocated on the + * heap by itself. */ + empty(); + + Element *el = other.head; + while( el != 0 ) { + append( new Element(*el) ); + el = el->BASE_EL(next); + } + return *this; +} + +template void DList:: + transfer(DList &other) +{ + /* Free the old list. The value list assumes items were allocated on the + * heap by itself. */ + empty(); + + head = other.head; + tail = other.tail; + listLen = other.listLen; + + other.abandon(); +} + +#else + +/* Assignement operator does deep copy. */ +template DList &DList:: + operator=(const DList &other) +{ + Element *el = other.head; + while( el != 0 ) { + append( new Element(*el) ); + el = el->BASE_EL(next); + } + return *this; +} + +template void DList:: + transfer(DList &other) +{ + head = other.head; + tail = other.tail; + listLen = other.listLen; + + other.abandon(); +} + +#endif + +#ifdef DOUBLELIST_VALUE + +/* Prepend a new item. Inlining this bloats the caller with new overhead. */ +template void DList:: + prepend(const T &item) +{ + addBefore(head, new Element(item)); +} + +/* Append a new item. Inlining this bloats the caller with the new overhead. */ +template void DList:: + append(const T &item) +{ + addAfter(tail, new Element(item)); +} + +/* Add a new item after a prev element. Inlining this bloats the caller with + * the new overhead. */ +template void DList:: + addAfter(Element *prev_el, const T &item) +{ + addAfter(prev_el, new Element(item)); +} + +/* Add a new item before a next element. Inlining this bloats the caller with + * the new overhead. */ +template void DList:: + addBefore(Element *next_el, const T &item) +{ + addBefore(next_el, new Element(item)); +} + +#endif + +/* + * The larger iterator operators. + */ + +/* Postfix ++ */ +template Element *DList::Iter:: + operator++(int) +{ + Element *rtn = ptr; + ptr = ptr->BASE_EL(next); + return rtn; +} + +/* Postfix -- */ +template Element *DList::Iter:: + operator--(int) +{ + Element *rtn = ptr; + ptr = ptr->BASE_EL(prev); + return rtn; +} + +/** + * \brief Insert an element immediately after an element in the list. + * + * If prev_el is NULL then new_el is prepended to the front of the list. If + * prev_el is not in the list or if new_el is already in a list, then + * undefined behaviour results. + */ +template void DList:: + addAfter(Element *prev_el, Element *new_el) +{ + /* Set the previous pointer of new_el to prev_el. We do + * this regardless of the state of the list. */ + new_el->BASE_EL(prev) = prev_el; + + /* Set forward pointers. */ + if (prev_el == 0) { + /* There was no prev_el, we are inserting at the head. */ + new_el->BASE_EL(next) = head; + head = new_el; + } + else { + /* There was a prev_el, we can access previous next. */ + new_el->BASE_EL(next) = prev_el->BASE_EL(next); + prev_el->BASE_EL(next) = new_el; + } + + /* Set reverse pointers. */ + if (new_el->BASE_EL(next) == 0) { + /* There is no next element. Set the tail pointer. */ + tail = new_el; + } + else { + /* There is a next element. Set it's prev pointer. */ + new_el->BASE_EL(next)->BASE_EL(prev) = new_el; + } + + /* Update list length. */ + listLen++; +} + +/** + * \brief Insert an element immediatly before an element in the list. + * + * If next_el is NULL then new_el is appended to the end of the list. If + * next_el is not in the list or if new_el is already in a list, then + * undefined behaviour results. + */ +template void DList:: + addBefore(Element *next_el, Element *new_el) +{ + /* Set the next pointer of the new element to next_el. We do + * this regardless of the state of the list. */ + new_el->BASE_EL(next) = next_el; + + /* Set reverse pointers. */ + if (next_el == 0) { + /* There is no next elememnt. We are inserting at the tail. */ + new_el->BASE_EL(prev) = tail; + tail = new_el; + } + else { + /* There is a next element and we can access next's previous. */ + new_el->BASE_EL(prev) = next_el->BASE_EL(prev); + next_el->BASE_EL(prev) = new_el; + } + + /* Set forward pointers. */ + if (new_el->BASE_EL(prev) == 0) { + /* There is no previous element. Set the head pointer.*/ + head = new_el; + } + else { + /* There is a previous element, set it's next pointer to new_el. */ + new_el->BASE_EL(prev)->BASE_EL(next) = new_el; + } + + /* Update list length. */ + listLen++; +} + +/** + * \brief Insert an entire list immediatly after an element in this list. + * + * Elements are moved, not copied. Afterwards, the other list is empty. If + * prev_el is NULL then the elements are prepended to the front of the list. + * If prev_el is not in the list then undefined behaviour results. All + * elements are inserted into the list at once, so this is an O(1) operation. + */ +template void DList:: + addAfter( Element *prev_el, DList &dl ) +{ + /* Do not bother if dl has no elements. */ + if ( dl.listLen == 0 ) + return; + + /* Set the previous pointer of dl.head to prev_el. We do + * this regardless of the state of the list. */ + dl.head->BASE_EL(prev) = prev_el; + + /* Set forward pointers. */ + if (prev_el == 0) { + /* There was no prev_el, we are inserting at the head. */ + dl.tail->BASE_EL(next) = head; + head = dl.head; + } + else { + /* There was a prev_el, we can access previous next. */ + dl.tail->BASE_EL(next) = prev_el->BASE_EL(next); + prev_el->BASE_EL(next) = dl.head; + } + + /* Set reverse pointers. */ + if (dl.tail->BASE_EL(next) == 0) { + /* There is no next element. Set the tail pointer. */ + tail = dl.tail; + } + else { + /* There is a next element. Set it's prev pointer. */ + dl.tail->BASE_EL(next)->BASE_EL(prev) = dl.tail; + } + + /* Update the list length. */ + listLen += dl.listLen; + + /* Empty out dl. */ + dl.head = dl.tail = 0; + dl.listLen = 0; +} + +/** + * \brief Insert an entire list immediately before an element in this list. + * + * Elements are moved, not copied. Afterwards, the other list is empty. If + * next_el is NULL then the elements are appended to the end of the list. If + * next_el is not in the list then undefined behaviour results. All elements + * are inserted at once, so this is an O(1) operation. + */ +template void DList:: + addBefore( Element *next_el, DList &dl ) +{ + /* Do not bother if dl has no elements. */ + if ( dl.listLen == 0 ) + return; + + /* Set the next pointer of dl.tail to next_el. We do + * this regardless of the state of the list. */ + dl.tail->BASE_EL(next) = next_el; + + /* Set reverse pointers. */ + if (next_el == 0) { + /* There is no next elememnt. We are inserting at the tail. */ + dl.head->BASE_EL(prev) = tail; + tail = dl.tail; + } + else { + /* There is a next element and we can access next's previous. */ + dl.head->BASE_EL(prev) = next_el->BASE_EL(prev); + next_el->BASE_EL(prev) = dl.tail; + } + + /* Set forward pointers. */ + if (dl.head->BASE_EL(prev) == 0) { + /* There is no previous element. Set the head pointer.*/ + head = dl.head; + } + else { + /* There is a previous element, set it's next pointer to new_el. */ + dl.head->BASE_EL(prev)->BASE_EL(next) = dl.head; + } + + /* Update list length. */ + listLen += dl.listLen; + + /* Empty out dl. */ + dl.head = dl.tail = 0; + dl.listLen = 0; +} + + +/** + * \brief Detach an element from the list. + * + * The element is not deleted. If the element is not in the list, then + * undefined behaviour results. + * + * \returns The element detached. + */ +template Element *DList:: + detach(Element *el) +{ + /* Set forward pointers to skip over el. */ + if (el->BASE_EL(prev) == 0) + head = el->BASE_EL(next); + else { + el->BASE_EL(prev)->BASE_EL(next) = + el->BASE_EL(next); + } + + /* Set reverse pointers to skip over el. */ + if (el->BASE_EL(next) == 0) + tail = el->BASE_EL(prev); + else { + el->BASE_EL(next)->BASE_EL(prev) = + el->BASE_EL(prev); + } + + /* Update List length and return element we detached. */ + listLen--; + return el; +} + +/** + * \brief Clear the list by deleting all elements. + * + * Each item in the list is deleted. The list is reset to its initial state. + */ +template void DList::empty() +{ + Element *nextToGo = 0, *cur = head; + + while (cur != 0) + { + nextToGo = cur->BASE_EL(next); + delete cur; + cur = nextToGo; + } + head = tail = 0; + listLen = 0; +} + +/** + * \brief Clear the list by forgetting all elements. + * + * All elements are abandoned, not deleted. The list is reset to it's initial + * state. + */ +template void DList::abandon() +{ + head = tail = 0; + listLen = 0; +} + +#ifdef AAPL_NAMESPACE +} +#endif diff --git a/src/aapl/dlist.h b/src/aapl/dlist.h new file mode 100644 index 00000000..9663caca --- /dev/null +++ b/src/aapl/dlist.h @@ -0,0 +1,65 @@ +/* + * Copyright 2001 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_DLIST_H +#define _AAPL_DLIST_H + +#define BASE_EL(name) name +#define DLMEL_TEMPDEF class Element +#define DLMEL_TEMPUSE Element +#define DList DList + +/** + * \addtogroup dlist + * @{ + */ + +/** + * \class DList + * \brief Basic doubly linked list. + * + * DList is the standard by-structure list type. This class requires the + * programmer to declare a list element type that has the necessary next and + * previous pointers in it. This can be achieved by inheriting from the + * DListEl class or by simply adding next and previous pointers directly into + * the list element class. + * + * DList does not assume ownership of elements in the list. If the elements + * are known to reside on the heap, the provided empty() routine can be used to + * delete all elements, however the destructor will not call this routine, it + * will simply abandon all the elements. It is up to the programmer to + * explicitly de-allocate items when necessary. + * + * \include ex_dlist.cpp + */ + +/*@}*/ + +#include "dlcommon.h" + +#undef BASE_EL +#undef DLMEL_TEMPDEF +#undef DLMEL_TEMPUSE +#undef DList + +#endif /* _AAPL_DLIST_H */ + diff --git a/src/aapl/dlistmel.h b/src/aapl/dlistmel.h new file mode 100644 index 00000000..f2004b81 --- /dev/null +++ b/src/aapl/dlistmel.h @@ -0,0 +1,72 @@ +/* + * Copyright 2001 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_DLISTMEL_H +#define _AAPL_DLISTMEL_H + +/** + * \addtogroup dlist + * @{ + */ + +/** + * \class DListMel + * \brief Doubly linked list for elements that may appear in multiple lists. + * + * This class is similar to DList, except that the user defined list element + * can inherit from multple DListEl classes and consequently be an element in + * multiple lists. In other words, DListMel allows a single instance of a data + * structure to be an element in multiple lists without the lists interfereing + * with one another. + * + * For each list that an element class is to appear in, the element must have + * unique next and previous pointers that can be unambiguously refered to with + * some base class name. This name is given to DListMel as a template argument + * so it can use the correct next and previous pointers in its list + * operations. + * + * DListMel does not assume ownership of elements in the list. If the elements + * are known to reside on the heap and are not contained in any other list or + * data structure, the provided empty() routine can be used to delete all + * elements, however the destructor will not call this routine, it will simply + * abandon all the elements. It is up to the programmer to explicitly + * de-allocate items when it is safe to do so. + * + * \include ex_dlistmel.cpp + */ + +/*@}*/ + +#define BASE_EL(name) BaseEl::name +#define DLMEL_TEMPDEF class Element, class BaseEl +#define DLMEL_TEMPUSE Element, BaseEl +#define DList DListMel + +#include "dlcommon.h" + +#undef BASE_EL +#undef DLMEL_TEMPDEF +#undef DLMEL_TEMPUSE +#undef DList + +#endif /* _AAPL_DLISTMEL_H */ + diff --git a/src/aapl/dlistval.h b/src/aapl/dlistval.h new file mode 100644 index 00000000..efdb1cc1 --- /dev/null +++ b/src/aapl/dlistval.h @@ -0,0 +1,72 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_DLISTVAL_H +#define _AAPL_DLISTVAL_H + +/** + * \addtogroup dlist + * @{ + */ + +/** + * \class DListVal + * \brief By-value doubly linked list. + * + * This class is a doubly linked list that does not require a list element + * type to be declared. The user instead gives a type that is to be stored in + * the list element. When inserting a new data item, the value is copied into + * a newly allocated element. This list is inteded to behave and be utilized + * like the list template found in the STL. + * + * DListVal is different from the other lists in that it allocates elements + * itself. The raw element insert interface is still exposed for convenience, + * however, the list assumes all elements in the list are allocated on the + * heap and are to be managed by the list. The destructor WILL delete the + * contents of the list. If the list is ever copied in from another list, the + * existing contents are deleted first. This is in contrast to DList and + * DListMel, which will never delete their contents to allow for statically + * allocated elements. + * + * \include ex_dlistval.cpp + */ + +/*@}*/ + +#define BASE_EL(name) name +#define DLMEL_TEMPDEF class T +#define DLMEL_TEMPUSE T +#define DList DListVal +#define Element DListValEl +#define DOUBLELIST_VALUE + +#include "dlcommon.h" + +#undef BASE_EL +#undef DLMEL_TEMPDEF +#undef DLMEL_TEMPUSE +#undef DList +#undef Element +#undef DOUBLELIST_VALUE + +#endif /* _AAPL_DLISTVAL_H */ + diff --git a/src/aapl/insertsort.h b/src/aapl/insertsort.h new file mode 100644 index 00000000..386fd9c6 --- /dev/null +++ b/src/aapl/insertsort.h @@ -0,0 +1,95 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_INSERTSORT_H +#define _AAPL_INSERTSORT_H + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup sort + * @{ + */ + +/** + * \class InsertSort + * \brief Insertion sort an array of data. + * + * InsertSort can be used to sort any array of objects of type T provided a + * compare class is given. InsertSort is in-place. It does not require any + * temporary storage. + * + * Objects are not made aware that they are being moved around in memory. + * Assignment operators, constructors and destructors are never invoked by the + * sort. + * + * InsertSort runs in O(n^2) time. It is most useful when sorting small arrays. + * where it can outperform the O(n*log(n)) sorters due to its simplicity. + * InsertSort is a not a stable sort. Elements with the same key will not have + * their relative ordering preserved. + */ + +/*@}*/ + +/* InsertSort. */ +template class InsertSort + : public Compare +{ +public: + /* Sorting interface routine. */ + void sort(T *data, long len); +}; + + +/** + * \brief Insertion sort an array of data. + */ +template + void InsertSort::sort(T *data, long len) +{ + /* For each next largest spot in the sorted array... */ + for ( T *dest = data; dest < data+len-1; dest++ ) { + /* Find the next smallest element in the unsorted array. */ + T *smallest = dest; + for ( T *src = dest+1; src < data+len; src++ ) { + /* If src is smaller than the current src, then use it. */ + if ( this->compare( *src, *smallest ) < 0 ) + smallest = src; + } + + if ( smallest != dest ) { + /* Swap dest, smallest. */ + char tmp[sizeof(T)]; + memcpy( tmp, dest, sizeof(T) ); + memcpy( dest, smallest, sizeof(T) ); + memcpy( smallest, tmp, sizeof(T) ); + } + } +} + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_INSERTSORT_H */ diff --git a/src/aapl/mergesort.h b/src/aapl/mergesort.h new file mode 100644 index 00000000..83f8b67b --- /dev/null +++ b/src/aapl/mergesort.h @@ -0,0 +1,141 @@ +/* + * Copyright 2001, 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_MERGESORT_H +#define _AAPL_MERGESORT_H + +#include "bubblesort.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup sort + * @{ + */ + +/** + * \class MergeSort + * \brief Merge sort an array of data. + * + * MergeSort can be used to sort any array of objects of type T provided a + * compare class is given. MergeSort is not in-place, it requires temporary + * storage equal to the size of the array. The temporary storage is allocated + * on the heap. + * + * Objects are not made aware that they are being moved around in memory. + * Assignment operators, constructors and destructors are never invoked by the + * sort. + * + * MergeSort runs in worst case O(n*log(n)) time. In most cases it is slower + * than QuickSort because more copying is neccessary. But on the other hand, + * it is a stable sort, meaning that objects with the same key have their + * relative ordering preserved. Also, its worst case is better. MergeSort + * switches to a BubbleSort when the size of the array being sorted is small. + * This happens when directly sorting a small array or when MergeSort calls + * itself recursively on a small portion of a larger array. + */ + +/*@}*/ + + +/* MergeSort. */ +template class MergeSort + : public BubbleSort +{ +public: + /* Sorting interface routine. */ + void sort(T *data, long len); + +private: + /* Recursive worker. */ + void doSort(T *tmpStor, T *data, long len); +}; + +#define _MS_BUBBLE_THRESH 16 + +/* Recursive mergesort worker. Split data, make recursive calls, merge + * results. */ +template< class T, class Compare> void MergeSort:: + doSort(T *tmpStor, T *data, long len) +{ + if ( len <= 1 ) + return; + + if ( len <= _MS_BUBBLE_THRESH ) { + BubbleSort::sort( data, len ); + return; + } + + long mid = len / 2; + + doSort( tmpStor, data, mid ); + doSort( tmpStor + mid, data + mid, len - mid ); + + /* Merge the data. */ + T *endLower = data + mid, *lower = data; + T *endUpper = data + len, *upper = data + mid; + T *dest = tmpStor; + while ( true ) { + if ( lower == endLower ) { + /* Possibly upper left. */ + if ( upper != endUpper ) + memcpy( dest, upper, (endUpper - upper) * sizeof(T) ); + break; + } + else if ( upper == endUpper ) { + /* Only lower left. */ + if ( lower != endLower ) + memcpy( dest, lower, (endLower - lower) * sizeof(T) ); + break; + } + else { + /* Both upper and lower left. */ + if ( this->compare(*lower, *upper) <= 0 ) + memcpy( dest++, lower++, sizeof(T) ); + else + memcpy( dest++, upper++, sizeof(T) ); + } + } + + /* Copy back from the tmpStor array. */ + memcpy( data, tmpStor, sizeof( T ) * len ); +} + +/** + * \brief Merge sort an array of data. + */ +template< class T, class Compare> + void MergeSort::sort(T *data, long len) +{ + /* Allocate the tmp space needed by merge sort, sort and free. */ + T *tmpStor = (T*) new char[sizeof(T) * len]; + doSort( tmpStor, data, len ); + delete[] (char*) tmpStor; +} + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_MERGESORT_H */ diff --git a/src/aapl/quicksort.h b/src/aapl/quicksort.h new file mode 100644 index 00000000..f23ec2ee --- /dev/null +++ b/src/aapl/quicksort.h @@ -0,0 +1,186 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_QUICKSORT_H +#define _AAPL_QUICKSORT_H + +#include "insertsort.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup sort + * @{ + */ + +/** + * \class QuickSort + * \brief Quick sort an array of data. + * + * QuickSort can be used to sort any array of objects of type T provided a + * compare class is given. QuickSort is in-place. It does not require any + * temporary storage. + * + * Objects are not made aware that they are being moved around in memory. + * Assignment operators, constructors and destructors are never invoked by the + * sort. + * + * QuickSort runs in O(n*log(n)) time in the average case. It is faster than + * mergsort in the average case because it does less moving of data. The + * performance of quicksort depends mostly on the choice of pivot. This + * implementation picks the pivot as the median of first, middle, last. This + * choice of pivot avoids the O(n^2) worst case for input already sorted, but + * it is still possible to encounter the O(n^2) worst case. For example an + * array of identical elements will run in O(n^2) + * + * QuickSort is not a stable sort. Elements with the same key will not have + * their relative ordering preserved. QuickSort switches to an InsertSort + * when the size of the array being sorted is small. This happens when + * directly sorting a small array or when QuickSort calls iteself recursively + * on a small portion of a larger array. + */ + +/*@}*/ + +/* QuickSort. */ +template class QuickSort : + public InsertSort +{ +public: + /* Sorting interface routine. */ + void sort(T *data, long len); + +private: + /* Recursive worker. */ + void doSort(T *start, T *end); + T *partition(T *start, T *end); + inline T *median(T *start, T *end); +}; + +#define _QS_INSERTION_THRESH 16 + +/* Finds the median of start, middle, end. */ +template T *QuickSort:: + median(T *start, T *end) +{ + T *pivot, *mid = start + (end-start)/2; + + /* CChoose the pivot. */ + if ( this->compare(*start, *mid) < 0 ) { + if ( this->compare(*mid, *end) < 0 ) + pivot = mid; + else if ( this->compare(*start, *end) < 0 ) + pivot = end; + else + pivot = start; + } + else if ( this->compare(*start, *end) < 0 ) + pivot = start; + else if ( this->compare(*mid, *end) < 0 ) + pivot = end; + else + pivot = mid; + + return pivot; +} + +template T *QuickSort:: + partition(T *start, T *end) +{ + /* Use the median of start, middle, end as the pivot. First save + * it off then move the last element to the free spot. */ + char pcPivot[sizeof(T)]; + T *pivot = median(start, end); + + memcpy( pcPivot, pivot, sizeof(T) ); + if ( pivot != end ) + memcpy( pivot, end, sizeof(T) ); + + T *first = start-1; + T *last = end; + pivot = (T*) pcPivot; + + /* Shuffle element to the correct side of the pivot, ending + * up with the free spot where the pivot will go. */ + while ( true ) { + /* Throw one element ahead to the free spot at last. */ + while ( true ) { + first += 1; + if ( first == last ) + goto done; + if ( this->compare( *first, *pivot ) > 0 ) { + memcpy(last, first, sizeof(T)); + break; + } + } + + /* Throw one element back to the free spot at first. */ + while ( true ) { + last -= 1; + if ( last == first ) + goto done; + if ( this->compare( *last, *pivot ) < 0 ) { + memcpy(first, last, sizeof(T)); + break; + } + } + } +done: + /* Put the pivot into the middle spot for it. */ + memcpy( first, pivot, sizeof(T) ); + return first; +} + + +template< class T, class Compare> void QuickSort:: + doSort(T *start, T *end) +{ + long len = end - start + 1; + if ( len > _QS_INSERTION_THRESH ) { + /* Use quicksort. */ + T *pivot = partition( start, end ); + doSort(start, pivot-1); + doSort(pivot+1, end); + } + else if ( len > 1 ) { + /* Array is small, use insertion sort. */ + InsertSort::sort( start, len ); + } +} + +/** + * \brief Quick sort an array of data. + */ +template< class T, class Compare> + void QuickSort::sort(T *data, long len) +{ + /* Call recursive worker. */ + doSort(data, data+len-1); +} + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_QUICKSORT_H */ diff --git a/src/aapl/resize.h b/src/aapl/resize.h new file mode 100644 index 00000000..6cc1090f --- /dev/null +++ b/src/aapl/resize.h @@ -0,0 +1,345 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_RESIZE_H +#define _AAPL_RESIZE_H + +#include + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/* This step is expressed in units of T. Changing this requires changes to + * docs in ResizeLin constructor. */ +#define LIN_DEFAULT_STEP 256 + +/* + * Resizing macros giving different resize methods. + */ + +/* If needed is greater than existing, give twice needed. */ +#define EXPN_UP( existing, needed ) \ + needed > existing ? (needed<<1) : existing + +/* If needed is less than 1 quarter existing, give twice needed. */ +#define EXPN_DOWN( existing, needed ) \ + needed < (existing>>2) ? (needed<<1) : existing + +/* If needed is greater than existing, give needed plus step. */ +#define LIN_UP( existing, needed ) \ + needed > existing ? (needed+step) : existing + +/* If needed is less than existing - 2 * step then give needed plus step. */ +#define LIN_DOWN( existing, needed ) \ + needed < (existing-(step<<1)) ? (needed+step) : existing + +/* Return existing. */ +#define CONST_UP( existing, needed ) existing + +/* Return existing. */ +#define CONST_DOWN( existing, needed ) existing + +/** + * \addtogroup vector + * @{ + */ + +/** \class ResizeLin + * \brief Linear table resizer. + * + * When an up resize or a down resize is needed, ResizeLin allocates the space + * needed plus some user defined step. The result is that when growing the + * vector in a linear fashion, the number of resizes is also linear. + * + * If only up resizing is done, then there will never be more than step unused + * spaces in the vector. If down resizing is done as well, there will never be + * more than 2*step unused spaces in the vector. The up resizing and down + * resizing policies are offset to improve performance when repeatedly + * inserting and removing a small number of elements relative to the step. + * This scheme guarantees that repetitive inserting and removing of a small + * number of elements will never result in repetative reallocation. + * + * The vectors pass sizes to the resizer in units of T, so the step gets + * interpreted as units of T. + */ + +/*@}*/ + +/* Linear resizing. */ +class ResizeLin +{ +protected: + /** + * \brief Default constructor. + * + * Intializes resize step to 256 units of the table type T. + */ + ResizeLin() : step(LIN_DEFAULT_STEP) { } + + /** + * \brief Determine the new table size when up resizing. + * + * If the existing size is insufficient for the space needed, then allocate + * the space needed plus the step. The step is in units of T. + */ + inline long upResize( long existing, long needed ) + { return LIN_UP(existing, needed); } + + /** + * \brief Determine the new table size when down resizing. + * + * If space needed is less than the existing - 2*step, then allocate the + * space needed space plus the step. The step is in units of T. + */ + inline long downResize( long existing, long needed ) + { return LIN_DOWN(existing, needed); } + +public: + /** + * \brief Step for linear resize. + * + * Amount of extra space in units of T added each time a resize must take + * place. This may be changed at any time. The step should be >= 0. + */ + long step; +}; + +/** + * \addtogroup vector + * @{ + */ + +/** \class ResizeCtLin + * \brief Linear table resizer with compile time step. + * + * When an up resize or a down resize is needed, ResizeCtLin allocates the + * space needed plus some compile time defined step. The result is that when + * growing the vector in a linear fashion, the number of resizes is also + * linear. + * + * If only up resizing is done, then there will never be more than step unused + * spaces in the vector. If down resizing is done as well, there will never be + * more than 2*step unused spaces in the vector. The up resizing and down + * resizing policies are offset to improve performance when repeatedly + * inserting and removing a small number of elements relative to the step. + * This scheme guarantees that repetitive inserting and removing of a small + * number of elements will never result in repetative reallocation. + * + * The vectors pass sizes to the resizer in units of T, so the step gets + * interpreted as units of T. + */ + +/*@}*/ + +/* Linear resizing. */ +template class ResizeCtLin +{ +protected: + /** + * \brief Determine the new table size when up resizing. + * + * If the existing size is insufficient for the space needed, then allocate + * the space needed plus the step. The step is in units of T. + */ + inline long upResize( long existing, long needed ) + { return LIN_UP(existing, needed); } + + /** + * \brief Determine the new table size when down resizing. + * + * If space needed is less than the existing - 2*step, then allocate the + * space needed space plus the step. The step is in units of T. + */ + inline long downResize( long existing, long needed ) + { return LIN_DOWN(existing, needed); } +}; + +/** + * \addtogroup vector + * @{ + */ + +/** \class ResizeConst + * \brief Constant table resizer. + * + * When an up resize is needed the existing size is always used. ResizeConst + * does not allow dynamic resizing. To use ResizeConst, the vector needs to be + * constructed with and initial allocation amount otherwise it will be + * unusable. + */ + +/*@}*/ + +/* Constant table resizing. */ +class ResizeConst +{ +protected: + /* Assert don't need more than exists. Return existing. */ + static inline long upResize( long existing, long needed ); + + /** + * \brief Determine the new table size when down resizing. + * + * Always returns the existing table size. + */ + static inline long downResize( long existing, long needed ) + { return CONST_DOWN(existing, needed); } +}; + +/** + * \brief Determine the new table size when up resizing. + * + * If the existing size is insufficient for the space needed, then an assertion + * will fail. Otherwise returns the existing size. + */ +inline long ResizeConst::upResize( long existing, long needed ) +{ + assert( needed <= existing ); + return CONST_UP(existing, needed); +} + +/** + * \addtogroup vector + * @{ + */ + +/** \class ResizeRunTime + * \brief Run time settable table resizer. + * + * ResizeRunTime can have it's up and down resizing policies set at run time. + * Both up and down policies can be set independently to one of Exponential, + * Linear, or Constant. See the documentation for ResizeExpn, ResizeLin, and + * ResizeConst for the details of the resizing policies. + * + * The policies may be changed at any time. The default policies are + * both Exponential. + */ + +/*@}*/ + +/* Run time resizing. */ +class ResizeRunTime +{ +protected: + /** + * \brief Default constuctor. + * + * The up and down resizing it initialized to Exponetial. The step + * defaults to 256 units of T. + */ + inline ResizeRunTime(); + + /** + * \brief Resizing policies. + */ + enum ResizeType { + Exponential, /*!< Exponential resizing. */ + Linear, /*!< Linear resizing. */ + Constant /*!< Constant table size. */ + }; + + inline long upResize( long existing, long needed ); + inline long downResize( long existing, long needed ); + +public: + /** + * \brief Step for linear resize. + * + * Amount of extra space in units of T added each time a resize must take + * place. This may be changed at any time. The step should be >= 0. + */ + long step; + + /** + * \brief Up resizing policy. + */ + ResizeType upResizeType; + + /** + * \brief Down resizing policy. + */ + ResizeType downResizeType; +}; + +inline ResizeRunTime::ResizeRunTime() +: + step( LIN_DEFAULT_STEP ), + upResizeType( Exponential ), + downResizeType( Exponential ) +{ +} + +/** + * \brief Determine the new table size when up resizing. + * + * Type of up resizing is determined by upResizeType. Exponential, Linear and + * Constant resizing is the same as that of ResizeExpn, ResizeLin and + * ResizeConst. + */ +inline long ResizeRunTime::upResize( long existing, long needed ) +{ + switch ( upResizeType ) { + case Exponential: + return EXPN_UP(existing, needed); + case Linear: + return LIN_UP(existing, needed); + case Constant: + assert( needed <= existing ); + return CONST_UP(existing, needed); + } + return 0; +}; + +/** + * \brief Determine the new table size when down resizing. + * + * Type of down resizing is determined by downResiizeType. Exponential, Linear + * and Constant resizing is the same as that of ResizeExpn, ResizeLin and + * ResizeConst. + */ +inline long ResizeRunTime::downResize( long existing, long needed ) +{ + switch ( downResizeType ) { + case Exponential: + return EXPN_DOWN(existing, needed); + case Linear: + return LIN_DOWN(existing, needed); + case Constant: + return CONST_DOWN(existing, needed); + } + return 0; +} + +/* Don't need these anymore. */ +#undef EXPN_UP +#undef EXPN_DOWN +#undef LIN_UP +#undef LIN_DOWN +#undef CONST_UP +#undef CONST_DOWN + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_RESIZE_H */ diff --git a/src/aapl/rope.h b/src/aapl/rope.h new file mode 100644 index 00000000..7a5e7b58 --- /dev/null +++ b/src/aapl/rope.h @@ -0,0 +1,237 @@ +/* + * Copyright 2016 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_ROPE_H +#define _AAPL_ROPE_H + +#include +#include +#include + +struct RopeBlock +{ + static const int BLOCK_SZ = 8192; + + RopeBlock() + : + size(BLOCK_SZ), + hoff(0), + toff(0) + { + } + + int size; + int hoff; + int toff; + RopeBlock *next; +}; + +struct Rope +{ + Rope() + : + hblk(0), + tblk(0), + ropeLen(0) + { + } + + /* Write to the tail block, at tail offset. */ + RopeBlock *hblk; + RopeBlock *tblk; + + /* Number of bytes in rope. */ + int ropeLen; + + RopeBlock *allocateBlock( int supporting ) + { + int size = ( supporting > RopeBlock::BLOCK_SZ ) ? supporting : RopeBlock::BLOCK_SZ; + char *bd = new char[sizeof(RopeBlock) + size]; + RopeBlock *block = (RopeBlock*) bd; + block->size = size; + block->hoff = 0; + block->toff = 0; + block->next = 0; + return block; + } + + char *data( RopeBlock *rb ) + { return (char*)rb + sizeof( RopeBlock ) + rb->hoff; } + + char *writeTo( RopeBlock *rb ) + { return (char*)rb + sizeof( RopeBlock ) + rb->toff; } + + int length( RopeBlock *rb ) + { return rb->toff - rb->hoff; } + + int length() + { return ropeLen; } + + int available( RopeBlock *rb ) + { return rb->size - rb->toff; } + + char *append( const char *src, int len ) + { + if ( tblk == 0 ) { + /* There are no blocks. */ + hblk = tblk = allocateBlock( len ); + } + else { + int avail = available( tblk ); + + /* Move to the next block? */ + if ( len > avail ) { + RopeBlock *block = allocateBlock( len ); + tblk->next = block; + tblk = block; + } + } + + char *ret = writeTo(tblk); + tblk->toff += len; + ropeLen += len; + + if ( src != 0 ) + memcpy( ret, src, len ); + return ret; + } + + char *appendBlock( int len ) + { + if ( tblk == 0 ) { + /* There are no blocks. */ + hblk = tblk = allocateBlock( len ); + } + else { + RopeBlock *block = allocateBlock( len ); + tblk->next = block; + tblk = block; + } + + char *ret = writeTo(tblk); + tblk->toff += len; + ropeLen += len; + return ret; + } + + /* Transfer data from other. Leaves it empty. */ + void append( Rope &other ) + { + if ( hblk == 0 ) { + transfer( other ); + } + else if ( other.hblk == 0 ) { + /* nothing to do, other list empty. */ + } + else { + tblk->next = other.hblk; + tblk = other.tblk; + ropeLen += other.ropeLen; + } + + other.abandon(); + } + + void empty() + { + RopeBlock *blk = hblk; + while ( blk != 0 ) { + RopeBlock *next = blk->next; + delete[] (char*)blk; + blk = next; + } + + hblk = 0; + tblk = 0; + ropeLen = 0; + } + + void abandon() + { + hblk = 0; + tblk = 0; + ropeLen = 0; + } + + void transfer( Rope &from ) + { + empty(); + + this->hblk = from.hblk; + this->tblk = from.tblk; + this->ropeLen = from.ropeLen; + + from.hblk = from.tblk = 0; + from.ropeLen = 0; + } +}; + + +/* + * StringStream for appending to streams with an ostream. + */ +struct RopeOutBuf +: + public std::streambuf +{ + RopeOutBuf( Rope &r ) + : + r(r) + { + } + + int_type overflow( int_type c ) + { + if ( c != EOF ) { + char z = c; + r.append( &z, 1 ); + } + return c; + } + + std::streamsize xsputn( const char *data, std::streamsize num ) + { + r.append( data, num ); + return num; + } + + Rope &r; +}; + +struct RopeStream +: + public std::ostream +{ + RopeStream( Rope &r ) + : + std::ostream( 0 ), + buf( r ) + { + rdbuf( &buf ); + } + + RopeOutBuf buf; +}; + + +#endif + diff --git a/src/aapl/sbstmap.h b/src/aapl/sbstmap.h new file mode 100644 index 00000000..3e159ab2 --- /dev/null +++ b/src/aapl/sbstmap.h @@ -0,0 +1,122 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_SBSTMAP_H +#define _AAPL_SBSTMAP_H + +#include "compare.h" +#include "svector.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \brief Element for BstMap. + * + * Stores the key and value pair. + */ +template struct SBstMapEl +{ + SBstMapEl() {} + SBstMapEl(const Key &key) : key(key) {} + SBstMapEl(const Key &key, const Value &val) : key(key), value(val) {} + + /** \brief The key */ + Key key; + + /** \brief The value. */ + Value value; +}; + +#ifdef AAPL_NAMESPACE +} +#endif + +/** + * \addtogroup bst + * @{ + */ + +/** + * \class SBstMap + * \brief Copy-on-write binary search table for key and value pairs. + * + * This is a map style binary search table that employs the copy-on-write + * mechanism for table data. BstMap stores key and value pairs in each + * element. The key and value can be any type. A compare class for the key + * must be supplied. + */ + +/*@}*/ + +#define BST_TEMPL_DECLARE class Key, class Value, \ + class Compare = CmpOrd, class Resize = ResizeExpn +#define BST_TEMPL_DEF class Key, class Value, class Compare, class Resize +#define BST_TEMPL_USE Key, Value, Compare, Resize +#define GET_KEY(el) ((el).key) +#define BstTable SBstMap +#define Vector SVector +#define Table STable +#define Element SBstMapEl +#define BSTMAP +#define SHARED_BST + +#include "bstcommon.h" + +#undef BST_TEMPL_DECLARE +#undef BST_TEMPL_DEF +#undef BST_TEMPL_USE +#undef GET_KEY +#undef BstTable +#undef Vector +#undef Table +#undef Element +#undef BSTMAP +#undef SHARED_BST + +/** + * \fn SBstMap::insert(const Key &key, BstMapEl **lastFound) + * \brief Insert the given key. + * + * If the given key does not already exist in the table then a new element + * having key is inserted. They key copy constructor and value default + * constructor are used to place the pair in the table. If lastFound is given, + * it is set to the new entry created. If the insert fails then lastFound is + * set to the existing pair of the same key. + * + * \returns The new element created upon success, null upon failure. + */ + +/** + * \fn SBstMap::insertMulti(const Key &key) + * \brief Insert the given key even if it exists already. + * + * If the key exists already then the new element having key is placed next + * to some other pair of the same key. InsertMulti cannot fail. The key copy + * constructor and the value default constructor are used to place the pair in + * the table. + * + * \returns The new element created. + */ + +#endif /* _AAPL_SBSTMAP_H */ diff --git a/src/aapl/sbstset.h b/src/aapl/sbstset.h new file mode 100644 index 00000000..947f78dc --- /dev/null +++ b/src/aapl/sbstset.h @@ -0,0 +1,95 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_SBSTSET_H +#define _AAPL_SBSTSET_H + +/** + * \addtogroup bst + * @{ + */ + +/** + * \class SBstSet + * \brief Copy-on-write binary search table for types that are the key. + * + * This is a set style binary search table that employs the copy-on-write + * mechanism for storing table data. BstSet is suitable for types that + * comprise the entire key. Rather than look into the element to retrieve the + * key, the element is the key. A class that contains a comparison routine + * for the key must be given. + */ + +/*@}*/ + +#include "compare.h" +#include "svector.h" + +#define BST_TEMPL_DECLARE class Key, class Compare = CmpOrd, \ + class Resize = ResizeExpn +#define BST_TEMPL_DEF class Key, class Compare, class Resize +#define BST_TEMPL_USE Key, Compare, Resize +#define GET_KEY(el) (el) +#define BstTable SBstSet +#define Vector SVector +#define Table STable +#define Element Key +#define BSTSET +#define SHARED_BST + +#include "bstcommon.h" + +#undef BST_TEMPL_DECLARE +#undef BST_TEMPL_DEF +#undef BST_TEMPL_USE +#undef GET_KEY +#undef BstTable +#undef Vector +#undef Table +#undef Element +#undef BSTSET +#undef SHARED_BST + +/** + * \fn SBstSet::insert(const Key &key, Key **lastFound) + * \brief Insert the given key. + * + * If the given key does not already exist in the table then it is inserted. + * The key's copy constructor is used to place the item in the table. If + * lastFound is given, it is set to the new entry created. If the insert fails + * then lastFound is set to the existing key of the same value. + * + * \returns The new element created upon success, null upon failure. + */ + +/** + * \fn SBstSet::insertMulti(const Key &key) + * \brief Insert the given key even if it exists already. + * + * If the key exists already then it is placed next to some other key of the + * same value. InsertMulti cannot fail. The key's copy constructor is used to + * place the item in the table. + * + * \returns The new element created. + */ + +#endif /* _AAPL_SBSTSET_H */ diff --git a/src/aapl/sbsttable.h b/src/aapl/sbsttable.h new file mode 100644 index 00000000..9cfed437 --- /dev/null +++ b/src/aapl/sbsttable.h @@ -0,0 +1,94 @@ +/* + * Copyright 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_SBSTTABLE_H +#define _AAPL_SBSTTABLE_H + +#include "compare.h" +#include "svector.h" + +/** + * \addtogroup bst + * @{ + */ + +/** + * \class SBstTable + * \brief Copy-on-write binary search table for structures that contain a key. + * + * This is a basic binary search table that employs a copy-on-write data + * storage mechanism. It can be used to contain a structure that has a key and + * possibly some data. The key should be a member of the element class and + * accessible with getKey(). A class containing the compare routine must be + * supplied. + */ + +/*@}*/ + +#define BST_TEMPL_DECLARE class Element, class Key, \ + class Compare = CmpOrd, class Resize = ResizeExpn +#define BST_TEMPL_DEF class Element, class Key, class Compare, class Resize +#define BST_TEMPL_USE Element, Key, Compare, Resize +#define GET_KEY(el) ((el).getKey()) +#define BstTable SBstTable +#define Vector SVector +#define Table STable +#define BSTTABLE +#define SHARED_BST + +#include "bstcommon.h" + +#undef BST_TEMPL_DECLARE +#undef BST_TEMPL_DEF +#undef BST_TEMPL_USE +#undef GET_KEY +#undef BstTable +#undef Vector +#undef Table +#undef BSTTABLE +#undef SHARED_BST + +/** + * \fn SBstTable::insert(const Key &key, Element **lastFound) + * \brief Insert a new element with the given key. + * + * If the given key does not already exist in the table a new element is + * inserted with the given key. A constructor taking only const Key& is used + * to initialize the new element. If lastFound is given, it is set to the new + * element created. If the insert fails then lastFound is set to the existing + * element with the same key. + * + * \returns The new element created upon success, null upon failure. + */ + +/** + * \fn SBstTable::insertMulti(const Key &key) + * \brief Insert a new element even if the key exists already. + * + * If the key exists already then the new element is placed next to some + * element with the same key. InsertMulti cannot fail. A constructor taking + * only const Key& is used to initialize the new element. + * + * \returns The new element created. + */ + +#endif /* _AAPL_SBSTTABLE_H */ diff --git a/src/aapl/svector.h b/src/aapl/svector.h new file mode 100644 index 00000000..54db2007 --- /dev/null +++ b/src/aapl/svector.h @@ -0,0 +1,1351 @@ +/* + * Copyright 2002, 2006 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_SVECTOR_H +#define _AAPL_SVECTOR_H + +#include +#include +#include +#include +#include "table.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup vector + * @{ + */ + +/** \class SVector + * \brief Copy-on-write dynamic array. + * + * SVector is a variant of Vector that employs copy-on-write behaviour. The + * SVector copy constructor and = operator make shallow copies. If a vector + * that references shared data is modified with insert, replace, append, + * prepend, setAs or remove, a new copy is made so as not to interfere with + * the shared data. However, shared individual elements may be modified by + * bypassing the SVector interface. + * + * SVector is a dynamic array that can be used to contain complex data + * structures that have constructors and destructors as well as simple types + * such as integers and pointers. + * + * SVector supports inserting, overwriting, and removing single or multiple + * elements at once. Constructors and destructors are called wherever + * appropriate. For example, before an element is overwritten, it's + * destructor is called. + * + * SVector provides automatic resizing of allocated memory as needed and + * offers different allocation schemes for controlling how the automatic + * allocation is done. Two senses of the the length of the data is + * maintained: the amount of raw memory allocated to the vector and the number + * of actual elements in the vector. The various allocation schemes control + * how the allocated space is changed in relation to the number of elements in + * the vector. + */ + +/*@}*/ + +/* SVector */ +template < class T, class Resize = ResizeExpn > class SVector : + public STable, public Resize +{ +private: + typedef STable BaseTable; + +public: + /** + * \brief Initialize an empty vector with no space allocated. + * + * If a linear resizer is used, the step defaults to 256 units of T. For a + * runtime vector both up and down allocation schemes default to + * Exponential. + */ + SVector() { } + + /** + * \brief Create a vector that contains an initial element. + * + * The vector becomes one element in length. The element's copy + * constructor is used to place the value in the vector. + */ + SVector(const T &val) { setAs(&val, 1); } + + /** + * \brief Create a vector that contains an array of elements. + * + * The vector becomes len elements in length. Copy constructors are used + * to place the new elements in the vector. + */ + SVector(const T *val, long len) { setAs(val, len); } + + /* Shallow copy. */ + SVector( const SVector &v ); + + /** + * \brief Free all memory used by the vector. + * + * The vector is reset to zero elements. Destructors are called on all + * elements in the vector. The space allocated for the vector is freed. + */ + ~SVector() { empty(); } + + /* Delete all items. */ + void empty(); + + /** + * \brief Deep copy another vector into this vector. + * + * Copies the entire contents of the other vector into this vector. Any + * existing contents are first deleted. Equivalent to setAs. + */ + void deepCopy( const SVector &v ) { setAs(v.data, v.length()); } + + /* Perform a shallow copy of another vector. */ + SVector &operator=( const SVector &v ); + + + /*@{*/ + /** + * \brief Insert one element at position pos. + * + * Elements in the vector from pos onward are shifted one space to the + * right. The copy constructor is used to place the element into this + * vector. If pos is greater than the length of the vector then undefined + * behaviour results. If pos is negative then it is treated as an offset + * relative to the length of the vector. + */ + void insert(long pos, const T &val) { insert(pos, &val, 1); } + + /* Insert an array of values. */ + void insert(long pos, const T *val, long len); + + /** + * \brief Insert all the elements from another vector at position pos. + * + * Elements in this vector from pos onward are shifted v.length() spaces + * to the right. The element's copy constructor is used to copy the items + * into this vector. The other vector is left unchanged. If pos is off the + * end of the vector, then undefined behaviour results. If pos is negative + * then it is treated as an offset relative to the length of the vector. + * Equivalent to vector.insert(pos, other.data, other.length()). + */ + void insert(long pos, const SVector &v) { insert(pos, v.data, v.length()); } + + /* Insert len copies of val into the vector. */ + void insertDup(long pos, const T &val, long len); + + /** + * \brief Insert one new element using the default constrcutor. + * + * Elements in the vector from pos onward are shifted one space to the right. + * The default constructor is used to init the new element. If pos is greater + * than the length of the vector then undefined behaviour results. If pos is + * negative then it is treated as an offset relative to the length of the + * vector. + */ + void insertNew(long pos) { insertNew(pos, 1); } + + /* Insert len new items using default constructor. */ + void insertNew(long pos, long len); + /*@}*/ + + /*@{*/ + /** + * \brief Remove one element at position pos. + * + * The element's destructor is called. Elements to the right of pos are + * shifted one space to the left to take up the free space. If pos is greater + * than or equal to the length of the vector then undefined behavior results. + * If pos is negative then it is treated as an offset relative to the length + * of the vector. + */ + void remove(long pos) { remove(pos, 1); } + + /* Delete a number of elements. */ + void remove(long pos, long len); + /*@}*/ + + /*@{*/ + /** + * \brief Replace one element at position pos. + * + * If there is an existing element at position pos (if pos is less than the + * length of the vector) then its destructor is called before the space is + * used. The copy constructor is used to place the element into the vector. + * If pos is greater than the length of the vector then undefined behaviour + * results. If pos is negative then it is treated as an offset relative to + * the length of the vector. + */ + void replace(long pos, const T &val) { replace(pos, &val, 1); } + + /* Replace with an array of values. */ + void replace(long pos, const T *val, long len); + + /** + * \brief Replace at position pos with all the elements of another vector. + * + * Replace at position pos with all the elements of another vector. The other + * vector is left unchanged. If there are existing elements at the positions + * to be replaced, then destructors are called before the space is used. Copy + * constructors are used to place the elements into this vector. It is + * allowable for the pos and length of the other vector to specify a + * replacement that overwrites existing elements and creates new ones. If pos + * is greater than the length of the vector then undefined behaviour results. + * If pos is negative, then it is treated as an offset relative to the length + * of the vector. + */ + void replace(long pos, const SVector &v) { replace(pos, v.data, v.length()); } + + /* Replace len items with len copies of val. */ + void replaceDup(long pos, const T &val, long len); + + /** + * \brief Replace at position pos with one new element. + * + * If there is an existing element at the position to be replaced (pos is + * less than the length of the vector) then the element's destructor is + * called before the space is used. The default constructor is used to + * initialize the new element. If pos is greater than the length of the + * vector then undefined behaviour results. If pos is negative, then it is + * treated as an offset relative to the length of the vector. + */ + void replaceNew(long pos) { replaceNew(pos, 1); } + + /* Replace len items at pos with newly constructed objects. */ + void replaceNew(long pos, long len); + /*@}*/ + + /*@{*/ + + /** + * \brief Set the contents of the vector to be val exactly. + * + * The vector becomes one element in length. Destructors are called on any + * existing elements in the vector. The element's copy constructor is used to + * place the val in the vector. + */ + void setAs(const T &val) { setAs(&val, 1); } + + /* Set to the contents of an array. */ + void setAs(const T *val, long len); + + /** + * \brief Set the vector to exactly the contents of another vector. + * + * The vector becomes v.length() elements in length. Destructors are called + * on any existing elements. Copy constructors are used to place the new + * elements in the vector. + */ + void setAs(const SVector &v) { setAs(v.data, v.length()); } + + /* Set as len copies of item. */ + void setAsDup(const T &item, long len); + + /** + * \brief Set the vector to exactly one new item. + * + * The vector becomes one element in length. Destructors are called on any + * existing elements in the vector. The default constructor is used to + * init the new item. + */ + void setAsNew() { setAsNew(1); } + + /* Set as newly constructed objects using the default constructor. */ + void setAsNew(long len); + /*@}*/ + + /*@{*/ + /** + * \brief Append one elment to the end of the vector. + * + * Copy constructor is used to place the element in the vector. + */ + void append(const T &val) { replace(BaseTable::length(), &val, 1); } + + /** + * \brief Append len elements to the end of the vector. + * + * Copy constructors are used to place the elements in the vector. + */ + void append(const T *val, long len) { replace(BaseTable::length(), val, len); } + + /** + * \brief Append the contents of another vector. + * + * The other vector is left unchanged. Copy constructors are used to place + * the elements in the vector. + */ + void append(const SVector &v) + { replace(BaseTable::length(), v.data, v.length()); } + + /** + * \brief Append len copies of item. + * + * The copy constructor is used to place the item in the vector. + */ + void appendDup(const T &item, long len) { replaceDup(BaseTable::length(), item, len); } + + /** + * \brief Append a single newly created item. + * + * The new element is initialized with the default constructor. + */ + void appendNew() { replaceNew(BaseTable::length(), 1); } + + /** + * \brief Append len newly created items. + * + * The new elements are initialized with the default constructor. + */ + void appendNew(long len) { replaceNew(BaseTable::length(), len); } + /*@}*/ + + + /*@{*/ + /** + * \brief Prepend one elment to the front of the vector. + * + * Copy constructor is used to place the element in the vector. + */ + void prepend(const T &val) { insert(0, &val, 1); } + + /** + * \brief Prepend len elements to the front of the vector. + * + * Copy constructors are used to place the elements in the vector. + */ + void prepend(const T *val, long len) { insert(0, val, len); } + + /** + * \brief Prepend the contents of another vector. + * + * The other vector is left unchanged. Copy constructors are used to place + * the elements in the vector. + */ + void prepend(const SVector &v) { insert(0, v.data, v.length()); } + + /** + * \brief Prepend len copies of item. + * + * The copy constructor is used to place the item in the vector. + */ + void prependDup(const T &item, long len) { insertDup(0, item, len); } + + /** + * \brief Prepend a single newly created item. + * + * The new element is initialized with the default constructor. + */ + void prependNew() { insertNew(0, 1); } + + /** + * \brief Prepend len newly created items. + * + * The new elements are initialized with the default constructor. + */ + void prependNew(long len) { insertNew(0, len); } + /*@}*/ + + /* Convenience access. */ + T &operator[](int i) const { return BaseTable::data[i]; } + long size() const { return BaseTable::length(); } + + /* Various classes for setting the iterator */ + struct Iter; + struct IterFirst { IterFirst( const SVector &v ) : v(v) { } const SVector &v; }; + struct IterLast { IterLast( const SVector &v ) : v(v) { } const SVector &v; }; + struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; + struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; + + /** + * \brief Shared Vector Iterator. + * \ingroup iterators + */ + struct Iter + { + /* Construct, assign. */ + Iter() : ptr(0), ptrBeg(0), ptrEnd(0) { } + + /* Construct. */ + Iter( const SVector &v ); + Iter( const IterFirst &vf ); + Iter( const IterLast &vl ); + inline Iter( const IterNext &vn ); + inline Iter( const IterPrev &vp ); + + /* Assign. */ + Iter &operator=( const SVector &v ); + Iter &operator=( const IterFirst &vf ); + Iter &operator=( const IterLast &vl ); + inline Iter &operator=( const IterNext &vf ); + inline Iter &operator=( const IterPrev &vl ); + + /** \brief Less than end? */ + bool lte() const { return ptr != ptrEnd; } + + /** \brief At end? */ + bool end() const { return ptr == ptrEnd; } + + /** \brief Greater than beginning? */ + bool gtb() const { return ptr != ptrBeg; } + + /** \brief At beginning? */ + bool beg() const { return ptr == ptrBeg; } + + /** \brief At first element? */ + bool first() const { return ptr == ptrBeg+1; } + + /** \brief At last element? */ + bool last() const { return ptr == ptrEnd-1; } + + /* Return the position. */ + long pos() const { return ptr - ptrBeg - 1; } + T &operator[](int i) const { return ptr[i]; } + + /** \brief Implicit cast to T*. */ + operator T*() const { return ptr; } + + /** \brief Dereference operator returns T&. */ + T &operator *() const { return *ptr; } + + /** \brief Arrow operator returns T*. */ + T *operator->() const { return ptr; } + + /** \brief Move to next item. */ + T *operator++() { return ++ptr; } + + /** \brief Move to next item. */ + T *operator++(int) { return ptr++; } + + /** \brief Move to next item. */ + T *increment() { return ++ptr; } + + /** \brief Move to previous item. */ + T *operator--() { return --ptr; } + + /** \brief Move to previous item. */ + T *operator--(int) { return ptr--; } + + /** \brief Move to previous item. */ + T *decrement() { return --ptr; } + + /** \brief Return the next item. Does not modify this. */ + inline IterNext next() const { return IterNext(*this); } + + /** \brief Return the previous item. Does not modify this. */ + inline IterPrev prev() const { return IterPrev(*this); } + + /** \brief The iterator is simply a pointer. */ + T *ptr; + + /* For testing endpoints. */ + T *ptrBeg, *ptrEnd; + }; + + /** \brief Return first element. */ + IterFirst first() { return IterFirst( *this ); } + + /** \brief Return last element. */ + IterLast last() { return IterLast( *this ); } + +protected: + void makeRawSpaceFor(long pos, long len); + + void setAsCommon(long len); + long replaceCommon(long pos, long len); + long insertCommon(long pos, long len); + + void upResize(long len); + void upResizeDup(long len); + void upResizeFromEmpty(long len); + void downResize(long len); + void downResizeDup(long len); +}; + +/** + * \brief Perform a shallow copy of the vector. + * + * Takes a reference to the contents of the other vector. + */ +template SVector:: + SVector(const SVector &v) +{ + /* Take a reference to other, if any data is allocated. */ + if ( v.data == 0 ) + BaseTable::data = 0; + else { + /* Get the source header, up the refcount and ref it. */ + STabHead *srcHead = ((STabHead*) v.data) - 1; + srcHead->refCount += 1; + BaseTable::data = (T*) (srcHead + 1); + } +} + +/** + * \brief Shallow copy another vector into this vector. + * + * Takes a reference to the other vector. The contents of this vector are + * first emptied. + * + * \returns A reference to this. + */ +template SVector & + SVector:: operator=( const SVector &v ) +{ + /* First clean out the current contents. */ + empty(); + + /* Take a reference to other, if any data is allocated. */ + if ( v.data == 0 ) + BaseTable::data = 0; + else { + /* Get the source header, up the refcount and ref it. */ + STabHead *srcHead = ((STabHead*) v.data) - 1; + srcHead->refCount += 1; + BaseTable::data = (T*) (srcHead + 1); + } + return *this; +} + +/* Init a vector iterator with just a vector. */ +template SVector:: + Iter::Iter( const SVector &v ) +{ + long length; + if ( v.data == 0 || (length=(((STabHead*)v.data)-1)->tabLen) == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = v.data; + ptrBeg = v.data-1; + ptrEnd = v.data+length; + } +} + +/* Init a vector iterator with the first of a vector. */ +template SVector:: + Iter::Iter( const IterFirst &vf ) +{ + long length; + if ( vf.v.data == 0 || (length=(((STabHead*)vf.v.data)-1)->tabLen) == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vf.v.data; + ptrBeg = vf.v.data-1; + ptrEnd = vf.v.data+length; + } +} + +/* Init a vector iterator with the last of a vector. */ +template SVector:: + Iter::Iter( const IterLast &vl ) +{ + long length; + if ( vl.v.data == 0 || (length=(((STabHead*)vl.v.data)-1)->tabLen) == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vl.v.data+length-1; + ptrBeg = vl.v.data-1; + ptrEnd = vl.v.data+length; + } +} + +/* Init a vector iterator with the next of some other iterator. */ +template SVector:: + Iter::Iter( const IterNext &vn ) +: + ptr(vn.i.ptr+1), + ptrBeg(vn.i.ptrBeg), + ptrEnd(vn.i.ptrEnd) +{ +} + +/* Init a vector iterator with the prev of some other iterator. */ +template SVector:: + Iter::Iter( const IterPrev &vp ) +: + ptr(vp.i.ptr-1), + ptrBeg(vp.i.ptrBeg), + ptrEnd(vp.i.ptrEnd) +{ +} + +/* Set a vector iterator with some vector. */ +template typename SVector::Iter & + SVector::Iter::operator=( const SVector &v ) +{ + long length; + if ( v.data == 0 || (length=(((STabHead*)v.data)-1)->tabLen) == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = v.data; + ptrBeg = v.data-1; + ptrEnd = v.data+length; + } + return *this; +} + +/* Set a vector iterator with the first element in a vector. */ +template typename SVector::Iter & + SVector::Iter::operator=( const IterFirst &vf ) +{ + long length; + if ( vf.v.data == 0 || (length=(((STabHead*)vf.v.data)-1)->tabLen) == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vf.v.data; + ptrBeg = vf.v.data-1; + ptrEnd = vf.v.data+length; + } + return *this; +} + +/* Set a vector iterator with the last element in a vector. */ +template typename SVector::Iter & + SVector::Iter::operator=( const IterLast &vl ) +{ + long length; + if ( vl.v.data == 0 || (length=(((STabHead*)vl.v.data)-1)->tabLen) == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vl.v.data+length-1; + ptrBeg = vl.v.data-1; + ptrEnd = vl.v.data+length; + } + return *this; +} + +/* Set a vector iterator with the next of some other iterator. */ +template typename SVector::Iter & + SVector::Iter::operator=( const IterNext &vn ) +{ + ptr = vn.i.ptr+1; + ptrBeg = vn.i.ptrBeg; + ptrEnd = vn.i.ptrEnd; + return *this; +} + +/* Set a vector iterator with the prev of some other iterator. */ +template typename SVector::Iter & + SVector::Iter::operator=( const IterPrev &vp ) +{ + ptr = vp.i.ptr-1; + ptrBeg = vp.i.ptrBeg; + ptrEnd = vp.i.ptrEnd; + return *this; +} + +/* Up resize the data for len elements using Resize::upResize to tell us the + * new length. Reads and writes allocLen. Does not read or write length. + * Assumes that there is some data allocated already. */ +template void SVector:: + upResize(long len) +{ + /* Get the current header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* Ask the resizer what the new length will be. */ + long newLen = Resize::upResize(head->allocLen, len); + + /* Did the data grow? */ + if ( newLen > head->allocLen ) { + head->allocLen = newLen; + + /* Table exists already, resize it up. */ + head = (STabHead*) realloc( head, sizeof(STabHead) + + sizeof(T) * newLen ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Save the data pointer. */ + BaseTable::data = (T*) (head + 1); + } +} + +/* Allocates a new buffer for an up resize that requires a duplication of the + * data. Uses Resize::upResize to get the allocation length. Reads and writes + * allocLen. This upResize does write the new length. Assumes that there is + * some data allocated already. */ +template void SVector:: + upResizeDup(long len) +{ + /* Get the current header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* Ask the resizer what the new length will be. */ + long newLen = Resize::upResize(head->allocLen, len); + + /* Dereferencing the existing data, decrement the refcount. */ + head->refCount -= 1; + + /* Table exists already, resize it up. */ + head = (STabHead*) malloc( sizeof(STabHead) + sizeof(T) * newLen ); + if ( head == 0 ) + throw std::bad_alloc(); + + head->refCount = 1; + head->allocLen = newLen; + head->tabLen = len; + + /* Save the data pointer. */ + BaseTable::data = (T*) (head + 1); +} + +/* Up resize the data for len elements using Resize::upResize to tell us the + * new length. Reads and writes allocLen. This upresize DOES write length. + * Assumes that no data is allocated. */ +template void SVector:: + upResizeFromEmpty(long len) +{ + /* There is no table yet. If the len is zero, then there is no need to + * create a table. */ + if ( len > 0 ) { + /* Ask the resizer what the new length will be. */ + long newLen = Resize::upResize(0, len); + + /* If len is greater than zero then we are always allocating the table. */ + STabHead *head = (STabHead*) malloc( sizeof(STabHead) + + sizeof(T) * newLen ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Set up the header and save the data pointer. Note that we set the + * length here. This differs from the other upResizes. */ + head->refCount = 1; + head->allocLen = newLen; + head->tabLen = len; + BaseTable::data = (T*) (head + 1); + } +} + +/* Down resize the data for len elements using Resize::downResize to determine + * the new length. Reads and writes allocLen. Does not read or write length. */ +template void SVector:: + downResize(long len) +{ + /* If there is already no length, then there is nothing we can do. */ + if ( BaseTable::data != 0 ) { + /* Get the current header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* Ask the resizer what the new length will be. */ + long newLen = Resize::downResize( head->allocLen, len ); + + /* Did the data shrink? */ + if ( newLen < head->allocLen ) { + if ( newLen == 0 ) { + /* Simply free the data. */ + free( head ); + BaseTable::data = 0; + } + else { + /* Save the new allocated length. */ + head->allocLen = newLen; + + /* Not shrinking to size zero, realloc it to the smaller size. */ + head = (STabHead*) realloc( head, sizeof(STabHead) + + sizeof(T) * newLen ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Save the new data ptr. */ + BaseTable::data = (T*) (head + 1); + } + } + } +} + +/* Allocate a new buffer for a down resize and duplication of the array. The + * new array will be len long and allocation size will be determined using + * Resize::downResize with the old array's allocLen. Does not actually copy + * any data. Reads and writes allocLen and writes the new len. */ +template void SVector:: + downResizeDup(long len) +{ + /* If there is already no length, then there is nothing we can do. */ + if ( BaseTable::data != 0 ) { + /* Get the current header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* Ask the resizer what the new length will be. */ + long newLen = Resize::downResize( head->allocLen, len ); + + /* Detaching from the existing head, decrement the refcount. */ + head->refCount -= 1; + + /* Not shrinking to size zero, malloc it to the smaller size. */ + head = (STabHead*) malloc( sizeof(STabHead) + sizeof(T) * newLen ); + if ( head == 0 ) + throw std::bad_alloc(); + + /* Save the new allocated length. */ + head->refCount = 1; + head->allocLen = newLen; + head->tabLen = len; + + /* Save the data pointer. */ + BaseTable::data = (T*) (head + 1); + } +} + +/** + * \brief Free all memory used by the vector. + * + * The vector is reset to zero elements. Destructors are called on all + * elements in the vector. The space allocated for the vector is freed. + */ +template void SVector:: + empty() +{ + if ( BaseTable::data != 0 ) { + /* Get the header and drop the refcount on the data. */ + STabHead *head = ((STabHead*) BaseTable::data) - 1; + head->refCount -= 1; + + /* If the refcount just went down to zero nobody else is referencing + * the data. */ + if ( head->refCount == 0 ) { + /* Call All destructors. */ + T *pos = BaseTable::data; + for ( long i = 0; i < head->tabLen; pos++, i++ ) + pos->~T(); + + /* Free the data space. */ + free( head ); + } + + /* Clear the pointer. */ + BaseTable::data = 0; + } +} + +/* Prepare for setting the contents of the vector to some array len long. + * Handles reusing the existing space, detaching from a common space or + * growing from zero length automatically. */ +template void SVector:: + setAsCommon(long len) +{ + if ( BaseTable::data != 0 ) { + /* Get the header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* If the refCount is one, then we can reuse the space. Otherwise we + * must detach from the referenced data create new space. */ + if ( head->refCount == 1 ) { + /* Call All destructors. */ + T *pos = BaseTable::data; + for ( long i = 0; i < head->tabLen; pos++, i++ ) + pos->~T(); + + /* Adjust the allocated length. */ + if ( len < head->tabLen ) + downResize( len ); + else if ( len > head->tabLen ) + upResize( len ); + + if ( BaseTable::data != 0 ) { + /* Get the header again and set the length. */ + head = ((STabHead*)BaseTable::data) - 1; + head->tabLen = len; + } + } + else { + /* Just detach from the data. */ + head->refCount -= 1; + BaseTable::data = 0; + + /* Make enough space. This will set the length. */ + upResizeFromEmpty( len ); + } + } + else { + /* The table is currently empty. Make enough space. This will set the + * length. */ + upResizeFromEmpty( len ); + } +} + +/** + * \brief Set the contents of the vector to be len elements exactly. + * + * The vector becomes len elements in length. Destructors are called on any + * existing elements in the vector. Copy constructors are used to place the + * new elements in the vector. + */ +template void SVector:: + setAs(const T *val, long len) +{ + /* Common stuff for setting the array to len long. */ + setAsCommon( len ); + + /* Copy data in. */ + T *dst = BaseTable::data; + const T *src = val; + for ( long i = 0; i < len; i++, dst++, src++ ) + new(dst) T(*src); +} + + +/** + * \brief Set the vector to len copies of item. + * + * The vector becomes len elements in length. Destructors are called on any + * existing elements in the vector. The element's copy constructor is used to + * copy the item into the vector. + */ +template void SVector:: + setAsDup(const T &item, long len) +{ + /* Do the common stuff for setting the array to len long. */ + setAsCommon( len ); + + /* Copy item in one spot at a time. */ + T *dst = BaseTable::data; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(item); +} + +/** + * \brief Set the vector to exactly len new items. + * + * The vector becomes len elements in length. Destructors are called on any + * existing elements in the vector. Default constructors are used to init the + * new items. + */ +template void SVector:: + setAsNew(long len) +{ + /* Do the common stuff for setting the array to len long. */ + setAsCommon( len ); + + /* Create items using default constructor. */ + T *dst = BaseTable::data; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(); +} + +/* Make space in vector for a replacement at pos of len items. Handles reusing + * existing space, detaching or growing from zero space. */ +template long SVector:: + replaceCommon(long pos, long len) +{ + if ( BaseTable::data != 0 ) { + /* Get the header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* If we are given a negative position to replace at then treat it as + * a position relative to the length. This doesn't have any meaning + * unless the length is at least one. */ + if ( pos < 0 ) + pos = head->tabLen + pos; + + /* The end is the one past the last item that we want to write to. */ + long i, endPos = pos + len; + + if ( head->refCount == 1 ) { + /* We can reuse the space. Make sure we have enough space. */ + if ( endPos > head->tabLen ) { + upResize( endPos ); + + /* Get the header again, whose addr may have changed after + * resizing. */ + head = ((STabHead*)BaseTable::data) - 1; + + /* Delete any objects we need to delete. */ + T *item = BaseTable::data + pos; + for ( i = pos; i < head->tabLen; i++, item++ ) + item->~T(); + + /* We are extending the vector, set the new data length. */ + head->tabLen = endPos; + } + else { + /* Delete any objects we need to delete. */ + T *item = BaseTable::data + pos; + for ( i = pos; i < endPos; i++, item++ ) + item->~T(); + } + } + else { + /* Use endPos to calc the end of the vector. */ + long newLen = endPos; + if ( newLen < head->tabLen ) + newLen = head->tabLen; + + /* Duplicate and grow up to endPos. This will set the length. */ + upResizeDup( newLen ); + + /* Copy from src up to pos. */ + const T *src = (T*) (head + 1); + T *dst = BaseTable::data; + for ( i = 0; i < pos; i++, dst++, src++) + new(dst) T(*src); + + /* Copy any items after the replace range. */ + for ( i += len, src += len, dst += len; + i < head->tabLen; i++, dst++, src++ ) + new(dst) T(*src); + } + } + else { + /* There is no data initially, must grow from zero. This will set the + * new length. */ + upResizeFromEmpty( len ); + } + + return pos; +} + + +/** + * \brief Replace len elements at position pos. + * + * If there are existing elements at the positions to be replaced, then + * destructors are called before the space is used. Copy constructors are used + * to place the elements into the vector. It is allowable for the pos and + * length to specify a replacement that overwrites existing elements and + * creates new ones. If pos is greater than the length of the vector then + * undefined behaviour results. If pos is negative, then it is treated as an + * offset relative to the length of the vector. + */ +template void SVector:: + replace(long pos, const T *val, long len) +{ + /* Common work for replacing in the vector. */ + pos = replaceCommon( pos, len ); + + /* Copy data in using copy constructor. */ + T *dst = BaseTable::data + pos; + const T *src = val; + for ( long i = 0; i < len; i++, dst++, src++ ) + new(dst) T(*src); +} + +/** + * \brief Replace at position pos with len copies of an item. + * + * If there are existing elements at the positions to be replaced, then + * destructors are called before the space is used. The copy constructor is + * used to place the element into this vector. It is allowable for the pos and + * length to specify a replacement that overwrites existing elements and + * creates new ones. If pos is greater than the length of the vector then + * undefined behaviour results. If pos is negative, then it is treated as an + * offset relative to the length of the vector. + */ +template void SVector:: + replaceDup(long pos, const T &val, long len) +{ + /* Common replacement stuff. */ + pos = replaceCommon( pos, len ); + + /* Copy data in using copy constructor. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(val); +} + +/** + * \brief Replace at position pos with len new elements. + * + * If there are existing elements at the positions to be replaced, then + * destructors are called before the space is used. The default constructor is + * used to initialize the new elements. It is allowable for the pos and length + * to specify a replacement that overwrites existing elements and creates new + * ones. If pos is greater than the length of the vector then undefined + * behaviour results. If pos is negative, then it is treated as an offset + * relative to the length of the vector. + */ +template void SVector:: + replaceNew(long pos, long len) +{ + /* Do the common replacement stuff. */ + pos = replaceCommon( pos, len ); + + /* Copy data in using copy constructor. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(); +} + +/** + * \brief Remove len elements at position pos. + * + * Destructor is called on all elements removed. Elements to the right of pos + * are shifted len spaces to the left to take up the free space. If pos is + * greater than or equal to the length of the vector then undefined behavior + * results. If pos is negative then it is treated as an offset relative to the + * length of the vector. + */ +template void SVector:: + remove(long pos, long len) +{ + /* If there is no data, we can't delete anything anyways. */ + if ( BaseTable::data != 0 ) { + /* Get the header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* If we are given a negative position to remove at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = head->tabLen + pos; + + /* The first position after the last item deleted. */ + long endPos = pos + len; + + /* The New data length. */ + long i, newLen = head->tabLen - len; + + if ( head->refCount == 1 ) { + /* We are the only ones using the data. We can reuse + * the existing space. */ + + /* The place in the data we are deleting at. */ + T *dst = BaseTable::data + pos; + + /* Call Destructors. */ + T *item = BaseTable::data + pos; + for ( i = 0; i < len; i += 1, item += 1 ) + item->~T(); + + /* Shift data over if necessary. */ + long lenToSlideOver = head->tabLen - endPos; + if ( len > 0 && lenToSlideOver > 0 ) + memmove(BaseTable::data + pos, dst + len, sizeof(T)*lenToSlideOver); + + /* Shrink the data if necessary. */ + downResize( newLen ); + + if ( BaseTable::data != 0 ) { + /* Get the header again (because of the resize) and set the + * new data length. */ + head = ((STabHead*)BaseTable::data) - 1; + head->tabLen = newLen; + } + } + else { + /* Must detach from the common data. Just copy the non-deleted + * items from the common data. */ + + /* Duplicate and grow down to newLen. This will set the length. */ + downResizeDup( newLen ); + + /* Copy over just the non-deleted parts. */ + const T *src = (T*) (head + 1); + T *dst = BaseTable::data; + for ( i = 0; i < pos; i++, dst++, src++ ) + new(dst) T(*src); + + /* ... and the second half. */ + for ( i += len, src += len; i < head->tabLen; i++, src++, dst++ ) + new(dst) T(*src); + } + } +} + +/* Shift over existing data. Handles reusing existing space, detaching or + * growing from zero space. */ +template long SVector:: + insertCommon(long pos, long len) +{ + if ( BaseTable::data != 0 ) { + /* Get the header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* If we are given a negative position to insert at then treat it as a + * position relative to the length. This only has meaning if there is + * existing data. */ + if ( pos < 0 ) + pos = head->tabLen + pos; + + /* Calculate the new length. */ + long i, newLen = head->tabLen + len; + + if ( head->refCount == 1 ) { + /* Up resize, we are growing. */ + upResize( newLen ); + + /* Get the header again, (the addr may have changed after + * resizing). */ + head = ((STabHead*)BaseTable::data) - 1; + + /* Shift over data at insert spot if needed. */ + if ( len > 0 && pos < head->tabLen ) { + memmove( BaseTable::data + pos + len, BaseTable::data + pos, + sizeof(T)*(head->tabLen - pos) ); + } + + /* Grow the length by the len inserted. */ + head->tabLen += len; + } + else { + /* Need to detach from the existing array. Copy over the other + * parts. This will set the length. */ + upResizeDup( newLen ); + + /* Copy over the parts around the insert. */ + const T *src = (T*) (head + 1); + T *dst = BaseTable::data; + for ( i = 0; i < pos; i++, dst++, src++ ) + new(dst) T(*src); + + /* ... and the second half. */ + for ( dst += len; i < head->tabLen; i++, src++, dst++ ) + new(dst) T(*src); + } + } + else { + /* There is no existing data. Start from zero. This will set the + * length. */ + upResizeFromEmpty( len ); + } + + return pos; +} + + +/** + * \brief Insert len elements at position pos. + * + * Elements in the vector from pos onward are shifted len spaces to the right. + * The copy constructor is used to place the elements into this vector. If pos + * is greater than the length of the vector then undefined behaviour results. + * If pos is negative then it is treated as an offset relative to the length + * of the vector. + */ +template void SVector:: + insert(long pos, const T *val, long len) +{ + /* Do the common insertion stuff. */ + pos = insertCommon( pos, len ); + + /* Copy data in element by element. */ + T *dst = BaseTable::data + pos; + const T *src = val; + for ( long i = 0; i < len; i++, dst++, src++ ) + new(dst) T(*src); +} + +/** + * \brief Insert len copies of item at position pos. + * + * Elements in the vector from pos onward are shifted len spaces to the right. + * The copy constructor is used to place the element into this vector. If pos + * is greater than the length of the vector then undefined behaviour results. + * If pos is negative then it is treated as an offset relative to the length + * of the vector. + */ +template void SVector:: + insertDup(long pos, const T &item, long len) +{ + /* Do the common insertion stuff. */ + pos = insertCommon( pos, len ); + + /* Copy the data item in one at a time. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(item); +} + + +/** + * \brief Insert len new elements using the default constructor. + * + * Elements in the vector from pos onward are shifted len spaces to the right. + * Default constructors are used to init the new elements. If pos is off the + * end of the vector then undefined behaviour results. If pos is negative then + * it is treated as an offset relative to the length of the vector. + */ +template void SVector:: + insertNew(long pos, long len) +{ + /* Do the common insertion stuff. */ + pos = insertCommon( pos, len ); + + /* Init new data with default constructors. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(); +} + +/* Makes space for len items, Does not init the items in any way. If pos is + * greater than the length of the vector then undefined behaviour results. + * Updates the length of the vector. */ +template void SVector:: + makeRawSpaceFor(long pos, long len) +{ + if ( BaseTable::data != 0 ) { + /* Get the header. */ + STabHead *head = ((STabHead*)BaseTable::data) - 1; + + /* Calculate the new length. */ + long i, newLen = head->tabLen + len; + + if ( head->refCount == 1 ) { + /* Up resize, we are growing. */ + upResize( newLen ); + + /* Get the header again, (the addr may have changed after + * resizing). */ + head = ((STabHead*)BaseTable::data) - 1; + + /* Shift over data at insert spot if needed. */ + if ( len > 0 && pos < head->tabLen ) { + memmove( BaseTable::data + pos + len, BaseTable::data + pos, + sizeof(T)*(head->tabLen - pos) ); + } + + /* Grow the length by the len inserted. */ + head->tabLen += len; + } + else { + /* Need to detach from the existing array. Copy over the other + * parts. This will set the length. */ + upResizeDup( newLen ); + + /* Copy over the parts around the insert. */ + const T *src = (T*) (head + 1); + T *dst = BaseTable::data; + for ( i = 0; i < pos; i++, dst++, src++ ) + new(dst) T(*src); + + /* ... and the second half. */ + for ( dst += len; i < head->tabLen; i++, src++, dst++ ) + new(dst) T(*src); + } + } + else { + /* There is no existing data. Start from zero. This will set the + * length. */ + upResizeFromEmpty( len ); + } +} + + +#ifdef AAPL_NAMESPACE +} +#endif + + +#endif /* _AAPL_SVECTOR_H */ diff --git a/src/aapl/table.h b/src/aapl/table.h new file mode 100644 index 00000000..303f473e --- /dev/null +++ b/src/aapl/table.h @@ -0,0 +1,253 @@ +/* + * Copyright 2001, 2002 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_TABLE_H +#define _AAPL_TABLE_H + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup vector + * @{ + */ + +/** \class Table + * \brief Base class for dynamic arrays. + * + * Table is used as the common data storage class for vectors. It does not + * provide any methods to operate on the data and as such it is not intended + * to be used directly. It exists so that algorithms that operatate on dynamic + * arrays can be written without knowing about the various vector classes that + * my exist. + */ + +/*@}*/ + +/* Table class. */ +template class Table +{ +public: + /* Default Constructor. */ + inline Table(); + + /** + * \brief Get the length of the vector. + * + * \returns the length of the vector. + */ + long length() const + { return tabLen; } + + /** + * \brief Table data. + * + * The pointer to the elements in the vector. Modifying the vector may + * cause this pointer to change. + */ + T *data; + + /** + * \brief Table length. + * + * The number of items of type T in the table. + */ + long tabLen; + + /** + * \brief Allocated length. + * + * The number of items for which there is room in the current allocation. + */ + long allocLen; +}; + +/** + * \brief Default constructor + * + * Initialize table data to empty. + */ +template inline Table::Table() +: + data(0), + tabLen(0), + allocLen(0) +{ +} + +/* Default shared table header class. */ +struct STabHead +{ + /** + * \brief Table length. + * + * The number of items of type T in the table. + */ + long tabLen; + + /** + * \brief Allocated length. + * + * The number of items for which there is room in the current allocation. + */ + long allocLen; + + /** + * \brief Ref Count. + * + * The number of shared vectors referencing this data. + */ + long refCount; +}; + +/** + * \addtogroup vector + * @{ + */ + +/** \class STable + * \brief Base class for implicitly shared dynamic arrays. + * + * STable is used as the common data storage class for shared vectors. It does + * not provide any methods to operate on the data and as such it is not + * intended to be used directly. It exists so that algorithms that operatate + * on dynamic arrays can be written without knowing about the various shared + * vector classes that my exist. + */ + +/*@}*/ + +/* STable class. */ +template class STable +{ +public: + /* Default Constructor. */ + inline STable(); + + /** + * \brief Get the length of the shared vector. + * + * \returns the length of the shared vector. + */ + long length() const + { return data == 0 ? 0 : (((STabHead*)data) - 1)->tabLen; } + + /** + * \brief Get header of the shared vector. + * + * \returns the header of the shared vector. + */ + STabHead *header() const + { return data == 0 ? 0 : (((STabHead*)data) - 1); } + + /** + * \brief Table data. + * + * The pointer to the elements in the vector. The shared table header is + * located just behind the data. Modifying the vector may cause this + * pointer to change. + */ + T *data; +}; + +/** + * \brief Default constructor + * + * Initialize shared table data to empty. + */ +template inline STable::STable() +: + data(0) +{ +} + +/* If needed is greater than existing, give twice needed. */ +#define EXPN_UP( existing, needed ) \ + needed > existing ? (needed<<1) : existing + +/* If needed is less than 1 quarter existing, give twice needed. */ +#define EXPN_DOWN( existing, needed ) \ + needed < (existing>>2) ? (needed<<1) : existing + +/** + * \addtogroup vector + * @{ + */ + +/** \class ResizeExpn + * \brief Exponential table resizer. + * + * ResizeExpn is the default table resizer. When an up resize is needed, space + * is doubled. When a down resize is needed, space is halved. The result is + * that when growing the vector in a linear fashion, the number of resizes of + * the allocated space behaves logarithmically. + * + * If only up resizes are done, there will never be more than 2 times the + * needed space allocated. If down resizes are done as well, there will never + * be more than 4 times the needed space allocated. ResizeExpn uses this 50% + * usage policy on up resizing and 25% usage policy on down resizing to + * improve performance when repeatedly inserting and removing a small number + * of elements relative to the size of the array. This scheme guarantees that + * repetitive inserting and removing of a small number of elements will never + * result in repetative reallocation. + * + * The sizes passed to the resizer from the vectors are in units of T. + */ + +/*@}*/ + +/* Exponential resizer. */ +class ResizeExpn +{ +protected: + /** + * \brief Determine the new table size when up resizing. + * + * If the existing size is insufficient for the space needed then allocate + * twice the space needed. Otherwise use the existing size. + * + * \returns The new table size. + */ + static inline long upResize( long existing, long needed ) + { return EXPN_UP( existing, needed ); } + + /** + * \brief Determine the new table size when down resizing. + * + * If the space needed is less than one quarter of the existing size then + * allocate twice the space needed. Otherwise use the exitsing size. + * + * \returns The new table size. + */ + static inline long downResize( long existing, long needed ) + { return EXPN_DOWN( existing, needed ); } +}; + +#undef EXPN_UP +#undef EXPN_DOWN + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_TABLE_H */ diff --git a/src/aapl/vector.h b/src/aapl/vector.h new file mode 100644 index 00000000..0ec385d5 --- /dev/null +++ b/src/aapl/vector.h @@ -0,0 +1,1190 @@ +/* + * Copyright 2002, 2006 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _AAPL_VECTOR_H +#define _AAPL_VECTOR_H + +#include +#include +#include +#include +#include "table.h" + +#ifdef AAPL_NAMESPACE +namespace Aapl { +#endif + +/** + * \addtogroup vector + * @{ + */ + +/** \class Vector + * \brief Dynamic array. + * + * This is typical vector implementation. It is a dynamic array that can be + * used to contain complex data structures that have constructors and + * destructors as well as simple types such as integers and pointers. + * + * Vector supports inserting, overwriting, and removing single or multiple + * elements at once. Constructors and destructors are called wherever + * appropriate. For example, before an element is overwritten, it's + * destructor is called. + * + * Vector provides automatic resizing of allocated memory as needed and offers + * different allocation schemes for controlling how the automatic allocation + * is done. Two senses of the the length of the data is maintained: the + * amount of raw memory allocated to the vector and the number of actual + * elements in the vector. The various allocation schemes control how the + * allocated space is changed in relation to the number of elements in the + * vector. + * + * \include ex_vector.cpp + */ + +/*@}*/ + +template < class T, class Resize = ResizeExpn > class Vector + : public Table, public Resize +{ +private: + typedef Table BaseTable; + +public: + /** + * \brief Initialize an empty vector with no space allocated. + * + * If a linear resizer is used, the step defaults to 256 units of T. For a + * runtime vector both up and down allocation schemes default to + * Exponential. + */ + Vector() { } + + /** + * \brief Create a vector that contains an initial element. + * + * The vector becomes one element in length. The element's copy + * constructor is used to place the value in the vector. + */ + Vector(const T &val) { setAs(&val, 1); } + + /** + * \brief Create a vector that contains an array of elements. + * + * The vector becomes len elements in length. Copy constructors are used + * to place the new elements in the vector. + */ + Vector(const T *val, long len) { setAs(val, len); } + + /* Deep copy. */ + Vector( const Vector &v ); + + /* Free all mem used by the vector. */ + ~Vector() { empty(); } + + /* Delete all items. */ + void empty(); + + /* Abandon the contents of the vector without deleteing. */ + void abandon(); + + /* Transfers the elements of another vector into this vector. First emptys + * the current vector. */ + void transfer( Vector &v ); + + /* Perform a deep copy of another vector into this vector. */ + Vector &operator=( const Vector &v ); + + /* Stack operations. */ + void push( const T &t ) { append( t ); } + void pop() { remove( BaseTable::tabLen - 1 ); } + T &top() { return BaseTable::data[BaseTable::tabLen - 1]; } + + /*@{*/ + /** + * \brief Insert one element at position pos. + * + * Elements in the vector from pos onward are shifted one space to the + * right. The copy constructor is used to place the element into this + * vector. If pos is greater than the length of the vector then undefined + * behaviour results. If pos is negative then it is treated as an offset + * relative to the length of the vector. + */ + void insert(long pos, const T &val) { insert(pos, &val, 1); } + + /* Insert an array of values. */ + void insert(long pos, const T *val, long len); + + /** + * \brief Insert all the elements from another vector at position pos. + * + * Elements in this vector from pos onward are shifted v.tabLen spaces to + * the right. The element's copy constructor is used to copy the items + * into this vector. The other vector is left unchanged. If pos is off the + * end of the vector, then undefined behaviour results. If pos is negative + * then it is treated as an offset relative to the length of the vector. + * Equivalent to vector.insert(pos, other.data, other.tabLen). + */ + void insert(long pos, const Vector &v) { insert(pos, v.data, v.tabLen); } + + /* Insert len copies of val into the vector. */ + void insertDup(long pos, const T &val, long len); + + /** + * \brief Insert one new element using the default constrcutor. + * + * Elements in the vector from pos onward are shifted one space to the + * right. The default constructor is used to init the new element. If pos + * is greater than the length of the vector then undefined behaviour + * results. If pos is negative then it is treated as an offset relative to + * the length of the vector. + */ + void insertNew(long pos) { insertNew(pos, 1); } + + /* Insert len new items using default constructor. */ + void insertNew(long pos, long len); + /*@}*/ + + /*@{*/ + /** + * \brief Remove one element at position pos. + * + * The element's destructor is called. Elements to the right of pos are + * shifted one space to the left to take up the free space. If pos is greater + * than or equal to the length of the vector then undefined behavior results. + * If pos is negative then it is treated as an offset relative to the length + * of the vector. + */ + void remove(long pos) { remove(pos, 1); } + + /* Delete a number of elements. */ + void remove(long pos, long len); + /*@}*/ + + /*@{*/ + /** + * \brief Replace one element at position pos. + * + * If there is an existing element at position pos (if pos is less than + * the length of the vector) then its destructor is called before the + * space is used. The copy constructor is used to place the element into + * the vector. If pos is greater than the length of the vector then + * undefined behaviour results. If pos is negative then it is treated as + * an offset relative to the length of the vector. + */ + void replace(long pos, const T &val) { replace(pos, &val, 1); } + + /* Replace with an array of values. */ + void replace(long pos, const T *val, long len); + + /** + * \brief Replace at position pos with all the elements of another vector. + * + * Replace at position pos with all the elements of another vector. The + * other vector is left unchanged. If there are existing elements at the + * positions to be replaced, then destructors are called before the space + * is used. Copy constructors are used to place the elements into this + * vector. It is allowable for the pos and length of the other vector to + * specify a replacement that overwrites existing elements and creates new + * ones. If pos is greater than the length of the vector then undefined + * behaviour results. If pos is negative, then it is treated as an offset + * relative to the length of the vector. + */ + void replace(long pos, const Vector &v) { replace(pos, v.data, v.tabLen); } + + /* Replace len items with len copies of val. */ + void replaceDup(long pos, const T &val, long len); + + /** + * \brief Replace at position pos with one new element. + * + * If there is an existing element at the position to be replaced (pos is + * less than the length of the vector) then the element's destructor is + * called before the space is used. The default constructor is used to + * initialize the new element. If pos is greater than the length of the + * vector then undefined behaviour results. If pos is negative, then it is + * treated as an offset relative to the length of the vector. + */ + void replaceNew(long pos) { replaceNew(pos, 1); } + + /* Replace len items at pos with newly constructed objects. */ + void replaceNew(long pos, long len); + /*@}*/ + + /*@{*/ + /** + * \brief Set the contents of the vector to be val exactly. + * + * The vector becomes one element in length. Destructors are called on any + * existing elements in the vector. The element's copy constructor is used + * to place the val in the vector. + */ + void setAs(const T &val) { setAs(&val, 1); } + + /* Set to the contents of an array. */ + void setAs(const T *val, long len); + + /** + * \brief Set the vector to exactly the contents of another vector. + * + * The vector becomes v.tabLen elements in length. Destructors are called + * on any existing elements. Copy constructors are used to place the new + * elements in the vector. + */ + void setAs(const Vector &v) { setAs(v.data, v.tabLen); } + + /* Set as len copies of item. */ + void setAsDup(const T &item, long len); + + /** + * \brief Set the vector to exactly one new item. + * + * The vector becomes one element in length. Destructors are called on any + * existing elements in the vector. The default constructor is used to + * init the new item. + */ + void setAsNew() { setAsNew(1); } + + /* Set as newly constructed objects using the default constructor. */ + void setAsNew(long len); + /*@}*/ + + /*@{*/ + /** + * \brief Append one elment to the end of the vector. + * + * Copy constructor is used to place the element in the vector. + */ + void append(const T &val) { replace(BaseTable::tabLen, &val, 1); } + + /** + * \brief Append len elements to the end of the vector. + * + * Copy constructors are used to place the elements in the vector. + */ + void append(const T *val, long len) { replace(BaseTable::tabLen, val, len); } + + /** + * \brief Append the contents of another vector. + * + * The other vector is left unchanged. Copy constructors are used to place the + * elements in the vector. + */ + void append(const Vector &v) { replace(BaseTable::tabLen, v.data, v.tabLen); } + + /** + * \brief Append len copies of item. + * + * The copy constructor is used to place the item in the vector. + */ + void appendDup(const T &item, long len) { replaceDup(BaseTable::tabLen, item, len); } + + /** + * \brief Append a single newly created item. + * + * The new element is initialized with the default constructor. + */ + void appendNew() { replaceNew(BaseTable::tabLen, 1); } + + /** + * \brief Append len newly created items. + * + * The new elements are initialized with the default constructor. + */ + void appendNew(long len) { replaceNew(BaseTable::tabLen, len); } + /*@}*/ + + /*@{*/ + /** \fn Vector::prepend(const T &val) + * \brief Prepend one elment to the front of the vector. + * + * Copy constructor is used to place the element in the vector. + */ + void prepend(const T &val) { insert(0, &val, 1); } + + /** + * \brief Prepend len elements to the front of the vector. + * + * Copy constructors are used to place the elements in the vector. + */ + void prepend(const T *val, long len) { insert(0, val, len); } + + /** + * \brief Prepend the contents of another vector. + * + * The other vector is left unchanged. Copy constructors are used to place the + * elements in the vector. + */ + void prepend(const Vector &v) { insert(0, v.data, v.tabLen); } + + /** + * \brief Prepend len copies of item. + * + * The copy constructor is used to place the item in the vector. + */ + void prependDup(const T &item, long len) { insertDup(0, item, len); } + + /** + * \brief Prepend a single newly created item. + * + * The new element is initialized with the default constructor. + */ + void prependNew() { insertNew(0, 1); } + + /** + * \brief Prepend len newly created items. + * + * The new elements are initialized with the default constructor. + */ + void prependNew(long len) { insertNew(0, len); } + /*@}*/ + + /* Convenience access. */ + T &operator[](int i) const { return BaseTable::data[i]; } + long size() const { return BaseTable::tabLen; } + + /* Forward this so a ref can be used. */ + struct Iter; + + /* Various classes for setting the iterator */ + struct IterFirst { IterFirst( const Vector &v ) : v(v) { } const Vector &v; }; + struct IterLast { IterLast( const Vector &v ) : v(v) { } const Vector &v; }; + struct IterNext { IterNext( const Iter &i ) : i(i) { } const Iter &i; }; + struct IterPrev { IterPrev( const Iter &i ) : i(i) { } const Iter &i; }; + + /** + * \brief Vector Iterator. + * \ingroup iterators + */ + struct Iter + { + /* Construct, assign. */ + Iter() : ptr(0), ptrBeg(0), ptrEnd(0) { } + + /* Construct. */ + Iter( const Vector &v ); + Iter( const IterFirst &vf ); + Iter( const IterLast &vl ); + inline Iter( const IterNext &vn ); + inline Iter( const IterPrev &vp ); + + /* Assign. */ + Iter &operator=( const Vector &v ); + Iter &operator=( const IterFirst &vf ); + Iter &operator=( const IterLast &vl ); + inline Iter &operator=( const IterNext &vf ); + inline Iter &operator=( const IterPrev &vl ); + + /** \brief Less than end? */ + bool lte() const { return ptr != ptrEnd; } + + /** \brief At end? */ + bool end() const { return ptr == ptrEnd; } + + /** \brief Greater than beginning? */ + bool gtb() const { return ptr != ptrBeg; } + + /** \brief At beginning? */ + bool beg() const { return ptr == ptrBeg; } + + /** \brief At first element? */ + bool first() const { return ptr == ptrBeg+1; } + + /** \brief At last element? */ + bool last() const { return ptr == ptrEnd-1; } + + /* Return the position. */ + long pos() const { return ptr - ptrBeg - 1; } + T &operator[](int i) const { return ptr[i]; } + + /** \brief Implicit cast to T*. */ + operator T*() const { return ptr; } + + /** \brief Dereference operator returns T&. */ + T &operator *() const { return *ptr; } + + /** \brief Arrow operator returns T*. */ + T *operator->() const { return ptr; } + + /** \brief Move to next item. */ + T *operator++() { return ++ptr; } + + /** \brief Move to next item. */ + T *operator++(int) { return ptr++; } + + /** \brief Move to next item. */ + T *increment() { return ++ptr; } + + /** \brief Move n items forward. */ + T *operator+=(long n) { return ptr+=n; } + + /** \brief Move to previous item. */ + T *operator--() { return --ptr; } + + /** \brief Move to previous item. */ + T *operator--(int) { return ptr--; } + + /** \brief Move to previous item. */ + T *decrement() { return --ptr; } + + /** \brief Move n items back. */ + T *operator-=(long n) { return ptr-=n; } + + /** \brief Return the next item. Does not modify this. */ + inline IterNext next() const { return IterNext(*this); } + + /** \brief Return the previous item. Does not modify this. */ + inline IterPrev prev() const { return IterPrev(*this); } + + /** \brief The iterator is simply a pointer. */ + T *ptr; + + /* For testing endpoints. */ + T *ptrBeg, *ptrEnd; + }; + + /** \brief Return first element. */ + IterFirst first() { return IterFirst( *this ); } + + /** \brief Return last element. */ + IterLast last() { return IterLast( *this ); } + +protected: + void makeRawSpaceFor(long pos, long len); + + void upResize(long len); + void downResize(long len); +}; + +/* Init a vector iterator with just a vector. */ +template Vector::Iter::Iter( const Vector &v ) +{ + if ( v.tabLen == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = v.data; + ptrBeg = v.data-1; + ptrEnd = v.data+v.tabLen; + } +} + +/* Init a vector iterator with the first of a vector. */ +template Vector::Iter::Iter( + const IterFirst &vf ) +{ + if ( vf.v.tabLen == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vf.v.data; + ptrBeg = vf.v.data-1; + ptrEnd = vf.v.data+vf.v.tabLen; + } +} + +/* Init a vector iterator with the last of a vector. */ +template Vector::Iter::Iter( + const IterLast &vl ) +{ + if ( vl.v.tabLen == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vl.v.data+vl.v.tabLen-1; + ptrBeg = vl.v.data-1; + ptrEnd = vl.v.data+vl.v.tabLen; + } +} + +/* Init a vector iterator with the next of some other iterator. */ +template Vector::Iter::Iter( + const IterNext &vn ) +: + ptr(vn.i.ptr+1), + ptrBeg(vn.i.ptrBeg), + ptrEnd(vn.i.ptrEnd) +{ +} + +/* Init a vector iterator with the prev of some other iterator. */ +template Vector::Iter::Iter( + const IterPrev &vp ) +: + ptr(vp.i.ptr-1), + ptrBeg(vp.i.ptrBeg), + ptrEnd(vp.i.ptrEnd) +{ +} + +/* Set a vector iterator with some vector. */ +template typename Vector::Iter & + Vector::Iter::operator=( const Vector &v ) +{ + if ( v.tabLen == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = v.data; + ptrBeg = v.data-1; + ptrEnd = v.data+v.tabLen; + } + return *this; +} + +/* Set a vector iterator with the first element in a vector. */ +template typename Vector::Iter & + Vector::Iter::operator=( const IterFirst &vf ) +{ + if ( vf.v.tabLen == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vf.v.data; + ptrBeg = vf.v.data-1; + ptrEnd = vf.v.data+vf.v.tabLen; + } + return *this; +} + +/* Set a vector iterator with the last element in a vector. */ +template typename Vector::Iter & + Vector::Iter::operator=( const IterLast &vl ) +{ + if ( vl.v.tabLen == 0 ) + ptr = ptrBeg = ptrEnd = 0; + else { + ptr = vl.v.data+vl.v.tabLen-1; + ptrBeg = vl.v.data-1; + ptrEnd = vl.v.data+vl.v.tabLen; + } + return *this; +} + +/* Set a vector iterator with the next of some other iterator. */ +template typename Vector::Iter & + Vector::Iter::operator=( const IterNext &vn ) +{ + ptr = vn.i.ptr+1; + ptrBeg = vn.i.ptrBeg; + ptrEnd = vn.i.ptrEnd; + return *this; +} + +/* Set a vector iterator with the prev of some other iterator. */ +template typename Vector::Iter & + Vector::Iter::operator=( const IterPrev &vp ) +{ + ptr = vp.i.ptr-1; + ptrBeg = vp.i.ptrBeg; + ptrEnd = vp.i.ptrEnd; + return *this; +} + +/** + * \brief Forget all elements in the vector. + * + * The contents of the vector are reset to null without without the space + * being freed. + */ +template void Vector:: + abandon() +{ + BaseTable::data = 0; + BaseTable::tabLen = 0; + BaseTable::allocLen = 0; +} + +/** + * \brief Transfer the contents of another vector into this vector. + * + * The dynamic array of the other vector is moved into this vector by + * reference. If this vector is non-empty then its contents are first deleted. + * Afterward the other vector will be empty. + */ +template void Vector:: + transfer( Vector &v ) +{ + empty(); + + BaseTable::data = v.data; + BaseTable::tabLen = v.tabLen; + BaseTable::allocLen = v.allocLen; + + v.abandon(); +} + +/** + * \brief Deep copy another vector into this vector. + * + * Copies the entire contents of the other vector into this vector. Any + * existing contents are first deleted. Equivalent to setAs. + * + * \returns A reference to this. + */ +template Vector &Vector:: + operator=( const Vector &v ) +{ + setAs(v.data, v.tabLen); + return *this; +} + +/* Up resize the data for len elements using Resize::upResize to tell us the + * new tabLen. Reads and writes allocLen. Does not read or write tabLen. */ +template void Vector:: + upResize(long len) +{ + /* Ask the resizer what the new tabLen will be. */ + long newLen = Resize::upResize(BaseTable::allocLen, len); + + /* Did the data grow? */ + if ( newLen > BaseTable::allocLen ) { + BaseTable::allocLen = newLen; + if ( BaseTable::data != 0 ) { + /* Table exists already, resize it up. */ + BaseTable::data = (T*) realloc( BaseTable::data, sizeof(T) * newLen ); + if ( BaseTable::data == 0 ) + throw std::bad_alloc(); + } + else { + /* Create the data. */ + BaseTable::data = (T*) malloc( sizeof(T) * newLen ); + if ( BaseTable::data == 0 ) + throw std::bad_alloc(); + } + } +} + +/* Down resize the data for len elements using Resize::downResize to determine + * the new tabLen. Reads and writes allocLen. Does not read or write tabLen. */ +template void Vector:: + downResize(long len) +{ + /* Ask the resizer what the new tabLen will be. */ + long newLen = Resize::downResize( BaseTable::allocLen, len ); + + /* Did the data shrink? */ + if ( newLen < BaseTable::allocLen ) { + BaseTable::allocLen = newLen; + if ( newLen == 0 ) { + /* Simply free the data. */ + free( BaseTable::data ); + BaseTable::data = 0; + } + else { + /* Not shrinking to size zero, realloc it to the smaller size. */ + BaseTable::data = (T*) realloc( BaseTable::data, sizeof(T) * newLen ); + if ( BaseTable::data == 0 ) + throw std::bad_alloc(); + } + } +} + +/** + * \brief Perform a deep copy of the vector. + * + * The contents of the other vector are copied into this vector. This vector + * gets the same allocation size as the other vector. All items are copied + * using the element's copy constructor. + */ +template Vector:: + Vector(const Vector &v) +{ + BaseTable::tabLen = v.tabLen; + BaseTable::allocLen = v.allocLen; + + if ( BaseTable::allocLen > 0 ) { + /* Allocate needed space. */ + BaseTable::data = (T*) malloc(sizeof(T) * BaseTable::allocLen); + if ( BaseTable::data == 0 ) + throw std::bad_alloc(); + + /* If there are any items in the src data, copy them in. */ + T *dst = BaseTable::data, *src = v.data; + for (long pos = 0; pos < BaseTable::tabLen; pos++, dst++, src++ ) + new(dst) T(*src); + } + else { + /* Nothing allocated. */ + BaseTable::data = 0; + } +} + +/** \fn Vector::~Vector() + * \brief Free all memory used by the vector. + * + * The vector is reset to zero elements. Destructors are called on all + * elements in the vector. The space allocated for the vector is freed. + */ + + +/** + * \brief Free all memory used by the vector. + * + * The vector is reset to zero elements. Destructors are called on all + * elements in the vector. The space allocated for the vector is freed. + */ +template void Vector:: + empty() +{ + if ( BaseTable::data != 0 ) { + /* Call All destructors. */ + T *pos = BaseTable::data; + for ( long i = 0; i < BaseTable::tabLen; pos++, i++ ) + pos->~T(); + + /* Free the data space. */ + free( BaseTable::data ); + BaseTable::data = 0; + BaseTable::tabLen = BaseTable::allocLen = 0; + } +} + +/** + * \brief Set the contents of the vector to be len elements exactly. + * + * The vector becomes len elements in length. Destructors are called on any + * existing elements in the vector. Copy constructors are used to place the + * new elements in the vector. + */ +template void Vector:: + setAs(const T *val, long len) +{ + /* Call All destructors. */ + long i; + T *pos = BaseTable::data; + for ( i = 0; i < BaseTable::tabLen; pos++, i++ ) + pos->~T(); + + /* Adjust the allocated length. */ + if ( len < BaseTable::tabLen ) + downResize( len ); + else if ( len > BaseTable::tabLen ) + upResize( len ); + + /* Set the new data length to exactly len. */ + BaseTable::tabLen = len; + + /* Copy data in. */ + T *dst = BaseTable::data; + const T *src = val; + for ( i = 0; i < len; i++, dst++, src++ ) + new(dst) T(*src); +} + +/** + * \brief Set the vector to len copies of item. + * + * The vector becomes len elements in length. Destructors are called on any + * existing elements in the vector. The element's copy constructor is used to + * copy the item into the vector. + */ +template void Vector:: + setAsDup(const T &item, long len) +{ + /* Call All destructors. */ + T *pos = BaseTable::data; + for ( long i = 0; i < BaseTable::tabLen; pos++, i++ ) + pos->~T(); + + /* Adjust the allocated length. */ + if ( len < BaseTable::tabLen ) + downResize( len ); + else if ( len > BaseTable::tabLen ) + upResize( len ); + + /* Set the new data length to exactly len. */ + BaseTable::tabLen = len; + + /* Copy item in one spot at a time. */ + T *dst = BaseTable::data; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(item); +} + +/** + * \brief Set the vector to exactly len new items. + * + * The vector becomes len elements in length. Destructors are called on any + * existing elements in the vector. Default constructors are used to init the + * new items. + */ +template void Vector:: + setAsNew(long len) +{ + /* Call All destructors. */ + T *pos = BaseTable::data; + for ( long i = 0; i < BaseTable::tabLen; pos++, i++ ) + pos->~T(); + + /* Adjust the allocated length. */ + if ( len < BaseTable::tabLen ) + downResize( len ); + else if ( len > BaseTable::tabLen ) + upResize( len ); + + /* Set the new data length to exactly len. */ + BaseTable::tabLen = len; + + /* Create items using default constructor. */ + T *dst = BaseTable::data; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(); +} + + +/** + * \brief Replace len elements at position pos. + * + * If there are existing elements at the positions to be replaced, then + * destructors are called before the space is used. Copy constructors are used + * to place the elements into the vector. It is allowable for the pos and + * length to specify a replacement that overwrites existing elements and + * creates new ones. If pos is greater than the length of the vector then + * undefined behaviour results. If pos is negative, then it is treated as an + * offset relative to the length of the vector. + */ +template void Vector:: + replace(long pos, const T *val, long len) +{ + long endPos, i; + T *item; + + /* If we are given a negative position to replace at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* The end is the one past the last item that we want + * to write to. */ + endPos = pos + len; + + /* Make sure we have enough space. */ + if ( endPos > BaseTable::tabLen ) { + upResize( endPos ); + + /* Delete any objects we need to delete. */ + item = BaseTable::data + pos; + for ( i = pos; i < BaseTable::tabLen; i++, item++ ) + item->~T(); + + /* We are extending the vector, set the new data length. */ + BaseTable::tabLen = endPos; + } + else { + /* Delete any objects we need to delete. */ + item = BaseTable::data + pos; + for ( i = pos; i < endPos; i++, item++ ) + item->~T(); + } + + /* Copy data in using copy constructor. */ + T *dst = BaseTable::data + pos; + const T *src = val; + for ( i = 0; i < len; i++, dst++, src++ ) + new(dst) T(*src); +} + +/** + * \brief Replace at position pos with len copies of an item. + * + * If there are existing elements at the positions to be replaced, then + * destructors are called before the space is used. The copy constructor is + * used to place the element into this vector. It is allowable for the pos and + * length to specify a replacement that overwrites existing elements and + * creates new ones. If pos is greater than the length of the vector then + * undefined behaviour results. If pos is negative, then it is treated as an + * offset relative to the length of the vector. + */ +template void Vector:: + replaceDup(long pos, const T &val, long len) +{ + long endPos, i; + T *item; + + /* If we are given a negative position to replace at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* The end is the one past the last item that we want + * to write to. */ + endPos = pos + len; + + /* Make sure we have enough space. */ + if ( endPos > BaseTable::tabLen ) { + upResize( endPos ); + + /* Delete any objects we need to delete. */ + item = BaseTable::data + pos; + for ( i = pos; i < BaseTable::tabLen; i++, item++ ) + item->~T(); + + /* We are extending the vector, set the new data length. */ + BaseTable::tabLen = endPos; + } + else { + /* Delete any objects we need to delete. */ + item = BaseTable::data + pos; + for ( i = pos; i < endPos; i++, item++ ) + item->~T(); + } + + /* Copy data in using copy constructor. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(val); +} + +/** + * \brief Replace at position pos with len new elements. + * + * If there are existing elements at the positions to be replaced, then + * destructors are called before the space is used. The default constructor is + * used to initialize the new elements. It is allowable for the pos and length + * to specify a replacement that overwrites existing elements and creates new + * ones. If pos is greater than the length of the vector then undefined + * behaviour results. If pos is negative, then it is treated as an offset + * relative to the length of the vector. + */ +template void Vector:: + replaceNew(long pos, long len) +{ + long endPos, i; + T *item; + + /* If we are given a negative position to replace at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* The end is the one past the last item that we want + * to write to. */ + endPos = pos + len; + + /* Make sure we have enough space. */ + if ( endPos > BaseTable::tabLen ) { + upResize( endPos ); + + /* Delete any objects we need to delete. */ + item = BaseTable::data + pos; + for ( i = pos; i < BaseTable::tabLen; i++, item++ ) + item->~T(); + + /* We are extending the vector, set the new data length. */ + BaseTable::tabLen = endPos; + } + else { + /* Delete any objects we need to delete. */ + item = BaseTable::data + pos; + for ( i = pos; i < endPos; i++, item++ ) + item->~T(); + } + + /* Copy data in using copy constructor. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(); +} + +/** + * \brief Remove len elements at position pos. + * + * Destructor is called on all elements removed. Elements to the right of pos + * are shifted len spaces to the left to take up the free space. If pos is + * greater than or equal to the length of the vector then undefined behavior + * results. If pos is negative then it is treated as an offset relative to the + * length of the vector. + */ +template void Vector:: + remove(long pos, long len) +{ + long newLen, lenToSlideOver, endPos; + T *dst, *item; + + /* If we are given a negative position to remove at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* The first position after the last item deleted. */ + endPos = pos + len; + + /* The new data length. */ + newLen = BaseTable::tabLen - len; + + /* The place in the data we are deleting at. */ + dst = BaseTable::data + pos; + + /* Call Destructors. */ + item = dst; + for ( long i = 0; i < len; i += 1, item += 1 ) + item->~T(); + + /* Shift data over if necessary. */ + lenToSlideOver = BaseTable::tabLen - endPos; + if ( len > 0 && lenToSlideOver > 0 ) + memmove(dst, dst + len, sizeof(T)*lenToSlideOver); + + /* Shrink the data if necessary. */ + downResize( newLen ); + + /* Set the new data length. */ + BaseTable::tabLen = newLen; +} + +/** + * \brief Insert len elements at position pos. + * + * Elements in the vector from pos onward are shifted len spaces to the right. + * The copy constructor is used to place the elements into this vector. If pos + * is greater than the length of the vector then undefined behaviour results. + * If pos is negative then it is treated as an offset relative to the length + * of the vector. + */ +template void Vector:: + insert(long pos, const T *val, long len) +{ + /* If we are given a negative position to insert at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* Calculate the new length. */ + long newLen = BaseTable::tabLen + len; + + /* Up resize, we are growing. */ + upResize( newLen ); + + /* Shift over data at insert spot if needed. */ + if ( len > 0 && pos < BaseTable::tabLen ) { + memmove(BaseTable::data + pos + len, BaseTable::data + pos, + sizeof(T)*(BaseTable::tabLen-pos)); + } + + /* Copy data in element by element. */ + T *dst = BaseTable::data + pos; + const T *src = val; + for ( long i = 0; i < len; i++, dst++, src++ ) + new(dst) T(*src); + + /* Set the new length. */ + BaseTable::tabLen = newLen; +} + +/** + * \brief Insert len copies of item at position pos. + * + * Elements in the vector from pos onward are shifted len spaces to the right. + * The copy constructor is used to place the element into this vector. If pos + * is greater than the length of the vector then undefined behaviour results. + * If pos is negative then it is treated as an offset relative to the length + * of the vector. + */ +template void Vector:: + insertDup(long pos, const T &item, long len) +{ + /* If we are given a negative position to insert at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* Calculate the new length. */ + long newLen = BaseTable::tabLen + len; + + /* Up resize, we are growing. */ + upResize( newLen ); + + /* Shift over data at insert spot if needed. */ + if ( len > 0 && pos < BaseTable::tabLen ) { + memmove(BaseTable::data + pos + len, BaseTable::data + pos, + sizeof(T)*(BaseTable::tabLen-pos)); + } + + /* Copy the data item in one at a time. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(item); + + /* Set the new length. */ + BaseTable::tabLen = newLen; +} + +/** + * \brief Insert len new elements using the default constructor. + * + * Elements in the vector from pos onward are shifted len spaces to the right. + * Default constructors are used to init the new elements. If pos is off the + * end of the vector then undefined behaviour results. If pos is negative then + * it is treated as an offset relative to the length of the vector. + */ +template void Vector:: + insertNew(long pos, long len) +{ + /* If we are given a negative position to insert at then + * treat it as a position relative to the length. */ + if ( pos < 0 ) + pos = BaseTable::tabLen + pos; + + /* Calculate the new length. */ + long newLen = BaseTable::tabLen + len; + + /* Up resize, we are growing. */ + upResize( newLen ); + + /* Shift over data at insert spot if needed. */ + if ( len > 0 && pos < BaseTable::tabLen ) { + memmove(BaseTable::data + pos + len, BaseTable::data + pos, + sizeof(T)*(BaseTable::tabLen-pos)); + } + + /* Init new data with default constructors. */ + T *dst = BaseTable::data + pos; + for ( long i = 0; i < len; i++, dst++ ) + new(dst) T(); + + /* Set the new length. */ + BaseTable::tabLen = newLen; +} + +/* Makes space for len items, Does not init the items in any way. If pos is + * greater than the length of the vector then undefined behaviour results. + * Updates the length of the vector. */ +template void Vector:: + makeRawSpaceFor(long pos, long len) +{ + /* Calculate the new length. */ + long newLen = BaseTable::tabLen + len; + + /* Up resize, we are growing. */ + upResize( newLen ); + + /* Shift over data at insert spot if needed. */ + if ( len > 0 && pos < BaseTable::tabLen ) { + memmove(BaseTable::data + pos + len, BaseTable::data + pos, + sizeof(T)*(BaseTable::tabLen-pos)); + } + + /* Save the new length. */ + BaseTable::tabLen = newLen; +} + +#ifdef AAPL_NAMESPACE +} +#endif + +#endif /* _AAPL_VECTOR_H */ diff --git a/src/aapl/version.mk b/src/aapl/version.mk new file mode 100644 index 00000000..fa74770a --- /dev/null +++ b/src/aapl/version.mk @@ -0,0 +1,2 @@ +VERSION = 2.14 +PUBDATE = March 2006 diff --git a/src/cgil/.gitignore b/src/cgil/.gitignore new file mode 100644 index 00000000..9ee64540 --- /dev/null +++ b/src/cgil/.gitignore @@ -0,0 +1,2 @@ +/Makefile.in +/Makefile diff --git a/src/cgil/Makefile.am b/src/cgil/Makefile.am new file mode 100644 index 00000000..57d12e80 --- /dev/null +++ b/src/cgil/Makefile.am @@ -0,0 +1,9 @@ + +data_DATA = $(CGIL_FILES) + +CGIL_FILES = ril.lm rlhc-main.lm \ + rlhc-c.lm rlhc-csharp.lm rlhc-go.lm rlhc-js.lm rlhc-ruby.lm \ + rlhc-crack.lm rlhc-d.lm rlhc-java.lm rlhc-julia.lm rlhc-ocaml.lm rlhc-rust.lm + +EXTRA_DIST = $(CGIL_FILES) + diff --git a/src/cgil/ragel.lm b/src/cgil/ragel.lm new file mode 100644 index 00000000..1325bbb3 --- /dev/null +++ b/src/cgil/ragel.lm @@ -0,0 +1,1027 @@ + +global GblActionParams: bool = false +global GblMachineMap: map = new map() +global GblCurMachine: machine +global GblTargetMachine: str = "" +global GblSearchMachine: str = "" +global GblWantSection: bool = false +global GblIncludeDepth: int = 0 +global GblImport: bool = false +global GblFileName: str = "" +global GblIncludePaths: list = new list() + +struct saved_globals + FileName: str + TargetMachine: str + SearchMachine: str + WantSection: bool + IncludeDepth: int + Import: bool +end + +global GblSavedGlobals: list = new list() + +void saveGlobals() +{ + new SG: saved_globals() + + SG->FileName = GblFileName + SG->TargetMachine = GblTargetMachine + SG->SearchMachine = GblSearchMachine + SG->WantSection = GblWantSection + SG->Import = GblImport + SG->IncludeDepth = GblIncludeDepth + + GblSavedGlobals->push_tail( SG ) +} + +void restoreGlobals() +{ + SG: saved_globals = GblSavedGlobals->pop_tail() + + GblFileName = SG->FileName + GblTargetMachine = SG->TargetMachine + GblSearchMachine = SG->SearchMachine + GblWantSection = SG->WantSection + GblImport = SG->Import + GblIncludeDepth = SG->IncludeDepth +} + +struct include_history_item + FileName: str + SectionName: str +end + +bool isDuplicateInclude( From: machine, FileName: str, SectionName: str ) +{ + for Item: include_history_item in From->IncludeHistory { + if Item->FileName == FileName && Item->SectionName == SectionName { + return true + } + } + return false +} + +void addIncludeItem( From: machine, FileName: str, SectionName: str ) +{ + new Item: include_history_item() + Item->FileName = FileName + Item->SectionName = SectionName + From->IncludeHistory->push_tail( Item ) +} + +struct machine + Name: str + ActionParams: map + IncludeHistory: list +end + + +rl ident + /( alpha | '_' ) ( alpha | digit | '_' )*/ + +rl number + / digit+ / + +rl hex_number + / '0x' [0-9a-fA-F]+ / + +rl hex_char + / '0x' [0-9a-fA-F]{2} / + +rl NL / '\n' / + +rl c_comment + / '/*' ( any | NL )* :>> '*/' / + +rl cpp_comment + / '//' [^\n]* NL / + +rl ruby_comment + / '#' [^\n]* NL / + +rl s_literal + / "'" ([^'\\\n] | '\\' (any | NL))* "'" / + +rl d_literal + / '"' ([^"\\] | NL | '\\' (any | NL))* '"' / + +rl host_re_literal + / '/' ([^/\\] | NL | '\\' (any | NL))* '/' / + +# +# Consuming ragel defintions without parsing. Used for included sections we +# don't want and for import (TODO). +# +namespace consume + lex + token h_word / [a-zA-Z_][a-zA-Z0-9_]* / + + token h_open /'{'/ + token h_close /'}'/ + + token h_number /digit+/ + token h_hex_number /'0x' [0-9a-fA-F]+/ + + token h_comment + / c_comment | cpp_comment / + + token h_string + / s_literal | d_literal / + + token h_whitespace + / ( [ \t] | NL )+ / + + token h_any / any / + end + + def host_tok + [h_word] + | [h_number] + | [h_hex_number] + | [h_comment] + | [h_string] + | [h_whitespace] + | [h_open host_tok* h_close] + | [h_any] + + lex + ignore /[\t\n ]+/ + ignore /'#' any* :> '\n'/ + + literal `}%% + + token word / [a-zA-Z_][a-zA-Z0-9_]* / + token uint / number / + token hex / hex_number / + + token string / + '"' ( [^"\\] | '\\' any )* '"' 'i'? | + "'" ( [^'\\] | '\\' any )* "'" 'i'? | + "[" ( [^\]\\] | '\\' any )* "]" 'i'? #| + #"/" ( [^\/\\] | '\\' any )* "/" 'i'? + / + + + token open /'{'/ -ni + token close ni- /'}'/ + token c_any / any / + end + + + # Garbling up a machine, no interpretation + def tok + [word] + | [uint] + | [hex] + | [string] + | [open host_tok* h_close] + | [c_any] +end + +# State reference. +namespace state_ref + lex + ignore /[\t\n ]+/ + literal `:: `; `) + token word /[a-zA-Z_][a-zA-Z0-9_]*/ + end + + def state_ref + [opt_name_sep state_ref_names] :Ref + + def opt_name_sep + [`::] :ColonColon + | [] :Empty + + # List of names separated by :: + def state_ref_names + [state_ref_names `:: word] :Rec + | [word] :Base +end + +namespace inline + + def inline_expr + [expr_item_list] :List + + def expr_item_list + [expr_item_list expr_item] :Rec + | [] :Empty + + def expr_item + [expr_any] :ExprAny + | [expr_symbol] :ExprSymbol + | [expr_interpret] :ExprInterpret + + def expr_any + [whitespace] :WS + | [comment] :Comment + | [string] :String + | [number] :Number + | [hex_number] :Hex + | [ident] :Ident + | [c_any] :Any + + def expr_symbol + [`,] :Comma | [`(] :Open | [`)] :Close | [`*] :Star | [`::] :DoubleColon + + def expr_interpret + [`fpc] :Fpc + | [`fc] :Fc + | [`fcurs] :Fcurs + | [`ftargs] :Ftargs + | [`fentry `( state_ref::state_ref state_ref::`)] :Fentry + | [var_ref] :VarRef + + def inline_block + [block_item_list] :List + + def block_item_list + [block_item block_item_list] :Rec + | [] :Base + + def block_item + [expr_any] :ExprAny + | [block_symbol] :BlockSymbol + | [block_interpret] :BlockInterpret + | [`{ inline_block `}] :RecBlock + + def block_symbol + [`,] :B1 | [`;] :B2 | [`(] :B3 | [`)] :B4 | [`*] :B5 | [`::] :B6 + + def block_interpret + [expr_interpret] :ExprInterpret + | [`fhold whitespace? `;] :Fhold + | [`fgoto whitespace? `* inline_expr `;] :FgotoExpr + | [`fnext whitespace? `* inline_expr `;] :FnextExpr + | [`fcall whitespace? `* inline_expr `;] :FcallExpr + | [`fncall whitespace? `* inline_expr `;] :FncallExpr + | [`fexec inline_expr `;] :Fexec + | [`fgoto state_ref::state_ref state_ref::`;] :FgotoSr + | [`fnext state_ref::state_ref state_ref::`;] :FnextSr + | [`fcall state_ref::state_ref state_ref::`;] :FcallSr + | [`fncall state_ref::state_ref state_ref::`;] :FncallSr + | [`fret `;] :Fret + | [`fnret `;] :Fnret + | [`fbreak `;] :Fbreak + | [`fnbreak `;] :Fnbreak +end + +namespace ragel + lex + literal `}%% -ni + + ignore /[\t\n ]+/ + ignore /'#' any* :> '\n'/ + + literal `^ `| `- `, `: `! `? `. + literal `( `) `{ -ni ni- `} `* `& `+ + + literal `-- `:> `:>> `<: `-> `** + + literal `|* `*| `=> + + literal `@ `> `< `% `$ + literal `from `to `eof `lerr `err + literal `when `inwhen `outwhen `>? `$? `%? + + literal `:= `|= `= `; `.. `../i `:: + + literal `>~ `$~ `%~ `<~ `@~ `<>~ + literal `>* `$* `%* `<* `@* `<>* + literal `>/ `$/ `%/ `/ + literal `>! `$! `%! `! + literal `>^ `$^ `%^ `<^ `@^ `<>^ + literal `<> + + literal `%%--{ -ni `%%++{ -ni + + token include_tok + /'include'/ + { + # Take off the include token. + input->pull( match_length ) + + # Parse the include details, up to the the ';' and stop there. + parse_stop Spec: include_spec(input)[] + + Fn: str + Machine: str + if Spec.string + Fn = $Spec.string + if Spec.word + Machine = $Spec.word + + Stream: stream = ragelInclude( Fn, Machine ) + + if Stream { + input->push( "}--%%" ) + input->push_stream( Stream ) + input->push( "%%--{" ) + } + } + + token import_tok + /'import'/ + { + # Take off the include token. + input->pull( match_length ) + + # Parse the import details, up to the the ';' and stop there. + parse_stop Spec: import_spec(input)[] + + Fn: str + if Spec.string + Fn = $Spec.string + + Stream: stream = ragelImport( Fn ) + + if Stream { + input->push( "}++%%" ) + input->push_stream( Stream ) + input->push( "%%++{" ) + } + } + + + literal `machine `action `variable `alphtype + `access `write `getkey `export `prepush + `postpop `nfaprepush `nfapostpop + + literal `:nfa `:nfa_greedy `:nfa_lazy `:nfa_wrap + `:nfa_wrap_greedy `:nfa_wrap_lazy + `:cond `:condplus `:condstar `): + + token string / + '"' ( [^"\\] | '\\' any )* '"' 'i'? | + "'" ( [^'\\] | '\\' any )* "'" 'i'? + / + + token lex_regex_open /'/'/ -ni + token lex_sqopen_pos /'['/ -ni + token lex_sqopen_neg /'[^'/ -ni + + token word / [a-zA-Z_][a-zA-Z0-9_]* / + token uint / number / + token hex / hex_number / + end + + def include_spec + [word `;] + | [string `;] + | [word string `;] + + def import_spec + [string `;] + + lex + token re_dot / '.' / + token re_star / '*' / + token re_char / ^( '\\' | '.' | '*' | '[' | '/' ) | '\\' . any / + token re_close / '/' 'i'? / + token re_sqopen_pos /'['/ + token re_sqopen_neg /'[^'/ + end + + lex + token re_or_dash / '-' / + token re_or_char / ^( '\\' | '-' | ']' ) | '\\' . any / + token re_or_sqclose / ']' / + end + + # Not cannot start with '{', terminated by ';', rewritten into { inline_expr } + token _inline_expr_reparse + /[^{;] [^;]* ';'/ { + R: str = input->pull( match_length - 1 ) + input->pull( 1 ) + input->push( "}" ) + input->push( R ) + input->push( "{" ) + } + + token variable_name /ident/ + + # This region is for deciding if we want to parse a ragel section, or if we + # want to consume it without interpreting. Consuming is for included + # sections we don't want and all sections in an imported file. + lex + token ign_select /''/ + { + if GblWantSection + input->push( make_token( typeid, '' ) ) + else + input->push( make_token( typeid, '' ) ) + } + + token ign_want // + token ign_ignore // + end + + # + # Machine name word. We inspect it to determine if we are interested in the + # section. + # + lex + token mn_word / [a-zA-Z_][a-zA-Z0-9_]* / + { + S: str = input->pull(match_length) + IgnWord: mn_word = make_token( typeid, S ) + input->push( IgnWord ) + + if ( GblImport ) + GblWantSection = false + else if ( GblSearchMachine != "" ) { + if ( S != GblSearchMachine ) + GblWantSection = false + else + GblWantSection = true + } + else { + GblWantSection = true + } + + Name: str = S #$lhs.mn_word + if GblTargetMachine != "" + Name = GblTargetMachine + + Machine: machine = GblMachineMap->find( Name ) + + if !Machine + { + Machine = new machine() + Machine->Name = Name + Machine->ActionParams = new map() + Machine->IncludeHistory = new list() + GblMachineMap->insert( Machine->Name, Machine ) + } + + GblCurMachine = Machine + + # print "want section: [GblWantSection] + } + end + + + def inline_expr_reparse + [_inline_expr_reparse] :Reparse + | [action_expr] :ActionExpr + + def join + [join `, expression] :Rec + | [expression] :Base + + def expression + [expr_left expression_op_list] :Expression + + def expression_op_list + [expression_op expression_op_list] :Op + | [] :Empty + + def expression_op + [`| term] :Or + | [`& term] :And + | [`- term] :Sub + | [`-- term] :Ssub + + def expr_left + [term] :Term + + def term + [term_left term_op_list_short] :Term + + def term_left + [factor_label] :FactorLabel + + # This list is done manually to get shortest match. + def term_op_list_short + [] :Empty + | [term_op term_op_list_short] :Terms + + def term_op + [factor_label] :None + | [`. factor_label] :Dot + | [`:> factor_label] :ColonLt + | [`:>> factor_label] :ColonLtLt + | [`<: factor_label] :GtColon + + def factor_label + [word `: factor_label] :Label + | [factor_ep] :Ep + + def factor_ep + [factor_aug `-> epsilon_target] :Epsilon + | [factor_aug] :Base + + def epsilon_target + [epsilon_target `:: word] :Rec + | [word] :Base + + def action_expr + [`{ CInlineExpr: inline::inline_expr inline::`}] :ActionExpr + + def action_block + [`{ CInlineBlock: inline::inline_block inline::`}] :ActionBlock + + def action_arg_list + [action_arg_list `, action_ref] :Rec + | [action_ref] :Base + + def opt_action_arg_list + [action_arg_list] :List + | [] :Empty + + def named_action_ref + [word] :Plain + { + if ( GblCurMachine->ActionParams->find( $lhs.word ) ) + reject + } + | [word `( opt_action_arg_list `)] :Args + { + if ( ! GblCurMachine->ActionParams->find( $lhs.word ) ) + reject + } + + def action_ref + [named_action_ref] :NamedRef + | [`( named_action_ref `)] :ParenNamed + | [action_block] :Block + + def priority_name + [word] :Word + + def error_name + [word] :Word + + def priority_aug + [uint] :NoSign + | [`+ uint] :Plus + | [`- uint] :Minus + + def aug_base + [`@] :Finish | [`>] :Enter | [`%] :Leave | [`$] :All + + def aug_cond + [`>?] :Start1 | [`$?] :All1 | [`%?] :Leave1 + | [`> `when] :Start2 | [`$ `when] :All2 | [`% `when] :Leave2 + | [`inwhen] :Start3 | [`when] :All3 | [`outwhen] :Leave3 + + def aug_to_state + [`>~] :Start1 | [`<~] :NotStart1 | [`$~] :All1 + | [`%~] :Final1 | [`@~] :NotFinal1 | [`<>~] :Middle1 + | [`> `to] :Start2 | [`< `to] :NotStart2 | [`$ `to] :All2 + | [`% `to] :Final2 | [`@ `to] :NotFinal2 | [`<> `to] :Middle2 + + def aug_from_state + [`>*] :Start1 | [`<*] :NotStart1 | [`$*] :All1 + | [`%*] :Final1 | [`@*] :NotFinal1 | [`<>*] :Middle1 + | [`> `from] :Start2 | [`< `from] :NotStart2 | [`$ `from] :All2 + | [`% `from] :Final2 | [`@ `from] :NotFinal2 | [`<> `from] :Middle2 + + def aug_eof + [`>/] :Start1 | [`/] :Middle1 + | [`> `eof] :Start2 | [`< `eof] :NotStart2 | [`$ `eof] :All2 + | [`% `eof] :Final2 | [`@ `eof] :NotFinal2 | [`<> `eof] :Middle2 + + def aug_gbl_error + [`>!] :Start1 | [`!] :Middle1 + | [`> `err] :Start2 | [`< `err] :NotStart2 | [`$ `err] :All2 + | [`% `err] :Final2 | [`@ `err] :NotFinal2 | [`<> `err] :Middle2 + + def aug_local_error + [`>^] :Start1 | [`<^] :NotStart1 | [`$^] :All1 + | [`%^] :Final1 | [`@^] :NotFinal1 | [`<>^] :Middle1 + | [`> `lerr] :Start2 | [`< `lerr] :NotStart2 | [`$ `lerr] :All2 + | [`% `lerr] :Final2 | [`@ `lerr] :NotFinal2 | [`<> `lerr] :Middle2 + + def factor_aug + [factor_aug aug_base action_ref] :ActionRef + | [factor_aug aug_base priority_aug] :PriorEmbed + | [factor_aug aug_base `( priority_name `, priority_aug `)] :NamedPriorEmbed + | [factor_aug aug_cond action_ref] :CondEmbed + | [factor_aug aug_cond `! action_ref] :NegCondEmbed + | [factor_aug aug_to_state action_ref] :ToStateAction + | [factor_aug aug_from_state action_ref] :FromStateAction + | [factor_aug aug_eof action_ref] :EofAction + | [factor_aug aug_gbl_error action_ref] :GblErrorAction + | [factor_aug aug_local_error action_ref] :LocalErrorDef + | [factor_aug aug_local_error `( error_name `, action_ref `)] :LocalErrorName + | [factor_rep] :Base + + def factor_rep + [factor_neg factor_rep_op_list] :Op + + def factor_rep_op_list + [factor_rep_op factor_rep_op_list] :Rec + | [] :Base + + def factor_rep_op + [`*] :Star + | [`**] :StarStar + | [`?] :Optional + | [`+] :Plus + | [`{ factor_rep_num `}] :ExactRep + | [`{ `, factor_rep_num `}] :MaxRep + | [`{ factor_rep_num `, `}] :MinRep + | [`{ LowRep: factor_rep_num `, HighRep: factor_rep_num `}] :RangeRep + + def factor_rep_num + [uint] :RepNum + + def factor_neg + [`! factor_neg] :Bang + | [`^ factor_neg] :Caret + | [factor] :Base + + def opt_max_arg + [`, action_ref] :Action + | [] :Empty + + def nfastar + [`:nfa] :Default + | [`:nfa_lazy] :Lazy + | [`:nfa_greedy] :Greedy + + def nfawrap + [`:nfa_wrap] :Default + | [`:nfa_wrap_lazy] :Lazy + | [`:nfa_wrap_greedy] :Greedy + + def colon_cond + [`:cond] :Cond + | [`:condstar] :CondStar + | [`:condplus] :CondPlus + + def factor + [alphabet_num] :AlphabetNum + | [word] :Word + | [string] :String + | [lex_sqopen_pos reg_or_data re_or_sqclose] :PosOrBlock + | [lex_sqopen_neg reg_or_data re_or_sqclose] :NegOrBlock + | [lex_regex_open regex re_close] :Regex + | [RL1: range_lit `.. RL2: range_lit] :Range + | [RL1: range_lit `../i RL2: range_lit] :RangeIndep + | [nfastar `( expression `, + Push: action_ref `, Pop: action_ref `, Init: action_ref `, Stay: action_ref `, + Repeat: action_ref `, Exit: action_ref `):] :Nfa + | [nfawrap `( expression `, + Push: action_ref `, Pop: action_ref `, Init: action_ref `, Stay: action_ref `, + Exit: action_ref `):] :NfaWrap + | [colon_cond `( expression `, + Init: action_ref `, Inc: action_ref `, Min: action_ref OptMax: opt_max_arg `):] :Cond + | [`( join `)] :Join + + def regex + [reg_item_rep_list] :List + + def reg_item_rep_list + [reg_item_rep_list reg_item_rep] :Rec + | [] :Base + + def reg_item_rep + [reg_item re_star] :Star + | [reg_item] :Base + + def reg_item + [re_sqopen_pos reg_or_data re_or_sqclose] :PosOrBlock + | [re_sqopen_neg reg_or_data re_or_sqclose] :NegOrBlock + | [re_dot] :Dot + | [re_char] :Char + + def reg_or_data + [reg_or_data reg_or_char] :Data + | [] :Base + + def reg_or_char + [re_or_char] :Char + | [Low: re_or_char re_or_dash High: re_or_char] :Range + + def range_lit + [string] :String + | [alphabet_num] :AN + + def alphabet_num + [uint] :Uint + | [`- uint] :Neg + | [hex] :Hex + + def lm_act + [`=> action_ref] :ActionRef + | [action_block] :ActionBlock + + def opt_lm_act + [lm_act] :Act + | [] :Empty + + def lm_stmt + [join opt_lm_act `;] :LmStmt commit + | [assignment] :Assignment + | [action_spec] :ActionSpec + + def lm_stmt_list + [lm_stmt_list lm_stmt] :Rec + | [lm_stmt] :Base + + def lm + [join] :Join + | [`|* lm_stmt_list `*|] :Lm + | [`:nfa `|* lm_stmt_list `*|] :LmNfa + + # + # Actions + # + def action_param + [word] :Word + + def action_param_list + [action_param_list `, action_param] :Rec + | [action_param] :Base + + def opt_action_param_list + [action_param_list] :List + | [] :Empty + + def action_params + [`( opt_action_param_list `)] :List + { + GblActionParams = true + } + + def action_spec + [`action word action_params action_block] :ActionSpecParams commit + { + # Track that this action has params so we can parse appropriately + # when reducing. + GblCurMachine->ActionParams->insert( $lhs.word, $lhs.word ) + + # Reset after parsing the block. + GblActionParams = false + } + | [`action word action_block] :ActionSpec commit + { + GblActionParams = false + } + + def def_name + [word] :Word + + # + # Machine Instantiations. + # + def assignment + [opt_export def_name `= join `;] :Assignment commit + + def instantiation + [opt_export def_name `:= lm `;] :Instantiation commit + + def nfa_expr + [nfa_expr `| term] :Union + | [term] :Base + + def nfa_round_spec + [Depth: uint `, Group: uint] :Spec + + def nfa_round_list + [nfa_round_list `, nfa_round_spec] :Recurse + | [nfa_round_spec] :Base + + def nfa_rounds + [`( nfa_round_list `)] :Rounds + + def nfa_union + [def_name `|= nfa_rounds nfa_expr `;] :NfaUnion commit + + def alphtype_type + [W1: word] :One + | [W1: word W2: word] :Two + + def opt_export + [`export] :Export + | [] :Base + + def write_arg + [word] :Word + + def machine_name + [`machine mn_word `;] :MachineName + + def open_inc + [`%%--{] :OpenInc + + def close_inc + [host::close_inc] :CloseInc + + def include_statement + [open_inc host::section* close_inc] :IncPost commit + + def open_imp + [`%%++{] :OpenImp + + def close_imp + [host::close_imp] :CloseImp + + def import_statement + [open_imp host::section* close_imp] :ImpPost commit + + def statement + [assignment] :Assignment + | [instantiation] :Instantiation + | [nfa_union] :NfaUnion + | [action_spec] :ActionSpec + | [`prepush action_block] :PrePush commit + | [`postpop action_block] :PostPop commit + | [`variable variable_name inline_expr_reparse] :Variable commit + | [`alphtype alphtype_type `;] :AlphType commit + | [`access inline_expr_reparse] :Access commit + | [`write Cmd: word ArgList: write_arg* `;] :Write commit + | [`getkey inline_expr_reparse] :GetKey commit + | [import_statement] :Import commit + | [include_statement] :Include commit + | [`nfaprepush action_block] :NfaPrePush commit + | [`nfapostpop action_block] :NfaPostPop commit + + def opt_machine_name + [machine_name] :MachineName + | [] :Empty + + def ragel_start + [opt_machine_name ign_want statement*] + | [opt_machine_name ign_ignore consume::tok*] +end + +str prepareLitString( Str: str ) +{ + # TODO: escape sequences + return suffix( prefix( Str, Str.length - 1 ), 1 ) +} + +bool isAbsolutePath( Path: str ) +{ + # TODO: implement this + return false +} + +namespace path + lex + token slash /'/'/ + token chars /[^\/]+/ + end + + def path + [Abs: slash? DirList: dir* File: chars] + + def dir + [chars slash] + + dir *concat_dir( Dir1: dir*, Dir2: dir* ) + { + for D: dir* in Dir1 { + if match D [] { + D = Dir2 + break + } + } + return Dir1 + } +end + +namespace host + token bom / 0xEF 0xBB 0xBF / + + def opt_bom + [bom] :Bom + | [] + + def section + [`%%{ ragel::opt_machine_name ragel::ign_want ragel::statement* ragel::`}%%] :MultiLine + | [`%%{ ragel::opt_machine_name ragel::ign_ignore consume::tok* consume::`}%%] :Consume + | [tok] :Token +end + +def start + [host::opt_bom SectionList: host::section*] + +list makeIncludePathChecks( CurFileName: str, IncFileName: str ) +{ + new L: list() + + parse CurPath: path::path[ CurFileName ] + parse IncPath: path::path[ IncFileName ] + + if match IncPath.Abs [slash] { + # Included file is absolute + L->push_tail( IncFileName ) + } + else { + # First add the location of current file + if match CurPath.DirList [] + L->push_tail( IncFileName ) + else { + # Current file path + Include Path + Include File + cons NewPath: path::path [ + CurPath.Abs + path::concat_dir( CurPath.DirList, IncPath.DirList ) + IncPath.File + ] + + L->push_tail( $NewPath ) + } + + # Next add include file locations. + for Path: str in GblIncludePaths { + parse IncPath: path::path[ CurFileName ] + L->push_tail( "[Path]/[IncFileName]" ) + } + } + return L +} + +stream ragelInclude( IncFileName: str, Machine: str ) +{ + if IncFileName + IncFileName = prepareLitString( IncFileName ) + + Checks: list + if IncFileName + Checks = makeIncludePathChecks( GblFileName, IncFileName ) + else { + Checks = new list() + Checks->push_tail( GblFileName ) + + } + + Stream: stream + OpenedName: str + for P: str in Checks { + Stream = open( P, "r" ) + if Stream { + OpenedName = P + break + } + } + + if !Stream { + print "error: could not open [IncFileName] + return nil + } + + # Default to the current machine if none is specified. + if !Machine + Machine = GblCurMachine->Name + + if isDuplicateInclude( GblCurMachine, IncFileName, Machine ) + return nil + + addIncludeItem( GblCurMachine, IncFileName, Machine ) + + saveGlobals() + + GblIncludeDepth = GblIncludeDepth + 1 + GblFileName = OpenedName + + # Set up the search and target machine names. Search is the machine we want + # to include and target is the machine we include to. + GblSearchMachine = Machine + GblTargetMachine = GblCurMachine->Name + + return Stream +} + +stream ragelImport( IncFileName: str ) +{ + if IncFileName + IncFileName = prepareLitString( IncFileName ) + + Checks: list + if IncFileName + Checks = makeIncludePathChecks( GblFileName, IncFileName ) + else { + Checks = new list() + Checks->push_tail( GblFileName ) + } + + Stream: stream + OpenedName: str + for P: str in Checks { + Stream = open( P, "r" ) + if Stream { + OpenedName = P + break + } + } + + if !Stream { + print "error: could not open [IncFileName] + return nil + } + + saveGlobals() + + GblFileName = OpenedName + GblImport = true + + return Stream +} diff --git a/src/cgil/ril.lm b/src/cgil/ril.lm new file mode 100644 index 00000000..cde6ce93 --- /dev/null +++ b/src/cgil/ril.lm @@ -0,0 +1,284 @@ +namespace host + lex + rl NL / '\n' / + + token escape + / '@' any / + + literal `={ `}= `${ `}$ `@{ `}@ + + token host_any / any / + end + + def tok + [`${ StmtList: stmt* `}$] :Stmt + | [`={ Expr: expr `}=] :Expr + | [escape] :Escape + | [host_any] :Any +end + +lex + rl NL / '\n' / + + rl s_literal + / "'" ([^'\\\n] | '\\' (any | NL))* "'" / + + rl d_literal + / '"' ([^"\\] | NL | '\\' (any | NL))* '"' / + + rl c_comment + / '/*' ( any | NL )* :>> '*/' / + + rl cpp_comment + / '//' [^\n]* NL / + + literal `array `value `TRUE `FALSE + `while `switch `case + `if `else `offset `index + `goto `deref `entry `label `default + `host `cast `match `pat + + literal `uint `const + `s8 `s16 `s32 `s64 + `s128 `nil `export + `fallthrough `u `c `break `continue + + token ident + /( alpha | '_' ) ( alpha | digit | '_' )*/ + + token uint + / digit+ / + + token hex_number + / '0x' [0-9a-fA-F]+ / + + ignore + / c_comment | cpp_comment / + + token string + / s_literal | d_literal / + + ignore / ( [ \t] | NL )+ / + + literal `$ `{ `} `= `[ `] + `- `, `. `; `( `) `: + `? `* `+ `> `< `& + `~ `! `!= `== `<< `>> + `+= `&& `|| `<= `>= + `@ `-= `-> `={ `${ `@{ +end + +def embedded_host + [`host `( string `, uint `) `={ TL: host::tok* host::`}=] :Expr +| [`host `( string `, uint `) `${ TL: host::tok* host::`}$] :Stmt +| [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] :Bare + +def type + [ident] :Ident +| [ident ident] :Ident2 +| [`uint] :Uint +| [`s8] :S8 +| [`s16] :S16 +| [`s32] :S32 +| [`s64] :S64 +| [`s128] :S128 + +def expr_factor + [embedded_host] :EmbeddedHost +| [ident] :Ident +| [ident `[ expr `]] :ArraySub +| [ident `[ expr `] `. Field: ident] :ArraySubField +| [`offset `( ident `, expr `)] :Offset +| [`deref `( ident `, expr `)] :Deref +| [number] :Number +| [`TRUE] :True +| [`FALSE] :False +| [`nil] :Nil +| [hex_number] :HexNumber +| [string] :String +| [embedded_host `-> expr_factor] :Access +| [`( expr `)] :Paren +| [`cast `( type `) expr_factor] :Cast + +def lvalue + [embedded_host] +| [ident] +| [ident `[ expr `]] +| [ident `[ expr `] `. ident] +| [embedded_host `-> lvalue] + +def expr_factor_op + [`! expr_factor_op] +| [`~ expr_factor_op] +| [expr_factor] + +def expr_bitwise + [expr_bitwise `& expr_factor_op] +| [expr_factor_op] + +def expr_mult + [expr_mult `* expr_bitwise] +| [expr_bitwise] + +def add_op + [`+] | [`-] + +def expr_add + [expr_add add_op expr_mult] +| [expr_mult] + +def shift_op + [`<<] | [`>>] + +def expr_shift + [expr_shift shift_op expr_add] +| [expr_add] + +def test_op + [`<] | [`>] | [`<=] | [`>=] | + [`==] | [`!=] | [`&&] | [`||] + +def expr_test + [expr_test test_op expr_shift] +| [expr_shift] + +def expr + [expr_test] + +def sint + [uint] +| [`- uint] + +def number + [`u `( uint `)] :Unsigned +| [`c `( uint `)] :Char +| [sint] :Number + +def comma_num + [`, number] + +def num_list + [number comma_num*] +| [] + +def static_array + [`array type ident `( number `, number `) `= `{ num_list `} `;] + +def static_value + [`value type ident `= number `;] + +def break_label + [ident `: `:] + +def while_stmt + [break_label? `while `( expr `) stmt] + +def else_if_clause + [`else `if `( expr `) stmt] + +def else_clause + [`else stmt] + +def if_stmt [ + `if `( expr `) stmt + else_if_clause* else_clause? +] + +def match_stmt + [`match `( E: expr `) `{ P: pat_block* D: default_block? `}] + +def pat_block + [`pat expr `{ stmt* `}] + +def switch_stmt + [`switch `( expr `) `{ stmt* `}] + +def case_block + [`case expr `{ stmt* `}] + +def default_block + [`default `{ stmt* `}] + +def case_label + [`case expr `:] + +def goto_label + [ident `:] + +def opt_init + [`= expr] +| [] + +def opt_ptr + [`*] +| [] + +def opt_const + [`const] +| [] + +def declaration + [opt_const type ident opt_init `;] + +def index_stmt + [`index type ident opt_init`;] + +def export_stmt + [`export type ident number `;] + +def goto_stmt + Id: int + [`goto ident `;] + +def fallthrough + [`fallthrough `;] + +def break_stmt + [`break ident? `;] + +def continue_stmt + [`continue ident? `;] + +def block + [`{ StmtList: stmt* `}] + +def expr_stmt + [expr `;] + +def assign_op + [`=] | [`+=] | [`-=] + +def assign_stmt + [LValue: lvalue assign_op expr `;] + +def stmt + [embedded_host] +| [static_array] +| [static_value] +| [declaration] +| [index_stmt] +| [export_stmt] +| [assign_stmt] +| [expr_stmt] +| [while_stmt] +| [if_stmt] +| [match_stmt] +| [switch_stmt] +| [case_block] +| [default_block] +| [case_label] +| [goto_label] +| [goto_stmt] +| [fallthrough] +| [break_stmt] +| [continue_stmt] +| [block] + +token bom / 0xEF 0xBB 0xBF / + +def opt_bom + [bom] :Bom +| [] + +def start + [opt_bom stmt*] diff --git a/src/cgil/rlhc-c.lm b/src/cgil/rlhc-c.lm new file mode 100644 index 00000000..dfc24d1d --- /dev/null +++ b/src/cgil/rlhc-c.lm @@ -0,0 +1,462 @@ +include 'ril.lm' + +namespace c_out + + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def c_out + [_IN_ _EX_ item*] +end + +namespace c_gen + global _: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case Stmt { + << "{ + " [stmt_list( StmtList )] + "} + } + case Expr { + << "([expr( Expr )])" + } + case Escape { + Str: str = $escape + << "[Str.suffix( 1 )]" + } + default { + << [Tok] + } + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case Expr + { + << "([tok_list( TL )])" + } + case Stmt + { + << "{ + " [tok_list( TL )] + "} + } + case Bare + { + << [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + << [embedded_host( EH )] + } + case Paren + { + << "([expr( expr )])" + } + case ArraySub + { + << "[ident]\[[expr( expr )]\]" + } + case ArraySubField + { + << "[ident]\[[expr( expr )]\].[Field]" + } + case Offset + { + << "[ident] + [expr( expr )]" + } + case Deref + { + << "(*( [expr(expr)] )) + } + case [`TRUE] + { + << "1" + } + case [`FALSE] + { + << "1" + } + case [N: `nil] + { + << "0" + } + case [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast `( T: type `) F: expr_factor] + { + << "( [type( T )] ) [expr_factor( F )]" + } + default { + # Catches cases not specified + << [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + << [embedded_host( EH )] + } + case [ident O: `[ TL: expr C: `]] + { + << [ident O expr( TL ) C] + } + case [I: ident `[ E: expr `] `. F: ident] + { + << "[I]\[[ expr( E )]\].[F] + } + case [E1: embedded_host `-> E2: lvalue] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + lvalue( E2 ) + } + default { + # Catches cases not specified + << [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + << ['!' expr_factor_op( _expr_factor_op )] + } + case [T: `~ expr_factor_op] + { + << ['~' expr_factor_op( _expr_factor_op )] + } + case [expr_factor] + { + << [expr_factor( expr_factor )] + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + << [expr_bitwise( _expr_bitwise ) A expr_factor_op( expr_factor_op )] + } + case [expr_factor_op] + { + << [expr_factor_op( expr_factor_op )] + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + << [expr_mult( _expr_mult ) T expr_bitwise( expr_bitwise )] + } + case [expr_bitwise] + { + << [expr_bitwise( expr_bitwise )] + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + << [expr_add( _expr_add ) Op expr_mult( expr_mult )] + } + case [expr_mult] + { + << [expr_mult( expr_mult )] + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + << [expr_shift( _expr_shift ) Op expr_add( expr_add )] + } + case [expr_add] + { + << [expr_add( expr_add )] + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + << [expr_test( _expr_test ) Op expr_shift( expr_shift )] + } + case [expr_shift] + { + << [expr_shift( expr_shift )] + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case S8 + << ['signed char '] + case S16 + << ['short '] + case S32 + << ['int '] + case S64 + << ['long '] + case S128 + << ['long long '] + case "uint" + << ['unsigned int '] + default + << [Type] + } + + void number( Number: number ) + { + switch Number + case Unsigned + << "[uint]u" + default + << [Number] + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + << "[number( Number )], " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + << [embedded_host( EH )] + } + case [A: static_array] { + << "static const [type(A.type)] " + "[A.ident] \[\] = { [num_list(A.num_list)] }; + } + case [V: static_value] { + << "static const [V.type] [V.ident] = [V.number]; + } + case [ + `if `( IfExpr: expr `) + IfStmt: stmt + ElseIfClauseList: else_if_clause* + ElseClauseOpt: else_clause? + ] { + << "if ( [expr(IfExpr)] ) + << " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + [`else `if `( ElseIfExpr: expr `) ElseIfStmt: stmt] + + << "else if ( [expr(ElseIfExpr)] ) + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + << "else + << " [stmt(ElseStmt)] + } + } + case [`while `( WhileExpr: expr `) WhileStmt: stmt] { + << "while ( [expr(WhileExpr)] ) + " [stmt(WhileStmt)] + } + case [M: match_stmt] { + << "switch ( [expr(M.E)] ) { + + for PB: pat_block in repeat( M.P ) { + << "case [expr( PB.expr )]: + "[stmt_list( PB._repeat_stmt )] + "break; + } + + if match M.D [D: default_block] { + << "default: + "[stmt_list( D._repeat_stmt )] + "break; + } + + << "} + } + case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { + << "switch ( [expr(SwitchExpr)] ) { + " [stmt_list(StmtList)] + "} + } + case [ES: expr_stmt] { + << "[expr(ES.expr)]; + } + case [B: block] { + << "{ + " [stmt_list(B.StmtList)] + "} + } + case [ + OptConst: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + << "[OptConst] [type(Type)] [Ident]" + + if match OptInit [`= Init: expr] { + << " = [expr(Init)] + } + + << "; + } + case [Export: export_stmt] + { + << "#define [Export.ident] [number(Export.number)] + } + case [fallthrough] + { + # Nothing needed here. + # C falls through by default. + } + case [Index: index_stmt] + { + << "const [type(Index.type)] *[Index.ident] + + if match Index.opt_init [E: `= expr] { + << [E expr(Index.opt_init.expr)] + } + + << "; + } + case [CB: case_block] + { + << "case [expr( CB.expr )]: + "[stmt_list( CB._repeat_stmt )] + "break; + } + case [DB: default_block] + { + << "default: + "[stmt_list( DB._repeat_stmt )] + "break; + } + case [CL: case_label] + { + << "case [expr( CL.expr )]: + } + case [AS: assign_stmt] + { + << "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + << [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + _ = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + CO: c_out::c_out = _->finish() + + if CO { + send Output + [CO] + } + else { + send stderr + "failed to parse output: [_->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + c_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-crack.lm b/src/cgil/rlhc-crack.lm new file mode 100644 index 00000000..bebe7cd5 --- /dev/null +++ b/src/cgil/rlhc-crack.lm @@ -0,0 +1,536 @@ +include 'ril.lm' + +namespace crack_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" | + "`" ( [^`\\] | '\\' any ) * "`" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def crack_out + [_IN_ _EX_ item*] +end + +namespace crack_gen + + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] { + send Parser + "if ( 1 ) { + " [stmt_list( StmtList )] + "} + } + case [host::`={ Expr: expr host::`}=] { + send Parser + "([expr( Expr )])" + } + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser + [Tok] + } + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "if ( 1 ) { + " [tok_list( TL )] + "} + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [`( E: expr `)] + { + send Parser + "([expr(E)])" + } + case [I: ident `[ E: expr `]] + { + send Parser + "[I]\[[expr( E )]\]" + } + case [`offset `( ident `, expr `)] + { + send Parser + [expr( ExprFactor.expr )] + } + case [`deref `( ident `, expr `)] + { + send Parser + [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] + } + case [`TRUE] + { + send Parser "1" + } + case [`FALSE] + { + send Parser "1" + } + case [N: `nil] + { + send Parser "0" + } + case [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast `( T: type `) F: expr_factor] + { + send Parser + "[type( T )] ( [expr_factor( F )] )" + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case [E1: embedded_host `-> E2: lvalue] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + lvalue( E2 ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['int16 '] + case "s16" + send Parser ['int16 '] + case "s32" + send Parser ['int32 '] + case "s64" + send Parser ['int64 '] + case "s128" + send Parser ['long long '] + case "uint" + send Parser ['uint32 '] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]u" + case [`c `( uint `) ] + send Parser "[Number.uint]" + default + send Parser [Number] + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )], " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [Array: static_array] { + send Parser + "const array\[[type(Array.type)]\] " + "[Array.ident] = \[ [num_list(Array.num_list)] \]; + } + case [Value: static_value] { + send Parser + "const [Value.type] [Value.ident] = [Value.number]; + } + case [ + `if `( IfExpr: expr `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) + " [flow_stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "else if ( [expr(ElseIfExpr)] ) + " [flow_stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [flow_stmt(ElseStmt)] + } + } + case [`while `( WhileExpr: expr `) WhileStmt: stmt] { + send Parser + "while ( [expr(WhileExpr)] ) + " [flow_stmt(WhileStmt)] + } + case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { + + require StmtList + [`case E1: expr `{ Inner: stmt* `} Rest: stmt*] + + send Parser + "if ( [expr(SwitchExpr)] == [expr(E1)] ) { + " [stmt_list(Inner)] + "} + + for S: stmt in repeat(Rest) { + switch S + case [`case E1: expr `{ Inner: stmt* `}] + { + send Parser + "else if ( [expr(SwitchExpr)] == [expr(E1)] ) { + " [stmt_list(Inner)] + "} + } + case + [`default `{ Inner: stmt* `}] + { + send Parser + "else { + " [stmt_list(Inner)] + "} + } + } + + send Parser + "; + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [`{ TL: stmt* `}] { + send Parser + "if ( 1 ) { + " [stmt_list(TL)] + "} + } + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + [TypeList type(Type) Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser + [Semi] + } + case [Export: export_stmt] + { + send Parser + "#define [Export.ident] [number(Export.number)] + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "int [Index.ident] + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + else { + send Parser + " = 0" + } + + send Parser "; + } + case [case_block] + { + send Parser + "case [expr( Stmt.case_block.expr )]: + "[stmt_list( Stmt.case_block._repeat_stmt )] + "break; + } + case [default_block] + { + send Parser + "default: + "[stmt_list( Stmt.default_block._repeat_stmt )] + "break; + } + case [case_label] + { + send Parser + "case [expr( Stmt.case_label.expr )]: + } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void flow_stmt( Stmt: stmt ) + { + switch Stmt + case [`{ TL: stmt* `}] { + send Parser + "{ + " [stmt_list(TL)] + "} + } + default { + stmt( Stmt ) + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + CO: crack_out::crack_out = Parser->finish() + + if CO { + send Output + [CO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + crack_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-csharp.lm b/src/cgil/rlhc-csharp.lm new file mode 100644 index 00000000..078157c5 --- /dev/null +++ b/src/cgil/rlhc-csharp.lm @@ -0,0 +1,480 @@ +include 'ril.lm' + +namespace csharp_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def csharp_out + [_IN_ _EX_ item*] +end + +namespace csharp_gen + + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] + send Parser "{[stmt_list( StmtList )]}" + case [host::`={ Expr: expr host::`}=] + send Parser "([expr( Expr )])" + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser [Tok] + } + } + } + + void embedded_host( EH: embedded_host ) + { + switch EH + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "{[tok_list( TL )]} + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case + [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [O:`( TL: expr C: `)] + { + send Parser + [O expr(TL) C] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case ['offset' '(' ident ',' expr ')'] + { + send Parser + [expr( ExprFactor.expr )] + } + case ['deref' '(' ident ',' expr ')'] + { + send Parser + [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] + } + case [T: `TRUE] + { + T.data = 'true' + send Parser [T] + } + case [F: `FALSE] + { + F.data = 'false' + send Parser [F] + } + case [N: `nil] + { + N.data = '0' + send Parser [N] + } + case [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast Open: `( Type: type Close: `) expr_factor] + { + send Parser [Open] + type( Type ) + send Parser [Close] + expr_factor( ExprFactor._expr_factor ) + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case [E1: embedded_host `-> E2: lvalue] + { + embedded_host( E1 ) + lvalue( E2 ) + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['sbyte '] + case "s16" + send Parser ['short '] + case "s32" + send Parser ['int '] + case "s64" + send Parser ['long '] + case "s128" + send Parser ['long long '] + case "uint" + send Parser ['uint '] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`c `( uint `) ] { + Str: str = $Number.uint + send Parser "'\\u[sprintf( "%04x", Str.atoi() )]'" + } + case [`u `( uint `) ] { + send Parser [$Number.uint] + } + default { + send Parser [$Number.sint] + } + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )], " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + send Parser + "static readonly [type(A.type)] \[\]" + "[A.ident] = { [num_list( A.num_list )] }; + } + case [V: static_value] { + send Parser + "static readonly [V.type] [V.ident] = [V.number]; + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) + " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "else if ( [expr(ElseIfExpr)] ) + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [stmt(ElseStmt)] + } + } + case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { + send Parser + "while ( [expr(WhileExpr)] ) + " [stmt(WhileStmt)] + } + case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { + send Parser + "switch ( [expr(SwitchExpr)] ) { + " [stmt_list(StmtList)] + "} + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + [L stmt_list(TL) R] + } + case [ + TypeList: opt_const Type: type Ident: ident + OptInit: opt_init Semi: `; + ] + { + send Parser + [TypeList type(Type) Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser + [Semi] + } + case [Export: export_stmt] + { + send Parser + "#define [Export.ident] [Export.number] + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "int [Index.ident]" + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + + send Parser ";" + } + case [case_block] + { + send Parser + "case [expr( Stmt.case_block.expr )]: + "[stmt_list( Stmt.case_block._repeat_stmt )] + "break; + } + case [default_block] + { + send Parser + "default: + "[stmt_list( Stmt.default_block._repeat_stmt )] + "break; + } + case [case_label] + { + send Parser + "case [expr( Stmt.case_label.expr )]: + } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + CSO: csharp_out::csharp_out = Parser->finish() + + if CSO { + send Output + [CSO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + csharp_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-d.lm b/src/cgil/rlhc-d.lm new file mode 100644 index 00000000..2a047e68 --- /dev/null +++ b/src/cgil/rlhc-d.lm @@ -0,0 +1,511 @@ +include 'ril.lm' + +namespace d_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def d_out + [_IN_ _EX_ item*] +end + +namespace d_gen + + global Parser: parser + + global HasDefault: list = new list() + + void pushHasDef( H: int ) + { + HasDefault->push( H ) + } + + int popHasDef() + { + return HasDefault->pop() + } + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] + send Parser "{[stmt_list( StmtList )]}" + case [host::`={ Expr: expr host::`}=] + send Parser "([expr( Expr )])" + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser [Tok] + } + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "{[tok_list( TL )]} + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host(EH)] + } + case + [O:`( TL: expr C: `)] + { + send Parser + [O expr( TL ) C] + } + case + [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr(TL) C] + } + case + [`offset `( ident `, expr `)] + { + send Parser + "& [ExprFactor.ident] \[ [expr(ExprFactor.expr)] \] + } + case + [`deref `( ident `, expr `)] + { + send Parser + "(*( [expr(ExprFactor.expr)] )) + } + case + [T: `TRUE] + { + T.data = '1' + send Parser [T] + } + case + [F: `FALSE] + { + F.data = '0' + send Parser [F] + } + case + [N: `nil] + { + N.data = 'null' + send Parser [N] + } + case + [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast Open: `( Type: type Close: `) expr_factor] + { + send Parser ['cast' Open] + type( Type ) + send Parser [Close] + expr_factor( ExprFactor._expr_factor ) + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host(EH)] + } + case + [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr(TL) C] + } + case [E1: embedded_host `-> E2: lvalue] + { + embedded_host( E1 ) + lvalue( E2 ) + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['byte '] + case "s16" + send Parser ['short '] + case "s32" + send Parser ['int '] + case "s64" + send Parser ['long '] + case "s128" + send Parser ['long long '] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]u" + default + send Parser [Number] + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )], " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host(EH)] + } + case [A: static_array] { + send Parser + "static const [type(A.type)]\[\] " + "[A.ident] = \[ [num_list( A.num_list )] \]; + } + case [V: static_value] { + send Parser + "static const [V.type] [V.ident] = [V.number]; + } + case [ + `if `( IfExpr: expr `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) + " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + [`else `if `( ElseIfExpr: expr `) ElseIfStmt: stmt] + + send Parser + "else if ( [expr(ElseIfExpr)] ) + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [stmt(ElseStmt)] + } + } + case [`while `( WhileExpr: expr `) WhileStmt: stmt] { + send Parser + "while ( [expr(WhileExpr)] ) + " [stmt(WhileStmt)] + } + case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { + pushHasDef( 0 ) + + send Parser + "switch ( [expr(SwitchExpr)] ) { + " [stmt_list(StmtList)] + + if ( popHasDef() == 0 ) { + send Parser + " default: break; + } + + send Parser + "} + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr( ExprExpr ) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + [L stmt_list( TL ) R] + } + case [ + OptConst: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + if match OptConst ['const'] { + send Parser + "const( [type( Type )] ) + } + else { + type( Type ) + } + + send Parser [Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser [Semi] + } + case [case_block] + { + send Parser + "case [expr( Stmt.case_block.expr )]: + "[stmt_list( Stmt.case_block._repeat_stmt )] + "break; + } + case [default_block] + { + send Parser + "default: + "[stmt_list( Stmt.default_block._repeat_stmt )] + "break; + + popHasDef() + pushHasDef( 1 ) + } + case [case_label] + { + send Parser + "case [expr( Stmt.case_label.expr )]: + } + case [export_stmt] + { + send Parser + "static const [type(Stmt.export_stmt.type)] " + "[Stmt.export_stmt.ident] = [number(Stmt.export_stmt.number)]; + } + case ['fallthrough' ';'] + { + send Parser "goto case;" + } + case [Index: index_stmt] + { + send Parser + "const([type(Index.type)]) *[Index.ident]" + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + + send Parser "; + } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + DO: d_out::d_out = Parser->finish() + + if DO { + send Output + [DO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + d_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-go.lm b/src/cgil/rlhc-go.lm new file mode 100644 index 00000000..b8353d10 --- /dev/null +++ b/src/cgil/rlhc-go.lm @@ -0,0 +1,454 @@ +include 'ril.lm' + +namespace out_go + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def out_go + [_IN_ _EX_ item*] +end + + + +namespace go_gen + + global _: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case Stmt { + << "{ + " [stmt_list( StmtList )] + "} + } + case Expr { + << "([expr( Expr )])" + } + case Escape { + Str: str = $Tok + << "[Str.suffix( 1 )]" + } + default { + << [Tok] + } + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case Expr + { + << ['(' tok_list( TL ) ')'] + } + case Stmt + { + << ['{' tok_list( TL ) '}\n'] + } + case Bare + { + << [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case EmbeddedHost + { + << [embedded_host(embedded_host)] + } + case Paren + { + << ['( ' expr(expr) ' )'] + } + case ArraySub + { + << [ident '[ ' expr( expr ) ' ]'] + } + case Offset + { + << "int([expr(expr )]) + } + case Deref + { + << [ident '[ ' expr( expr ) ' ]' ] + } + case True + { + << "true" + } + case False + { + << "false" + } + case Nil + { + << "0" + } + case Access + { + embedded_host(embedded_host) + expr_factor(_expr_factor) + } + case Cast + { + << [type(type) '( ' expr_factor(_expr_factor) ' )' ] + } + default { + # Catches cases not specified + << [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + << [embedded_host(EH)] + } + case [ident `[ TL: expr `]] + { + << [ident '[' expr( TL ) ']'] + } + case [E1: embedded_host `-> E2: lvalue] + { + embedded_host( E1 ) + lvalue( E2 ) + } + default { + # Catches cases not specified + << [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + << ['! ' expr_factor_op( _expr_factor_op )] + } + case [T: `~ expr_factor_op] + { + << ['^ ' expr_factor_op( _expr_factor_op )] + } + case [expr_factor] + { + << [expr_factor( ExprFactorOp.expr_factor )] + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + << [expr_bitwise( _expr_bitwise ) ' & ' expr_factor_op( expr_factor_op )] + } + case [expr_factor_op] + { + << [expr_factor_op( ExprBitwise.expr_factor_op )] + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + << [expr_mult( _expr_mult ) ' * ' expr_bitwise( expr_bitwise )] + } + case [expr_bitwise] + { + << [expr_bitwise( expr_bitwise )] + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + << [expr_add( _expr_add ) ' ' Op ' ' expr_mult( expr_mult )] + } + case [expr_mult] + { + << [expr_mult( ExprAdd.expr_mult )] + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + << [expr_shift( _expr_shift ) ' ' Op ' ' expr_add( expr_add )] + } + case [expr_add] + { + << [expr_add( expr_add )] + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + << [expr_test( _expr_test ) ' ' Op ' ' expr_shift( expr_shift )] + } + case [expr_shift] + { + << [expr_shift( ExprTest.expr_shift )] + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case S8 + << ['int8'] + case S16 + << ['int16'] + case S32 + << ['int32'] + case S64 + << ['int64'] + case S128 + << ['long long'] + default + << [Type] + } + + void number( Number: number ) + { + switch Number + case Unsigned + << [uint] + default + << [Number] + } + + void num_list( NumList: num_list ) + { + number( NumList.number ) + for CommaNum: comma_num in NumList { + << [', ' number( CommaNum.number )] + } + } + + # Go must have {} around if and for statements. We strip any blocks from + # these statments and force our own. + void strip_block_stmt( Stmt: stmt ) + { + if match Stmt [`{ StmtList: stmt* `}] + stmt_list(StmtList) + else + stmt( Stmt ) + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + << [embedded_host(EH)] + } + case [A: static_array] { + << "var [A.ident] = \[\] " + "[type(A.type)] { [num_list(A.num_list)] } + } + case [V: static_value] { + << "var [V.ident] [type(V.type)] = [V.number] + "var _ = [V.ident] + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ] { + # if-statements with only the if clause can go out as an if. + << "if [expr(IfExpr)] { + " [strip_block_stmt(IfStmt)] + "} + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + # If the if-statement has more than just an if clause it goes out as a switch. + << "if [expr( IfExpr )] { + " [strip_block_stmt( IfStmt )] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + << "} else if [expr(ElseIfExpr)] { + " [strip_block_stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + << "} else { + " [strip_block_stmt(ElseStmt)] + } + + << "} + } + case ["while ( TRUE )" WhileStmt: stmt] { + << "for { + " [strip_block_stmt(WhileStmt)] + "} + } + case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { + << "for [expr(WhileExpr)] { + " [strip_block_stmt(WhileStmt)] + "} + } + case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { + << "switch [expr(SwitchExpr)] { + " [stmt_list(StmtList)] + "} + } + case [ExprExpr: expr `;] { + << [expr(ExprExpr) ';'] + } + case [B: block] { + << "{ + " [stmt_list(B.StmtList)] + "} + } + case [D: declaration] + { + << "var [D.ident] [type(D.type)]" + + if match D.opt_init ['=' Expr: expr] { + << " = [expr(Expr)]" + } + + << ['\n'] + } + case [ES: export_stmt] + { + << "#define [ES.ident] [number(ES.number)] + } + case [fallthrough] + { + << "fallthrough + } + case [Index: index_stmt] + { + << "var [Index.ident] int" + + if match Index.opt_init ['=' Expr: expr] { + << " = [expr(Expr)]" + } + + << ['\n'] + } + case [CB: case_block] + { + << "case [expr( CB.expr )]: + " [stmt_list( CB._repeat_stmt )] + } + case [DB: default_block] + { + << "default: + " [stmt_list( DB._repeat_stmt )] + } + case [CL: case_label] + { + << "case [expr( CL.expr )]: + } + case [AS: assign_stmt] + { + << "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + << "[Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + _ = new parser() + Input: _input = _->gets() + Input->auto_trim(true) + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + GO: out_go::out_go = _->finish() + + if GO { + send Output + [GO] + } + else { + send stderr + "failed to parse output: [_->error] + } + + } +end + +void trans( Output: stream, Start: start ) +{ + go_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-java.lm b/src/cgil/rlhc-java.lm new file mode 100644 index 00000000..a458369f --- /dev/null +++ b/src/cgil/rlhc-java.lm @@ -0,0 +1,504 @@ +include 'ril.lm' + +namespace java_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def java_out + [_IN_ _EX_ item*] +end + +namespace java_gen + + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] { + send Parser + "{ + " [stmt_list( StmtList )] + "} + } + case [host::`={ Expr: expr host::`}=] + send Parser "([expr( Expr )])" + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser [Tok] + } + } + } + + void embedded_host( EH: embedded_host ) + { + switch EH + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "{ + " [tok_list( TL )] + "} + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case + [O:`( TL: expr C: `)] + { + send Parser + [O expr(TL) C] + } + case + [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case + ['offset' '(' ident ',' expr ')'] + { + send Parser + [expr( ExprFactor.expr )] + } + case + ['deref' '(' ident ',' expr ')'] + { + send Parser + [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] + } + case + [T: `TRUE] + { + T.data = 'true' + send Parser [T] + } + case + [F: `FALSE] + { + F.data = 'false' + send Parser [F] + } + case + [N: `nil] + { + N.data = '0' + send Parser [N] + } + case + [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast Open: `( Type: type Close: `) expr_factor] + { + send Parser [Open] + type( Type ) + send Parser [Close] + expr_factor( ExprFactor._expr_factor ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case + [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case [E1: embedded_host `-> E2: lvalue] + { + embedded_host( E1 ) + lvalue( E2 ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['byte '] + case "s16" + send Parser ['short '] + case "s32" + send Parser ['int '] + case "s64" + send Parser ['long '] + case "s128" + send Parser ['long long '] + case "uint" + send Parser ['int '] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]" + default + send Parser [Number] + } + + void java_num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )], " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + send Parser + "private static [type(A.type)] " + "[A.ident] \[\] = { [java_num_list(A.num_list)] }; + } + case [V: static_value] { + send Parser + "private static [V.type] [V.ident] = [V.number]; + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) + " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "else if ( [expr(ElseIfExpr)] ) + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [stmt(ElseStmt)] + } + } + case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { + send Parser + "while ( [expr(WhileExpr)] ) + " [stmt(WhileStmt)] + } + case [BL: break_label? 'while' '(' WhileExpr: expr ')' '{' StmtList: stmt* '}' ] { + if match BL [bl: break_label] + send Parser "[bl.ident]: " + + send Parser + "while ( [expr(WhileExpr)] ) { + " [stmt_list(StmtList)] + "} + } + case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { + send Parser + "switch ( [expr(SwitchExpr)] ) { + " [stmt_list(StmtList)] + "} + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + "{ + " [stmt_list(TL)] + "} + } + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + [TypeList type(Type) Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser + [Semi] + } + case [Export: export_stmt] + { + send Parser + "static final char [Export.ident] = [number(Export.number)]; + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "int [Index.ident]" + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + send Parser "; + + } + case [case_block] + { + send Parser + "case [expr( Stmt.case_block.expr )]: + "[stmt_list( Stmt.case_block._repeat_stmt )] + "break; + } + case [default_block] + { + send Parser + "default: + "[stmt_list( Stmt.default_block._repeat_stmt )] + "break; + } + case [goto_label] { } + case [G: goto_stmt] { } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + JO: java_out::java_out = Parser->finish() + + if JO { + send Output + [JO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + java_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-js.lm b/src/cgil/rlhc-js.lm new file mode 100644 index 00000000..29a61346 --- /dev/null +++ b/src/cgil/rlhc-js.lm @@ -0,0 +1,504 @@ +include 'ril.lm' + +namespace js_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def js_out + [_IN_ _EX_ item*] +end + +namespace js_gen + + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] { + send Parser + "{ + " [stmt_list( StmtList )] + "} + } + case [host::`={ Expr: expr host::`}=] { + send Parser + "([expr( Expr )])" + } + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser + [Tok] + } + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "{ + " [tok_list( TL )] + "} + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [O:`( TL: expr C: `)] + { + send Parser + [O expr(TL) C] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case ['offset' '(' ident ',' expr ')'] + { + send Parser + [expr( ExprFactor.expr )] + } + case ['deref' '(' ident ',' expr ')'] + { + send Parser [ExprFactor.ident] + if $ExprFactor.ident == 'data' + send Parser ['.charCodeAt(' expr( ExprFactor.expr ) ')'] + else + send Parser ['[' expr( ExprFactor.expr ) ']'] + } + case [T: `TRUE] + { + T.data = 'true' + send Parser [T] + } + case [F: `FALSE] + { + F.data = 'false' + send Parser [F] + } + case [N: `nil] + { + N.data = '-1' + send Parser [N] + } + case [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast Open: `( type Close: `) expr_factor] + { + expr_factor( ExprFactor._expr_factor ) + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case [I: ident `[ E: expr `] `. F: ident] + { + send Parser + "[I]\[[ expr( E )]\].[F] + } + case [E1: embedded_host `-> E2: lvalue] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + lvalue( E2 ) + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test_op( Op: test_op ) + { + switch Op + case [ `== ] + send Parser '===' + case [ `!= ] + send Parser '!==' + default + send Parser [Op] + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + expr_test_op( ExprTest.test_op ) + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser [Number.uint] + default + send Parser [Number] + } + + void type( Type: type ) + { + switch Type + case 'u8' + send Parser 'Uint8' + case 'u16' + send Parser 'Uint16' + case 'u32' + send Parser 'Uint32' + case 's8' + send Parser 'Int8' + case 's16' + send Parser 'Int16' + case 's32' + send Parser 'Int32' + default + send Parser 'Float64' + } + + void num_list( NumList: num_list ) + { + number( NumList.number ) + for CommaNum: comma_num in NumList { + send Parser [', '] + number( CommaNum.number ) + } + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + send Parser + "var [A.ident] = new [type(A.type)]Array(\[ [num_list(A.num_list)]\]); + } + case [V: static_value] { + send Parser + "var [V.ident] = [V.number]; + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) + " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "else if ( [expr(ElseIfExpr)] ) + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [stmt(ElseStmt)] + } + } + case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { + send Parser + "while ( [expr(WhileExpr)] ) + " [stmt(WhileStmt)] + } + case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { + send Parser + "switch ( [expr(SwitchExpr)] ) { + " [stmt_list(StmtList)] + "} + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + "{ + " [stmt_list(TL)] + "} + } + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + "var [Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser + [Semi] + } + case [Export: export_stmt] + { + send Parser + "var [Export.ident] = [number(Export.number)]; + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "var [Index.ident]" + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + + send Parser + "; + } + case [case_block] + { + send Parser + "case [expr( Stmt.case_block.expr )]: + "[stmt_list( Stmt.case_block._repeat_stmt )] + "break; + } + case [default_block] + { + send Parser + "default: + "[stmt_list( Stmt.default_block._repeat_stmt )] + "break; + } + case [goto_label] { } + case [G: goto_stmt] { } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + send Parser + "'use strict'; + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + CO: js_out::js_out = Parser->finish() + + if CO { + send Output + [CO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + js_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-julia.lm b/src/cgil/rlhc-julia.lm new file mode 100644 index 00000000..72108994 --- /dev/null +++ b/src/cgil/rlhc-julia.lm @@ -0,0 +1,561 @@ +include 'ril.lm' + +namespace julia_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + literal `function `end `while `if `else `elseif + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def kw + [`function _IN_] + | [`while _IN_] + | [`if _IN_] + | [_EX_ `elseif _IN_] + | [_EX_ `else _IN_] + | [_EX_ `end] + + def item + [comment] + | [kw] + | [id] + | [number] + | [symbol] + | [string] + | [`{ _IN_ item* _EX_ `} ] + + def julia_out + [_IN_ _EX_ item*] +end + +namespace julia_gen + + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] { + send Parser + "begin + " [stmt_list( StmtList )] + "end + } + case [host::`={ Expr: expr host::`}=] { + send Parser + "([expr( Expr )])" + } + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser + [Tok] + } + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + [tok_list( TL )] + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [`( E: expr `)] + { + send Parser + "([expr(E)])" + } + case [I: ident `[ E: expr `]] + { + send Parser + "[I]\[1+([expr( E )])\]" + } + case [`offset `( ident `, expr `)] + { + send Parser + [expr( ExprFactor.expr )] + } + case [`deref `( I: ident `, E: expr `)] + { + send Parser + "[I]\[1+([ expr( E ) ])\]" + } + case [`TRUE] + { + send Parser "true" + } + case [`FALSE] + { + send Parser "false" + } + case [N: `nil] + { + send Parser "0" + } + case [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast `( T: type `) F: expr_factor] + { + send Parser + "convert([type( T )], [expr_factor( F )] )" + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[1+(' expr(E) ')]'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [I: ident `[ E: expr `]] + { + send Parser + "[I]\[1+([expr( E )])\]" + } + case [E1: embedded_host `-> E2: lvalue] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + lvalue( E2 ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[1+(' expr(E) ')]'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['Int8'] + case "u8" + send Parser ['UInt8'] + case "s16" + send Parser ['Int16'] + case "s32" + send Parser ['Int32'] + case "s64" + send Parser ['Int64'] + case "s128" + send Parser ['Int128'] + case "uint" + send Parser ['UInt'] + case "int" + send Parser ['Int'] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]u" + default + send Parser [Number] + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )], " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + send Parser + "const [A.ident] = [type(A.type)]\[[num_list(A.num_list)]\] + } + case [V: static_value] { + send Parser + "const [V.ident] = [V.number] + } + # case [declaration] + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + [Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + else { + send Parser + "= 0 + } + + send Parser + [Semi] + } + case [ + `if `( IfExpr: expr `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if [expr(IfExpr)] + " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + [`else `if `( ElseIfExpr: expr `) ElseIfStmt: stmt] + + send Parser + "elseif [expr(ElseIfExpr)] + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [stmt(ElseStmt)] + } + + send Parser + "end + } + case [`while `( WhileExpr: expr `) WhileStmt: stmt] { + send Parser + "while [expr(WhileExpr)] + " [stmt(WhileStmt)] + "end + } + case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { + + require StmtList + [`case E1: expr `{ Inner: stmt* `} Rest: stmt*] + + send Parser + "if [expr(SwitchExpr)] == [expr(E1)] + " [stmt_list(Inner)] + + for S: stmt in repeat(Rest) { + switch S + case [`case E1: expr `{ Inner: stmt* `}] + { + send Parser + "elseif [expr(SwitchExpr)] == [expr(E1)] + " [stmt_list(Inner)] + } + case + [`default `{ Inner: stmt* `}] + { + send Parser + "else + " [stmt_list(Inner)] + } + } + + send Parser + "end + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr)] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + [stmt_list(TL)] + } + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + [TypeList type(Type) Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser "; + } + case [Export: export_stmt] + { + send Parser + "#define [Export.ident] [number(Export.number)] + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "[Index.ident]" + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + else { + send Parser " = 0 " + + } + + send Parser "; + } + case [case_block] + { + send Parser + "@case [expr( Stmt.case_block.expr )] begin + "[stmt_list( Stmt.case_block._repeat_stmt )] + "end + } + case [default_block] + { + send Parser + "default: + "[stmt_list( Stmt.default_block._repeat_stmt )] + "break; + } + case [case_label] + { + send Parser + "@case [expr( Stmt.case_label.expr )] + } + case [L: goto_label] + { + send Parser + "@label [L.ident] + } + case [G: goto_stmt] + { + send Parser + "@goto [G.ident] + } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)] + } + default { + # catches unspecified cases + send Parser + "[Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + CO: julia_out::julia_out = Parser->finish() + + if CO { + send Output + [CO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + julia_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-main.lm b/src/cgil/rlhc-main.lm new file mode 100644 index 00000000..f9abea7e --- /dev/null +++ b/src/cgil/rlhc-main.lm @@ -0,0 +1,19 @@ +InputFile: str = argv->pop() +OutputFile: str = argv->pop() + +# +# Open input and parse +# +Input: stream = open( InputFile, "r" ) +parse Start: start[ Input ] +if ( !Start ) { + print( error, '\n' ) + exit(1) +} + +# +# Translate +# +Output: stream = open( OutputFile, "w" ) +trans( Output, Start ) + diff --git a/src/cgil/rlhc-ocaml.lm b/src/cgil/rlhc-ocaml.lm new file mode 100644 index 00000000..f68b61be --- /dev/null +++ b/src/cgil/rlhc-ocaml.lm @@ -0,0 +1,609 @@ +include 'ril.lm' + +namespace ocaml_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '(*' any* :>> '*)' + / + + literal `begin `end `{ `} + + token id + /[a-zA-Z_][a-zA-Z_0-9]* "'"? / + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' | '`' / + + token string / + '"' ( [^"\\\n] | '\\' any ) * '"' | + "'" ( [^'\\\n] | '\\' any ) * "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`begin _IN_ item* _EX_ `end ] + | [`{ _IN_ item* _EX_ `} ] + + def ocaml_out + [_IN_ _EX_ item*] +end + +namespace ml_gen + + global StaticVarMap: map = new map() + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] + send Parser + "begin + "[stmt_list( StmtList )] + "end; + case [host::`={ Expr: expr host::`}=] + send Parser "([expr( Expr )])" + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser [Tok] + } + } + } + + void embedded_host( EH: embedded_host ) + { + switch EH + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "begin + "[tok_list( TL )] + "end; + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [`( TL: expr `)] + { + send Parser + "( [ expr(TL) ] )" + } + case [I: ident `[ TL: expr `]] + { + if ( StaticVarMap->find( $I ) || $I == 'stack' ) { + send Parser + "[ ExprFactor.ident ].([ expr( TL ) ])" + } + else { + send Parser + "[ ExprFactor.ident ].\[[ expr( TL ) ]\]" + } + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '.(' expr(E) ')'] + } + case ['offset' '(' ident ',' expr ')'] + { + send Parser + [expr( ExprFactor.expr )] + } + case ['deref' '(' I: ident ',' Expr: expr ')'] + { + if ( $I == 'data' ) + send Parser 'Char.code ' + + if ( StaticVarMap->find( $I ) ) { + send Parser + "[I].( [ expr( Expr ) ] )" + } + else { + send Parser + "[I].\[[ expr( Expr ) ]\]" + } + } + case [T: `TRUE] + { + T.data = 'true' + send Parser [T] + } + case [F: `FALSE] + { + F.data = 'false' + send Parser [F] + } + case [N: `nil] + { + N.data = '0' + send Parser [N] + } + case [Number: number] + { + number( Number ) + } + case [I: ident] { + if ( StaticVarMap->find( $I ) ) { + send Parser + [^I] + } + else { + send Parser + "[^I].contents" + } + } + case [E1: embedded_host `-> E2: expr_factor] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast `( type `) expr_factor] + { + send Parser + [expr_factor( ExprFactor._expr_factor )] + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case [E1: embedded_host `-> E2: lvalue] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + lvalue( E2 ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '.(' E ')'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser " lnot " + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [L: expr_bitwise `& R: expr_factor_op] + { + send Parser + "[expr_bitwise( L )] land [expr_factor_op( R )]" + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + switch Op + case [`<<] + send Parser " lsl " + default + send Parser " asr " + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + switch Op + case [`==] + send Parser "= " + default + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['char '] + case "s16" + send Parser ['short '] + case "s32" + send Parser ['int '] + case "s64" + send Parser ['long '] + case "s128" + send Parser ['long long '] + case "uint" + send Parser ['int '] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]u" + default + send Parser [Number] + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )]; " + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + StaticVarMap->insert( $A.ident, ' ' ) + send Parser + "let [A.ident] : int array = \[| + " [num_list(A.num_list)] + "|\] + } + case [V: static_value] { + StaticVarMap->insert( $V.ident, ' ' ) + send Parser + "let [V.ident] : [V.type] = [V.number] + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if [expr(IfExpr)] then + "begin + " [stmt(IfStmt)] + "end + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "else if [expr(ElseIfExpr)] then + "begin + " [stmt(ElseIfStmt)] + "end + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + "begin + " [stmt(ElseStmt)] + "end + } + + send Parser + ";" + } + case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { + send Parser + "while [expr(WhileExpr)] do + " [stmt(WhileStmt)] + "done; + } + case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { + require StmtList + [`case E1: expr `{ Inner: stmt* `} Rest: stmt*] + + send Parser + "if [expr(SwitchExpr)] = [expr(E1)] then + "begin + " [stmt_list(Inner)] + "end + + for S: stmt in repeat(Rest) { + switch S + case [`case E1: expr `{ Inner: stmt* `}] + { + send Parser + "else if [expr(SwitchExpr)] = [expr(E1)] then + "begin + " [stmt_list(Inner)] + "end + } + case + [`default `{ Inner: stmt* `}] + { + send Parser + "else + "begin + " [stmt_list(Inner)] + "end + } + } + + send Parser + "; + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + "begin + "[stmt_list(TL)] + "end; + } + case [D: declaration] { + send Parser + "let [D.ident] : [type(D.type)] ref " + + switch D.opt_init + case [E: `= expr] { + send Parser + "= ref ( [expr(D.opt_init.expr)] )" + } + default { + send Parser + "= ref 0" + } + + send Parser + " in + } + case [Export: export_stmt] + { + send Parser + "#define [Export.ident] [number(Export.number)] + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "let [Index.ident] : int ref " + + switch Index.opt_init + case [E: `= expr] { + send Parser + "= ref ( [expr(Index.opt_init.expr)] )" + } + default { + send Parser + "= ref 0" + } + + send Parser + " in + } + case [case_block] + { + send Parser + "| [expr( Stmt.case_block.expr )] -> + "begin + "[stmt_list( Stmt.case_block._repeat_stmt )] + "end; + } + case [default_block] + { + send Parser + "| _ -> + "[stmt_list( Stmt.default_block._repeat_stmt )] + } + case [case_label] + { + send Parser + "case [expr( Stmt.case_label.expr )]: + } + case [AS: assign_stmt] + { + switch AS.assign_op + case [`=] + { + switch AS.LValue + case "stack\[[expr]\]" { + send Parser + "Array.set stack top.contents [expr(AS.expr)]; + } + case "nfa_bp\[[expr]\].state" { + send Parser + "Array.set nfa_bp_state nfa_len.contents [expr(AS.expr)]; + } + case "nfa_bp\[[expr]\].p" { + send Parser + "Array.set nfa_bp_p nfa_len.contents [expr(AS.expr)]; + } + default { + send Parser + "[lvalue(AS.LValue)] := [expr(AS.expr)]; + } + } + case [`+=] + { + parse RhsAsFactor: expr_factor [$AS.LValue] + send Parser + "[lvalue(AS.LValue)] := [expr_factor(RhsAsFactor)] + [expr(AS.expr)]; + } + case [`-=] + { + parse RhsAsFactor: expr_factor [$AS.LValue] + send Parser + "[lvalue(AS.LValue)] := [expr_factor(RhsAsFactor)] - [expr(AS.expr)]; + } + default { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + MO: ocaml_out::ocaml_out = Parser->finish() + + if MO { + send Output + [MO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } + +end + +void trans( Output: stream, Start: start ) +{ + ml_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-ruby.lm b/src/cgil/rlhc-ruby.lm new file mode 100644 index 00000000..87119465 --- /dev/null +++ b/src/cgil/rlhc-ruby.lm @@ -0,0 +1,527 @@ +include 'ril.lm' + +namespace ruby_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '#' any* :> '\n' + / + + literal `def `class `begin `end `while `if + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) * "'" | + "/" ( [^/\\] | '\\' any ) * "/" + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' | '{' | '}' | '\\' / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [`begin _IN_] + | [`class _IN_] + | [`while _IN_] + | [`if _IN_] + | [`def _IN_] + | [_EX_ `end] + + def ruby_out + [_IN_ _EX_ item*] +end + +global Parser: parser + +void tok_list( TL: host::tok* ) +{ + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] + { + send Parser + "begin + "[stmt_list( StmtList )] + "end + } + case [host::`={ Expr: expr host::`}=] + expr( Expr ) + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default { + send Parser [Tok] + } + } +} + +void embedded_host( EH: embedded_host ) +{ + switch EH + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "begin + " [tok_list( TL )] + "end + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } +} + +void expr_factor( ExprFactor: expr_factor ) +{ + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [O:`( TL: expr C: `)] + { + send Parser + [O expr(TL) C] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case ['offset' '(' ident ',' expr ')'] + { + send Parser + [expr( ExprFactor.expr )] + } + case ['deref' '(' ident ',' expr ')'] + { + send Parser + [ ExprFactor.ident '[' expr( ExprFactor.expr ) ']'] + if $ExprFactor.ident == 'data' + send Parser '.ord' + } + case [T: `TRUE] + { + T.data = 'true' + send Parser [T] + } + case [F: `FALSE] + { + F.data = 'false' + send Parser [F] + } + case [N: `nil] + { + N.data = '0' + send Parser [N] + } + case [Number: number] + { + ruby_number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast Open: `( Type: type Close: `) expr_factor] + { + #send Parser [Open] + #type( Type ) + #send Parser [Close] + expr_factor( ExprFactor._expr_factor ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } +} + +void lvalue( ExprFactor: lvalue ) +{ + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [ident O: `[ TL: expr C: `]] + { + send Parser + [ExprFactor.ident O expr( TL ) C] + } + case [E1: embedded_host `-> E2: lvalue] + { + embedded_host( E1 ) + lvalue( E2 ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' E ']'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } +} + +void expr_factor_op( ExprFactorOp: expr_factor_op ) +{ + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [T: `~ expr_factor_op] + { + send Parser [T] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } +} + +void expr_bitwise( ExprBitwise: expr_bitwise ) +{ + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } +} + +void expr_mult( ExprMult: expr_mult ) +{ + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } +} + +void expr_add( ExprAdd: expr_add ) +{ + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } +} + +void expr_shift( ExprShift: expr_shift ) +{ + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } +} + +void expr_test( ExprTest: expr_test ) +{ + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } +} + +void expr( Expr: expr ) +{ + expr_test( Expr.expr_test ) +} + +void type( Type: type ) +{ + switch Type + case "s8" + send Parser ['byte '] + case "s16" + send Parser ['short '] + case "s32" + send Parser ['int '] + case "s64" + send Parser ['long '] + case "s128" + send Parser ['long long '] + case "uint" + send Parser ['int '] + default + send Parser [Type] +} + +void ruby_number( Number: number ) +{ + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]" + default + send Parser [Number] +} + +void ruby_num_list( NumList: num_list ) +{ + for Number: number in NumList + send Parser "[ruby_number( Number )], " +} + +void stmt( Stmt: stmt ) +{ + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + send Parser + "class << self + " attr_accessor :[ A.ident ] + " private :[ A.ident ], :[ A.ident ]= + "end + "self.[ A.ident ] = \[ + " [ruby_num_list( A.num_list )] + "\] + " + } + case [V: static_value] { + send Parser + "class << self + " attr_accessor :[ V.ident ] + "end + "self.[ V.ident ] = [ V.number ]; + " + } + case [ + 'if' O: `( IfExpr: expr C: `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) + " [stmt(IfStmt)] + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "elsif ( [expr(ElseIfExpr)] ) + " [stmt(ElseIfStmt)] + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else + " [stmt(ElseStmt)] + } + send Parser + "end + } + case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] { + send Parser + "while ( [expr(WhileExpr)] ) + " [stmt(WhileStmt)] + "end + } + case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] { + send Parser + "case [expr(SwitchExpr)] + "when -2 then + "begin + " [stmt_list(StmtList)] + "end + "end + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + "begin + "[stmt_list(TL)] + "end + } + # [declaration] + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + [Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + else { + send Parser + "= 0 + } + + send Parser + [Semi] + } + case [Export: export_stmt] + { + send Parser + "class << self + " attr_accessor :[ Export.ident ] + "end + "self.[ Export.ident ] = [ ruby_number(Export.number) ]; + " + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [Index: index_stmt] + { + send Parser + "[Index.ident]" + + if match Index.opt_init [E: `= expr] { + send Parser + [E expr(Index.opt_init.expr)] + } + else { + send Parser + "= 0 + } + + send Parser "; + } + case [case_block] + { + send Parser + "end + "when [expr( Stmt.case_block.expr )] then + "begin + "[stmt_list( Stmt.case_block._repeat_stmt )] + } + case [default_block] + { + send Parser + "end + "else + "begin + "[stmt_list( Stmt.default_block._repeat_stmt )] + } + case [goto_label] {} + case [goto_stmt] {} + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + case [continue_stmt] + { + send Parser + "next; + } + default { + # catches unspecified cases + send Parser [Stmt] + } +} + +void stmt_list( StmtList: stmt* ) +{ + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) +} + +void ruby_trans( Output: stream, Start: start ) +{ + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + RO: ruby_out::ruby_out = Parser->finish() + + if RO { + send Output + [RO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } +} + +void trans( Output: stream, Start: start ) +{ + ruby_trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/cgil/rlhc-rust.lm b/src/cgil/rlhc-rust.lm new file mode 100644 index 00000000..03f8b688 --- /dev/null +++ b/src/cgil/rlhc-rust.lm @@ -0,0 +1,534 @@ +include 'ril.lm' + +namespace rust_out + token _IN_ /''/ + token _EX_ /''/ + + lex + token comment / + '//' any* :> '\n' | + '/*' any* :>> '*/' + / + + token id + /[a-zA-Z_][a-zA-Z_0-9]*/ + + token number / + [0-9]+ + / + + token symbol / + '!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' | + '+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' | + '=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' | + '~' / + + literal `{ `} + + token lifetime / "'" id / + + token string / + '"' ( [^"\\] | '\\' any ) * '"' | + "'" ( [^'\\] | '\\' any ) "'" + / + + ignore + /[ \t\v\r\n]+/ + end + + def item + [comment] + | [id] + | [number] + | [symbol] + | [string] + | [lifetime] + | [`{ _IN_ item* _EX_ `} ] + + def rust_out + [_IN_ _EX_ item*] +end + +namespace rust_gen + + global Parser: parser + + void tok_list( TL: host::tok* ) + { + for Tok: host::tok in repeat(TL) { + switch Tok + case [host::`${ StmtList: stmt* host::`}$] + send Parser "{[stmt_list( StmtList )]}" + case [host::`={ Expr: expr host::`}=] + send Parser "([expr( Expr )])" + case [E: escape] { + Str: str = $E + send Parser + "[Str.suffix( 1 )]" + } + default + send Parser [Tok] + } + } + + void embedded_host( EmbeddedHost: embedded_host ) + { + switch EmbeddedHost + case [`host `( string `, uint `) `={ TL: host::tok* host::`}=] + { + send Parser + "([tok_list( TL )])" + } + case [`host `( string `, uint `) `${ TL: host::tok* host::`}$] + { + send Parser + "{[tok_list( TL )]} + } + case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@] + { + send Parser + [tok_list( TL )] + } + } + + void expr_factor( ExprFactor: expr_factor ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [O:`( TL: expr C: `)] + { + send Parser + [O expr(TL) C] + } + case [I: ident `[ E: expr `]] + { + send Parser + "[I]\[([expr( E )]) as usize\]" + } + case ['offset' '(' ident ',' expr ')'] + { + send Parser + "( [expr( ExprFactor.expr )] ) as i32" + } + case ['deref' '(' I: ident ',' E: expr ')'] + { + send Parser + "[I]\[([expr( E )]) as usize\] + } + case [T: `TRUE] + { + T.data = 'true' + send Parser [T] + } + case [F: `FALSE] + { + F.data = 'false' + send Parser [F] + } + case [N: `nil] + { + N.data = '0' + send Parser [N] + } + case [Number: number] + { + number( Number ) + } + case [E1: embedded_host `-> E2: expr_factor] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + expr_factor( E2 ) + } + case [`cast `( T: type `) E: expr_factor] + { + send Parser + "( [expr_factor( E )] ) as [type(T)]" + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' expr(E) ' as usize]'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void lvalue( ExprFactor: lvalue ) + { + switch ExprFactor + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [I: ident O: `[ E: expr C: `]] + { + send Parser + "[I]\[([expr( E )]) as usize\] + } + case [E1: embedded_host `-> E2: lvalue] + { + # The accessor operator is contained wihtin the lhs. + embedded_host( E1 ) + lvalue( E2 ) + } + case [I: ident `[ E: expr `] `. F: ident] { + send Parser + [^I '_' ^F '[' expr(E) ' as usize]'] + } + default { + # Catches cases not specified + send Parser [ExprFactor] + } + } + + void expr_factor_op( ExprFactorOp: expr_factor_op ) + { + switch ExprFactorOp + case [B: `! expr_factor_op] + { + send Parser [B] + expr_factor_op( ExprFactorOp._expr_factor_op ) + } + case [`~ EFO: expr_factor_op] + { + send Parser + "![expr_factor_op( EFO )] + } + case [expr_factor] + { + expr_factor( ExprFactorOp.expr_factor ) + } + } + + void expr_bitwise( ExprBitwise: expr_bitwise ) + { + switch ExprBitwise + case [expr_bitwise A: `& expr_factor_op] + { + expr_bitwise( ExprBitwise._expr_bitwise ) + send Parser [A] + expr_factor_op( ExprBitwise.expr_factor_op ) + } + case [expr_factor_op] + { + expr_factor_op( ExprBitwise.expr_factor_op ) + } + } + + void expr_mult( ExprMult: expr_mult ) + { + switch ExprMult + case [expr_mult T: `* expr_bitwise] + { + expr_mult( ExprMult._expr_mult ) + send Parser [T] + expr_bitwise( ExprMult.expr_bitwise ) + } + case [expr_bitwise] + { + expr_bitwise( ExprMult.expr_bitwise ) + } + } + + void expr_add( ExprAdd: expr_add ) + { + switch ExprAdd + case [expr_add Op: add_op expr_mult] + { + expr_add( ExprAdd._expr_add ) + send Parser [Op] + expr_mult( ExprAdd.expr_mult ) + } + case [expr_mult] + { + expr_mult( ExprAdd.expr_mult ) + } + } + + void expr_shift( ExprShift: expr_shift ) + { + switch ExprShift + case [expr_shift Op: shift_op expr_add] + { + expr_shift( ExprShift._expr_shift ) + send Parser [Op] + expr_add( ExprShift.expr_add ) + } + case [expr_add] + { + expr_add( ExprShift.expr_add ) + } + } + + void expr_test( ExprTest: expr_test ) + { + switch ExprTest + case [expr_test Op: test_op expr_shift] + { + expr_test( ExprTest._expr_test ) + send Parser [Op] + expr_shift( ExprTest.expr_shift ) + } + case [expr_shift] + { + expr_shift( ExprTest.expr_shift ) + } + } + + void expr( Expr: expr ) + { + expr_test( Expr.expr_test ) + } + + void type( Type: type ) + { + switch Type + case "s8" + send Parser ['i8 '] + case "s16" + send Parser ['i16 '] + case "s32" + send Parser ['i32 '] + case "s64" + send Parser ['i64 '] + case "s128" + send Parser ['i128'] + case "int" + send Parser ['i32'] + case "uint" + send Parser ['u32'] + default + send Parser [Type] + } + + void number( Number: number ) + { + switch Number + case [`u `( uint `) ] + send Parser "[Number.uint]" + default + send Parser [Number] + } + + void num_list( NumList: num_list ) + { + for Number: number in NumList + send Parser "[number( Number )], " + send Parser "0" + } + + void stmt( Stmt: stmt ) + { + switch Stmt + case [EH: embedded_host] + { + send Parser + [embedded_host( EH )] + } + case [A: static_array] { + Length: int = 1 + for Number: number in A.num_list + Length = Length + 1 + + send Parser + "static [A.ident]: \[[type(A.type)]; [Length]\] = \[ [num_list(A.num_list)] \]; + } + case [V: static_value] { + send Parser + "static [V.ident]: i32 = [V.number]; + } + case [D: declaration] { + send Parser + "let mut [D.ident] " + + switch D.opt_init + case [E: `= expr] { + send Parser + "= [expr(D.opt_init.expr)]; + } + default { + send Parser + "= 0; + } + } + case [Index: index_stmt] + { + send Parser + "let mut [Index.ident] :i32" + + switch Index.opt_init + case [E: `= expr] { + send Parser + "= [expr(Index.opt_init.expr)]; + } + default { + send Parser + "= 0; + } + } + case [ + 'if' `( IfExpr: expr `) IfStmt: stmt + ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause? + ] { + send Parser + "if ( [expr(IfExpr)] ) { + " [stmt(IfStmt)] + "} + + for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) { + match ElseIfClause + ['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt] + + send Parser + "else if ( [expr(ElseIfExpr)] ) { + " [stmt(ElseIfStmt)] + "} + } + + if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) { + send Parser + "else { + " [stmt(ElseStmt)] + "} + } + } + case [`continue CL: ident `;] { + send Parser + "continue '[CL]; + } + case [`break BL: ident `;] { + send Parser + "break '[BL]; + } + case [BL: break_label? 'while' '(' WhileExpr: expr ')' '{' StmtList: stmt* '}' ] { + if match BL [bl: break_label] + send Parser "'[bl.ident]: " + + send Parser + "while ( [expr(WhileExpr)] ) { + " [stmt_list(StmtList)] + "} + } + case [`switch `( SwitchExpr: expr `) `{ StmtList: stmt* `}] { + send Parser + "match ( [expr(SwitchExpr)] ) { + " [stmt_list(StmtList)] + + NeedDef: bool = true + for Stmt: stmt in repeat(StmtList) { + if match Stmt [default_block] + NeedDef = false + } + + if NeedDef { + send Parser + " _ => {} + } + + send Parser + "} + } + case [ExprExpr: expr Semi: `;] { + send Parser + [expr(ExprExpr) Semi] + } + case [L: `{ TL: stmt* R: `}] { + send Parser + [L stmt_list(TL) R] + } + case [ + TypeList: opt_const Type: type + Ident: ident OptInit: opt_init Semi: `; + ] + { + send Parser + [TypeList type(Type) Ident] + + if match OptInit [E: `= expr] { + send Parser + [E expr(OptInit.expr)] + } + + send Parser + [Semi] + } + case [Export: export_stmt] + { + send Parser + "#define [Export.ident] [number(Export.number)] + } + case ['fallthrough' ';'] + { + # Nothing needed here. + } + case [case_block] + { + send Parser + "[expr( Stmt.case_block.expr )] => { + "[stmt_list( Stmt.case_block._repeat_stmt )] + "} + } + case [default_block] + { + send Parser + "_ => { + "[stmt_list( Stmt.default_block._repeat_stmt )] + "} + } + case [case_label] + { + send Parser + "case [expr( Stmt.case_label.expr )]: + } + case [AS: assign_stmt] + { + send Parser + "[lvalue(AS.LValue) AS.assign_op expr(AS.expr)]; + } + default { + # catches unspecified cases + send Parser [Stmt] + } + } + + void stmt_list( StmtList: stmt* ) + { + for Stmt: stmt in repeat( StmtList ) + stmt( Stmt ) + } + + void trans( Output: stream, Start: start ) + { + Parser = new parser() + + if ( Start.opt_bom.bom ) + send Output [Start.opt_bom.bom] + + stmt_list( Start._repeat_stmt ) + + CO: rust_out::rust_out = Parser->finish() + + if CO { + send Output + [CO] + } + else { + send stderr + "failed to parse output: [Parser->error] + } + } +end + +void trans( Output: stream, Start: start ) +{ + rust_gen::trans( Output, Start ) +} + +include 'rlhc-main.lm' diff --git a/src/libfsm/.exrc b/src/libfsm/.exrc new file mode 100644 index 00000000..412b360f --- /dev/null +++ b/src/libfsm/.exrc @@ -0,0 +1,28 @@ +if &cp | set nocp | endif +let s:cpo_save=&cpo +set cpo&vim +map j +map  k +map Q gq +nmap gx NetrwBrowseX +nnoremap NetrwBrowseX :call netrw#NetrwBrowseX(expand(""),0) +let &cpo=s:cpo_save +unlet s:cpo_save +set autoindent +set autowriteall +set backspace=2 +set fileencodings=ucs-bom,utf-8,default,latin1 +set helplang=en +set incsearch +set nojoinspaces +set makeprg=make\ -j4 +set printoptions=paper:letter +set ruler +set runtimepath=~/.vim,/var/lib/vim/addons,/usr/share/vim/vimfiles,/usr/share/vim/vim74,/usr/share/vim/vimfiles/after,/var/lib/vim/addons/after,~/.vim/after +set showcmd +set showmatch +set suffixes=.bak,~,.swp,.o,.info,.aux,.log,.dvi,.bbl,.blg,.brf,.cb,.ind,.idx,.ilg,.inx,.out,.toc +set viminfo='20,\"50 +set visualbell +set nowritebackup +" vim: set ft=vim : diff --git a/src/libfsm/.gitignore b/src/libfsm/.gitignore new file mode 100644 index 00000000..78db4968 --- /dev/null +++ b/src/libfsm/.gitignore @@ -0,0 +1,57 @@ +/tags +/Makefile +/Makefile.in +/rlscan.cc +/rlparse.cc +/rlparse.h +/version.h +/config.h +/config.h.in +/config.h.in~ +/ragel +/ragel.exe +/.deps +/stamp-h1 +/rlhc +/rlhc.c + +/*.lo + +# Parsing +/parse.c +/rlreduce.cc +/ldparse.c +/ldreduce.cc + +# Common testing file. +/tmp.rl +/tmp.c +/tmp.cc +/tmp.d +/tmp.go +/tmp.ps +/tmp.ml +/tmp.cmi +/tmp.cmx +/tmp.rs +/tmp.crk +/tmp.jl +/tmp +/input + +# The ragel frontend doesn't support OCaml lexical rules yet, so a util is +# needed. +/util.ml +/util.cmi +/util.cmx + +/libragel.a +/libragel.la +/libfsm.a +/libfsm.la +/.libs + +/CMakeFiles +/cmake_install.cmake + +/*.pack diff --git a/src/libfsm/CMakeLists.txt b/src/libfsm/CMakeLists.txt new file mode 100644 index 00000000..3e797981 --- /dev/null +++ b/src/libfsm/CMakeLists.txt @@ -0,0 +1,154 @@ +# Package name +set(_PACKAGE_NAME ragel) + +# Read project configuration from ../configure.ac file +file(STRINGS ../configure.ac _PROJECT_CONFIGS + REGEX "(RAGEL_VERSION=)|(RAGEL_PUBDATE=)") +foreach(_PROJECT_CONFIG ${_PROJECT_CONFIGS}) + if(_PROJECT_CONFIG MATCHES "RAGEL_VERSION=\"([^\"]+)") + string(STRIP ${CMAKE_MATCH_1} RAGEL_VERSION) + endif() + if(_PROJECT_CONFIG MATCHES "RAGEL_PUBDATE=\"([^\"]+)") + string(STRIP ${CMAKE_MATCH_1} RAGEL_PUBDATE) + endif() +endforeach() + +## Generate headers +configure_file(version.h.cmake.in version.h @ONLY) +configure_file(ragel-config.cmake.in + "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config.cmake" @ONLY) + +# Runtime headers +set(RUNTIME_HDR + action.h fsmgraph.h ragel.h common.h + gendata.h redfsm.h dot.h) + +# Other CMake modules +include(GNUInstallDirs) + +# libfsm +add_library(libfsm + buffer.h codegen.h + actloop.h actexp.h + tables.h + binary.h bingoto.h binbreak.h binvar.h + flat.h flatgoto.h flatbreak.h flatvar.h + switch.h switchgoto.h switchbreak.h switchvar.h + goto.h gotoloop.h gotoexp.h + ipgoto.h asm.h + idbase.cc fsmstate.cc fsmbase.cc fsmattach.cc fsmmin.cc fsmgraph.cc + fsmap.cc fsmcond.cc fsmnfa.cc common.cc redfsm.cc gendata.cc + allocgen.cc codegen.cc + actexp.cc binvar.cc + tables.cc tabgoto.cc tabbreak.cc tabvar.cc + binary.cc bingoto.cc binbreak.cc actloop.cc + flat.cc flatgoto.cc flatbreak.cc flatvar.cc + switch.cc switchgoto.cc switchbreak.cc switchvar.cc + goto.cc gotoloop.cc gotoexp.cc ipgoto.cc + dot.cc asm.cc) + +target_include_directories(libfsm + PUBLIC + $ + $ + $ + $) + +set_target_properties(libfsm PROPERTIES + OUTPUT_NAME fsm) + +# libragel +add_library(libragel + # dist + parsedata.h parsetree.h inputdata.h pcheck.h reducer.h rlscan.h load.h + parsetree.cc longest.cc parsedata.cc inputdata.cc load.cc reducer.cc) + +if(BUILD_STANDALONE) + # libragel acts as an intermediate library so we can apply + # flags we want to apply to all ragel targets to libragel + # and they'll automatically propogate. This is a best effort + # to get `-static` placed sooner in the link line where it + # matters at least. + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(libragel PUBLIC -static) + else() + message(FATAL_ERROR "Unsupported toolset for standalone build.") + endif() +endif() + +target_link_libraries(libragel PRIVATE colm::libcolm) + +target_include_directories(libragel + PUBLIC + $ + $ + $ + $ + $ + $) + +set_target_properties(libragel PROPERTIES + OUTPUT_NAME ragel) + +set_property(TARGET libragel APPEND PROPERTY + COMPILE_DEFINITIONS BINDIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") + +# ragel program + +set(RAGEL_LM + rlparse.lm + ragel.lm + rlreduce.lm) + +add_custom_command(OUTPUT + "${CMAKE_CURRENT_BINARY_DIR}/parse.c" + "${CMAKE_CURRENT_BINARY_DIR}/rlreduce.cc" + DEPENDS ${RAGEL_LM} #$(COLM_BINDEP) + COMMAND colm::colm + ARGS -c -b rlparseC + -o "${CMAKE_CURRENT_BINARY_DIR}/parse.c" + -m "${CMAKE_CURRENT_BINARY_DIR}/rlreduce.cc" + rlparse.lm + WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") + +add_executable(ragel + main.cc + "${CMAKE_CURRENT_BINARY_DIR}/parse.c" + "${CMAKE_CURRENT_BINARY_DIR}/rlreduce.cc") + +target_link_libraries(ragel libragel libfsm) + +foreach(_SUBDIR host-ruby host-asm host-julia host-ocaml host-c host-d + host-csharp host-go host-java host-rust host-crack host-js) + add_subdirectory(${_SUBDIR}) +endforeach() + +if(${PROJECT_NAME}_MAKE_INSTALL) + if(NOT DEFINED CMAKE_INSTALL_CMAKEDIR) + set(CMAKE_INSTALL_CMAKEDIR + "${CMAKE_INSTALL_LIBDIR}/cmake/${_PACKAGE_NAME}" + CACHE STRING "CMake packages") + endif() + install(FILES ${RUNTIME_HDR} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ragel") + install(TARGETS libfsm libragel ragel + EXPORT ${_PACKAGE_NAME}-targets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") + install(EXPORT ${_PACKAGE_NAME}-targets + NAMESPACE ${_PACKAGE_NAME}:: + DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") + export(EXPORT ${_PACKAGE_NAME}-targets + NAMESPACE ${_PACKAGE_NAME}:: + FILE "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-targets.cmake") + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config-version.cmake" + VERSION ${RAGEL_VERSION} + COMPATIBILITY AnyNewerVersion) + install(FILES + "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config.cmake" + "${PROJECT_BINARY_DIR}/${_PACKAGE_NAME}-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") +endif() diff --git a/src/libfsm/Makefile.am b/src/libfsm/Makefile.am new file mode 100644 index 00000000..5efcb9b4 --- /dev/null +++ b/src/libfsm/Makefile.am @@ -0,0 +1,43 @@ +# libfsm contains only the FSM construction code and the backend code +# generators for C, asm and cgil (Code Gen Intermediate Language) . It is +# useful for building state machine code generators in programs not connected +# to the ragel language. +lib_LTLIBRARIES = libfsm.la + +libfsminclude_HEADERS = \ + action.h fsmgraph.h common.h \ + gendata.h redfsm.h dot.h asm.h ragel.h + +# nodist_pkginclude_HEADERS = config.h + +# +# libfsm: state machine construction and direct code generation. +# +libfsm_la_CPPFLAGS = -I$(top_srcdir)/src/aapl + +dist_libfsm_la_SOURCES = \ + parsedata.h idbase.h codegen.h \ + actloop.h actexp.h \ + tables.h \ + binary.h bingoto.h binbreak.h binvar.h \ + flat.h flatgoto.h flatbreak.h flatvar.h \ + switch.h switchgoto.h switchbreak.h switchvar.h \ + goto.h gotoloop.h gotoexp.h \ + ipgoto.h asm.h \ + idbase.cc fsmstate.cc fsmbase.cc fsmattach.cc fsmmin.cc fsmgraph.cc \ + fsmap.cc fsmcond.cc fsmnfa.cc common.cc redfsm.cc gendata.cc \ + allocgen.cc codegen.cc \ + actexp.cc binvar.cc \ + tables.cc tabgoto.cc tabbreak.cc tabvar.cc \ + binary.cc bingoto.cc binbreak.cc actloop.cc \ + flat.cc flatgoto.cc flatbreak.cc flatvar.cc \ + switch.cc switchgoto.cc switchbreak.cc switchvar.cc \ + goto.cc gotoloop.cc gotoexp.cc ipgoto.cc \ + dot.cc asm.cc + +libfsm_la_LDFLAGS = -no-undefined + +if LINKER_NO_UNDEFINED +libfsm_la_LDFLAGS += -Wl,--no-undefined +endif + diff --git a/src/libfsm/actexp.cc b/src/libfsm/actexp.cc new file mode 100644 index 00000000..771d4623 --- /dev/null +++ b/src/libfsm/actexp.cc @@ -0,0 +1,218 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "actexp.h" +#include "redfsm.h" +#include "gendata.h" + +void ActExp::FROM_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->fromStateAction != 0 ) + act = state->fromStateAction->actListId + 1; + fromStateActions.value( act ); +} + +void ActExp::COND_ACTION( RedCondPair *cond ) +{ + int action = 0; + if ( cond->action != 0 ) + action = cond->action->actListId + 1; + condActions.value( action ); +} + +void ActExp::TO_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->toStateAction != 0 ) + act = state->toStateAction->actListId + 1; + toStateActions.value( act ); +} + +void ActExp::EOF_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->eofAction != 0 ) + act = state->eofAction->actListId + 1; + eofActions.value( act ); +} + +void ActExp::NFA_PUSH_ACTION( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->push != 0 ) + act = targ->push->actListId+1; + nfaPushActions.value( act ); +} + +void ActExp::NFA_POP_TEST( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->popTest != 0 ) + act = targ->popTest->actListId+1; + nfaPopTrans.value( act ); +} + + +/* Write out the function switch. This switch is keyed on the values + * of the func index. */ +std::ostream &ActExp::FROM_STATE_ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numFromStateRefs > 0 ) { + /* Write the entry label. */ + out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( 0, false, false ) ); + out << "\n\t"; + } + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +/* Write out the function switch. This switch is keyed on the values + * of the func index. */ +std::ostream &ActExp::ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numTransRefs > 0 ) { + /* Write the entry label. */ + out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( 0, false, false ) ); + out << "\n\t"; + } + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +/* Write out the function switch. This switch is keyed on the values + * of the func index. */ +std::ostream &ActExp::TO_STATE_ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numToStateRefs > 0 ) { + /* Write the entry label. */ + out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( 0, false, false ) ); + out << "\n\t"; + } + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &ActExp::EOF_ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numEofRefs > 0 ) { + /* Write the entry label. */ + out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( 0, true, false ) ); + out << "\n\t"; + } + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + + +void ActExp::FROM_STATE_ACTIONS() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " switch ( " << ARR_REF( fromStateActions ) << "[" << vCS() << "] ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + "\n"; + } +} + +void ActExp::REG_ACTIONS( std::string cond ) +{ + out << + " switch ( " << ARR_REF( condActions ) << "[" << cond << "] ) {\n"; + ACTION_SWITCH() << + " }\n" + "\n"; +} +void ActExp::TO_STATE_ACTIONS() +{ + if ( redFsm->anyToStateActions() ) { + out << + " switch ( " << ARR_REF( toStateActions ) << "[" << vCS() << "] ) {\n"; + TO_STATE_ACTION_SWITCH() << + " }\n" + "\n"; + } +} + + +void ActExp::EOF_ACTIONS() +{ + if ( redFsm->anyEofActions() ) { + out << + " switch ( " << ARR_REF( eofActions ) << "[" << vCS() << "] ) {\n"; + EOF_ACTION_SWITCH() << + " }\n"; + } +} + +void ActExp::NFA_FROM_STATE_ACTION_EXEC() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " switch ( " << ARR_REF( fromStateActions ) << "[nfa_bp[nfa_len].state] ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + "\n"; + } +} + diff --git a/src/libfsm/actexp.h b/src/libfsm/actexp.h new file mode 100644 index 00000000..49165755 --- /dev/null +++ b/src/libfsm/actexp.h @@ -0,0 +1,62 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _BINEXP_H +#define _BINEXP_H + +#include "binary.h" + +struct RedStateAp; +struct RedCondPair; + +class ActExp + : public virtual Tables +{ +public: + ActExp( const CodeGenArgs &args ) + : + Tables( args ) + {} + + virtual void FROM_STATE_ACTION( RedStateAp *state ); + virtual void COND_ACTION( RedCondPair *cond ); + virtual void TO_STATE_ACTION( RedStateAp *state ); + virtual void EOF_ACTION( RedStateAp *state ); + + virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); + virtual void NFA_POP_TEST( RedNfaTarg *targ ); + + virtual std::ostream &FROM_STATE_ACTION_SWITCH(); + virtual std::ostream &ACTION_SWITCH(); + virtual std::ostream &TO_STATE_ACTION_SWITCH(); + virtual std::ostream &EOF_ACTION_SWITCH(); + + virtual void TO_STATE_ACTIONS(); + virtual void REG_ACTIONS( std::string cond ); + virtual void FROM_STATE_ACTIONS(); + virtual void EOF_ACTIONS(); + + virtual void NFA_FROM_STATE_ACTION_EXEC(); +}; + +#endif + diff --git a/src/libfsm/action.h b/src/libfsm/action.h new file mode 100644 index 00000000..5c79befe --- /dev/null +++ b/src/libfsm/action.h @@ -0,0 +1,119 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _ACTION_H +#define _ACTION_H + +#include "fsmgraph.h" + +struct NameInst; +struct NameRef; +struct FsmLongestMatch; +struct InlineList; +struct FsmLongestMatchPart; +struct Action; +struct CondSpace; + +/* + * Inline code tree + */ +struct InlineItem +{ + enum Type + { + Text, Goto, Call, Ncall, Next, GotoExpr, CallExpr, NcallExpr, NextExpr, Ret, Nret, + PChar, Char, Hold, Curs, Targs, Entry, Exec, Break, Nbreak, + LmSwitch, LmSetActId, LmSetTokEnd, LmOnLast, LmOnNext, LmOnLagBehind, + LmInitAct, LmInitTokStart, LmSetTokStart, LmNfaOnNext, LmNfaOnLast, LmNfaOnEof, Stmt, Subst, + NfaWrapAction, NfaWrapConds + }; + + InlineItem( const InputLoc &loc, std::string data, Type type ) : + loc(loc), data(data), nameRef(0), children(0), type(type) { } + + InlineItem( const InputLoc &loc, NameRef *nameRef, Type type ) : + loc(loc), nameRef(nameRef), children(0), type(type) { } + + InlineItem( const InputLoc &loc, FsmLongestMatch *longestMatch, + FsmLongestMatchPart *longestMatchPart, Type type ) : loc(loc), + nameRef(0), children(0), longestMatch(longestMatch), + longestMatchPart(longestMatchPart), type(type) { } + + InlineItem( const InputLoc &loc, NameInst *nameTarg, Type type ) : + loc(loc), nameRef(0), nameTarg(nameTarg), children(0), + type(type) { } + + InlineItem( const InputLoc &loc, Type type ) : + loc(loc), nameRef(0), children(0), type(type) { } + + InlineItem( const InputLoc &loc, Action *wrappedAction, Type type ) + : + loc(loc), nameRef(0), children(0), longestMatch(0), + longestMatchPart(0), wrappedAction(wrappedAction), type(type) + {} + + InlineItem( const InputLoc &loc, CondSpace *condSpace, + const CondKeySet &condKeySet, Type type ) + : + loc(loc), nameRef(0), children(0), longestMatch(0), + longestMatchPart(0), wrappedAction(0), condSpace(condSpace), + condKeySet(condKeySet), type(type) + {} + + ~InlineItem(); + + InputLoc loc; + std::string data; + NameRef *nameRef; + NameInst *nameTarg; + InlineList *children; + FsmLongestMatch *longestMatch; + FsmLongestMatchPart *longestMatchPart; + long substPos; + Action *wrappedAction; + CondSpace *condSpace; + CondKeySet condKeySet; + Type type; + + InlineItem *prev, *next; +}; + +/* Normally this would be atypedef, but that would entail including DList from + * ptreetypes, which should be just typedef forwards. */ +struct InlineList : public DList { }; + +struct InlineBlock +{ + InlineBlock( const InputLoc &loc, InlineList *inlineList ) + : loc(loc), inlineList(inlineList) {} + + ~InlineBlock() + { + inlineList->empty(); + delete inlineList; + } + + InputLoc loc; + InlineList *inlineList; +}; + +#endif diff --git a/src/libfsm/actloop.cc b/src/libfsm/actloop.cc new file mode 100644 index 00000000..675e78fa --- /dev/null +++ b/src/libfsm/actloop.cc @@ -0,0 +1,229 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "actloop.h" +#include "redfsm.h" +#include "gendata.h" + +void ActLoop::FROM_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->fromStateAction != 0 ) + act = state->fromStateAction->location+1; + fromStateActions.value( act ); +} + +void ActLoop::COND_ACTION( RedCondPair *cond ) +{ + int act = 0; + if ( cond->action != 0 ) + act = cond->action->location+1; + condActions.value( act ); +} + +void ActLoop::TO_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->toStateAction != 0 ) + act = state->toStateAction->location+1; + toStateActions.value( act ); +} + +void ActLoop::EOF_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->eofAction != 0 ) + act = state->eofAction->location+1; + eofActions.value( act ); +} + +void ActLoop::NFA_PUSH_ACTION( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->push != 0 ) + act = targ->push->actListId+1; + nfaPushActions.value( act ); +} + +void ActLoop::NFA_POP_TEST( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->popTest != 0 ) + act = targ->popTest->actListId+1; + nfaPopTrans.value( act ); +} + +std::ostream &ActLoop::FROM_STATE_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numFromStateRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; + ACTION( out, act, IlOpts( 0, false, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &ActLoop::ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numTransRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; + ACTION( out, act, IlOpts( 0, false, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &ActLoop::TO_STATE_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numToStateRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; + ACTION( out, act, IlOpts( 0, false, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + + +std::ostream &ActLoop::EOF_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numEofRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t " << CASE( STR( act->actionId ) ) << " {\n"; + ACTION( out, act, IlOpts( 0, true, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + + + +void ActLoop::FROM_STATE_ACTIONS() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( fromStateActions ) + "[" + vCS() + "]" ) << ";\n" + " " << nacts << " = " << CAST(UINT()) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + " " << nacts << " -= 1;\n" + " " << acts << " += 1;\n" + " }\n" + "\n"; + } +} + +void ActLoop::REG_ACTIONS( std::string cond ) +{ + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( condActions ) + "[" + cond + "]" ) << ";\n" + " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " )\n" + " {\n"; + ACTION_SWITCH() << + " }\n" + " " << nacts << " -= 1;\n" + " " << acts << " += 1;\n" + " }\n" + "\n"; +} + +void ActLoop::TO_STATE_ACTIONS() +{ + if ( redFsm->anyToStateActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( toStateActions ) + "[" + vCS() + "]" ) << ";\n" + " " << nacts << " = " << CAST(UINT()) << DEREF( ARR_REF( actions ), acts.ref() ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), acts.ref() ) << " ) {\n"; + TO_STATE_ACTION_SWITCH() << + " }\n" + " " << nacts << " -= 1;\n" + " " << acts << " += 1;\n" + " }\n" + "\n"; + } +} + +void ActLoop::EOF_ACTIONS() +{ + if ( redFsm->anyEofActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( eofActions ) + "[" + vCS() + "]" ) << ";\n" + " " << nacts << " = " << CAST(UINT()) << DEREF( ARR_REF( actions ), acts.ref() ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), acts.ref() ) << " ) {\n"; + EOF_ACTION_SWITCH() << + " }\n" + " " << nacts << " -= 1;\n" + " " << acts << " += 1;\n" + " }\n"; + } +} + +void ActLoop::NFA_FROM_STATE_ACTION_EXEC() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( fromStateActions ) + "[nfa_bp[nfa_len].state]" ) << ";\n" + " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), acts.ref() ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), acts.ref() ) << " ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + " " << nacts << " -= 1;\n" + " " << acts << " += 1;\n" + " }\n" + "\n"; + } +} + diff --git a/src/libfsm/actloop.h b/src/libfsm/actloop.h new file mode 100644 index 00000000..238ba72a --- /dev/null +++ b/src/libfsm/actloop.h @@ -0,0 +1,63 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _BINLOOP_H +#define _BINLOOP_H + +#include "binary.h" +#include "tables.h" + +struct RedStateAp; +struct RedCondPair; + +class ActLoop + : public virtual Tables +{ +public: + ActLoop( const CodeGenArgs &args ) + : + Tables( args ) + {} + + virtual void FROM_STATE_ACTION( RedStateAp *state ); + virtual void COND_ACTION( RedCondPair *cond ); + virtual void TO_STATE_ACTION( RedStateAp *state ); + virtual void EOF_ACTION( RedStateAp *state ); + + virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); + virtual void NFA_POP_TEST( RedNfaTarg *targ ); + + virtual std::ostream &FROM_STATE_ACTION_SWITCH(); + virtual std::ostream &ACTION_SWITCH(); + virtual std::ostream &TO_STATE_ACTION_SWITCH(); + virtual std::ostream &EOF_ACTION_SWITCH(); + + virtual void FROM_STATE_ACTIONS(); + virtual void REG_ACTIONS( std::string cond ); + virtual void TO_STATE_ACTIONS(); + virtual void EOF_ACTIONS(); + + virtual void NFA_FROM_STATE_ACTION_EXEC(); +}; + + +#endif diff --git a/src/libfsm/allocgen.cc b/src/libfsm/allocgen.cc new file mode 100644 index 00000000..02278bfa --- /dev/null +++ b/src/libfsm/allocgen.cc @@ -0,0 +1,136 @@ +/* + * Copyright 2005-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "parsedata.h" +#include "fsmgraph.h" +#include "gendata.h" + +/* + * Code generators. + */ +#include "bingoto.h" +#include "binbreak.h" +#include "binvar.h" +#include "flatgoto.h" +#include "flatbreak.h" +#include "flatvar.h" +#include "switchgoto.h" +#include "switchbreak.h" +#include "switchvar.h" +#include "gotoloop.h" +#include "gotoexp.h" +#include "ipgoto.h" +#include "asm.h" + +CodeGenData *makeCodeGenAsm( const HostLang *hostLang, const CodeGenArgs &args ) +{ + return new AsmCodeGen( args ); +} + +/* Invoked by the parser when a ragel definition is opened. */ +CodeGenData *makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ) +{ + FsmGbl *id = args.id; + CodeGenData *codeGen = 0; + BackendFeature feature = hostLang->feature; + if ( args.forceVar ) + feature = VarFeature; + + switch ( args.codeStyle ) { + case GenBinaryLoop: + if ( feature == GotoFeature ) + codeGen = new BinGotoLoop( args ); + else if ( feature == BreakFeature ) + codeGen = new BinBreakLoop( args ); + else + codeGen = new BinVarLoop( args ); + break; + + case GenBinaryExp: + if ( feature == GotoFeature ) + codeGen = new BinGotoExp( args ); + else if ( feature == BreakFeature ) + codeGen = new BinBreakExp( args ); + else + codeGen = new BinVarExp( args ); + break; + + case GenFlatLoop: + if ( feature == GotoFeature ) + codeGen = new FlatGotoLoop( args ); + else if ( feature == BreakFeature ) + codeGen = new FlatBreakLoop( args ); + else + codeGen = new FlatVarLoop( args ); + break; + + case GenFlatExp: + if ( feature == GotoFeature ) + codeGen = new FlatGotoExp( args ); + else if ( feature == BreakFeature ) + codeGen = new FlatBreakExp( args ); + else + codeGen = new FlatVarExp( args ); + break; + case GenSwitchLoop: + if ( feature == GotoFeature ) + codeGen = new SwitchGotoLoop( args ); + else if ( feature == BreakFeature ) + codeGen = new SwitchBreakLoop( args ); + else + codeGen = new SwitchVarLoop( args ); + break; + + case GenSwitchExp: + if ( feature == GotoFeature ) + codeGen = new SwitchGotoExp( args ); + else if ( feature == BreakFeature ) + codeGen = new SwitchBreakExp( args ); + else + codeGen = new SwitchVarExp( args ); + break; + + + case GenGotoLoop: + if ( feature == GotoFeature ) + codeGen = new GotoLoop(args); + else + id->error() << "unsupported lang/style combination" << endp; + break; + case GenGotoExp: + if ( feature == GotoFeature ) + codeGen = new GotoExp(args); + else + id->error() << "unsupported lang/style combination" << endp; + break; + + case GenIpGoto: + if ( feature == GotoFeature ) + codeGen = new IpGoto(args); + else + id->error() << "unsupported lang/style combination" << endp; + break; + } + + return codeGen; +} diff --git a/src/libfsm/asm.cc b/src/libfsm/asm.cc new file mode 100644 index 00000000..ecfe1c0f --- /dev/null +++ b/src/libfsm/asm.cc @@ -0,0 +1,2046 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "asm.h" +#include "redfsm.h" +#include "gendata.h" +#include "bstmap.h" +#include "ragel.h" +#include "redfsm.h" +#include "bstmap.h" +#include "gendata.h" +#include "parsedata.h" +#include + +using std::ostream; +using std::ostringstream; +using std::string; +using std::endl; +using std::istream; +using std::ifstream; +using std::ostream; +using std::ios; +using std::cin; +using std::endl; + +extern int numSplitPartitions; +bool printStatistics = false; + +/* Enables transition logging in the form that score-based state sorting can + * processes. This bit of code is intended to increase locality and reduce + * cache misses. Gains are minimal, 1-2%. */ +// #define LOG_TRANS 1 + +void asmLineDirective( ostream &out, const char *fileName, int line ) +{ + /* Write the preprocessor line info for to the input file. */ + out << "#line " << line << " \""; + for ( const char *pc = fileName; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + out << "\\\\"; + else + out << *pc; + } + out << '"'; + + out << '\n'; +} + +/* Init code gen with in parameters. */ +AsmCodeGen::AsmCodeGen( const CodeGenArgs &args ) +: + CodeGenData( args ), + nextLmSwitchLabel( 1 ), + stackCS( false ) +{ +} + +void AsmCodeGen::genAnalysis() +{ + /* For directly executable machines there is no required state + * ordering. Choose a depth-first ordering to increase the + * potential for fall-throughs. */ + redFsm->depthFirstOrdering(); + + /* Choose default transitions and make the flat transitions by character class. */ + redFsm->chooseDefaultSpan(); + redFsm->makeFlatClass(); + + /* If any errors have occured in the input file then don't write anything. */ + if ( red->id->errorCount > 0 ) + return; + + redFsm->setInTrans(); + + /* Anlayze Machine will find the final action reference counts, among other + * things. We will use these in reporting the usage of fsm directives in + * action code. */ + red->analyzeMachine(); +} + +/* Write out the fsm name. */ +string AsmCodeGen::FSM_NAME() +{ + return fsmName; +} + +/* Emit the offset of the start state as a decimal integer. */ +string AsmCodeGen::START_STATE_ID() +{ + ostringstream ret; + ret << redFsm->startState->id; + return ret.str(); +}; + +string AsmCodeGen::ACCESS() +{ + ostringstream ret; + if ( red->accessExpr != 0 ) + INLINE_LIST( ret, red->accessExpr, 0, false, false ); + return ret.str(); +} + + +string AsmCodeGen::P() +{ + ostringstream ret; + if ( red->pExpr == 0 ) + ret << "%r12"; + else { + INLINE_LIST( ret, red->pExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::PE() +{ + ostringstream ret; + if ( red->peExpr == 0 ) + ret << "%r13"; + else { + INLINE_LIST( ret, red->peExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::vCS() +{ + ostringstream ret; + if ( red->csExpr == 0 ) { + if ( stackCS ) + ret << "-48(%rbp)"; + else + ret << "%r11"; + } + else { + INLINE_LIST( ret, red->csExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::TOP() +{ + ostringstream ret; + if ( red->topExpr == 0 ) + ret << "-64(%rbp)"; + else { + ret << "("; + INLINE_LIST( ret, red->topExpr, 0, false, false ); + ret << ")"; + } + return ret.str(); +} + +string AsmCodeGen::NFA_STACK() +{ + return string( "-80(%rbp)" ); +} + +string AsmCodeGen::NFA_TOP() +{ + return string( "-88(%rbp)" ); +} + +string AsmCodeGen::NFA_SZ() +{ + return string( "-96(%rbp)" ); +} + +string AsmCodeGen::STACK() +{ + ostringstream ret; + if ( red->stackExpr == 0 ) + ret << "-56(%rbp)"; + else { + ret << "("; + INLINE_LIST( ret, red->stackExpr, 0, false, false ); + ret << ")"; + } + return ret.str(); +} + +string AsmCodeGen::vEOF() +{ + ostringstream ret; + if ( red->eofExpr == 0 ) + ret << "-8(%rbp)"; + else { + INLINE_LIST( ret, red->eofExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::TOKSTART() +{ + ostringstream ret; + if ( red->tokstartExpr == 0 ) + ret << "-16(%rbp)"; + else { + INLINE_LIST( ret, red->tokstartExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::TOKEND() +{ + ostringstream ret; + if ( red->tokendExpr == 0 ) + ret << "-24(%rbp)"; + else { + INLINE_LIST( ret, red->tokendExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::ACT() +{ + ostringstream ret; + if ( red->actExpr == 0 ) + ret << "-32(%rbp)"; + else { + INLINE_LIST( ret, red->actExpr, 0, false, false ); + } + return ret.str(); +} + +string AsmCodeGen::NBREAK() +{ + return string("-33(%rbp)"); +} + +string AsmCodeGen::GET_KEY() +{ + ostringstream ret; + if ( red->getKeyExpr != 0 ) { + /* Emit the user supplied method of retrieving the key. */ + ret << "("; + INLINE_LIST( ret, red->getKeyExpr, 0, false, false ); + ret << ")"; + } + else { + /* Expression for retrieving the key, use simple dereference. */ + ret << "(" << P() << ")"; + } + return ret.str(); +} + +string AsmCodeGen::COND_KEY( CondKey key ) +{ + ostringstream ret; + ret << "$" << key.getVal(); + return ret.str(); +} + + +/* Write out a key from the fsm code gen. Depends on wether or not the key is + * signed. */ +string AsmCodeGen::KEY( Key key ) +{ + ostringstream ret; + ret << "$" << key.getVal(); + return ret.str(); +} + +bool AsmCodeGen::isAlphTypeSigned() +{ + return keyOps->isSigned; +} + +void AsmCodeGen::EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) +{ + /* The parser gives fexec two children. The double brackets are for D + * code. If the inline list is a single word it will get interpreted as a + * C-style cast by the D compiler. */ + + ret << + " subq $1, "; + INLINE_LIST( ret, item->children, targState, inFinish, false ); + ret << + "\n" + " movq "; + INLINE_LIST( ret, item->children, targState, inFinish, false ); + ret << ", " << P() << "\n"; +} + +void AsmCodeGen::LM_SWITCH( ostream &ret, GenInlineItem *item, + int targState, int inFinish, bool csForced ) +{ + long done = nextLmSwitchLabel++; + + ret << + " movq " << ACT() << ", %rax\n"; + + for ( GenInlineList::Iter lma = *item->children; lma.lte(); lma++ ) { + long l = nextLmSwitchLabel++; + + /* Write the case label, the action and the case break. */ + if ( lma->lmId < 0 ) { + } + else { + ret << + " cmpq $" << lma->lmId << ", %rax\n" + " jne " << LABEL( "lm_switch_next", l ) << "\n"; + } + + INLINE_LIST( ret, lma->children, targState, inFinish, csForced ); + + ret << + " jmp " << LABEL( "lm_done", done ) << "\n" + "" << LABEL( "lm_switch_next", l ) << ":\n"; + } + + ret << + "" << LABEL( "lm_done", done ) << ":\n"; +} + +void AsmCodeGen::SET_ACT( ostream &ret, GenInlineItem *item ) +{ + ret << + " movq $" << item->lmId << ", " << ACT() << "\n"; +} + +void AsmCodeGen::SET_TOKEND( ostream &ret, GenInlineItem *item ) +{ + /* Sets tokend, there may be an offset. */ + ret << + " movq " << P() << ", %rax\n"; + + if ( item->offset != 0 ) { + out << + " addq $" << item->offset << ", %rax\n"; + } + + out << + " movq %rax, " << TOKEND() << "\n"; +} + +void AsmCodeGen::GET_TOKEND( ostream &ret, GenInlineItem *item ) +{ + ret << + " movq " << TOKEND() << ", " << "%rax\n"; +} + +void AsmCodeGen::INIT_TOKSTART( ostream &ret, GenInlineItem *item ) +{ + ret << + " movq $0, " << TOKSTART() << "\n"; +} + +void AsmCodeGen::INIT_ACT( ostream &ret, GenInlineItem *item ) +{ + ret << + " movq $0, " << ACT() << "\n"; +} + +void AsmCodeGen::SET_TOKSTART( ostream &ret, GenInlineItem *item ) +{ + ret << + " movq " << P() << ", " << TOKSTART() << "\n"; +} + +void AsmCodeGen::HOST_STMT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + } +} + +void AsmCodeGen::HOST_EXPR( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + } +} + +void AsmCodeGen::HOST_TEXT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + } +} + +void AsmCodeGen::GEN_STMT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + } +} + +void AsmCodeGen::GEN_EXPR( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + } +} + +void AsmCodeGen::LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) +{ + /* The parser gives fexec two children. The double brackets are for D code. + * If the inline list is a single word it will get interpreted as a C-style + * cast by the D compiler. This should be in the D code generator. */ + INLINE_LIST( ret, item->children, targState, inFinish, false ); + + ret << + " movq %rax, " << P() << "\n" + " subq $1, " << P() << "\n"; +} + +void AsmCodeGen::NBREAK( ostream &ret, int targState, bool csForced ) +{ + outLabelUsed = true; + ret << + " addq $1, " << P() << "\n"; + + if ( !csForced ) { + ret << + " movq $" << targState << ", " << vCS() << "\n"; + } + + ret << + " movb $1, " << NBREAK() << "\n" + " jmp " << LABEL( "pop" ) << "\n"; +} + +/* Write out an inline tree structure. Walks the list and possibly calls out + * to virtual functions than handle language specific items in the tree. */ +void AsmCodeGen::INLINE_LIST( ostream &ret, GenInlineList *inlineList, + int targState, bool inFinish, bool csForced ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case GenInlineItem::Text: + ret << item->data; + break; + case GenInlineItem::Goto: + GOTO( ret, item->targState->id, inFinish ); + break; + case GenInlineItem::Call: + CALL( ret, item->targState->id, targState, inFinish ); + break; + case GenInlineItem::Next: + NEXT( ret, item->targState->id, inFinish ); + break; + case GenInlineItem::Ret: + RET( ret, inFinish ); + break; + case GenInlineItem::PChar: + ret << P(); + break; + case GenInlineItem::Char: + ret << GET_KEY(); + break; + case GenInlineItem::Hold: + ret << + " subq $1, " << P() << "\n"; + break; + case GenInlineItem::Exec: + EXEC( ret, item, targState, inFinish ); + break; + case GenInlineItem::Curs: + CURS( ret, inFinish ); + break; + case GenInlineItem::Targs: + TARGS( ret, inFinish, targState ); + break; + case GenInlineItem::Entry: + ret << item->targState->id; + break; + case GenInlineItem::GotoExpr: + GOTO_EXPR( ret, item, inFinish ); + break; + case GenInlineItem::CallExpr: + CALL_EXPR( ret, item, targState, inFinish ); + break; + case GenInlineItem::NextExpr: + NEXT_EXPR( ret, item, inFinish ); + break; + case GenInlineItem::LmSwitch: + LM_SWITCH( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::LmSetActId: + SET_ACT( ret, item ); + break; + case GenInlineItem::LmSetTokEnd: + SET_TOKEND( ret, item ); + break; + case GenInlineItem::LmGetTokEnd: + GET_TOKEND( ret, item ); + break; + case GenInlineItem::LmInitTokStart: + INIT_TOKSTART( ret, item ); + break; + case GenInlineItem::LmInitAct: + INIT_ACT( ret, item ); + break; + case GenInlineItem::LmSetTokStart: + SET_TOKSTART( ret, item ); + break; + case GenInlineItem::Break: + BREAK( ret, targState, csForced ); + break; + /* Stubbed. */ + case GenInlineItem::Ncall: + NCALL( ret, item->targState->id, targState, inFinish ); + break; + case GenInlineItem::NcallExpr: + NCALL_EXPR( ret, item, targState, inFinish ); + break; + case GenInlineItem::Nret: + NRET( ret, inFinish ); + break; + case GenInlineItem::Nbreak: + NBREAK( ret, targState, csForced ); + break; + case GenInlineItem::LmCase: + break; + + case GenInlineItem::LmExec: + LM_EXEC( ret, item, targState, inFinish ); + break; + + case GenInlineItem::LmHold: + ret << + " subq $1, " << P() << "\n"; + break; + case GenInlineItem::NfaClear: + ret << + " movq $0, " << NFA_TOP() << "\n"; + break; + + case GenInlineItem::HostStmt: + HOST_STMT( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::HostExpr: + HOST_EXPR( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::HostText: + HOST_TEXT( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::GenStmt: + GEN_STMT( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::GenExpr: + GEN_EXPR( ret, item, targState, inFinish, csForced ); + break; + /* Handled at the top level. */ + case GenInlineItem::NfaWrapAction: + case GenInlineItem::NfaWrapConds: + break; + } + } +} +/* Write out paths in line directives. Escapes any special characters. */ +string AsmCodeGen::LDIR_PATH( char *path ) +{ + ostringstream ret; + for ( char *pc = path; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + ret << "\\\\"; + else + ret << *pc; + } + return ret.str(); +} + +void AsmCodeGen::ACTION( ostream &ret, GenAction *action, int targState, + bool inFinish, bool csForced ) +{ + /* Write the preprocessor line info for going into the source file. */ + asmLineDirective( ret, action->loc.fileName, action->loc.line ); + + /* Write the block and close it off. */ + INLINE_LIST( ret, action->inlineList, targState, inFinish, csForced ); +} + +void AsmCodeGen::CONDITION( ostream &ret, GenAction *condition ) +{ + ret << "\n"; + asmLineDirective( ret, condition->loc.fileName, condition->loc.line ); + INLINE_LIST( ret, condition->inlineList, 0, false, false ); +} + +bool singleItem( GenAction *action, GenInlineItem::Type type ) +{ + return action->inlineList->length() == 1 && + action->inlineList->head->type == type; +} + +void AsmCodeGen::NFA_CONDITION( ostream &ret, GenAction *condition, bool last ) +{ + if ( singleItem( condition, GenInlineItem::NfaWrapAction ) ) + { + GenAction *action = condition->inlineList->head->wrappedAction; + ACTION( out, action, 0, false, false ); + } + else if ( singleItem( condition, GenInlineItem::NfaWrapConds ) ) + { + GenCondSpace *condSpace = condition->inlineList->head->condSpace; + const CondKeySet &condKeySet = condition->inlineList->head->condKeySet; + + out << " movq $0, %r9\n"; + for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + out << + " pushq %r9\n"; + + CONDITION( out, *csi ); + out << + "\n" + " test %eax, %eax\n" + " setne %cl\n" + " movsbq %cl, %rcx\n" + " salq $" << csi.pos() << ", %rcx\n" + " popq %r9\n" + " addq %rcx, %r9\n"; + } + + for ( int c = 0; c < condKeySet.length(); c++ ) { + CondKey key = condKeySet[c]; + out << + " cmpq " << COND_KEY( key ) << ", %r9\n" + " je 102f\n"; + } + + out << + " jmp " << LABEL( "pop_fail" ) << "\n" + "102:\n"; + } + else { + CONDITION( ret, condition ); + out << + " test %eax, %eax\n" + " jz " << LABEL( "pop_fail" ) << "\n"; + } +} + +string AsmCodeGen::ERROR_STATE() +{ + ostringstream ret; + if ( redFsm->errState != 0 ) + ret << redFsm->errState->id; + else + ret << "-1"; + return ret.str(); +} + +string AsmCodeGen::FIRST_FINAL_STATE() +{ + ostringstream ret; + if ( redFsm->firstFinState != 0 ) + ret << redFsm->firstFinState->id; + else + ret << redFsm->nextStateId; + return ret.str(); +} + +void AsmCodeGen::writeInit() +{ + if ( !noCS ) { + /* Don't use vCS here. vCS may assumes CS needs to be on the stack. + * Just use the interface register. */ + out << + " movq $" << redFsm->startState->id << ", %r11\n"; + } + + if ( redFsm->anyNfaStates() ) { + out << + " movq $0, " << NFA_TOP() << "\n"; + } + + /* If there are any calls, then the stack top needs initialization. */ + if ( redFsm->anyActionCalls() || redFsm->anyActionRets() ) { + out << + " movq $0, " << TOP() << "\n"; + } + + if ( red->hasLongestMatch ) { + out << + " movq $0, " << TOKSTART() << "\n" + " movq $0, " << TOKEND() << "\n" + " movq $0, " << ACT() << "\n"; + } +} + +string AsmCodeGen::DATA_PREFIX() +{ + if ( !noPrefix ) + return FSM_NAME() + "_"; + return ""; +} + +/* Emit the alphabet data type. */ +string AsmCodeGen::ALPH_TYPE() +{ + string ret = alphType->data1; + if ( alphType->data2 != 0 ) { + ret += " "; + ret += + alphType->data2; + } + return ret; +} + +void AsmCodeGen::STATIC_CONST_INT( const string &name, const string &value ) +{ + out << + " .align 8\n" + " .type " << name << ", @object\n" + " .size " << name << ", 8\n" << + name << ":\n" + " .long " << value << "\n"; +} + +void AsmCodeGen::STATE_IDS() +{ + if ( redFsm->startState != 0 ) + STATIC_CONST_INT( START(), START_STATE_ID() ); + + if ( !noFinal ) + STATIC_CONST_INT( FIRST_FINAL(), FIRST_FINAL_STATE() ); + + if ( !noError ) + STATIC_CONST_INT( ERROR(), ERROR_STATE() ); + + out << "\n"; + + if ( red->entryPointNames.length() > 0 ) { + for ( EntryNameVect::Iter en = red->entryPointNames; en.lte(); en++ ) { + ostringstream ret; + ret << redFsm->startState->id; + + STATIC_CONST_INT( string( DATA_PREFIX() + "en_" + *en ), + ret.str() ); + } + out << "\n"; + } +} + +void AsmCodeGen::writeStart() +{ + out << START_STATE_ID(); +} + +void AsmCodeGen::writeFirstFinal() +{ + out << FIRST_FINAL_STATE(); +} + +void AsmCodeGen::writeError() +{ + out << ERROR_STATE(); +} + +string AsmCodeGen::PTR_CONST() +{ + return "const "; +} + +string AsmCodeGen::PTR_CONST_END() +{ + return ""; +} + +std::ostream &AsmCodeGen::OPEN_ARRAY( string type, string name ) +{ + out << "static const " << type << " " << name << "[] = {\n"; + return out; +} + +std::ostream &AsmCodeGen::CLOSE_ARRAY() +{ + return out << "};\n"; +} + +std::ostream &AsmCodeGen::STATIC_VAR( string type, string name ) +{ + out << "static const " << type << " " << name; + return out; +} + +string AsmCodeGen::UINT( ) +{ + return "unsigned int"; +} + +string AsmCodeGen::ARR_OFF( string ptr, string offset ) +{ + return ptr + " + " + offset; +} + +string AsmCodeGen::CAST( string type ) +{ + return "(" + type + ")"; +} + +string AsmCodeGen::NULL_ITEM() +{ + return "0"; +} + +string AsmCodeGen::POINTER() +{ + return " *"; +} + +std::ostream &AsmCodeGen::SWITCH_DEFAULT() +{ + return out; +} + +string AsmCodeGen::CTRL_FLOW() +{ + return ""; +} + +void AsmCodeGen::writeExports() +{ + if ( red->exportList.length() > 0 ) { + for ( ExportList::Iter ex = red->exportList; ex.lte(); ex++ ) { + out << "#define " << DATA_PREFIX() << "ex_" << ex->name << " " << + KEY(ex->key) << "\n"; + } + out << "\n"; + } +} + +string AsmCodeGen::LABEL( const char *type, long i ) +{ + std::stringstream s; + s << ".L" << red->machineId << "_" << type << "_" << i; + return s.str(); +} + +string AsmCodeGen::LABEL( const char *name ) +{ + std::stringstream s; + s << ".L" << red->machineId << "_" << name; + return s.str(); +} + +void AsmCodeGen::emitSingleIfElseIf( RedStateAp *state ) +{ + /* Load up the singles. */ + int numSingles = state->outSingle.length(); + RedTransEl *data = state->outSingle.data; + + /* Write out the single indices. */ + for ( int j = 0; j < numSingles; j++ ) { + out << + " cmpb " << KEY( data[j].lowKey ) << ", %r10b\n" + " je " << TRANS_GOTO_TARG( data[j].value ) << "\n"; + } +} + +void AsmCodeGen::emitSingleJumpTable( RedStateAp *state, string def ) +{ + int numSingles = state->outSingle.length(); + RedTransEl *data = state->outSingle.data; + + long long low = data[0].lowKey.getVal(); + long long high = data[numSingles-1].lowKey.getVal(); + + if ( def.size() == 0 ) + def = LABEL( "sjf", state->id ); + + out << + " movzbq %r10b, %rax\n" + " subq $" << low << ", %rax\n" + " cmpq $" << (high - low) << ", %rax\n" + " ja " << def << "\n" + " leaq " << LABEL( "sjt", state->id ) << "(%rip), %rcx\n" + " movslq (%rcx,%rax,4), %rdx\n" + " addq %rcx, %rdx\n" + " jmp *%rdx\n" + " .section .rodata\n" + " .align 4\n" + << LABEL( "sjt", state->id ) << ":\n"; + + for ( long long j = 0; j < numSingles; j++ ) { + /* Fill in gap between prev and this. */ + if ( j > 0 ) { + long long span = keyOps->span( data[j-1].lowKey, data[j].lowKey ) - 2; + for ( long long k = 0; k < span; k++ ) { + out << " .long " << def << " - " << + LABEL( "sjt", state->id ) << "\n"; + } + } + + out << " .long " << TRANS_GOTO_TARG( data[j].value ) << " - " << + LABEL( "sjt", state->id ) << "\n"; + } + + out << + " .text\n" + "" << LABEL( "sjf", state->id ) << ":\n"; +} + + +void AsmCodeGen::emitRangeBSearch( RedStateAp *state, int low, int high ) +{ + static int nl = 1; + + /* Get the mid position, staying on the lower end of the range. */ + int mid = (low + high) >> 1; + RedTransEl *data = state->outRange.data; + + /* Determine if we need to look higher or lower. */ + bool anyLower = mid > low; + bool anyHigher = mid < high; + + /* Determine if the keys at mid are the limits of the alphabet. */ + bool limitLow = keyOps->eq( data[mid].lowKey, keyOps->minKey ); + bool limitHigh = keyOps->eq( data[mid].highKey, keyOps->maxKey ); + +// string nf = TRANS_GOTO_TARG( state->defTrans ); + + /* For some reason the hop is faster and results in smaller code. Not sure + * why. */ + string nf = LABEL( "nf", state->id ); + + if ( anyLower && anyHigher ) { + int l1 = nl++; + string targ = TRANS_GOTO_TARG( data[mid].value ); + + /* Can go lower and higher than mid. */ + out << + " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" + " jge " << LABEL( "nl", l1 ) << "\n"; + + + emitRangeBSearch( state, low, mid-1 ); + + out << + LABEL( "nl", l1 ) << ":\n"; + + if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { + out << + " cmpb " << KEY ( data[mid].highKey ) << ", %r10b\n"; + } + + out << + " jle " << targ << "\n"; + + emitRangeBSearch( state, mid+1, high ); + } + else if ( anyLower && !anyHigher ) { + + string targ; + if ( limitHigh ) + targ = TRANS_GOTO_TARG( data[mid].value ); + else + targ = LABEL( "nl", nl++ ); + + /* Can go lower than mid but not higher. */ + + out << + " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" + " jge " << targ << "\n"; + + emitRangeBSearch( state, low, mid-1 ); + + /* If the higher is the highest in the alphabet then there is no sense + * testing it. */ + if ( !limitHigh ) { + + out << + targ << ":\n"; + + if ( ! keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { + out << + " cmpb " << KEY ( data[mid].highKey ) << ", %r10b\n"; + } + + out << + " jg " << nf << "\n"; + + TRANS_GOTO( data[mid].value ); + } + } + else if ( !anyLower && anyHigher ) { + string targ; + if ( limitLow ) + targ = TRANS_GOTO_TARG( data[mid].value ); + else + targ = LABEL( "nl", nl++ ); + + /* Can go higher than mid but not lower. */ + + out << + " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n" + " jle " << targ << "\n"; + + emitRangeBSearch( state, mid+1, high ); + + /* If the lower end is the lowest in the alphabet then there is no + * sense testing it. */ + if ( !limitLow ) { + + out << + targ << ":\n"; + + if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { + out << + " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n"; + } + + out << + " jl " << nf << "\n"; + + TRANS_GOTO( data[mid].value ); + } + } + else { + /* Cannot go higher or lower than mid. It's mid or bust. What + * tests to do depends on limits of alphabet. */ + if ( !limitLow && !limitHigh ) { + + if ( !keyOps->eq( data[mid].lowKey, data[mid].highKey ) ) { + out << + " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" + " jl " << nf << "\n" + " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n" + " jg " << nf << "\n"; + } + else { + out << + " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" + " jne " << nf << "\n"; + } + + TRANS_GOTO( data[mid].value ); + } + else if ( limitLow && !limitHigh ) { + + out << + " cmpb " << KEY( data[mid].highKey ) << ", %r10b\n" + " jg " << nf << "\n"; + + TRANS_GOTO( data[mid].value ); + } + else if ( !limitLow && limitHigh ) { + + out << + " cmpb " << KEY( data[mid].lowKey ) << ", %r10b\n" + " jl " << nf << "\n"; + + TRANS_GOTO( data[mid].value ); + } + else { + /* Both high and low are at the limit. No tests to do. */ + TRANS_GOTO( data[mid].value ); + } + } +} + +void AsmCodeGen::emitCharClassIfElseIf( RedStateAp *st ) +{ + long long span = st->high - st->low + 1; + for ( long long pos = 0; pos < span; pos++ ) { + out << + " cmpb " << KEY( st->low + pos ) << ", %r10b\n" + " je " << TRANS_GOTO_TARG( st->transList[pos] ) << "\n"; + } +} + +void AsmCodeGen::emitCharClassJumpTable( RedStateAp *st, string def ) +{ + long long low = st->low; + long long high = st->high; + + if ( def.size() == 0 ) + def = LABEL( "ccf", st->id ); + + out << + " movzbq %r10b, %rax\n" + " subq $" << low << ", %rax\n" + " cmpq $" << (high - low) << ", %rax\n" + " ja " << def << "\n" + " leaq " << LABEL( "cct", st->id ) << "(%rip), %rcx\n" + " movslq (%rcx,%rax,4), %rdx\n" + " addq %rcx, %rdx\n" + " jmp *%rdx\n" + " .section .rodata\n" + " .align 4\n" + << LABEL( "cct", st->id ) << ":\n"; + + long long span = st->high - st->low + 1; + for ( long long pos = 0; pos < span; pos++ ) { + out << " .long " << TRANS_GOTO_TARG( st->transList[pos] ) << " - " << + LABEL( "cct", st->id ) << "\n"; + } + + out << + " .text\n" + "" << LABEL( "ccf", st->id ) << ":\n"; +} + +void AsmCodeGen::NFA_PUSH( RedStateAp *st ) +{ + if ( st->nfaTargs != 0 && st->nfaTargs->length() > 0 ) { + if ( red->nfaPrePushExpr != 0 ) { + out << " movq $" << st->nfaTargs->length() << ", %rdi\n"; + INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); + } + + for ( RedNfaTargs::Iter t = *st->nfaTargs; t.lte(); t++ ) { + out << + " movq " << NFA_STACK() << ", %rax\n" + " movq " << NFA_TOP() << ", %rcx\n" + " imulq $24, %rcx\n" + " movq $" << t->state->id << ", 0(%rax,%rcx,)\n" + " movq " << P() << ", 8(%rax,%rcx,)\n"; + + out << + " # pop action id " << t->id << "\n" + " movq $" << t->id << ", 16(%rax,%rcx,)\n"; + + if ( t->push ) { + for ( GenActionTable::Iter item = t->push->key; item.lte(); item++ ) { + ACTION( out, item->value, st->id, false, + t->push->anyNextStmt() ); + out << "\n"; + } + } + + out << + " movq " << NFA_TOP() << ", %rcx\n" + " addq $1, %rcx\n" + " movq %rcx, " << NFA_TOP() << "\n"; + } + } +} + +void AsmCodeGen::STATE_GOTOS() +{ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Writing code above state gotos. */ + IN_TRANS_ACTIONS( st ); + + if ( st->labelNeeded ) + out << LABEL( "st", st->id ) << ":\n"; + + + /* need to do this if the transition is an eof transition, or if the action + * contains fexec. Otherwise, no need. */ + if ( eof ) { + out << + " cmpq " << P() << ", " << vEOF() << "\n"; + + if ( st->isFinal ) + out << " je " << LABEL( "out", st->id ) << "\n"; + else + out << " je " << LABEL( "pop", st->id ) << "\n"; + } + + if ( st->toStateAction != 0 ) { + /* Remember that we wrote an action. Write every action in the list. */ + for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) { + ACTION( out, item->value, st->id, false, + st->toStateAction->anyNextStmt() ); + out << "\n"; + } + } + + if ( st == redFsm->errState ) { + out << LABEL( "en", st->id ) << ":\n"; + + /* Break out here. */ + outLabelUsed = true; + + out << + " movq $" << st->id << ", " << vCS() << "\n" + " jmp " << LABEL( "pop" ) << "\n"; + } + else { + /* Advance and test buffer pos. */ + if ( st->labelNeeded ) { + out << + " addq $1, " << P() << "\n"; + + } + + /* This is the entry label for starting a run. */ + out << LABEL( "en", st->id ) << ":\n"; + + if ( !noEnd ) { + if ( eof ) { + out << + " cmpq " << P() << ", " << PE() << "\n" + " jne " << LABEL( "nope", st->id ) << "\n" << + " cmpq " << P() << ", " << vEOF() << "\n" + " jne " << LABEL( "out", st->id ) << "\n" << + LABEL( "nope", st->id ) << ":\n"; + } + else { + out << + " cmpq " << P() << ", " << PE() << "\n" + " je " << LABEL( "out", st->id ) << "\n"; + } + } + + NFA_PUSH( st ); + + if ( st->fromStateAction != 0 ) { + /* Remember that we wrote an action. Write every action in the list. */ + for ( GenActionTable::Iter item = st->fromStateAction->key; + item.lte(); item++ ) + { + ACTION( out, item->value, st->id, false, + st->fromStateAction->anyNextStmt() ); + out << "\n"; + } + } + + if ( !noEnd && eof ) { + out << + " cmpq " << P() << ", " << vEOF() << "\n" + " jne " << LABEL( "neofd", st->id ) << "\n"; + + if ( st->eofTrans != 0 ) + TRANS_GOTO( st->eofTrans ); + else { + if ( st->isFinal || !redFsm->anyNfaStates() ) + out << "jmp " << LABEL( "out", st->id ) << "\n"; + else + out << "jmp " << LABEL( "pop", st->id ) << "\n"; + } + + out << + " jmp " << LABEL( "deofd", st->id ) << "\n"; + + out << LABEL( "neofd", st->id ) << ":\n"; + } + + /* Record the prev state if necessary. */ + if ( st->anyRegCurStateRef() ) { + out << + " movq $" << st->id << ", -72(%rbp)\n"; + } + + +#ifdef LOG_TRANS + out << + " movzbl (" << P() << "), %r10d\n" + " movq $" << machineId << ", %rdi\n" + " movq $" << st->id << ", %rsi\n" + " movslq %r10d, %rdx\n" + " call " << LABEL( "log_trans" ) << "\n" + ; +#endif + + /* Load *p. */ + if ( st->transList != 0 ) { + long lowKey = redFsm->lowKey.getVal(); + long highKey = redFsm->highKey.getVal(); + + out << + " movzbl (" << P() << "), %r10d\n" + " cmpl $" << lowKey << ", %r10d\n" + " jl " << LABEL( "nf", st->id ) << "\n" + " cmpl $" << highKey << ", %r10d\n" + " jg " << LABEL( "nf", st->id ) << "\n" + " subl " << KEY( lowKey ) << ", %r10d\n" + " leaq " << LABEL( "char_class" ) << "(%rip), %rcx\n" + " movslq %r10d, %rax\n" + " movb (%rcx, %rax), %r10b\n" + ; + + + long len = ( st->high - st->low + 1 ); + + if ( len < 8 ) + emitCharClassIfElseIf( st ); + else { + string def; + if ( st->outRange.length() == 0 ) + def = TRANS_GOTO_TARG( st->defTrans ); + emitCharClassJumpTable( st, def ); + } + } + + /* Write the default transition. */ + out << LABEL( "nf", st->id ) << ":\n"; + TRANS_GOTO( st->defTrans ); + + if ( !noEnd && eof ) { + out << LABEL( "deofd", st->id) << ":\n"; + } + } + } +} + +unsigned int AsmCodeGen::TO_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->toStateAction != 0 ) + act = state->toStateAction->location+1; + return act; +} + +unsigned int AsmCodeGen::FROM_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->fromStateAction != 0 ) + act = state->fromStateAction->location+1; + return act; +} + +unsigned int AsmCodeGen::EOF_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->eofAction != 0 ) + act = state->eofAction->location+1; + return act; +} + +bool AsmCodeGen::useAgainLabel() +{ + return redFsm->anyActionRets() || + redFsm->anyActionByValControl() || + redFsm->anyRegNextStmt(); +} + +void AsmCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << + " jmp " << LABEL( "st", gotoDest ) << "\n"; +} + +void AsmCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + if ( red->prePushExpr != 0 ) + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + + ret << + " movq " << STACK() << ", %rax\n" + " movq " << TOP() << ", %rcx\n" + " movq $" << targState << ", (%rax, %rcx, 8)\n" + " addq $1, %rcx\n" + " movq %rcx, " << TOP() << "\n" + ; + + ret << + " jmp " << LABEL( "st", callDest ) << "\n"; + ; +} + +void AsmCodeGen::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + if ( red->prePushExpr != 0 ) + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + + ret << + "\n" + " movq "; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ", %rdx\n" + "\n" + " movq " << STACK() << ", %rax\n" + " movq " << TOP() << ", %rcx\n" + " movq $" << targState << ", (%rax, %rcx, 8)\n" + " addq $1, %rcx\n" + " movq %rcx, " << TOP() << "\n" + " movq %rdx, " << vCS() << "\n" + ; + + ret << + " jmp " << LABEL( "again" ) << "\n"; +} + +void AsmCodeGen::RET( ostream &ret, bool inFinish ) +{ + ret << + " movq " << STACK() << ", %rax\n" + " movq " << TOP() << ", %rcx\n" + " subq $1, %rcx\n" + " movq (%rax, %rcx, 8), %rax\n" + " movq %rax, " << vCS() << "\n" + " movq %rcx, " << TOP() << "\n"; + + if ( red->postPopExpr != 0 ) + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + + ret << + " jmp " << LABEL("again") << "\n"; +} + +void AsmCodeGen::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << " movq "; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ", " << vCS() << "\n"; + + ret << + " jmp " << LABEL("again") << "\n"; +} + +void AsmCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish ) +{ + ret << + " movq $" << nextDest << ", " << vCS() << "\n"; +} + +void AsmCodeGen::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << " movq "; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ", " << vCS() << "\n"; +} + +void AsmCodeGen::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + if ( red->prePushExpr != 0 ) + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + + ret << + " movq " << STACK() << ", %rax\n" + " movq " << TOP() << ", %rcx\n" + " movq $" << targState << ", (%rax, %rcx, 8)\n" + " addq $1, %rcx\n" + " movq %rcx, " << TOP() << "\n" + " movq $" << callDest << ", " << vCS() << "\n"; +} + +void AsmCodeGen::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, + int targState, bool inFinish ) +{ + if ( red->prePushExpr != 0 ) + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + + ret << + "\n" + " movq "; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ", %rdx\n" + "\n" + " movq " << STACK() << ", %rax\n" + " movq " << TOP() << ", %rcx\n" + " movq $" << targState << ", (%rax, %rcx, 8)\n" + " addq $1, %rcx\n" + " movq %rcx, " << TOP() << "\n" + " movq %rdx, " << vCS() << "\n"; +} + +void AsmCodeGen::NRET( ostream &ret, bool inFinish ) +{ + ret << + " movq " << STACK() << ", %rax\n" + " movq " << TOP() << ", %rcx\n" + " subq $1, %rcx\n" + " movq (%rax, %rcx, 8), %rax\n" + " movq %rax, " << vCS() << "\n" + " movq %rcx, " << TOP() << "\n"; + + if ( red->postPopExpr != 0 ) + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); +} + +void AsmCodeGen::CURS( ostream &ret, bool inFinish ) +{ + ret << + " movq -72(%rbp), %rax\n"; +} + +void AsmCodeGen::TARGS( ostream &ret, bool inFinish, int targState ) +{ + ret << + " movq $" << targState << ", %rax\n"; +} + +void AsmCodeGen::BREAK( ostream &ret, int targState, bool csForced ) +{ + outLabelUsed = true; + ret << "{" << P() << "++; "; + if ( !csForced ) + ret << vCS() << " = " << targState << "; "; + ret << CTRL_FLOW() << "goto _out;}"; +} + +bool AsmCodeGen::IN_TRANS_ACTIONS( RedStateAp *state ) +{ + bool anyWritten = false; + + /* Emit any transitions that have actions and that go to this state. */ + for ( int it = 0; it < state->numInCondTests; it++ ) { + /* Write the label for the transition so it can be jumped to. */ + RedTransAp *trans = state->inCondTests[it]; + out << LABEL( "ctr", trans->id ) << ":\n"; + + if ( trans->condSpace->condSet.length() == 1 ) { + RedCondPair *tp, *fp; + if ( trans->numConds() == 1 ) { + /* The single condition is either false or true, errCond is the + * opposite. */ + if ( trans->outCondKey(0) == 0 ) { + fp = trans->outCond(0); + tp = trans->errCond(); + } + else { + tp = trans->outCond(0); + fp = trans->errCond(); + } + } + else { + /* Full list, goes false, then true. */ + fp = trans->outCond(0); + tp = trans->outCond(1); + } + + GenCondSet::Iter csi = trans->condSpace->condSet; + CONDITION( out, *csi ); + + out << + " test %eax, %eax\n" + " je " << TRANS_GOTO_TARG( fp ) << "\n" + " jmp " << TRANS_GOTO_TARG( tp ) << "\n"; + } + else { + out << " movq $0, %r9\n"; + + for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { + out << + " pushq %r9\n"; + + CONDITION( out, *csi ); + out << + "\n" + " test %eax, %eax\n" + " setne %cl\n" + " movsbq %cl, %rcx\n" + " salq $" << csi.pos() << ", %rcx\n" + " popq %r9\n" + " addq %rcx, %r9\n"; + } + + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + RedCondPair *pair = trans->outCond( c ); + out << + " cmpq " << COND_KEY( key ) << ", %r9\n" + " je " << TRANS_GOTO_TARG( pair ) << "\n"; + + } + + RedCondPair *err = trans->errCond(); + if ( err != 0 ) { + out << + " jmp " << TRANS_GOTO_TARG( err ) << "\n"; + } + } + } + + /* Emit any transitions that have actions and that go to this state. */ + for ( int it = 0; it < state->numInConds; it++ ) { + RedCondPair *pair = state->inConds[it]; + if ( pair->action != 0 /* && pair->labelNeeded */ ) { + /* Remember that we wrote an action so we know to write the + * line directive for going back to the output. */ + anyWritten = true; + + /* Write the label for the transition so it can be jumped to. */ + out << LABEL( "tr", pair->id ) << ":\n"; + + /* If the action contains a next, then we must preload the current + * state since the action may or may not set it. */ + if ( pair->action->anyNextStmt() ) { + out << + " movq $" << pair->targ->id << ", " << vCS() << "\n"; + } + + if ( redFsm->anyRegNbreak() ) { + out << + " movb $0, " << NBREAK() << "\n"; + } + + /* Write each action in the list. */ + for ( GenActionTable::Iter item = pair->action->key; item.lte(); item++ ) { + ACTION( out, item->value, pair->targ->id, false, + pair->action->anyNextStmt() ); + out << "\n"; + } + + if ( redFsm->anyRegNbreak() ) { + out << + " cmpb $0, " << NBREAK() << "\n" + " jne " << LABEL( "pop" ) << "\n"; + outLabelUsed = true; + } + + + /* If the action contains a next then we need to reload, otherwise + * jump directly to the target state. */ + if ( pair->action->anyNextStmt() ) + out << " jmp " << LABEL( "again" ) << "\n"; + else + out << " jmp " << LABEL( "st", pair->targ->id ) << "\n"; + } + } + + return anyWritten; +} + +std::string AsmCodeGen::TRANS_GOTO_TARG( RedCondPair *pair ) +{ + std::stringstream s; + if ( pair->action != 0 ) { + /* Go to the transition which will go to the state. */ + s << LABEL( "tr", pair->id ); + } + else { + /* Go directly to the target state. */ + s << LABEL( "st", pair->targ->id ); + } + return s.str(); +} + +std::string AsmCodeGen::TRANS_GOTO_TARG( RedTransAp *trans ) +{ + if ( trans->condSpace != 0 ) { + /* Need to jump to the trans since there are conditions. */ + return LABEL( "ctr", trans->id ); + } + else { + return TRANS_GOTO_TARG( &trans->p ); + } +} + +/* Emit the goto to take for a given transition. */ +std::ostream &AsmCodeGen::TRANS_GOTO( RedTransAp *trans ) +{ + out << " jmp " << TRANS_GOTO_TARG( trans ) << "\n"; + return out; +} + +std::ostream &AsmCodeGen::EXIT_STATES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + out << + LABEL( "out", st->id ) << ":\n" + " movq $" << st->id << ", " << vCS() << "\n" + " jmp " << LABEL( "out" ) << "\n"; + + out << + LABEL( "pop", st->id ) << ":\n" + " movq $" << st->id << ", " << vCS() << "\n" + " jmp " << LABEL( "pop" ) << "\n"; + } + return out; +} + +std::ostream &AsmCodeGen::AGAIN_CASES() +{ + /* Jump into the machine based on the current state. */ + out << + " leaq " << LABEL( "again_jmp" ) << "(%rip), %rcx\n"; + + if ( stackCS ) { + out << + " movq " << vCS() << ", %r11\n"; + } + + out << + " movq (%rcx,%r11,8), %rcx\n" + " jmp *%rcx\n" + " .section .rodata\n" + " .align 8\n" + << LABEL( "again_jmp" ) << ":\n"; + + for ( int stId = 0; stId < redFsm->stateList.length(); stId++ ) { + out << + " .quad " << LABEL( "st", stId ) << "\n"; + } + + out << + " .text\n"; + + return out; +} + +std::ostream &AsmCodeGen::ENTRY_CASES() +{ + out << + " movq (%rcx,%r11,8), %rcx\n" + " jmp *%rcx\n" + " .section .rodata\n" + " .align 8\n" + << LABEL( "entry_jmp" ) << ":\n"; + + for ( int stId = 0; stId < redFsm->stateList.length(); stId++ ) { + out << + " .quad " << LABEL( "en", stId ) << "\n"; + } + + out << + " .text\n"; + return out; +} + + +std::ostream &AsmCodeGen::FINISH_CASES() +{ + /* The current state is in %rax. */ + /*long done = */ nextLmSwitchLabel++; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + out << + " cmpq $" << st->id << ", %rax\n" + " jne " << LABEL( "fc", st->id ) << "\n"; + + if ( st->fromStateAction != 0 ) { + /* Remember that we wrote an action. Write every action in the list. */ + for ( GenActionTable::Iter item = st->fromStateAction->key; + item.lte(); item++ ) + { + ACTION( out, item->value, st->id, false, + st->fromStateAction->anyNextStmt() ); + out << "\n"; + } + } + + out << + " jmp " << TRANS_GOTO_TARG( st->eofTrans ) << "\n" << + LABEL( "fc", st->id ) << ":\n"; + } + } + + return out; +} + +void AsmCodeGen::setLabelsNeeded( GenInlineList *inlineList ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case GenInlineItem::Goto: case GenInlineItem::Call: { + /* Mark the target as needing a label. */ + item->targState->labelNeeded = true; + break; + } + default: break; + } + + if ( item->children != 0 ) + setLabelsNeeded( item->children ); + } +} + +void AsmCodeGen::setLabelsNeeded( RedCondPair *pair ) +{ + /* If there is no action with a next statement, then the label will be + * needed. */ + if ( pair->action == 0 || !pair->action->anyNextStmt() ) + pair->targ->labelNeeded = true; + + /* Need labels for states that have goto or calls in action code + * invoked on characters (ie, not from out action code). */ + if ( pair->action != 0 ) { + /* Loop the actions. */ + for ( GenActionTable::Iter act = pair->action->key; act.lte(); act++ ) { + /* Get the action and walk it's tree. */ + setLabelsNeeded( act->value->inlineList ); + } + } +} + +/* Set up labelNeeded flag for each state. */ +void AsmCodeGen::setLabelsNeeded() +{ + /* If we use the _again label, then we the _again switch, which uses all + * labels. */ + if ( useAgainLabel() ) { + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->labelNeeded = true; + } + else { + /* Do not use all labels by default, init all labelNeeded vars to false. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->labelNeeded = false; + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + if ( trans->condSpace == 0 ) + setLabelsNeeded( &trans->p ); + } + + for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) + setLabelsNeeded( &cond->p ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofAction != 0 ) { + for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) + setLabelsNeeded( item->value->inlineList ); + } + } + } + + if ( !noEnd ) { + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->outNeeded = st->labelNeeded; + } +} + +void AsmCodeGen::writeData() +{ + STATE_IDS(); + + long long maxSpan = keyOps->span( redFsm->lowKey, redFsm->highKey ); + + out << + " .type " << LABEL( "char_class" ) << ", @object\n" << + LABEL( "char_class" ) << ":\n"; + + for ( long long pos = 0; pos < maxSpan; pos++ ) { + out << + " .byte " << redFsm->classMap[pos] << "\n"; + } + +#ifdef LOG_TRANS + out << + LABEL( "fmt_log_trans" ) << ":\n" + " .string \"%i %i %i\\n\"\n"; +#endif +} + +void AsmCodeGen::setNfaIds() +{ + long nextId = 1; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) { + targ->id = nextId; + nextId += 1; + } + } + } +} + +void AsmCodeGen::writeExec() +{ + /* Must set labels immediately before writing because we may depend on the + * noend write option. */ + setLabelsNeeded(); + testEofUsed = false; + outLabelUsed = false; + + setNfaIds(); + + /* If there are eof actions then we need to run code after exporting the + * final state to vCS. Since the interface register is calee-save, we need + * it to live on the stack. */ + stackCS = redFsm->anyEofActivity(); + + /* + * This code needs 88 bytes of stack (offset 0 from %rbp). + * + * cv : %r9 -- caller-save, used internally, condition char, undefined in + * conditions and actions, can use + * + * pc : %r10b -- caller-save, used internally, undefined in conditions + * actions, can use + * + * cs : %r11 -- caller-save, written by write init, read and + * written by exec, undefined in conditions and actions + * + * p : %r12 -- callee-save, interface, persistent + * + * pe : %r13 -- callee-save, interface, persistent + * + * eof: -8(%rbp) + * + * ts: -16(%rbp) + * + * te: -24(%rbp) + * + * act: -32(%rbp) + * + * _nbreak: -40(%rbp) + * + * stackCS: -48(%rbp) + * + * stack: -56(%rbp) + * top: -64(%rbp) + * + * _ps: -72(%rbp) + * + * nfa_stack -80(%rbp) + * nfa_top -88(%rbp) + * nfa_sz -96(%rbp) + */ + + if ( redFsm->anyRegCurStateRef() ) { + out << + " movq $0, -72(%rbp)\n"; + } + + if ( stackCS ) { + /* Only need a persistent cs in the case of eof actions when exiting the + * block. Where CS lives is a matter of performance though, so we should + * only do this if necessary. */ + out << + " movq %r11, " << vCS() << "\n"; + } + + if ( useAgainLabel() ) { + out << + " jmp " << LABEL( "resume" ) << "\n" + << LABEL( "again" ) << ":\n"; + + AGAIN_CASES(); + } + + if ( useAgainLabel() || redFsm->anyNfaStates() ) + out << LABEL( "resume" ) << ":\n"; + + /* Jump into the machine based on the current state. */ + out << + " leaq " << LABEL( "entry_jmp" ) << "(%rip), %rcx\n"; + + if ( stackCS ) { + out << + " movq " << vCS() << ", %r11\n"; + } + + ENTRY_CASES(); + + STATE_GOTOS(); + + EXIT_STATES(); + + out << LABEL( "pop" ) << ":\n"; + + if ( redFsm->anyNfaStates() ) { + out << + " movq " << NFA_TOP() << ", %rcx\n" + " cmpq $0, %rcx\n" + " je " << LABEL( "nfa_stack_empty" ) << "\n" + " movq " << NFA_TOP() << ", %rcx\n" + " subq $1, %rcx\n" + " movq %rcx, " << NFA_TOP() << "\n" + " movq " << NFA_STACK() << ", %rax\n" + " imulq $24, %rcx\n" + " movq 0(%rax,%rcx,), %r11\n" + " movq 8(%rax,%rcx,), " << P() << "\n" + " movq %r11, " << vCS() << "\n" + ; + + if ( redFsm->bAnyNfaPops ) { + out << + " movq %r11, %r14\n" + " movq 16(%rax,%rcx,), %rax\n"; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) { + + /* Write the entry label. */ + out << + " # pop action select\n" + " cmp $" << targ->id << ", %rax\n" + " jne 100f\n"; + + if ( targ->popTest != 0 ) { + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = targ->popTest->key; item.lte(); item++ ) + NFA_CONDITION( out, item->value, item.last() ); + } + + out << + " jmp 101f\n" + "100:\n"; + + } + } + } + + out << + "101:\n" + " movq %r14, %r11\n"; + } + + out << + " jmp " << LABEL( "resume" ) << "\n" << + LABEL( "pop_fail" ) << ":\n" + " movq $" << ERROR_STATE() << ", " << vCS() << "\n" + " jmp " << LABEL( "resume" ) << "\n" << + LABEL( "nfa_stack_empty" ) << ":\n"; + } + + if ( stackCS ) { + out << + " movq " << vCS() << ", %r11\n"; + } + + out << + "# WRITE EXEC END\n"; + + out << LABEL( "out" ) << ":\n"; + + if ( stackCS ) { + out << + " movq " << vCS() << ", %r11\n"; + } + +#ifdef LOG_TRANS + out << + " jmp " << LABEL( "skip" ) << "\n" << + LABEL( "log_trans" ) << ":\n" + " movq %rdx, %rcx\n" + " movq %rsi, %rdx\n" + " movq %rdi, %rsi\n" + " movq " << LABEL( "fmt_log_trans" ) << "@GOTPCREL(%rip), %rdi\n" + " movq $0, %rax\n" + " call printf@PLT\n" + " ret\n" << + LABEL( "skip" ) << ":\n" + "\n"; +#endif +} diff --git a/src/libfsm/asm.h b/src/libfsm/asm.h new file mode 100644 index 00000000..2506e582 --- /dev/null +++ b/src/libfsm/asm.h @@ -0,0 +1,248 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _ASM_H +#define _ASM_H + +#include +#include +#include +#include + +#include "common.h" +#include "gendata.h" +#include "ragel.h" + +using std::string; +using std::ostream; + +/* Integer array line length. */ +#define IALL_INTEGRAL 8 +#define IALL_STRING 128 + +/* Forwards. */ +struct RedFsmAp; +struct RedStateAp; +struct CodeGenData; +struct GenAction; +struct NameInst; +struct GenInlineItem; +struct GenInlineList; +struct RedAction; +struct FsmLongestMatch; +struct FsmLongestMatchPart; +class AsmCodeGen; +struct RedTransAp; +struct RedStateAp; +struct GenStateCond; + +string itoa( int i ); + +/* + * class AsmCodeGen + */ +class AsmCodeGen : public CodeGenData +{ +public: + AsmCodeGen( const CodeGenArgs &args ); + virtual ~AsmCodeGen() {} + + virtual void writeInit(); + virtual void writeStart(); + virtual void writeFirstFinal(); + virtual void writeError(); + + virtual void statsSummary() {} + virtual void genAnalysis(); + +protected: + string FSM_NAME(); + string START_STATE_ID(); + string KEY( Key key ); + string COND_KEY( CondKey key ); + string LDIR_PATH( char *path ); + virtual void ACTION( ostream &ret, GenAction *action, int targState, + bool inFinish, bool csForced ); + void CONDITION( ostream &ret, GenAction *condition ); + void NFA_CONDITION( ostream &ret, GenAction *condition, bool last ); + string ALPH_TYPE(); + + bool isAlphTypeSigned(); + + string GET_KEY(); + + string P(); + string PE(); + string vEOF(); + string NBREAK(); + + string ACCESS(); + string vCS(); + string STACK(); + string TOP(); + string TOKSTART(); + string TOKEND(); + string ACT(); + + string NFA_STACK(); + string NFA_TOP(); + string NFA_SZ(); + + string DATA_PREFIX(); + string PM() { return "_" + DATA_PREFIX() + "partition_map"; } + string C() { return "_" + DATA_PREFIX() + "cond_spaces"; } + string CK() { return "_" + DATA_PREFIX() + "cond_keys"; } + string K() { return "_" + DATA_PREFIX() + "trans_keys"; } + string I() { return "_" + DATA_PREFIX() + "indices"; } + string CO() { return "_" + DATA_PREFIX() + "cond_offsets"; } + string KO() { return "_" + DATA_PREFIX() + "key_offsets"; } + string IO() { return "_" + DATA_PREFIX() + "index_offsets"; } + string CL() { return "_" + DATA_PREFIX() + "cond_lengths"; } + string SL() { return "_" + DATA_PREFIX() + "single_lengths"; } + string RL() { return "_" + DATA_PREFIX() + "range_lengths"; } + string A() { return "_" + DATA_PREFIX() + "actions"; } + string TA() { return "_" + DATA_PREFIX() + "trans_actions"; } + string TT() { return "_" + DATA_PREFIX() + "trans_targs"; } + string TSA() { return "_" + DATA_PREFIX() + "to_state_actions"; } + string FSA() { return "_" + DATA_PREFIX() + "from_state_actions"; } + string EA() { return "_" + DATA_PREFIX() + "eof_actions"; } + string ET() { return "_" + DATA_PREFIX() + "eof_trans"; } + string SP() { return "_" + DATA_PREFIX() + "key_spans"; } + string CSP() { return "_" + DATA_PREFIX() + "cond_key_spans"; } + string START() { return DATA_PREFIX() + "start"; } + string ERROR() { return DATA_PREFIX() + "error"; } + string FIRST_FINAL() { return DATA_PREFIX() + "first_final"; } + string CTXDATA() { return DATA_PREFIX() + "ctxdata"; } + + string LABEL( const char *type, long i ); + string LABEL( const char *name ); + + void INLINE_LIST( ostream &ret, GenInlineList *inlineList, + int targState, bool inFinish, bool csForced ); + void EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); + void LM_SWITCH( ostream &ret, GenInlineItem *item, int targState, + int inFinish, bool csForced ); + void SET_ACT( ostream &ret, GenInlineItem *item ); + void INIT_TOKSTART( ostream &ret, GenInlineItem *item ); + void INIT_ACT( ostream &ret, GenInlineItem *item ); + void SET_TOKSTART( ostream &ret, GenInlineItem *item ); + void SET_TOKEND( ostream &ret, GenInlineItem *item ); + void GET_TOKEND( ostream &ret, GenInlineItem *item ); + void STATIC_CONST_INT( const string &name, const string &val ); + void STATE_IDS(); + + string ERROR_STATE(); + string FIRST_FINAL_STATE(); + + bool outLabelUsed; + bool testEofUsed; + bool againLabelUsed; + long nextLmSwitchLabel; + bool stackCS; + + void NBREAK( ostream &ret, int targState, bool csForced ); + void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void NRET( ostream &ret, bool inFinish ); + + void HOST_STMT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ); + void HOST_EXPR( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ); + void HOST_TEXT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ); + void GEN_STMT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ); + void GEN_EXPR( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ); + +public: + + virtual string NULL_ITEM(); + virtual string POINTER(); + virtual ostream &SWITCH_DEFAULT(); + virtual ostream &OPEN_ARRAY( string type, string name ); + virtual ostream &CLOSE_ARRAY(); + virtual ostream &STATIC_VAR( string type, string name ); + virtual string ARR_OFF( string ptr, string offset ); + virtual string CAST( string type ); + virtual string UINT(); + virtual string PTR_CONST(); + virtual string PTR_CONST_END(); + virtual string CTRL_FLOW(); + + virtual void writeExports(); + + unsigned int TO_STATE_ACTION( RedStateAp *state ); + unsigned int FROM_STATE_ACTION( RedStateAp *state ); + unsigned int EOF_ACTION( RedStateAp *state ); + + void COND_TRANSLATE( GenStateCond *stateCond ); + void STATE_CONDS( RedStateAp *state, bool genDefault ); + + std::ostream &EXIT_STATES(); + std::string TRANS_GOTO_TARG( RedTransAp *trans ); + std::string TRANS_GOTO_TARG( RedCondPair *pair ); + std::ostream &TRANS_GOTO( RedTransAp *trans ); + std::ostream &AGAIN_CASES(); + std::ostream &FINISH_CASES(); + std::ostream &ENTRY_CASES(); + + void GOTO( ostream &ret, int gotoDest, bool inFinish ); + void CALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NEXT( ostream &ret, int nextDest, bool inFinish ); + void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void RET( ostream &ret, bool inFinish ); + void CURS( ostream &ret, bool inFinish ); + void TARGS( ostream &ret, bool inFinish, int targState ); + void BREAK( ostream &ret, int targState, bool csForced ); + void LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); + + virtual void writeData(); + virtual void writeExec(); + + bool useAgainLabel(); + + void NFA_PUSH( RedStateAp *state ); + bool IN_TRANS_ACTIONS( RedStateAp *state ); + void STATE_GOTOS(); + + void emitSingleIfElseIf( RedStateAp *state ); + void emitSingleJumpTable( RedStateAp *state, std::string def ); + void emitRangeBSearch( RedStateAp *state, int low, int high ); + void emitCharClassIfElseIf( RedStateAp *state ); + void emitCharClassJumpTable( RedStateAp *state, std::string def ); + + /* Set up labelNeeded flag for each state. */ + void setLabelsNeeded( RedCondPair *pair ); + void setLabelsNeeded( GenInlineList *inlineList ); + void setLabelsNeeded(); + + void setNfaIds(); + + void genOutputLineDirective( ostream &out ) {} + void genLineDirective( ostream &out, int line, const char *file ) {} +}; + +#endif diff --git a/src/libfsm/binary.cc b/src/libfsm/binary.cc new file mode 100644 index 00000000..39b58a47 --- /dev/null +++ b/src/libfsm/binary.cc @@ -0,0 +1,819 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "binary.h" +#include "redfsm.h" +#include "gendata.h" + +#include + +void Binary::genAnalysis() +{ + redFsm->sortByStateId(); + + /* Choose default transitions and the single transition. */ + redFsm->chooseDefaultSpan(); + + /* Choose the singles. */ + redFsm->moveSelectTransToSingle(); + + if ( redFsm->errState != 0 ) + redFsm->getErrorCond(); + + /* If any errors have occured in the input file then don't write anything. */ + if ( red->id->errorCount > 0 ) + return; + + /* Anlayze Machine will find the final action reference counts, among other + * things. We will use these in reporting the usage of fsm directives in + * action code. */ + red->analyzeMachine(); + + setKeyType(); + + /* Run the analysis pass over the table data. */ + setTableState( TableArray::AnalyzePass ); + tableDataPass(); + + /* Switch the tables over to the code gen mode. */ + setTableState( TableArray::GeneratePass ); +} + + +void Binary::tableDataPass() +{ + if ( type == Loop ) + taActions(); + + taKeyOffsets(); + taSingleLens(); + taRangeLens(); + taIndexOffsets(); + taIndices(); + + taTransCondSpacesWi(); + taTransOffsetsWi(); + taTransLengthsWi(); + + taTransCondSpaces(); + taTransOffsets(); + taTransLengths(); + + taCondTargs(); + taCondActions(); + + taToStateActions(); + taFromStateActions(); + taEofActions(); + taEofConds(); + taEofTrans(); + + taKeys(); + taCondKeys(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void Binary::writeData() +{ + if ( type == Loop ) { + /* If there are any transtion functions then output the array. If there + * are none, don't bother emitting an empty array that won't be used. */ + if ( redFsm->anyActions() ) + taActions(); + } + + taKeyOffsets(); + taKeys(); + taSingleLens(); + taRangeLens(); + taIndexOffsets(); + + taTransCondSpaces(); + taTransOffsets(); + taTransLengths(); + + taCondKeys(); + taCondTargs(); + taCondActions(); + + if ( redFsm->anyToStateActions() ) + taToStateActions(); + + if ( redFsm->anyFromStateActions() ) + taFromStateActions(); + + if ( redFsm->anyEofActions() ) + taEofActions(); + + taEofConds(); + + if ( redFsm->anyEofTrans() ) + taEofTrans(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); + + STATE_IDS(); +} + + +void Binary::setKeyType() +{ + transKeys.setType( ALPH_TYPE(), alphType->size, alphType->isChar ); + transKeys.isSigned = keyOps->isSigned; +} + +void Binary::setTableState( TableArray::State state ) +{ + for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { + TableArray *tableArray = *i; + tableArray->setState( state ); + } +} + +void Binary::taKeyOffsets() +{ + keyOffsets.start(); + + int curKeyOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + keyOffsets.value( curKeyOffset ); + curKeyOffset += st->outSingle.length() + st->outRange.length()*2; + } + + keyOffsets.finish(); +} + + +void Binary::taSingleLens() +{ + singleLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + singleLens.value( st->outSingle.length() ); + + singleLens.finish(); +} + + +void Binary::taRangeLens() +{ + rangeLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + rangeLens.value( st->outRange.length() ); + + rangeLens.finish(); +} + +void Binary::taIndexOffsets() +{ + indexOffsets.start(); + + int curIndOffset = 0; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Write the index offset. */ + indexOffsets.value( curIndOffset ); + + /* Move the index offset ahead. */ + curIndOffset += st->outSingle.length() + st->outRange.length(); + if ( st->defTrans != 0 ) + curIndOffset += 1; + } + + indexOffsets.finish(); +} + +void Binary::taToStateActions() +{ + toStateActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + TO_STATE_ACTION(st); + + toStateActions.finish(); +} + +void Binary::taFromStateActions() +{ + fromStateActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + FROM_STATE_ACTION(st); + + fromStateActions.finish(); +} + +void Binary::taEofActions() +{ + eofActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + EOF_ACTION( st ); + + eofActions.finish(); +} + +void Binary::taEofConds() +{ + /* + * EOF Cond Spaces + */ + eofCondSpaces.start(); + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->outCondSpace != 0 ) + eofCondSpaces.value( st->outCondSpace->condSpaceId ); + else + eofCondSpaces.value( -1 ); + } + eofCondSpaces.finish(); + + /* + * EOF Cond Key Indixes + */ + eofCondKeyOffs.start(); + + int curOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long off = 0; + if ( st->outCondSpace != 0 ) { + off = curOffset; + curOffset += st->outCondKeys.length(); + } + eofCondKeyOffs.value( off ); + } + + eofCondKeyOffs.finish(); + + /* + * EOF Cond Key Lengths. + */ + eofCondKeyLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long len = 0; + if ( st->outCondSpace != 0 ) + len = st->outCondKeys.length(); + eofCondKeyLens.value( len ); + } + + eofCondKeyLens.finish(); + + /* + * EOF Cond Keys + */ + eofCondKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->outCondSpace != 0 ) { + for ( int c = 0; c < st->outCondKeys.length(); c++ ) { + CondKey key = st->outCondKeys[c]; + eofCondKeys.value( key.getVal() ); + } + } + } + + eofCondKeys.finish(); +} + +void Binary::taEofTrans() +{ + eofTrans.start(); + + /* Need to compute transition positions. */ + int totalTrans = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + totalTrans += st->outSingle.length(); + totalTrans += st->outRange.length(); + if ( st->defTrans != 0 ) + totalTrans += 1; + } + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long trans = 0; + if ( st->eofTrans != 0 ) { + trans = totalTrans + 1; + totalTrans += 1; + } + + eofTrans.value( trans ); + } + + eofTrans.finish(); +} + +void Binary::taKeys() +{ + transKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Loop the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + transKeys.value( stel->lowKey.getVal() ); + } + + /* Loop the state's transitions. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + /* Lower key. */ + transKeys.value( rtel->lowKey.getVal() ); + + /* Upper key. */ + transKeys.value( rtel->highKey.getVal() ); + } + } + + transKeys.finish(); +} + +void Binary::taIndices() +{ + indices.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) + indices.value( stel->value->id ); + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) + indices.value( rtel->value->id ); + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) + indices.value( st->defTrans->id ); + } + + indices.finish(); +} + +void Binary::taTransCondSpaces() +{ + transCondSpaces.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + } + + transCondSpaces.finish(); +} + +void Binary::taTransOffsets() +{ + transOffsets.start(); + + int curOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + } + + errCondOffset = curOffset; + + transOffsets.finish(); +} + +void Binary::taTransLengths() +{ + transLengths.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + transLengths.value( trans->numConds() ); + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + transLengths.value( trans->numConds() ); + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + transLengths.value( trans->numConds() ); + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + transLengths.value( trans->numConds() ); + } + } + + transLengths.finish(); +} + +void Binary::taTransCondSpacesWi() +{ + transCondSpacesWi.start(); + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + /* Cond Space id. */ + if ( trans->condSpace != 0 ) + transCondSpacesWi.value( trans->condSpace->condSpaceId ); + else + transCondSpacesWi.value( -1 ); + } + + transCondSpacesWi.finish(); +} + +void Binary::taTransOffsetsWi() +{ + transOffsetsWi.start(); + + int curOffset = 0; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + transOffsetsWi.value( curOffset ); + + TransApSet::Iter next = trans; + next.increment(); + + curOffset += trans->numConds(); + } + + transOffsetsWi.finish(); +} + +void Binary::taTransLengthsWi() +{ + transLengthsWi.start(); + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + transLengthsWi.value( trans->numConds() ); + + TransApSet::Iter next = trans; + next.increment(); + } + + transLengthsWi.finish(); +} + +void Binary::taCondKeys() +{ + condKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + } + + condKeys.finish(); +} + +void Binary::taCondTargs() +{ + condTargs.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + } + + if ( redFsm->errCond != 0 ) { + RedCondPair *cond = &redFsm->errCond->p; + condTargs.value( cond->targ->id ); + } + + condTargs.finish(); +} + +void Binary::taCondActions() +{ + condActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + COND_ACTION( cond ); + } + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + COND_ACTION( cond ); + } + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + COND_ACTION( cond ); + } + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + COND_ACTION( cond ); + } + } + } + + if ( redFsm->errCond != 0 ) { + RedCondPair *cond = &redFsm->errCond->p; + COND_ACTION( cond ); + } + + condActions.finish(); +} + +void Binary::taNfaTargs() +{ + nfaTargs.start(); + + /* Offset of zero means no NFA targs, put a filler there. */ + nfaTargs.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaTargs.value( st->nfaTargs->length() ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + nfaTargs.value( targ->state->id ); + } + } + + nfaTargs.finish(); +} + +/* These need to mirror nfa targs. */ +void Binary::taNfaPushActions() +{ + nfaPushActions.start(); + + nfaPushActions.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaPushActions.value( 0 ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_PUSH_ACTION( targ ); + } + } + + nfaPushActions.finish(); +} + +void Binary::taNfaPopTrans() +{ + nfaPopTrans.start(); + + nfaPopTrans.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + + nfaPopTrans.value( 0 ); + + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_POP_TEST( targ ); + } + } + + nfaPopTrans.finish(); +} + +void Binary::taNfaOffsets() +{ + nfaOffsets.start(); + + /* Offset of zero means no NFA targs, real targs start at 1. */ + long offset = 1; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs == 0 ) { + nfaOffsets.value( 0 ); + } + else { + nfaOffsets.value( offset ); + offset += 1 + st->nfaTargs->length(); + } + } + + nfaOffsets.finish(); +} + + +/* Write out the array of actions. */ +std::ostream &Binary::ACTIONS_ARRAY() +{ + out << "\t0, "; + int totalActions = 1; + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Write out the length, which will never be the last character. */ + out << act->key.length() << ", "; + /* Put in a line break every 8 */ + if ( totalActions++ % 8 == 7 ) + out << "\n\t"; + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) { + out << item->value->actionId; + if ( ! (act.last() && item.last()) ) + out << ", "; + + /* Put in a line break every 8 */ + if ( totalActions++ % 8 == 7 ) + out << "\n\t"; + } + } + out << "\n"; + return out; +} + +void Binary::taActions() +{ + actions.start(); + + /* Put "no-action" at the beginning. */ + actions.value( 0 ); + + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Write out the length, which will never be the last character. */ + actions.value( act->key.length() ); + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) + actions.value( item->value->actionId ); + } + + actions.finish(); +} + + + + diff --git a/src/libfsm/binary.h b/src/libfsm/binary.h new file mode 100644 index 00000000..d947483d --- /dev/null +++ b/src/libfsm/binary.h @@ -0,0 +1,98 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _C_BINARY_H +#define _C_BINARY_H + +#include +#include "codegen.h" +#include "tables.h" + +/* Forwards. */ +struct CodeGenData; +struct NameInst; +struct RedTransAp; +struct RedStateAp; + +class Binary + : public virtual Tables +{ +protected: + enum Type { + Loop = 1, Exp + }; + +public: + Binary( const CodeGenArgs &args, Type type ) + : + Tables( args ), + type(type) + {} + +protected: + Type type; + + std::ostream &COND_KEYS_v1(); + std::ostream &COND_SPACES_v1(); + std::ostream &INDICES(); + std::ostream &INDEX_OFFSETS(); + std::ostream &SINGLE_LENS(); + std::ostream &RANGE_LENS(); + std::ostream &TRANS_TARGS_WI(); + std::ostream &ACTIONS_ARRAY(); + + void taKeyOffsets(); + void taSingleLens(); + void taRangeLens(); + void taIndexOffsets(); + void taIndices(); + void taTransCondSpacesWi(); + void taTransOffsetsWi(); + void taTransLengthsWi(); + void taTransCondSpaces(); + void taTransOffsets(); + void taTransLengths(); + void taCondTargs(); + void taCondActions(); + void taToStateActions(); + void taFromStateActions(); + void taEofTrans(); + void taEofConds(); + void taEofActions(); + void taKeys(); + void taActions(); + void taCondKeys(); + void taNfaTargs(); + void taNfaOffsets(); + void taNfaPushActions(); + void taNfaPopTrans(); + + void setKeyType(); + + void setTableState( TableArray::State ); + + virtual void writeData(); + virtual void tableDataPass(); + virtual void genAnalysis(); +}; + +#endif diff --git a/src/libfsm/binbreak.cc b/src/libfsm/binbreak.cc new file mode 100644 index 00000000..18b71542 --- /dev/null +++ b/src/libfsm/binbreak.cc @@ -0,0 +1,132 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "binbreak.h" + +void BinBreak::LOCATE_TRANS() +{ + out << + " " << keys << " = " << OFFSET( ARR_REF( transKeys ), ARR_REF( keyOffsets ) + "[" + vCS() + "]" ) << ";\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexOffsets ) << "[" << vCS() << "];\n" + "\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( singleLens ) << "[" << vCS() << "];\n" + " " << have << " = 0;\n" + " if ( " << klen << " > 0 ) {\n" + " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" + " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + " << klen << " - 1;\n" + " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << keys << " += " << klen << ";\n" + " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << have << " = 1;\n" + " " << trans << " += " << CAST( UINT() ) << "(_mid - " << keys << ");\n" + " break;\n" + " }\n" + " }\n" + " }\n" + "\n" + " " << klen << " = " << CAST("int") << ARR_REF( rangeLens ) << "[" << vCS() << "];\n" + " if ( " << have << " == 0 && " << klen << " > 0 ) {\n" + " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" + " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + (" << klen << "<<1) - 2;\n" + " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n" + " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _upper = _mid - 2;\n" + " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid + 1" ) << " )\n" + " _lower = _mid + 2;\n" + " else {\n" + " " << trans << " += " << CAST( UINT() ) << "((_mid - " << keys << ")>>1);\n" + " break;\n" + " }\n" + " }\n" + " }\n" + "\n"; +} + +void BinBreak::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + std::stringstream success, error; + + out << + " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + if ( red->condSpaceList.length() > 0 ) + COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); + + success << + cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; + + error << + cond << " = " << errCondOffset << ";\n"; + + out << + " {\n" + " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" + " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" + " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << error.str() << "\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << success.str() << "\n" + " break;\n" + " }\n" + " }\n" + " }\n" + ; + } + + out << EMIT_LABEL( _match_cond ); +} + diff --git a/src/libfsm/binbreak.h b/src/libfsm/binbreak.h new file mode 100644 index 00000000..1b48ab24 --- /dev/null +++ b/src/libfsm/binbreak.h @@ -0,0 +1,71 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_BINBREAK_H +#define RAGEL_BINBREAK_H + +#include "binary.h" +#include "actloop.h" +#include "actexp.h" + +struct BinBreak +: + public Binary, public TabBreak +{ + BinBreak( const CodeGenArgs &args, Binary::Type type ) + : + Tables( args ), + Binary( args, type ), + TabBreak( args ) + {} + + void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class BinBreakLoop + : public BinBreak, public ActLoop +{ +public: + BinBreakLoop( const CodeGenArgs &args ) + : + Tables( args ), + BinBreak( args, Loop ), + ActLoop( args ) + {} +}; + + +class BinBreakExp + : public BinBreak, public ActExp +{ +public: + BinBreakExp( const CodeGenArgs &args ) + : + Tables( args ), + BinBreak( args, Exp ), + ActExp( args ) + {} +}; + + +#endif diff --git a/src/libfsm/bingoto.cc b/src/libfsm/bingoto.cc new file mode 100644 index 00000000..1f4a818d --- /dev/null +++ b/src/libfsm/bingoto.cc @@ -0,0 +1,131 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "bingoto.h" + +void BinGoto::LOCATE_TRANS() +{ + out << + " " << keys << " = " << OFFSET( ARR_REF( transKeys ), ARR_REF( keyOffsets ) + "[" + vCS() + "]" ) << ";\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexOffsets ) << "[" << vCS() << "];\n" + "\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( singleLens ) << "[" << vCS() << "];\n" + " if ( " << klen << " > 0 ) {\n" + " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" + " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + " << klen << " - 1;\n" + " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << keys << " += " << klen << ";\n" + " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << trans << " += " << CAST( UINT() ) << "(_mid - " << keys << ");\n" + " goto " << _match << ";\n" + " }\n" + " }\n" + " }\n" + "\n" + " " << klen << " = " << CAST("int") << ARR_REF( rangeLens ) << "[" << vCS() << "];\n" + " if ( " << klen << " > 0 ) {\n" + " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" + " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + (" << klen << "<<1) - 2;\n" + " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n" + " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _upper = _mid - 2;\n" + " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid + 1" ) << " )\n" + " _lower = _mid + 2;\n" + " else {\n" + " " << trans << " += " << CAST( UINT() ) << "((_mid - " << keys << ")>>1);\n" + " break;\n" + " }\n" + " }\n" + " }\n" + "\n"; + + out << EMIT_LABEL( _match ); +} + + +void BinGoto::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + std::stringstream success, error; + + out << + " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + if ( red->condSpaceList.length() > 0 ) + COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); + + success << + cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; + + error << + cond << " = " << errCondOffset << ";\n"; + + out << + " {\n" + " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" + " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" + " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << error.str() << "\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << success.str() << "\n" + " break;\n" + " }\n" + " }\n" + " }\n" + ; + } +} + diff --git a/src/libfsm/bingoto.h b/src/libfsm/bingoto.h new file mode 100644 index 00000000..18fa8397 --- /dev/null +++ b/src/libfsm/bingoto.h @@ -0,0 +1,71 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_BINGOTO_H +#define RAGEL_BINGOTO_H + +#include "binary.h" +#include "actloop.h" +#include "actexp.h" + +struct BinGoto +: + public Binary, public TabGoto +{ + BinGoto( const CodeGenArgs &args, Binary::Type type ) + : + Tables( args ), + Binary( args, type ), + TabGoto( args ) + {} + + void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class BinGotoLoop + : public BinGoto, public ActLoop +{ +public: + BinGotoLoop( const CodeGenArgs &args ) + : + Tables( args ), + BinGoto( args, Loop ), + ActLoop( args ) + {} +}; + + +class BinGotoExp + : public BinGoto, public ActExp +{ +public: + BinGotoExp( const CodeGenArgs &args ) + : + Tables( args ), + BinGoto( args, Exp ), + ActExp( args ) + {} +}; + + +#endif diff --git a/src/libfsm/binvar.cc b/src/libfsm/binvar.cc new file mode 100644 index 00000000..27e40c03 --- /dev/null +++ b/src/libfsm/binvar.cc @@ -0,0 +1,137 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "binvar.h" +#include "parsedata.h" + +void BinVar::LOCATE_TRANS() +{ + out << + " " << keys << " = " << OFFSET( ARR_REF( transKeys ), ARR_REF( keyOffsets ) + "[" + vCS() + "]" ) << ";\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexOffsets ) << "[" << vCS() << "];\n" + "\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( singleLens ) << "[" << vCS() << "];\n" + " " << have << " = 0;\n" + " if ( " << klen << " > 0 ) {\n" + " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" + " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + " << klen << " - 1;\n" + " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" + " _bsc = 1;\n" + " while ( _bsc == 1 ) {\n" + " if ( _upper < _lower ) {\n" + " " << keys << " += " << klen << ";\n" + " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" + " _bsc = 0;\n" + " }\n" + " else {\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << have << " = 1;\n" + " " << trans << " += " << CAST( UINT() ) << "(_mid - " << keys << ");\n" + " _bsc = 0;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " " << klen << " = " << CAST("int") << ARR_REF( rangeLens ) << "[" << vCS() << "];\n" + " if ( " << have << " == 0 && " << klen << " > 0 ) {\n" + " " << INDEX( ALPH_TYPE(), "_lower" ) << " = " << keys << ";\n" + " " << INDEX( ALPH_TYPE(), "_upper" ) << " = " << keys << " + (" << klen << "<<1) - 2;\n" + " " << INDEX( ALPH_TYPE(), "_mid" ) << ";\n" + " _bsc = 1;\n" + " while ( _bsc == 1 ) {\n" + " if ( _upper < _lower ) {\n" + " " << trans << " += " << CAST( UINT() ) << "" << klen << ";\n" + " _bsc = 0;\n" + " }\n" + " else {\n" + " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n" + " if ( " << GET_KEY() << " < " << DEREF( ARR_REF( transKeys ), "_mid" ) << " )\n" + " _upper = _mid - 2;\n" + " else if ( " << GET_KEY() << " > " << DEREF( ARR_REF( transKeys ), "_mid + 1" ) << " )\n" + " _lower = _mid + 2;\n" + " else {\n" + " " << trans << " += " << CAST( UINT() ) << "((_mid - " << keys << ")>>1);\n" + " _bsc = 0;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n"; +} + +void BinVar::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + std::stringstream success, error; + + out << + " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + if ( red->condSpaceList.length() > 0 ) + COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); + + success << + cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; + + error << + cond << " = " << errCondOffset << ";\n"; + + out << + " {\n" + " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" + " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" + " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" + " _bsc = 1;\n" + " while ( _bsc == 1 ) {\n" + " if ( _upper < _lower ) {\n" + " " << error.str() << "\n" + " _bsc = 0;\n" + " }\n" + " else {\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << success.str() << "\n" + " _bsc = 0;\n" + " }\n" + " }\n" + " }\n" + " }\n" + ; + } +} + diff --git a/src/libfsm/binvar.h b/src/libfsm/binvar.h new file mode 100644 index 00000000..cbbcef79 --- /dev/null +++ b/src/libfsm/binvar.h @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_BINVAR_H +#define RAGEL_BINVAR_H + +#include "binary.h" +#include "actloop.h" +#include "actexp.h" + +struct BinVar +: + public Binary, public TabVar +{ + BinVar( const CodeGenArgs &args, Binary::Type type ) + : + Tables( args ), + Binary( args, type ), + TabVar( args ) + {} + + void VAR_COND_BIN_SEARCH( Variable &var, TableArray &keys, std::string ok, std::string error ); + + void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class BinVarLoop + : public BinVar, public ActLoop +{ +public: + BinVarLoop( const CodeGenArgs &args ) + : + Tables( args ), + BinVar( args, Loop ), + ActLoop( args ) + {} +}; + +class BinVarExp +: + public BinVar, public ActExp +{ +public: + BinVarExp( const CodeGenArgs &args ) + : + Tables( args ), + BinVar( args, Exp ), + ActExp( args ) + {} +}; + +#endif diff --git a/src/libfsm/codegen.cc b/src/libfsm/codegen.cc new file mode 100644 index 00000000..db8cc60a --- /dev/null +++ b/src/libfsm/codegen.cc @@ -0,0 +1,1202 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "codegen.h" +#include "ragel.h" +#include "redfsm.h" +#include "gendata.h" +#include "parsedata.h" +#include +#include +#include +#include + + +using std::ostream; +using std::ostringstream; +using std::string; +using std::endl; +using std::istream; +using std::ifstream; +using std::ostream; +using std::ios; +using std::cin; +using std::endl; + +std::ostream &operator<<( std::ostream &out, Variable &v ) +{ + out << v.name; + v.isReferenced = true; + return out; +} + +std::ostream &operator<<( std::ostream &out, GotoLabel &l ) +{ + out << l.name; + l.isReferenced = true; + return out; +} + +TableArray::TableArray( const char *name, CodeGen &codeGen ) +: + state(InitialState), + name(name), + width(0), + isSigned(true), + isChar(false), + stringTables( codeGen.stringTables ), + iall( codeGen.stringTables ? IALL_STRING : IALL_INTEGRAL ), + values(0), + + /* + * Use zero for min and max because + * we we null terminate every array. + */ + min(0), + max(0), + + codeGen(codeGen), + out(codeGen.out), + ln(0), + isReferenced(false), + started(false) +{ + codeGen.arrayVector.append( this ); +} + +std::string TableArray::ref() +{ + isReferenced = true; + return string("_") + codeGen.DATA_PREFIX() + name; +} + +long long TableArray::size() +{ + return width * values; +} + +void TableArray::startAnalyze() +{ +} + +void TableArray::valueAnalyze( long long v ) +{ + values += 1; + if ( v < min ) + min = v; + if ( v > max ) + max = v; +} + +void TableArray::finishAnalyze() +{ + if ( codeGen.backend == Direct ) { + /* Calculate the type if it is not already set. */ + if ( type.empty() ) { + if ( min >= S8BIT_MIN && max <= S8BIT_MAX ) { + type = "signed char"; + width = sizeof(char); + } + else if ( min >= S16BIT_MIN && max <= S16BIT_MAX ) { + type = "short"; + width = sizeof(short); + } + else if ( min >= S32BIT_MIN && max <= S32BIT_MAX ) { + type = "int"; + width = sizeof(int); + } + else if ( min >= S64BIT_MAX && max <= S64BIT_MAX ) { + type = "long"; + width = sizeof(long); + } + else { + type = "long long"; + width = sizeof(long long); + } + } + } + else { + /* Calculate the type if it is not already set. */ + if ( type.empty() ) { + if ( min >= S8BIT_MIN && max <= S8BIT_MAX ) { + type = "s8"; + width = sizeof(char); + } + else if ( min >= S16BIT_MIN && max <= S16BIT_MAX ) { + type = "s16"; + width = sizeof(short); + } + else if ( min >= S32BIT_MIN && max <= S32BIT_MAX ) { + type = "s32"; + width = sizeof(int); + } + else if ( min >= S64BIT_MAX && max <= S64BIT_MAX ) { + type = "s64"; + width = sizeof(long); + } + else { + type = "s128"; + width = sizeof(long long); + } + } + } +} + +void TableArray::startGenerate() +{ + if ( codeGen.backend == Direct ) { + if ( stringTables ) { + out << "static const char S_" << codeGen.DATA_PREFIX() << name << + "[] __attribute__((aligned (16))) = \n\t\""; + } + else { + out << "static const " << type << " " << + "_" << codeGen.DATA_PREFIX() << name << + "[] = {\n\t"; + } + } + else { + out << "array " << type << " " << + "_" << codeGen.DATA_PREFIX() << name << + "( " << min << ", " << max << " ) = { "; + } +} + +void TableArray::stringGenerate( long long value ) +{ + char c; + short h; + int i; +#if SIZEOF_INT != SIZEOF_LONG + long l; +#endif + unsigned char *p = 0; + int n = 0; + switch ( width ) { + case sizeof( char ): + c = value; + p = (unsigned char *)&c; + n = sizeof(char); + break; + case sizeof( short ): + h = value; + p = (unsigned char *)&h; + n = sizeof(short); + break; + case sizeof( int ): + i = value; + p = (unsigned char *)&i; + n = sizeof(int); + break; +#if SIZEOF_INT != SIZEOF_LONG + case sizeof( long ): + l = value; + p = (unsigned char *)&l; + n = sizeof(long); + break; +#endif + } + + std::ios_base::fmtflags prevFlags = out.flags( std::ios::hex ); + int prevFill = out.fill( '0' ); + + while ( n-- > 0 ) { + out << '\\'; + out << 'x'; + out << std::setw(2) << (unsigned int) *p++; + } + + out.flags( prevFlags ); + out.fill( prevFill ); +} + +void TableArray::valueGenerate( long long v ) +{ + if ( codeGen.backend == Direct ) { + if ( stringTables ) { + stringGenerate( v ); + + if ( ++ln % iall == 0 ) { + out << "\"\n\t\""; + ln = 0; + } + } + else { + if ( isChar ) + out << "c(" << v << ")"; + else if ( !isSigned ) + out << v << "u"; + else + out << v; + + if ( ( ++ln % iall ) == 0 ) { + out << ",\n\t"; + ln = 0; + } + else { + out << ", "; + } + } + } + else { + if ( isChar ) + out << "c(" << v << ")"; + else if ( !isSigned ) + out << "u(" << v << ")"; + else + out << v; + out << ", "; + } +} + +void TableArray::finishGenerate() +{ + if ( codeGen.backend == Direct ) { + if ( stringTables ) { + out << "\";\nconst " << type << " *_" << codeGen.DATA_PREFIX() << name << + " = (const " << type << "*) S_" << codeGen.DATA_PREFIX() << name << ";\n\n"; + + } + else { + if ( isChar ) + out << "c(0)\n};\n\n"; + else if ( !isSigned ) + out << "0u\n};\n\n"; + else + out << "0\n};\n\n"; + } + } + else { + if ( isChar ) + out << "c(0) };\n\n"; + else if ( !isSigned ) + out << "u(0) };\n\n"; + else + out << "0 };\n\n"; + } + + if ( codeGen.red->id->printStatistics ) { + codeGen.red->id->stats() << name << "\t" << values << "\t" << + size() << "\t" << endl; + } + + codeGen.tableData += size(); +} + +void TableArray::start() +{ + assert( !started ); + started = true; + switch ( state ) { + case InitialState: + break; + case AnalyzePass: + startAnalyze(); + break; + case GeneratePass: + if ( isReferenced ) + startGenerate(); + break; + } +} + +void TableArray::value( long long v ) +{ + assert( started ); + switch ( state ) { + case InitialState: + break; + case AnalyzePass: + valueAnalyze( v ); + break; + case GeneratePass: + if ( isReferenced ) + valueGenerate( v ); + break; + } +} + +void TableArray::finish() +{ + assert( started ); + started = false; + switch ( state ) { + case InitialState: + break; + case AnalyzePass: + finishAnalyze(); + break; + case GeneratePass: + if ( isReferenced ) + finishGenerate(); + break; + } +} + +/* Init code gen with in parameters. */ +CodeGen::CodeGen( const CodeGenArgs &args ) +: + CodeGenData( args ), + cpc( "_cpc" ), + pop_test( "_pop_test" ), + new_recs( "new_recs" ), + alt( "_alt" ), + tableData( 0 ), + backend( args.id->hostLang->backend ), + stringTables( args.id->stringTables ), + + nfaTargs( "nfa_targs", *this ), + nfaOffsets( "nfa_offsets", *this ), + nfaPushActions( "nfa_push_actions", *this ), + nfaPopTrans( "nfa_pop_trans", *this ) +{ +} + +void CodeGen::statsSummary() +{ + if ( red->id->printStatistics ) + red->id->stats() << "table-data\t\t" << tableData << endl << endl; +} + + +string CodeGen::CAST( string type ) +{ + if ( backend == Direct ) + return "(" + type + ")"; + else + return "cast(" + type + ")"; +} + +/* Write out the fsm name. */ +string CodeGen::FSM_NAME() +{ + return fsmName; +} + +/* Emit the offset of the start state as a decimal integer. */ +string CodeGen::START_STATE_ID() +{ + ostringstream ret; + ret << redFsm->startState->id; + return ret.str(); +}; + + +string CodeGen::ACCESS() +{ + ostringstream ret; + if ( red->accessExpr != 0 ) { + ret << OPEN_HOST_PLAIN(); + INLINE_LIST( ret, red->accessExpr, 0, false, false ); + ret << CLOSE_HOST_PLAIN(); + ret << ACCESS_OPER(); + } + return ret.str(); +} + + +string CodeGen::P() +{ + ostringstream ret; + if ( red->pExpr == 0 ) + ret << "p"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->pExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::PE() +{ + ostringstream ret; + if ( red->peExpr == 0 ) + ret << "pe"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->peExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::vEOF() +{ + ostringstream ret; + if ( red->eofExpr == 0 ) + ret << "eof"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->eofExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::vCS() +{ + ostringstream ret; + if ( red->csExpr == 0 ) + ret << ACCESS() << "cs"; + else { + /* Emit the user supplied method of retrieving the key. */ + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->csExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::TOP() +{ + ostringstream ret; + if ( red->topExpr == 0 ) + ret << ACCESS() + "top"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->topExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::STACK() +{ + ostringstream ret; + if ( red->stackExpr == 0 ) + ret << ACCESS() + "stack"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->stackExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::ACT() +{ + ostringstream ret; + if ( red->actExpr == 0 ) + ret << ACCESS() + "act"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->actExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::TOKSTART() +{ + ostringstream ret; + if ( red->tokstartExpr == 0 ) + ret << ACCESS() + "ts"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->tokstartExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::TOKEND() +{ + ostringstream ret; + if ( red->tokendExpr == 0 ) + ret << ACCESS() + "te"; + else { + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->tokendExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + return ret.str(); +} + +string CodeGen::GET_KEY() +{ + ostringstream ret; + if ( red->getKeyExpr != 0 ) { + /* Emit the user supplied method of retrieving the key. */ + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, red->getKeyExpr, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + } + else { + /* Expression for retrieving the key, use simple dereference. */ + ret << "( " << DEREF( "data", P() ) << ")"; + } + return ret.str(); +} + +/* Write out a key from the fsm code gen. Depends on wether or not the key is + * signed. */ +string CodeGen::KEY( Key key ) +{ + if ( backend == Direct ) { + ostringstream ret; + if ( alphType->isChar ) + ret << "c(" << (unsigned long) key.getVal() << ")"; + else if ( keyOps->isSigned || !keyOps->explicitUnsigned ) + ret << key.getVal(); + else + ret << (unsigned long) key.getVal() << "u"; + return ret.str(); + } + else { + ostringstream ret; + if ( alphType->isChar ) + ret << "c(" << (unsigned long) key.getVal() << ")"; + else if ( keyOps->isSigned || !keyOps->explicitUnsigned ) + ret << key.getVal(); + else + ret << "u(" << (unsigned long) key.getVal() << ")"; + return ret.str(); + } +} + +bool CodeGen::isAlphTypeSigned() +{ + return keyOps->isSigned; +} + +void CodeGen::DECLARE( std::string type, Variable &var, std::string init ) +{ + if ( var.isReferenced ) + out << type << " " << var.name << init << ";\n"; +} + +void CodeGen::EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) +{ + /* The parser gives fexec two children. The double brackets are for D + * code. If the inline list is a single word it will get interpreted as a + * C-style cast by the D compiler. */ + ret << OPEN_GEN_BLOCK() << P() << " = (("; + INLINE_LIST( ret, item->children, targState, inFinish, false ); + ret << "))-1;" << CLOSE_GEN_BLOCK() << "\n"; +} + +void CodeGen::LM_SWITCH( ostream &ret, GenInlineItem *item, + int targState, int inFinish, bool csForced ) +{ + ret << + OPEN_GEN_BLOCK() << "switch( " << ACT() << " ) {\n"; + + for ( GenInlineList::Iter lma = *item->children; lma.lte(); lma++ ) { + /* Write the case label, the action and the case break. */ + if ( lma->lmId < 0 ) + ret << " " << DEFAULT() << " {\n"; + else + ret << " " << CASE( STR(lma->lmId) ) << " {\n"; + + /* Write the block and close it off. */ + INLINE_LIST( ret, lma->children, targState, inFinish, csForced ); + + ret << CEND() << "\n}\n"; + } + + ret << + " }" << CLOSE_GEN_BLOCK() << "\n" + "\t"; +} + +void CodeGen::LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ) +{ + /* The parser gives fexec two children. The double brackets are for D + * code. If the inline list is a single word it will get interpreted as a + * C-style cast by the D compiler. This should be in the D code generator. */ + ret << P() << " = (("; + INLINE_LIST( ret, item->children, targState, inFinish, false ); + ret << "))-1;\n"; +} + +void CodeGen::SET_ACT( ostream &ret, GenInlineItem *item ) +{ + ret << ACT() << " = " << item->lmId << ";"; +} + +void CodeGen::SET_TOKEND( ostream &ret, GenInlineItem *item ) +{ + /* The tokend action sets tokend. */ + ret << TOKEND() << " = " << P(); + if ( item->offset != 0 ) + out << "+" << item->offset; + out << ";"; +} + +void CodeGen::GET_TOKEND( ostream &ret, GenInlineItem *item ) +{ + ret << TOKEND(); +} + +void CodeGen::INIT_TOKSTART( ostream &ret, GenInlineItem *item ) +{ + ret << TOKSTART() << " = " << NIL() << ";"; +} + +void CodeGen::INIT_ACT( ostream &ret, GenInlineItem *item ) +{ + ret << ACT() << " = 0;"; +} + +void CodeGen::SET_TOKSTART( ostream &ret, GenInlineItem *item ) +{ + ret << TOKSTART() << " = " << P() << ";"; +} + +void CodeGen::HOST_STMT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + ret << OPEN_HOST_BLOCK( item->loc.fileName, item->loc.line ); + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + ret << CLOSE_HOST_BLOCK(); + } +} + +#if 0 +void CodeGen::LM_CASE( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + } +} +#endif + +void CodeGen::HOST_EXPR( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + ret << CLOSE_HOST_EXPR(); + } +} + +void CodeGen::HOST_TEXT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + ret << OPEN_HOST_PLAIN(); + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + ret << CLOSE_HOST_PLAIN(); + } +} + +void CodeGen::GEN_STMT( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + ret << OPEN_GEN_BLOCK(); + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + ret << CLOSE_GEN_BLOCK(); + } +} + +void CodeGen::GEN_EXPR( ostream &ret, GenInlineItem *item, + int targState, bool inFinish, bool csForced ) +{ + if ( item->children->length() > 0 ) { + /* Write the block and close it off. */ + ret << OPEN_GEN_EXPR(); + INLINE_LIST( ret, item->children, targState, inFinish, csForced ); + ret << CLOSE_GEN_EXPR(); + } +} + +void CodeGen::INLINE_EXPR( ostream &ret, GenInlineList *inlineList ) +{ + ret << OPEN_HOST_EXPR(); + INLINE_LIST( ret, inlineList, 0, false, false ); + ret << CLOSE_HOST_EXPR(); +} + +void CodeGen::INLINE_BLOCK( ostream &ret, GenInlineExpr *inlineExpr ) +{ + out << OPEN_HOST_BLOCK( inlineExpr ); + INLINE_LIST( out, inlineExpr->inlineList, 0, false, false ); + out << CLOSE_HOST_BLOCK(); +} + +void CodeGen::INLINE_PLAIN( ostream &ret, GenInlineExpr *inlineExpr ) +{ + +} + +/* Write out an inline tree structure. Walks the list and possibly calls out + * to virtual functions than handle language specific items in the tree. */ +void CodeGen::INLINE_LIST( ostream &ret, GenInlineList *inlineList, + int targState, bool inFinish, bool csForced ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case GenInlineItem::Text: + if ( backend == Direct ) + ret << item->data; + else + translatedHostData( ret, item->data ); + break; + case GenInlineItem::Goto: + GOTO( ret, item->targState->id, inFinish ); + break; + case GenInlineItem::Call: + CALL( ret, item->targState->id, targState, inFinish ); + break; + case GenInlineItem::Ncall: + NCALL( ret, item->targState->id, targState, inFinish ); + break; + case GenInlineItem::Next: + NEXT( ret, item->targState->id, inFinish ); + break; + case GenInlineItem::Ret: + RET( ret, inFinish ); + break; + case GenInlineItem::Nret: + NRET( ret, inFinish ); + break; + case GenInlineItem::PChar: + ret << P(); + break; + case GenInlineItem::Char: + ret << OPEN_GEN_EXPR() << GET_KEY() << CLOSE_GEN_EXPR(); + break; + case GenInlineItem::Hold: + ret << OPEN_GEN_BLOCK() << P() << " = " << P() << " - 1; " << CLOSE_GEN_BLOCK(); + break; + case GenInlineItem::LmHold: + ret << P() << " = " << P() << " - 1;"; + break; + case GenInlineItem::NfaClear: + ret << "nfa_len = 0; "; + break; + case GenInlineItem::Exec: + EXEC( ret, item, targState, inFinish ); + break; + case GenInlineItem::Curs: + CURS( ret, inFinish ); + break; + case GenInlineItem::Targs: + TARGS( ret, inFinish, targState ); + break; + case GenInlineItem::Entry: + ret << item->targState->id; + break; + case GenInlineItem::GotoExpr: + GOTO_EXPR( ret, item, inFinish ); + break; + case GenInlineItem::CallExpr: + CALL_EXPR( ret, item, targState, inFinish ); + break; + case GenInlineItem::NcallExpr: + NCALL_EXPR( ret, item, targState, inFinish ); + break; + case GenInlineItem::NextExpr: + NEXT_EXPR( ret, item, inFinish ); + break; + case GenInlineItem::LmSwitch: + LM_SWITCH( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::LmExec: + LM_EXEC( ret, item, targState, inFinish ); + break; + case GenInlineItem::LmCase: + /* Not encountered here, in the lm switch. */ + break; + case GenInlineItem::LmSetActId: + SET_ACT( ret, item ); + break; + case GenInlineItem::LmSetTokEnd: + SET_TOKEND( ret, item ); + break; + case GenInlineItem::LmGetTokEnd: + GET_TOKEND( ret, item ); + break; + case GenInlineItem::LmInitTokStart: + INIT_TOKSTART( ret, item ); + break; + case GenInlineItem::LmInitAct: + INIT_ACT( ret, item ); + break; + case GenInlineItem::LmSetTokStart: + SET_TOKSTART( ret, item ); + break; + case GenInlineItem::Break: + BREAK( ret, targState, csForced ); + break; + case GenInlineItem::Nbreak: + NBREAK( ret, targState, csForced ); + break; + case GenInlineItem::HostStmt: + HOST_STMT( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::HostExpr: + HOST_EXPR( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::HostText: + HOST_TEXT( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::GenStmt: + GEN_STMT( ret, item, targState, inFinish, csForced ); + break; + case GenInlineItem::GenExpr: + GEN_EXPR( ret, item, targState, inFinish, csForced ); + break; + /* These should not be encountered. We handle these Nfa wraps at the top level. */ + case GenInlineItem::NfaWrapAction: + case GenInlineItem::NfaWrapConds: + break; + } + } +} + +/* Write out paths in line directives. Escapes any special characters. */ +string CodeGen::LDIR_PATH( char *path ) +{ + ostringstream ret; + for ( char *pc = path; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + ret << "\\\\"; + else + ret << *pc; + } + return ret.str(); +} + +void CodeGen::ACTION( ostream &ret, GenAction *action, IlOpts opts ) +{ + ret << '\t'; + ret << OPEN_HOST_BLOCK( action->loc.fileName, action->loc.line ); + INLINE_LIST( ret, action->inlineList, opts.targState, opts.inFinish, opts.csForced ); + ret << CLOSE_HOST_BLOCK(); + ret << "\n"; + genOutputLineDirective( ret ); +} + +void CodeGen::CONDITION( ostream &ret, GenAction *condition ) +{ + ret << OPEN_HOST_EXPR( condition->loc.fileName, condition->loc.line ); + INLINE_LIST( ret, condition->inlineList, 0, false, false ); + ret << CLOSE_HOST_EXPR(); + ret << "\n"; + genOutputLineDirective( ret ); +} + +void CodeGen::NFA_CONDITION( ostream &ret, GenAction *condition, bool last ) +{ + if ( condition->inlineList->length() == 1 && + condition->inlineList->head->type == + GenInlineItem::NfaWrapAction ) + { + GenAction *action = condition->inlineList->head->wrappedAction; + ACTION( out, action, IlOpts( 0, false, false ) ); + } + else if ( condition->inlineList->length() == 1 && + condition->inlineList->head->type == + GenInlineItem::NfaWrapConds ) + { + ret << + " " << cpc << " = 0;\n"; + + GenCondSpace *condSpace = condition->inlineList->head->condSpace; + for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + ret << + " if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + ret << " ) " << cpc << " += " << condValOffset << ";\n"; + } + + const CondKeySet &keys = condition->inlineList->head->condKeySet; + if ( keys.length() > 0 ) { + ret << pop_test << " = "; + for ( CondKeySet::Iter cki = keys; cki.lte(); cki++ ) { + ret << "" << cpc << " == " << *cki; + if ( !cki.last() ) + ret << " || "; + } + ret << ";\n"; + } + else { + ret << pop_test << " = 0;\n"; + } + + if ( !last ) { + ret << + "if ( !" << pop_test << " )\n" + " break;\n"; + } + } + else { + ret << pop_test << " = "; + CONDITION( ret, condition ); + ret << ";\n"; + + if ( !last ) { + ret << + "if ( !" << pop_test << " )\n" + " break;\n"; + } + } +} + +void CodeGen::NFA_POP_TEST_EXEC() +{ + out << + " " << pop_test << " = 1;\n" + " switch ( nfa_bp[nfa_len].popTrans ) {\n"; + + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; + redAct.lte(); redAct++ ) + { + if ( redAct->numNfaPopTestRefs > 0 ) { + /* Write the entry label. */ + out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) + NFA_CONDITION( out, item->value, item.last() ); + + out << CEND() << "\n}\n"; + } + } + + out << + " }\n" + "\n"; +} + + +string CodeGen::ERROR_STATE() +{ + ostringstream ret; + if ( redFsm->errState != 0 ) + ret << redFsm->errState->id; + else + ret << "-1"; + return ret.str(); +} + +string CodeGen::FIRST_FINAL_STATE() +{ + ostringstream ret; + if ( redFsm->firstFinState != 0 ) + ret << redFsm->firstFinState->id; + else + ret << redFsm->nextStateId; + return ret.str(); +} + +void CodeGen::writeInit() +{ + out << " {\n"; + + if ( !noCS ) + out << "\t" << vCS() << " = " << CAST("int") << START() << ";\n"; + + if ( redFsm->anyNfaStates() ) + out << "\t" << "nfa_len = 0;\n"; + + /* If there are any calls, then the stack top needs initialization. */ + if ( redFsm->anyActionCalls() || redFsm->anyActionNcalls() || + redFsm->anyActionRets() || redFsm->anyActionNrets() ) + { + out << "\t" << TOP() << " = 0;\n"; + } + + if ( red->hasLongestMatch ) { + out << + " " << TOKSTART() << " = " << NIL() << ";\n" + " " << TOKEND() << " = " << NIL() << ";\n"; + + if ( redFsm->usingAct() ) { + out << + " " << ACT() << " = 0;\n"; + } + } + out << " }\n"; +} + +string CodeGen::DATA_PREFIX() +{ + if ( !noPrefix ) + return FSM_NAME() + "_"; + return ""; +} + +/* Emit the alphabet data type. */ +string CodeGen::ALPH_TYPE() +{ + string ret = alphType->data1; + if ( alphType->data2 != 0 ) { + ret += " "; + ret += + alphType->data2; + } + return ret; +} + +void CodeGen::VALUE( string type, string name, string value ) +{ + if ( backend == Direct ) + out << "static const " << type << " " << name << " = " << value << ";\n"; + else + out << "value " << type << " " << name << " = " << value << ";\n"; +} + +string CodeGen::STR( int v ) +{ + ostringstream s; + s << v; + return s.str(); +} + +void CodeGen::STATE_IDS() +{ + if ( redFsm->startState != 0 ) + VALUE( "int", START(), START_STATE_ID() ); + + if ( !noFinal ) + VALUE( "int", FIRST_FINAL(), FIRST_FINAL_STATE() ); + + if ( !noError ) + VALUE( "int", ERROR(), ERROR_STATE() ); + + out << "\n"; + + if ( red->entryPointNames.length() > 0 ) { + for ( EntryNameVect::Iter en = red->entryPointNames; en.lte(); en++ ) { + string name = DATA_PREFIX() + "en_" + *en; + VALUE( "int", name, STR( red->entryPointIds[en.pos()] ) ); + } + out << "\n"; + } +} + +void CodeGen::writeStart() +{ + out << START_STATE_ID(); +} + +void CodeGen::writeFirstFinal() +{ + out << FIRST_FINAL_STATE(); +} + +void CodeGen::writeError() +{ + out << ERROR_STATE(); +} + +void CodeGen::writeExports() +{ + if ( red->exportList.length() > 0 ) { + for ( ExportList::Iter ex = red->exportList; ex.lte(); ex++ ) { + out << EXPORT( ALPH_TYPE(), + DATA_PREFIX() + "ex_" + ex->name, KEY(ex->key) ) << "\n"; + } + out << "\n"; + } +} + +void CodeGen::NFA_PUSH( std::string state ) +{ + if ( redFsm->anyNfaStates() ) { + out << + " if ( " << ARR_REF( nfaOffsets ) << "[" << state << "] != 0 ) {\n" + " " << alt << " = 0; \n" + " " << new_recs << " = " << CAST("int") << ARR_REF( nfaTargs ) << "[" << CAST("int") << + ARR_REF( nfaOffsets ) << "[" << state << "]];\n"; + + if ( red->nfaPrePushExpr != 0 ) { + out << OPEN_HOST_BLOCK( red->nfaPrePushExpr ); + INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); + out << CLOSE_HOST_BLOCK(); + out << "\n"; + genOutputLineDirective( out ); + } + + out << + " while ( " << alt << " < " << new_recs << " ) { \n"; + + + out << + " nfa_bp[nfa_len].state = " << CAST("int") << ARR_REF( nfaTargs ) << "[" << CAST("int") << + ARR_REF( nfaOffsets ) << "[" << state << "] + 1 + " << alt << "];\n" + " nfa_bp[nfa_len].p = " << P() << ";\n"; + + if ( redFsm->bAnyNfaPops ) { + out << + " nfa_bp[nfa_len].popTrans = " << ARR_REF( nfaPopTrans ) << "[" << CAST("long") << + ARR_REF( nfaOffsets ) << "[" << state << "] + 1 + " << alt << "];\n" + "\n" + ; + } + + if ( redFsm->bAnyNfaPushes ) { + out << + " switch ( " << ARR_REF( nfaPushActions ) << "[" << CAST("int") << + ARR_REF( nfaOffsets ) << "[" << state << "] + 1 + " << alt << "] ) {\n"; + + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; + redAct.lte(); redAct++ ) + { + if ( redAct->numNfaPushRefs > 0 ) { + /* Write the entry label. */ + out << "\t " << CASE( STR( redAct->actListId+1 ) ) << " {\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, false, false ) ); + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + out << + " }\n"; + } + + + out << + " nfa_len += 1;\n" + " " << alt << " += 1;\n" + " }\n" + " }\n" + ; + } +} + +void CodeGen::NFA_POST_POP() +{ + if ( red->nfaPostPopExpr != 0 ) { + out << OPEN_HOST_BLOCK( red->nfaPostPopExpr ); + INLINE_LIST( out, red->nfaPostPopExpr->inlineList, 0, false, false ); + out << CLOSE_HOST_BLOCK(); + } +} diff --git a/src/libfsm/codegen.h b/src/libfsm/codegen.h new file mode 100644 index 00000000..dcc24e3b --- /dev/null +++ b/src/libfsm/codegen.h @@ -0,0 +1,460 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _C_CODEGEN_H +#define _C_CODEGEN_H + +#include +#include +#include +#include "common.h" +#include "gendata.h" +#include "vector.h" +#include "idbase.h" + +using std::string; +using std::ostream; + +/* Integer array line length. */ +//#define IALL 8 + +#define IALL_INTEGRAL 8 +#define IALL_STRING 128 + + +/* Forwards. */ +struct RedFsmAp; +struct RedStateAp; +struct CodeGenData; +struct GenAction; +struct NameInst; +struct GenInlineItem; +struct GenInlineList; +struct RedAction; +struct FsmLongestMatch; +struct LongestMatchPart; + +string itoa( int i ); + +struct Variable +{ + Variable( const char *name ) : name(name), isReferenced(false) {} + + const std::string ref() { isReferenced = true; return name; } + + const char *name; + bool isReferenced; +}; + +struct GotoLabel +{ + GotoLabel( const char *name ) : name(name), isReferenced(false) {} + + const std::string ref() { isReferenced = true; return name; } + + const char *name; + bool isReferenced; +}; + +std::ostream &operator<<( std::ostream &out, GotoLabel &l ); +std::ostream &operator<<( std::ostream &out, Variable &v ); + +struct TableArray; +typedef Vector ArrayVector; +class CodeGen; + +struct TableArray +{ + enum State { + InitialState = 1, + AnalyzePass, + GeneratePass + }; + + TableArray( const char *name, CodeGen &codeGen ); + + void start(); + void startAnalyze(); + void startGenerate(); + + void setType( std::string type, int width, bool isChar ) + { + this->type = type; + this->width = width; + this->isChar = isChar; + } + + std::string ref(); + + void value( long long v ); + + void valueAnalyze( long long v ); + void valueGenerate( long long v ); + void stringGenerate( long long value ); + + void finish(); + void finishAnalyze(); + void finishGenerate(); + + void setState( TableArray::State state ) + { this->state = state; } + + long long size(); + + State state; + const char *name; + std::string type; + int width; + bool isSigned; + bool isChar; + bool stringTables; + int iall; + long long values; + long long min; + long long max; + CodeGen &codeGen; + std::ostream &out; + int ln; + bool isReferenced; + bool started; +}; + +struct IlOpts +{ + IlOpts( int targState, bool inFinish, bool csForced ) + : targState(targState), inFinish(inFinish), csForced(csForced) {} + + int targState; + bool inFinish; + bool csForced; +}; + + +/* + * class CodeGen + */ +class CodeGen : public CodeGenData +{ +public: + CodeGen( const CodeGenArgs &args ); + + virtual ~CodeGen() {} + + virtual void writeInit(); + virtual void writeStart(); + virtual void writeFirstFinal(); + virtual void writeError(); + virtual void statsSummary(); + +protected: + friend struct TableArray; + typedef Vector ArrayVector; + ArrayVector arrayVector; + + Variable cpc; + Variable pop_test; + Variable new_recs; + Variable alt; + + string FSM_NAME(); + string START_STATE_ID(); + void taActions(); + string KEY( Key key ); + string LDIR_PATH( char *path ); + + void ACTION( ostream &ret, GenAction *action, IlOpts opts ); + void NFA_CONDITION( ostream &ret, GenAction *condition, bool last ); + void NFA_POP_TEST_EXEC(); + void CONDITION( ostream &ret, GenAction *condition ); + string ALPH_TYPE(); + + bool isAlphTypeSigned(); + long long tableData; + RagelBackend backend; + bool stringTables; + BackendFeature backendFeature; + + TableArray nfaTargs; + TableArray nfaOffsets; + TableArray nfaPushActions; + TableArray nfaPopTrans; + + virtual string GET_KEY(); + + string P(); + string PE(); + string vEOF(); + + string ACCESS(); + string vCS(); + string STACK(); + string TOP(); + string TOKSTART(); + string TOKEND(); + string ACT(); + + string DATA_PREFIX(); + string START() { return DATA_PREFIX() + "start"; } + string ERROR() { return DATA_PREFIX() + "error"; } + string FIRST_FINAL() { return DATA_PREFIX() + "first_final"; } + + /* Declare a variable only if referenced. */ + void DECLARE( std::string type, Variable &var, std::string init = "" ); + + string CAST( string type ); + + string ARR_TYPE( const TableArray &ta ) + { return ta.type; } + + string ARR_REF( TableArray &ta ) + { return ta.ref(); } + + void INLINE_EXPR( ostream &ret, GenInlineList *inlineList ); + void INLINE_BLOCK( ostream &ret, GenInlineExpr *inlineExpr ); + void INLINE_PLAIN( ostream &ret, GenInlineExpr *inlineExpr ); + + void INLINE_LIST( ostream &ret, GenInlineList *inlineList, + int targState, bool inFinish, bool csForced ); + virtual void GOTO( ostream &ret, int gotoDest, bool inFinish ) = 0; + virtual void CALL( ostream &ret, int callDest, int targState, bool inFinish ) = 0; + virtual void NCALL( ostream &ret, int callDest, int targState, bool inFinish ) = 0; + virtual void NEXT( ostream &ret, int nextDest, bool inFinish ) = 0; + virtual void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) = 0; + virtual void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) = 0; + virtual void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, + int targState, bool inFinish ) = 0; + virtual void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, + int targState, bool inFinish ) = 0; + virtual void RET( ostream &ret, bool inFinish ) = 0; + virtual void NRET( ostream &ret, bool inFinish ) = 0; + virtual void BREAK( ostream &ret, int targState, bool csForced ) = 0; + virtual void NBREAK( ostream &ret, int targState, bool csForced ) = 0; + virtual void CURS( ostream &ret, bool inFinish ) = 0; + virtual void TARGS( ostream &ret, bool inFinish, int targState ) = 0; + void EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); + void LM_SWITCH( ostream &ret, GenInlineItem *item, int targState, + int inFinish, bool csForced ); + void LM_EXEC( ostream &ret, GenInlineItem *item, int targState, int inFinish ); + void SET_ACT( ostream &ret, GenInlineItem *item ); + void INIT_TOKSTART( ostream &ret, GenInlineItem *item ); + void INIT_ACT( ostream &ret, GenInlineItem *item ); + void SET_TOKSTART( ostream &ret, GenInlineItem *item ); + void SET_TOKEND( ostream &ret, GenInlineItem *item ); + void GET_TOKEND( ostream &ret, GenInlineItem *item ); + + void HOST_STMT( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); + void HOST_EXPR( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); + void HOST_TEXT( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); + void GEN_STMT( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); + void GEN_EXPR( ostream &ret, GenInlineItem *item, int targState, bool inFinish, bool csForced ); + + void STATE_IDS(); + + string ERROR_STATE(); + string FIRST_FINAL_STATE(); + + string STR( int v ); + + void VALUE( string type, string name, string value ); + + string ACCESS_OPER() + { return backend == Direct ? "" : " -> "; } + + string OPEN_HOST_EXPR() + { return backend == Direct ? "(" : "host( \"-\", 1 ) ={"; } + + string OPEN_HOST_EXPR( string fileName, int line ) + { + return backend == Direct ? "(" : "host( \"" + fileName + "\", " + STR(line) + " ) ={"; + } + + string CLOSE_HOST_EXPR() + { return backend == Direct ? ")" : "}="; } + + string OPEN_HOST_BLOCK( string fileName, int line ) + { + if ( backend == Direct ) { + std::stringstream ss; + ss << "{\n" ; + (*genLineDirective)( ss, lineDirectives, line, fileName.c_str() ); + return ss.str(); + } + else { + return "host( \"" + fileName + "\", " + STR(line) + " ) ${"; + } + } + + string OPEN_HOST_BLOCK( GenInlineExpr *inlineExpr ) + { + return OPEN_HOST_BLOCK( inlineExpr->loc.fileName, inlineExpr->loc.line ); + } + + string CLOSE_HOST_BLOCK() + { return backend == Direct ? "}\n" : "}$"; } + + string OPEN_HOST_PLAIN() + { return backend == Direct ? "" : "host( \"-\", 1 ) @{"; } + + string CLOSE_HOST_PLAIN() + { return backend == Direct ? "" : "}@"; } + + string OPEN_GEN_EXPR() + { return backend == Direct ? "(" : "={"; } + + string CLOSE_GEN_EXPR() + { return backend == Direct ? ")" : "}="; } + + string OPEN_GEN_BLOCK() + { return backend == Direct ? "{" : "${"; } + + string CLOSE_GEN_BLOCK() + { return backend == Direct ? "}" : "}$"; } + + string OPEN_GEN_PLAIN() + { return backend == Direct ? "" : "@{"; } + + string CLOSE_GEN_PLAIN() + { return backend == Direct ? "" : "}@"; } + + string INT() + { return "int"; } + + string UINT() + { return backend == Direct ? "unsigned int" : "uint"; } + + string INDEX( string type, string name ) + { + if ( backend == Direct ) + return "const " + type + " *" + name; + else + return "index " + type + " " + name; + } + + string INDEX( string type ) + { + if ( backend == Direct ) + return "const " + type + " *"; + else + return "index " + type + " "; + } + + string LABEL( string name ) + { + return name + ": "; + } + + string EMIT_LABEL( GotoLabel label ) + { + if ( label.isReferenced ) + return std::string(label.name) + ": {}\n"; + else + return ""; + } + + string OFFSET( string arr, string off ) + { + if ( backend == Direct ) + return "( " + arr + " + (" + off + "))"; + else + return "offset( " + arr + ", " + off + " )"; + } + + string TRUE() + { + if ( backend == Direct ) + return "1"; + else + return "TRUE"; + } + + string DEREF( string arr, string off ) + { + if ( backend == Direct ) + return "(*( " + off + "))"; + else + return "deref( " + arr + ", " + off + " )"; + } + + string CASE( string val ) + { + if ( backend == Direct ) + return "case " + val + ": "; + else + return "case " + val; + } + + string DEFAULT() + { + if ( backend == Direct ) + return "default:"; + else + return "default"; + } + + string CEND( ) + { + if ( backend == Direct ) + return " break; "; + else + return " "; + } + + string FALLTHROUGH() + { + if ( backend == Direct ) + return " "; + else + return "fallthrough;"; + } + + string NIL() + { + if ( backend == Direct ) + return "0"; + else + return "nil"; + } + + string EXPORT( string type, string name, string value ) + { + if ( backend == Direct ) + return "#define " + name + " " + value; + else + return "export " + type + " " + name + " " + value + ";"; + } + + void NFA_POST_POP(); + virtual void NFA_PUSH( std::string ); + virtual void NFA_POP() = 0; + virtual void LOCATE_TRANS() {} + virtual void LOCATE_COND() {} + virtual void EOF_TRANS() {} + + + virtual void COND_EXEC( std::string expr ) {} + virtual void COND_BIN_SEARCH( Variable &var, TableArray &keys, std::string ok, std::string error ) {} + +public: + virtual void writeExports(); +}; + +#endif diff --git a/src/libfsm/common.cc b/src/libfsm/common.cc new file mode 100644 index 00000000..9ebbd905 --- /dev/null +++ b/src/libfsm/common.cc @@ -0,0 +1,352 @@ +/* + * Copyright 2006-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common.h" +#include "stdlib.h" +#include +#include +#include "ragel.h" + +/* + * C + */ + +const char *defaultOutFnC( const char *inputFileName ) +{ + const char *ext = findFileExtension( inputFileName ); + if ( ext != 0 && strcmp( ext, ".rh" ) == 0 ) + return fileNameFromStem( inputFileName, ".h" ); + else + return fileNameFromStem( inputFileName, ".c" ); +} + +HostType hostTypesC[] = +{ + { "char", 0, "char", true, true, false, CHAR_MIN, CHAR_MAX, 0, 0, sizeof(char) }, + { "signed", "char", "char", true, true, false, CHAR_MIN, CHAR_MAX, 0, 0, sizeof(char) }, + { "unsigned", "char", "uchar", false, true, false, 0, 0, 0, UCHAR_MAX, sizeof(unsigned char) }, + { "short", 0, "short", true, true, false, SHRT_MIN, SHRT_MAX, 0, 0, sizeof(short) }, + { "signed", "short", "short", true, true, false, SHRT_MIN, SHRT_MAX, 0, 0, sizeof(short) }, + { "unsigned", "short", "ushort", false, true, false, 0, 0, 0, USHRT_MAX, sizeof(unsigned short) }, + { "int", 0, "int", true, true, false, INT_MIN, INT_MAX, 0, 0, sizeof(int) }, + { "signed", "int", "int", true, true, false, INT_MIN, INT_MAX, 0, 0, sizeof(int) }, + { "unsigned", "int", "uint", false, true, false, 0, 0, 0, UINT_MAX, sizeof(unsigned int) }, + { "long", 0, "long", true, true, false, LONG_MIN, LONG_MAX, 0, 0, sizeof(long) }, + { "signed", "long", "long", true, true, false, LONG_MIN, LONG_MAX, 0, 0, sizeof(long) }, + { "unsigned", "long", "ulong", false, true, false, 0, 0, 0, ULONG_MAX, sizeof(unsigned long) }, +}; + +const HostLang hostLangC = { + hostTypesC, + 12, + 0, + true, + false, /* loopLabels */ + Direct, + GotoFeature, + &makeCodeGen, + &defaultOutFnC, + &genLineDirectiveC +}; + +/* + * ASM + */ +const char *defaultOutFnAsm( const char *inputFileName ) +{ + return fileNameFromStem( inputFileName, ".s" ); +} + +HostType hostTypesAsm[] = +{ + { "char", 0, "char", true, true, false, CHAR_MIN, CHAR_MAX, 0, 0, sizeof(char) }, + { "unsigned", "char", "uchar", false, true, false, 0, 0, 0, UCHAR_MAX, sizeof(unsigned char) }, + { "short", 0, "short", true, true, false, SHRT_MIN, SHRT_MAX, 0, 0, sizeof(short) }, + { "unsigned", "short", "ushort", false, true, false, 0, 0, 0, USHRT_MAX, sizeof(unsigned short) }, + { "int", 0, "int", true, true, false, INT_MIN, INT_MAX, 0, 0, sizeof(int) }, + { "unsigned", "int", "uint", false, true, false, 0, 0, 0, UINT_MAX, sizeof(unsigned int) }, + { "long", 0, "long", true, true, false, LONG_MIN, LONG_MAX, 0, 0, sizeof(long) }, + { "unsigned", "long", "ulong", false, true, false, 0, 0, 0, ULONG_MAX, sizeof(unsigned long) }, +}; + +const HostLang hostLangAsm = { + hostTypesAsm, + 8, + 0, + true, + false, /* loopLabels */ + Direct, + GotoFeature, + &makeCodeGenAsm, + &defaultOutFnC, + &genLineDirectiveAsm +}; + +HostType *findAlphType( const HostLang *hostLang, const char *s1 ) +{ + for ( int i = 0; i < hostLang->numHostTypes; i++ ) { + if ( strcmp( s1, hostLang->hostTypes[i].data1 ) == 0 && + hostLang->hostTypes[i].data2 == 0 ) + { + return hostLang->hostTypes + i; + } + } + + return 0; +} + +HostType *findAlphType( const HostLang *hostLang, const char *s1, const char *s2 ) +{ + for ( int i = 0; i < hostLang->numHostTypes; i++ ) { + if ( strcmp( s1, hostLang->hostTypes[i].data1 ) == 0 && + hostLang->hostTypes[i].data2 != 0 && + strcmp( s2, hostLang->hostTypes[i].data2 ) == 0 ) + { + return hostLang->hostTypes + i; + } + } + + return 0; +} + +HostType *findAlphTypeInternal( const HostLang *hostLang, const char *s1 ) +{ + for ( int i = 0; i < hostLang->numHostTypes; i++ ) { + if ( strcmp( s1, hostLang->hostTypes[i].internalName ) == 0 ) + return hostLang->hostTypes + i; + } + + return 0; +} + +std::streamsize output_filter::countAndWrite( const char *s, std::streamsize n ) +{ + for ( int i = 0; i < n; i++ ) { + switch ( s[i] ) { + case '\n': + line += 1; + break; + case '{': + /* If we detec an open block then eliminate the single-indent + * addition, which is to account for single statements. */ + singleIndent = false; + level += 1; + break; + case '}': + level -= 1; + break; + } + } + + return std::filebuf::xsputn( s, n ); +} + +bool openSingleIndent( const char *s, int n ) +{ + if ( n >= 3 && memcmp( s, "if ", 3 ) == 0 ) + return true; + + if ( n >= 8 && memcmp( s, "else if ", 8 ) == 0 ) + return true; + + if ( n >= 5 && memcmp( s, "else\n", 4 ) == 0 ) + return true; + + return false; +} + +/* Counts newlines before sending sync. */ +int output_filter::sync( ) +{ + line += 1; + return std::filebuf::sync(); +} + +/* Counts newlines before sending data out to file. */ +std::streamsize output_filter::xsputn( const char *s, std::streamsize n ) +{ + std::streamsize ret = n; + int l; + +restart: + if ( indent ) { + /* Consume mode Looking for the first non-whitespace. */ + while ( n > 0 && ( *s == ' ' || *s == '\t' ) ) { + s += 1; + n -= 1; + } + + if ( n > 0 ) { + int tabs = level + ( singleIndent ? 1 : 0 ); + + if ( *s == '}' ) { + /* If the next char is de-dent, then reduce the tabs. This is + * not a stream state change. The level reduction will be + * computed in write. */ + tabs -= 1; + } + + /* Note that the count and write will eliminate this if it detects + * an open block. */ + if ( openSingleIndent( s, n ) ) + singleIndent = true; + else + singleIndent = false; + + if ( *s != '#' ) { + /* Found some data, print the indentation and turn off indentation + * mode. */ + for ( l = 0; l < tabs; l++ ) + countAndWrite( "\t", 1 ); + } + + + indent = 0; + + goto restart; + } + } + else { + char *nl; + if ( (nl = (char*)memchr( s, '\n', n )) ) { + /* Print up to and including the newline. */ + int wl = nl - s + 1; + countAndWrite( s, wl ); + + /* Go into consume state. If we see more non-indentation chars we + * will generate the appropriate indentation level. */ + s += wl; + n -= wl; + indent = true; + goto restart; + } + else { + /* Indentation off, or no indent trigger (newline). */ + countAndWrite( s, n ); + } + } + + // What to do here? + return ret; +} + +/* Scans a string looking for the file extension. If there is a file + * extension then pointer returned points to inside the string + * passed in. Otherwise returns null. */ +const char *findFileExtension( const char *stemFile ) +{ + const char *ppos = stemFile + strlen(stemFile) - 1; + + /* Scan backwards from the end looking for the first dot. + * If we encounter a '/' before the first dot, then stop the scan. */ + while ( 1 ) { + /* If we found a dot or got to the beginning of the string then + * we are done. */ + if ( ppos == stemFile || *ppos == '.' ) + break; + + /* If we hit a / then there is no extension. Done. */ + if ( *ppos == '/' ) { + ppos = stemFile; + break; + } + ppos--; + } + + /* If we got to the front of the string then bail we + * did not find an extension */ + if ( ppos == stemFile ) + ppos = 0; + + return ppos; +} + +/* Make a file name from a stem. Removes the old filename suffix and + * replaces it with a new one. Returns a newed up string. */ +const char *fileNameFromStem( const char *stemFile, const char *suffix ) +{ + long len = strlen( stemFile ); + assert( len > 0 ); + + /* Get the extension. */ + const char *ppos = findFileExtension( stemFile ); + + /* If an extension was found, then shorten what we think the len is. */ + if ( ppos != 0 ) + len = ppos - stemFile; + + /* Make the return string from the stem and the suffix. */ + char *retVal = new char[ len + strlen( suffix ) + 1 ]; + strncpy( retVal, stemFile, len ); + strcpy( retVal + len, suffix ); + + return retVal; +} + +exit_object endp; + +void operator<<( std::ostream &out, exit_object & ) +{ + out << std::endl; + throw AbortCompile( 1 ); +} + +void genLineDirectiveC( std::ostream &out, bool lineDirectives, int line, const char *fileName ) +{ + if ( !lineDirectives ) + out << "/* "; + + out << "#line " << line << " \""; + for ( const char *pc = fileName; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + out << "\\\\"; + else if ( *pc == '"' ) + out << "\\\""; + else + out << *pc; + } + out << '"'; + + if ( !lineDirectives ) + out << " */"; + + out << '\n'; +} + +void genLineDirectiveAsm( std::ostream &out, bool lineDirectives, int line, const char *fileName ) +{ + out << "/* #line " << line << " \""; + for ( const char *pc = fileName; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + out << "\\\\"; + else if ( *pc == '"' ) + out << "\\\""; + else + out << *pc; + } + out << '"'; + out << " */\n"; +} + +void genLineDirectiveTrans( std::ostream &out, bool lineDirectives, int line, const char *fileName ) +{ +} diff --git a/src/libfsm/common.h b/src/libfsm/common.h new file mode 100644 index 00000000..142eb735 --- /dev/null +++ b/src/libfsm/common.h @@ -0,0 +1,504 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include +#include +#include "dlist.h" + +struct colm_location; + +struct InputData; +struct CodeGenData; +struct HostLang; +struct CodeGenArgs; + +enum RagelBackend +{ + Direct, + Translated +}; + +enum BackendFeature +{ + GotoFeature, + BreakFeature, + VarFeature +}; + +#define S8BIT_MIN -128 +#define S8BIT_MAX 127 + +#define U8BIT_MIN 0 +#define U8BIT_MAX 255 + +#define S16BIT_MIN -32768 +#define S16BIT_MAX 32767 + +#define U16BIT_MIN 0 +#define U16BIT_MAX 65535 + +#define S31BIT_MIN -1073741824l +#define S31BIT_MAX 1073741823l + +#define S32BIT_MIN -2147483648l +#define S32BIT_MAX 2147483647l + +#define U32BIT_MIN 0 +#define U32BIT_MAX 4294967295l + +#define S64BIT_MIN (-9223372036854775807LL - 1LL) +#define S64BIT_MAX 9223372036854775807LL + +#define U64BIT_MIN 0 +#define U64BIT_MAX 18446744073709551615ULL + +struct ParserLoc +{ + const char *fileName; + int line; + int col; +}; + +/* Location in an input file. */ +struct InputLoc +{ + InputLoc( colm_location *pcloc ); + + InputLoc() : fileName(0), line(-1), col(-1) {} + + InputLoc( const ParserLoc loc ) + { + fileName = loc.fileName; + line = loc.line; + col = loc.col; + + if ( fileName == 0 ) + fileName = "-"; + if ( line == 0 ) + line = 1; + } + + InputLoc( const InputLoc &loc ) + { + fileName = loc.fileName; + line = loc.line; + col = loc.col; + + if ( fileName == 0 ) + fileName = "-"; + if ( line == 0 ) + line = 1; + } + + InputLoc( const char *fileName, int line, int col ) + : fileName(fileName), line(line), col(col) {} + + const char *fileName; + int line; + int col; +}; + +extern InputLoc internal; + +typedef unsigned long long Size; + +struct Key +{ +private: + long key; + +public: + friend struct KeyOps; + + Key( ) {} + Key( const Key &key ) : key(key.key) {} + Key( long key ) : key(key) {} + + /* Returns the value used to represent the key. This value must be + * interpreted based on signedness. */ + long getVal() const { return key; }; + + bool isUpper() const { return ( 'A' <= key && key <= 'Z' ); } + bool isLower() const { return ( 'a' <= key && key <= 'z' ); } + bool isPrintable() const + { + return ( 7 <= key && key <= 13 ) || ( 32 <= key && key < 127 ); + } + + Key toUpper() const + { return Key( 'A' + ( key - 'a' ) ); } + Key toLower() const + { return Key( 'a' + ( key - 'A' ) ); } +}; + +struct CondKey +{ +private: + long key; + +public: + friend inline bool operator<( const CondKey key1, const CondKey key2 ); + friend inline bool operator>( const CondKey key1, const CondKey key2 ); + friend inline bool operator==( const CondKey key1, const CondKey key2 ); + friend inline CondKey operator+( const CondKey key1, const CondKey key2 ); + friend inline CondKey operator-( const CondKey key1, const CondKey key2 ); + + friend struct KeyOps; + + CondKey( ) {} + CondKey( const CondKey &key ) : key(key.key) {} + CondKey( long key ) : key(key) {} + + /* Returns the value used to represent the key. This value must be + * interpreted based on signedness. */ + long getVal() const { return key; }; + + bool isUpper() const { return ( 'A' <= key && key <= 'Z' ); } + bool isLower() const { return ( 'a' <= key && key <= 'z' ); } + bool isPrintable() const + { + return ( 7 <= key && key <= 13 ) || ( 32 <= key && key < 127 ); + } + + CondKey toUpper() const + { return CondKey( 'A' + ( key - 'a' ) ); } + CondKey toLower() const + { return CondKey( 'a' + ( key - 'A' ) ); } + + /* Decrement. Needed only for ranges. */ + inline void decrement(); + inline void increment(); +}; + +inline CondKey operator+(const CondKey key1, const CondKey key2) +{ + return CondKey( key1.key + key2.key ); +} + +inline CondKey operator-(const CondKey key1, const CondKey key2) +{ + return CondKey( key1.key - key2.key ); +} + +struct HostType +{ + const char *data1; + const char *data2; + const char *internalName; + bool isSigned; + bool isOrd; + bool isChar; + long long sMinVal; + long long sMaxVal; + unsigned long long uMinVal; + unsigned long long uMaxVal; + unsigned int size; +}; + +typedef void (*GenLineDirectiveT)( std::ostream &out, bool nld, int line, const char *file ); +typedef const char *(*DefaultOutFnT)( const char *inputFileName ); +typedef CodeGenData *(*MakeCodeGenT)( const HostLang *hostLang, const CodeGenArgs &args ); + +struct HostLang +{ + HostType *hostTypes; + int numHostTypes; + int defaultAlphType; + bool explicitUnsigned; + bool loopLabels; + + RagelBackend backend; + BackendFeature feature; + + MakeCodeGenT makeCodeGen; + DefaultOutFnT defaultOutFn; + GenLineDirectiveT genLineDirective; +}; + +void genLineDirectiveC( std::ostream &out, bool nld, int line, const char *file ); +void genLineDirectiveAsm( std::ostream &out, bool nld, int line, const char *file ); +void genLineDirectiveTrans( std::ostream &out, bool nld, int line, const char *file ); + +extern const HostLang hostLangC; +extern const HostLang hostLangAsm; + +HostType *findAlphType( const HostLang *hostLang, const char *s1 ); +HostType *findAlphType( const HostLang *hostLang, const char *s1, const char *s2 ); +HostType *findAlphTypeInternal( const HostLang *hostLang, const char *s1 ); + +const char *defaultOutFnC( const char *inputFileName ); +extern HostType hostTypesC[]; + +/* An abstraction of the key operators that manages key operations such as + * comparison and increment according the signedness of the key. */ +struct KeyOps +{ + /* Defaults to C "char" type: Signed 8 bit. */ + KeyOps() + : + isSigned(true), + explicitUnsigned(true), + minKey(CHAR_MIN), + maxKey(CHAR_MAX) + {} + + bool isSigned; + bool explicitUnsigned; + Key minKey, maxKey; + + void setAlphType( const HostLang *hostLang, const HostType *alphType ) + { + isSigned = alphType->isSigned; + explicitUnsigned = hostLang->explicitUnsigned; + + if ( isSigned ) { + minKey = (long) alphType->sMinVal; + maxKey = (long) alphType->sMaxVal; + } + else { + minKey = (long) alphType->uMinVal; + maxKey = (long) alphType->uMaxVal; + } + } + + /* Compute the distance between two keys. */ + Size span( Key key1, Key key2 ) + { + return isSigned ? + (unsigned long long)( + (long long)key2.key - + (long long)key1.key + 1) : + (unsigned long long)( + (unsigned long)key2.key) - + (unsigned long long)((unsigned long)key1.key) + 1; + } + + Size alphSize() + { return span( minKey, maxKey ); } + + inline bool lt( const Key key1, const Key key2 ) + { + return this->isSigned ? key1.key < key2.key : + (unsigned long)key1.key < (unsigned long)key2.key; + } + + inline bool le( const Key key1, const Key key2 ) + { + return this->isSigned ? key1.key <= key2.key : + (unsigned long)key1.key <= (unsigned long)key2.key; + } + + inline bool gt( const Key key1, const Key key2 ) + { + return this->isSigned ? key1.key > key2.key : + (unsigned long)key1.key > (unsigned long)key2.key; + } + + inline bool ge( const Key key1, const Key key2 ) + { + return this->isSigned ? key1.key >= key2.key : + (unsigned long)key1.key >= (unsigned long)key2.key; + } + + inline bool eq( const Key key1, const Key key2 ) + { + return key1.key == key2.key; + } + + inline bool ne( const Key key1, const Key key2 ) + { + return key1.key != key2.key; + } + + inline Key add(const Key key1, const Key key2) + { + /* FIXME: must be made aware of isSigned. */ + return Key( key1.key + key2.key ); + } + + inline Key sub(const Key key1, const Key key2) + { + /* FIXME: must be made aware of isSigned. */ + return Key( key1.key - key2.key ); + } + + /* Decrement. Needed only for ranges. */ + inline void decrement( Key &key ) + { + key.key = this->isSigned ? key.key - 1 : ((unsigned long)key.key)-1; + } + + /* Increment. Needed only for ranges. */ + inline void increment( Key &key ) + { + key.key = this->isSigned ? key.key+1 : ((unsigned long)key.key)+1; + } + + /* Returns the key casted to a long long. This form of the key does not + * require any signedness interpretation. */ + inline long long getLongLong( const Key &key ) + { + return this->isSigned ? (long long)key.key : (long long)(unsigned long)key.key; + } +}; + +/* CondKey */ + +inline bool operator<( const CondKey key1, const CondKey key2 ) +{ + return key1.key < key2.key; +} + +inline bool operator>( const CondKey key1, const CondKey key2 ) +{ + return key1.key > key2.key; +} + +inline bool operator==( const CondKey key1, const CondKey key2 ) +{ + return key1.key == key2.key; +} + +/* Increment. Needed only for ranges. */ +inline void CondKey::increment() +{ + key = key + 1; +} + + +/* Filter on the output stream that keeps track of the number of lines + * output. */ +class output_filter +: + public std::filebuf +{ +public: + output_filter( const char *fileName ) + : + fileName(fileName), + line(1), + level(0), + indent(false), + singleIndent(false) + {} + + virtual int sync(); + virtual std::streamsize xsputn( const char* s, std::streamsize n ); + + std::streamsize countAndWrite( const char* s, std::streamsize n ); + + const char *fileName; + int line; + int level; + bool indent; + bool singleIndent; +}; + +class nullbuf +: + public std::streambuf +{ +public: + virtual std::streamsize xsputn( const char * s, std::streamsize n ) + { return n; } + + virtual int overflow( int c ) + { return 1; } +}; + +class cfilebuf : public std::streambuf +{ +public: + cfilebuf( char *fileName, FILE* file ) : fileName(fileName), file(file) { } + char *fileName; + FILE *file; + + int sync() + { + fflush( file ); + return 0; + } + + int overflow( int c ) + { + if ( c != EOF ) + fputc( c, file ); + return 0; + } + + std::streamsize xsputn( const char* s, std::streamsize n ) + { + std::streamsize written = fwrite( s, 1, n, file ); + return written; + } +}; + +class costream : public std::ostream +{ +public: + costream( cfilebuf *b ) : + std::ostream(b), b(b) {} + + ~costream() + { delete b; } + + void fclose() + { ::fclose( b->file ); } + + cfilebuf *b; +}; + + +const char *findFileExtension( const char *stemFile ); +const char *fileNameFromStem( const char *stemFile, const char *suffix ); + +struct Export +{ + Export( std::string name, Key key ) + : name(name), key(key) {} + + std::string name; + Key key; + + Export *prev, *next; +}; + +typedef DList ExportList; + +struct exit_object { }; +extern exit_object endp; +void operator<<( std::ostream &out, exit_object & ); + +enum RagelFrontend +{ + KelbtBased, + ReduceBased +}; + +CodeGenData *makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ); +CodeGenData *makeCodeGenAsm( const HostLang *hostLang, const CodeGenArgs &args ); + +#endif diff --git a/src/libfsm/config.h.cmake.in b/src/libfsm/config.h.cmake.in new file mode 100644 index 00000000..ad4bf494 --- /dev/null +++ b/src/libfsm/config.h.cmake.in @@ -0,0 +1,13 @@ +/* config.h Generated from config.h.cmake.in by cmake */ + +#ifndef _COLM_CONFIG_H +#define _COLM_CONFIG_H + +#cmakedefine DEBUG 1 + +#cmakedefine HAVE_SYS_WAIT_H 1 + +#cmakedefine SIZEOF_INT @SIZEOF_INT@ +#cmakedefine SIZEOF_LONG @SIZEOF_LONG@ + +#endif /* _COLM_CONFIG_H */ diff --git a/src/libfsm/dot.cc b/src/libfsm/dot.cc new file mode 100644 index 00000000..f41ebc27 --- /dev/null +++ b/src/libfsm/dot.cc @@ -0,0 +1,392 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "dot.h" +#include "gendata.h" +#include "parsedata.h" + +using std::istream; +using std::ifstream; +using std::ostream; +using std::ios; +using std::cin; +using std::endl; + +void GraphvizDotGen::key( Key key ) +{ + if ( id->displayPrintables && key.isPrintable() ) { + // Output values as characters, ensuring we escape the quote (") character + char cVal = (char) key.getVal(); + switch ( cVal ) { + case '"': case '\\': + out << "'\\" << cVal << "'"; + break; + case '\a': + out << "'\\\\a'"; + break; + case '\b': + out << "'\\\\b'"; + break; + case '\t': + out << "'\\\\t'"; + break; + case '\n': + out << "'\\\\n'"; + break; + case '\v': + out << "'\\\\v'"; + break; + case '\f': + out << "'\\\\f'"; + break; + case '\r': + out << "'\\\\r'"; + break; + case ' ': + out << "SP"; + break; + default: + out << "'" << cVal << "'"; + break; + } + } + else { + if ( keyOps->isSigned ) + out << key.getVal(); + else + out << (unsigned long) key.getVal(); + } +} + +void GraphvizDotGen::condSpec( CondSpace *condSpace, long condVals ) +{ + if ( condSpace != 0 ) { + out << "("; + for ( CondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + bool set = condVals & (1 << csi.pos()); + if ( !set ) + out << "!"; + (*csi)->actionName( out ); + if ( !csi.last() ) + out << ", "; + } + out << ")"; + } +} + +void GraphvizDotGen::onChar( Key lowKey, Key highKey, CondSpace *condSpace, long condVals ) +{ + /* Output the key. Possibly a range. */ + key( lowKey ); + if ( keyOps->ne( highKey, lowKey ) ) { + out << ".."; + key( highKey ); + } + + condSpec( condSpace, condVals ); +} + + +void GraphvizDotGen::fromStateAction( StateAp *fromState ) +{ + int n = 0; + ActionTable *actionTables[3] = { 0, 0, 0 }; + + if ( fromState->fromStateActionTable.length() != 0 ) + actionTables[n++] = &fromState->fromStateActionTable; + + + /* Loop the existing actions and write out what's there. */ + for ( int a = 0; a < n; a++ ) { + for ( ActionTable::Iter actIt = actionTables[a]->first(); actIt.lte(); actIt++ ) { + Action *action = actIt->value; + action->actionName( out ); + if ( a < n-1 || !actIt.last() ) + out << ", "; + } + } + + if ( n > 0 ) + out << " / "; +} + +void GraphvizDotGen::transAction( StateAp *fromState, TransData *trans ) +{ + int n = 0; + ActionTable *actionTables[3] = { 0, 0, 0 }; + + if ( trans->actionTable.length() != 0 ) + actionTables[n++] = &trans->actionTable; + if ( trans->toState != 0 && trans->toState->toStateActionTable.length() != 0 ) + actionTables[n++] = &trans->toState->toStateActionTable; + + if ( n > 0 ) + out << " / "; + + /* Loop the existing actions and write out what's there. */ + for ( int a = 0; a < n; a++ ) { + for ( ActionTable::Iter actIt = actionTables[a]->first(); actIt.lte(); actIt++ ) { + Action *action = actIt->value; + action->actionName( out ); + if ( a < n-1 || !actIt.last() ) + out << ", "; + } + } +} + +void GraphvizDotGen::action( ActionTable *actionTable ) +{ + /* The action. */ + out << " / "; + for ( ActionTable::Iter actIt = actionTable->first(); actIt.lte(); actIt++ ) { + Action *action = actIt->value; + action->actionName( out ); + if ( !actIt.last() ) + out << ", "; + } +} + +void GraphvizDotGen::transList( StateAp *state ) +{ + /* Build the set of unique transitions out of this state. */ + RedTransSet stTransSet; + for ( TransList::Iter tel = state->outList; tel.lte(); tel++ ) { + if ( tel->plain() ) { + TransDataAp *tdap = tel->tdap(); + + /* Write out the from and to states. */ + out << "\t" << state->alg.stateNum << " -> "; + + if ( tdap->toState == 0 ) + out << "err_" << state->alg.stateNum; + else + out << tdap->toState->alg.stateNum; + + /* Begin the label. */ + out << " [ label = \""; + + fromStateAction( state ); + + onChar( tel->lowKey, tel->highKey, 0, 0 ); + + /* Write the action and close the transition. */ + transAction( state, tdap ); + + out << "\" ];\n"; + } + else { + for ( CondList::Iter ctel = tel->tcap()->condList; ctel.lte(); ctel++ ) { + /* Write out the from and to states. */ + out << "\t" << state->alg.stateNum << " -> "; + + if ( ctel->toState == 0 ) + out << "err_" << state->alg.stateNum; + else + out << ctel->toState->alg.stateNum; + + /* Begin the label. */ + out << " [ label = \""; + + fromStateAction( state ); + + onChar( tel->lowKey, tel->highKey, tel->condSpace, ctel->key.getVal() ); + + /* Write the action and close the transition. */ + transAction( state, ctel ); + out << "\" ];\n"; + } + } + } + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter nfa = *state->nfaOut; nfa.lte(); nfa++ ) { + out << "\t" << state->alg.stateNum << + " -> " << nfa->toState->alg.stateNum << + " [ label = \"EP," << nfa->order << " "; + + fromStateAction( state ); + +// if ( nfa->popTest.length() > 0 || +// nfa->popAction.length() > 0 || +// nfa->popCondKeys.length() > 0 ) +// { +// out << " / "; +// } + + if ( nfa->popCondKeys.length() > 0 ) { + for ( CondKeySet::Iter key = nfa->popCondKeys; key.lte(); key++ ) { + out << "("; + long condVals = *key; + for ( CondSet::Iter csi = nfa->popCondSpace->condSet; csi.lte(); csi++ ) { + bool set = condVals & (1 << csi.pos()); + if ( !set ) + out << "!"; + (*csi)->actionName( out ); + if ( !csi.last() ) + out << ", "; + } + out << ") "; + } + } + + if ( nfa->popAction.length() > 0 ) { + for ( ActionTable::Iter pa = nfa->popAction; pa.lte(); pa++ ) { + pa->value->actionName( out ); + if ( !pa.last() ) + out << ","; + } + } + + if ( nfa->popTest.length() > 0 ) { + for ( ActionTable::Iter pt = nfa->popTest; pt.lte(); pt++ ) { + pt->value->actionName( out ); + if ( !pt.last() ) + out << ","; + } + } + + out << "\" ];"; + } + } +} + +bool GraphvizDotGen::makeNameInst( std::string &res, NameInst *nameInst ) +{ + bool written = false; + if ( nameInst->parent != 0 ) + written = makeNameInst( res, nameInst->parent ); + + if ( !nameInst->name.empty() ) { + if ( written ) + res += '_'; + res += nameInst->name; + written = true; + } + + return written; +} + +void GraphvizDotGen::write( ) +{ + out << + "digraph " << fsmName << " {\n" + " rankdir=LR;\n"; + + /* Define the psuedo states. Transitions will be done after the states + * have been defined as either final or not final. */ + out << " node [ shape = point ];\n"; + + if ( fsm->startState != 0 ) + out << " ENTRY;\n"; + + /* Psuedo states for entry points in the entry map. */ + for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { + StateAp *state = en->value; + out << " en_" << state->alg.stateNum << ";\n"; + } + + /* Psuedo states for final states with eof actions. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + //if ( st->eofTrans != 0 && st->eofTrans->action != 0 ) + // out << " eof_" << st->id << ";\n"; + if ( st->eofActionTable.length() > 0 ) + out << " eof_" << st->alg.stateNum << ";\n"; + } + + out << " node [ shape = circle, height = 0.2 ];\n"; + + /* Psuedo states for states whose default actions go to error. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + bool needsErr = false; + for ( TransList::Iter tel = st->outList; tel.lte(); tel++ ) { + if ( tel->plain() ) { + if ( tel->tdap()->toState == 0 ) { + needsErr = true; + break; + } + } + else { + for ( CondList::Iter ctel = tel->tcap()->condList; ctel.lte(); ctel++ ) { + if ( ctel->toState == 0 ) { + needsErr = true; + break; + } + } + } + } + + if ( needsErr ) + out << " err_" << st->alg.stateNum << " [ label=\"\"];\n"; + } + + /* Attributes common to all nodes, plus double circle for final states. */ + out << " node [ fixedsize = true, height = 0.65, shape = doublecircle ];\n"; + + /* List Final states. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + if ( st->isFinState() ) + out << " " << st->alg.stateNum << ";\n"; + } + + /* List transitions. */ + out << " node [ shape = circle ];\n"; + + /* Walk the states. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) + transList( st ); + + /* Transitions into the start state. */ + if ( fsm->startState != 0 ) + out << " ENTRY -> " << fsm->startState->alg.stateNum << " [ label = \"IN\" ];\n"; + + for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { + NameInst *nameInst = fsmCtx->nameIndex[en->key]; + std::string name; + makeNameInst( name, nameInst ); + StateAp *state = en->value; + out << " en_" << state->alg.stateNum << + " -> " << state->alg.stateNum << + " [ label = \"" << name << "\" ];\n"; + } + + /* Out action transitions. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + if ( st->eofActionTable.length() != 0 ) { + out << " " << st->alg.stateNum << " -> eof_" << + st->alg.stateNum << " [ label = \"EOF"; + + for ( CondKeySet::Iter i = st->outCondKeys; i.lte(); i++ ) { + if ( i.pos() > 0 ) + out << "|"; + condSpec( st->outCondSpace, *i ); + } + + action( &st->eofActionTable ); + out << "\" ];\n"; + } + } + + out << + "}\n"; +} + diff --git a/src/libfsm/dot.h b/src/libfsm/dot.h new file mode 100644 index 00000000..13f53532 --- /dev/null +++ b/src/libfsm/dot.h @@ -0,0 +1,53 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GVDOTGEN_H +#define _GVDOTGEN_H + +#include +#include "gendata.h" + + +class GraphvizDotGen : public RedBase +{ +public: + GraphvizDotGen( FsmGbl *id, FsmCtx *fsmCtx, FsmAp *fsm, + std::string fsmName, int machineId, std::ostream &out ) + : + RedBase(id, fsmCtx, fsm, fsmName, machineId), + out(out) + {} + + bool makeNameInst( std::string &res, NameInst *nameInst ); + void action( ActionTable *actionTable ); + void transAction( StateAp *fromState, TransData *trans ); + void key( Key key ); + void condSpec( CondSpace *condSpace, long condVals ); + void onChar( Key lowKey, Key highKey, CondSpace *condSpace, long condVals ); + void transList( StateAp *state ); + void write(); + void fromStateAction( StateAp *fromState ); + + ostream &out; +}; + +#endif diff --git a/src/libfsm/flat.cc b/src/libfsm/flat.cc new file mode 100644 index 00000000..8cda30db --- /dev/null +++ b/src/libfsm/flat.cc @@ -0,0 +1,576 @@ +/* + * Copyright 2004-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "flat.h" +#include "redfsm.h" +#include "gendata.h" + +void Flat::genAnalysis() +{ + redFsm->sortByStateId(); + + /* Choose default transitions and the single transition. */ + redFsm->chooseDefaultSpan(); + + /* Do flat expand. */ + redFsm->makeFlatClass(); + + /* If any errors have occured in the input file then don't write anything. */ + if ( red->id->errorCount > 0 ) + return; + + /* Anlayze Machine will find the final action reference counts, among other + * things. We will use these in reporting the usage of fsm directives in + * action code. */ + red->analyzeMachine(); + + setKeyType(); + + /* Run the analysis pass over the table data. */ + setTableState( TableArray::AnalyzePass ); + tableDataPass(); + + /* Switch the tables over to the code gen mode. */ + setTableState( TableArray::GeneratePass ); +} + +void Flat::tableDataPass() +{ + if ( type == Flat::Loop ) { + if ( redFsm->anyActions() ) + taActions(); + } + + taKeys(); + taCharClass(); + taFlatIndexOffset(); + + taIndices(); + taIndexDefaults(); + taTransCondSpaces(); + + if ( red->condSpaceList.length() > 0 ) + taTransOffsets(); + + taCondTargs(); + taCondActions(); + + taToStateActions(); + taFromStateActions(); + taEofConds(); + taEofActions(); + taEofTrans(); + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void Flat::writeData() +{ + if ( type == Flat::Loop ) { + /* If there are any transtion functions then output the array. If there + * are none, don't bother emitting an empty array that won't be used. */ + if ( redFsm->anyActions() ) + taActions(); + } + + taKeys(); + taCharClass(); + taFlatIndexOffset(); + + taIndices(); + taIndexDefaults(); + taTransCondSpaces(); + if ( red->condSpaceList.length() > 0 ) + taTransOffsets(); + taCondTargs(); + taCondActions(); + + if ( redFsm->anyToStateActions() ) + taToStateActions(); + + if ( redFsm->anyFromStateActions() ) + taFromStateActions(); + + taEofConds(); + + if ( redFsm->anyEofActions() ) + taEofActions(); + + if ( redFsm->anyEofTrans() ) + taEofTrans(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); + + STATE_IDS(); +} + + +void Flat::setKeyType() +{ + transKeys.setType( ALPH_TYPE(), alphType->size, alphType->isChar ); + transKeys.isSigned = keyOps->isSigned; +} + +void Flat::setTableState( TableArray::State state ) +{ + for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { + TableArray *tableArray = *i; + tableArray->setState( state ); + } +} + +void Flat::taFlatIndexOffset() +{ + flatIndexOffset.start(); + + int curIndOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Write the index offset. */ + flatIndexOffset.value( curIndOffset ); + + /* Move the index offset ahead. */ + if ( st->transList != 0 ) + curIndOffset += ( st->high - st->low + 1 ); + } + + flatIndexOffset.finish(); +} + +void Flat::taCharClass() +{ + charClass.start(); + + if ( redFsm->classMap != 0 ) { + long long maxSpan = keyOps->span( redFsm->lowKey, redFsm->highKey ); + + for ( long long pos = 0; pos < maxSpan; pos++ ) + charClass.value( redFsm->classMap[pos] ); + } + + charClass.finish(); +} + +void Flat::taToStateActions() +{ + toStateActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Write any eof action. */ + TO_STATE_ACTION(st); + } + + toStateActions.finish(); +} + +void Flat::taFromStateActions() +{ + fromStateActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Write any eof action. */ + FROM_STATE_ACTION( st ); + } + + fromStateActions.finish(); +} + +void Flat::taEofActions() +{ + eofActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Write any eof action. */ + EOF_ACTION( st ); + } + + eofActions.finish(); +} + +void Flat::taEofConds() +{ + /* + * EOF Cond Spaces + */ + eofCondSpaces.start(); + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->outCondSpace != 0 ) + eofCondSpaces.value( st->outCondSpace->condSpaceId ); + else + eofCondSpaces.value( -1 ); + } + eofCondSpaces.finish(); + + /* + * EOF Cond Key Indixes + */ + eofCondKeyOffs.start(); + + int curOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long off = 0; + if ( st->outCondSpace != 0 ) { + off = curOffset; + curOffset += st->outCondKeys.length(); + } + eofCondKeyOffs.value( off ); + } + + eofCondKeyOffs.finish(); + + /* + * EOF Cond Key Lengths. + */ + eofCondKeyLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long len = 0; + if ( st->outCondSpace != 0 ) + len = st->outCondKeys.length(); + eofCondKeyLens.value( len ); + } + + eofCondKeyLens.finish(); + + /* + * EOF Cond Keys + */ + eofCondKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->outCondSpace != 0 ) { + for ( int c = 0; c < st->outCondKeys.length(); c++ ) { + CondKey key = st->outCondKeys[c]; + eofCondKeys.value( key.getVal() ); + } + } + } + + eofCondKeys.finish(); +} + +void Flat::taEofTrans() +{ + /* Transitions must be written ordered by their id. */ + RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) + transPtrs[trans->id] = trans; + + long *transPos = new long[redFsm->transSet.length()]; + for ( int t = 0; t < redFsm->transSet.length(); t++ ) { + /* Save the position. Needed for eofTargs. */ + RedTransAp *trans = transPtrs[t]; + transPos[trans->id] = t; + } + + eofTrans.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long trans = 0; + + if ( st->eofTrans != 0 ) + trans = transPos[st->eofTrans->id] + 1; + + eofTrans.value( trans ); + } + + eofTrans.finish(); + + delete[] transPtrs; + delete[] transPos; +} + +void Flat::taKeys() +{ + transKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->transList ) { + /* Emit just low key and high key. */ + transKeys.value( st->low ); + transKeys.value( st->high ); + } + else { + /* Emit an impossible range so the driver fails the lookup. */ + transKeys.value( 1 ); + transKeys.value( 0 ); + } + } + + transKeys.finish(); +} + +void Flat::taIndices() +{ + indices.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->transList != 0 ) { + long long span = st->high - st->low + 1; + for ( long long pos = 0; pos < span; pos++ ) + indices.value( st->transList[pos]->id ); + } + } + + indices.finish(); +} + +void Flat::taIndexDefaults() +{ + indexDefaults.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) + indexDefaults.value( st->defTrans->id ); + else + indexDefaults.value( 0 ); + } + + indexDefaults.finish(); +} + + +void Flat::taTransCondSpaces() +{ + transCondSpaces.start(); + + /* Transitions must be written ordered by their id. */ + RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + transPtrs[trans->id] = trans; + } + + /* Keep a count of the num of items in the array written. */ + for ( int t = 0; t < redFsm->transSet.length(); t++ ) { + /* Save the position. Needed for eofTargs. */ + RedTransAp *trans = transPtrs[t]; + + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + delete[] transPtrs; + + transCondSpaces.finish(); +} + +void Flat::taTransOffsets() +{ + transOffsets.start(); + + /* Transitions must be written ordered by their id. */ + RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) + transPtrs[trans->id] = trans; + + /* Keep a count of the num of items in the array written. */ + int curOffset = 0; + for ( int t = 0; t < redFsm->transSet.length(); t++ ) { + /* Save the position. Needed for eofTargs. */ + RedTransAp *trans = transPtrs[t]; + + transOffsets.value( curOffset ); + + curOffset += trans->condFullSize(); + } + + delete[] transPtrs; + + transOffsets.finish(); +} + +void Flat::taCondTargs() +{ + condTargs.start(); + + /* Transitions must be written ordered by their id. */ + RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) + transPtrs[trans->id] = trans; + + /* Keep a count of the num of items in the array written. */ + for ( int t = 0; t < redFsm->transSet.length(); t++ ) { + /* Save the position. Needed for eofTargs. */ + RedTransAp *trans = transPtrs[t]; + + long fullSize = trans->condFullSize(); + RedCondPair **fullPairs = new RedCondPair*[fullSize]; + for ( long k = 0; k < fullSize; k++ ) + fullPairs[k] = trans->errCond(); + + for ( int c = 0; c < trans->numConds(); c++ ) + fullPairs[trans->outCondKey( c ).getVal()] = trans->outCond( c ); + + for ( int k = 0; k < fullSize; k++ ) { + RedCondPair *cond = fullPairs[k]; + condTargs.value( cond->targ->id ); + } + + delete[] fullPairs; + } + delete[] transPtrs; + + condTargs.finish(); +} + +void Flat::taCondActions() +{ + condActions.start(); + + /* Transitions must be written ordered by their id. */ + RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()]; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) + transPtrs[trans->id] = trans; + + /* Keep a count of the num of items in the array written. */ + for ( int t = 0; t < redFsm->transSet.length(); t++ ) { + /* Save the position. Needed for eofTargs. */ + RedTransAp *trans = transPtrs[t]; + + long fullSize = trans->condFullSize(); + RedCondPair **fullPairs = new RedCondPair*[fullSize]; + for ( long k = 0; k < fullSize; k++ ) + fullPairs[k] = trans->errCond(); + + for ( int c = 0; c < trans->numConds(); c++ ) + fullPairs[trans->outCondKey( c ).getVal()] = trans->outCond( c ); + + for ( int k = 0; k < fullSize; k++ ) { + RedCondPair *cond = fullPairs[k]; + COND_ACTION( cond ); + } + delete[] fullPairs; + } + delete[] transPtrs; + + condActions.finish(); +} + +/* Write out the array of actions. */ +void Flat::taActions() +{ + actions.start(); + + /* Add in the the empty actions array. */ + actions.value( 0 ); + + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Length first. */ + actions.value( act->key.length() ); + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) + actions.value( item->value->actionId ); + } + + actions.finish(); +} + +void Flat::taNfaTargs() +{ + nfaTargs.start(); + + /* Offset of zero means no NFA targs, put a filler there. */ + nfaTargs.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaTargs.value( st->nfaTargs->length() ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + nfaTargs.value( targ->state->id ); + } + } + + nfaTargs.finish(); +} + +/* These need to mirror nfa targs. */ +void Flat::taNfaPushActions() +{ + nfaPushActions.start(); + + nfaPushActions.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaPushActions.value( 0 ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_PUSH_ACTION( targ ); + } + } + + nfaPushActions.finish(); +} + +void Flat::taNfaPopTrans() +{ + nfaPopTrans.start(); + + nfaPopTrans.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + + nfaPopTrans.value( 0 ); + + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_POP_TEST( targ ); + } + } + + nfaPopTrans.finish(); +} + + +void Flat::taNfaOffsets() +{ + nfaOffsets.start(); + + /* Offset of zero means no NFA targs, real targs start at 1. */ + long offset = 1; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs == 0 ) { + nfaOffsets.value( 0 ); + } + else { + nfaOffsets.value( offset ); + offset += 1 + st->nfaTargs->length(); + } + } + + nfaOffsets.finish(); +} + + + + + + + + diff --git a/src/libfsm/flat.h b/src/libfsm/flat.h new file mode 100644 index 00000000..1e54f5ab --- /dev/null +++ b/src/libfsm/flat.h @@ -0,0 +1,94 @@ +/* + * Copyright 2004-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _C_FLAT_H +#define _C_FLAT_H + +#include +#include "codegen.h" +#include "tables.h" + +/* Forwards. */ +struct CodeGenData; +struct NameInst; +struct RedTransAp; +struct RedStateAp; + +class Flat + : public virtual Tables +{ +protected: + enum Type { + Loop = 1, Exp + }; + +public: + Flat( const CodeGenArgs &args, Type type ) + : + Tables( args ), + type(type) + {} + + virtual ~Flat() { } + +protected: + Type type; + + void taKeys(); + void taKeySpans(); + void taCharClass(); + void taActions(); + void taFlatIndexOffset(); + void taIndices(); + void taIndexDefaults(); + void taTransCondSpaces(); + void taTransOffsets(); + void taCondTargs(); + void taCondActions(); + void taToStateActions(); + void taFromStateActions(); + void taEofActions(); + void taEofTrans(); + void taEofConds(); + void taNfaTargs(); + void taNfaOffsets(); + void taNfaPushActions(); + void taNfaPopTrans(); + + void setKeyType(); + + std::ostream &INDICES(); + std::ostream &TRANS_COND_SPACES(); + std::ostream &TRANS_OFFSETS(); + std::ostream &TRANS_LENGTHS(); + std::ostream &COND_KEYS(); + std::ostream &COND_TARGS(); + std::ostream &COND_ACTIONS(); + + virtual void setTableState( TableArray::State ); + + virtual void genAnalysis(); + virtual void tableDataPass(); + virtual void writeData(); +}; + +#endif diff --git a/src/libfsm/flatbreak.cc b/src/libfsm/flatbreak.cc new file mode 100644 index 00000000..08342625 --- /dev/null +++ b/src/libfsm/flatbreak.cc @@ -0,0 +1,118 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "flatbreak.h" + +void FlatBreak::LOCATE_TRANS() +{ + if ( redFsm->classMap == 0 ) { + out << + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n"; + } + else { + long lowKey = redFsm->lowKey.getVal(); + long highKey = redFsm->highKey.getVal(); + + bool limitLow = keyOps->eq( lowKey, keyOps->minKey ); + bool limitHigh = keyOps->eq( highKey, keyOps->maxKey ); + + out << + " " << keys << " = " << OFFSET( ARR_REF( transKeys ), "(" + vCS() + "<<1)" ) << ";\n" + " " << inds << " = " << OFFSET( ARR_REF( indices ), + ARR_REF( flatIndexOffset ) + "[" + vCS() + "]" ) << ";\n" + "\n"; + + if ( !limitLow || !limitHigh ) { + out << " if ( "; + + if ( !limitHigh ) + out << GET_KEY() << " <= " << highKey; + + if ( !limitHigh && !limitLow ) + out << " && "; + + if ( !limitLow ) + out << GET_KEY() << " >= " << lowKey; + + out << " ) {\n"; + } + + out << + " " << ic << " = " << CAST("int") << ARR_REF( charClass ) << "[" << CAST("int") << GET_KEY() << + " - " << lowKey << "];\n" + " if ( " << ic << " <= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "+1" ) << " && " << + "" << ic << " >= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "" ) << " )\n" + " " << trans << " = " << CAST(UINT()) << DEREF( ARR_REF( indices ), + inds.ref() + " + " + CAST("int") + "( " + ic.ref() + " - " + CAST("int") + + DEREF( ARR_REF( transKeys ), keys.ref() + "" ) + " ) " ) << "; \n" + " else\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << + "[" << vCS() << "]" << ";\n"; + + if ( !limitLow || !limitHigh ) { + out << + " }\n" + " else {\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n" + " }\n" + "\n"; + } + } + + +} + +void FlatBreak::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + out << + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + out << + " switch ( " << ARR_REF( transCondSpaces ) << "[" << trans << "] ) {\n" + "\n"; + + for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { + GenCondSpace *condSpace = csi; + if ( condSpace->numTransRefs > 0 ) { + out << " " << CASE( STR(condSpace->condSpaceId) ) << " {\n"; + for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + out << "if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + out << " ) " << cpc << " += " << condValOffset << ";\n"; + } + + out << + " " << CEND() << "\n}\n"; + } + } + + out << + " }\n" + " " << cond << " += " << CAST( UINT() ) << "" << cpc << ";\n"; + } +} diff --git a/src/libfsm/flatbreak.h b/src/libfsm/flatbreak.h new file mode 100644 index 00000000..23400000 --- /dev/null +++ b/src/libfsm/flatbreak.h @@ -0,0 +1,72 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_FLATBREAK_H +#define RAGEL_FLATBREAK_H + +#include "flat.h" +#include "actloop.h" +#include "actexp.h" + +struct FlatBreak +: + public Flat, public TabBreak +{ + FlatBreak( const CodeGenArgs &args, Flat::Type type ) + : + Tables( args ), + Flat( args, type ), + TabBreak( args ) + {} + + void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class FlatBreakLoop + : public FlatBreak, public ActLoop +{ +public: + FlatBreakLoop( const CodeGenArgs &args ) + : + Tables( args ), + FlatBreak( args, Flat::Loop ), + ActLoop( args ) + {} +}; + +/* + * FlatBreakExp + */ +class FlatBreakExp + : public FlatBreak, public ActExp +{ +public: + FlatBreakExp( const CodeGenArgs &args ) + : + Tables( args ), + FlatBreak( args, Flat::Exp ), + ActExp( args ) + {} +}; + +#endif diff --git a/src/libfsm/flatgoto.cc b/src/libfsm/flatgoto.cc new file mode 100644 index 00000000..c3206191 --- /dev/null +++ b/src/libfsm/flatgoto.cc @@ -0,0 +1,118 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "flatgoto.h" + +void FlatGoto::LOCATE_TRANS() +{ + if ( redFsm->classMap == 0 ) { + out << + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n"; + } + else { + long lowKey = redFsm->lowKey.getVal(); + long highKey = redFsm->highKey.getVal(); + + bool limitLow = keyOps->eq( lowKey, keyOps->minKey ); + bool limitHigh = keyOps->eq( highKey, keyOps->maxKey ); + + out << + " " << keys << " = " << OFFSET( ARR_REF( transKeys ), "(" + vCS() + "<<1)" ) << ";\n" + " " << inds << " = " << OFFSET( ARR_REF( indices ), + ARR_REF( flatIndexOffset ) + "[" + vCS() + "]" ) << ";\n" + "\n"; + + if ( !limitLow || !limitHigh ) { + out << " if ( "; + + if ( !limitHigh ) + out << GET_KEY() << " <= " << highKey; + + if ( !limitHigh && !limitLow ) + out << " && "; + + if ( !limitLow ) + out << GET_KEY() << " >= " << lowKey; + + out << " ) {\n"; + } + + out << + " " << ic << " = " << CAST("int") << ARR_REF( charClass ) << "[" << CAST("int") << GET_KEY() << + " - " << lowKey << "];\n" + " if ( " << ic << " <= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "+1" ) << " && " << + "" << ic << " >= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "" ) << " )\n" + " " << trans << " = " << CAST(UINT()) << DEREF( ARR_REF( indices ), + inds.ref() + " + " + CAST("int") + "( " + ic.ref() + " - " + CAST("int") + + DEREF( ARR_REF( transKeys ), keys.ref() + "" ) + " ) " ) << "; \n" + " else\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << + "[" << vCS() << "]" << ";\n"; + + if ( !limitLow || !limitHigh ) { + out << + " }\n" + " else {\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n" + " }\n" + "\n"; + } + } + +} + + +void FlatGoto::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + out << + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + out << + " switch ( " << ARR_REF( transCondSpaces ) << "[" << trans << "] ) {\n" + "\n"; + + for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { + GenCondSpace *condSpace = csi; + if ( condSpace->numTransRefs > 0 ) { + out << " " << CASE( STR(condSpace->condSpaceId) ) << " {\n"; + for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + out << "if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + out << " ) " << cpc << " += " << condValOffset << ";\n"; + } + + out << + " " << CEND() << "\n}\n"; + } + } + + out << + " }\n" + " " << cond << " += " << CAST( UINT() ) << "" << cpc << ";\n"; + } +} diff --git a/src/libfsm/flatgoto.h b/src/libfsm/flatgoto.h new file mode 100644 index 00000000..e21b6cd9 --- /dev/null +++ b/src/libfsm/flatgoto.h @@ -0,0 +1,72 @@ +/* + * Copyright 2018-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_FLATGOTO_H +#define RAGEL_FLATGOTO_H + +#include "flat.h" +#include "actloop.h" +#include "actexp.h" + +struct FlatGoto +: + public Flat, public TabGoto +{ + FlatGoto( const CodeGenArgs &args, Flat::Type type ) + : + Tables( args ), + Flat( args, type ), + TabGoto( args ) + {} + + void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class FlatGotoLoop + : public FlatGoto, public ActLoop +{ +public: + FlatGotoLoop( const CodeGenArgs &args ) + : + Tables( args ), + FlatGoto( args, Flat::Loop ), + ActLoop( args ) + {} +}; + +/* + * FlatGotoExp + */ +class FlatGotoExp + : public FlatGoto, public ActExp +{ +public: + FlatGotoExp( const CodeGenArgs &args ) + : + Tables( args ), + FlatGoto( args, Flat::Exp ), + ActExp( args ) + {} +}; + +#endif diff --git a/src/libfsm/flatvar.cc b/src/libfsm/flatvar.cc new file mode 100644 index 00000000..40902940 --- /dev/null +++ b/src/libfsm/flatvar.cc @@ -0,0 +1,117 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "flatvar.h" +#include "parsedata.h" + +void FlatVar::LOCATE_TRANS() +{ + if ( redFsm->classMap == 0 ) { + out << + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n"; + } + else { + long lowKey = redFsm->lowKey.getVal(); + long highKey = redFsm->highKey.getVal(); + + bool limitLow = keyOps->eq( lowKey, keyOps->minKey ); + bool limitHigh = keyOps->eq( highKey, keyOps->maxKey ); + + out << + " " << keys << " = " << OFFSET( ARR_REF( transKeys ), "(" + vCS() + "<<1)" ) << ";\n" + " " << inds << " = " << OFFSET( ARR_REF( indices ), + ARR_REF( flatIndexOffset ) + "[" + vCS() + "]" ) << ";\n" + "\n"; + + if ( !limitLow || !limitHigh ) { + out << " if ( "; + + if ( !limitHigh ) + out << GET_KEY() << " <= " << highKey; + + if ( !limitHigh && !limitLow ) + out << " && "; + + if ( !limitLow ) + out << GET_KEY() << " >= " << lowKey; + + out << " ) {\n"; + } + + out << + " " << ic << " = " << CAST("int") << ARR_REF( charClass ) << "[" << CAST("int") << GET_KEY() << + " - " << lowKey << "];\n" + " if ( " << ic << " <= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "+1" ) << " && " << + "" << ic << " >= " << CAST("int") << DEREF( ARR_REF( transKeys ), keys.ref() + "" ) << " )\n" + " " << trans << " = " << CAST(UINT()) << DEREF( ARR_REF( indices ), + inds.ref() + " + " + CAST("int") + "( " + ic.ref() + " - " + CAST("int") + + DEREF( ARR_REF( transKeys ), keys.ref() + "" ) + " ) " ) << "; \n" + " else\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << + "[" << vCS() << "]" << ";\n"; + + if ( !limitLow || !limitHigh ) { + out << + " }\n" + " else {\n" + " " << trans << " = " << CAST(UINT()) << ARR_REF( indexDefaults ) << "[" << vCS() << "]" << ";\n" + " }\n" + "\n"; + } + } +} + +void FlatVar::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + out << + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + out << + " switch ( " << ARR_REF( transCondSpaces ) << "[" << trans << "] ) {\n" + "\n"; + + for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { + GenCondSpace *condSpace = csi; + if ( condSpace->numTransRefs > 0 ) { + out << " " << CASE( STR(condSpace->condSpaceId) ) << " {\n"; + for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + out << "if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + out << " ) " << cpc << " += " << condValOffset << ";\n"; + } + + out << + " " << CEND() << "\n}\n"; + } + } + + out << + " }\n" + " " << cond << " += " << CAST( UINT() ) << "" << cpc << ";\n"; + } +} diff --git a/src/libfsm/flatvar.h b/src/libfsm/flatvar.h new file mode 100644 index 00000000..9cd80eab --- /dev/null +++ b/src/libfsm/flatvar.h @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_FLATVAR_H +#define RAGEL_FLATVAR_H + +#include "flat.h" +#include "actloop.h" +#include "actexp.h" + +struct FlatVar +: + public Flat, public TabVar +{ + FlatVar( const CodeGenArgs &args, Flat::Type type ) + : + Tables( args ), + Flat( args, type ), + TabVar( args ) + {} + + void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class FlatVarLoop + : public FlatVar, public ActLoop +{ +public: + FlatVarLoop( const CodeGenArgs &args ) + : + Tables( args ), + FlatVar( args, Flat::Loop ), + ActLoop( args ) + {} +}; + +class FlatVarExp +: + public FlatVar, public ActExp +{ +public: + FlatVarExp( const CodeGenArgs &args ) + : + Tables( args ), + FlatVar( args, Flat::Exp ), + ActExp( args ) + {} +}; + +#endif diff --git a/src/libfsm/fsmap.cc b/src/libfsm/fsmap.cc new file mode 100644 index 00000000..98863f52 --- /dev/null +++ b/src/libfsm/fsmap.cc @@ -0,0 +1,1200 @@ +/* + * Copyright 2002-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "fsmgraph.h" +#include +using std::endl; + +/* Insert an action into an action table. */ +void ActionTable::setAction( int ordering, Action *action ) +{ + /* Multi-insert in case specific instances of an action appear in a + * transition more than once. */ + insertMulti( ordering, action ); +} + +/* Set all the action from another action table in this table. */ +void ActionTable::setActions( const ActionTable &other ) +{ + for ( ActionTable::Iter action = other; action.lte(); action++ ) + insertMulti( action->key, action->value ); +} + +void ActionTable::setActions( int *orderings, Action **actions, int nActs ) +{ + for ( int a = 0; a < nActs; a++ ) + insertMulti( orderings[a], actions[a] ); +} + +bool ActionTable::hasAction( Action *action ) +{ + for ( int a = 0; a < length(); a++ ) { + if ( data[a].value == action ) + return true; + } + return false; +} + +/* Insert an action into an action table. */ +void LmActionTable::setAction( int ordering, FsmLongestMatchPart *action ) +{ + /* Multi-insert in case specific instances of an action appear in a + * transition more than once. */ + insertMulti( ordering, action ); +} + +/* Set all the action from another action table in this table. */ +void LmActionTable::setActions( const LmActionTable &other ) +{ + for ( LmActionTable::Iter action = other; action.lte(); action++ ) + insertMulti( action->key, action->value ); +} + +void ErrActionTable::setAction( int ordering, Action *action, int transferPoint ) +{ + insertMulti( ErrActionTableEl( action, ordering, transferPoint ) ); +} + +void ErrActionTable::setActions( const ErrActionTable &other ) +{ + for ( ErrActionTable::Iter act = other; act.lte(); act++ ) + insertMulti( ErrActionTableEl( act->action, act->ordering, act->transferPoint ) ); +} + +/* Insert a priority into this priority table. Looks out for priorities on + * duplicate keys. */ +void PriorTable::setPrior( int ordering, PriorDesc *desc ) +{ + PriorEl *lastHit = 0; + PriorEl *insed = insert( PriorEl(ordering, desc), &lastHit ); + if ( insed == 0 ) { + /* This already has a priority on the same key as desc. Overwrite the + * priority if the ordering is larger (later in time). */ + if ( ordering >= lastHit->ordering ) + *lastHit = PriorEl( ordering, desc ); + } +} + +/* Set all the priorities from a priorTable in this table. */ +void PriorTable::setPriors( const PriorTable &other ) +{ + /* Loop src priorities once to overwrite duplicates. */ + PriorTable::Iter priorIt = other; + for ( ; priorIt.lte(); priorIt++ ) + setPrior( priorIt->ordering, priorIt->desc ); +} + +/* Set the priority of starting transitions. Isolates the start state so it has + * no other entry points, then sets the priorities of all the transitions out + * of the start state. If the start state is final, then the outPrior of the + * start state is also set. The idea is that a machine that accepts the null + * string can still specify the starting trans prior for when it accepts the + * null word. */ +void FsmAp::startFsmPrior( int ordering, PriorDesc *prior ) +{ + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + /* Walk all transitions out of the start state. */ + for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + trans->tdap()->priorTable.setPrior( ordering, prior ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + cond->priorTable.setPrior( ordering, prior ); + } + } + } + + if ( startState->nfaOut != 0 ) { + for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) + na->priorTable.setPrior( ordering, prior ); + } + + /* If the new start state is final then set the out priority. This follows + * the same convention as setting start action in the out action table of + * a final start state. */ + if ( startState->stateBits & STB_ISFINAL ) + startState->outPriorTable.setPrior( ordering, prior ); + + /* Start fsm priorities are a special case that may require + * minimization afterwards. */ + afterOpMinimize( true ); +} + +/* Set the priority of all transitions in a graph. Walks all transition lists + * and all def transitions. */ +void FsmAp::allTransPrior( int ordering, PriorDesc *prior ) +{ + /* Walk the list of all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Walk the out list of the state. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + trans->tdap()->priorTable.setPrior( ordering, prior ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + cond->priorTable.setPrior( ordering, prior ); + } + } + } + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter na = *state->nfaOut; na.lte(); na++ ) + na->priorTable.setPrior( ordering, prior ); + } + } +} + +/* Set the priority of all transitions that go into a final state. Note that if + * any entry states are final, we will not be setting the priority of any + * transitions that may go into those states in the future. The graph does not + * support pending in transitions in the same way pending out transitions are + * supported. */ +void FsmAp::finishFsmPrior( int ordering, PriorDesc *prior ) +{ + /* Walk all final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { + /* Walk all in transitions of the final state. */ + for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) + t->priorTable.setPrior( ordering, prior ); + for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) + t->priorTable.setPrior( ordering, prior ); + + if ( (*state)->nfaIn != 0 ) { + for ( NfaInList::Iter na = *(*state)->nfaIn; na.lte(); na++ ) + na->priorTable.setPrior( ordering, prior ); + } + } +} + +/* Set the priority of any future out transitions that may be made going out of + * this state machine. */ +void FsmAp::leaveFsmPrior( int ordering, PriorDesc *prior ) +{ + /* Set priority in all final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + (*state)->outPriorTable.setPrior( ordering, prior ); +} + + +/* Set actions to execute on starting transitions. Isolates the start state + * so it has no other entry points, then adds to the transition functions + * of all the transitions out of the start state. If the start state is final, + * then the func is also added to the start state's out func list. The idea is + * that a machine that accepts the null string can execute a start func when it + * matches the null word, which can only be done when leaving the start/final + * state. */ +void FsmAp::startFsmAction( int ordering, Action *action ) +{ + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + /* Walk the start state's transitions, setting functions. */ + for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + trans->tdap()->actionTable.setAction( ordering, action ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + cond->actionTable.setAction( ordering, action ); + } + } + } + + /* If start state is final then add the action to the out action table. + * This means that when the null string is accepted the start action will + * not be bypassed. */ + if ( startState->stateBits & STB_ISFINAL ) + startState->outActionTable.setAction( ordering, action ); + + if ( startState->nfaOut != 0 ) { + for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) { + + StateAp *state = na->toState; + + /* Walk the start state's transitions, setting functions. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + trans->tdap()->actionTable.setAction( ordering, action ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + cond->actionTable.setAction( ordering, action ); + } + } + } + + /* If start state is final then add the action to the out action table. + * This means that when the null string is accepted the start action will + * not be bypassed. */ + if ( state->stateBits & STB_ISFINAL ) + state->outActionTable.setAction( ordering, action ); + + } + } + + afterOpMinimize( true ); +} + +/* Set functions to execute on all transitions. Walks the out lists of all + * states. */ +void FsmAp::allTransAction( int ordering, Action *action ) +{ + /* Walk all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Walk the out list of the state. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + trans->tdap()->actionTable.setAction( ordering, action ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + cond->actionTable.setAction( ordering, action ); + } + } + } + } +} + +/* Specify functions to execute upon entering final states. If the start state + * is final we can't really specify a function to execute upon entering that + * final state the first time. So function really means whenever entering a + * final state from within the same fsm. */ +void FsmAp::finishFsmAction( int ordering, Action *action ) +{ + /* Walk all final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { + /* Walk the final state's in list. */ + for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) + t->actionTable.setAction( ordering, action ); + for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) + t->actionTable.setAction( ordering, action ); + } +} + +/* Add functions to any future out transitions that may be made going out of + * this state machine. */ +void FsmAp::leaveFsmAction( int ordering, Action *action ) +{ + /* Insert the action in the outActionTable of all final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + (*state)->outActionTable.setAction( ordering, action ); +} + +/* Add functions to the longest match action table for constructing scanners. */ +void FsmAp::longMatchAction( int ordering, FsmLongestMatchPart *lmPart ) +{ + /* Walk all final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) { + /* Walk the final state's in list. */ + for ( TransInList::Iter t = (*state)->inTrans; t.lte(); t++ ) + t->lmActionTable.setAction( ordering, lmPart ); + for ( CondInList::Iter t = (*state)->inCond; t.lte(); t++ ) + t->lmActionTable.setAction( ordering, lmPart ); + } +} + +void FsmAp::fillGaps( StateAp *state ) +{ + /* + * First pass fills in the the caps between transitions. + */ + if ( state->outList.length() == 0 ) { + /* Add the range on the lower and upper bound. */ + attachNewTrans( state, 0, ctx->keyOps->minKey, ctx->keyOps->maxKey ); + } + else { + TransList srcList; + srcList.transfer( state->outList ); + + /* Check for a gap at the beginning. */ + TransList::Iter trans = srcList, next; + if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) { + /* Make the high key and append. */ + Key highKey = trans->lowKey; + ctx->keyOps->decrement( highKey ); + + attachNewTrans( state, 0, ctx->keyOps->minKey, highKey ); + } + + /* Write the transition. */ + next = trans.next(); + state->outList.append( trans ); + + /* Keep the last high end. */ + Key lastHigh = trans->highKey; + + /* Loop each source range. */ + for ( trans = next; trans.lte(); trans = next ) { + /* Make the next key following the last range. */ + Key nextKey = lastHigh; + ctx->keyOps->increment( nextKey ); + + /* Check for a gap from last up to here. */ + if ( ctx->keyOps->lt( nextKey, trans->lowKey ) ) { + /* Make the high end of the range that fills the gap. */ + Key highKey = trans->lowKey; + ctx->keyOps->decrement( highKey ); + + attachNewTrans( state, 0, nextKey, highKey ); + } + + /* Reduce the transition. If it reduced to anything then add it. */ + next = trans.next(); + state->outList.append( trans ); + + /* Keep the last high end. */ + lastHigh = trans->highKey; + } + + /* Now check for a gap on the end to fill. */ + if ( ctx->keyOps->lt( lastHigh, ctx->keyOps->maxKey ) ) { + /* Get a copy of the default. */ + ctx->keyOps->increment( lastHigh ); + + attachNewTrans( state, 0, lastHigh, ctx->keyOps->maxKey ); + } + } + + /* + * Second pass fills in gaps in condition lists. + */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) + continue; + + CondList srcList; + srcList.transfer( trans->tcap()->condList ); + + CondList::Iter cond = srcList, next; + + /* Check for gap at the beginning. */ + if ( cond->key > 0 ) { + for ( CondKey key = 0; key < cond->key; key.increment() ) + attachNewCond( trans, state, 0, key ); + } + + next = cond.next(); + trans->tcap()->condList.append( cond ); + + CondKey lastKey = cond->key; + + for ( cond = next; cond.lte(); cond = next ) { + /* Make the next key following the last range. */ + CondKey nextKey = lastKey; + nextKey.increment(); + + /* Check for a gap from last up to here. */ + if ( nextKey < cond->key ) { + for ( CondKey key = nextKey; key < cond->key; key.increment() ) + attachNewCond( trans, state, 0, key ); + } + + next = cond.next(); + trans->tcap()->condList.append( cond ); + + lastKey = cond->key; + } + + CondKey high = (trans->condSpace == 0) ? + 0 : (1 << trans->condSpace->condSet.length()); + + /* Now check for a gap on the end to fill. */ + if ( lastKey < high ) { + /* Get a copy of the default. */ + lastKey.increment(); + + for ( CondKey key = lastKey; key < high; key.increment() ) + attachNewCond( trans, state, 0, key ); + } + } +} + +void FsmAp::setErrorActions( StateAp *state, const ActionTable &other ) +{ + /* Fill any gaps in the out list with an error transition. */ + fillGaps( state ); + + /* Set error transitions in the transitions that go to error. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState == 0 ) + trans->tdap()->actionTable.setActions( other ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState == 0 ) + cond->actionTable.setActions( other ); + } + } + } +} + +void FsmAp::setErrorAction( StateAp *state, int ordering, Action *action ) +{ + /* Fill any gaps in the out list with an error transition. */ + fillGaps( state ); + + /* Set error transitions in the transitions that go to error. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState == 0 ) + trans->tdap()->actionTable.setAction( ordering, action ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState == 0 ) + cond->actionTable.setAction( ordering, action ); + } + } + } +} + + +/* Give a target state for error transitions. */ +void FsmAp::setErrorTarget( StateAp *state, StateAp *target, int *orderings, + Action **actions, int nActs ) +{ + /* Fill any gaps in the out list with an error transition. */ + fillGaps( state ); + + /* Set error target in the transitions that go to error. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState == 0 ) { + /* The trans goes to error, redirect it. */ + redirectErrorTrans( trans->tdap()->fromState, target, trans->tdap() ); + trans->tdap()->actionTable.setActions( orderings, actions, nActs ); + } + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState == 0 ) { + /* The trans goes to error, redirect it. */ + redirectErrorTrans( cond->fromState, target, cond ); + cond->actionTable.setActions( orderings, actions, nActs ); + } + } + } + } +} + +void FsmAp::transferOutActions( StateAp *state ) +{ + for ( ActionTable::Iter act = state->outActionTable; act.lte(); act++ ) + state->eofActionTable.setAction( act->key, act->value ); + state->outActionTable.empty(); +} + +void FsmAp::transferErrorActions( StateAp *state, int transferPoint ) +{ + for ( int i = 0; i < state->errActionTable.length(); ) { + ErrActionTableEl *act = state->errActionTable.data + i; + if ( act->transferPoint == transferPoint ) { + /* Transfer the error action and remove it. */ + setErrorAction( state, act->ordering, act->action ); + if ( ! state->isFinState() ) + state->eofActionTable.setAction( act->ordering, act->action ); + state->errActionTable.vremove( i ); + } + else { + /* Not transfering and deleting, skip over the item. */ + i += 1; + } + } +} + +/* Set error actions in the start state. */ +void FsmAp::startErrorAction( int ordering, Action *action, int transferPoint ) +{ + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + /* Add the actions. */ + startState->errActionTable.setAction( ordering, action, transferPoint ); + + afterOpMinimize( true ); +} + +/* Set error actions in all states where there is a transition out. */ +void FsmAp::allErrorAction( int ordering, Action *action, int transferPoint ) +{ + /* Insert actions in the error action table of all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) + state->errActionTable.setAction( ordering, action, transferPoint ); +} + +/* Set error actions in final states. */ +void FsmAp::finalErrorAction( int ordering, Action *action, int transferPoint ) +{ + /* Add the action to the error table of final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + (*state)->errActionTable.setAction( ordering, action, transferPoint ); +} + +void FsmAp::notStartErrorAction( int ordering, Action *action, int transferPoint ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState ) + state->errActionTable.setAction( ordering, action, transferPoint ); + } +} + +void FsmAp::notFinalErrorAction( int ordering, Action *action, int transferPoint ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( ! state->isFinState() ) + state->errActionTable.setAction( ordering, action, transferPoint ); + } +} + +/* Set error actions in the states that have transitions into a final state. */ +void FsmAp::middleErrorAction( int ordering, Action *action, int transferPoint ) +{ + /* Isolate the start state in case it is reachable from in inside the + * machine, in which case we don't want it set. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState && ! state->isFinState() ) + state->errActionTable.setAction( ordering, action, transferPoint ); + } +} + +/* Set EOF actions in the start state. */ +void FsmAp::startEOFAction( int ordering, Action *action ) +{ + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + /* Add the actions. */ + startState->eofActionTable.setAction( ordering, action ); + + afterOpMinimize( true ); +} + +/* Set EOF actions in all states where there is a transition out. */ +void FsmAp::allEOFAction( int ordering, Action *action ) +{ + /* Insert actions in the EOF action table of all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) + state->eofActionTable.setAction( ordering, action ); +} + +/* Set EOF actions in final states. */ +void FsmAp::finalEOFAction( int ordering, Action *action ) +{ + /* Add the action to the error table of final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + (*state)->eofActionTable.setAction( ordering, action ); +} + +void FsmAp::notStartEOFAction( int ordering, Action *action ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState ) + state->eofActionTable.setAction( ordering, action ); + } +} + +void FsmAp::notFinalEOFAction( int ordering, Action *action ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( ! state->isFinState() ) + state->eofActionTable.setAction( ordering, action ); + } +} + +/* Set EOF actions in the states that have transitions into a final state. */ +void FsmAp::middleEOFAction( int ordering, Action *action ) +{ + /* Set the actions in all states that are not the start state and not final. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState && ! state->isFinState() ) + state->eofActionTable.setAction( ordering, action ); + } +} + +/* + * Set To State Actions. + */ + +/* Set to state actions in the start state. */ +void FsmAp::startToStateAction( int ordering, Action *action ) +{ + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + startState->toStateActionTable.setAction( ordering, action ); + + afterOpMinimize( true ); +} + +/* Set to state actions in all states. */ +void FsmAp::allToStateAction( int ordering, Action *action ) +{ + /* Insert the action on all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) + state->toStateActionTable.setAction( ordering, action ); +} + +/* Set to state actions in final states. */ +void FsmAp::finalToStateAction( int ordering, Action *action ) +{ + /* Add the action to the error table of final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + (*state)->toStateActionTable.setAction( ordering, action ); +} + +void FsmAp::notStartToStateAction( int ordering, Action *action ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState ) + state->toStateActionTable.setAction( ordering, action ); + } +} + +void FsmAp::notFinalToStateAction( int ordering, Action *action ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( ! state->isFinState() ) + state->toStateActionTable.setAction( ordering, action ); + } +} + +/* Set to state actions in states that are not final and not the start state. */ +void FsmAp::middleToStateAction( int ordering, Action *action ) +{ + /* Set the action in all states that are not the start state and not final. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState && ! state->isFinState() ) + state->toStateActionTable.setAction( ordering, action ); + } +} + +/* + * Set From State Actions. + */ + +void FsmAp::startFromStateAction( int ordering, Action *action ) +{ + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + startState->fromStateActionTable.setAction( ordering, action ); + + afterOpMinimize( true ); +} + +void FsmAp::allFromStateAction( int ordering, Action *action ) +{ + /* Insert the action on all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) + state->fromStateActionTable.setAction( ordering, action ); +} + +void FsmAp::finalFromStateAction( int ordering, Action *action ) +{ + /* Add the action to the error table of final states. */ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + (*state)->fromStateActionTable.setAction( ordering, action ); +} + +void FsmAp::notStartFromStateAction( int ordering, Action *action ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState ) + state->fromStateActionTable.setAction( ordering, action ); + } +} + +void FsmAp::notFinalFromStateAction( int ordering, Action *action ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( ! state->isFinState() ) + state->fromStateActionTable.setAction( ordering, action ); + } +} + +void FsmAp::middleFromStateAction( int ordering, Action *action ) +{ + /* Set the action in all states that are not the start state and not final. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + if ( state != startState && ! state->isFinState() ) + state->fromStateActionTable.setAction( ordering, action ); + } +} + +/* Shift the function ordering of the start transitions to start + * at fromOrder and increase in units of 1. Useful before staring. + * Returns the maximum number of order numbers used. */ +int FsmAp::shiftStartActionOrder( int fromOrder ) +{ + int maxUsed = 0; + + /* Walk the start state's transitions, shifting function ordering. */ + for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + int curFromOrder = fromOrder; + ActionTable::Iter action = trans->tdap()->actionTable; + for ( ; action.lte(); action++ ) + action->key = curFromOrder++; + + /* Keep track of the max number of orders used. */ + if ( curFromOrder - fromOrder > maxUsed ) + maxUsed = curFromOrder - fromOrder; + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + /* Walk the function data for the transition and set the keys to + * increasing values starting at fromOrder. */ + int curFromOrder = fromOrder; + ActionTable::Iter action = cond->actionTable; + for ( ; action.lte(); action++ ) + action->key = curFromOrder++; + + /* Keep track of the max number of orders used. */ + if ( curFromOrder - fromOrder > maxUsed ) + maxUsed = curFromOrder - fromOrder; + } + } + } + + return maxUsed; +} + +/* Remove all priorities. */ +void FsmAp::clearAllPriorities() +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Clear out priority data. */ + state->outPriorTable.empty(); + + /* Clear transition data from the out transitions. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) + trans->tdap()->priorTable.empty(); + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) + cond->priorTable.empty(); + } + } + + if ( state->nfaIn != 0 ) { + for ( NfaInList::Iter na = *state->nfaIn; na.lte(); na++ ) + na->priorTable.empty(); + } + } +} + +/* Zeros out the function ordering keys. This may be called before minimization + * when it is known that no more fsm operations are going to be done. This + * will achieve greater reduction as states will not be separated on the basis + * of function ordering. */ +void FsmAp::nullActionKeys( ) +{ + /* For each state... */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Walk the transitions for the state. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + /* Walk the action table for the transition. */ + for ( ActionTable::Iter action = trans->tdap()->actionTable; + action.lte(); action++ ) + action->key = 0; + + /* Walk the action table for the transition. */ + for ( LmActionTable::Iter action = trans->tdap()->lmActionTable; + action.lte(); action++ ) + action->key = 0; + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + /* Walk the action table for the transition. */ + for ( ActionTable::Iter action = cond->actionTable; + action.lte(); action++ ) + action->key = 0; + + /* Walk the action table for the transition. */ + for ( LmActionTable::Iter action = cond->lmActionTable; + action.lte(); action++ ) + action->key = 0; + } + } + } + + /* Null the action keys of the to state action table. */ + for ( ActionTable::Iter action = state->toStateActionTable; + action.lte(); action++ ) + action->key = 0; + + /* Null the action keys of the from state action table. */ + for ( ActionTable::Iter action = state->fromStateActionTable; + action.lte(); action++ ) + action->key = 0; + + /* Null the action keys of the out transtions. */ + for ( ActionTable::Iter action = state->outActionTable; + action.lte(); action++ ) + action->key = 0; + + /* Null the action keys of the error action table. */ + for ( ErrActionTable::Iter action = state->errActionTable; + action.lte(); action++ ) + action->ordering = 0; + + /* Null the action keys eof action table. */ + for ( ActionTable::Iter action = state->eofActionTable; + action.lte(); action++ ) + action->key = 0; + } +} + +/* Walk the list of states and verify that non final states do not have out + * data, that all stateBits are cleared, and that there are no states with + * zero foreign in transitions. */ +void FsmAp::verifyStates() +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Non final states should not have leaving data. */ + if ( ! (state->stateBits & STB_ISFINAL) ) { + assert( state->outActionTable.length() == 0 ); + assert( state->outCondSpace == 0 ); + assert( state->outCondKeys.length() == 0 ); + assert( state->outPriorTable.length() == 0 ); + } + + /* Data used in algorithms should be cleared. */ + assert( (state->stateBits & STB_BOTH) == 0 ); + assert( state->foreignInTrans > 0 ); + } +} + +/* Compare two transitions according to their relative priority. Since the + * base transition has no priority associated with it, the default is to + * return equal. */ +int FsmAp::comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 ) +{ + /* Looking for differing priorities on same keys. Need to concurrently + * scan the priority lists. */ + PriorTable::Iter pd1 = priorTable1; + PriorTable::Iter pd2 = priorTable2; + while ( pd1.lte() && pd2.lte() ) { + /* Check keys. */ + if ( pd1->desc->key < pd2->desc->key ) + pd1.increment(); + else if ( pd1->desc->key > pd2->desc->key ) + pd2.increment(); + /* Keys are the same, check priorities. */ + else if ( pd1->desc->priority < pd2->desc->priority ) { + if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { + if ( ! priorInteraction ) { + priorInteraction = true; + guardId = pd1->desc->guardId; + } + } + return -1; + } + else if ( pd1->desc->priority > pd2->desc->priority ) { + if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { + if ( ! priorInteraction ) { + priorInteraction = true; + guardId = pd1->desc->guardId; + } + } + return 1; + } + else { + /* Keys and priorities are equal, advance both. */ + pd1.increment(); + pd2.increment(); + } + } + + /* No differing priorities on the same key. */ + return 0; +} + +int FsmAp::compareCondListBitElim( const CondList &condList1, const CondList &condList2 ) +{ + typedef ValPairIter< PiList > ValPairIterPiListCondAp; + ValPairIterPiListCondAp outPair( condList1, condList2 ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + case ValPairIterPiListCondAp::RangeInS1: { + int compareRes = FsmAp::compareCondBitElimPtr( outPair.s1Tel.trans, 0 ); + if ( compareRes != 0 ) + return compareRes; + break; + } + case ValPairIterPiListCondAp::RangeInS2: { + int compareRes = FsmAp::compareCondBitElimPtr( 0, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + } + case ValPairIterPiListCondAp::RangeOverlap: { + int compareRes = FsmAp::compareCondBitElimPtr( + outPair.s1Tel.trans, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + }} + } + return 0; +} + +/* Compares two transitions according to priority and functions. Pointers + * should not be null. Does not consider to state or from state. Compare two + * transitions according to the data contained in the transitions. Data means + * any properties added to user transitions that may differentiate them. Since + * the base transition has no data, the default is to return equal. */ +int FsmAp::compareTransData( TransAp *trans1, TransAp *trans2 ) +{ + if ( trans1->condSpace < trans2->condSpace ) + return -1; + else if ( trans2->condSpace < trans1->condSpace ) + return 1; + + if ( trans1->plain() ) { + int compareRes = FsmAp::compareCondDataPtr( trans1->tdap(), trans2->tdap() ); + if ( compareRes != 0 ) + return compareRes; + } + else { + typedef ValPairIter< PiList > ValPairIterPiListCondAp; + ValPairIterPiListCondAp outPair( trans1->tcap()->condList, + trans2->tcap()->condList ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + case ValPairIterPiListCondAp::RangeInS1: { + int compareRes = FsmAp::compareCondDataPtr( outPair.s1Tel.trans, 0 ); + if ( compareRes != 0 ) + return compareRes; + break; + } + case ValPairIterPiListCondAp::RangeInS2: { + int compareRes = FsmAp::compareCondDataPtr( 0, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + } + case ValPairIterPiListCondAp::RangeOverlap: { + int compareRes = FsmAp::compareCondDataPtr( + outPair.s1Tel.trans, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + }} + } + } + return 0; +} + +/* Compares two transitions according to priority and functions. Pointers + * should not be null. Does not consider to state or from state. Compare two + * transitions according to the data contained in the transitions. Data means + * any properties added to user transitions that may differentiate them. Since + * the base transition has no data, the default is to return equal. */ +template< class Trans > int FsmAp::compareCondData( Trans *trans1, Trans *trans2 ) +{ + /* Compare the prior table. */ + int cmpRes = CmpPriorTable::compare( trans1->priorTable, + trans2->priorTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Compare longest match action tables. */ + cmpRes = CmpLmActionTable::compare(trans1->lmActionTable, + trans2->lmActionTable); + if ( cmpRes != 0 ) + return cmpRes; + + /* Compare action tables. */ + return CmpActionTable::compare(trans1->actionTable, + trans2->actionTable); +} + +/* Compares two transitions according to priority and functions. Pointers + * should not be null. Does not consider to state or from state. Compare two + * transitions according to the data contained in the transitions. Data means + * any properties added to user transitions that may differentiate them. Since + * the base transition has no data, the default is to return equal. */ +template< class Trans > int FsmAp::compareCondBitElim( Trans *trans1, Trans *trans2 ) +{ + if ( trans1->toState < trans2->toState ) + return -1; + else if ( trans1->toState > trans2->toState ) + return 1; + + /* Compare the prior table. */ + int cmpRes = CmpPriorTable::compare( trans1->priorTable, + trans2->priorTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Compare longest match action tables. */ + cmpRes = CmpLmActionTable::compare(trans1->lmActionTable, + trans2->lmActionTable); + if ( cmpRes != 0 ) + return cmpRes; + + /* Compare action tables. */ + return CmpActionTable::compare(trans1->actionTable, + trans2->actionTable); +} + +/* Compare the properties of states that are embedded by users. Compares out + * priorities, out transitions, to, from, out, error and eof action tables. */ +int FsmAp::compareStateData( const StateAp *state1, const StateAp *state2 ) +{ + /* Compare the out priority table. */ + int cmpRes = CmpPriorTable:: + compare( state1->outPriorTable, state2->outPriorTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Test to state action tables. */ + cmpRes = CmpActionTable::compare( state1->toStateActionTable, + state2->toStateActionTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Test from state action tables. */ + cmpRes = CmpActionTable::compare( state1->fromStateActionTable, + state2->fromStateActionTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Test out action tables. */ + cmpRes = CmpActionTable::compare( state1->outActionTable, + state2->outActionTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Out condition space and set of vals. */ + if ( state1->outCondSpace < state2->outCondSpace ) + return -1; + else if ( state1->outCondSpace > state2->outCondSpace ) + return 1; + + cmpRes = CmpTable::compare( state1->outCondKeys, + state2->outCondKeys ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Test out error action tables. */ + cmpRes = CmpErrActionTable::compare( state1->errActionTable, + state2->errActionTable ); + if ( cmpRes != 0 ) + return cmpRes; + + /* Test eof action tables. */ + cmpRes = CmpActionTable::compare( state1->eofActionTable, + state2->eofActionTable ); + if ( cmpRes != 0 ) + return cmpRes; + + return CmpTable::compare( + state1->lmNfaParts, state2->lmNfaParts ); +} + + +/* Invoked when a state looses its final state status and the leaving + * transition embedding data should be deleted. */ +void FsmAp::clearOutData( StateAp *state ) +{ + /* Kill the out actions and priorities. */ + state->outCondSpace = 0; + state->outCondKeys.empty(); + state->outActionTable.empty(); + state->outPriorTable.empty(); +} + +bool FsmAp::hasOutData( StateAp *state ) +{ + return ( state->outActionTable.length() > 0 || + state->outCondSpace != 0 || + state->outCondKeys.length() > 0 || + state->outPriorTable.length() > 0 || + state->outCondSpace != 0 ); +} + +/* + * Setting Conditions. + */ + +FsmRes FsmAp::startFsmCondition( Action *condAction, bool sense ) +{ + CondSet set; + CondKeySet vals; + set.insert( condAction ); + vals.append( sense ? 1 : 0 ); + + /* Make sure the start state has no other entry points. */ + isolateStartState( this ); + + FsmRes res = embedCondition( this, startState, set, vals ); + if ( !res.success() ) + return res; + + if ( startState->nfaOut != 0 ) { + /* Only one level. */ + for ( NfaTransList::Iter na = *startState->nfaOut; na.lte(); na++ ) { + res = embedCondition( this, startState, set, vals ); + if ( !res.success() ) + return res; + } + } + + afterOpMinimize( true ); + + return FsmRes( FsmRes::Fsm(), this ); +} + +void FsmAp::allTransCondition( Action *condAction, bool sense ) +{ + CondSet set; + CondKeySet vals; + set.insert( condAction ); + vals.append( sense ? 1 : 0 ); + + for ( StateList::Iter state = stateList; state.lte(); state++ ) + embedCondition( this, state, set, vals ); +} + +void FsmAp::leaveFsmCondition( Action *condAction, bool sense ) +{ + for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) + addOutCondition( *state, condAction, sense ); +} diff --git a/src/libfsm/fsmattach.cc b/src/libfsm/fsmattach.cc new file mode 100644 index 00000000..5e7e5e7c --- /dev/null +++ b/src/libfsm/fsmattach.cc @@ -0,0 +1,857 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "fsmgraph.h" + +#include +using namespace std; + +void FsmAp::attachStateDict( StateAp *from, StateAp *to ) +{ + if ( to->stateDictIn == 0 ) + to->stateDictIn = new StateSet; + + bool inserted = to->stateDictIn->insert( from ); + assert( inserted ); + + if ( from != to ) { + if ( misfitAccounting ) { + if ( to->foreignInTrans == 0 ) + stateList.append( misfitList.detach( to ) ); + } + + to->foreignInTrans += 1; + } +} + +void FsmAp::detachStateDict( StateAp *from, StateAp *to ) +{ + bool removed = to->stateDictIn->remove( from ); + assert( removed ); + + to->foreignInTrans -= 1; + + if ( from != to ) { + if ( misfitAccounting ) { + if ( to->foreignInTrans == 0 ) + misfitList.append( stateList.detach( to ) ); + } + } +} + +void FsmAp::attachToNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ) +{ + if ( to->nfaIn == 0 ) + to->nfaIn = new NfaInList; + + nfaTrans->fromState = from; + nfaTrans->toState = to; + + attachToInList( from, to, to->nfaIn->head, nfaTrans ); +} + +void FsmAp::detachFromNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ) +{ + nfaTrans->fromState = 0; + nfaTrans->toState = 0; + + detachFromInList( from, to, to->nfaIn->head, nfaTrans ); +} + +template< class Head > void FsmAp::attachToInList( StateAp *from, + StateAp *to, Head *&head, Head *trans ) +{ + trans->ilnext = head; + trans->ilprev = 0; + + /* If in trans list is not empty, set the head->prev to trans. */ + if ( head != 0 ) + head->ilprev = trans; + + /* Now insert ourselves at the front of the list. */ + head = trans; + + /* Keep track of foreign transitions for from and to. */ + if ( from != to ) { + if ( misfitAccounting ) { + /* If the number of foreign in transitions is about to go up to 1 then + * move it from the misfit list to the main list. */ + if ( to->foreignInTrans == 0 ) + stateList.append( misfitList.detach( to ) ); + } + + to->foreignInTrans += 1; + } +}; + +/* Detach a transition from an inlist. The head of the inlist must be supplied. */ +template< class Head > void FsmAp::detachFromInList( StateAp *from, StateAp *to, + Head *&head, Head *trans ) +{ + if ( trans->ilprev == 0 ) + head = trans->ilnext; + else + trans->ilprev->ilnext = trans->ilnext; + + if ( trans->ilnext != 0 ) + trans->ilnext->ilprev = trans->ilprev; + + /* Keep track of foreign transitions for from and to. */ + if ( from != to ) { + to->foreignInTrans -= 1; + + if ( misfitAccounting ) { + /* If the number of foreign in transitions goes down to 0 then move it + * from the main list to the misfit list. */ + if ( to->foreignInTrans == 0 ) + misfitList.append( stateList.detach( to ) ); + } + } +} + +CondAp *FsmAp::attachNewCond( TransAp *trans, StateAp *from, StateAp *to, CondKey onChar ) +{ + /* Sub-transition for conditions. */ + CondAp *condAp = new CondAp( trans ); + condAp->key = onChar; + trans->tcap()->condList.append( condAp ); + + condAp->fromState = from; + condAp->toState = to; + + /* Attach in list. */ + if ( to != 0 ) + attachToInList( from, to, to->inCond.head, condAp ); + + return condAp; +} + +TransAp *FsmAp::attachNewTrans( StateAp *from, StateAp *to, Key lowKey, Key highKey ) +{ + /* Make the new transition. */ + TransDataAp *retVal = new TransDataAp(); + + /* Make the entry in the out list for the transitions. */ + from->outList.append( retVal ); + + /* Set the the keys of the new trans. */ + retVal->lowKey = lowKey; + retVal->highKey = highKey; + + retVal->fromState = from; + retVal->toState = to; + + /* Attach in list. */ + if ( to != 0 ) + attachToInList( from, to, to->inTrans.head, retVal ); + + return retVal; +} + +/* Attach for range lists or for the default transition. This attach should + * be used when a transition already is allocated and must be attached to a + * target state. Does not handle adding the transition into the out list. */ +void FsmAp::attachTrans( StateAp *from, StateAp *to, TransDataAp *trans ) +{ + assert( trans->fromState == 0 && trans->toState == 0 ); + + trans->fromState = from; + trans->toState = to; + + if ( to != 0 ) { + /* For now always attache the one and only condList element. */ + attachToInList( from, to, to->inTrans.head, trans ); + } +} + +void FsmAp::attachTrans( StateAp *from, StateAp *to, CondAp *trans ) +{ + assert( trans->fromState == 0 && trans->toState == 0 ); + + trans->fromState = from; + trans->toState = to; + + if ( to != 0 ) { + /* For now always attache the one and only condList element. */ + attachToInList( from, to, to->inCond.head, trans ); + } +} + +/* Redirect a transition away from error and towards some state. This is just + * like attachTrans except it requires fromState to be set and does not touch + * it. */ +void FsmAp::redirectErrorTrans( StateAp *from, StateAp *to, TransDataAp *trans ) +{ + assert( trans->fromState != 0 && trans->toState == 0 ); + trans->toState = to; + + if ( to != 0 ) { + /* Attach using the inList pointer as the head pointer. */ + attachToInList( from, to, to->inTrans.head, trans ); + } +} + +void FsmAp::redirectErrorTrans( StateAp *from, StateAp *to, CondAp *trans ) +{ + assert( trans->fromState != 0 && trans->toState == 0 ); + trans->toState = to; + + if ( to != 0 ) { + /* Attach using the inList pointer as the head pointer. */ + attachToInList( from, to, to->inCond.head, trans ); + } +} + +/* Detach for out/in lists or for default transition. */ +void FsmAp::detachTrans( StateAp *from, StateAp *to, TransDataAp *trans ) +{ + assert( trans->fromState == from && trans->toState == to ); + + trans->fromState = 0; + trans->toState = 0; + + if ( to != 0 ) { + detachFromInList( from, to, to->inTrans.head, trans ); + } +} + +void FsmAp::detachTrans( StateAp *from, StateAp *to, CondAp *trans ) +{ + assert( trans->fromState == from && trans->toState == to ); + + trans->fromState = 0; + trans->toState = 0; + + if ( to != 0 ) { + detachFromInList( from, to, to->inCond.head, trans ); + } +} + + +/* Detach a state from the graph. Detaches and deletes transitions in and out + * of the state. Empties inList and outList. Removes the state from the final + * state set. A detached state becomes useless and should be deleted. */ +void FsmAp::detachState( StateAp *state ) +{ + while ( state->inTrans.head != 0 ) { + /* Get pointers to the trans and the state. */ + TransDataAp *trans = state->inTrans.head; + + StateAp *fromState = trans->fromState; + + /* Detach the transitions from the source state. */ + detachTrans( fromState, state, trans ); + fromState->outList.detach( trans ); + delete trans->tdap(); + } + + /* Detach the in transitions from the inList list of transitions. */ + while ( state->inCond.head != 0 ) { + /* Get pointers to the trans and the state. */ + CondAp *condAp = state->inCond.head; + TransAp *trans = condAp->transAp; + + StateAp *fromState = condAp->fromState; + + /* Detach the transitions from the source state. */ + detachTrans( fromState, state, condAp ); + + trans->tcap()->condList.detach( condAp ); + delete condAp; + + if ( trans->tcap()->condList.length() == 0 ) { + /* Ok to delete the transition. */ + fromState->outList.detach( trans ); + delete trans->tcap(); + } + } + + /* Remove the entry points in on the machine. */ + while ( state->entryIds.length() > 0 ) + unsetEntry( state->entryIds[0], state ); + + /* Detach out range transitions. */ + for ( TransList::Iter trans = state->outList; trans.lte(); ) { + TransList::Iter next = trans.next(); + if ( trans->plain() ) { + detachTrans( state, trans->tdap()->toState, trans->tdap() ); + delete trans->tdap(); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); ) { + CondList::Iter next = cond.next(); + detachTrans( state, cond->toState, cond ); + delete cond; + cond = next; + } + trans->tcap()->condList.abandon(); + delete trans->tcap(); + } + trans = next; + } + + /* Delete all of the out range pointers. */ + state->outList.abandon(); + + /* Unset final stateness before detaching from graph. */ + if ( state->stateBits & STB_ISFINAL ) + finStateSet.remove( state ); + + if ( state->nfaIn != 0 ) { + while ( state->nfaIn->head != 0 ) { + NfaTrans *trans = state->nfaIn->head; + StateAp *fromState = trans->fromState; + + detachFromNfa( fromState, state, trans ); + fromState->nfaOut->detach( trans ); + delete trans; + } + delete state->nfaIn; + state->nfaIn = 0; + } + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter t = *state->nfaOut; t.lte(); ) { + NfaTransList::Iter next = t.next(); + detachFromNfa( t->fromState, t->toState, t ); + state->nfaOut->detach( t ); + delete t; + t = next; + } + state->nfaOut->abandon(); + delete state->nfaOut; + state->nfaOut = 0; + } + + if ( state->stateDictIn != 0 ) { + for ( StateSet::Iter s = *state->stateDictIn; s.lte(); s++ ) { + bool removed = (*s)->stateDictEl->stateSet.remove( state ); + assert( removed ); + } + + delete state->stateDictIn; + state->stateDictIn = 0; + } + + if ( state->stateDictEl != 0 ) { + for ( StateSet::Iter s = state->stateDictEl->stateSet; s.lte(); s++ ) + detachStateDict( state, *s ); + + stateDict.detach( state->stateDictEl ); + delete state->stateDictEl; + state->stateDictEl = 0; + + nfaList.detach( state ); + } +} + +TransDataAp *FsmAp::dupTransData( StateAp *from, TransDataAp *srcTrans ) +{ + /* Make a new transition. */ + TransDataAp *newTrans = new TransDataAp(); + newTrans->condSpace = srcTrans->condSpace; + + attachTrans( from, srcTrans->tdap()->toState, newTrans ); + addInTrans( newTrans, srcTrans->tdap() ); + + return newTrans; +} + + +/* Duplicate a transition. Makes a new transition that is attached to the same + * dest as srcTrans. The new transition has functions and priority taken from + * srcTrans. Used for merging a transition in to a free spot. The trans can + * just be dropped in. It does not conflict with an existing trans and need + * not be crossed. Returns the new transition. */ +TransAp *FsmAp::dupTrans( StateAp *from, TransAp *srcTrans ) +{ + if ( srcTrans->plain() ) { + /* Make a new transition. */ + TransDataAp *newTrans = new TransDataAp(); + newTrans->condSpace = srcTrans->condSpace; + + attachTrans( from, srcTrans->tdap()->toState, newTrans ); + addInTrans( newTrans, srcTrans->tdap() ); + + return newTrans; + } + else { + /* Make a new transition. */ + TransAp *newTrans = new TransCondAp(); + newTrans->condSpace = srcTrans->condSpace; + + for ( CondList::Iter sc = srcTrans->tcap()->condList; sc.lte(); sc++ ) { + /* Sub-transition for conditions. */ + CondAp *newCond = new CondAp( newTrans ); + newCond->key = sc->key; + newTrans->tcap()->condList.append( newCond ); + + /* We can attach the transition, one does not exist. */ + attachTrans( from, sc->toState, newCond ); + + /* Call the user callback to add in the original source transition. */ + addInTrans( newCond, sc.ptr ); + } + + return newTrans; + } +} + +/* Duplicate a transition. Makes a new transition that is attached to the same + * dest as srcTrans. The new transition has functions and priority taken from + * srcTrans. Used for merging a transition in to a free spot. The trans can + * just be dropped in. It does not conflict with an existing trans and need + * not be crossed. Returns the new transition. */ +CondAp *FsmAp::dupCondTrans( StateAp *from, TransAp *destParent, CondAp *srcTrans ) +{ + /* Sub-transition for conditions. */ + CondAp *newCond = new CondAp( destParent ); + + /* We can attach the transition, one does not exist. */ + attachTrans( from, srcTrans->toState, newCond ); + + /* Call the user callback to add in the original source transition. */ + addInTrans( newCond, srcTrans ); + + return newCond; +} + +/* In crossing, src trans and dest trans both go to existing states. Make one + * state from the sets of states that src and dest trans go to. */ +template< class Trans > Trans *FsmAp::fsmAttachStates( StateAp *from, + Trans *destTrans, Trans *srcTrans ) +{ + /* The priorities are equal. We must merge the transitions. Does the + * existing trans go to the state we are to attach to? ie, are we to + * simply double up the transition? */ + StateAp *toState = srcTrans->toState; + StateAp *existingState = destTrans->toState; + + if ( existingState == toState ) { + /* The transition is a double up to the same state. Copy the src + * trans into itself. We don't need to merge in the from out trans + * data, that was done already. */ + addInTrans( destTrans, srcTrans ); + } + else { + /* The trans is not a double up. Dest trans cannot be the same as src + * trans. Set up the state set. */ + StateSet stateSet; + + /* We go to all the states the existing trans goes to, plus... */ + if ( existingState->stateDictEl == 0 ) + stateSet.insert( existingState ); + else + stateSet.insert( existingState->stateDictEl->stateSet ); + + /* ... all the states that we have been told to go to. */ + if ( toState->stateDictEl == 0 ) + stateSet.insert( toState ); + else + stateSet.insert( toState->stateDictEl->stateSet ); + + /* Look for the state. If it is not there already, make it. */ + StateDictEl *lastFound; + if ( stateDict.insert( stateSet, &lastFound ) ) { + /* Make a new state representing the combination of states in + * stateSet. It gets added to the fill list. This means that we + * need to fill in it's transitions sometime in the future. We + * don't do that now (ie, do not recurse). */ + StateAp *combinState = addState(); + + /* Link up the dict element and the state. */ + lastFound->targState = combinState; + combinState->stateDictEl = lastFound; + + /* Setup the in links. */ + for ( StateSet::Iter s = stateSet; s.lte(); s++ ) + attachStateDict( combinState, *s ); + + /* Add to the fill list. */ + nfaList.append( combinState ); + } + + /* Get the state insertted/deleted. */ + StateAp *targ = lastFound->targState; + + /* Detach the state from existing state. */ + detachTrans( from, existingState, destTrans ); + + /* Re-attach to the new target. */ + attachTrans( from, targ, destTrans ); + + /* Add in src trans to the existing transition that we redirected to + * the new state. We don't need to merge in the from out trans data, + * that was done already. */ + addInTrans( destTrans, srcTrans ); + } + + return destTrans; +} + +/* Two transitions are to be crossed, handle the possibility of either going + * to the error state. */ +template < class Trans > Trans *FsmAp::mergeTrans( StateAp *from, + Trans *destTrans, Trans *srcTrans ) +{ + Trans *retTrans = 0; + if ( destTrans->toState == 0 && srcTrans->toState == 0 ) { + /* Error added into error. */ + addInTrans( destTrans, srcTrans ); + retTrans = destTrans; + } + else if ( destTrans->toState == 0 && srcTrans->toState != 0 ) { + /* Non error added into error we need to detach and reattach, */ + detachTrans( from, destTrans->toState, destTrans ); + attachTrans( from, srcTrans->toState, destTrans ); + addInTrans( destTrans, srcTrans ); + retTrans = destTrans; + } + else if ( srcTrans->toState == 0 ) { + /* Dest goes somewhere but src doesn't, just add it it in. */ + addInTrans( destTrans, srcTrans ); + retTrans = destTrans; + } + else { + /* Both go somewhere, run the actual cross. */ + retTrans = fsmAttachStates( from, destTrans, srcTrans ); + } + + return retTrans; +} + +/* Find the trans with the higher priority. If src is lower priority then dest then + * src is ignored. If src is higher priority than dest, then src overwrites dest. If + * the priorities are equal, then they are merged. */ +CondAp *FsmAp::crossCondTransitions( StateAp *from, TransAp *destParent, + CondAp *destTrans, CondAp *srcTrans ) +{ + CondAp *retTrans; + + /* Compare the priority of the dest and src transitions. */ + int compareRes = comparePrior( destTrans->priorTable, srcTrans->priorTable ); + if ( compareRes < 0 ) { + /* Src trans has a higher priority than dest, src overwrites dest. + * Detach dest and return a copy of src. */ + detachTrans( from, destTrans->toState, destTrans ); + delete destTrans; + retTrans = dupCondTrans( from, destParent, srcTrans ); + } + else if ( compareRes > 0 ) { + /* The dest trans has a higher priority, use dest. */ + retTrans = destTrans; + } + else { + /* Src trans and dest trans have the same priority, they must be merged. */ + retTrans = mergeTrans( from, destTrans, srcTrans ); + } + + /* Return the transition that resulted from the cross. */ + return retTrans; +} + +TransAp *FsmAp::copyTransForExpansion( StateAp *from, TransAp *srcTrans ) +{ + /* This is the dup without the attach. */ + TransCondAp *newTrans = new TransCondAp(); + newTrans->condSpace = srcTrans->condSpace; + + if ( srcTrans->plain() ) { + TransDataAp *srcData = srcTrans->tdap(); + CondAp *newCond = new CondAp( newTrans ); + newCond->key = 0; + + attachTrans( srcData->fromState, srcData->toState, newCond ); + + /* Call the user callback to add in the original source transition. */ + //addInTrans( newCond, srcData ); + + /* Not a copy of ourself, get the functions and priorities. */ + newCond->lmActionTable.setActions( srcData->lmActionTable ); + newCond->actionTable.setActions( srcData->actionTable ); + newCond->priorTable.setPriors( srcData->priorTable ); + + newTrans->condList.append( newCond ); + } + else { + for ( CondList::Iter sc = srcTrans->tcap()->condList; sc.lte(); sc++ ) { + /* Sub-transition for conditions. */ + CondAp *newCond = new CondAp( newTrans ); + newCond->key = sc->key; + + attachTrans( sc->fromState, sc->toState, newCond ); + + /* Call the user callback to add in the original source transition. */ + addInTrans( newCond, sc.ptr ); + + newTrans->condList.append( newCond ); + } + } + + /* Set up the transition's keys and append to the dest list. */ + newTrans->lowKey = srcTrans->lowKey; + newTrans->highKey = srcTrans->highKey; + + return newTrans; +} + +void FsmAp::freeEffectiveTrans( TransAp *trans ) +{ + for ( CondList::Iter sc = trans->tcap()->condList; sc.lte(); ) { + CondList::Iter next = sc.next(); + detachTrans( sc->fromState, sc->toState, sc ); + delete sc; + sc = next; + } + trans->tcap()->condList.abandon(); + delete trans->tcap(); +} + +TransDataAp *FsmAp::crossTransitionsBothPlain( StateAp *from, + TransDataAp *destTrans, TransDataAp *srcTrans ) +{ + /* Neither have cond space and no expansion took place. Cross them. */ + TransDataAp *retTrans; + + /* Compare the priority of the dest and src transitions. */ + int compareRes = comparePrior( destTrans->priorTable, srcTrans->priorTable ); + if ( compareRes < 0 ) { + /* Src trans has a higher priority than dest, src overwrites dest. + * Detach dest and return a copy of src. */ + detachTrans( from, destTrans->toState, destTrans ); + delete destTrans; + retTrans = dupTransData( from, srcTrans ); + } + else if ( compareRes > 0 ) { + /* The dest trans has a higher priority, use dest. */ + retTrans = destTrans; + } + else { + /* Src trans and dest trans have the same priority, they must be merged. */ + retTrans = mergeTrans( from, destTrans, srcTrans ); + } + + /* Return the transition that resulted from the cross. */ + return retTrans; +} + +/* Find the trans with the higher priority. If src is lower priority then dest then + * src is ignored. If src is higher priority than dest, then src overwrites dest. If + * the priorities are equal, then they are merged. */ +TransAp *FsmAp::crossTransitions( StateAp *from, + TransAp *destTrans, TransAp *srcTrans ) +{ + if ( destTrans->plain() && srcTrans->plain() ) { + /* Return the transition that resulted from the cross. */ + return crossTransitionsBothPlain( from, + destTrans->tdap(), srcTrans->tdap() ); + } + else { + /* At least one is non-empty. Target is non-empty. Need to work in + * condition spaced. */ + CondSpace *mergedSpace = expandCondSpace( destTrans, srcTrans ); + + /* If the dest state cond space does not equal the merged, we have to + * rewrite it. If the src state cond space does not equal, we have to + * copy it. */ + + TransAp *effSrcTrans = srcTrans; + + if ( srcTrans->condSpace != mergedSpace ) { + effSrcTrans = copyTransForExpansion( from, srcTrans ); + CondSpace *orig = effSrcTrans->condSpace; + effSrcTrans->condSpace = mergedSpace; + expandConds( from, effSrcTrans, orig, mergedSpace ); + } + + if ( destTrans->condSpace != mergedSpace ) { + /* Make the transition into a conds transition. If dest is a plain + * transition, we have to replace it with a conds transition. */ + if ( destTrans->plain() ) + destTrans = convertToCondAp( from, destTrans->tdap() ); + + /* Now expand the dest. */ + CondSpace *orig = destTrans->condSpace; + destTrans->condSpace = mergedSpace; + expandConds( from, destTrans, orig, mergedSpace ); + } + + /* The destination list. */ + CondList destList; + + /* Set up an iterator to stop at breaks. */ + typedef ValPairIter< PiList > ValPairIterPiListCondAp; + ValPairIterPiListCondAp outPair( destTrans->tcap()->condList, + effSrcTrans->tcap()->condList ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + case ValPairIterPiListCondAp::RangeInS1: { + /* The pair iter is the authority on the keys. It may have needed + * to break the dest range. */ + CondAp *destCond = outPair.s1Tel.trans; + destCond->key = outPair.s1Tel.key; + destList.append( destCond ); + break; + } + case ValPairIterPiListCondAp::RangeInS2: { + /* Src range may get crossed with dest's default transition. */ + CondAp *newCond = dupCondTrans( from, destTrans, outPair.s2Tel.trans ); + + /* Set up the transition's keys and append to the dest list. */ + newCond->key = outPair.s2Tel.key; + destList.append( newCond ); + break; + } + case ValPairIterPiListCondAp::RangeOverlap: { + /* Exact overlap, cross them. */ + CondAp *newTrans = crossCondTransitions( from, destTrans, + outPair.s1Tel.trans, outPair.s2Tel.trans ); + + /* Set up the transition's keys and append to the dest list. */ + newTrans->key = outPair.s1Tel.key; + destList.append( newTrans ); + break; + }} + } + + /* Abandon the old outList and transfer destList into it. */ + destTrans->tcap()->condList.transfer( destList ); + + /* Delete the duplicate. Don't detach anything. */ + if ( srcTrans != effSrcTrans ) + freeEffectiveTrans( effSrcTrans ); + + return destTrans; + } +} + +/* Copy the transitions in srcList to the outlist of dest. The srcList should + * not be the outList of dest, otherwise you would be copying the contents of + * srcList into itself as it's iterated: bad news. */ +void FsmAp::outTransCopy( StateAp *dest, TransAp *srcList ) +{ + /* The destination list. */ + TransList destList; + + /* Set up an iterator to stop at breaks. */ + typedef RangePairIter< PiList > RangePairIterPiListTransAp; + RangePairIterPiListTransAp outPair( ctx, dest->outList, srcList ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + case RangePairIterPiListTransAp::RangeInS1: { + /* The pair iter is the authority on the keys. It may have needed + * to break the dest range. */ + TransAp *destTrans = outPair.s1Tel.trans; + destTrans->lowKey = outPair.s1Tel.lowKey; + destTrans->highKey = outPair.s1Tel.highKey; + destList.append( destTrans ); + break; + } + case RangePairIterPiListTransAp::RangeInS2: { + /* Src range may get crossed with dest's default transition. */ + TransAp *newTrans = dupTrans( dest, outPair.s2Tel.trans ); + + /* Set up the transition's keys and append to the dest list. */ + newTrans->lowKey = outPair.s2Tel.lowKey; + newTrans->highKey = outPair.s2Tel.highKey; + destList.append( newTrans ); + break; + } + case RangePairIterPiListTransAp::RangeOverlap: { + /* Exact overlap, cross them. */ + TransAp *newTrans = crossTransitions( dest, + outPair.s1Tel.trans, outPair.s2Tel.trans ); + + /* Set up the transition's keys and append to the dest list. */ + newTrans->lowKey = outPair.s1Tel.lowKey; + newTrans->highKey = outPair.s1Tel.highKey; + destList.append( newTrans ); + break; + } + case RangePairIterPiListTransAp::BreakS1: { + /* Since we are always writing to the dest trans, the dest needs + * to be copied when it is broken. The copy goes into the first + * half of the break to "break it off". */ + outPair.s1Tel.trans = dupTrans( dest, outPair.s1Tel.trans ); + break; + } + case RangePairIterPiListTransAp::BreakS2: + break; + } + } + + /* Abandon the old outList and transfer destList into it. */ + dest->outList.transfer( destList ); +} + +/* Move all the transitions that go into src so that they go into dest. */ +void FsmAp::moveInwardTrans( StateAp *dest, StateAp *src ) +{ + /* Do not try to move in trans to and from the same state. */ + assert( dest != src ); + + /* If src is the start state, dest becomes the start state. */ + if ( src == startState ) { + unsetStartState(); + setStartState( dest ); + } + + /* For each entry point into, create an entry point into dest, when the + * state is detached, the entry points to src will be removed. */ + for ( EntryIdSet::Iter enId = src->entryIds; enId.lte(); enId++ ) + changeEntry( *enId, dest, src ); + + /* Move the transitions in inList. */ + while ( src->inTrans.head != 0 ) { + /* Get trans and from state. */ + TransDataAp *trans = src->inTrans.head; + StateAp *fromState = trans->fromState; + + /* Detach from src, reattach to dest. */ + detachTrans( fromState, src, trans ); + attachTrans( fromState, dest, trans ); + } + + /* Move the transitions in inList. */ + while ( src->inCond.head != 0 ) { + /* Get trans and from state. */ + CondAp *trans = src->inCond.head; + StateAp *fromState = trans->fromState; + + /* Detach from src, reattach to dest. */ + detachTrans( fromState, src, trans ); + attachTrans( fromState, dest, trans ); + } + + /* Move inward nfa links. */ + if ( src->nfaIn != 0 ) { + while ( src->nfaIn->head != 0 ) { + NfaTrans *trans = src->nfaIn->head; + StateAp *fromState = trans->fromState; + + detachFromNfa( fromState, src, trans ); + attachToNfa( fromState, dest, trans ); + } + } +} diff --git a/src/libfsm/fsmbase.cc b/src/libfsm/fsmbase.cc new file mode 100644 index 00000000..cc2c8757 --- /dev/null +++ b/src/libfsm/fsmbase.cc @@ -0,0 +1,854 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "fsmgraph.h" +#include "parsedata.h" +#include "action.h" + +#include +#include +#include + +FsmCtx::FsmCtx( FsmGbl *fsmGbl ) +: + minimizeLevel(fsmGbl->minimizeLevel), + minimizeOpt(fsmGbl->minimizeOpt), + + /* No limit. */ + stateLimit(STATE_UNLIMITED), + + printStatistics(fsmGbl->printStatistics), + + checkPriorInteraction(fsmGbl->checkPriorInteraction), + + unionOp(false), + + condsCheckDepth(0), + + curActionOrd(0), + curPriorOrd(0), + + nextPriorKey(0), + nextCondId(0), + + fsmGbl(fsmGbl), + generatingSectionSubset(false), + lmRequiresErrorState(false), + nameIndex(0), + + getKeyExpr(0), + accessExpr(0), + prePushExpr(0), + postPopExpr(0), + nfaPrePushExpr(0), + nfaPostPopExpr(0), + pExpr(0), + peExpr(0), + eofExpr(0), + csExpr(0), + topExpr(0), + stackExpr(0), + actExpr(0), + tokstartExpr(0), + tokendExpr(0), + dataExpr(0) +{ + keyOps = new KeyOps; + condData = new CondData; +} + +FsmCtx::~FsmCtx() +{ + delete keyOps; + delete condData; + priorDescList.empty(); + + actionList.empty(); + + if ( getKeyExpr != 0 ) + delete getKeyExpr; + if ( accessExpr != 0 ) + delete accessExpr; + if ( prePushExpr != 0 ) + delete prePushExpr; + if ( postPopExpr != 0 ) + delete postPopExpr; + if ( nfaPrePushExpr != 0 ) + delete nfaPrePushExpr; + if ( nfaPostPopExpr != 0 ) + delete nfaPostPopExpr; + if ( pExpr != 0 ) + delete pExpr; + if ( peExpr != 0 ) + delete peExpr; + if ( eofExpr != 0 ) + delete eofExpr; + if ( csExpr != 0 ) + delete csExpr; + if ( topExpr != 0 ) + delete topExpr; + if ( stackExpr != 0 ) + delete stackExpr; + if ( actExpr != 0 ) + delete actExpr; + if ( tokstartExpr != 0 ) + delete tokstartExpr; + if ( tokendExpr != 0 ) + delete tokendExpr; + if ( dataExpr != 0 ) + delete dataExpr; +} + +/* Graph constructor. */ +FsmAp::FsmAp( FsmCtx *ctx ) +: + ctx( ctx ), + + priorInteraction(false), + + /* No start state. */ + startState(0), + errState(0), + + /* Misfit accounting is a switch, turned on only at specific times. It + * controls what happens when states have no way in from the outside + * world.. */ + misfitAccounting(false) +{ +} + +/* Copy all graph data including transitions. */ +FsmAp::FsmAp( const FsmAp &graph ) +: + ctx( graph.ctx ), + + priorInteraction(false), + + /* Lists start empty. Will be filled by copy. */ + stateList(), + misfitList(), + + /* Copy in the entry points, + * pointers will be resolved later. */ + entryPoints(graph.entryPoints), + startState(graph.startState), + errState(0), + + /* Will be filled by copy. */ + finStateSet(), + + /* Misfit accounting is only on during merging. */ + misfitAccounting(false) +{ + /* Create the states and record their map in the original state. */ + StateList::Iter origState = graph.stateList; + for ( ; origState.lte(); origState++ ) { + /* Make the new state. */ + StateAp *newState = new StateAp( *origState ); + + /* Add the state to the list. */ + stateList.append( newState ); + + /* Set the mapsTo item of the old state. */ + origState->alg.stateMap = newState; + } + + /* Derefernce all the state maps. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + /* The points to the original in the src machine. The taget's duplicate + * is in the statemap. */ + StateAp *toState = trans->tdap()->toState != 0 ? + trans->tdap()->toState->alg.stateMap : 0; + + /* Attach The transition to the duplicate. */ + trans->tdap()->toState = 0; + attachTrans( state, toState, trans->tdap() ); + + } + else { + for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { + /* The points to the original in the src machine. The taget's duplicate + * is in the statemap. */ + StateAp *toState = cti->toState != 0 ? cti->toState->alg.stateMap : 0; + + /* Attach The transition to the duplicate. */ + cti->toState = 0; + attachTrans( state, toState, cti ); + } + } + } + + /* Fix the eofTarg, if set. */ + if ( state->eofTarget != 0 ) + state->eofTarget = state->eofTarget->alg.stateMap; + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter n = *state->nfaOut; n.lte(); n++ ) { + StateAp *targ = n->toState->alg.stateMap; + n->toState = 0; + attachToNfa( state, targ, n ); + } + } + } + + /* Fix the state pointers in the entry points array. */ + EntryMapEl *eel = entryPoints.data; + for ( int e = 0; e < entryPoints.length(); e++, eel++ ) { + /* Get the duplicate of the state. */ + eel->value = eel->value->alg.stateMap; + + /* Foreign in transitions must be built up when duping machines so + * increment it here. */ + eel->value->foreignInTrans += 1; + } + + /* Fix the start state pointer and the new start state's count of in + * transiions. */ + startState = startState->alg.stateMap; + startState->foreignInTrans += 1; + + /* Build the final state set. */ + StateSet::Iter st = graph.finStateSet; + for ( ; st.lte(); st++ ) + finStateSet.insert((*st)->alg.stateMap); +} + +/* Deletes all transition data then deletes each state. */ +FsmAp::~FsmAp() +{ + /* Delete all the transitions. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Iterate the out transitions, deleting them. */ + for ( TransList::Iter n, t = state->outList; t.lte(); ) { + n = t.next(); + if ( t->plain() ) + delete t->tdap(); + else + delete t->tcap(); + t = n; + } + state->outList.abandon(); + + if ( state->nfaIn != 0 ) { + delete state->nfaIn; + state->nfaIn = 0; + } + + if ( state->nfaOut != 0 ) { + state->nfaOut->empty(); + delete state->nfaOut; + state->nfaOut = 0; + } + } + + /* Delete all the states. */ + stateList.empty(); +} + +/* Set a state final. The state has its isFinState set to true and the state + * is added to the finStateSet. */ +void FsmAp::setFinState( StateAp *state ) +{ + /* Is it already a fin state. */ + if ( state->stateBits & STB_ISFINAL ) + return; + + state->stateBits |= STB_ISFINAL; + finStateSet.insert( state ); +} + +/* Set a state non-final. The has its isFinState flag set false and the state + * is removed from the final state set. */ +void FsmAp::unsetFinState( StateAp *state ) +{ + /* Is it already a non-final state? */ + if ( ! (state->stateBits & STB_ISFINAL) ) + return; + + /* When a state looses its final state status it must relinquish all the + * properties that are allowed only for final states. */ + clearOutData( state ); + + state->stateBits &= ~ STB_ISFINAL; + finStateSet.remove( state ); +} + +/* Set and unset a state as the start state. */ +void FsmAp::setStartState( StateAp *state ) +{ + /* Sould change from unset to set. */ + assert( startState == 0 ); + startState = state; + + if ( misfitAccounting ) { + /* If the number of foreign in transitions is about to go up to 1 then + * take it off the misfit list and put it on the head list. */ + if ( state->foreignInTrans == 0 ) + stateList.append( misfitList.detach( state ) ); + } + + /* Up the foreign in transitions to the state. */ + state->foreignInTrans += 1; +} + +void FsmAp::unsetStartState() +{ + /* Should change from set to unset. */ + assert( startState != 0 ); + + /* Decrement the entry's count of foreign entries. */ + startState->foreignInTrans -= 1; + + if ( misfitAccounting ) { + /* If the number of foreign in transitions just went down to 0 then take + * it off the main list and put it on the misfit list. */ + if ( startState->foreignInTrans == 0 ) + misfitList.append( stateList.detach( startState ) ); + } + + startState = 0; +} + +/* Associate an id with a state. Makes the state a named entry point. Has no + * effect if the entry point is already mapped to the state. */ +void FsmAp::setEntry( int id, StateAp *state ) +{ + /* Insert the id into the state. If the state is already labelled with id, + * nothing to do. */ + if ( state->entryIds.insert( id ) ) { + /* Insert the entry and assert that it succeeds. */ + entryPoints.insertMulti( id, state ); + + if ( misfitAccounting ) { + /* If the number of foreign in transitions is about to go up to 1 then + * take it off the misfit list and put it on the head list. */ + if ( state->foreignInTrans == 0 ) + stateList.append( misfitList.detach( state ) ); + } + + /* Up the foreign in transitions to the state. */ + state->foreignInTrans += 1; + } +} + +/* Remove the association of an id with a state. The state looses it's entry + * point status. Assumes that the id is indeed mapped to state. */ +void FsmAp::unsetEntry( int id, StateAp *state ) +{ + /* Find the entry point in on id. */ + EntryMapEl *enLow = 0, *enHigh = 0; + entryPoints.findMulti( id, enLow, enHigh ); + while ( enLow->value != state ) + enLow += 1; + + /* Remove the record from the map. */ + entryPoints.remove( enLow ); + + /* Remove the state's sense of the link. */ + state->entryIds.remove( id ); + state->foreignInTrans -= 1; + if ( misfitAccounting ) { + /* If the number of foreign in transitions just went down to 0 then take + * it off the main list and put it on the misfit list. */ + if ( state->foreignInTrans == 0 ) + misfitList.append( stateList.detach( state ) ); + } +} + +/* Remove all association of an id with states. Assumes that the id is indeed + * mapped to a state. */ +void FsmAp::unsetEntry( int id ) +{ + /* Find the entry point in on id. */ + EntryMapEl *enLow = 0, *enHigh = 0; + entryPoints.findMulti( id, enLow, enHigh ); + for ( EntryMapEl *mel = enLow; mel <= enHigh; mel++ ) { + /* Remove the state's sense of the link. */ + mel->value->entryIds.remove( id ); + mel->value->foreignInTrans -= 1; + if ( misfitAccounting ) { + /* If the number of foreign in transitions just went down to 0 + * then take it off the main list and put it on the misfit list. */ + if ( mel->value->foreignInTrans == 0 ) + misfitList.append( stateList.detach( mel->value ) ); + } + } + + /* Remove the records from the entry points map. */ + entryPoints.removeMulti( enLow, enHigh ); +} + + +void FsmAp::changeEntry( int id, StateAp *to, StateAp *from ) +{ + /* Find the entry in the entry map. */ + EntryMapEl *enLow = 0, *enHigh = 0; + entryPoints.findMulti( id, enLow, enHigh ); + while ( enLow->value != from ) + enLow += 1; + + /* Change it to the new target. */ + enLow->value = to; + + /* Remove from's sense of the link. */ + from->entryIds.remove( id ); + from->foreignInTrans -= 1; + if ( misfitAccounting ) { + /* If the number of foreign in transitions just went down to 0 then take + * it off the main list and put it on the misfit list. */ + if ( from->foreignInTrans == 0 ) + misfitList.append( stateList.detach( from ) ); + } + + /* Add to's sense of the link. */ + if ( to->entryIds.insert( id ) != 0 ) { + if ( misfitAccounting ) { + /* If the number of foreign in transitions is about to go up to 1 then + * take it off the misfit list and put it on the head list. */ + if ( to->foreignInTrans == 0 ) + stateList.append( misfitList.detach( to ) ); + } + + /* Up the foreign in transitions to the state. */ + to->foreignInTrans += 1; + } +} + + +/* Clear all entry points from a machine. */ +void FsmAp::unsetAllEntryPoints() +{ + for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) { + /* Kill all the state's entry points at once. */ + if ( en->value->entryIds.length() > 0 ) { + en->value->foreignInTrans -= en->value->entryIds.length(); + + if ( misfitAccounting ) { + /* If the number of foreign in transitions just went down to 0 + * then take it off the main list and put it on the misfit + * list. */ + if ( en->value->foreignInTrans == 0 ) + misfitList.append( stateList.detach( en->value ) ); + } + + /* Clear the set of ids out all at once. */ + en->value->entryIds.empty(); + } + } + + /* Now clear out the entry map all at once. */ + entryPoints.empty(); +} + +/* Assigning an epsilon transition into final states. */ +void FsmAp::epsilonTrans( int id ) +{ + for ( StateSet::Iter fs = finStateSet; fs.lte(); fs++ ) + (*fs)->epsilonTrans.append( id ); +} + +/* Mark all states reachable from state. Traverses transitions forward. Used + * for removing states that have no path into them. */ +void FsmAp::markReachableFromHere( StateAp *state ) +{ + /* Base case: return; */ + if ( state->stateBits & STB_ISMARKED ) + return; + + /* Set this state as processed. We are going to visit all states that this + * state has a transition to. */ + state->stateBits |= STB_ISMARKED; + + /* Recurse on all out transitions. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + markReachableFromHere( trans->tdap()->toState ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + markReachableFromHere( cond->toState ); + } + } + } + + /* Recurse on all states that compose us. */ + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter st = *state->nfaOut; st.lte(); st++ ) + markReachableFromHere( st->toState ); + } + + if ( state->stateDictEl != 0 ) { + for ( StateSet::Iter ss = state->stateDictEl->stateSet; ss.lte(); ss++ ) + markReachableFromHere( *ss ); + } +} + +/* Any transitions to another state? */ +bool FsmAp::anyRegularTransitions( StateAp *state ) +{ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + StateAp *toState = trans->tdap()->toState; + if ( toState != 0 ) + return true; + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + StateAp *toState = cond->toState; + if ( toState != 0 ) + return true; + } + } + } + return false; +} + +void FsmAp::markReachableFromHereStopFinal( StateAp *state ) +{ + /* Base case: return; */ + if ( state->stateBits & STB_ISMARKED ) + return; + + /* Set this state as processed. We are going to visit all states that this + * state has a transition to. */ + state->stateBits |= STB_ISMARKED; + + /* Recurse on all out transitions. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + StateAp *toState = trans->tdap()->toState; + if ( toState != 0 && !toState->isFinState() ) + markReachableFromHereStopFinal( toState ); + + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + StateAp *toState = cond->toState; + if ( toState != 0 && !toState->isFinState() ) + markReachableFromHereStopFinal( toState ); + } + } + } + + /* Recurse on all states that compose us. */ + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter st = *state->nfaOut; st.lte(); st++ ) + markReachableFromHereStopFinal( st->toState ); + } + + if ( state->stateDictEl != 0 ) { + for ( StateSet::Iter ss = state->stateDictEl->stateSet; ss.lte(); ss++ ) + markReachableFromHereStopFinal( *ss ); + } +} + +/* Mark all states reachable from state. Traverse transitions backwards. Used + * for removing dead end paths in graphs. */ +void FsmAp::markReachableFromHereReverse( StateAp *state ) +{ + /* Base case: return; */ + if ( state->stateBits & STB_ISMARKED ) + return; + + /* Set this state as processed. We are going to visit all states with + * transitions into this state. */ + state->stateBits |= STB_ISMARKED; + + /* Recurse on all items in transitions. */ + for ( TransInList::Iter t = state->inTrans; t.lte(); t++ ) + markReachableFromHereReverse( t->fromState ); + for ( CondInList::Iter t = state->inCond; t.lte(); t++ ) + markReachableFromHereReverse( t->fromState ); +} + +/* Determine if there are any entry points into a start state other than the + * start state. Setting starting transitions requires that the start state be + * isolated. In most cases a start state will already be isolated. */ +bool FsmAp::isStartStateIsolated() +{ + /* If there are any in transitions then the state is not isolated. */ + if ( startState->inTrans.head != 0 ) + return false; + if ( startState->inCond.head != 0 ) + return false; + + /* If there are any entry points then isolated. */ + if ( startState->entryIds.length() > 0 ) + return false; + + return true; +} + +/* Bring in other's entry points. Assumes others states are going to be + * copied into this machine. */ +void FsmAp::copyInEntryPoints( FsmAp *other ) +{ + /* Use insert multi because names are not unique. */ + for ( EntryMap::Iter en = other->entryPoints; en.lte(); en++ ) + entryPoints.insertMulti( en->key, en->value ); +} + + +void FsmAp::unsetAllFinStates() +{ + for ( StateSet::Iter st = finStateSet; st.lte(); st++ ) + (*st)->stateBits &= ~ STB_ISFINAL; + finStateSet.empty(); +} + +void FsmAp::setFinBits( int finStateBits ) +{ + for ( int s = 0; s < finStateSet.length(); s++ ) + finStateSet.data[s]->stateBits |= finStateBits; +} + +void FsmAp::unsetFinBits( int finStateBits ) +{ + for ( int s = 0; s < finStateSet.length(); s++ ) + finStateSet.data[s]->stateBits &= ~ finStateBits; +} + + +/* Tests the integrity of the transition lists and the fromStates. */ +void FsmAp::verifyIntegrity() +{ + int count = 0; + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Walk the out transitions and assert fromState is correct. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + assert( trans->tdap()->fromState == state ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + assert( cond->fromState == state ); + } + } + } + + /* Walk the inlist and assert toState is correct. */ + for ( TransInList::Iter t = state->inTrans; t.lte(); t++ ) { + assert( t->toState == state ); + } + for ( CondInList::Iter t = state->inCond; t.lte(); t++ ) { + assert( t->toState == state ); + } + + count += 1; + } + + assert( stateList.length() == count ); +} + +void FsmAp::verifyReachability() +{ + /* Mark all the states that can be reached + * through the set of entry points. */ + markReachableFromHere( startState ); + for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) + markReachableFromHere( en->value ); + + /* Check that everything got marked. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + /* Assert it got marked and then clear the mark. */ + assert( st->stateBits & STB_ISMARKED ); + st->stateBits &= ~ STB_ISMARKED; + } +} + +void FsmAp::verifyNoDeadEndStates() +{ + /* Mark all states that have paths to the final states. */ + for ( StateSet::Iter pst = finStateSet; pst.lte(); pst++ ) + markReachableFromHereReverse( *pst ); + + /* Start state gets honorary marking. Must be done AFTER recursive call. */ + startState->stateBits |= STB_ISMARKED; + + /* Make sure everything got marked. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + /* Assert the state got marked and unmark it. */ + assert( st->stateBits & STB_ISMARKED ); + st->stateBits &= ~ STB_ISMARKED; + } +} + +void FsmAp::depthFirstOrdering( StateAp *state ) +{ + /* Nothing to do if the state is already on the list. */ + if ( state->stateBits & STB_ONLIST ) + return; + + /* Doing depth first, put state on the list. */ + state->stateBits |= STB_ONLIST; + stateList.append( state ); + + /* Recurse on everything ranges. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) + depthFirstOrdering( trans->tdap()->toState ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) + depthFirstOrdering( cond->toState ); + } + } + } + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter s = *state->nfaOut; s.lte(); s++ ) + depthFirstOrdering( s->toState ); + } +} + +/* Ordering states by transition connections. */ +void FsmAp::depthFirstOrdering() +{ + /* Init on state list flags. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) + st->stateBits &= ~STB_ONLIST; + + /* Clear out the state list, we will rebuild it. */ + int stateListLen = stateList.length(); + stateList.abandon(); + + /* Add back to the state list from the start state and all other entry + * points. */ + if ( errState != 0 ) + depthFirstOrdering( errState ); + + depthFirstOrdering( startState ); + for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) + depthFirstOrdering( en->value ); + + /* Make sure we put everything back on. */ + assert( stateListLen == stateList.length() ); +} + +/* Stable sort the states by final state status. */ +void FsmAp::sortStatesByFinal() +{ + /* Move forward through the list and move final states onto the end. */ + StateAp *state = 0; + StateAp *next = stateList.head; + StateAp *last = stateList.tail; + while ( state != last ) { + /* Move forward and load up the next. */ + state = next; + next = state->next; + + /* Throw to the end? */ + if ( state->isFinState() ) { + stateList.detach( state ); + stateList.append( state ); + } + } +} + +void FsmAp::setStateNumbers( int base ) +{ + for ( StateList::Iter state = stateList; state.lte(); state++ ) + state->alg.stateNum = base++; +} + +bool FsmAp::checkErrTrans( StateAp *state, CondAp *trans ) +{ + /* Might go directly to error state. */ + if ( trans->toState == 0 ) + return true; + + return false; +} + +bool FsmAp::checkErrTrans( StateAp *state, TransAp *trans ) +{ + /* + * Look for a gap between this transition and the previous. + */ + if ( trans->prev == 0 ) { + /* If this is the first transition. */ + if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) + return true; + } + else { + /* Not the first transition. Compare against the prev. */ + TransAp *prev = trans->prev; + Key nextKey = prev->highKey; + ctx->keyOps->increment( nextKey ); + if ( ctx->keyOps->lt( nextKey, trans->lowKey ) ) + return true; + } + + if ( trans->plain() ) { + if ( trans->tdap()->toState == 0 ) + return true; + } + else { + /* Check for gaps in the condition list. */ + if ( trans->tcap()->condList.length() < trans->condFullSize() ) + return true; + + /* Check all destinations. */ + for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { + if ( checkErrTrans( state, cti ) ) + return true; + } + } + + return false; +} + +bool FsmAp::checkErrTransFinish( StateAp *state ) +{ + /* Check if there are any ranges already. */ + if ( state->outList.length() == 0 ) + return true; + else { + /* Get the last and check for a gap on the end. */ + TransAp *last = state->outList.tail; + if ( ctx->keyOps->lt( last->highKey, ctx->keyOps->maxKey ) ) + return true; + } + return 0; +} + +bool FsmAp::hasErrorTrans() +{ + bool result; + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + for ( TransList::Iter tr = st->outList; tr.lte(); tr++ ) { + result = checkErrTrans( st, tr ); + if ( result ) + return true; + } + result = checkErrTransFinish( st ); + if ( result ) + return true; + } + return false; +} diff --git a/src/libfsm/fsmcond.cc b/src/libfsm/fsmcond.cc new file mode 100644 index 00000000..b2339c12 --- /dev/null +++ b/src/libfsm/fsmcond.cc @@ -0,0 +1,520 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Setting conditions and merging states with conditions are similar activities + * when expressed in code. The critical difference is that a merge is a union + * of multiple paths. We have to take both paths. Setting a condition, however, + * is a restriction. We have to expand the transition to follow both values of + * the condition, then remove the one that is not set. + */ + +#include "fsmgraph.h" +#include "mergesort.h" +#include "parsedata.h" + +#include +#include + +long TransAp::condFullSize() + { return condSpace == 0 ? 1 : condSpace->fullSize(); } + +void FsmAp::expandCondKeys( CondKeySet &condKeys, CondSpace *fromSpace, + CondSpace *mergedSpace ) +{ + CondSet fromCS, mergedCS; + + if ( fromSpace != 0 ) + fromCS.insert( fromSpace->condSet ); + + if ( mergedSpace != 0 ) + mergedCS.insert( mergedSpace->condSet ); + + /* Need to transform condition element to the merged set. */ + for ( int cti = 0; cti < condKeys.length(); cti++ ) { + long origVal = condKeys[cti]; + long newVal = 0; + + /* Iterate the bit positions in the from set. */ + for ( CondSet::Iter csi = fromCS; csi.lte(); csi++ ) { + /* If set, find it in the merged set and flip the bit to 1. */ + if ( origVal & (1 << csi.pos()) ) { + /* The condition is set. Find the bit position in the merged + * set. */ + Action **cim = mergedCS.find( *csi ); + long bitPos = (cim - mergedCS.data); + newVal |= 1 << bitPos; + } + } + + if ( origVal != newVal ) + condKeys[cti] = newVal; + } + + /* Need to double up the whole transition list for each condition test in + * merged that is not in from. The one we add has the bit in question set. + * */ + for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { + Action **cim = fromCS.find( *csi ); + if ( cim == 0 ) { + CondKeySet newItems; + newItems.append( condKeys ); + for ( int cti = 0; cti < condKeys.length(); cti++ ) { + int key = condKeys[cti] | (1 << csi.pos()); + newItems.insert( key ); + } + + condKeys.setAs( newItems ); + } + } +} + +void FsmAp::expandConds( StateAp *fromState, TransAp *trans, + CondSpace *fromSpace, CondSpace *mergedSpace ) +{ + CondSet fromCS, mergedCS; + + if ( fromSpace != 0 ) + fromCS.insert( fromSpace->condSet ); + + if ( mergedSpace != 0 ) + mergedCS.insert( mergedSpace->condSet ); + + /* Need to transform condition element to the merged set. */ + for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { + long origVal = cti->key.getVal(); + long newVal = 0; + + /* Iterate the bit positions in the from set. */ + for ( CondSet::Iter csi = fromCS; csi.lte(); csi++ ) { + /* If set, find it in the merged set and flip the bit to 1. */ + if ( origVal & (1 << csi.pos()) ) { + /* The condition is set. Find the bit position in the merged + * set. */ + Action **cim = mergedCS.find( *csi ); + long bitPos = (cim - mergedCS.data); + newVal |= 1 << bitPos; + } + } + + if ( origVal != newVal ) + cti->key = newVal; + } + + /* Need to double up the whole transition list for each condition test in + * merged that is not in from. The one we add has the bit in question set. + * */ + for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { + Action **cim = fromCS.find( *csi ); + if ( cim == 0 ) { + CondList newItems; + for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { + /* Sub-transition for conditions. */ + CondAp *cond = new CondAp( trans ); + + /* Attach only if our caller wants the expanded transitions + * attached. */ + attachTrans( fromState, cti->toState, cond ); + + /* Call the user callback to add in the original source transition. */ + addInTrans( cond, cti.ptr ); + + cond->key = cti->key.getVal() | (1 << csi.pos()); + + newItems.append( cond ); + } + + /* Merge newItems in. Both the condList and newItems are sorted. Make + * a sorted list out of them. */ + CondAp *dest = trans->tcap()->condList.head; + while ( dest != 0 && newItems.head != 0 ) { + if ( newItems.head->key.getVal() > dest->key.getVal() ) { + dest = dest->next; + } + else { + /* Pop the item for insertion. */ + CondAp *ins = newItems.detachFirst(); + trans->tcap()->condList.addBefore( dest, ins ); + } + } + + /* Append the rest of the items. */ + trans->tcap()->condList.append( newItems ); + } + } +} + +CondSpace *FsmAp::expandCondSpace( TransAp *destTrans, TransAp *srcTrans ) +{ + CondSet destCS, srcCS; + CondSet mergedCS; + + if ( destTrans->condSpace != 0 ) + destCS.insert( destTrans->condSpace->condSet ); + + if ( srcTrans->condSpace != 0 ) + srcCS.insert( srcTrans->condSpace->condSet ); + + mergedCS.insert( destCS ); + mergedCS.insert( srcCS ); + + return addCondSpace( mergedCS ); +} + +StateAp *FsmAp::copyStateForExpansion( StateAp *srcState ) +{ + StateAp *newState = new StateAp(); + newState->outCondSpace = srcState->outCondSpace; + newState->outCondKeys = srcState->outCondKeys; + return newState; +} + +void FsmAp::mergeOutConds( StateAp *destState, StateAp *srcState, bool leaving ) +{ + if ( destState == srcState ) + return; + + bool bothFinal = destState->isFinState() && srcState->isFinState(); + bool unionOp = !leaving; + + CondSet destCS, srcCS; + CondSet mergedCS; + + if ( destState->outCondSpace != 0 ) + destCS.insert( destState->outCondSpace->condSet ); + + if ( srcState->outCondSpace != 0 ) + srcCS.insert( srcState->outCondSpace->condSet ); + + mergedCS.insert( destCS ); + mergedCS.insert( srcCS ); + + if ( mergedCS.length() > 0 ) { + CondSpace *mergedSpace = addCondSpace( mergedCS ); + + CondSpace *srcSpace = srcState->outCondSpace; + CondKeySet srcKeys = srcState->outCondKeys; + + if ( srcSpace != mergedSpace ) { + /* Prep the key list with zero item if necessary. */ + if ( srcSpace == 0 ) + srcKeys.append( 0 ); + + expandCondKeys( srcKeys, srcSpace, mergedSpace ); + } + + if ( destState->outCondSpace != mergedSpace ) { + /* Prep the key list with zero item if necessary. */ + if ( destState->outCondSpace == 0 ) + destState->outCondKeys.append( 0 ); + + /* Now expand the dest. */ + expandCondKeys( destState->outCondKeys, destState->outCondSpace, mergedSpace ); + } + + destState->outCondSpace = mergedSpace; + + if ( unionOp && bothFinal ) { + /* Keys can come from either. */ + for ( CondKeySet::Iter c = srcKeys; c.lte(); c++ ) + destState->outCondKeys.insert( *c ); + } + else { + /* Keys need to be in both sets. */ + for ( long c = 0; c < destState->outCondKeys.length(); ) { + if ( !srcKeys.find( destState->outCondKeys[c] ) ) + destState->outCondKeys.CondKeyVect::remove( c, 1 ); + else + c++; + } + } + } +} + +CondSpace *FsmAp::addCondSpace( const CondSet &condSet ) +{ + CondSpace *condSpace = ctx->condData->condSpaceMap.find( condSet ); + if ( condSpace == 0 ) { + condSpace = new CondSpace( condSet ); + ctx->condData->condSpaceMap.insert( condSpace ); + } + return condSpace; +} + +TransDataAp *FsmAp::convertToTransAp( StateAp *from, CondAp *cond ) +{ + TransDataAp *newTrans = new TransDataAp(); + newTrans->lowKey = cond->transAp->lowKey; + newTrans->highKey = cond->transAp->highKey; + + newTrans->lmActionTable.setActions( cond->lmActionTable ); + newTrans->actionTable.setActions( cond->actionTable ); + newTrans->priorTable.setPriors( cond->priorTable ); + + attachTrans( from, cond->toState, newTrans ); + + /* Detach in list. */ + detachTrans( from, cond->toState, cond ); + delete cond->transAp; + delete cond; + + return newTrans; +} + +TransCondAp *FsmAp::convertToCondAp( StateAp *from, TransDataAp *trans ) +{ + TransCondAp *newTrans = new TransCondAp(); + newTrans->lowKey = trans->lowKey; + newTrans->highKey = trans->highKey; + newTrans->condSpace = trans->condSpace; + + CondAp *newCond = new CondAp( newTrans ); + newCond->key = 0; + newTrans->condList.append( newCond ); + + newCond->lmActionTable.setActions( trans->lmActionTable ); + newCond->actionTable.setActions( trans->actionTable ); + newCond->priorTable.setPriors( trans->priorTable ); + + attachTrans( from, trans->toState, newCond ); + + /* Detach in list. */ + detachTrans( from, trans->toState, trans ); + delete trans; + + return newTrans; +} + +void FsmAp::convertToCondAp( StateAp *state ) +{ + /* First replace TransDataAp with cond versions. */ + TransList destList; + for ( TransList::Iter tr = state->outList; tr.lte(); ) { + TransList::Iter next = tr.next(); + if ( tr->plain() ) { + TransCondAp *newTrans = convertToCondAp( state, tr->tdap() ); + destList.append( newTrans ); + } + else { + destList.append( tr ); + } + + tr = next; + } + + state->outList.abandon(); + state->outList.transfer( destList ); +} + +void FsmAp::doEmbedCondition( StateAp *state, + const CondSet &set, const CondKeySet &vals ) +{ + convertToCondAp( state ); + + for ( TransList::Iter tr = state->outList; tr.lte(); tr++ ) { + + /* The source (being embedded). */ + CondSpace *srcSpace = addCondSpace( set ); + CondKeySet srcVals = vals; + + /* Extract cond key set from the condition list. We will use this to + * compute the intersection of the cond keys. */ + CondSpace *trSpace = tr->condSpace; + CondKeySet trVals; + if ( tr->condSpace == 0 ) + trVals.append( 0 ); + else { + for ( CondList::Iter cti = tr->tcap()->condList; cti.lte(); cti++ ) { + long key = cti->key.getVal(); + trVals.append( key ); + } + } + + /* Construct merged. */ + CondSet mergedCS; + if ( tr->condSpace != 0 ) + mergedCS.insert( tr->condSpace->condSet ); + mergedCS.insert( set ); + + CondSpace *mergedSpace = addCondSpace( mergedCS ); + + if ( srcSpace != mergedSpace ) { + /* Prep the key list with zero item if necessary. */ + if ( srcSpace == 0 ) + srcVals.append( 0 ); + + expandCondKeys( srcVals, srcSpace, mergedSpace ); + } + + if ( trSpace != mergedSpace ) { + /* Don't need to prep the key list with zero item, will be there + * (see above). */ + expandCondKeys( trVals, trSpace, mergedSpace ); + } + + /* Implement AND, in two parts. */ + CondKeySet newItems; + for ( CondKeySet::Iter c = srcVals; c.lte(); c++ ) { + if ( trVals.find( *c ) ) + newItems.insert( *c ); + } + + for ( CondKeySet::Iter c = trVals; c.lte(); c++ ) { + if ( srcVals.find( *c ) ) + newItems.insert( *c ); + } + + /* Expand the transitions, then we remove anything not in the computed + * list of keys. This approach allows us to embed combinations of + * senses, rather than cond-sense pairs. Necessary for out conditions. */ + CondSpace *orig = tr->condSpace; + tr->condSpace = mergedSpace; + expandConds( state, tr, orig, mergedSpace ); + + /* After expansion, remove anything not in newItems. */ + for ( CondList::Iter cti = tr->tcap()->condList; cti.lte(); ) { + long key = cti->key.getVal(); + + if ( !newItems.find( key ) ) { + /* Delete. */ + CondList::Iter next = cti.next(); + + CondAp *cond = cti; + detachTrans( state, cond->toState, cond ); + tr->tcap()->condList.detach( cond ); + delete cond; + + cti = next; + } + else { + /* Leave alone. */ + cti++; + } + } + } +} + +FsmRes FsmAp::embedCondition( FsmAp *fsm, StateAp *state, const CondSet &set, const CondKeySet &vals ) +{ + /* Turn on misfit accounting to possibly catch the old start state. */ + fsm->setMisfitAccounting( true ); + + /* Worker. */ + fsm->doEmbedCondition( state, set, vals ); + + /* Fill in any states that were newed up as combinations of others. */ + FsmRes res = fillInStates( fsm ); + if ( !res.success() ) + return res; + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + return res; +} + +void FsmAp::addOutCondition( StateAp *state, Action *condAction, bool sense ) +{ + CondSet origCS; + if ( state->outCondSpace != 0 ) + origCS.insert( state->outCondSpace->condSet ); + + CondSet mergedCS; + mergedCS.insert( origCS ); + + bool added = mergedCS.insert( condAction ); + if ( !added ) { + + /* Already exists in the cond set. For every transition, if the + * sense is identical to what we are embedding, leave it alone. If + * the sense is opposite, delete it. */ + + /* Find the position. */ + long pos = 0; + for ( CondSet::Iter csi = mergedCS; csi.lte(); csi++ ) { + if ( *csi == condAction ) + pos = csi.pos(); + } + + for ( int cti = 0; cti < state->outCondKeys.length(); ) { + long key = state->outCondKeys[cti]; + + bool set = ( key & ( 1 << pos ) ) != 0; + if ( sense xor set ) { + /* Delete. */ + state->outCondKeys.CondKeyVect::remove( cti, 1 ); + } + else { + /* Leave alone. */ + cti++; + } + } + } + else { + /* Does not exist in the cond set. We will add it. */ + + if ( state->outCondSpace == 0 ) { + /* Note that unlike transitions, we start here with an empty key + * list. Add the item */ + state->outCondKeys.append( 0 ); + } + + /* Allocate a cond space for the merged set. */ + CondSpace *mergedCondSpace = addCondSpace( mergedCS ); + state->outCondSpace = mergedCondSpace; + + /* FIXME: assumes one item always. */ + + /* Translate original condition values, making space for the new bit + * (possibly) introduced by the condition embedding. */ + for ( int cti = 0; cti < state->outCondKeys.length(); cti++ ) { + long origVal = state->outCondKeys[cti]; + long newVal = 0; + + /* For every set bit in the orig, find it's position in the merged + * and set the bit appropriately. */ + for ( CondSet::Iter csi = origCS; csi.lte(); csi++ ) { + /* If set, find it in the merged set and flip the bit to 1. If + * not set, there is nothing to do (convenient eh?) */ + if ( origVal & (1 << csi.pos()) ) { + /* The condition is set. Find the bit position in the + * merged set. */ + Action **cim = mergedCS.find( *csi ); + long bitPos = (cim - mergedCS.data); + newVal |= 1 << bitPos; + } + } + + if ( origVal != newVal ) + state->outCondKeys[cti] = newVal; + + /* Now set the new bit appropriately. Since it defaults to zero we + * only take action if sense is positive. */ + if ( sense ) { + Action **cim = mergedCS.find( condAction ); + int pos = cim - mergedCS.data; + state->outCondKeys[cti] = state->outCondKeys[cti] | (1 << pos); + } + } + } +} diff --git a/src/libfsm/fsmgraph.cc b/src/libfsm/fsmgraph.cc new file mode 100644 index 00000000..819bfa96 --- /dev/null +++ b/src/libfsm/fsmgraph.cc @@ -0,0 +1,1948 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "fsmgraph.h" +#include "mergesort.h" +#include "action.h" + +using std::endl; + +Action::~Action() +{ + /* If we were created by substitution of another action then we don't own the inline list. */ + if ( substOf == 0 && inlineList != 0 ) { + inlineList->empty(); + delete inlineList; + inlineList = 0; + } +} + +InlineItem::~InlineItem() +{ + if ( children != 0 ) { + children->empty(); + delete children; + } +} + +/* Make a new state. The new state will be put on the graph's + * list of state. The new state can be created final or non final. */ +StateAp *FsmAp::addState() +{ + /* Make the new state to return. */ + StateAp *state = new StateAp(); + + if ( misfitAccounting ) { + /* Create the new state on the misfit list. All states are created + * with no foreign in transitions. */ + misfitList.append( state ); + } + else { + /* Create the new state. */ + stateList.append( state ); + } + + return state; +} + +/* Construct an FSM that is the concatenation of an array of characters. A new + * machine will be made that has len+1 states with one transition between each + * state for each integer in str. IsSigned determines if the integers are to + * be considered as signed or unsigned ints. */ +FsmAp *FsmAp::concatFsm( FsmCtx *ctx, Key *str, int len ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Make the first state and set it as the start state. */ + StateAp *last = fsm->addState(); + fsm->setStartState( last ); + + /* Attach subsequent states. */ + for ( int i = 0; i < len; i++ ) { + StateAp *newState = fsm->addState(); + fsm->attachNewTrans( last, newState, str[i], str[i] ); + last = newState; + } + + /* Make the last state the final state. */ + fsm->setFinState( last ); + + return fsm; +} + +/* Case insensitive version of concatFsm. */ +FsmAp *FsmAp::concatFsmCI( FsmCtx *ctx, Key *str, int len ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Make the first state and set it as the start state. */ + StateAp *last = fsm->addState(); + fsm->setStartState( last ); + + /* Attach subsequent states. */ + for ( int i = 0; i < len; i++ ) { + StateAp *newState = fsm->addState(); + + KeySet keySet( ctx->keyOps ); + if ( str[i].isLower() ) + keySet.insert( str[i].toUpper() ); + if ( str[i].isUpper() ) + keySet.insert( str[i].toLower() ); + keySet.insert( str[i] ); + + for ( int i = 0; i < keySet.length(); i++ ) + fsm->attachNewTrans( last, newState, keySet[i], keySet[i] ); + + last = newState; + } + + /* Make the last state the final state. */ + fsm->setFinState( last ); + + return fsm; +} + + +/* Construct a machine that matches one character. A new machine will be made + * that has two states with a single transition between the states. */ +FsmAp *FsmAp::concatFsm( FsmCtx *ctx, Key chr ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Two states first start, second final. */ + fsm->setStartState( fsm->addState() ); + + StateAp *end = fsm->addState(); + fsm->setFinState( end ); + + /* Attach on the character. */ + fsm->attachNewTrans( fsm->startState, end, chr, chr ); + + return fsm; +} + +/* Case insensitive version of single-char concat FSM. */ +FsmAp *FsmAp::concatFsmCI( FsmCtx *ctx, Key chr ) +{ + return concatFsmCI( ctx, &chr, 1 ); +} + + +/* Construct a machine that matches any character in set. A new machine will + * be made that has two states and len transitions between the them. The set + * should be ordered correctly accroding to KeyOps and should not contain + * any duplicates. */ +FsmAp *FsmAp::orFsm( FsmCtx *ctx, Key *set, int len ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Two states first start, second final. */ + fsm->setStartState( fsm->addState() ); + + StateAp *end = fsm->addState(); + fsm->setFinState( end ); + + for ( int i = 1; i < len; i++ ) + assert( ctx->keyOps->lt( set[i-1], set[i] ) ); + + /* Attach on all the integers in the given string of ints. */ + for ( int i = 0; i < len; i++ ) + fsm->attachNewTrans( fsm->startState, end, set[i], set[i] ); + + return fsm; +} + +FsmAp *FsmAp::dotFsm( FsmCtx *ctx ) +{ + FsmAp *retFsm = FsmAp::rangeFsm( ctx, + ctx->keyOps->minKey, ctx->keyOps->maxKey ); + return retFsm; +} + +FsmAp *FsmAp::dotStarFsm( FsmCtx *ctx ) +{ + FsmAp *retFsm = FsmAp::rangeStarFsm( ctx, + ctx->keyOps->minKey, ctx->keyOps->maxKey ); + return retFsm; +} + +/* Construct a machine that matches a range of characters. A new machine will + * be made with two states and a range transition between them. The range will + * match any characters from low to high inclusive. Low should be less than or + * equal to high otherwise undefined behaviour results. IsSigned determines + * if the integers are to be considered as signed or unsigned ints. */ +FsmAp *FsmAp::rangeFsm( FsmCtx *ctx, Key low, Key high ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Two states first start, second final. */ + fsm->setStartState( fsm->addState() ); + + StateAp *end = fsm->addState(); + fsm->setFinState( end ); + + /* Attach using the range of characters. */ + fsm->attachNewTrans( fsm->startState, end, low, high ); + + return fsm; +} + +FsmAp *FsmAp::notRangeFsm( FsmCtx *ctx, Key low, Key high ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Two states first start, second final. */ + fsm->setStartState( fsm->addState() ); + + StateAp *end = fsm->addState(); + fsm->setFinState( end ); + + /* Attach using the range of characters. */ + if ( ctx->keyOps->lt( ctx->keyOps->minKey, low ) ) { + ctx->keyOps->decrement( low ); + fsm->attachNewTrans( fsm->startState, end, ctx->keyOps->minKey, low ); + } + + if ( ctx->keyOps->lt( high, ctx->keyOps->maxKey ) ) { + ctx->keyOps->increment( high ); + fsm->attachNewTrans( fsm->startState, end, high, ctx->keyOps->maxKey ); + } + + return fsm; +} + + +FsmAp *FsmAp::rangeFsmCI( FsmCtx *ctx, Key lowKey, Key highKey ) +{ + FsmAp *retFsm = rangeFsm( ctx, lowKey, highKey ); + + /* Union the portion that covers alphas. */ + if ( lowKey.getVal() <= 'z' ) { + int low, high; + if ( lowKey.getVal() <= 'a' ) + low = 'a'; + else + low = lowKey.getVal(); + + if ( highKey.getVal() >= 'a' ) { + if ( highKey.getVal() >= 'z' ) + high = 'z'; + else + high = highKey.getVal(); + + /* Add in upper(low) .. upper(high) */ + + FsmAp *addFsm = FsmAp::rangeFsm( ctx, toupper(low), toupper(high) ); + FsmRes res = FsmAp::unionOp( retFsm, addFsm ); + retFsm = res.fsm; + } + } + + if ( lowKey.getVal() <= 'Z' ) { + int low, high; + if ( lowKey.getVal() <= 'A' ) + low = 'A'; + else + low = lowKey.getVal(); + + if ( highKey.getVal() >= 'A' ) { + if ( highKey.getVal() >= 'Z' ) + high = 'Z'; + else + high = highKey.getVal(); + + /* Add in lower(low) .. lower(high) */ + FsmAp *addFsm = FsmAp::rangeFsm( ctx, tolower(low), tolower(high) ); + FsmRes res = FsmAp::unionOp( retFsm, addFsm ); + retFsm = res.fsm; + } + } + + return retFsm; +} + +/* Construct a machine that a repeated range of characters. */ +FsmAp *FsmAp::rangeStarFsm( FsmCtx *ctx, Key low, Key high ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* One state which is final and is the start state. */ + fsm->setStartState( fsm->addState() ); + fsm->setFinState( fsm->startState ); + + /* Attach start to start using range of characters. */ + fsm->attachNewTrans( fsm->startState, fsm->startState, low, high ); + + return fsm; +} + +/* Construct a machine that matches the empty string. A new machine will be + * made with only one state. The new state will be both a start and final + * state. IsSigned determines if the machine has a signed or unsigned + * alphabet. Fsm operations must be done on machines with the same alphabet + * signedness. */ +FsmAp *FsmAp::lambdaFsm( FsmCtx *ctx ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Give it one state with no transitions making it + * the start state and final state. */ + fsm->setStartState( fsm->addState() ); + fsm->setFinState( fsm->startState ); + + return fsm; +} + +/* Construct a machine that matches nothing at all. A new machine will be + * made with only one state. It will not be final. */ +FsmAp *FsmAp::emptyFsm( FsmCtx *ctx ) +{ + FsmAp *fsm = new FsmAp( ctx ); + + /* Give it one state with no transitions making it + * the start state and final state. */ + fsm->setStartState( fsm->addState() ); + + return fsm; +} + +void FsmAp::transferOutData( StateAp *destState, StateAp *srcState ) +{ + for ( TransList::Iter trans = destState->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) { + /* Get the actions data from the outActionTable. */ + trans->tdap()->actionTable.setActions( srcState->outActionTable ); + + /* Get the priorities from the outPriorTable. */ + trans->tdap()->priorTable.setPriors( srcState->outPriorTable ); + } + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) { + /* Get the actions data from the outActionTable. */ + cond->actionTable.setActions( srcState->outActionTable ); + + /* Get the priorities from the outPriorTable. */ + cond->priorTable.setPriors( srcState->outPriorTable ); + } + } + } + } + + if ( destState->nfaOut != 0 ) { + for ( NfaTransList::Iter na = *destState->nfaOut; na.lte(); na++ ) + transferOutToNfaTrans( na, srcState ); + } +} + +/* Union worker used by union, set diff (subtract) and intersection. */ +FsmRes FsmAp::doUnion( FsmAp *fsm, FsmAp *other ) +{ + /* Build a state set consisting of both start states */ + StateSet startStateSet; + startStateSet.insert( fsm->startState ); + startStateSet.insert( other->startState ); + + /* Both of the original start states loose their start state status. */ + fsm->unsetStartState(); + other->unsetStartState(); + + /* Bring in the rest of other's entry points. */ + fsm->copyInEntryPoints( other ); + other->entryPoints.empty(); + + /* Merge the lists. This will move all the states from other + * into this. No states will be deleted. */ + fsm->stateList.append( other->stateList ); + fsm->misfitList.append( other->misfitList ); + + /* Move the final set data from other into this. */ + fsm->finStateSet.insert(other->finStateSet); + other->finStateSet.empty(); + + /* Since other's list is empty, we can delete the fsm without + * affecting any states. */ + delete other; + + /* Create a new start state. */ + fsm->setStartState( fsm->addState() ); + + /* Merge the start states. */ + fsm->mergeStateList( fsm->startState, startStateSet.data, startStateSet.length() ); + + /* Fill in any new states made from merging. */ + return fillInStates( fsm ); +} + +bool FsmAp::inEptVect( EptVect *eptVect, StateAp *state ) +{ + if ( eptVect != 0 ) { + /* Vect is there, walk it looking for state. */ + for ( int i = 0; i < eptVect->length(); i++ ) { + if ( eptVect->data[i].targ == state ) + return true; + } + } + return false; +} + +/* Fill epsilon vectors in a root state from a given starting point. Epmploys + * a depth first search through the graph of epsilon transitions. */ +void FsmAp::epsilonFillEptVectFrom( StateAp *root, StateAp *from, bool parentLeaving ) +{ + /* Walk the epsilon transitions out of the state. */ + for ( EpsilonTrans::Iter ep = from->epsilonTrans; ep.lte(); ep++ ) { + /* Find the entry point, if the it does not resove, ignore it. */ + EntryMapEl *enLow, *enHigh; + if ( entryPoints.findMulti( *ep, enLow, enHigh ) ) { + /* Loop the targets. */ + for ( EntryMapEl *en = enLow; en <= enHigh; en++ ) { + /* Do not add the root or states already in eptVect. */ + StateAp *targ = en->value; + if ( targ != from && !inEptVect(root->eptVect, targ) ) { + /* Maybe need to create the eptVect. */ + if ( root->eptVect == 0 ) + root->eptVect = new EptVect(); + + /* If moving to a different graph or if any parent is + * leaving then we are leaving. */ + bool leaving = parentLeaving || + root->owningGraph != targ->owningGraph; + + /* All ok, add the target epsilon and recurse. */ + root->eptVect->append( EptVectEl(targ, leaving) ); + epsilonFillEptVectFrom( root, targ, leaving ); + } + } + } + } +} + +void FsmAp::shadowReadWriteStates() +{ + /* Init isolatedShadow algorithm data. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) + st->isolatedShadow = 0; + + /* Any states that may be both read from and written to must + * be shadowed. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + /* Find such states by looping through stateVect lists, which give us + * the states that will be read from. May cause us to visit the states + * that we are interested in more than once. */ + if ( st->eptVect != 0 ) { + /* For all states that will be read from. */ + for ( EptVect::Iter ept = *st->eptVect; ept.lte(); ept++ ) { + /* Check for read and write to the same state. */ + StateAp *targ = ept->targ; + if ( targ->eptVect != 0 ) { + /* State is to be written to, if the shadow is not already + * there, create it. */ + if ( targ->isolatedShadow == 0 ) { + StateAp *shadow = addState(); + mergeStates( shadow, targ ); + targ->isolatedShadow = shadow; + } + + /* Write shadow into the state vector so that it is the + * state that the epsilon transition will read from. */ + ept->targ = targ->isolatedShadow; + } + } + } + } +} + +void FsmAp::resolveEpsilonTrans() +{ + /* Walk the state list and invoke recursive worker on each state. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) + epsilonFillEptVectFrom( st, st, false ); + + /* Prevent reading from and writing to of the same state. */ + shadowReadWriteStates(); + + /* For all states that have epsilon transitions out, draw the transitions, + * clear the epsilon transitions. */ + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + /* If there is a state vector, then create the pre-merge state. */ + if ( st->eptVect != 0 ) { + /* Merge all the epsilon targets into the state. */ + for ( EptVect::Iter ept = *st->eptVect; ept.lte(); ept++ ) { + if ( ept->leaving ) + mergeStatesLeaving( st, ept->targ ); + else + mergeStates( st, ept->targ ); + } + + /* Clean up the target list. */ + delete st->eptVect; + st->eptVect = 0; + } + + /* Clear the epsilon transitions vector. */ + st->epsilonTrans.empty(); + } +} + +FsmRes FsmAp::applyNfaTrans( FsmAp *fsm, StateAp *fromState, StateAp *toState, NfaTrans *nfaTrans ) +{ + fsm->setMisfitAccounting( true ); + + fsm->mergeStates( fromState, toState, false ); + + /* Epsilons can caused merges which leave behind unreachable states. */ + FsmRes res = FsmAp::fillInStates( fsm ); + if ( !res.success() ) + return res; + + /* Can nuke the epsilon transition that we will never + * follow. */ + fsm->detachFromNfa( fromState, toState, nfaTrans ); + fromState->nfaOut->detach( nfaTrans ); + delete nfaTrans; + + if ( fromState->nfaOut->length() == 0 ) { + delete fromState->nfaOut; + fromState->nfaOut = 0; + } + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + return FsmRes( FsmRes::Fsm(), fsm ); +} + +void FsmAp::globOp( FsmAp **others, int numOthers ) +{ + for ( int m = 0; m < numOthers; m++ ) { + assert( ctx == others[m]->ctx ); + } + + /* All other machines loose start states status. */ + for ( int m = 0; m < numOthers; m++ ) + others[m]->unsetStartState(); + + /* Bring the other machines into this. */ + for ( int m = 0; m < numOthers; m++ ) { + /* Bring in the rest of other's entry points. */ + copyInEntryPoints( others[m] ); + others[m]->entryPoints.empty(); + + /* Merge the lists. This will move all the states from other into + * this. No states will be deleted. */ + stateList.append( others[m]->stateList ); + assert( others[m]->misfitList.length() == 0 ); + + /* Move the final set data from other into this. */ + finStateSet.insert( others[m]->finStateSet ); + others[m]->finStateSet.empty(); + + /* Since other's list is empty, we can delete the fsm without + * affecting any states. */ + delete others[m]; + } +} + +/* Used near the end of an fsm construction. Any labels that are still around + * are referenced only by gotos and calls and they need to be made into + * deterministic entry points. */ +void FsmAp::deterministicEntry() +{ + /* States may loose their entry points, turn on misfit accounting. */ + setMisfitAccounting( true ); + + /* Get a copy of the entry map then clear all the entry points. As we + * iterate the old entry map finding duplicates we will add the entry + * points for the new states that we create. */ + EntryMap prevEntry = entryPoints; + unsetAllEntryPoints(); + + for ( int enId = 0; enId < prevEntry.length(); ) { + /* Count the number of states on this entry key. */ + int highId = enId; + while ( highId < prevEntry.length() && prevEntry[enId].key == prevEntry[highId].key ) + highId += 1; + + int numIds = highId - enId; + if ( numIds == 1 ) { + /* Only a single entry point, just set the entry. */ + setEntry( prevEntry[enId].key, prevEntry[enId].value ); + } + else { + /* Multiple entry points, need to create a new state and merge in + * all the targets of entry points. */ + StateAp *newEntry = addState(); + for ( int en = enId; en < highId; en++ ) + mergeStates( newEntry, prevEntry[en].value ); + + /* Add the new state as the single entry point. */ + setEntry( prevEntry[enId].key, newEntry ); + } + + enId += numIds; + } + + /* The old start state may be unreachable. Remove the misfits and turn off + * misfit accounting. */ + removeMisfits(); + setMisfitAccounting( false ); +} + +/* Unset any final states that are no longer to be final due to final bits. */ +void FsmAp::unsetKilledFinals() +{ + /* Duplicate the final state set before we begin modifying it. */ + StateSet fin( finStateSet ); + + for ( int s = 0; s < fin.length(); s++ ) { + /* Check for killing bit. */ + StateAp *state = fin.data[s]; + if ( state->stateBits & STB_GRAPH1 ) { + /* One final state is a killer, set to non-final. */ + unsetFinState( state ); + } + + /* Clear all killing bits. Non final states should never have had those + * state bits set in the first place. */ + state->stateBits &= ~STB_GRAPH1; + } +} + +/* Unset any final states that are no longer to be final due to final bits. */ +void FsmAp::unsetIncompleteFinals() +{ + /* Duplicate the final state set before we begin modifying it. */ + StateSet fin( finStateSet ); + + for ( int s = 0; s < fin.length(); s++ ) { + /* Check for one set but not the other. */ + StateAp *state = fin.data[s]; + if ( state->stateBits & STB_BOTH && + (state->stateBits & STB_BOTH) != STB_BOTH ) + { + /* One state wants the other but it is not there. */ + unsetFinState( state ); + } + + /* Clear wanting bits. Non final states should never have had those + * state bits set in the first place. */ + state->stateBits &= ~STB_BOTH; + } +} + +/* Kleene star operator. Makes this machine the kleene star of itself. Any + * transitions made going out of the machine and back into itself will be + * notified that they are leaving transitions by having the leavingFromState + * callback invoked. */ +FsmRes FsmAp::starOp( FsmAp *fsm ) +{ + /* The start func orders need to be shifted before doing the star. */ + fsm->ctx->curActionOrd += fsm->shiftStartActionOrder( fsm->ctx->curActionOrd ); + + /* Turn on misfit accounting to possibly catch the old start state. */ + fsm->setMisfitAccounting( true ); + + /* Create the new new start state. It will be set final after the merging + * of the final states with the start state is complete. */ + StateAp *prevStartState = fsm->startState; + fsm->unsetStartState(); + fsm->setStartState( fsm->addState() ); + + /* Merge the new start state with the old one to isolate it. */ + fsm->mergeStates( fsm->startState, prevStartState ); + + if ( !fsm->startState->isFinState() ) { + /* Common case, safe to merge. */ + for ( StateSet::Iter st = fsm->finStateSet; st.lte(); st++ ) + fsm->mergeStatesLeaving( *st, fsm->startState ); + } + else { + /* Merge the start state into all final states. Except the start state on + * the first pass. If the start state is set final we will be doubling up + * its transitions, which will get transfered to any final states that + * follow it in the final state set. This will be determined by the order + * of items in the final state set. To prevent this we just merge with the + * start on a second pass. */ + StateSet origFin = fsm->finStateSet; + for ( StateSet::Iter st = origFin; st.lte(); st++ ) { + if ( *st != fsm->startState ) + fsm->mergeStatesLeaving( *st, fsm->startState ); + } + + /* Now it is safe to merge the start state with itself (provided it + * is set final). */ + if ( fsm->startState->isFinState() ) + fsm->mergeStatesLeaving( fsm->startState, fsm->startState ); + } + + /* Now ensure the new start state is a final state. */ + fsm->setFinState( fsm->startState ); + + /* Fill in any states that were newed up as combinations of others. */ + FsmRes res = FsmAp::fillInStates( fsm ); + if ( !res.success() ) + return res; + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + fsm->afterOpMinimize(); + + return res; +} + +FsmRes FsmAp::plusOp( FsmAp *fsm ) +{ + /* Need a duplicate for the star end. */ + FsmAp *factorDup = new FsmAp( *fsm ); + + /* Star the duplicate. */ + FsmRes res1 = FsmAp::starOp( factorDup ); + if ( !res1.success() ) + return res1; + + FsmRes res2 = FsmAp::concatOp( fsm, res1.fsm ); + if ( !res2.success() ) + return res2; + + return res2; +} + +FsmRes FsmAp::questionOp( FsmAp *fsm ) +{ + /* Make the null fsm. */ + FsmAp *nu = FsmAp::lambdaFsm( fsm->ctx ); + + /* Perform the question operator. */ + FsmRes res = FsmAp::unionOp( fsm, nu ); + if ( !res.success() ) + return res; + + return res; +} + +FsmRes FsmAp::exactRepeatOp( FsmAp *fsm, int times ) +{ + /* Zero repetitions produces lambda machine. */ + if ( times == 0 ) { + FsmCtx *fsmCtx = fsm->ctx; + delete fsm; + return FsmRes( FsmRes::Fsm(), FsmAp::lambdaFsm( fsmCtx ) ); + } + + /* The start func orders need to be shifted before doing the + * repetition. */ + fsm->ctx->curActionOrd += fsm->shiftStartActionOrder( fsm->ctx->curActionOrd ); + + /* A repeat of one does absolutely nothing. */ + if ( times == 1 ) + return FsmRes( FsmRes::Fsm(), fsm ); + + /* Make a machine to make copies from. */ + FsmAp *copyFrom = new FsmAp( *fsm ); + + /* Concatentate duplicates onto the end up until before the last. */ + for ( int i = 1; i < times-1; i++ ) { + FsmAp *dup = new FsmAp( *copyFrom ); + FsmRes res = concatOp( fsm, dup ); + if ( !res.success() ) { + delete copyFrom; + return res; + } + } + + /* Now use the copyFrom on the end. */ + FsmRes res = concatOp( fsm, copyFrom ); + if ( !res.success()) + return res; + + res.fsm->afterOpMinimize(); + + return res; +} + +FsmRes FsmAp::maxRepeatOp( FsmAp *fsm, int times ) +{ + /* Zero repetitions produces lambda machine. */ + if ( times == 0 ) { + FsmCtx *fsmCtx = fsm->ctx; + delete fsm; + return FsmRes( FsmRes::Fsm(), FsmAp::lambdaFsm( fsmCtx ) ); + } + + fsm->ctx->curActionOrd += fsm->shiftStartActionOrder( fsm->ctx->curActionOrd ); + + /* A repeat of one optional merely allows zero string. */ + if ( times == 1 ) { + isolateStartState( fsm ); + fsm->setFinState( fsm->startState ); + return FsmRes( FsmRes::Fsm(), fsm ); + } + + /* Make a machine to make copies from. */ + FsmAp *copyFrom = new FsmAp( *fsm ); + + /* The state set used in the from end of the concatentation. Starts with + * the initial final state set, then after each concatenation, gets set to + * the the final states that come from the the duplicate. */ + StateSet lastFinSet( fsm->finStateSet ); + + /* Set the initial state to zero to allow zero copies. */ + isolateStartState( fsm ); + fsm->setFinState( fsm->startState ); + + /* Concatentate duplicates onto the end up until before the last. */ + for ( int i = 1; i < times-1; i++ ) { + /* Make a duplicate for concating and set the fin bits to graph 2 so we + * can pick out it's final states after the optional style concat. */ + FsmAp *dup = new FsmAp( *copyFrom ); + dup->setFinBits( STB_GRAPH2 ); + FsmRes res = concatOp( fsm, dup, false, &lastFinSet, true ); + if ( !res.success() ) { + delete copyFrom; + return res; + } + + /* Clear the last final state set and make the new one by taking only + * the final states that come from graph 2.*/ + lastFinSet.empty(); + for ( int i = 0; i < fsm->finStateSet.length(); i++ ) { + /* If the state came from graph 2, add it to the last set and clear + * the bits. */ + StateAp *fs = fsm->finStateSet[i]; + if ( fs->stateBits & STB_GRAPH2 ) { + lastFinSet.insert( fs ); + fs->stateBits &= ~STB_GRAPH2; + } + } + } + + /* Now use the copyFrom on the end, no bits set, no bits to clear. */ + FsmRes res = concatOp( fsm, copyFrom, false, &lastFinSet, true ); + if ( !res.success() ) + return res; + + res.fsm->afterOpMinimize(); + + return res; +} + +FsmRes FsmAp::minRepeatOp( FsmAp *fsm, int times ) +{ + if ( times == 0 ) { + /* Acts just like a star op on the machine to return. */ + return FsmAp::starOp( fsm ); + } + else { + /* Take a duplicate for the star below. */ + FsmAp *dup = new FsmAp( *fsm ); + + /* Do repetition on the first half. */ + FsmRes exact = FsmAp::exactRepeatOp( fsm, times ); + if ( !exact.success() ) { + delete dup; + return exact; + } + + /* Star the duplicate. */ + FsmRes star = FsmAp::starOp( dup ); + if ( !star.success() ) { + delete exact.fsm; + return star; + } + + /* Tack on the kleene star. */ + return FsmAp::concatOp( exact.fsm, star.fsm ); + } +} + +FsmRes FsmAp::rangeRepeatOp( FsmAp *fsm, int lowerRep, int upperRep ) +{ + if ( lowerRep == 0 && upperRep == 0 ) { + FsmCtx *fsmCtx = fsm->ctx; + delete fsm; + return FsmRes( FsmRes::Fsm(), FsmAp::lambdaFsm( fsmCtx ) ); + } + else if ( lowerRep == 0 ) { + /* Just doing max repetition. Already guarded against n == 0. */ + return FsmAp::maxRepeatOp( fsm, upperRep ); + } + else if ( lowerRep == upperRep ) { + /* Just doing exact repetition. Already guarded against n == 0. */ + return FsmAp::exactRepeatOp( fsm, lowerRep ); + } + else { + /* This is the case that 0 < lowerRep < upperRep. Take a + * duplicate for the optional repeat. */ + FsmAp *dup = new FsmAp( *fsm ); + + /* Do repetition on the first half. */ + FsmRes exact = FsmAp::exactRepeatOp( fsm, lowerRep ); + if ( !exact.success() ) { + delete dup; + return exact; + } + + /* Do optional repetition on the second half. */ + FsmRes optional = FsmAp::maxRepeatOp( dup, upperRep - lowerRep ); + if ( !optional.success() ) { + delete exact.fsm; + return optional; + } + + /* Concat two halves. */ + return FsmAp::concatOp( exact.fsm, optional.fsm ); + } +} + +/* Concatenates other to the end of this machine. Other is deleted. Any + * transitions made leaving this machine and entering into other are notified + * that they are leaving transitions by having the leavingFromState callback + * invoked. Supports specifying the fromStates (istead of first final state + * set). This is useful for a max-repeat schenario, where from states are not + * all of first's final states. Also supports treating the concatentation as + * optional, which leaves the final states of the first machine as final. */ +FsmRes FsmAp::concatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq, StateSet *fromStates, bool optional ) +{ + for ( PriorTable::Iter g = other->startState->guardedInTable; g.lte(); g++ ) { + fsm->allTransPrior( 0, g->desc ); + other->allTransPrior( 0, g->desc->other ); + } + + /* Assert same signedness and return graph concatenation op. */ + assert( fsm->ctx == other->ctx ); + + /* For the merging process. */ + StateSet finStateSetCopy, startStateSet; + + /* Turn on misfit accounting for both graphs. */ + fsm->setMisfitAccounting( true ); + other->setMisfitAccounting( true ); + + /* Get the other's start state. */ + StateAp *otherStartState = other->startState; + + /* Unset other's start state before bringing in the entry points. */ + other->unsetStartState(); + + /* Bring in the rest of other's entry points. */ + fsm->copyInEntryPoints( other ); + other->entryPoints.empty(); + + /* Bring in other's states into our state lists. */ + fsm->stateList.append( other->stateList ); + fsm->misfitList.append( other->misfitList ); + + /* If from states is not set, then get a copy of our final state set before + * we clobber it and use it instead. */ + if ( fromStates == 0 ) { + finStateSetCopy = fsm->finStateSet; + fromStates = &finStateSetCopy; + } + + /* Unset all of our final states and get the final states from other. */ + if ( !optional ) + fsm->unsetAllFinStates(); + fsm->finStateSet.insert( other->finStateSet ); + + /* Since other's lists are empty, we can delete the fsm without + * affecting any states. */ + delete other; + + /* Merge our former final states with the start state of other. */ + for ( int i = 0; i < fromStates->length(); i++ ) { + StateAp *state = fromStates->data[i]; + + /* Merge the former final state with other's start state. */ + fsm->mergeStatesLeaving( state, otherStartState ); + + /* If the former final state was not reset final then we must clear + * the state's out trans data. If it got reset final then it gets to + * keep its out trans data. This must be done before fillInStates gets + * called to prevent the data from being sourced. */ + if ( ! state->isFinState() ) + fsm->clearOutData( state ); + } + + /* Fill in any new states made from merging. */ + FsmRes res = fillInStates( fsm ); + if ( !res.success() ) + return res; + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + res.fsm->afterOpMinimize( lastInSeq ); + + return res; +} + +FsmRes FsmAp::rightStartConcatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) +{ + PriorDesc *priorDesc0 = fsm->ctx->allocPriorDesc(); + PriorDesc *priorDesc1 = fsm->ctx->allocPriorDesc(); + + /* Set up the priority descriptors. The left machine gets the + * lower priority where as the right get the higher start priority. */ + priorDesc0->key = fsm->ctx->nextPriorKey++; + priorDesc0->priority = 0; + fsm->allTransPrior( fsm->ctx->curPriorOrd++, priorDesc0 ); + + /* The start transitions of the right machine gets the higher + * priority. Use the same unique key. */ + priorDesc1->key = priorDesc0->key; + priorDesc1->priority = 1; + other->startFsmPrior( fsm->ctx->curPriorOrd++, priorDesc1 ); + + return concatOp( fsm, other, lastInSeq ); +} + +/* Returns union of fsm and other. Other is deleted. */ +FsmRes FsmAp::unionOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) +{ + assert( fsm->ctx == other->ctx ); + + fsm->ctx->unionOp = true; + + fsm->setFinBits( STB_GRAPH1 ); + other->setFinBits( STB_GRAPH2 ); + + /* Turn on misfit accounting for both graphs. */ + fsm->setMisfitAccounting( true ); + other->setMisfitAccounting( true ); + + /* Call Worker routine. */ + FsmRes res = doUnion( fsm, other ); + if ( !res.success() ) + return res; + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + fsm->ctx->unionOp = false; + fsm->unsetFinBits( STB_BOTH ); + + fsm->afterOpMinimize( lastInSeq ); + + return res; +} + +/* Intersects other with this machine. Other is deleted. */ +FsmRes FsmAp::intersectOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) +{ + assert( fsm->ctx == other->ctx ); + + /* Turn on misfit accounting for both graphs. */ + fsm->setMisfitAccounting( true ); + other->setMisfitAccounting( true ); + + /* Set the fin bits on this and other to want each other. */ + fsm->setFinBits( STB_GRAPH1 ); + other->setFinBits( STB_GRAPH2 ); + + /* Call worker Or routine. */ + FsmRes res = doUnion( fsm, other ); + if ( !res.success() ) + return res; + + /* Unset any final states that are no longer to + * be final due to final bits. */ + fsm->unsetIncompleteFinals(); + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + /* Remove states that have no path to a final state. */ + fsm->removeDeadEndStates(); + + fsm->afterOpMinimize( lastInSeq ); + + return res; +} + +/* Set subtracts other machine from this machine. Other is deleted. */ +FsmRes FsmAp::subtractOp( FsmAp *fsm, FsmAp *other, bool lastInSeq ) +{ + assert( fsm->ctx == other->ctx ); + + /* Turn on misfit accounting for both graphs. */ + fsm->setMisfitAccounting( true ); + other->setMisfitAccounting( true ); + + /* Set the fin bits of other to be killers. */ + other->setFinBits( STB_GRAPH1 ); + + /* Call worker Or routine. */ + FsmRes res = doUnion( fsm, other ); + if ( !res.success() ) + return res; + + /* Unset any final states that are no longer to + * be final due to final bits. */ + fsm->unsetKilledFinals(); + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + /* Remove states that have no path to a final state. */ + fsm->removeDeadEndStates(); + + fsm->afterOpMinimize( lastInSeq ); + + return res; +} + +FsmRes FsmAp::epsilonOp( FsmAp *fsm ) +{ + fsm->setMisfitAccounting( true ); + + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) + st->owningGraph = 0; + + /* Perform merges. */ + fsm->resolveEpsilonTrans(); + + /* Epsilons can caused merges which leave behind unreachable states. */ + FsmRes res = FsmAp::fillInStates( fsm ); + if ( !res.success() ) + return res; + + /* Remove the misfits and turn off misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + return res; +} + +/* Make a new maching by joining together a bunch of machines without making + * any transitions between them. A negative finalId results in there being no + * final id. */ +FsmRes FsmAp::joinOp( FsmAp *fsm, int startId, int finalId, FsmAp **others, int numOthers ) +{ + for ( int m = 0; m < numOthers; m++ ) { + assert( fsm->ctx == others[m]->ctx ); + } + + /* Set the owning machines. Start at one. Zero is reserved for the start + * and final states. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) + st->owningGraph = 1; + for ( int m = 0; m < numOthers; m++ ) { + for ( StateList::Iter st = others[m]->stateList; st.lte(); st++ ) + st->owningGraph = 2+m; + } + + /* All machines loose start state status. */ + fsm->unsetStartState(); + for ( int m = 0; m < numOthers; m++ ) + others[m]->unsetStartState(); + + /* Bring the other machines into this. */ + for ( int m = 0; m < numOthers; m++ ) { + /* Bring in the rest of other's entry points. */ + fsm->copyInEntryPoints( others[m] ); + others[m]->entryPoints.empty(); + + /* Merge the lists. This will move all the states from other into + * this. No states will be deleted. */ + fsm->stateList.append( others[m]->stateList ); + assert( others[m]->misfitList.length() == 0 ); + + /* Move the final set data from other into this. */ + fsm->finStateSet.insert( others[m]->finStateSet ); + others[m]->finStateSet.empty(); + + /* Since other's list is empty, we can delete the fsm without + * affecting any states. */ + delete others[m]; + } + + /* Look up the start entry point. */ + EntryMapEl *enLow = 0, *enHigh = 0; + bool findRes = fsm->entryPoints.findMulti( startId, enLow, enHigh ); + if ( ! findRes ) { + /* No start state. Set a default one and proceed with the join. Note + * that the result of the join will be a very uninteresting machine. */ + fsm->setStartState( fsm->addState() ); + } + else { + /* There is at least one start state, create a state that will become + * the new start state. */ + StateAp *newStart = fsm->addState(); + fsm->setStartState( newStart ); + + /* The start state is in an owning machine class all it's own. */ + newStart->owningGraph = 0; + + /* Create the set of states to merge from. */ + StateSet stateSet; + for ( EntryMapEl *en = enLow; en <= enHigh; en++ ) + stateSet.insert( en->value ); + + /* Merge in the set of start states into the new start state. */ + fsm->mergeStateList( newStart, stateSet.data, stateSet.length() ); + } + + /* Take a copy of the final state set, before unsetting them all. This + * will allow us to call clearOutData on the states that don't get + * final state status back back. */ + StateSet finStateSetCopy = fsm->finStateSet; + + /* Now all final states are unset. */ + fsm->unsetAllFinStates(); + + if ( finalId >= 0 ) { + /* Create the implicit final state. */ + StateAp *finState = fsm->addState(); + fsm->setFinState( finState ); + + /* Assign an entry into the final state on the final state entry id. Note + * that there may already be an entry on this id. That's ok. Also set the + * final state owning machine id. It's in a class all it's own. */ + fsm->setEntry( finalId, finState ); + finState->owningGraph = 0; + } + + /* Hand over to workers for resolving epsilon trans. This will merge states + * with the targets of their epsilon transitions. */ + fsm->resolveEpsilonTrans(); + + /* Invoke the relinquish final callback on any states that did not get + * final state status back. */ + for ( StateSet::Iter st = finStateSetCopy; st.lte(); st++ ) { + if ( !((*st)->stateBits & STB_ISFINAL) ) + fsm->clearOutData( *st ); + } + + /* Fill in any new states made from merging. */ + FsmRes res = FsmAp::fillInStates( fsm ); + if ( !res.success() ) + return res; + + /* Joining can be messy. Instead of having misfit accounting on (which is + * tricky here) do a full cleaning. */ + fsm->removeUnreachableStates(); + + return res; +} + +/* Ensure that the start state is free of entry points (aside from the fact + * that it is the start state). If the start state has entry points then Make a + * new start state by merging with the old one. Useful before modifying start + * transitions. If the existing start state has any entry points other than the + * start state entry then modifying its transitions changes more than the start + * transitions. So isolate the start state by separating it out such that it + * only has start stateness as it's entry point. */ +FsmRes FsmAp::isolateStartState( FsmAp *fsm ) +{ + /* Do nothing if the start state is already isolated. */ + if ( fsm->isStartStateIsolated() ) + return FsmRes( FsmRes::Fsm(), fsm ); + + /* Turn on misfit accounting to possibly catch the old start state. */ + fsm->setMisfitAccounting( true ); + + /* This will be the new start state. The existing start + * state is merged with it. */ + StateAp *prevStartState = fsm->startState; + fsm->unsetStartState(); + fsm->setStartState( fsm->addState() ); + + /* Merge the new start state with the old one to isolate it. */ + fsm->mergeStates( fsm->startState, prevStartState ); + + /* Stfil and stateDict will be empty because the merging of the old start + * state into the new one will not have any conflicting transitions. */ + assert( fsm->stateDict.treeSize == 0 ); + assert( fsm->nfaList.length() == 0 ); + + /* The old start state may be unreachable. Remove the misfits and turn off + * misfit accounting. */ + fsm->removeMisfits(); + fsm->setMisfitAccounting( false ); + + return FsmRes( FsmRes::Fsm(), fsm ); +} + +StateAp *FsmAp::dupStartState() +{ + StateAp *dup = addState(); + mergeStates( dup, startState ); + return dup; +} + +/* A state merge which represents the drawing in of leaving transitions. If + * there is any out data then we duplicate the source state, transfer the out + * data, then merge in the state. The new state will be reaped because it will + * not be given any in transitions. */ +void FsmAp::mergeStatesLeaving( StateAp *destState, StateAp *srcState ) +{ + if ( !hasOutData( destState ) ) { + /* Perform the merge, indicating we are leaving, which will affect how + * out conds are merged. */ + mergeStates( destState, srcState, true ); + } + else { + /* Dup the source state. */ + StateAp *ssMutable = addState(); + mergeStates( ssMutable, srcState ); + + /* Do out data transfer (and out condition embedding). */ + transferOutData( ssMutable, destState ); + + if ( destState->outCondSpace != 0 ) { + + doEmbedCondition( ssMutable, destState->outCondSpace->condSet, + destState->outCondKeys ); + } + + /* Now we merge with dest, setting leaving = true. This dictates how + * out conditions should be merged. */ + mergeStates( destState, ssMutable, true ); + } +} + +void FsmAp::checkEpsilonRegularInteraction( const PriorTable &t1, const PriorTable &t2 ) +{ + for ( PriorTable::Iter pd1 = t1; pd1.lte(); pd1++ ) { + for ( PriorTable::Iter pd2 = t2; pd2.lte(); pd2++ ) { + /* Looking for unequal guarded priorities with the same key. */ + if ( pd1->desc->key == pd2->desc->key ) { + if ( pd1->desc->priority < pd2->desc->priority || + pd1->desc->priority > pd2->desc->priority ) + { + if ( ctx->checkPriorInteraction && pd1->desc->guarded ) { + if ( ! priorInteraction ) { + priorInteraction = true; + guardId = pd1->desc->guardId; + } + } + } + } + } + } +} + +void FsmAp::mergeStateProperties( StateAp *destState, StateAp *srcState ) +{ + /* Draw in any properties of srcState into destState. */ + if ( srcState == destState ) { + /* Duplicate the list to protect against write to source. The + * priorities sets are not copied in because that would have no + * effect. */ + destState->epsilonTrans.append( EpsilonTrans( srcState->epsilonTrans ) ); + + /* Get all actions, duplicating to protect against write to source. */ + destState->toStateActionTable.setActions( + ActionTable( srcState->toStateActionTable ) ); + destState->fromStateActionTable.setActions( + ActionTable( srcState->fromStateActionTable ) ); + destState->outActionTable.setActions( ActionTable( srcState->outActionTable ) ); + destState->errActionTable.setActions( ErrActionTable( srcState->errActionTable ) ); + destState->eofActionTable.setActions( ActionTable( srcState->eofActionTable ) ); + + /* Not touching guarded-in table or out conditions. Probably should + * leave some of the above alone as well. */ + } + else { + /* Get the epsilons, out priorities. */ + destState->epsilonTrans.append( srcState->epsilonTrans ); + destState->outPriorTable.setPriors( srcState->outPriorTable ); + + /* Get all actions. */ + destState->toStateActionTable.setActions( srcState->toStateActionTable ); + destState->fromStateActionTable.setActions( srcState->fromStateActionTable ); + destState->outActionTable.setActions( srcState->outActionTable ); + destState->errActionTable.setActions( srcState->errActionTable ); + destState->eofActionTable.setActions( srcState->eofActionTable ); + destState->lmNfaParts.insert( srcState->lmNfaParts ); + destState->guardedInTable.setPriors( srcState->guardedInTable ); + } +} + +void FsmAp::mergeStateBits( StateAp *destState, StateAp *srcState ) +{ + /* Get bits and final state status. Note in the above code we depend on the + * original final state status being present. */ + destState->stateBits |= ( srcState->stateBits & ~STB_ISFINAL ); + if ( srcState->isFinState() ) + setFinState( destState ); +} + +void FsmAp::mergeNfaTransitions( StateAp *destState, StateAp *srcState ) +{ + /* Copy in any NFA transitions. */ + if ( srcState->nfaOut != 0 ) { + if ( destState->nfaOut == 0 ) + destState->nfaOut = new NfaTransList; + + for ( NfaTransList::Iter nt = *srcState->nfaOut; nt.lte(); nt++ ) { + NfaTrans *trans = new NfaTrans( + nt->pushTable, nt->restoreTable, + nt->popFrom, nt->popCondSpace, nt->popCondKeys, + nt->popAction, nt->popTest, nt->order ); + + destState->nfaOut->append( trans ); + attachToNfa( destState, nt->toState, trans ); + } + } +} + +void FsmAp::checkPriorInteractions( StateAp *destState, StateAp *srcState ) +{ + /* Run a check on priority interactions between epsilon transitions and + * regular transitions. This can't be used to affect machine construction, + * only to check for priority guards. */ + if ( destState->nfaOut != 0 ) { + for ( NfaTransList::Iter nt = *destState->nfaOut; nt.lte(); nt++ ) { + for ( TransList::Iter trans = destState->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + checkEpsilonRegularInteraction( + trans->tdap()->priorTable, nt->priorTable ); + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; + cond.lte(); cond++ ) + { + checkEpsilonRegularInteraction( + cond->priorTable, nt->priorTable ); + + } + } + } + } + } +} + +void FsmAp::mergeStates( StateAp *destState, StateAp *srcState, bool leaving ) +{ + /* Transitions. */ + outTransCopy( destState, srcState->outList.head ); + + /* Properties such as out data, to/from actions. */ + mergeStateProperties( destState, srcState ); + + /* Merge out conditions, depends on the operation (leaving or not). */ + mergeOutConds( destState, srcState, leaving ); + + /* State bits, including final state stats. Out conds depnds on this + * happening after. */ + mergeStateBits( destState, srcState ); + + /* Draw in the NFA transitions. */ + mergeNfaTransitions( destState, srcState ); + + /* Hacked in check for priority interactions, allowing detection of some + * bad situations. */ + checkPriorInteractions( destState, srcState ); +} + +void FsmAp::mergeStateList( StateAp *destState, + StateAp **srcStates, int numSrc ) +{ + for ( int s = 0; s < numSrc; s++ ) + mergeStates( destState, srcStates[s] ); +} + +void FsmAp::cleanAbortedFill( StateAp *state ) +{ + /* Iterate the out transitions, deleting them. */ + for ( TransList::Iter n, t = state->outList; t.lte(); ) { + n = t.next(); + if ( t->plain() ) + delete t->tdap(); + else + delete t->tcap(); + t = n; + } + + state->outList.abandon(); + + if ( state->nfaIn != 0 ) { + delete state->nfaIn; + state->nfaIn = 0; + } + + if ( state->nfaOut != 0 ) { + state->nfaOut->empty(); + delete state->nfaOut; + state->nfaOut = 0; + } +} + +void FsmAp::cleanAbortedFill() +{ + while ( nfaList.length() > 0 ) { + StateAp *state = nfaList.head; + + StateSet *stateSet = &state->stateDictEl->stateSet; + //mergeStateList( state, stateSet->data, stateSet->length() ); + + for ( StateSet::Iter s = *stateSet; s.lte(); s++ ) + detachStateDict( state, *s ); + + nfaList.detach( state ); + } + + /* Disassociated state dict elements from states. */ + for ( StateDict::Iter sdi = stateDict; sdi.lte(); sdi++ ) + sdi->targState->stateDictEl = 0; + + /* Delete all the state dict elements. */ + stateDict.empty(); + + /* Delete all the transitions. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) + cleanAbortedFill( state ); + + /* Delete all the states. */ + stateList.empty(); + + /* Delete all the transitions. */ + for ( StateList::Iter state = misfitList; state.lte(); state++ ) + cleanAbortedFill( state ); + + /* Delete all the states. */ + misfitList.empty(); +} + +bool FsmAp::overStateLimit() +{ + if ( ctx->stateLimit > FsmCtx::STATE_UNLIMITED ) { + long states = misfitList.length() + stateList.length(); + if ( states > ctx->stateLimit ) + return true; + } + return false; +} + +bool FsmAp::fillAbort( FsmRes &res, FsmAp *fsm ) +{ + if ( fsm->priorInteraction ) { + fsm->cleanAbortedFill(); + int guardId = fsm->guardId; + delete fsm; + res = FsmRes( FsmRes::PriorInteraction(), guardId ); + return true; + } + + if ( fsm->overStateLimit() ) { + fsm->cleanAbortedFill(); + delete fsm; + res = FsmRes( FsmRes::TooManyStates() ); + return true; + } + + return false; +} + +FsmRes FsmAp::fillInStates( FsmAp *fsm ) +{ + /* Used as return value on success. Filled in with error on abort. */ + FsmRes res( FsmRes::Fsm(), fsm ); + + /* Merge any states that are awaiting merging. This will likey cause other + * states to be added to the NFA list. */ + while ( true ) { + if ( fillAbort( res, fsm ) ) + return res; + + if ( fsm->nfaList.length() == 0 ) + break; + + StateAp *state = fsm->nfaList.head; + + StateSet *stateSet = &state->stateDictEl->stateSet; + fsm->mergeStateList( state, stateSet->data, stateSet->length() ); + + for ( StateSet::Iter s = *stateSet; s.lte(); s++ ) + fsm->detachStateDict( state, *s ); + + fsm->nfaList.detach( state ); + } + + /* The NFA list is empty at this point. There are no state sets we need to + * preserve. */ + + /* Disassociated state dict elements from states. */ + for ( StateDict::Iter sdi = fsm->stateDict; sdi.lte(); sdi++ ) + sdi->targState->stateDictEl = 0; + + /* Delete all the state dict elements. */ + fsm->stateDict.empty(); + + return res; +} + +/* Check if a machine defines a single character. This is useful in validating + * ranges and machines to export. */ +bool FsmAp::checkSingleCharMachine() +{ + /* Must have two states. */ + if ( stateList.length() != 2 ) + return false; + /* The start state cannot be final. */ + if ( startState->isFinState() ) + return false; + /* There should be only one final state. */ + if ( finStateSet.length() != 1 ) + return false; + /* The final state cannot have any transitions out. */ + if ( finStateSet[0]->outList.length() != 0 ) + return false; + /* The start state should have only one transition out. */ + if ( startState->outList.length() != 1 ) + return false; + /* The singe transition out of the start state should not be a range. */ + TransAp *startTrans = startState->outList.head; + if ( ctx->keyOps->ne( startTrans->lowKey, startTrans->highKey ) ) + return false; + return true; +} + +FsmRes FsmAp::condCostFromState( FsmAp *fsm, StateAp *state, long depth ) +{ + /* Nothing to do if the state is already on the list. */ + if ( state->stateBits & STB_ONLIST ) + return FsmRes( FsmRes::Fsm(), fsm ); + + if ( depth > fsm->ctx->condsCheckDepth ) + return FsmRes( FsmRes::Fsm(), fsm ); + + /* Doing depth first, put state on the list. */ + state->stateBits |= STB_ONLIST; + + /* Recurse on everything ranges. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) { + FsmRes res = condCostFromState( fsm, trans->tdap()->toState, depth + 1 ); + if ( !res.success() ) + return res; + } + } + else { + for ( CondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { + if ( (*csi)->costMark ) + return FsmRes( FsmRes::CondCostTooHigh(), (*csi)->costId ); + } + + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) { + FsmRes res = condCostFromState( fsm, cond->toState, depth + 1 ); + if ( !res.success() ) + return res; + } + } + } + } + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter n = *state->nfaOut; n.lte(); n++ ) { + /* We do not increment depth here since this is an epsilon transition. */ + FsmRes res = condCostFromState( fsm, n->toState, depth ); + if ( !res.success() ) + return res; + } + } + + for ( ActionTable::Iter a = state->fromStateActionTable; a.lte(); a++ ) { + if ( a->value->costMark ) + return FsmRes( FsmRes::CondCostTooHigh(), a->value->costId ); + } + + return FsmRes( FsmRes::Fsm(), fsm ); +} + + +/* Returns either success (using supplied fsm), or some error condition. */ +FsmRes FsmAp::condCostSearch( FsmAp *fsm ) +{ + /* Init on state list flags. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) + st->stateBits &= ~STB_ONLIST; + + FsmRes res = condCostFromState( fsm, fsm->startState, 1 ); + if ( !res.success() ) + delete fsm; + return res; +} + +void FsmAp::condCost( Action *action, long repId ) +{ + action->costMark = true; + action->costId = repId; +} + +/* + * This algorithm assigns a price to each state visit, then adds that to a + * running total. Note that we do not guard against multiple visits to a state, + * since we are estimating runtime cost. + * + * We rely on a character histogram and are looking for a probability of being + * in any given state, given that histogram, simple and very effective. + */ +void FsmAp::breadthFromState( double &total, int &minDepth, double *histogram, + FsmAp *fsm, StateAp *state, long depth, int maxDepth, double stateScore ) +{ + if ( depth > maxDepth ) + return; + + /* Recurse on everything ranges. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + + /* Compute target state score. */ + double span = 0; + for ( int i = trans->lowKey.getVal(); i <= trans->highKey.getVal(); i++ ) + span += histogram[i]; + + double targetStateScore = stateScore * ( span ); + + /* Add to the level. */ + total += targetStateScore; + + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 ) { + if ( trans->tdap()->toState->isFinState() && ( minDepth < 0 || depth < minDepth ) ) + minDepth = depth; + + breadthFromState( total, minDepth, histogram, fsm, trans->tdap()->toState, + depth + 1, maxDepth, targetStateScore ); + } + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 ) { + if ( cond->toState->isFinState() && ( minDepth < 0 || depth < minDepth ) ) + minDepth = depth; + + breadthFromState( total, minDepth, histogram, fsm, cond->toState, + depth + 1, maxDepth, targetStateScore ); + } + } + } + } + + if ( state->nfaOut != 0 ) { + for ( NfaTransList::Iter n = *state->nfaOut; n.lte(); n++ ) { + if ( n->toState->isFinState() && ( minDepth < 0 || depth < minDepth ) ) + minDepth = depth; + + /* We do not increment depth here since this is an epsilon transition. */ + breadthFromState( total, minDepth, histogram, fsm, n->toState, depth, maxDepth, stateScore ); + } + } +} + +void FsmAp::breadthFromEntry( double &total, int &minDepth, double *histogram, FsmAp *fsm, StateAp *state ) +{ + long depth = 1; + int maxDepth = 5; + double stateScore = 1.0; + + FsmAp::breadthFromState( total, minDepth, histogram, fsm, state, depth, maxDepth, stateScore ); +} + + +void FsmAp::applyEntryPriorGuard( FsmAp *fsm, long repId ) +{ + PriorDesc *priorDesc0 = fsm->ctx->allocPriorDesc(); + PriorDesc *priorDesc1 = fsm->ctx->allocPriorDesc(); + + priorDesc0->key = fsm->ctx->nextPriorKey; + priorDesc0->priority = 0; + priorDesc0->guarded = true; + priorDesc0->guardId = repId; + priorDesc0->other = priorDesc1; + + priorDesc1->key = fsm->ctx->nextPriorKey; + priorDesc1->priority = 1; + priorDesc1->guarded = true; + priorDesc1->guardId = repId; + priorDesc1->other = priorDesc0; + + /* Roll over for next allocation. */ + fsm->ctx->nextPriorKey += 1; + + /* Only need to set the first. Second is referenced using 'other' field. */ + fsm->startState->guardedInTable.setPrior( 0, priorDesc0 ); +} + +void FsmAp::applyRepeatPriorGuard( FsmAp *fsm, long repId ) +{ + PriorDesc *priorDesc2 = fsm->ctx->allocPriorDesc(); + PriorDesc *priorDesc3 = fsm->ctx->allocPriorDesc(); + + priorDesc2->key = fsm->ctx->nextPriorKey; + priorDesc2->priority = 0; + priorDesc2->guarded = true; + priorDesc2->guardId = repId; + priorDesc2->other = priorDesc3; + + priorDesc3->key = fsm->ctx->nextPriorKey; + priorDesc3->guarded = true; + priorDesc3->priority = 1; + priorDesc3->guardId = repId; + priorDesc3->other = priorDesc2; + + /* Roll over for next allocation. */ + fsm->ctx->nextPriorKey += 1; + + /* Only need to set the first. Second is referenced using 'other' field. */ + fsm->startState->guardedInTable.setPrior( 0, priorDesc2 ); + + fsm->allTransPrior( fsm->ctx->curPriorOrd++, priorDesc3 ); + fsm->leaveFsmPrior( fsm->ctx->curPriorOrd++, priorDesc2 ); +} + +FsmRes FsmAp::condPlus( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ) +{ + condCost( ini, repId ); + condCost( inc, repId ); + condCost( min, repId ); + if ( max != 0 ) + condCost( max, repId ); + + fsm->startFsmAction( 0, inc ); + + if ( max != 0 ) { + FsmRes res = fsm->startFsmCondition( max, true ); + if ( !res.success() ) + return res; + } + + /* Need a duplicated for the star end. */ + FsmAp *dup = new FsmAp( *fsm ); + + applyRepeatPriorGuard( dup, repId ); + + /* Star the duplicate. */ + FsmRes dupStar = FsmAp::starOp( dup ); + if ( !dupStar.success() ) { + delete fsm; + return dupStar; + } + + FsmRes res = FsmAp::concatOp( fsm, dupStar.fsm ); + if ( !res.success() ) + return res; + + /* End plus operation. */ + + res.fsm->leaveFsmCondition( min, true ); + + /* Init action. */ + res.fsm->startFromStateAction( 0, ini ); + + /* Leading priority guard. */ + applyEntryPriorGuard( res.fsm, repId ); + + return res; +} + +FsmRes FsmAp::condStar( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ) +{ + condCost( ini, repId ); + condCost( inc, repId ); + condCost( min, repId ); + if ( max != 0 ) + condCost( max, repId ); + + /* Increment. */ + fsm->startFsmAction( 0, inc ); + + /* Max (optional). */ + if ( max != 0 ) { + FsmRes res = fsm->startFsmCondition( max, true ); + if ( !res.success() ) + return res; + } + + applyRepeatPriorGuard( fsm, repId ); + + /* Star. */ + FsmRes res = FsmAp::starOp( fsm ); + if ( !res.success() ) + return res; + + /* Restrict leaving. */ + res.fsm->leaveFsmCondition( min, true ); + + /* Init action. */ + res.fsm->startFromStateAction( 0, ini ); + + /* Leading priority guard. */ + applyEntryPriorGuard( res.fsm, repId ); + + return res; +} + +/* Remove duplicates of unique actions from an action table. */ +void FsmAp::removeDups( ActionTable &table ) +{ + /* Scan through the table looking for unique actions to + * remove duplicates of. */ + for ( int i = 0; i < table.length(); i++ ) { + /* Remove any duplicates ahead of i. */ + for ( int r = i+1; r < table.length(); ) { + if ( table[r].value == table[i].value ) + table.vremove(r); + else + r += 1; + } + } +} + +/* Remove duplicates from action lists. This operates only on transition and + * eof action lists and so should be called once all actions have been + * transfered to their final resting place. */ +void FsmAp::removeActionDups() +{ + /* Loop all states. */ + for ( StateList::Iter state = stateList; state.lte(); state++ ) { + /* Loop all transitions. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) + removeDups( trans->tdap()->actionTable ); + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) + removeDups( cond->actionTable ); + } + } + removeDups( state->toStateActionTable ); + removeDups( state->fromStateActionTable ); + removeDups( state->eofActionTable ); + } +} + diff --git a/src/libfsm/fsmgraph.h b/src/libfsm/fsmgraph.h new file mode 100644 index 00000000..aee6f718 --- /dev/null +++ b/src/libfsm/fsmgraph.h @@ -0,0 +1,2694 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _FSMGRAPH_H +#define _FSMGRAPH_H + +#include "config.h" +#include "ragel.h" +#include "common.h" +#include "vector.h" +#include "bstset.h" +#include "compare.h" +#include "avltree.h" +#include "dlist.h" +#include "dlistmel.h" +#include "bstmap.h" +#include "sbstmap.h" +#include "sbstset.h" +#include "sbsttable.h" +#include "avlset.h" +#include "avlmap.h" + +#include +#include +#include +#include + + +/* Flags that control merging. */ +#define STB_GRAPH1 0x01 +#define STB_GRAPH2 0x02 +#define STB_BOTH 0x03 +#define STB_ISFINAL 0x04 +#define STB_ISMARKED 0x08 +#define STB_ONLIST 0x10 +#define STB_NFA_REP 0x20 + +using std::ostream; + +struct TransAp; +struct StateAp; +struct FsmAp; +struct Action; +struct FsmLongestMatchPart; +struct CondSpace; +struct FsmCtx; +struct InlineBlock; +struct InlineList; + +struct TooManyStates {}; + +struct PriorInteraction +{ + PriorInteraction( long long id ) : id(id) {} + long long id; +}; + +struct NfaRound +{ + NfaRound( long depth, long groups ) + : depth(depth), groups(groups) {} + + long depth; + long groups; +}; + +typedef Vector NfaRoundVect; + +struct CondCostTooHigh +{ + CondCostTooHigh( long long costId ) + : costId(costId) {} + + long long costId; +}; + + +/* State list element for unambiguous access to list element. */ +struct FsmListEl +{ + StateAp *prev, *next; +}; + +/* This is the marked index for a state pair. Used in minimization. It keeps + * track of whether or not the state pair is marked. */ +struct MarkIndex +{ + MarkIndex(int states); + ~MarkIndex(); + + void markPair(int state1, int state2); + bool isPairMarked(int state1, int state2); + +private: + int numStates; + bool *array; +}; + +/* Transistion Action Element. */ +typedef SBstMapEl< int, Action* > ActionTableEl; + +/* Nodes in the tree that use this action. */ +struct NameInst; +struct InlineList; +typedef Vector NameInstVect; + +struct ActionParam +{ + ActionParam( std::string name ) + : name(name) {} + + std::string name; +}; + +typedef Vector ActionParamList; + +typedef Vector ActionArgList; + +struct CmpActionArgList +{ + static inline int compare( const ActionArgList *list1, const ActionArgList *list2 ) + { + return CmpTable::compare( *list1, *list2 ); + } +}; + +typedef BstMap ActionArgListMap; +typedef BstMapEl ActionArgListMapEl; + +/* Element in list of actions. Contains the string for the code to exectute. */ +struct Action +: + public DListEl, + public AvlTreeEl +{ +public: + + Action( const InputLoc &loc, std::string name, InlineList *inlineList, int condId ) + : + loc(loc), + name(name), + inlineList(inlineList), + actionId(-1), + numTransRefs(0), + numToStateRefs(0), + numFromStateRefs(0), + numEofRefs(0), + numCondRefs(0), + numNfaRefs(0), + anyCall(false), + isLmAction(false), + condId(condId), + costMark(false), + costId(0), + paramList(0), + argListMap(0), + substOf(0), + argList(0) + { + } + + ~Action(); + + static Action *cons( const InputLoc &loc, Action *substOf, + ActionArgList *argList, int condId ) + { + Action *action = new Action( loc, std::string(), 0, condId ); + action->substOf = substOf; + action->argList = argList; + action->inlineList = substOf->inlineList; + return action; + } + + /* Key for action dictionary. */ + std::string getKey() const { return name; } + + /* Data collected during parse. */ + InputLoc loc; + std::string name; + InlineList *inlineList; + int actionId; + + void actionName( ostream &out ) + { + if ( name.empty() ) + out << loc.line << ":" << loc.col; + else + out << name; + } + + /* Nodes in the name tree where the action is embedded. This serves as the + * root for name searches. Since actions can be used multiple times we use + * a vector. Name resolver deals with contracts. */ + NameInstVect embedRoots; + + /* Number of references in the final machine. */ + int numRefs() + { + return numTransRefs + numToStateRefs + + numFromStateRefs + numEofRefs + + numNfaRefs; + } + + int numTransRefs; + int numToStateRefs; + int numFromStateRefs; + int numEofRefs; + int numCondRefs; + int numNfaRefs; + bool anyCall; + + bool isLmAction; + int condId; + + bool costMark; + long long costId; + + ActionParamList *paramList; + ActionArgListMap *argListMap; + Action *substOf; + ActionArgList *argList; +}; + +struct CmpCondId +{ + static inline int compare( const Action *cond1, const Action *cond2 ) + { + if ( cond1->condId < cond2->condId ) + return -1; + else if ( cond1->condId > cond2->condId ) + return 1; + return 0; + } +}; + +/* A list of actions. */ +typedef DList ActionList; +typedef AvlTree ActionDict; + +/* Structure for reverse action mapping. */ +struct RevActionMapEl +{ + char *name; + InputLoc location; +}; + + +/* Transition Action Table. */ +struct ActionTable + : public SBstMap< int, Action*, CmpOrd > +{ + void setAction( int ordering, Action *action ); + void setActions( int *orderings, Action **actions, int nActs ); + void setActions( const ActionTable &other ); + + bool hasAction( Action *action ); +}; + +typedef SBstSet< Action*, CmpOrd > ActionSet; +typedef CmpSTable< Action*, CmpOrd > CmpActionSet; + +/* Transistion Action Element. */ +typedef SBstMapEl< int, FsmLongestMatchPart* > LmActionTableEl; + +/* Transition Action Table. */ +struct LmActionTable + : public SBstMap< int, FsmLongestMatchPart*, CmpOrd > +{ + void setAction( int ordering, FsmLongestMatchPart *action ); + void setActions( const LmActionTable &other ); +}; + +/* Compare of a whole action table element (key & value). */ +struct CmpActionTableEl +{ + static int compare( const ActionTableEl &action1, + const ActionTableEl &action2 ) + { + if ( action1.key < action2.key ) + return -1; + else if ( action1.key > action2.key ) + return 1; + else if ( action1.value < action2.value ) + return -1; + else if ( action1.value > action2.value ) + return 1; + return 0; + } +}; + +/* Compare for ActionTable. */ +typedef CmpSTable< ActionTableEl, CmpActionTableEl > CmpActionTable; + +/* Compare of a whole lm action table element (key & value). */ +struct CmpLmActionTableEl +{ + static int compare( const LmActionTableEl &lmAction1, + const LmActionTableEl &lmAction2 ) + { + if ( lmAction1.key < lmAction2.key ) + return -1; + else if ( lmAction1.key > lmAction2.key ) + return 1; + else if ( lmAction1.value < lmAction2.value ) + return -1; + else if ( lmAction1.value > lmAction2.value ) + return 1; + return 0; + } +}; + +/* Compare for ActionTable. */ +typedef CmpSTable< LmActionTableEl, CmpLmActionTableEl > CmpLmActionTable; + +/* Action table element for error action tables. Adds the encoding of transfer + * point. */ +struct ErrActionTableEl +{ + ErrActionTableEl( Action *action, int ordering, int transferPoint ) + : ordering(ordering), action(action), transferPoint(transferPoint) { } + + /* Ordering and id of the action embedding. */ + int ordering; + Action *action; + + /* Id of point of transfere from Error action table to transtions and + * eofActionTable. */ + int transferPoint; + + int getKey() const { return ordering; } +}; + +struct ErrActionTable + : public SBstTable< ErrActionTableEl, int, CmpOrd > +{ + void setAction( int ordering, Action *action, int transferPoint ); + void setActions( const ErrActionTable &other ); +}; + +/* Compare of an error action table element (key & value). */ +struct CmpErrActionTableEl +{ + static int compare( const ErrActionTableEl &action1, + const ErrActionTableEl &action2 ) + { + if ( action1.ordering < action2.ordering ) + return -1; + else if ( action1.ordering > action2.ordering ) + return 1; + else if ( action1.action < action2.action ) + return -1; + else if ( action1.action > action2.action ) + return 1; + else if ( action1.transferPoint < action2.transferPoint ) + return -1; + else if ( action1.transferPoint > action2.transferPoint ) + return 1; + return 0; + } +}; + +/* Compare for ErrActionTable. */ +typedef CmpSTable< ErrActionTableEl, CmpErrActionTableEl > CmpErrActionTable; + + +/* Descibe a priority, shared among PriorEls. + * Has key and whether or not used. */ +struct PriorDesc +{ + PriorDesc() + : + key(0), + priority(0), + guarded(false), + guardId(0), + other(0) + {} + + int key; + int priority; + bool guarded; + long long guardId; + PriorDesc *other; + + PriorDesc *prev, *next; +}; + +typedef DList PriorDescList; + +/* Element in the arrays of priorities for transitions and arrays. Ordering is + * unique among instantiations of machines, desc is shared. */ +struct PriorEl +{ + PriorEl( int ordering, PriorDesc *desc ) + : ordering(ordering), desc(desc) { } + + int ordering; + PriorDesc *desc; +}; + +/* Compare priority elements, which are ordered by the priority descriptor + * key. */ +struct PriorElCmp +{ + static inline int compare( const PriorEl &pel1, const PriorEl &pel2 ) + { + if ( pel1.desc->key < pel2.desc->key ) + return -1; + else if ( pel1.desc->key > pel2.desc->key ) + return 1; + else + return 0; + } +}; + + +/* Priority Table. */ +struct PriorTable + : public SBstSet< PriorEl, PriorElCmp > +{ + void setPrior( int ordering, PriorDesc *desc ); + void setPriors( const PriorTable &other ); +}; + +/* Compare of prior table elements for distinguising state data. */ +struct CmpPriorEl +{ + static inline int compare( const PriorEl &pel1, const PriorEl &pel2 ) + { + if ( pel1.desc < pel2.desc ) + return -1; + else if ( pel1.desc > pel2.desc ) + return 1; + else if ( pel1.ordering < pel2.ordering ) + return -1; + else if ( pel1.ordering > pel2.ordering ) + return 1; + return 0; + } +}; + +/* Compare of PriorTable distinguising state data. Using a compare of the + * pointers is a little more strict than it needs be. It requires that + * prioritiy tables have the exact same set of priority assignment operators + * (from the input lang) to be considered equal. + * + * Really only key-value pairs need be tested and ordering be merged. However + * this would require that in the fuseing of states, priority descriptors be + * chosen for the new fused state based on priority. Since the out transition + * lists and ranges aren't necessarily going to line up, this is more work for + * little gain. Final compression resets all priorities first, so this would + * only be useful for compression at every operator, which is only an + * undocumented test feature. + */ +typedef CmpSTable CmpPriorTable; + +/* Plain action list that imposes no ordering. */ +typedef Vector TransFuncList; + +/* Comparison for TransFuncList. */ +typedef CmpTable< int, CmpOrd > TransFuncListCompare; + +/* In transition list. Like DList except only has head pointers, which is all + * that is required. Insertion and deletion is handled by the graph. This class + * provides the iterator of a single list. */ +template struct InList +{ + InList() : head(0) { } + + Element *head; + + struct Iter + { + /* Default construct. */ + Iter() : ptr(0) { } + + /* Construct, assign from a list. */ + Iter( const InList &il ) : ptr(il.head) { } + Iter &operator=( const InList &dl ) { ptr = dl.head; return *this; } + + /* At the end */ + bool lte() const { return ptr != 0; } + bool end() const { return ptr == 0; } + + /* At the first, last element. */ + bool first() const { return ptr && ptr->ilprev == 0; } + bool last() const { return ptr && ptr->ilnext == 0; } + + /* Cast, dereference, arrow ops. */ + operator Element*() const { return ptr; } + Element &operator *() const { return *ptr; } + Element *operator->() const { return ptr; } + + /* Increment, decrement. */ + inline void operator++(int) { ptr = ptr->ilnext; } + inline void operator--(int) { ptr = ptr->ilprev; } + + /* The iterator is simply a pointer. */ + Element *ptr; + }; +}; + +struct TransData +{ + TransData() + : + fromState(0), toState(0) + {} + + TransData( const TransData &other ) + : + fromState(0), toState(0), + actionTable(other.actionTable), + priorTable(other.priorTable), + lmActionTable(other.lmActionTable) + { + } + + StateAp *fromState; + StateAp *toState; + + /* The function table and priority for the transition. */ + ActionTable actionTable; + PriorTable priorTable; + + LmActionTable lmActionTable; +}; + + +/* The element for the sub-list within a TransAp. These specify the transitions + * and are keyed by the condition expressions. */ +struct CondAp + : public TransData +{ + CondAp( TransAp *transAp ) + : + TransData(), + transAp(transAp), + key(0) + {} + + CondAp( const CondAp &other, TransAp *transAp ) + : + TransData( other ), + transAp(transAp), + key(other.key) + { + } + + /* Owning transition. */ + TransAp *transAp; + + CondKey key; + + /* Pointers for outlist. */ + CondAp *prev, *next; + + /* Pointers for in-list. */ + CondAp *ilprev, *ilnext; +}; + +typedef DList CondList; + +struct TransCondAp; +struct TransDataAp; + +/* Transition class that implements actions and priorities. */ +struct TransAp +{ + TransAp() + : condSpace(0) {} + + TransAp( const TransAp &other ) + : + lowKey(other.lowKey), + highKey(other.highKey), + condSpace(other.condSpace) + { + } + + ~TransAp() + { + // delete condList.head; + // condList.abandon(); + } + + bool plain() const + { return condSpace == 0; } + + TransCondAp *tcap(); + TransDataAp *tdap(); + + long condFullSize(); + + Key lowKey, highKey; + + /* Which conditions are tested on this range. */ + CondSpace *condSpace; + + /* Pointers for outlist. */ + TransAp *prev, *next; +}; + +struct TransCondAp + : public TransAp +{ + TransCondAp() + : + TransAp() + {} + + TransCondAp( const TransCondAp &other ) + : + TransAp( other ), + condList() + {} + + ~TransCondAp() + { + condList.empty(); + } + + /* Cond trans list. Sorted by key value. */ + CondList condList; +}; + +struct TransDataAp + : public TransAp, public TransData +{ + TransDataAp() + : + TransAp(), + TransData() + {} + + TransDataAp( const TransDataAp &other ) + : + TransAp( other ), + TransData( other ) + {} + + /* Pointers for in-list. */ + TransDataAp *ilprev, *ilnext; +}; + +inline TransCondAp *TransAp::tcap() + { return this->condSpace != 0 ? static_cast( this ) : 0; } + +inline TransDataAp *TransAp::tdap() + { return this->condSpace == 0 ? static_cast( this ) : 0; } + +typedef DList TransList; + +/* Need the base vector type for accessing underlying remove function. */ +typedef BstSet CondKeySet; +typedef Vector CondKeyVect; + +/* State class that implements actions and priorities. */ + +struct NfaActions +{ + NfaActions( Action *push, Action *pop, int order ) + : push(push), pop(pop), order(order) {} + + Action *push; + Action *pop; + + int order; + + ActionTable pushTable; + ActionTable popTable; +}; + +struct NfaTrans +{ + NfaTrans( int order ) + : + fromState(0), + toState(0), + order(order), + popCondSpace(0) + { + } + + NfaTrans( const ActionTable &pushTable, + const ActionTable &restoreTable, + const ActionTable &popFrom, + CondSpace *popCondSpace, + const CondKeySet popCondKeys, + const ActionTable &popAction, + const ActionTable &popTable, + int order ) + : + fromState(0), toState(0), + order(order), + pushTable(pushTable), + restoreTable(restoreTable), + popFrom(popFrom), + popCondSpace(popCondSpace), + popCondKeys(popCondKeys), + popAction(popAction), + popTest(popTable) + {} + + NfaTrans( const NfaTrans &other ) + : + fromState(0), toState(0), + order(other.order), + pushTable(other.pushTable), + restoreTable(other.restoreTable), + popCondSpace(other.popCondSpace), + popCondKeys(other.popCondKeys), + popAction(other.popAction), + popTest(other.popTest), + priorTable(other.priorTable) + {} + + + StateAp *fromState; + StateAp *toState; + + int order; + + ActionTable pushTable; + ActionTable restoreTable; + + /* + * 1. Conditions transferred (always tested first) + * 2. Actions transferred + * 3. Pop actions created during epsilon draw. + */ + ActionTable popFrom; + CondSpace *popCondSpace; + CondKeySet popCondKeys; + + ActionTable popAction; + ActionTable popTest; + + PriorTable priorTable; + + NfaTrans *prev, *next; + NfaTrans *ilprev, *ilnext; +}; + + +typedef BstMap NfaStateMap; +typedef BstMapEl NfaStateMapEl; + +typedef DList NfaTransList; +typedef InList NfaInList; + +struct CmpNfaTrans +{ + static int compare( NfaTrans *t1, NfaTrans *t2 ) + { + /* This comparison is too strong. (okay to use something too strong -- + * we just don't find minimal). * */ + if ( t1->toState < t2->toState ) + return -1; + else if ( t1->toState > t2->toState ) + return 1; + else if ( t1->order < t2->order ) + return -1; + else if ( t1->order > t2->order ) + return 1; + else + { + int r = CmpActionTable::compare( t1->pushTable, t2->pushTable ); + if ( r != 0 ) + return r; + + r = CmpActionTable::compare( t1->restoreTable, t2->restoreTable ); + if ( r != 0 ) + return r; + + if ( t1->popCondSpace < t2->popCondSpace ) + return -1; + else if ( t1->popCondSpace > t2->popCondSpace ) + return 1; + + r = CmpTable::compare( t1->popCondKeys, t2->popCondKeys ); + if ( r != 0 ) + return r; + + r = CmpActionTable::compare( t1->popTest, t2->popTest ); + if ( r != 0 ) + return r; + + r = CmpActionTable::compare( t1->popAction, t2->popAction ); + if ( r != 0 ) + return r; + } + + return 0; + } +}; + +struct CmpNfaTransList +{ + static int compare( const NfaTransList &l1, const NfaTransList &l2 ) + { + if ( l1.length() < l2.length() ) + return -1; + else if ( l1.length() > l2.length() ) + return 1; + else { + NfaTransList::Iter i1 = l1; + NfaTransList::Iter i2 = l2; + while ( i1.lte() ) { + int r = CmpNfaTrans::compare( i1, i2 ); + if ( r != 0 ) + return r; + i1++, i2++; + } + } + return 0; + } +}; + +struct CmpNfaStateMapEl +{ + static int compare( const NfaStateMapEl &el1, const NfaStateMapEl &el2 ) + { + if ( el1.key < el2.key ) + return -1; + else if ( el1.key > el2.key ) + return 1; + else if ( el1.value.push < el2.value.push ) + return -1; + else if ( el1.value.push > el2.value.push ) + return 1; + else if ( el1.value.pop < el2.value.pop ) + return -1; + else if ( el1.value.pop > el2.value.pop ) + return 1; + else if ( el1.value.order < el2.value.order ) + return -1; + else if ( el1.value.order > el2.value.order ) + return 1; + return 0; + } +}; + +/* Set of states, list of states. */ +typedef BstSet StateSet; +typedef DList StateList; + +/* A element in a state dict. */ +struct StateDictEl +: + public AvlTreeEl +{ + StateDictEl(const StateSet &stateSet) + : stateSet(stateSet) { } + + const StateSet &getKey() { return stateSet; } + StateSet stateSet; + StateAp *targState; +}; + +/* Dictionary mapping a set of states to a target state. */ +typedef AvlTree< StateDictEl, StateSet, CmpTable > StateDict; + +struct TransEl +{ + /* Constructors. */ + TransEl() { } + TransEl( Key lowKey, Key highKey ) + : lowKey(lowKey), highKey(highKey) { } + TransEl( Key lowKey, Key highKey, TransAp *value ) + : lowKey(lowKey), highKey(highKey), value(value) { } + + Key lowKey, highKey; + TransAp *value; +}; + +struct CmpKey +{ + CmpKey() + : keyOps(0) {} + + KeyOps *keyOps; + + int compare( const Key key1, const Key key2 ) + { + if ( keyOps->lt( key1, key2 ) ) + return -1; + else if ( keyOps->gt( key1, key2 ) ) + return 1; + else + return 0; + } +}; + +/* Vector based set of key items. */ +struct KeySet +: + public BstSet +{ + KeySet( KeyOps *keyOps ) + { + CmpKey::keyOps = keyOps; + } +}; + +struct MinPartition +{ + MinPartition() : active(false) { } + + StateList list; + bool active; + + MinPartition *prev, *next; +}; + +/* Epsilon transition stored in a state. Specifies the target */ +typedef Vector EpsilonTrans; + +/* List of states that are to be drawn into this. */ +struct EptVectEl +{ + EptVectEl( StateAp *targ, bool leaving ) + : targ(targ), leaving(leaving) { } + + StateAp *targ; + bool leaving; +}; +typedef Vector EptVect; + +/* Set of entry ids that go into this state. */ +typedef BstSet EntryIdSet; + +/* Set of longest match items that may be active in a given state. */ +typedef BstSet LmItemSet; + +/* A Conditions which is to be + * transfered on pending out transitions. */ +struct OutCond +{ + OutCond( Action *action, bool sense ) + : action(action), sense(sense) {} + + Action *action; + bool sense; +}; + +struct CmpOutCond +{ + static int compare( const OutCond &outCond1, const OutCond &outCond2 ) + { + if ( outCond1.action < outCond2.action ) + return -1; + else if ( outCond1.action > outCond2.action ) + return 1; + else if ( outCond1.sense < outCond2.sense ) + return -1; + else if ( outCond1.sense > outCond2.sense ) + return 1; + return 0; + } +}; + +/* Conditions. */ +typedef BstSet< Action*, CmpCondId > CondSet; +typedef CmpTable< Action*, CmpCondId > CmpCondSet; + +struct CondSpace + : public AvlTreeEl +{ + CondSpace( const CondSet &condSet ) + : condSet(condSet) {} + + const CondSet &getKey() { return condSet; } + + long fullSize() + { return ( 1 << condSet.length() ); } + + CondSet condSet; + long condSpaceId; +}; + +typedef Vector CondSpaceVect; + +typedef AvlTree CondSpaceMap; + +typedef Vector LongVect; + +struct CondData +{ + CondSpaceMap condSpaceMap; + + ~CondData() + { + condSpaceMap.empty(); + } +}; + +struct FsmGbl +{ + FsmGbl( const HostLang *hostLang ) + : + printStatistics(false), + errorCount(0), + displayPrintables(false), + hostLang(hostLang), + stringTables(false), + checkPriorInteraction(0), + wantDupsRemoved(true), + minimizeLevel(MinimizePartition2), + minimizeOpt(MinimizeMostOps) + {} + + bool printStatistics; + + /* + * Error reporting. + */ + + /* PROGNAME: txt */ + std::ostream &error(); + + /* file:loc: txt */ + std::ostream &error( const InputLoc &loc ); + + /* txt */ + std::ostream &error_plain(); + + /* file:loc: warning: txt */ + std::ostream &warning( const InputLoc &loc ); + + /* Stats reporting. */ + std::ostream &stats(); + + /* Requested info. */ + std::ostream &info(); + + std::stringstream libcerr; + std::stringstream libcout; + + int errorCount; + void abortCompile( int code ); + bool displayPrintables; + + const HostLang *hostLang; + bool stringTables; + bool checkPriorInteraction; + bool wantDupsRemoved; + + MinimizeLevel minimizeLevel; + MinimizeOpt minimizeOpt; +}; + +/* All FSM operations must be between machines that have been created using the + * same context object. */ +struct FsmCtx +{ + FsmCtx( FsmGbl *fsmGbl ); + ~FsmCtx(); + + KeyOps *keyOps; + CondData *condData; + MinimizeLevel minimizeLevel; + MinimizeOpt minimizeOpt; + + static const int STATE_UNLIMITED = 0; + + long stateLimit; + bool printStatistics; + bool checkPriorInteraction; + + bool unionOp; + + long condsCheckDepth; + + /* Counting the action and priority ordering. */ + int curActionOrd; + int curPriorOrd; + + int nextPriorKey; + int nextCondId; + + PriorDesc *allocPriorDesc() + { + PriorDesc *priorDesc = new PriorDesc(); + priorDescList.append( priorDesc ); + return priorDesc; + } + + PriorDescList priorDescList; + + FsmGbl *fsmGbl; + + /* List of actions. Will be pasted into a switch statement. */ + ActionList actionList; + + ExportList exportList; + + bool generatingSectionSubset; + bool lmRequiresErrorState; + + /* Make name ids to name inst pointers. */ + NameInst **nameIndex; + + /* Element type and get key expression. */ + InlineList *getKeyExpr; + InlineList *accessExpr; + + /* Stack management */ + InlineBlock *prePushExpr; + InlineBlock *postPopExpr; + + /* Nfa stack managment. */ + InlineBlock *nfaPrePushExpr; + InlineBlock *nfaPostPopExpr; + + /* Overriding variables. */ + InlineList *pExpr; + InlineList *peExpr; + InlineList *eofExpr; + InlineList *csExpr; + InlineList *topExpr; + InlineList *stackExpr; + InlineList *actExpr; + InlineList *tokstartExpr; + InlineList *tokendExpr; + InlineList *dataExpr; + + Action *newNfaWrapAction( const char *name, InlineList *inlineList, Action *optWrap ); + void createNfaActions( FsmAp *fsm ); + + /* Checking the contents of actions. */ + void checkAction( Action *action ); + void checkInlineList( Action *act, InlineList *inlineList ); + + void analyzeAction( Action *action, InlineList *inlineList ); + void analyzeGraph( FsmAp *graph ); + + void finalizeInstance( FsmAp *graph ); + void prepareReduction( FsmAp *sectionGraph ); +}; + +typedef InList CondInList; +typedef InList TransInList; + +struct NfaStateEl +{ + StateAp *prev, *next; +}; + +typedef DListMel NfaStateList; + +struct StateAp + : public NfaStateEl +{ + StateAp(); + StateAp(const StateAp &other); + ~StateAp(); + + /* Is the state final? */ + bool isFinState() { return stateBits & STB_ISFINAL; } + + /* Out transition list and the pointer for the default out trans. */ + TransList outList; + + /* In transition Lists. */ + TransInList inTrans; + CondInList inCond; + + /* Set only during scanner construction when actions are added. NFA to DFA + * code can ignore this. */ + StateAp *eofTarget; + + /* Entry points into the state. */ + EntryIdSet entryIds; + + /* Epsilon transitions. */ + EpsilonTrans epsilonTrans; + + /* Number of in transitions from states other than ourselves. */ + int foreignInTrans; + + /* Temporary data for various algorithms. */ + union { + /* When duplicating the fsm we need to map each + * state to the new state representing it. */ + StateAp *stateMap; + + /* When minimizing machines by partitioning, this maps to the group + * the state is in. */ + MinPartition *partition; + + /* Identification for printing and stable minimization. */ + int stateNum; + + } alg; + + /* Data used in epsilon operation, maybe fit into alg? */ + StateAp *isolatedShadow; + int owningGraph; + + /* A pointer to a dict element that contains the set of states this state + * represents. This cannot go into alg, because alg.next is used during + * the merging process. */ + StateDictEl *stateDictEl; + StateSet *stateDictIn; + + NfaTransList *nfaOut; + NfaInList *nfaIn; + + /* When drawing epsilon transitions, holds the list of states to merge + * with. */ + EptVect *eptVect; + + /* Bits controlling the behaviour of the state during collapsing to dfa. */ + int stateBits; + + /* State list elements. */ + StateAp *next, *prev; + + /* + * Priority and Action data. + */ + + /* Out priorities transfered to out transitions. */ + PriorTable outPriorTable; + + /* The following two action tables are distinguished by the fact that when + * toState actions are executed immediatly after transition actions of + * incoming transitions and the current character will be the same as the + * one available then. The fromState actions are executed immediately + * before the transition actions of outgoing transitions and the current + * character is same as the one available then. */ + + /* Actions to execute upon entering into a state. */ + ActionTable toStateActionTable; + + /* Actions to execute when going from the state to the transition. */ + ActionTable fromStateActionTable; + + /* Actions to add to any future transitions that leave via this state. */ + ActionTable outActionTable; + + /* Conditions to add to any future transiions that leave via this state. */ + CondSpace *outCondSpace; + CondKeySet outCondKeys; + + /* Error action tables. */ + ErrActionTable errActionTable; + + /* Actions to execute on eof. */ + ActionTable eofActionTable; + + /* Set of longest match items that may be active in this state. */ + LmItemSet lmItemSet; + + PriorTable guardedInTable; + + /* Used by the NFA-based scanner to track the origin of final states. We + * only use it in cases where just one match is possible, starting with the + * final state duplicates that are drawn using NFA transitions. */ + LmItemSet lmNfaParts; +}; + +/* Return and re-entry for the co-routine iterators. This should ALWAYS be + * used inside of a block. */ +#define CO_RETURN(label) \ + itState = label; \ + return; \ + entry##label: {} + +/* Return and re-entry for the co-routine iterators. This should ALWAYS be + * used inside of a block. */ +#define CO_RETURN2(label, uState) \ + itState = label; \ + userState = uState; \ + return; \ + entry##label: {} + +template struct PiList +{ + PiList() + : ptr(0) {} + + PiList( const DList &l ) + : ptr(l.head) {} + + PiList( Item *ptr ) + : ptr(ptr) {} + + operator Item *() const { return ptr; } + Item *operator->() const { return ptr; } + + bool end() { return ptr == 0; } + void clear() { ptr = 0; } + + PiList next() + { return PiList( ptr->next ); } + + Item *ptr; +}; + +template struct PiSingle +{ + PiSingle() + : ptr(0) {} + + PiSingle( Item *ptr ) + : ptr(ptr) {} + + operator Item *() const { return ptr; } + Item *operator->() const { return ptr; } + + bool end() { return ptr == 0; } + void clear() { ptr = 0; } + + /* Next is always nil. */ + PiSingle next() + { return PiSingle( 0 ); } + + Item *ptr; +}; + +template struct PiVector +{ + PiVector() + : ptr(0), length(0) {} + + PiVector( const Vector &v ) + : ptr(v.data), length(v.length()) {} + + PiVector( Item *ptr, long length ) + : ptr(ptr), length(length) {} + + operator Item *() const { return ptr; } + Item *operator->() const { return ptr; } + + bool end() { return length == 0; } + void clear() { ptr = 0; length = 0; } + + PiVector next() + { return PiVector( ptr + 1, length - 1 ); } + + Item *ptr; + long length; +}; + + +template struct ValPairIter +{ + /* Encodes the states that are meaningful to the of caller the iterator. */ + enum UserState + { + RangeInS1, RangeInS2, + RangeOverlap, + }; + + /* Encodes the different states that an fsm iterator can be in. */ + enum IterState { + Begin, + ConsumeS1Range, ConsumeS2Range, + OnlyInS1Range, OnlyInS2Range, + ExactOverlap, End + }; + + ValPairIter( const ItemIter1 &list1, const ItemIter2 &list2 ); + + template struct NextTrans + { + CondKey key; + ItemIter trans; + ItemIter next; + + NextTrans() { key = 0; } + + void load() { + if ( trans.end() ) + next.clear(); + else { + next = trans->next; + key = trans->key; + } + } + + void set( const ItemIter &t ) { + trans = t; + load(); + } + + void increment() { + trans = next; + load(); + } + }; + + /* Query iterator. */ + bool lte() { return itState != End; } + bool end() { return itState == End; } + void operator++(int) { findNext(); } + void operator++() { findNext(); } + + /* Iterator state. */ + ItemIter1 list1; + ItemIter2 list2; + IterState itState; + UserState userState; + + NextTrans s1Tel; + NextTrans s2Tel; + Key bottomLow, bottomHigh; + ItemIter1 *bottomTrans1; + ItemIter2 *bottomTrans2; + +private: + void findNext(); +}; + +/* Init the iterator by advancing to the first item. */ +template + ValPairIter:: + ValPairIter( const ItemIter1 &list1, const ItemIter2 &list2 ) +: + list1(list1), + list2(list2), + itState(Begin) +{ + findNext(); +} + +/* Advance to the next transition. When returns, trans points to the next + * transition, unless there are no more, in which case end() returns true. */ +template + void ValPairIter::findNext() +{ + /* Jump into the iterator routine base on the iterator state. */ + switch ( itState ) { + case Begin: goto entryBegin; + case ConsumeS1Range: goto entryConsumeS1Range; + case ConsumeS2Range: goto entryConsumeS2Range; + case OnlyInS1Range: goto entryOnlyInS1Range; + case OnlyInS2Range: goto entryOnlyInS2Range; + case ExactOverlap: goto entryExactOverlap; + case End: goto entryEnd; + } + +entryBegin: + /* Set up the next structs at the head of the transition lists. */ + s1Tel.set( list1 ); + s2Tel.set( list2 ); + + /* Concurrently scan both out ranges. */ + while ( true ) { + if ( s1Tel.trans.end() ) { + /* We are at the end of state1's ranges. Process the rest of + * state2's ranges. */ + while ( !s2Tel.trans.end() ) { + /* Range is only in s2. */ + CO_RETURN2( ConsumeS2Range, RangeInS2 ); + s2Tel.increment(); + } + break; + } + else if ( s2Tel.trans.end() ) { + /* We are at the end of state2's ranges. Process the rest of + * state1's ranges. */ + while ( !s1Tel.trans.end() ) { + /* Range is only in s1. */ + CO_RETURN2( ConsumeS1Range, RangeInS1 ); + s1Tel.increment(); + } + break; + } + /* Both state1's and state2's transition elements are good. + * The signiture of no overlap is a back key being in front of a + * front key. */ + else if ( s1Tel.key < s2Tel.key ) { + /* A range exists in state1 that does not overlap with state2. */ + CO_RETURN2( OnlyInS1Range, RangeInS1 ); + s1Tel.increment(); + } + else if ( s2Tel.key < s1Tel.key ) { + /* A range exists in state2 that does not overlap with state1. */ + CO_RETURN2( OnlyInS2Range, RangeInS2 ); + s2Tel.increment(); + } + else { + /* There is an exact overlap. */ + CO_RETURN2( ExactOverlap, RangeOverlap ); + + s1Tel.increment(); + s2Tel.increment(); + } + } + + /* Done, go into end state. */ + CO_RETURN( End ); +} + +template struct RangePairIter +{ + /* Encodes the states that are meaningful to the of caller the iterator. */ + enum UserState + { + RangeInS1, RangeInS2, + RangeOverlap, + BreakS1, BreakS2 + }; + + /* Encodes the different states that an fsm iterator can be in. */ + enum IterState { + Begin, + ConsumeS1Range, ConsumeS2Range, + OnlyInS1Range, OnlyInS2Range, + S1SticksOut, S1SticksOutBreak, + S2SticksOut, S2SticksOutBreak, + S1DragsBehind, S1DragsBehindBreak, + S2DragsBehind, S2DragsBehindBreak, + ExactOverlap, End + }; + + RangePairIter( FsmCtx *ctx, const ItemIter1 &list1, const ItemIter2 &list2 ); + + template struct NextTrans + { + Key lowKey, highKey; + ItemIter trans; + ItemIter next; + + NextTrans() + { + highKey = 0; + lowKey = 0; + } + + void load() { + if ( trans.end() ) + next.clear(); + else { + next = trans.next(); + lowKey = trans->lowKey; + highKey = trans->highKey; + } + } + + void set( const ItemIter &t ) { + trans = t; + load(); + } + + void increment() { + trans = next; + load(); + } + }; + + /* Query iterator. */ + bool lte() { return itState != End; } + bool end() { return itState == End; } + void operator++(int) { findNext(); } + void operator++() { findNext(); } + + FsmCtx *ctx; + + /* Iterator state. */ + ItemIter1 list1; + ItemIter2 list2; + IterState itState; + UserState userState; + + NextTrans s1Tel; + NextTrans s2Tel; + Key bottomLow, bottomHigh; + ItemIter1 bottomTrans1; + ItemIter2 bottomTrans2; + +private: + void findNext(); +}; + +/* Init the iterator by advancing to the first item. */ +template RangePairIter:: + RangePairIter( FsmCtx *ctx, const ItemIter1 &list1, const ItemIter2 &list2 ) +: + ctx(ctx), + list1(list1), + list2(list2), + itState(Begin) +{ + bottomLow = 0; + bottomHigh = 0; + findNext(); +} + +/* Advance to the next transition. When returns, trans points to the next + * transition, unless there are no more, in which case end() returns true. */ +template + void RangePairIter::findNext() +{ + /* Jump into the iterator routine base on the iterator state. */ + switch ( itState ) { + case Begin: goto entryBegin; + case ConsumeS1Range: goto entryConsumeS1Range; + case ConsumeS2Range: goto entryConsumeS2Range; + case OnlyInS1Range: goto entryOnlyInS1Range; + case OnlyInS2Range: goto entryOnlyInS2Range; + case S1SticksOut: goto entryS1SticksOut; + case S1SticksOutBreak: goto entryS1SticksOutBreak; + case S2SticksOut: goto entryS2SticksOut; + case S2SticksOutBreak: goto entryS2SticksOutBreak; + case S1DragsBehind: goto entryS1DragsBehind; + case S1DragsBehindBreak: goto entryS1DragsBehindBreak; + case S2DragsBehind: goto entryS2DragsBehind; + case S2DragsBehindBreak: goto entryS2DragsBehindBreak; + case ExactOverlap: goto entryExactOverlap; + case End: goto entryEnd; + } + +entryBegin: + /* Set up the next structs at the head of the transition lists. */ + s1Tel.set( list1 ); + s2Tel.set( list2 ); + + /* Concurrently scan both out ranges. */ + while ( true ) { + if ( s1Tel.trans.end() ) { + /* We are at the end of state1's ranges. Process the rest of + * state2's ranges. */ + while ( !s2Tel.trans.end() ) { + /* Range is only in s2. */ + CO_RETURN2( ConsumeS2Range, RangeInS2 ); + s2Tel.increment(); + } + break; + } + else if ( s2Tel.trans.end() ) { + /* We are at the end of state2's ranges. Process the rest of + * state1's ranges. */ + while ( !s1Tel.trans.end() ) { + /* Range is only in s1. */ + CO_RETURN2( ConsumeS1Range, RangeInS1 ); + s1Tel.increment(); + } + break; + } + /* Both state1's and state2's transition elements are good. + * The signiture of no overlap is a back key being in front of a + * front key. */ + else if ( ctx->keyOps->lt( s1Tel.highKey, s2Tel.lowKey ) ) { + /* A range exists in state1 that does not overlap with state2. */ + CO_RETURN2( OnlyInS1Range, RangeInS1 ); + s1Tel.increment(); + } + else if ( ctx->keyOps->lt( s2Tel.highKey, s1Tel.lowKey ) ) { + /* A range exists in state2 that does not overlap with state1. */ + CO_RETURN2( OnlyInS2Range, RangeInS2 ); + s2Tel.increment(); + } + /* There is overlap, must mix the ranges in some way. */ + else if ( ctx->keyOps->lt( s1Tel.lowKey, s2Tel.lowKey ) ) { + /* Range from state1 sticks out front. Must break it into + * non-overlaping and overlaping segments. */ + bottomLow = s2Tel.lowKey; + bottomHigh = s1Tel.highKey; + s1Tel.highKey = s2Tel.lowKey; + ctx->keyOps->decrement( s1Tel.highKey ); + bottomTrans1 = s1Tel.trans; + + /* Notify the caller that we are breaking s1. This gives them a + * chance to duplicate s1Tel[0,1].value. */ + CO_RETURN2( S1SticksOutBreak, BreakS1 ); + + /* Broken off range is only in s1. */ + CO_RETURN2( S1SticksOut, RangeInS1 ); + + /* Advance over the part sticking out front. */ + s1Tel.lowKey = bottomLow; + s1Tel.highKey = bottomHigh; + s1Tel.trans = bottomTrans1; + } + else if ( ctx->keyOps->lt( s2Tel.lowKey, s1Tel.lowKey ) ) { + /* Range from state2 sticks out front. Must break it into + * non-overlaping and overlaping segments. */ + bottomLow = s1Tel.lowKey; + bottomHigh = s2Tel.highKey; + s2Tel.highKey = s1Tel.lowKey; + ctx->keyOps->decrement( s2Tel.highKey ); + bottomTrans2 = s2Tel.trans; + + /* Notify the caller that we are breaking s2. This gives them a + * chance to duplicate s2Tel[0,1].value. */ + CO_RETURN2( S2SticksOutBreak, BreakS2 ); + + /* Broken off range is only in s2. */ + CO_RETURN2( S2SticksOut, RangeInS2 ); + + /* Advance over the part sticking out front. */ + s2Tel.lowKey = bottomLow; + s2Tel.highKey = bottomHigh; + s2Tel.trans = bottomTrans2; + } + /* Low ends are even. Are the high ends even? */ + else if ( ctx->keyOps->lt( s1Tel.highKey, s2Tel.highKey ) ) { + /* Range from state2 goes longer than the range from state1. We + * must break the range from state2 into an evenly overlaping + * segment. */ + bottomLow = s1Tel.highKey; + ctx->keyOps->increment( bottomLow ); + bottomHigh = s2Tel.highKey; + s2Tel.highKey = s1Tel.highKey; + bottomTrans2 = s2Tel.trans; + + /* Notify the caller that we are breaking s2. This gives them a + * chance to duplicate s2Tel[0,1].value. */ + CO_RETURN2( S2DragsBehindBreak, BreakS2 ); + + /* Breaking s2 produces exact overlap. */ + CO_RETURN2( S2DragsBehind, RangeOverlap ); + + /* Advance over the front we just broke off of range 2. */ + s2Tel.lowKey = bottomLow; + s2Tel.highKey = bottomHigh; + s2Tel.trans = bottomTrans2; + + /* Advance over the entire s1Tel. We have consumed it. */ + s1Tel.increment(); + } + else if ( ctx->keyOps->lt( s2Tel.highKey, s1Tel.highKey ) ) { + /* Range from state1 goes longer than the range from state2. We + * must break the range from state1 into an evenly overlaping + * segment. */ + bottomLow = s2Tel.highKey; + ctx->keyOps->increment( bottomLow ); + bottomHigh = s1Tel.highKey; + s1Tel.highKey = s2Tel.highKey; + bottomTrans1 = s1Tel.trans; + + /* Notify the caller that we are breaking s1. This gives them a + * chance to duplicate s2Tel[0,1].value. */ + CO_RETURN2( S1DragsBehindBreak, BreakS1 ); + + /* Breaking s1 produces exact overlap. */ + CO_RETURN2( S1DragsBehind, RangeOverlap ); + + /* Advance over the front we just broke off of range 1. */ + s1Tel.lowKey = bottomLow; + s1Tel.highKey = bottomHigh; + s1Tel.trans = bottomTrans1; + + /* Advance over the entire s2Tel. We have consumed it. */ + s2Tel.increment(); + } + else { + /* There is an exact overlap. */ + CO_RETURN2( ExactOverlap, RangeOverlap ); + + s1Tel.increment(); + s2Tel.increment(); + } + } + + /* Done, go into end state. */ + CO_RETURN( End ); +} + + +/* Compare lists of epsilon transitions. Entries are name ids of targets. */ +typedef CmpTable< int, CmpOrd > CmpEpsilonTrans; + +/* Compare class for the Approximate minimization. */ +class ApproxCompare +{ +public: + ApproxCompare( FsmCtx *ctx = 0 ) : ctx(ctx) { } + int compare( const StateAp *pState1, const StateAp *pState2 ); + FsmCtx *ctx; +}; + +/* Compare class for the initial partitioning of a partition minimization. */ +class InitPartitionCompare +{ +public: + InitPartitionCompare( FsmCtx *ctx = 0 ) : ctx(ctx) { } + int compare( const StateAp *pState1, const StateAp *pState2 ); + FsmCtx *ctx; +}; + +/* Compare class for the regular partitioning of a partition minimization. */ +class PartitionCompare +{ +public: + PartitionCompare( FsmCtx *ctx = 0 ) : ctx(ctx) { } + int compare( const StateAp *pState1, const StateAp *pState2 ); + FsmCtx *ctx; +}; + +/* Compare class for a minimization that marks pairs. Provides the shouldMark + * routine. */ +class MarkCompare +{ +public: + MarkCompare( FsmCtx *ctx ) : ctx(ctx) { } + bool shouldMark( MarkIndex &markIndex, const StateAp *pState1, + const StateAp *pState2 ); + FsmCtx *ctx; +}; + +/* List of partitions. */ +typedef DList< MinPartition > PartitionList; + +/* List of transtions out of a state. */ +typedef Vector TransListVect; + +/* Entry point map used for keeping track of entry points in a machine. */ +typedef BstSet< int > EntryIdSet; +typedef BstMapEl< int, StateAp* > EntryMapEl; +typedef BstMap< int, StateAp* > EntryMap; +typedef Vector EntryMapBase; + +struct BreadthCost +{ + BreadthCost( std::string name, double cost ) + : name(name), cost(cost) {} + + std::string name; + double cost; +}; + +struct BreadthResult +{ + BreadthResult( double start ) : start(start) {} + + double start; + Vector costs; +}; + +/* Result of an operation. */ +struct FsmRes +{ + struct Fsm {}; + struct TooManyStates {}; + struct PriorInteraction {}; + struct CondCostTooHigh {}; + struct InternalError {}; + + enum Type + { + TypeFsm = 1, + TypeTooManyStates, + TypePriorInteraction, + TypeCondCostTooHigh, + TypeInternalError, + }; + + FsmRes( const Fsm &, FsmAp *fsm ) + : fsm(fsm), type(TypeFsm) {} + + FsmRes( const TooManyStates & ) + : fsm(0), type(TypeTooManyStates) {} + + FsmRes( const PriorInteraction &, long long guardId ) + : fsm(0), type(TypePriorInteraction), id(guardId) {} + + FsmRes( const CondCostTooHigh &, long long costId ) + : fsm(0), type(TypeCondCostTooHigh), id(costId) {} + + FsmRes( const InternalError & ) + : fsm(0), type(TypeInternalError) {} + + bool success() + { return fsm != 0; } + + operator FsmAp*() + { return type == TypeFsm ? fsm : 0; } + FsmAp *operator->() + { return type == TypeFsm ? fsm : 0; } + + FsmAp *fsm; + Type type; + long long id; +}; + +/* Graph class that implements actions and priorities. */ +struct FsmAp +{ + /* Constructors/Destructors. */ + FsmAp( FsmCtx *ctx ); + FsmAp( const FsmAp &graph ); + ~FsmAp(); + + FsmCtx *ctx; + + bool priorInteraction; + int guardId; + + /* The list of states. */ + StateList stateList; + StateList misfitList; + NfaStateList nfaList; + StateDict stateDict; + + /* The map of entry points. */ + EntryMap entryPoints; + + /* The start state. */ + StateAp *startState; + + /* Error state, possibly created only when the final machine has been + * created and the XML machine is about to be written. No transitions + * point to this state. */ + StateAp *errState; + + /* The set of final states. */ + StateSet finStateSet; + + /* Misfit Accounting. Are misfits put on a separate list. */ + bool misfitAccounting; + + /* + * Transition actions and priorities. + */ + + /* Set priorities on transtions. */ + void startFsmPrior( int ordering, PriorDesc *prior ); + void allTransPrior( int ordering, PriorDesc *prior ); + void finishFsmPrior( int ordering, PriorDesc *prior ); + void leaveFsmPrior( int ordering, PriorDesc *prior ); + + /* Action setting support. */ + void transferOutActions( StateAp *state ); + void transferErrorActions( StateAp *state, int transferPoint ); + void setErrorActions( StateAp *state, const ActionTable &other ); + void setErrorAction( StateAp *state, int ordering, Action *action ); + + /* Fill all spaces in a transition list with an error transition. */ + void fillGaps( StateAp *state ); + + /* Similar to setErrorAction, instead gives a state to go to on error. */ + void setErrorTarget( StateAp *state, StateAp *target, int *orderings, + Action **actions, int nActs ); + + /* Set actions to execute. */ + void startFsmAction( int ordering, Action *action ); + void allTransAction( int ordering, Action *action ); + void finishFsmAction( int ordering, Action *action ); + void leaveFsmAction( int ordering, Action *action ); + void longMatchAction( int ordering, FsmLongestMatchPart *lmPart ); + + /* Set conditions. */ + CondSpace *addCondSpace( const CondSet &condSet ); + + void convertToCondAp( StateAp *state ); + +private: + /* Can generate states. */ + void doEmbedCondition( StateAp *state, + const CondSet &set, const CondKeySet &vals ); + + +public: + static FsmRes embedCondition( FsmAp *fsm, StateAp *state, const CondSet &set, + const CondKeySet &vals ); + + FsmRes startFsmCondition( Action *condAction, bool sense ); + void allTransCondition( Action *condAction, bool sense ); + void leaveFsmCondition( Action *condAction, bool sense ); + + /* Set error actions to execute. */ + void startErrorAction( int ordering, Action *action, int transferPoint ); + void allErrorAction( int ordering, Action *action, int transferPoint ); + void finalErrorAction( int ordering, Action *action, int transferPoint ); + void notStartErrorAction( int ordering, Action *action, int transferPoint ); + void notFinalErrorAction( int ordering, Action *action, int transferPoint ); + void middleErrorAction( int ordering, Action *action, int transferPoint ); + + /* Set EOF actions. */ + void startEOFAction( int ordering, Action *action ); + void allEOFAction( int ordering, Action *action ); + void finalEOFAction( int ordering, Action *action ); + void notStartEOFAction( int ordering, Action *action ); + void notFinalEOFAction( int ordering, Action *action ); + void middleEOFAction( int ordering, Action *action ); + + /* Set To State actions. */ + void startToStateAction( int ordering, Action *action ); + void allToStateAction( int ordering, Action *action ); + void finalToStateAction( int ordering, Action *action ); + void notStartToStateAction( int ordering, Action *action ); + void notFinalToStateAction( int ordering, Action *action ); + void middleToStateAction( int ordering, Action *action ); + + /* Set From State actions. */ + void startFromStateAction( int ordering, Action *action ); + void allFromStateAction( int ordering, Action *action ); + void finalFromStateAction( int ordering, Action *action ); + void notStartFromStateAction( int ordering, Action *action ); + void notFinalFromStateAction( int ordering, Action *action ); + void middleFromStateAction( int ordering, Action *action ); + + /* Shift the action ordering of the start transitions to start at + * fromOrder and increase in units of 1. Useful before kleene star + * operation. */ + int shiftStartActionOrder( int fromOrder ); + + /* Clear all priorities from the fsm to so they won't affcet minimization + * of the final fsm. */ + void clearAllPriorities(); + + /* Zero out all the function keys. */ + void nullActionKeys(); + + /* Walk the list of states and verify state properties. */ + void verifyStates(); + + /* Misfit Accounting. Are misfits put on a separate list. */ + void setMisfitAccounting( bool val ) + { misfitAccounting = val; } + + /* Set and Unset a state as final. */ + void setFinState( StateAp *state ); + void unsetFinState( StateAp *state ); + + void setStartState( StateAp *state ); + void unsetStartState( ); + + /* Set and unset a state as an entry point. */ + void setEntry( int id, StateAp *state ); + void changeEntry( int id, StateAp *to, StateAp *from ); + void unsetEntry( int id, StateAp *state ); + void unsetEntry( int id ); + void unsetAllEntryPoints(); + + /* Epsilon transitions. */ + void epsilonTrans( int id ); + + void checkEpsilonRegularInteraction( const PriorTable &t1, const PriorTable &t2 ); + +private: + /* Can generate staes. */ + void shadowReadWriteStates(); + + void afterOpMinimize( bool lastInSeq = true ); + + void removeDups( ActionTable &table ); + +public: + + void removeActionDups(); + + /* + * Basic attaching and detaching. + */ + + /* Common to attaching/detaching list and default. */ + template < class Head > void attachToInList( StateAp *from, + StateAp *to, Head *&head, Head *trans ); + template < class Head > void detachFromInList( StateAp *from, + StateAp *to, Head *&head, Head *trans ); + + void attachToNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ); + void detachFromNfa( StateAp *from, StateAp *to, NfaTrans *nfaTrans ); + + void attachStateDict( StateAp *from, StateAp *to ); + void detachStateDict( StateAp *from, StateAp *to ); + + /* Attach with a new transition. */ + CondAp *attachNewCond( TransAp *trans, StateAp *from, + StateAp *to, CondKey onChar ); + TransAp *attachNewTrans( StateAp *from, StateAp *to, + Key onChar1, Key onChar2 ); + + /* Attach with an existing transition that already in an out list. */ + void attachTrans( StateAp *from, StateAp *to, TransDataAp *trans ); + void attachTrans( StateAp *from, StateAp *to, CondAp *trans ); + + /* Redirect a transition away from error and towards some state. */ + void redirectErrorTrans( StateAp *from, StateAp *to, TransDataAp *trans ); + void redirectErrorTrans( StateAp *from, StateAp *to, CondAp *trans ); + + /* Detach a transition from a target state. */ + void detachTrans( StateAp *from, StateAp *to, TransDataAp *trans ); + void detachTrans( StateAp *from, StateAp *to, CondAp *trans ); + + /* Detach a state from the graph. */ + void detachState( StateAp *state ); + + /* + * NFA to DFA conversion routines. + */ + + /* Duplicate a transition that will dropin to a free spot. */ + TransDataAp *dupTransData( StateAp *from, TransDataAp *srcTrans ); + TransAp *dupTrans( StateAp *from, TransAp *srcTrans ); + CondAp *dupCondTrans( StateAp *from, TransAp *destParent, CondAp *srcTrans ); + +private: + /* In crossing, two transitions both go to real states. Can generate + * states. */ + template< class Trans > Trans *fsmAttachStates( + StateAp *from, Trans *destTrans, Trans *srcTrans ); + +public: + void expandConds( StateAp *fromState, TransAp *trans, + CondSpace *fromSpace, CondSpace *mergedSpace ); + TransAp *copyTransForExpansion( StateAp *fromState, TransAp *srcTrans ); + StateAp *copyStateForExpansion( StateAp *srcState ); + void freeEffectiveTrans( TransAp *srcTrans ); + +private: + /* Two transitions are to be crossed, handle the possibility of either + * going to the error state. Can generate states. */ + template< class Trans > Trans *mergeTrans( StateAp *from, + Trans *destTrans, Trans *srcTrans ); + +public: + /* Compare deterimne relative priorities of two transition tables. */ + int comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 ); + + void addOutCondition( StateAp *state, Action *condAction, bool sense ); + + void expandCondKeys( CondKeySet &condKeys, CondSpace *fromSpace, + CondSpace *mergedSpace ); + + /* Back to trans ap (minimmization) */ + TransDataAp *convertToTransAp( StateAp *from, CondAp *cond ); + + /* Cross a src transition with one that is already occupying a spot. */ + TransCondAp *convertToCondAp( StateAp *state, TransDataAp *trans ); + CondSpace *expandCondSpace( TransAp *destTrans, TransAp *srcTrans ); + +private: + /* Can generate states. */ + TransAp *crossTransitions( StateAp *from, + TransAp *destTrans, TransAp *srcTrans ); + TransDataAp *crossTransitionsBothPlain( StateAp *from, + TransDataAp *destTrans, TransDataAp *srcTrans ); + CondAp *crossCondTransitions( StateAp *from, + TransAp *destParent, CondAp *destTrans, CondAp *srcTrans ); + +public: + void prepareNfaRound(); + void finalizeNfaRound(); + + void outTransCopy( StateAp *dest, TransAp *srcList ); + void nfaMergeStates( StateAp *destState, StateAp **srcStates, int numSrc ); + void mergeOutConds( StateAp *destState, StateAp *srcState, bool leaving = false ); + void checkPriorInteractions( StateAp *destState, StateAp *srcState ); + void mergeNfaTransitions( StateAp *destState, StateAp *srcState ); + void mergeStateProperties( StateAp *destState, StateAp *srcState ); + void mergeStatesLeaving( StateAp *destState, StateAp *srcState ); + void mergeStateBits( StateAp *destState, StateAp *srcState ); + void mergeStates( StateAp *destState, StateAp *srcState, bool leaving = false ); + + /* Merge a set of states into destState. */ + void mergeStateList( StateAp *destState, StateAp **srcStates, int numSrc ); + + /* Make all states that are combinations of other states and that + * have not yet had their out transitions filled in. This will + * empty out stateDict and stFil. */ + void cleanAbortedFill( StateAp *state ); + void cleanAbortedFill(); + bool overStateLimit(); + void nfaFillInStates(); + + /* + * Transition Comparison. + */ + + template< class Trans > int compareCondBitElim( Trans *trans1, Trans *trans2 ); + template< class Trans > int compareCondBitElimPtr( Trans *trans1, Trans *trans2 ); + int compareCondListBitElim( const CondList &condList1, const CondList &condList2 ); + + /* Compare priority and function table of transitions. */ + static int compareTransData( TransAp *trans1, TransAp *trans2 ); + template< class Trans > static int compareCondData( Trans *trans1, Trans *trans2 ); + + /* Compare transition data. Either of the pointers may be null. */ + static int compareTransDataPtr( TransAp *trans1, TransAp *trans2 ); + template< class Trans > static int compareCondDataPtr( Trans *trans1, Trans *trans2 ); + + /* Compare target state and transition data. Either pointer may be null. */ + static int compareFullPtr( TransAp *trans1, TransAp *trans2 ); + + /* Compare target partitions. Either pointer may be null. */ + static int compareTransPartPtr( TransAp *trans1, TransAp *trans2 ); + template< class Trans > static int compareCondPartPtr( Trans *trans1, Trans *trans2 ); + + static int comparePart( TransAp *trans1, TransAp *trans2 ); + + /* Check marked status of target states. Either pointer may be null. */ + static bool shouldMarkPtr( MarkIndex &markIndex, + TransAp *trans1, TransAp *trans2 ); + + /* + * Callbacks. + */ + + /* Add in the properties of srcTrans into this. */ + template< class Trans > void addInTrans( Trans *destTrans, Trans *srcTrans ); + + /* Compare states on data stored in the states. */ + static int compareStateData( const StateAp *state1, const StateAp *state2 ); + + /* Out transition data. */ + void clearOutData( StateAp *state ); + bool hasOutData( StateAp *state ); + void transferOutData( StateAp *destState, StateAp *srcState ); + + /* + * Allocation. + */ + + /* New up a state and add it to the graph. */ + StateAp *addState(); + + /* + * Building basic machines + */ + + static FsmAp *concatFsm( FsmCtx *ctx, Key c ); + static FsmAp *concatFsmCI( FsmCtx *ctx, Key c ); + static FsmAp *concatFsm( FsmCtx *ctx, Key *str, int len ); + static FsmAp *concatFsmCI( FsmCtx *ctx, Key *str, int len ); + static FsmAp *orFsm( FsmCtx *ctx, Key *set, int len ); + static FsmAp *rangeFsm( FsmCtx *ctx, Key low, Key high ); + static FsmAp *rangeFsmCI( FsmCtx *ctx, Key low, Key high ); + static FsmAp *rangeStarFsm( FsmCtx *ctx, Key low, Key high ); + static FsmAp *emptyFsm( FsmCtx *ctx ); + static FsmAp *lambdaFsm( FsmCtx *ctx ); + static FsmAp *dotFsm( FsmCtx *ctx ); + static FsmAp *dotStarFsm( FsmCtx *ctx ); + static FsmAp *notRangeFsm( FsmCtx *ctx, Key low, Key high ); + + /* + * Fsm operators. + */ + + static FsmRes starOp( FsmAp *fsm ); + static FsmRes plusOp( FsmAp *fsm ); + static FsmRes questionOp( FsmAp *fsm ); + + static FsmRes exactRepeatOp( FsmAp *fsm, int times ); + static FsmRes maxRepeatOp( FsmAp *fsm, int times ); + static FsmRes minRepeatOp( FsmAp *fsm, int times ); + static FsmRes rangeRepeatOp( FsmAp *fsm, int lower, int upper ); + + static FsmRes concatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true, + StateSet *fromStates = 0, bool optional = false ); + static FsmRes unionOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); + static FsmRes intersectOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); + static FsmRes subtractOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); + static FsmRes epsilonOp( FsmAp *fsm ); + static FsmRes joinOp( FsmAp *fsm, int startId, int finalId, FsmAp **others, int numOthers ); + + static FsmRes rightStartConcatOp( FsmAp *fsm, FsmAp *other, bool lastInSeq = true ); + + void transferOutToNfaTrans( NfaTrans *trans, StateAp *state ); + + enum NfaRepeatMode { + NfaLegacy = 1, + NfaGreedy, + NfaLazy + }; + + static FsmRes applyNfaTrans( FsmAp *fsm, StateAp *fromState, StateAp *toState, NfaTrans *nfaTrans ); + + /* Results in an NFA. */ + static FsmRes nfaUnionOp( FsmAp *fsm, FsmAp **others, int n, int depth, std::ostream &stats ); + static FsmRes nfaRepeatOp( FsmAp *fsm, Action *push, Action *pop, Action *init, + Action *stay, Action *repeat, Action *exit ); + + static FsmRes nfaRepeatOp2( FsmAp *fsm, Action *push, Action *pop, Action *init, + Action *stay, Action *repeat, Action *exit, NfaRepeatMode mode = NfaGreedy ); + static FsmRes nfaWrap( FsmAp *fsm, Action *push, Action *pop, Action *init, + Action *stay, Action *exit, NfaRepeatMode mode = NfaGreedy ); + + static FsmRes nfaUnion( const NfaRoundVect &roundsList, FsmAp **machines, + int numMachines, std::ostream &stats, bool printStatistics ); + + static FsmRes condPlus( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ); + static FsmRes condStar( FsmAp *fsm, long repId, Action *ini, Action *inc, Action *min, Action *max ); + + /* Make a new start state that has no entry points. Will not change the + * meaning of the fsm. */ + static FsmRes isolateStartState( FsmAp *fsm ); + + /* + * Analysis Functions + */ + static FsmRes condCostFromState( FsmAp *fsm, StateAp *state, long depth ); + static FsmRes condCostSearch( FsmAp *fsm ); + static void breadthFromEntry( double &total, int &minDepth, double *histogram, FsmAp *fsm, StateAp *state ); + static void breadthFromState( double &total, int &minDepth, double *histogram, FsmAp *fsm, StateAp *state, + long depth, int maxDepth, double stateScore); + + /* + * Operator workers + */ + void globOp( FsmAp **others, int numOthers ); + void deterministicEntry(); + + /* Determine if there are any entry points into a start state other than + * the start state. */ + bool isStartStateIsolated(); + + /* Make a new start state that has no entry points. Will not change the + * meaning of the fsm. */ + StateAp *dupStartState(); + + /* Workers for resolving epsilon transitions. */ + bool inEptVect( EptVect *eptVect, StateAp *targ ); + void epsilonFillEptVectFrom( StateAp *root, StateAp *from, bool parentLeaving ); + void resolveEpsilonTrans(); + + static bool fillAbort( FsmRes &res, FsmAp *fsm ); + + static FsmRes fillInStates( FsmAp *fsm ); + + /* Workers for concatenation and union. */ + static FsmRes doUnion( FsmAp *fsm, FsmAp *other ); + static FsmRes doConcat( FsmAp *fsm, FsmAp *other, StateSet *fromStates, bool optional ); + + static void condCost( Action *action, long repId ); + static void applyEntryPriorGuard( FsmAp *fsm, long repId ); + static void applyRepeatPriorGuard( FsmAp *fsm, long repId ); + + /* + * Final states + */ + + /* Unset any final states that are no longer to be final + * due to final bits. */ + void unsetIncompleteFinals(); + void unsetKilledFinals(); + + /* Bring in other's entry points. Assumes others states are going to be + * copied into this machine. */ + void copyInEntryPoints( FsmAp *other ); + + /* Ordering states. */ + void depthFirstOrdering( StateAp *state ); + void depthFirstOrdering(); + void sortStatesByFinal(); + + /* Set sqequential state numbers starting at 0. */ + void setStateNumbers( int base ); + + /* Unset all final states. */ + void unsetAllFinStates(); + + /* Set the bits of final states and clear the bits of non final states. */ + void setFinBits( int finStateBits ); + void unsetFinBits( int finStateBits ); + + /* + * Self-consistency checks. + */ + + /* Run a sanity check on the machine. */ + void verifyIntegrity(); + + /* Verify that there are no unreachable states, or dead end states. */ + void verifyReachability(); + void verifyNoDeadEndStates(); + + /* + * Path pruning + */ + + /* Mark all states reachable from state. */ + void markReachableFromHereReverse( StateAp *state ); + + /* Mark all states reachable from state. */ + void markReachableFromHere( StateAp *state ); + void markReachableFromHereStopFinal( StateAp *state ); + + /* Any transitions to another state? */ + bool anyRegularTransitions( StateAp *state ); + + /* Removes states that cannot be reached by any path in the fsm and are + * thus wasted silicon. */ + void removeDeadEndStates(); + + /* Removes states that cannot be reached by any path in the fsm and are + * thus wasted silicon. */ + long removeUnreachableStates(); + + /* Remove error actions from states on which the error transition will + * never be taken. */ + bool outListCovers( StateAp *state ); + bool anyErrorRange( StateAp *state ); + + /* Remove states that are on the misfit list. */ + void removeMisfits(); + + /* + * FSM Minimization + */ + + /* Minimization by partitioning. */ + void minimizePartition1(); + void minimizePartition2(); + + /* Minimize the final state Machine. The result is the minimal fsm. Slow + * but stable, correct minimization. Uses n^2 space (lookout) and average + * n^2 time. Worst case n^3 time, but a that is a very rare case. */ + void minimizeStable(); + + /* Minimize the final state machine. Does not find the minimal fsm, but a + * pretty good approximation. Does not use any extra space. Average n^2 + * time. Worst case n^3 time, but a that is a very rare case. */ + void minimizeApproximate(); + + /* This is the worker for the minimize approximate solution. It merges + * states that have identical out transitions. */ + bool minimizeRound( ); + + /* Given an intial partioning of states, split partitions that have out trans + * to differing partitions. */ + int partitionRound( StateAp **statePtrs, MinPartition *parts, int numParts ); + + /* Split partitions that have a transition to a previously split partition, until + * there are no more partitions to split. */ + int splitCandidates( StateAp **statePtrs, MinPartition *parts, int numParts ); + + /* Fuse together states in the same partition. */ + void fusePartitions( MinPartition *parts, int numParts ); + + /* Mark pairs where out final stateness differs, out trans data differs, + * trans pairs go to a marked pair or trans data differs. Should get + * alot of pairs. */ + void initialMarkRound( MarkIndex &markIndex ); + + /* One marking round on all state pairs. Considers if trans pairs go + * to a marked state only. Returns whether or not a pair was marked. */ + bool markRound( MarkIndex &markIndex ); + + /* Move the in trans into src into dest. */ + void moveInwardTrans(StateAp *dest, StateAp *src); + + /* Make state src and dest the same state. */ + void fuseEquivStates( StateAp *dest, StateAp *src ); + + /* Find any states that didn't get marked by the marking algorithm and + * merge them into the primary states of their equivalence class. */ + void fuseUnmarkedPairs( MarkIndex &markIndex ); + + /* Merge neighboring transitions go to the same state and have the same + * transitions data. */ + void compressTransitions(); + + /* Returns true if there is a transtion (either explicit or by a gap) to + * the error state. */ + bool checkErrTrans( StateAp *state, TransAp *trans ); + bool checkErrTrans( StateAp *state, CondAp *trans ); + bool checkErrTransFinish( StateAp *state ); + bool hasErrorTrans(); + + /* Check if a machine defines a single character. This is useful in + * validating ranges and machines to export. */ + bool checkSingleCharMachine( ); + + bool elimCondBits(); +}; + +/* Callback invoked when another trans (or possibly this) is added into this + * transition during the merging process. Draw in any properties of srcTrans + * into this transition. AddInTrans is called when a new transitions is made + * that will be a duplicate of another transition or a combination of several + * other transitions. AddInTrans will be called for each transition that the + * new transition is to represent. */ +template< class Trans > void FsmAp::addInTrans( Trans *destTrans, Trans *srcTrans ) +{ + /* Protect against adding in from ourselves. */ + if ( srcTrans == destTrans ) { + /* Adding in ourselves, need to make a copy of the source transitions. + * The priorities are not copied in as that would have no effect. */ + destTrans->lmActionTable.setActions( LmActionTable(srcTrans->lmActionTable) ); + destTrans->actionTable.setActions( ActionTable(srcTrans->actionTable) ); + } + else { + /* Not a copy of ourself, get the functions and priorities. */ + destTrans->lmActionTable.setActions( srcTrans->lmActionTable ); + destTrans->actionTable.setActions( srcTrans->actionTable ); + destTrans->priorTable.setPriors( srcTrans->priorTable ); + } +} + +/* Compares two transition pointers according to priority and functions. + * Either pointer may be null. Does not consider to state or from state. */ +template< class Trans > int FsmAp::compareCondDataPtr( Trans *trans1, Trans *trans2 ) +{ + if ( trans1 == 0 && trans2 != 0 ) + return -1; + else if ( trans1 != 0 && trans2 == 0 ) + return 1; + else if ( trans1 != 0 ) { + /* Both of the transition pointers are set. */ + int compareRes = compareCondData( trans1, trans2 ); + if ( compareRes != 0 ) + return compareRes; + } + return 0; +} + +/* Compares two transition pointers according to priority and functions. + * Either pointer may be null. Does not consider to state or from state. */ +template< class Trans > int FsmAp::compareCondBitElimPtr( Trans *trans1, Trans *trans2 ) +{ + if ( trans1 == 0 && trans2 != 0 ) + return -1; + else if ( trans1 != 0 && trans2 == 0 ) + return 1; + else if ( trans1 != 0 ) { + /* Both of the transition pointers are set. */ + int compareRes = compareCondBitElim( trans1, trans2 ); + if ( compareRes != 0 ) + return compareRes; + } + return 0; +} + +struct NameInst; + +/* Tree nodes. */ + +struct NfaUnion; +struct MachineDef; +struct FsmLongestMatch; +struct FsmLongestMatchPart; +struct FsmLmPartList; +struct Range; +struct LengthDef; +struct Action; +struct InlineList; + +/* Reference to a named state. */ +struct NameRef : public Vector {}; +typedef Vector NameRefList; +typedef Vector NameTargList; + +/* + * FsmLongestMatch + * + * Wherever possible the item match will execute on the character. If not + * possible the item match will execute on a lookahead character and either + * hold the current char (if one away) or backup. + * + * How to handle the problem of backing up over a buffer break? + * + * Don't want to use pending out transitions for embedding item match because + * the role of item match action is different: it may sometimes match on the + * final transition, or may match on a lookahead character. + * + * Don't want to invent a new operator just for this. So just trail action + * after machine, this means we can only use literal actions. + * + * The item action may + * + * What states of the machine will be final. The item actions that wrap around + * on the last character will go straight to the start state. + * + * Some transitions will be lookahead transitions, they will hold the current + * character. Crossing them with regular transitions must be restricted + * because it does not make sense. The transition cannot simultaneously hold + * and consume the current character. + */ +struct FsmLongestMatchPart +{ + FsmLongestMatchPart( Action *action, int longestMatchId ) + : + action(action), + longestMatchId(longestMatchId), + inLmSelect(false) + { } + + Action *action; + Action *setActId; + Action *actOnLast; + Action *actOnNext; + Action *actLagBehind; + Action *actNfaOnLast; + Action *actNfaOnNext; + Action *actNfaOnEof; + int longestMatchId; + bool inLmSelect; + FsmLongestMatch *longestMatch; + + FsmLongestMatchPart *prev, *next; +}; + +/* Declare a new type so that ptreetypes.h need not include dlist.h. */ +struct FsmLmPartList + : DList {}; + +struct FsmLongestMatch +{ + /* Construct with a list of joins */ + FsmLongestMatch( FsmLmPartList *longestMatchList ) + : + longestMatchList(longestMatchList), + lmSwitchHandlesError(false) + { + } + + FsmLmPartList *longestMatchList; + bool lmSwitchHandlesError; + + void restart( FsmAp *graph, TransAp *trans ); + void restart( FsmAp *graph, CondAp *cond ); +}; + +struct NameMapVal +{ + Vector vals; +}; + +/* Tree of instantiated names. */ +typedef AvlMapEl NameMapEl; +typedef AvlMap NameMap; +typedef Vector NameVect; + +/* Node in the tree of instantiated names. */ +struct NameInst +{ + NameInst( const InputLoc &loc, NameInst *parent, std::string name, int id, bool isLabel ) : + loc(loc), parent(parent), name(name), id(id), isLabel(isLabel), + isLongestMatch(false), numRefs(0), numUses(0), start(0), final(0) {} + + ~NameInst(); + + InputLoc loc; + + /* Keep parent pointers in the name tree to retrieve + * fully qulified names. */ + NameInst *parent; + + std::string name; + int id; + bool isLabel; + bool isLongestMatch; + + int numRefs; + int numUses; + + /* Names underneath us, excludes anonymous names. */ + NameMap children; + + /* All names underneath us in order of appearance. */ + NameVect childVect; + + /* Join scopes need an implicit "final" target. */ + NameInst *start, *final; + + /* During a fsm generation walk, lists the names that are referenced by + * epsilon operations in the current scope. After the link is made by the + * epsilon reference and the join operation is complete, the label can + * have its refcount decremented. Once there are no more references the + * entry point can be removed from the fsm returned. */ + NameVect referencedNames; + + /* Pointers for the name search queue. */ + NameInst *prev, *next; + + /* Check if this name inst or any name inst below is referenced. */ + bool anyRefsRec(); +}; + +typedef DList NameInstList; + +extern const int ORD_PUSH; +extern const int ORD_RESTORE; +extern const int ORD_COND; +extern const int ORD_COND2; +extern const int ORD_TEST; + +#endif diff --git a/src/libfsm/fsmmin.cc b/src/libfsm/fsmmin.cc new file mode 100644 index 00000000..cabe3968 --- /dev/null +++ b/src/libfsm/fsmmin.cc @@ -0,0 +1,934 @@ +/* + * Copyright 2002-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "fsmgraph.h" +#include "mergesort.h" + +struct MergeSortInitPartition + : public MergeSort +{ + MergeSortInitPartition( FsmCtx *ctx ) + { + InitPartitionCompare::ctx = ctx; + } +}; + +struct MergeSortPartition + : public MergeSort +{ + MergeSortPartition( FsmCtx *ctx ) + { + PartitionCompare::ctx = ctx; + } +}; + +struct MergeSortApprox + : public MergeSort +{ + MergeSortApprox( FsmCtx *ctx ) + { + ApproxCompare::ctx = ctx; + } +}; + +int FsmAp::partitionRound( StateAp **statePtrs, MinPartition *parts, int numParts ) +{ + /* Need a mergesort object and a single partition compare. */ + MergeSortPartition mergeSort( ctx ); + PartitionCompare partCompare; + + /* For each partition. */ + for ( int p = 0; p < numParts; p++ ) { + /* Fill the pointer array with the states in the partition. */ + StateList::Iter state = parts[p].list; + for ( int s = 0; state.lte(); state++, s++ ) + statePtrs[s] = state; + + /* Sort the states using the partitioning compare. */ + int numStates = parts[p].list.length(); + mergeSort.sort( statePtrs, numStates ); + + /* Assign the states into partitions based on the results of the sort. */ + int destPart = p, firstNewPart = numParts; + for ( int s = 1; s < numStates; s++ ) { + /* If this state differs from the last then move to the next partition. */ + if ( partCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { + /* The new partition is the next avail spot. */ + destPart = numParts; + numParts += 1; + } + + /* If the state is not staying in the first partition, then + * transfer it to its destination partition. */ + if ( destPart != p ) { + StateAp *state = parts[p].list.detach( statePtrs[s] ); + parts[destPart].list.append( state ); + } + } + + /* Fix the partition pointer for all the states that got moved to a new + * partition. This must be done after the states are transfered so the + * result of the sort is not altered. */ + for ( int newPart = firstNewPart; newPart < numParts; newPart++ ) { + StateList::Iter state = parts[newPart].list; + for ( ; state.lte(); state++ ) + state->alg.partition = &parts[newPart]; + } + } + + return numParts; +} + +/** + * \brief Minimize by partitioning version 1. + * + * Repeatedly tries to split partitions until all partitions are unsplittable. + * Produces the most minimal FSM possible. + */ +void FsmAp::minimizePartition1() +{ + /* Need one mergesort object and partition compares. */ + MergeSortInitPartition mergeSort( ctx ); + InitPartitionCompare initPartCompare( ctx ); + + /* Nothing to do if there are no states. */ + if ( stateList.length() == 0 ) + return; + + /* + * First thing is to partition the states by final state status and + * transition functions. This gives us an initial partitioning to work + * with. + */ + + /* Make a array of pointers to states. */ + int numStates = stateList.length(); + StateAp** statePtrs = new StateAp*[numStates]; + + /* Fill up an array of pointers to the states for easy sorting. */ + StateList::Iter state = stateList; + for ( int s = 0; state.lte(); state++, s++ ) + statePtrs[s] = state; + + /* Sort the states using the array of states. */ + mergeSort.sort( statePtrs, numStates ); + + /* An array of lists of states is used to partition the states. */ + MinPartition *parts = new MinPartition[numStates]; + + /* Assign the states into partitions. */ + int destPart = 0; + for ( int s = 0; s < numStates; s++ ) { + /* If this state differs from the last then move to the next partition. */ + if ( s > 0 && initPartCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { + /* Move to the next partition. */ + destPart += 1; + } + + /* Put the state into its partition. */ + statePtrs[s]->alg.partition = &parts[destPart]; + parts[destPart].list.append( statePtrs[s] ); + } + + /* We just moved all the states from the main list into partitions without + * taking them off the main list. So clean up the main list now. */ + stateList.abandon(); + + /* Split partitions. */ + int numParts = destPart + 1; + while ( true ) { + /* Test all partitions for splitting. */ + int newNum = partitionRound( statePtrs, parts, numParts ); + + /* When no partitions can be split, stop. */ + if ( newNum == numParts ) + break; + + numParts = newNum; + } + + /* Fuse states in the same partition. The states will end up back on the + * main list. */ + fusePartitions( parts, numParts ); + + /* Cleanup. */ + delete[] statePtrs; + delete[] parts; +} + +/* Split partitions that need splittting, decide which partitions might need + * to be split as a result, continue until there are no more that might need + * to be split. */ +int FsmAp::splitCandidates( StateAp **statePtrs, MinPartition *parts, int numParts ) +{ + /* Need a mergesort and a partition compare. */ + MergeSortPartition mergeSort( ctx ); + PartitionCompare partCompare( ctx ); + + /* The lists of unsplitable (partList) and splitable partitions. + * Only partitions in the splitable list are check for needing splitting. */ + PartitionList partList, splittable; + + /* Initially, all partitions are born from a split (the initial + * partitioning) and can cause other partitions to be split. So any + * partition with a state with a transition out to another partition is a + * candidate for splitting. This will make every partition except possibly + * partitions of final states split candidates. */ + for ( int p = 0; p < numParts; p++ ) { + /* Assume not active. */ + parts[p].active = false; + + /* Look for a trans out of any state in the partition. */ + for ( StateList::Iter state = parts[p].list; state.lte(); state++ ) { + /* If there is at least one transition out to another state then + * the partition becomes splittable. */ + if ( state->outList.length() > 0 ) { + parts[p].active = true; + break; + } + } + + /* If it was found active then it goes on the splittable list. */ + if ( parts[p].active ) + splittable.append( &parts[p] ); + else + partList.append( &parts[p] ); + } + + /* While there are partitions that are splittable, pull one off and try + * to split it. If it splits, determine which partitions may now be split + * as a result of the newly split partition. */ + while ( splittable.length() > 0 ) { + MinPartition *partition = splittable.detachFirst(); + + /* Fill the pointer array with the states in the partition. */ + StateList::Iter state = partition->list; + for ( int s = 0; state.lte(); state++, s++ ) + statePtrs[s] = state; + + /* Sort the states using the partitioning compare. */ + int numStates = partition->list.length(); + mergeSort.sort( statePtrs, numStates ); + + /* Assign the states into partitions based on the results of the sort. */ + MinPartition *destPart = partition; + int firstNewPart = numParts; + for ( int s = 1; s < numStates; s++ ) { + /* If this state differs from the last then move to the next partition. */ + if ( partCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { + /* The new partition is the next avail spot. */ + destPart = &parts[numParts]; + numParts += 1; + } + + /* If the state is not staying in the first partition, then + * transfer it to its destination partition. */ + if ( destPart != partition ) { + StateAp *state = partition->list.detach( statePtrs[s] ); + destPart->list.append( state ); + } + } + + /* Fix the partition pointer for all the states that got moved to a new + * partition. This must be done after the states are transfered so the + * result of the sort is not altered. */ + int newPart; + for ( newPart = firstNewPart; newPart < numParts; newPart++ ) { + StateList::Iter state = parts[newPart].list; + for ( ; state.lte(); state++ ) + state->alg.partition = &parts[newPart]; + } + + /* Put the partition we just split and any new partitions that came out + * of the split onto the inactive list. */ + partition->active = false; + partList.append( partition ); + for ( newPart = firstNewPart; newPart < numParts; newPart++ ) { + parts[newPart].active = false; + partList.append( &parts[newPart] ); + } + + if ( destPart == partition ) + continue; + + /* Now determine which partitions are splittable as a result of + * splitting partition by walking the in lists of the states in + * partitions that got split. Partition is the faked first item in the + * loop. */ + MinPartition *causalPart = partition; + newPart = firstNewPart - 1; + while ( newPart < numParts ) { + /* Loop all states in the causal partition. */ + StateList::Iter state = causalPart->list; + for ( ; state.lte(); state++ ) { + /* Walk all transition into the state and put the partition + * that the from state is in onto the splittable list. */ + for ( TransInList::Iter t = state->inTrans; t.lte(); t++ ) { + MinPartition *fromPart = t->fromState->alg.partition; + if ( ! fromPart->active ) { + fromPart->active = true; + partList.detach( fromPart ); + splittable.append( fromPart ); + } + } + for ( CondInList::Iter t = state->inCond; t.lte(); t++ ) { + MinPartition *fromPart = t->fromState->alg.partition; + if ( ! fromPart->active ) { + fromPart->active = true; + partList.detach( fromPart ); + splittable.append( fromPart ); + } + } + } + + newPart += 1; + causalPart = &parts[newPart]; + } + } + return numParts; +} + + +/** + * \brief Minimize by partitioning version 2 (best alg). + * + * Repeatedly tries to split partitions that may splittable until there are no + * more partitions that might possibly need splitting. Runs faster than + * version 1. Produces the most minimal fsm possible. + */ +void FsmAp::minimizePartition2() +{ + /* Need a mergesort and an initial partition compare. */ + MergeSortInitPartition mergeSort( ctx ); + InitPartitionCompare initPartCompare( ctx ); + + /* Nothing to do if there are no states. */ + if ( stateList.length() == 0 ) + return; + + /* + * First thing is to partition the states by final state status and + * transition functions. This gives us an initial partitioning to work + * with. + */ + + /* Make a array of pointers to states. */ + int numStates = stateList.length(); + StateAp** statePtrs = new StateAp*[numStates]; + + /* Fill up an array of pointers to the states for easy sorting. */ + StateList::Iter state = stateList; + for ( int s = 0; state.lte(); state++, s++ ) + statePtrs[s] = state; + + /* Sort the states using the array of states. */ + mergeSort.sort( statePtrs, numStates ); + + /* An array of lists of states is used to partition the states. */ + MinPartition *parts = new MinPartition[numStates]; + + /* Assign the states into partitions. */ + int destPart = 0; + for ( int s = 0; s < numStates; s++ ) { + /* If this state differs from the last then move to the next partition. */ + if ( s > 0 && initPartCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) { + /* Move to the next partition. */ + destPart += 1; + } + + /* Put the state into its partition. */ + statePtrs[s]->alg.partition = &parts[destPart]; + parts[destPart].list.append( statePtrs[s] ); + } + + /* We just moved all the states from the main list into partitions without + * taking them off the main list. So clean up the main list now. */ + stateList.abandon(); + + /* Split partitions. */ + int numParts = splitCandidates( statePtrs, parts, destPart+1 ); + + /* Fuse states in the same partition. The states will end up back on the + * main list. */ + fusePartitions( parts, numParts ); + + /* Cleanup. */ + delete[] statePtrs; + delete[] parts; +} + +void FsmAp::initialMarkRound( MarkIndex &markIndex ) +{ + /* P and q for walking pairs. */ + StateAp *p = stateList.head, *q; + + /* Need an initial partition compare. */ + InitPartitionCompare initPartCompare( ctx ); + + /* Walk all unordered pairs of (p, q) where p != q. + * The second depth of the walk stops before reaching p. This + * gives us all unordered pairs of states (p, q) where p != q. */ + while ( p != 0 ) { + q = stateList.head; + while ( q != p ) { + /* If the states differ on final state status, out transitions or + * any transition data then they should be separated on the initial + * round. */ + if ( initPartCompare.compare( p, q ) != 0 ) + markIndex.markPair( p->alg.stateNum, q->alg.stateNum ); + + q = q->next; + } + p = p->next; + } +} + +#ifdef TO_UPGRADE_CONDS +bool FsmAp::markRound( MarkIndex &markIndex ) +{ + /* P an q for walking pairs. Take note if any pair gets marked. */ + StateAp *p = stateList.head, *q; + bool pairWasMarked = false; + + /* Need a mark comparison. */ + MarkCompare markCompare( ctx ); + + /* Walk all unordered pairs of (p, q) where p != q. + * The second depth of the walk stops before reaching p. This + * gives us all unordered pairs of states (p, q) where p != q. */ + while ( p != 0 ) { + q = stateList.head; + while ( q != p ) { + /* Should we mark the pair? */ + if ( !markIndex.isPairMarked( p->alg.stateNum, q->alg.stateNum ) ) { + if ( markCompare.shouldMark( markIndex, p, q ) ) { + markIndex.markPair( p->alg.stateNum, q->alg.stateNum ); + pairWasMarked = true; + } + } + q = q->next; + } + p = p->next; + } + + return pairWasMarked; +} +#endif + +#ifdef TO_UPGRADE_CONDS +/** + * \brief Minimize by pair marking. + * + * Decides if each pair of states is distinct or not. Uses O(n^2) memory and + * should only be used on small graphs. Produces the most minmimal FSM + * possible. + */ +void FsmAp::minimizeStable() +{ + /* Set the state numbers. */ + setStateNumbers( 0 ); + + /* This keeps track of which pairs have been marked. */ + MarkIndex markIndex( stateList.length() ); + + /* Mark pairs where final stateness, out trans, or trans data differ. */ + initialMarkRound( markIndex ); + + /* While the last round of marking succeeded in marking a state + * continue to do another round. */ + int modified = markRound( markIndex ); + while (modified) + modified = markRound( markIndex ); + + /* Merge pairs that are unmarked. */ + fuseUnmarkedPairs( markIndex ); +} +#endif + +#ifdef TO_UPGRADE_CONDS +bool FsmAp::minimizeRound() +{ + /* Nothing to do if there are no states. */ + if ( stateList.length() == 0 ) + return false; + + /* Need a mergesort on approx compare and an approx compare. */ + MergeSortApprox mergeSort( ctx ); + ApproxCompare approxCompare( ctx ); + + /* Fill up an array of pointers to the states. */ + StateAp **statePtrs = new StateAp*[stateList.length()]; + StateList::Iter state = stateList; + for ( int s = 0; state.lte(); state++, s++ ) + statePtrs[s] = state; + + bool modified = false; + + /* Sort The list. */ + mergeSort.sort( statePtrs, stateList.length() ); + + /* Walk the list looking for duplicates next to each other, + * merge in any duplicates. */ + StateAp **pLast = statePtrs; + StateAp **pState = statePtrs + 1; + for ( int i = 1; i < stateList.length(); i++, pState++ ) { + if ( approxCompare.compare( *pLast, *pState ) == 0 ) { + /* Last and pState are the same, so fuse together. Move forward + * with pState but not with pLast. If any more are identical, we + * must */ + fuseEquivStates( *pLast, *pState ); + modified = true; + } + else { + /* Last and this are different, do not set to merge them. Move + * pLast to the current (it may be way behind from merging many + * states) and pState forward one to consider the next pair. */ + pLast = pState; + } + } + delete[] statePtrs; + return modified; +} +#endif + +#ifdef TO_UPGRADE_CONDS +/** + * \brief Minmimize by an approximation. + * + * Repeatedly tries to find states with transitions out to the same set of + * states on the same set of keys until no more identical states can be found. + * Does not produce the most minimial FSM possible. + */ +void FsmAp::minimizeApproximate() +{ + /* While the last minimization round succeeded in compacting states, + * continue to try to compact states. */ + while ( true ) { + bool modified = minimizeRound(); + if ( ! modified ) + break; + } +} +#endif + + +/* Remove states that have no path to them from the start state. Recursively + * traverses the graph marking states that have paths into them. Then removes + * all states that did not get marked. */ +long FsmAp::removeUnreachableStates() +{ + long origLen = stateList.length(); + + /* Misfit accounting should be off and there should be no states on the + * misfit list. */ + assert( !misfitAccounting && misfitList.length() == 0 ); + + /* Mark all the states that can be reached + * through the existing set of entry points. */ + markReachableFromHere( startState ); + for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) + markReachableFromHere( en->value ); + + /* Delete all states that are not marked + * and unmark the ones that are marked. */ + StateAp *state = stateList.head; + while ( state ) { + StateAp *next = state->next; + + if ( state->stateBits & STB_ISMARKED ) + state->stateBits &= ~ STB_ISMARKED; + else { + detachState( state ); + stateList.detach( state ); + delete state; + } + + state = next; + } + + return origLen - stateList.length(); +} + +bool FsmAp::outListCovers( StateAp *state ) +{ + /* Must be at least one range to cover. */ + if ( state->outList.length() == 0 ) + return false; + + /* The first must start at the lower bound. */ + TransList::Iter trans = state->outList.first(); + if ( ctx->keyOps->lt( ctx->keyOps->minKey, trans->lowKey ) ) + return false; + + /* Loop starts at second el. */ + trans.increment(); + + /* Loop checks lower against prev upper. */ + for ( ; trans.lte(); trans++ ) { + /* Lower end of the trans must be one greater than the + * previous' high end. */ + Key lowKey = trans->lowKey; + ctx->keyOps->decrement( lowKey ); + if ( ctx->keyOps->lt( trans->prev->highKey, lowKey ) ) + return false; + } + + /* Require that the last range extends to the upper bound. */ + trans = state->outList.last(); + if ( ctx->keyOps->lt( trans->highKey, ctx->keyOps->maxKey ) ) + return false; + + return true; +} + +/* Remove states that that do not lead to a final states. Works recursivly traversing + * the graph in reverse (starting from all final states) and marking seen states. Then + * removes states that did not get marked. */ +void FsmAp::removeDeadEndStates() +{ + /* Misfit accounting should be off and there should be no states on the + * misfit list. */ + assert( !misfitAccounting && misfitList.length() == 0 ); + + /* Mark all states that have paths to the final states. */ + StateAp **st = finStateSet.data; + int nst = finStateSet.length(); + for ( int i = 0; i < nst; i++, st++ ) + markReachableFromHereReverse( *st ); + + /* Start state gets honorary marking. If the machine accepts nothing we + * still want the start state to hang around. This must be done after the + * recursive call on all the final states so that it does not cause the + * start state in transitions to be skipped when the start state is + * visited by the traversal. */ + startState->stateBits |= STB_ISMARKED; + + /* Delete all states that are not marked + * and unmark the ones that are marked. */ + StateAp *state = stateList.head; + while ( state != 0 ) { + StateAp *next = state->next; + + if ( state->stateBits & STB_ISMARKED ) + state->stateBits &= ~ STB_ISMARKED; + else { + detachState( state ); + stateList.detach( state ); + delete state; + } + + state = next; + } +} + +/* Remove states on the misfit list. To work properly misfit accounting should + * be on when this is called. The detaching of a state will likely cause + * another misfit to be collected and it can then be removed. */ +void FsmAp::removeMisfits() +{ + while ( misfitList.length() > 0 ) { + /* Get the first state. */ + StateAp *state = misfitList.head; + + /* Detach and delete. */ + detachState( state ); + + /* The state was previously on the misfit list and detaching can only + * remove in transitions so the state must still be on the misfit + * list. */ + misfitList.detach( state ); + delete state; + } +} + +/* Fuse src into dest because they have been deemed equivalent states. + * Involves moving transitions into src to go into dest and invoking + * callbacks. Src is deleted detached from the graph and deleted. */ +void FsmAp::fuseEquivStates( StateAp *dest, StateAp *src ) +{ + /* This would get ugly. */ + assert( dest != src ); + + /* Cur is a duplicate. We can merge it with trail. */ + moveInwardTrans( dest, src ); + + detachState( src ); + stateList.detach( src ); + delete src; +} + +void FsmAp::fuseUnmarkedPairs( MarkIndex &markIndex ) +{ + StateAp *p = stateList.head, *nextP, *q; + + /* Definition: The primary state of an equivalence class is the first state + * encounterd that belongs to the equivalence class. All equivalence + * classes have primary state including equivalence classes with one state + * in it. */ + + /* For each unmarked pair merge p into q and delete p. q is always the + * primary state of it's equivalence class. We wouldn't have landed on it + * here if it were not, because it would have been deleted. + * + * Proof that q is the primaray state of it's equivalence class: Assume q + * is not the primary state of it's equivalence class, then it would be + * merged into some state that came before it and thus p would be + * equivalent to that state. But q is the first state that p is equivalent + * to so we have a contradiction. */ + + /* Walk all unordered pairs of (p, q) where p != q. + * The second depth of the walk stops before reaching p. This + * gives us all unordered pairs of states (p, q) where p != q. */ + while ( p != 0 ) { + nextP = p->next; + + q = stateList.head; + while ( q != p ) { + /* If one of p or q is a final state then mark. */ + if ( ! markIndex.isPairMarked( p->alg.stateNum, q->alg.stateNum ) ) { + fuseEquivStates( q, p ); + break; + } + q = q->next; + } + p = nextP; + } +} + +void FsmAp::fusePartitions( MinPartition *parts, int numParts ) +{ + /* For each partition, fuse state 2, 3, ... into state 1. */ + for ( int p = 0; p < numParts; p++ ) { + /* Assume that there will always be at least one state. */ + StateAp *first = parts[p].list.head, *toFuse = first->next; + + /* Put the first state back onto the main state list. Don't bother + * removing it from the partition list first. */ + stateList.append( first ); + + /* Fuse the rest of the state into the first. */ + while ( toFuse != 0 ) { + /* Save the next. We will trash it before it is needed. */ + StateAp *next = toFuse->next; + + /* Put the state to be fused in to the first back onto the main + * list before it is fuse. the graph. The state needs to be on + * the main list for the detach from the graph to work. Don't + * bother removing the state from the partition list first. We + * need not maintain it. */ + stateList.append( toFuse ); + + /* Now fuse to the first. */ + fuseEquivStates( first, toFuse ); + + /* Go to the next that we saved before trashing the next pointer. */ + toFuse = next; + } + + /* We transfered the states from the partition list into the main list without + * removing the states from the partition list first. Clean it up. */ + parts[p].list.abandon(); + } +} + +/* Merge neighboring transitions that go to the same state and have the same + * transitions data. */ +void FsmAp::compressTransitions() +{ + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->outList.length() > 1 ) { + for ( TransList::Iter trans = st->outList, next = trans.next(); next.lte(); ) { + Key nextLow = next->lowKey; + ctx->keyOps->decrement( nextLow ); + + /* Require there be no conditions in either of the merge + * candidates. */ + bool merge = false; + TransDataAp *td; + TransDataAp *tn; + + if ( trans->plain() && + next->plain() && + ctx->keyOps->eq( trans->highKey, nextLow ) ) + { + td = trans->tdap(); + tn = next->tdap(); + + /* Check the condition target and action data. */ + if ( td->toState == tn->toState && CmpActionTable::compare( + td->actionTable, tn->actionTable ) == 0 ) + { + merge = true; + } + } + + if ( merge ) { + trans->highKey = next->highKey; + st->outList.detach( tn ); + detachTrans( tn->fromState, tn->toState, tn ); + delete tn; + next = trans.next(); + } + else { + trans.increment(); + next.increment(); + } + } + } + } +} + +bool FsmAp::elimCondBits() +{ + bool modified = false; + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + restart: + for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { + if ( !trans->plain() ) { + CondSpace *cs = trans->condSpace; + + for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ ) { + long bit = 1 << csi.pos(); + + /* Sort into on and off lists. */ + CondList on; + CondList off; + TransCondAp *tcap = trans->tcap(); + while ( tcap->condList.length() > 0 ) { + CondAp *cond = tcap->condList.detachFirst(); + if ( cond->key.getVal() & bit ) { + cond->key = CondKey( cond->key.getVal() & ~bit ); + on.append( cond ); + } + else { + off.append( cond ); + } + } + + bool merge = false; + if ( on.length() > 0 && on.length() == off.length() ) { + /* test if the same */ + int cmpRes = compareCondListBitElim( on, off ); + if ( cmpRes == 0 ) + merge = true; + } + + if ( merge ) { + if ( cs->condSet.length() == 1 ) { + /* clear out the on-list. */ + while ( on.length() > 0 ) { + CondAp *cond = on.detachFirst(); + detachTrans( st, cond->toState, cond ); + } + + /* turn back into a plain transition. */ + CondAp *cond = off.detachFirst(); + TransAp *n = convertToTransAp( st, cond ); + TransAp *before = trans->prev; + st->outList.detach( trans ); + st->outList.addAfter( before, n ); + modified = true; + goto restart; + } + else + { + CondSet newSet = cs->condSet; + newSet.Vector::remove( csi.pos(), 1 ); + trans->condSpace = addCondSpace( newSet ); + + /* clear out the on-list. */ + while ( on.length() > 0 ) { + CondAp *cond = on.detachFirst(); + detachTrans( st, cond->toState, cond ); + } + } + } + + /* Turn back into a single list. */ + while ( on.length() > 0 || off.length() > 0 ) { + if ( on.length() == 0 ) { + while ( off.length() > 0 ) + tcap->condList.append( off.detachFirst() ); + } + else if ( off.length() == 0 ) { + while ( on.length() > 0 ) { + CondAp *cond = on.detachFirst(); + cond->key = CondKey( cond->key.getVal() | bit ); + tcap->condList.append( cond ); + } + } + else { + if ( off.head->key.getVal() < ( on.head->key.getVal() | bit ) ) { + tcap->condList.append( off.detachFirst() ); + } + else { + CondAp *cond = on.detachFirst(); + cond->key = CondKey( cond->key.getVal() | bit ); + tcap->condList.append( cond ); + } + } + } + + if ( merge ) { + modified = true; + goto restart; + } + } + } + } + } + return modified; +} + +/* Perform minimization after an operation according + * to the command line args. */ +void FsmAp::afterOpMinimize( bool lastInSeq ) +{ + /* Switch on the prefered minimization algorithm. */ + if ( ctx->minimizeOpt == MinimizeEveryOp || ( ctx->minimizeOpt == MinimizeMostOps && lastInSeq ) ) { + /* First clean up the graph. FsmAp operations may leave these + * lying around. There should be no dead end states. The subtract + * intersection operators are the only places where they may be + * created and those operators clean them up. */ + removeUnreachableStates(); + + switch ( ctx->minimizeLevel ) { + #ifdef TO_UPGRADE_CONDS + case MinimizeApprox: + minimizeApproximate(); + break; + #endif + case MinimizePartition1: + minimizePartition1(); + break; + case MinimizePartition2: + minimizePartition2(); + break; + #ifdef TO_UPGRADE_CONDS + case MinimizeStable: + minimizeStable(); + break; + #endif + } + } +} + diff --git a/src/libfsm/fsmnfa.cc b/src/libfsm/fsmnfa.cc new file mode 100644 index 00000000..cde4f82d --- /dev/null +++ b/src/libfsm/fsmnfa.cc @@ -0,0 +1,660 @@ +/* + * Copyright 2015-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "fsmgraph.h" +#include "mergesort.h" +#include "parsedata.h" + +using std::endl; + +void FsmAp::nfaFillInStates() +{ + long count = nfaList.length(); + + /* Can this lead to too many DFAs? Since the nfa merge is removing misfits, + * it is possible we remove a state that is on the nfa list, but we don't + * adjust count. */ + + /* Merge any states that are awaiting merging. This will likey cause + * other states to be added to the stfil list. */ + while ( nfaList.length() > 0 && count-- ) { + StateAp *state = nfaList.head; + + StateSet *stateSet = &state->stateDictEl->stateSet; + nfaMergeStates( state, stateSet->data, stateSet->length() ); + + for ( StateSet::Iter s = *stateSet; s.lte(); s++ ) + detachStateDict( state, *s ); + + nfaList.detach( state ); + } +} + +void FsmAp::prepareNfaRound() +{ + for ( StateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->nfaOut != 0 && ! (st->stateBits & STB_NFA_REP) ) { + StateSet set; + for ( NfaTransList::Iter to = *st->nfaOut; to.lte(); to++ ) + set.insert( to->toState ); + + st->stateDictEl = new StateDictEl( set ); + st->stateDictEl->targState = st; + stateDict.insert( st->stateDictEl ); + delete st->nfaOut; + st->nfaOut = 0; + + nfaList.append( st ); + } + } +} + +void FsmAp::finalizeNfaRound() +{ + /* For any remaining NFA states, remove from the state dict. We need to + * keep the state sets. */ + for ( NfaStateList::Iter ns = nfaList; ns.lte(); ns++ ) + stateDict.detach( ns->stateDictEl ); + + /* Disassociate non-nfa states from their state dicts. */ + for ( StateDict::Iter sdi = stateDict; sdi.lte(); sdi++ ) + sdi->targState->stateDictEl = 0; + + /* Delete the state dict elements for non-nfa states. */ + stateDict.empty(); + + /* Transfer remaining stateDictEl sets to nfaOut. */ + while ( nfaList.length() > 0 ) { + StateAp *state = nfaList.head; + state->nfaOut = new NfaTransList; + for ( StateSet::Iter ss = state->stateDictEl->stateSet; ss.lte(); ss++ ) { + /* Attach it using the NFA transitions data structure (propigates + * to output). */ + NfaTrans *trans = new NfaTrans( /* 0, 0, */ 1 ); + state->nfaOut->append( trans ); + attachToNfa( state, *ss, trans ); + + detachStateDict( state, *ss ); + } + delete state->stateDictEl; + state->stateDictEl = 0; + nfaList.detach( state ); + } +} + +void FsmAp::nfaMergeStates( StateAp *destState, + StateAp **srcStates, int numSrc ) +{ + for ( int s = 0; s < numSrc; s++ ) { + mergeStates( destState, srcStates[s] ); + + while ( misfitList.length() > 0 ) { + StateAp *state = misfitList.head; + + /* Detach and delete. */ + detachState( state ); + misfitList.detach( state ); + delete state; + } + } +} + + +/* + * WRT action ordering. + * + * All the pop restore actions get an ordering of -2 to cause them to always + * execute first. This is the action that restores the state and we need that + * to happen before any user actions. + */ +const int ORD_PUSH = 0; +const int ORD_RESTORE = -3; +const int ORD_COND = -1; +const int ORD_COND2 = -2; +const int ORD_TEST = 1073741824; + +void FsmAp::transferOutToNfaTrans( NfaTrans *trans, StateAp *state ) +{ + trans->popFrom = state->fromStateActionTable; + trans->popCondSpace = state->outCondSpace; + trans->popCondKeys = state->outCondKeys; + trans->priorTable.setPriors( state->outPriorTable ); + trans->popAction.setActions( state->outActionTable ); +} + +FsmRes FsmAp::nfaWrap( FsmAp *fsm, Action *push, Action *pop, Action *init, + Action *stay, Action *exit, NfaRepeatMode mode ) +{ + /* + * First Concat. + */ + StateSet origFinals = fsm->finStateSet; + + /* Get the orig start state. */ + StateAp *origStartState = fsm->startState; + + /* New start state. */ + StateAp *newStart = fsm->addState(); + + newStart->nfaOut = new NfaTransList; + + const int orderInit = 0; + const int orderStay = mode == NfaGreedy ? 3 : 1; + const int orderExit = mode == NfaGreedy ? 1 : 3; + + NfaTrans *trans; + if ( init ) { + /* Transition into the repetition. Doesn't make much sense to flip this + * statically false, but provided for consistency of interface. Allows + * an init so we can have only local state manipulation. */ + trans = new NfaTrans( orderInit ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, init ); + + newStart->nfaOut->append( trans ); + fsm->attachToNfa( newStart, origStartState, trans ); + } + + StateAp *newFinal = fsm->addState(); + + for ( StateSet::Iter orig = origFinals; orig.lte(); orig++ ) { + /* For every final state, we place a new final state in front of it, + * with an NFA transition to the original. This is the "stay" choice. */ + StateAp *repl = fsm->addState(); + fsm->moveInwardTrans( repl, *orig ); + + repl->nfaOut = new NfaTransList; + + if ( stay != 0 ) { + /* Transition to original final state. Represents staying. */ + trans = new NfaTrans( orderStay ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, stay ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, *orig, trans ); + } + + if ( exit != 0 ) { + /* Transition to thew new final. Represents exiting. */ + trans = new NfaTrans( orderExit ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, exit ); + + fsm->transferOutToNfaTrans( trans, *orig ); + repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, newFinal, trans ); + } + + fsm->unsetFinState( *orig ); + } + + fsm->unsetStartState(); + fsm->setStartState( newStart ); + fsm->setFinState( newFinal ); + + return FsmRes( FsmRes::Fsm(), fsm ); +} + + +FsmRes FsmAp::nfaRepeatOp2( FsmAp *fsm, Action *push, Action *pop, Action *init, + Action *stay, Action *repeat, Action *exit, NfaRepeatMode mode ) +{ + /* + * First Concat. + */ + StateSet origFinals = fsm->finStateSet; + + /* Get the orig start state. */ + StateAp *origStartState = fsm->startState; + StateAp *repStartState = fsm->dupStartState(); + + /* New start state. */ + StateAp *newStart1 = fsm->addState(); + StateAp *newStart2 = fsm->addState(); + + newStart1->nfaOut = new NfaTransList; + newStart2->nfaOut = new NfaTransList; + + const int orderInit = 0; + const int orderStay = mode == NfaGreedy ? 3 : 1; + const int orderRepeat = mode == NfaGreedy ? 2 : 2; + const int orderExit = mode == NfaGreedy ? 1 : 3; + + NfaTrans *trans; + if ( init ) { + /* Transition into the repetition. Doesn't make much sense to flip this + * statically false, but provided for consistency of interface. Allows + * an init so we can have only local state manipulation. */ + trans = new NfaTrans( orderInit ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, init ); + + newStart1->nfaOut->append( trans ); + fsm->attachToNfa( newStart1, newStart2, trans ); + } + + StateAp *newFinal = fsm->addState(); + + if ( exit ) { + trans = new NfaTrans( orderExit ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, exit ); + + newStart2->nfaOut->append( trans ); + fsm->attachToNfa( newStart1, newFinal, trans ); + } + + if ( repeat ) { + trans = new NfaTrans( orderRepeat ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, repeat ); + + newStart2->nfaOut->append( trans ); + fsm->attachToNfa( newStart1, origStartState, trans ); + } + + for ( StateSet::Iter orig = origFinals; orig.lte(); orig++ ) { + /* For every final state, we place a new final state in front of it, + * with an NFA transition to the original. This is the "stay" choice. */ + StateAp *repl = fsm->addState(); + fsm->moveInwardTrans( repl, *orig ); + + repl->nfaOut = new NfaTransList; + + if ( stay != 0 ) { + /* Transition to original final state. Represents staying. */ + trans = new NfaTrans( orderStay ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, stay ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, *orig, trans ); + } + + /* Transition back to the start. Represents repeat. */ + if ( repeat != 0 ) { + trans = new NfaTrans( orderRepeat ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, repeat ); + + fsm->transferOutToNfaTrans( trans, *orig ); + repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, repStartState, trans ); + } + + if ( exit != 0 ) { + /* Transition to thew new final. Represents exiting. */ + trans = new NfaTrans( orderExit ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, exit ); + + fsm->transferOutToNfaTrans( trans, *orig ); + repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, newFinal, trans ); + } + + fsm->unsetFinState( *orig ); + } + + fsm->unsetStartState(); + fsm->setStartState( newStart1 ); + fsm->setFinState( newFinal ); + + return FsmRes( FsmRes::Fsm(), fsm ); +} + + +/* This version contains the init, increment and test in the nfa pop actions. + * This is a compositional operator since it doesn't leave any actions to + * trailing characters, where they may interact with other actions that use the + * same variables. */ +FsmRes FsmAp::nfaRepeatOp( FsmAp *fsm, Action *push, Action *pop, Action *init, + Action *stay, Action *repeat, Action *exit ) +{ + /* + * First Concat. + */ + StateSet origFinals = fsm->finStateSet; + + /* Get the orig start state. */ + StateAp *origStartState = fsm->startState; + StateAp *repStartState = fsm->dupStartState(); + + /* New start state. */ + StateAp *newStart = fsm->addState(); + + newStart->nfaOut = new NfaTransList; + + NfaTrans *trans; + if ( init ) { + /* Transition into the repetition. Doesn't make much sense to flip this + * statically false, but provided for consistency of interface. Allows + * an init so we can have only local state manipulation. */ + trans = new NfaTrans( 1 ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, init ); + + newStart->nfaOut->append( trans ); + fsm->attachToNfa( newStart, origStartState, trans ); + } + + StateAp *newFinal = fsm->addState(); + + for ( StateSet::Iter orig = origFinals; orig.lte(); orig++ ) { + /* For every final state, we place a new final state in front of it, + * with an NFA transition to the original. This is the "stay" choice. */ + StateAp *repl = fsm->addState(); + fsm->moveInwardTrans( repl, *orig ); + + repl->nfaOut = new NfaTransList; + + const int orderStay = 3; + const int orderRepeat = 2; + const int orderExit = 1; + + if ( stay != 0 ) { + /* Transition to original final state. Represents staying. */ + trans = new NfaTrans( orderStay ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, stay ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, *orig, trans ); + } + + /* Transition back to the start. Represents repeat. */ + if ( repeat != 0 ) { + trans = new NfaTrans( orderRepeat ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, repeat ); + + fsm->transferOutToNfaTrans( trans, *orig ); + repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, repStartState, trans ); + } + + if ( exit != 0 ) { + /* Transition to thew new final. Represents exiting. */ + trans = new NfaTrans( orderExit ); + + trans->pushTable.setAction( ORD_PUSH, push ); + trans->restoreTable.setAction( ORD_RESTORE, pop ); + trans->popTest.setAction( ORD_TEST, exit ); + + fsm->transferOutToNfaTrans( trans, *orig ); + repl->fromStateActionTable.setActions( (*orig)->fromStateActionTable ); + + repl->nfaOut->append( trans ); + fsm->attachToNfa( repl, newFinal, trans ); + } + + fsm->unsetFinState( *orig ); + } + + fsm->unsetStartState(); + fsm->setStartState( newStart ); + fsm->setFinState( newFinal ); + + return FsmRes( FsmRes::Fsm(), fsm ); +} + + +/* Unions others with fsm. Others are deleted. */ +FsmRes FsmAp::nfaUnionOp( FsmAp *fsm, FsmAp **others, int n, int depth, ostream &stats ) +{ + /* Mark existing NFA states as NFA_REP states, which excludes them from the + * prepare NFA round. We must treat them as final NFA states and not try to + * make them deterministic. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + if ( st->nfaOut != 0 ) + st->stateBits |= STB_NFA_REP; + } + + for ( int o = 0; o < n; o++ ) { + for ( StateList::Iter st = others[o]->stateList; st.lte(); st++ ) { + if ( st->nfaOut != 0 ) + st->stateBits |= STB_NFA_REP; + } + } + + for ( int o = 0; o < n; o++ ) + assert( fsm->ctx == others[o]->ctx ); + + /* Not doing misfit accounting here. If we wanted to, it would need to be + * made nfa-compatibile. */ + + /* Build a state set consisting of both start states */ + StateSet startStateSet; + startStateSet.insert( fsm->startState ); + for ( int o = 0; o < n; o++ ) + startStateSet.insert( others[o]->startState ); + + /* Both of the original start states loose their start state status. */ + fsm->unsetStartState(); + for ( int o = 0; o < n; o++ ) + others[o]->unsetStartState(); + + /* Bring in the rest of other's entry points. */ + for ( int o = 0; o < n; o++ ) { + fsm->copyInEntryPoints( others[o] ); + others[o]->entryPoints.empty(); + } + + for ( int o = 0; o < n; o++ ) { + /* Merge the lists. This will move all the states from other + * into this. No states will be deleted. */ + fsm->stateList.append( others[o]->stateList ); + fsm->misfitList.append( others[o]->misfitList ); + // nfaList.append( others[o]->nfaList ); + } + + for ( int o = 0; o < n; o++ ) { + /* Move the final set data from other into this. */ + fsm->finStateSet.insert( others[o]->finStateSet ); + others[o]->finStateSet.empty(); + } + + for ( int o = 0; o < n; o++ ) { + /* Since other's list is empty, we can delete the fsm without + * affecting any states. */ + delete others[o]; + } + + /* Create a new start state. */ + fsm->setStartState( fsm->addState() ); + + if ( depth == 0 ) { + fsm->startState->stateDictEl = new StateDictEl( startStateSet ); + fsm->nfaList.append( fsm->startState ); + + for ( StateSet::Iter s = startStateSet; s.lte(); s++ ) { + NfaTrans *trans = new NfaTrans( /* 0, 0, */ 0 ); + + if ( fsm->startState->nfaOut == 0 ) + fsm->startState->nfaOut = new NfaTransList; + + fsm->startState->nfaOut->append( trans ); + fsm->attachToNfa( fsm->startState, *s, trans ); + } + } + else { + /* Merge the start states. */ + if ( fsm->ctx->printStatistics ) + stats << "nfa-fill-round\t0" << endl; + + fsm->nfaMergeStates( fsm->startState, startStateSet.data, startStateSet.length() ); + + long removed = fsm->removeUnreachableStates(); + if ( fsm->ctx->printStatistics ) + stats << "round-unreach\t" << removed << endl; + + /* Fill in any new states made from merging. */ + for ( long i = 1; i < depth; i++ ) { + if ( fsm->ctx->printStatistics ) + stats << "nfa-fill-round\t" << i << endl; + + if ( fsm->nfaList.length() == 0 ) + break; + + fsm->nfaFillInStates( ); + + long removed = fsm->removeUnreachableStates(); + if ( fsm->ctx->printStatistics ) + stats << "round-unreach\t" << removed << endl; + } + + fsm->finalizeNfaRound(); + + long maxStateSetSize = 0; + long count = 0; + for ( StateList::Iter s = fsm->stateList; s.lte(); s++ ) { + if ( s->nfaOut != 0 && s->nfaOut->length() > 0 ) { + count += 1; + if ( s->nfaOut->length() > maxStateSetSize ) + maxStateSetSize = s->nfaOut->length(); + } + } + + if ( fsm->ctx->printStatistics ) { + stats << "fill-list\t" << count << endl; + stats << "state-dict\t" << fsm->stateDict.length() << endl; + stats << "states\t" << fsm->stateList.length() << endl; + stats << "max-ss\t" << maxStateSetSize << endl; + } + + fsm->removeUnreachableStates(); + + if ( fsm->ctx->printStatistics ) + stats << "post-unreachable\t" << fsm->stateList.length() << endl; + + fsm->minimizePartition2(); + + if ( fsm->ctx->printStatistics ) { + stats << "post-min\t" << fsm->stateList.length() << std::endl; + stats << std::endl; + } + } + + return FsmRes( FsmRes::Fsm(), fsm ); +} + +FsmRes FsmAp::nfaUnion( const NfaRoundVect &roundsList, + FsmAp **machines, int numMachines, + std::ostream &stats, bool printStatistics ) +{ + long sumPlain = 0, sumMin = 0; + for ( int i = 0; i < numMachines; i++ ) { + sumPlain += machines[i]->stateList.length(); + + machines[i]->removeUnreachableStates(); + machines[i]->minimizePartition2(); + + sumMin += machines[i]->stateList.length(); + } + + if ( printStatistics ) { + stats << "sum-plain\t" << sumPlain << endl; + stats << "sum-minimized\t" << sumMin << endl; + } + + /* For each round. */ + for ( NfaRoundVect::Iter r = roundsList; r.lte(); r++ ) { + + if ( printStatistics ) { + stats << "depth\t" << r->depth << endl; + stats << "grouping\t" << r->groups << endl; + } + + int numGroups = 0; + int start = 0; + while ( start < numMachines ) { + /* If nfa-group-max is zero, don't group, put all terms into a single + * n-depth NFA. */ + int amount = r->groups == 0 ? numMachines : r->groups; + if ( ( start + amount ) > numMachines ) + amount = numMachines - start; + + FsmAp **others = machines + start + 1; + FsmRes res = FsmAp::nfaUnionOp( machines[start], others, (amount - 1), r->depth, stats ); + machines[start] = res.fsm; + + start += amount; + numGroups++; + } + + if ( numGroups == 1 ) + break; + + /* Move the group starts into the groups array. */ + FsmAp **groups = new FsmAp*[numGroups]; + int g = 0; + start = 0; + while ( start < numMachines ) { + groups[g] = machines[start]; + start += r->groups == 0 ? numMachines : r->groups; + g++; + } + + delete[] machines; + machines = groups; + numMachines = numGroups; + } + + FsmAp *ret = machines[0]; + return FsmRes( FsmRes::Fsm(), ret ); +} diff --git a/src/libfsm/fsmstate.cc b/src/libfsm/fsmstate.cc new file mode 100644 index 00000000..03a4df34 --- /dev/null +++ b/src/libfsm/fsmstate.cc @@ -0,0 +1,603 @@ +/* + * Copyright 2002-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "fsmgraph.h" + +#include +#include +#include + +/* Construct a mark index for a specified number of states. Must new up + * an array that is states^2 in size. */ +MarkIndex::MarkIndex( int states ) : numStates(states) +{ + /* Total pairs is states^2. Actually only use half of these, but we allocate + * them all to make indexing into the array easier. */ + int total = states * states; + + /* New up chars so that individual DListEl constructors are + * not called. Zero out the mem manually. */ + array = new bool[total]; + memset( array, 0, sizeof(bool) * total ); +} + +/* Free the array used to store state pairs. */ +MarkIndex::~MarkIndex() +{ + delete[] array; +} + +/* Mark a pair of states. States are specified by their number. The + * marked states are moved from the unmarked list to the marked list. */ +void MarkIndex::markPair(int state1, int state2) +{ + int pos = ( state1 >= state2 ) ? + ( state1 * numStates ) + state2 : + ( state2 * numStates ) + state1; + + array[pos] = true; +} + +/* Returns true if the pair of states are marked. Returns false otherwise. + * Ordering of states given does not matter. */ +bool MarkIndex::isPairMarked(int state1, int state2) +{ + int pos = ( state1 >= state2 ) ? + ( state1 * numStates ) + state2 : + ( state2 * numStates ) + state1; + + return array[pos]; +} + +/* Create a new fsm state. State has not out transitions or in transitions, not + * out out transition data and not number. */ +StateAp::StateAp() +: + /* No out or in transitions. */ + outList(), + inTrans(), + inCond(), + + /* No EOF target. */ + eofTarget(0), + + /* No entry points, or epsilon trans. */ + entryIds(), + epsilonTrans(), + + /* No transitions in from other states. */ + foreignInTrans(0), + + /* Only used during merging. Normally null. */ + stateDictEl(0), + stateDictIn(0), + + nfaOut(0), + nfaIn(0), + + eptVect(0), + + /* No state identification bits. */ + stateBits(0), + + /* No Priority data. */ + outPriorTable(), + + /* No Action data. */ + toStateActionTable(), + fromStateActionTable(), + outActionTable(), + outCondSpace(0), + outCondKeys(), + errActionTable(), + eofActionTable(), + guardedInTable(), + lmNfaParts() +{ +} + +/* Copy everything except actual the transitions. That is left up to the + * FsmAp copy constructor. */ +StateAp::StateAp(const StateAp &other) +: + /* All lists are cleared. They will be filled in when the + * individual transitions are duplicated and attached. */ + outList(), + inTrans(), + inCond(), + + /* Set this using the original state's eofTarget. It will get mapped back + * to the new machine in the Fsm copy constructor. */ + eofTarget(other.eofTarget), + + /* Duplicate the entry id set and epsilon transitions. These + * are sets of integers and as such need no fixing. */ + entryIds(other.entryIds), + epsilonTrans(other.epsilonTrans), + + /* No transitions in from other states. */ + foreignInTrans(0), + + /* This is only used during merging. Normally null. */ + stateDictEl(0), + stateDictIn(0), + + nfaOut(0), + nfaIn(0), + + eptVect(0), + + /* Fsm state data. */ + stateBits(other.stateBits), + + /* Copy in priority data. */ + outPriorTable(other.outPriorTable), + + /* Copy in action data. */ + toStateActionTable(other.toStateActionTable), + fromStateActionTable(other.fromStateActionTable), + outActionTable(other.outActionTable), + outCondSpace(other.outCondSpace), + outCondKeys(other.outCondKeys), + errActionTable(other.errActionTable), + eofActionTable(other.eofActionTable), + + guardedInTable(other.guardedInTable), + lmNfaParts(other.lmNfaParts) +{ + /* Duplicate all the transitions. */ + for ( TransList::Iter trans = other.outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + /* Duplicate and store the orginal target in the transition. This will + * be corrected once all the states have been created. */ + TransDataAp *newTrans = new TransDataAp( *trans->tdap() ); + assert( trans->tdap()->lmActionTable.length() == 0 ); + newTrans->toState = trans->tdap()->toState; + outList.append( newTrans ); + } + else { + /* Duplicate and store the orginal target in the transition. This will + * be corrected once all the states have been created. */ + TransAp *newTrans = new TransCondAp( *trans->tcap() ); + + for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++ ) { + CondAp *newCondTrans = new CondAp( *cti, newTrans ); + newCondTrans->key = cti->key; + + newTrans->tcap()->condList.append( newCondTrans ); + + assert( cti->lmActionTable.length() == 0 ); + + newCondTrans->toState = cti->toState; + } + + outList.append( newTrans ); + } + } + + /* Dup the nfa trans. */ + if ( other.nfaOut != 0 ) { + nfaOut = new NfaTransList; + for ( NfaTransList::Iter trans = *other.nfaOut; trans.lte(); trans++ ) { + NfaTrans *newtrans = new NfaTrans( *trans ); + newtrans->toState = trans->toState; + + nfaOut->append( newtrans ); + } + } +} + +/* If there is a state dict element, then delete it. Everything else is left + * up to the FsmGraph destructor. */ +StateAp::~StateAp() +{ + if ( stateDictEl != 0 ) + delete stateDictEl; + + if ( stateDictIn != 0 ) + delete stateDictIn; + + if ( nfaIn != 0 ) + delete nfaIn; + + if ( nfaOut != 0 ) { + nfaOut->empty(); + delete nfaOut; + } +} + +#ifdef TO_UPGRADE_CONDS +/* Compare two states using pointers to the states. With the approximate + * compare, the idea is that if the compare finds them the same, they can + * immediately be merged. */ +int ApproxCompare::compare( const StateAp *state1, const StateAp *state2 ) +{ + int compareRes; + + /* Test final state status. */ + if ( (state1->stateBits & STB_ISFINAL) && !(state2->stateBits & STB_ISFINAL) ) + return -1; + else if ( !(state1->stateBits & STB_ISFINAL) && (state2->stateBits & STB_ISFINAL) ) + return 1; + + /* Test epsilon transition sets. */ + compareRes = CmpEpsilonTrans::compare( state1->epsilonTrans, + state2->epsilonTrans ); + if ( compareRes != 0 ) + return compareRes; + + /* Compare the out transitions. */ + compareRes = FsmAp::compareStateData( state1, state2 ); + if ( compareRes != 0 ) + return compareRes; + + /* Use a pair iterator to get the transition pairs. */ + RangePairIter outPair( ctx, state1->outList.head, state2->outList.head ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + + case RangePairIter::RangeInS1: + compareRes = FsmAp::compareFullPtr( outPair.s1Tel.trans, 0 ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIter::RangeInS2: + compareRes = FsmAp::compareFullPtr( 0, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIter::RangeOverlap: + compareRes = FsmAp::compareFullPtr( + outPair.s1Tel.trans, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIter::BreakS1: + case RangePairIter::BreakS2: + break; + } + } + + /* Check EOF targets. */ + if ( state1->eofTarget < state2->eofTarget ) + return -1; + else if ( state1->eofTarget > state2->eofTarget ) + return 1; + + if ( state1->guardedIn || !state2->guardedIn ) + return -1; + else if ( !state1->guardedIn || state2->guardedIn ) + return 1; + + /* Got through the entire state comparison, deem them equal. */ + return 0; +} +#endif + + +/* Compare class used in the initial partition. */ +int InitPartitionCompare::compare( const StateAp *state1, const StateAp *state2 ) +{ + int compareRes; + + if ( state1->nfaOut == 0 && state2->nfaOut != 0 ) + return -1; + else if ( state1->nfaOut != 0 && state2->nfaOut == 0 ) + return 1; + else if ( state1->nfaOut != 0 ) { + compareRes = CmpNfaTransList::compare( + *state1->nfaOut, *state2->nfaOut ); + if ( compareRes != 0 ) + return compareRes; + } + + /* Test final state status. */ + if ( (state1->stateBits & STB_ISFINAL) && !(state2->stateBits & STB_ISFINAL) ) + return -1; + else if ( !(state1->stateBits & STB_ISFINAL) && (state2->stateBits & STB_ISFINAL) ) + return 1; + + /* Test epsilon transition sets. */ + compareRes = CmpEpsilonTrans::compare( state1->epsilonTrans, + state2->epsilonTrans ); + if ( compareRes != 0 ) + return compareRes; + + /* Compare the out transitions. */ + compareRes = FsmAp::compareStateData( state1, state2 ); + if ( compareRes != 0 ) + return compareRes; + + /* Use a pair iterator to test the transition pairs. */ + typedef RangePairIter< PiList > RangePairIterPiListTransAp; + RangePairIterPiListTransAp + outPair( ctx, state1->outList, state2->outList ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + + case RangePairIterPiListTransAp::RangeInS1: + compareRes = FsmAp::compareTransDataPtr( outPair.s1Tel.trans, 0 ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIterPiListTransAp::RangeInS2: + compareRes = FsmAp::compareTransDataPtr( 0, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIterPiListTransAp::RangeOverlap: + compareRes = FsmAp::compareTransDataPtr( + outPair.s1Tel.trans, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIterPiListTransAp::BreakS1: + case RangePairIterPiListTransAp::BreakS2: + break; + } + } + + return 0; +} + +/* Compare class for the sort that does the partitioning. */ +int PartitionCompare::compare( const StateAp *state1, const StateAp *state2 ) +{ + int compareRes; + + /* Use a pair iterator to get the transition pairs. */ + typedef RangePairIter< PiList > RangePairIterPiListTransAp; + RangePairIterPiListTransAp outPair( ctx, state1->outList, state2->outList ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + + case RangePairIterPiListTransAp::RangeInS1: + compareRes = FsmAp::compareTransPartPtr( outPair.s1Tel.trans, 0 ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIterPiListTransAp::RangeInS2: + compareRes = FsmAp::compareTransPartPtr( 0, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIterPiListTransAp::RangeOverlap: + compareRes = FsmAp::compareTransPartPtr( + outPair.s1Tel.trans, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + + case RangePairIterPiListTransAp::BreakS1: + case RangePairIterPiListTransAp::BreakS2: + break; + } + } + + /* Test eof targets. */ + if ( state1->eofTarget == 0 && state2->eofTarget != 0 ) + return -1; + else if ( state1->eofTarget != 0 && state2->eofTarget == 0 ) + return 1; + else if ( state1->eofTarget != 0 ) { + /* Both eof targets are set. */ + compareRes = CmpOrd< MinPartition* >::compare( + state1->eofTarget->alg.partition, state2->eofTarget->alg.partition ); + if ( compareRes != 0 ) + return compareRes; + } + + return 0; +} + +#ifdef TO_UPGRADE_CONDS +/* Compare class for the sort that does the partitioning. */ +bool MarkCompare::shouldMark( MarkIndex &markIndex, const StateAp *state1, + const StateAp *state2 ) +{ + /* Use a pair iterator to get the transition pairs. */ + RangePairIter outPair( ctx, state1->outList.head, state2->outList.head ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + + case RangePairIter::RangeInS1: + if ( FsmAp::shouldMarkPtr( markIndex, outPair.s1Tel.trans, 0 ) ) + return true; + break; + + case RangePairIter::RangeInS2: + if ( FsmAp::shouldMarkPtr( markIndex, 0, outPair.s2Tel.trans ) ) + return true; + break; + + case RangePairIter::RangeOverlap: + if ( FsmAp::shouldMarkPtr( markIndex, + outPair.s1Tel.trans, outPair.s2Tel.trans ) ) + return true; + break; + + case RangePairIter::BreakS1: + case RangePairIter::BreakS2: + break; + } + } + + return false; +} +#endif + +/* + * Transition Comparison. + */ + +int FsmAp::comparePart( TransAp *trans1, TransAp *trans2 ) +{ + if ( trans1->plain() ) { + int compareRes = FsmAp::compareCondPartPtr( trans1->tdap(), trans2->tdap() ); + if ( compareRes != 0 ) + return compareRes; + } + else { + /* Use a pair iterator to get the transition pairs. */ + typedef ValPairIter< PiList > ValPairIterPiListCondAp; + ValPairIterPiListCondAp outPair( trans1->tcap()->condList, + trans2->tcap()->condList ); + for ( ; !outPair.end(); outPair++ ) { + switch ( outPair.userState ) { + + case ValPairIterPiListCondAp::RangeInS1: { + int compareRes = FsmAp::compareCondPartPtr( outPair.s1Tel.trans, 0 ); + if ( compareRes != 0 ) + return compareRes; + break; + } + + case ValPairIterPiListCondAp::RangeInS2: { + int compareRes = FsmAp::compareCondPartPtr( 0, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + } + + case ValPairIterPiListCondAp::RangeOverlap: { + int compareRes = FsmAp::compareCondPartPtr( + outPair.s1Tel.trans, outPair.s2Tel.trans ); + if ( compareRes != 0 ) + return compareRes; + break; + }} + } + } + + return 0; +} + +/* Compare target partitions. Either pointer may be null. */ +int FsmAp::compareTransPartPtr( TransAp *trans1, TransAp *trans2 ) +{ + if ( trans1 != 0 ) { + /* If trans1 is set then so should trans2. The initial partitioning + * guarantees this for us. */ + return comparePart( trans1, trans2 ); + } + + return 0; +} + +template< class Trans > int FsmAp::compareCondPartPtr( Trans *trans1, Trans *trans2 ) +{ + if ( trans1 != 0 ) { + /* If trans1 is set then so should trans2. The initial partitioning + * guarantees this for us. */ + if ( trans1->toState == 0 && trans2->toState != 0 ) + return -1; + else if ( trans1->toState != 0 && trans2->toState == 0 ) + return 1; + else if ( trans1->toState != 0 ) { + /* Both of targets are set. */ + return CmpOrd< MinPartition* >::compare( + trans1->toState->alg.partition, trans2->toState->alg.partition ); + } + } + return 0; +} + + +/* Compares two transition pointers according to priority and functions. + * Either pointer may be null. Does not consider to state or from state. */ +int FsmAp::compareTransDataPtr( TransAp *trans1, TransAp *trans2 ) +{ + if ( trans1 == 0 && trans2 != 0 ) + return -1; + else if ( trans1 != 0 && trans2 == 0 ) + return 1; + else if ( trans1 != 0 ) { + /* Both of the transition pointers are set. */ + int compareRes = compareTransData( trans1, trans2 ); + if ( compareRes != 0 ) + return compareRes; + } + return 0; +} + +#ifdef TO_UPGRADE_CONDS +/* Compares two transitions according to target state, priority and functions. + * Does not consider from state. Either of the pointers may be null. */ +int FsmAp::compareFullPtr( TransAp *trans1, TransAp *trans2 ) +{ + /* << "FIXME: " << __PRETTY_FUNCTION__ << std::endl; */ + + if ( (trans1 != 0) ^ (trans2 != 0) ) { + /* Exactly one of the transitions is set. */ + if ( trans1 != 0 ) + return -1; + else + return 1; + } + else if ( trans1 != 0 ) { + /* Both of the transition pointers are set. Test target state, + * priority and funcs. */ + if ( tai(trans1)->tcap()->condList.head->toState < tai(trans2)->tcap()->condList.head->toState ) + return -1; + else if ( tai(trans1)->tcap()->condList.head->toState > tai(trans2)->tcap()->condList.head->toState ) + return 1; + else if ( tai(trans1)->tcap()->condList.head->toState != 0 ) { + /* Test transition data. */ + int compareRes = compareTransData( trans1, trans2 ); + if ( compareRes != 0 ) + return compareRes; + } + } + return 0; +} +#endif + +#ifdef TO_UPGRADE_CONDS +bool FsmAp::shouldMarkPtr( MarkIndex &markIndex, TransAp *trans1, + TransAp *trans2 ) +{ + /* << "FIXME: " << __PRETTY_FUNCTION__ << std::endl; */ + + if ( (trans1 != 0) ^ (trans2 != 0) ) { + /* Exactly one of the transitions is set. The initial mark round + * should rule out this case. */ + assert( false ); + } + else if ( trans1 != 0 ) { + /* Both of the transitions are set. If the target pair is marked, then + * the pair we are considering gets marked. */ + return markIndex.isPairMarked( tai(trans1)->tcap()->condList.head->toState->alg.stateNum, + tai(trans2)->tcap()->condList.head->toState->alg.stateNum ); + } + + /* Neither of the transitiosn are set. */ + return false; +} +#endif diff --git a/src/libfsm/gendata.cc b/src/libfsm/gendata.cc new file mode 100644 index 00000000..4e3253ad --- /dev/null +++ b/src/libfsm/gendata.cc @@ -0,0 +1,1732 @@ +/* + * Copyright 2005-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "gendata.h" +#include "ragel.h" +#include "parsedata.h" +#include "fsmgraph.h" +#include "action.h" + +#include +#include + +string itoa( int i ) +{ + char buf[16]; + sprintf( buf, "%i", i ); + return buf; +} + +void openHostBlock( char opener, InputData *id, ostream &out, const char *fileName, int line ) +{ + out << "host( \""; + for ( const char *pc = fileName; *pc != 0; pc++ ) { + if ( *pc == '\\' ) + out << "\\\\"; + else + out << *pc; + } + out << "\", " << line << " ) " << opener << "{"; +} + +void Reducer::appendTrans( TransListVect &outList, Key lowKey, + Key highKey, TransAp *trans ) +{ + if ( trans->plain() ) { + if ( trans->tdap()->toState != 0 || trans->tdap()->actionTable.length() > 0 ) + outList.append( TransEl( lowKey, highKey, trans ) ); + } + else { + /* Add once if any cond has a to-state or an action table. */ + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->toState != 0 || cond->actionTable.length() > 0 ) { + outList.append( TransEl( lowKey, highKey, trans ) ); + break; + } + } + } +} + +void Reducer::reduceActionTables() +{ + /* Reduce the actions tables to a set. */ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + RedActionTable *actionTable = 0; + + /* Reduce To State Actions. */ + if ( st->toStateActionTable.length() > 0 ) { + if ( actionTableMap.insert( st->toStateActionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + + /* Reduce From State Actions. */ + if ( st->fromStateActionTable.length() > 0 ) { + if ( actionTableMap.insert( st->fromStateActionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + + /* Reduce EOF actions. */ + if ( st->eofActionTable.length() > 0 ) { + if ( actionTableMap.insert( st->eofActionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + + /* Loop the transitions and reduce their actions. */ + for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { + if ( trans->plain() ) { + if ( trans->tdap()->actionTable.length() > 0 ) { + if ( actionTableMap.insert( trans->tdap()->actionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + if ( cond->actionTable.length() > 0 ) { + if ( actionTableMap.insert( cond->actionTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + } + } + } + + if ( st->nfaOut != 0 ) { + for ( NfaTransList::Iter n = *st->nfaOut; n.lte(); n++ ) { + if ( actionTableMap.insert( n->pushTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + + if ( actionTableMap.insert( n->restoreTable, &actionTable ) ) + actionTable->id = nextActionTableId++; + + if ( actionTableMap.insert( n->popAction, &actionTable ) ) + actionTable->id = nextActionTableId++; + + if ( actionTableMap.insert( n->popTest, &actionTable ) ) + actionTable->id = nextActionTableId++; + } + } + } +} + + +void Reducer::makeText( GenInlineList *outList, InlineItem *item ) +{ + GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), GenInlineItem::Text ); + inlineItem->data = item->data; + + outList->append( inlineItem ); +} + +void Reducer::makeTargetItem( GenInlineList *outList, NameInst *nameTarg, + GenInlineItem::Type type ) +{ + long targetState; + if ( fsmCtx->generatingSectionSubset ) + targetState = -1; + else { + EntryMapEl *targ = fsm->entryPoints.find( nameTarg->id ); + targetState = targ->value->alg.stateNum; + } + + /* Make the item. */ + GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), type ); + inlineItem->targId = targetState; + outList->append( inlineItem ); +} + + +void Reducer::makeSubList( GenInlineList *outList, const InputLoc &loc, + InlineList *inlineList, GenInlineItem::Type type ) +{ + /* Fill the sub list. */ + GenInlineList *subList = new GenInlineList; + makeGenInlineList( subList, inlineList ); + + /* Make the item. */ + GenInlineItem *inlineItem = new GenInlineItem( loc, type ); + inlineItem->children = subList; + outList->append( inlineItem ); +} + +/* Make a sublist item with a given type. */ +void Reducer::makeSubList( GenInlineList *outList, + InlineList *inlineList, GenInlineItem::Type type ) +{ + makeSubList( outList, InputLoc(), inlineList, type ); +} + +void Reducer::makeLmOnLast( GenInlineList *outList, InlineItem *item ) +{ + makeSetTokend( outList, 1 ); + + if ( item->longestMatchPart->action != 0 ) { + Action *action = item->longestMatchPart->action; + makeSubList( outList, action->loc, action->inlineList, + GenInlineItem::HostStmt ); + } +} + +void Reducer::makeLmOnNext( GenInlineList *outList, InlineItem *item ) +{ + makeSetTokend( outList, 0 ); + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmHold ) ); + + if ( item->longestMatchPart->action != 0 ) { + Action *action = item->longestMatchPart->action; + makeSubList( outList, action->loc, action->inlineList, + GenInlineItem::HostStmt ); + } +} + +void Reducer::makeExecGetTokend( GenInlineList *outList ) +{ + /* Make the Exec item. */ + GenInlineItem *execItem = new GenInlineItem( InputLoc(), GenInlineItem::LmExec ); + execItem->children = new GenInlineList; + + /* Make the GetTokEnd */ + GenInlineItem *getTokend = new GenInlineItem( InputLoc(), GenInlineItem::LmGetTokEnd ); + execItem->children->append( getTokend ); + + outList->append( execItem ); +} + +void Reducer::makeLmOnLagBehind( GenInlineList *outList, InlineItem *item ) +{ + /* Jump to the tokend. */ + makeExecGetTokend( outList ); + + if ( item->longestMatchPart->action != 0 ) { + Action *action = item->longestMatchPart->action; + makeSubList( outList, action->loc, action->inlineList, + GenInlineItem::HostStmt ); + } +} + +void Reducer::makeLmSwitch( GenInlineList *outList, InlineItem *item ) +{ + GenInlineItem *lmSwitch = new GenInlineItem( InputLoc(), GenInlineItem::LmSwitch ); + GenInlineList *lmList = lmSwitch->children = new GenInlineList; + FsmLongestMatch *longestMatch = item->longestMatch; + + /* We can't put the here because we may need to handle the error + * case and in that case p should not be changed. Instead use a default + * label in the switch to adjust p when user actions are not set. An id of + * -1 indicates the default. */ + + if ( longestMatch->lmSwitchHandlesError ) { + /* If the switch handles error then we should have also forced the + * error state. */ + assert( fsm->errState != 0 ); + + GenInlineItem *errCase = new GenInlineItem( InputLoc(), GenInlineItem::HostStmt ); + errCase->lmId = 0; + errCase->children = new GenInlineList; + + GenInlineItem *host = new GenInlineItem( item->loc, GenInlineItem::HostStmt ); + host->children = new GenInlineList; + errCase->children->append( host ); + + /* Make the item. This should probably be an LM goto, would eliminate + * need for wrapping in host statement. .*/ + GenInlineItem *gotoItem = new GenInlineItem( InputLoc(), GenInlineItem::Goto ); + gotoItem->targId = fsm->errState->alg.stateNum; + host->children->append( gotoItem ); + + lmList->append( errCase ); + } + + bool needDefault = false; + for ( FsmLmPartList::Iter lmi = *longestMatch->longestMatchList; lmi.lte(); lmi++ ) { + if ( lmi->inLmSelect ) { + if ( lmi->action == 0 ) + needDefault = true; + else { + /* Open the action. Write it with the context that sets up _p + * when doing control flow changes from inside the machine. */ + GenInlineItem *lmCase = new GenInlineItem( InputLoc(), GenInlineItem::LmCase ); + lmCase->lmId = lmi->longestMatchId; + lmCase->children = new GenInlineList; + + makeExecGetTokend( lmCase->children ); + + GenInlineItem *subHost = new GenInlineItem( lmi->action->loc, + GenInlineItem::HostStmt ); + subHost->children = new GenInlineList; + makeGenInlineList( subHost->children, lmi->action->inlineList ); + lmCase->children->append( subHost ); + + lmList->append( lmCase ); + } + } + } + + if ( needDefault ) { + GenInlineItem *defCase = new GenInlineItem( item->loc, GenInlineItem::HostStmt ); + defCase->lmId = -1; + defCase->children = new GenInlineList; + + makeExecGetTokend( defCase->children ); + + lmList->append( defCase ); + } + + outList->append( lmSwitch ); +} + +void Reducer::makeLmNfaOnNext( GenInlineList *outList, InlineItem *item ) +{ + makeSetTokend( outList, 0 ); + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmHold ) ); + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::NfaClear ) ); + + if ( item->longestMatchPart->action != 0 ) { + Action *action = item->longestMatchPart->action; + makeSubList( outList, action->loc, action->inlineList, + GenInlineItem::HostStmt ); + } +} + +void Reducer::makeLmNfaOnEof( GenInlineList *outList, InlineItem *item ) +{ + makeSetTokend( outList, 0 ); + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::NfaClear ) ); + + if ( item->longestMatchPart->action != 0 ) { + Action *action = item->longestMatchPart->action; + makeSubList( outList, action->loc, action->inlineList, + GenInlineItem::HostStmt ); + } +} + +void Reducer::makeLmNfaOnLast( GenInlineList *outList, InlineItem *item ) +{ + makeSetTokend( outList, 1 ); + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::NfaClear ) ); + + if ( item->longestMatchPart->action != 0 ) { + Action *action = item->longestMatchPart->action; + makeSubList( outList, action->loc, action->inlineList, + GenInlineItem::HostStmt ); + } +} + + +void Reducer::makeSetTokend( GenInlineList *outList, long offset ) +{ + GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), GenInlineItem::LmSetTokEnd ); + inlineItem->offset = offset; + outList->append( inlineItem ); +} + +void Reducer::makeSetAct( GenInlineList *outList, long lmId ) +{ + GenInlineItem *inlineItem = new GenInlineItem( InputLoc(), GenInlineItem::LmSetActId ); + inlineItem->lmId = lmId; + outList->append( inlineItem ); +} + +void Reducer::makeGenInlineList( GenInlineList *outList, InlineList *inList ) +{ + for ( InlineList::Iter item = *inList; item.lte(); item++ ) { + switch ( item->type ) { + case InlineItem::Text: + makeText( outList, item ); + break; + case InlineItem::Goto: + makeTargetItem( outList, item->nameTarg, GenInlineItem::Goto ); + break; + case InlineItem::GotoExpr: + makeSubList( outList, item->children, GenInlineItem::GotoExpr ); + break; + case InlineItem::Call: + makeTargetItem( outList, item->nameTarg, GenInlineItem::Call ); + break; + case InlineItem::CallExpr: + makeSubList( outList, item->children, GenInlineItem::CallExpr ); + break; + case InlineItem::Ncall: + makeTargetItem( outList, item->nameTarg, GenInlineItem::Ncall ); + break; + case InlineItem::NcallExpr: + makeSubList( outList, item->children, GenInlineItem::NcallExpr ); + break; + case InlineItem::Next: + makeTargetItem( outList, item->nameTarg, GenInlineItem::Next ); + break; + case InlineItem::NextExpr: + makeSubList( outList, item->children, GenInlineItem::NextExpr ); + break; + case InlineItem::Break: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Break ) ); + break; + case InlineItem::Nbreak: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Nbreak ) ); + break; + case InlineItem::Ret: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Ret ) ); + break; + case InlineItem::Nret: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Nret ) ); + break; + case InlineItem::PChar: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::PChar ) ); + break; + case InlineItem::Char: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Char ) ); + break; + case InlineItem::Curs: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Curs ) ); + break; + case InlineItem::Targs: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Targs ) ); + break; + case InlineItem::Entry: + makeTargetItem( outList, item->nameTarg, GenInlineItem::Entry ); + break; + + case InlineItem::Hold: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::Hold ) ); + break; + case InlineItem::Exec: + makeSubList( outList, item->children, GenInlineItem::Exec ); + break; + + case InlineItem::LmSetActId: + makeSetAct( outList, item->longestMatchPart->longestMatchId ); + break; + case InlineItem::LmSetTokEnd: + makeSetTokend( outList, 1 ); + break; + + case InlineItem::LmOnLast: + makeLmOnLast( outList, item ); + break; + case InlineItem::LmOnNext: + makeLmOnNext( outList, item ); + break; + case InlineItem::LmOnLagBehind: + makeLmOnLagBehind( outList, item ); + break; + case InlineItem::LmSwitch: + makeLmSwitch( outList, item ); + break; + + case InlineItem::LmNfaOnLast: + makeLmNfaOnLast( outList, item ); + break; + case InlineItem::LmNfaOnNext: + makeLmNfaOnNext( outList, item ); + break; + case InlineItem::LmNfaOnEof: + makeLmNfaOnEof( outList, item ); + break; + + case InlineItem::LmInitAct: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmInitAct ) ); + break; + case InlineItem::LmInitTokStart: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmInitTokStart ) ); + break; + case InlineItem::LmSetTokStart: + outList->append( new GenInlineItem( InputLoc(), GenInlineItem::LmSetTokStart ) ); + hasLongestMatch = true; + break; + case InlineItem::Stmt: + makeSubList( outList, item->children, GenInlineItem::GenStmt ); + break; + case InlineItem::Subst: { + /* Find the subst action. */ + Action *subst = curInlineAction->argList->data[item->substPos]; + makeGenInlineList( outList, subst->inlineList ); + break; + } + case InlineItem::NfaWrapAction: { + GenAction *wrap = allActions + item->wrappedAction->actionId; + GenInlineItem *gii = new GenInlineItem( InputLoc(), + GenInlineItem::NfaWrapAction ); + gii->wrappedAction = wrap; + outList->append( gii ); + break; + } + case InlineItem::NfaWrapConds: { + GenCondSpace *condSpace = allCondSpaces + item->condSpace->condSpaceId; + + GenInlineItem *gii = new GenInlineItem( InputLoc(), + GenInlineItem::NfaWrapConds ); + gii->condSpace = condSpace; + gii->condKeySet = item->condKeySet; + outList->append( gii ); + break; + }} + } +} + +void Reducer::makeExports() +{ + for ( ExportList::Iter exp = fsmCtx->exportList; exp.lte(); exp++ ) + exportList.append( new Export( exp->name, exp->key ) ); +} + +void Reducer::makeAction( Action *action ) +{ + GenInlineList *genList = new GenInlineList; + + curInlineAction = action; + makeGenInlineList( genList, action->inlineList ); + curInlineAction = 0; + + newAction( curAction++, action->name, action->loc, genList ); +} + + +void Reducer::makeActionList() +{ + /* Determine which actions to write. */ + int nextActionId = 0; + for ( ActionList::Iter act = fsmCtx->actionList; act.lte(); act++ ) { + if ( act->numRefs() > 0 || act->numCondRefs > 0 ) + act->actionId = nextActionId++; + } + + /* Write the list. */ + initActionList( nextActionId ); + curAction = 0; + + for ( ActionList::Iter act = fsmCtx->actionList; act.lte(); act++ ) { + if ( act->actionId >= 0 ) + makeAction( act ); + } +} + +void Reducer::makeActionTableList() +{ + /* Must first order the action tables based on their id. */ + int numTables = nextActionTableId; + RedActionTable **tables = new RedActionTable*[numTables]; + for ( ActionTableMap::Iter at = actionTableMap; at.lte(); at++ ) + tables[at->id] = at; + + initActionTableList( numTables ); + curActionTable = 0; + + for ( int t = 0; t < numTables; t++ ) { + long length = tables[t]->key.length(); + + /* Collect the action table. */ + RedAction *redAct = allActionTables + curActionTable; + redAct->actListId = curActionTable; + redAct->key.setAsNew( length ); + + for ( ActionTable::Iter atel = tables[t]->key; atel.lte(); atel++ ) { + redAct->key[atel.pos()].key = 0; + redAct->key[atel.pos()].value = allActions + + atel->value->actionId; + } + + /* Insert into the action table map. */ + redFsm->actionMap.insert( redAct ); + + curActionTable += 1; + } + + delete[] tables; +} + +void Reducer::makeConditions() +{ + if ( fsm->ctx->condData->condSpaceMap.length() > 0 ) { + /* Allocate condition space ids. */ + long nextCondSpaceId = 0; + for ( CondSpaceMap::Iter cs = fsm->ctx->condData->condSpaceMap; cs.lte(); cs++ ) + cs->condSpaceId = nextCondSpaceId++; + + /* Allocate the array of conditions and put them on the list. */ + long length = fsm->ctx->condData->condSpaceMap.length(); + allCondSpaces = new GenCondSpace[length]; + for ( long c = 0; c < length; c++ ) + condSpaceList.append( &allCondSpaces[c] ); + + long curCondSpace = 0; + for ( CondSpaceMap::Iter cs = fsm->ctx->condData->condSpaceMap; cs.lte(); cs++ ) { + /* Transfer the id. */ + allCondSpaces[curCondSpace].condSpaceId = cs->condSpaceId; + + curCondSpace += 1; + } + } + + makeActionList(); + makeActionTableList(); + + if ( fsm->ctx->condData->condSpaceMap.length() > 0 ) { + long curCondSpace = 0; + for ( CondSpaceMap::Iter cs = fsm->ctx->condData->condSpaceMap; cs.lte(); cs++ ) { + for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ ) + condSpaceItem( curCondSpace, (*csi)->actionId ); + curCondSpace += 1; + } + } +} + +bool Reducer::makeNameInst( std::string &res, NameInst *nameInst ) +{ + bool written = false; + if ( nameInst->parent != 0 ) + written = makeNameInst( res, nameInst->parent ); + + if ( !nameInst->name.empty() ) { + if ( written ) + res += '_'; + res += nameInst->name; + written = true; + } + + return written; +} + +void Reducer::makeEntryPoints() +{ + /* List of entry points other than start state. */ + if ( fsm->entryPoints.length() > 0 || fsmCtx->lmRequiresErrorState ) { + if ( fsmCtx->lmRequiresErrorState ) + setForcedErrorState(); + + for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) { + /* Get the name instantiation from nameIndex. */ + NameInst *nameInst = fsmCtx->nameIndex[en->key]; + std::string name; + makeNameInst( name, nameInst ); + StateAp *state = en->value; + addEntryPoint( strdup(name.c_str()), state->alg.stateNum ); + } + } +} + +void Reducer::makeStateActions( StateAp *state ) +{ + RedActionTable *toStateActions = 0; + if ( state->toStateActionTable.length() > 0 ) + toStateActions = actionTableMap.find( state->toStateActionTable ); + + RedActionTable *fromStateActions = 0; + if ( state->fromStateActionTable.length() > 0 ) + fromStateActions = actionTableMap.find( state->fromStateActionTable ); + + if ( toStateActions != 0 || fromStateActions != 0 ) { + long to = -1; + if ( toStateActions != 0 ) + to = toStateActions->id; + + long from = -1; + if ( fromStateActions != 0 ) + from = fromStateActions->id; + + setStateActions( curState, to, from, -1 ); + } +} + +void Reducer::makeTrans( Key lowKey, Key highKey, TransAp *trans ) +{ + RedCondEl *outConds; + int numConds; + + assert( ( allStates + curState ) != redFsm->errState ); + + if ( trans->plain() ) { + long targ = -1; + long action = -1; + + /* First reduce the action. */ + RedActionTable *actionTable = 0; + if ( trans->tdap()->actionTable.length() > 0 ) + actionTable = actionTableMap.find( trans->tdap()->actionTable ); + + if ( trans->tdap()->toState != 0 ) + targ = trans->tdap()->toState->alg.stateNum; + + if ( actionTable != 0 ) + action = actionTable->id; + + /* Make the new transitions. */ + RedStateAp *targState = targ >= 0 ? (allStates + targ) : redFsm->getErrorState(); + RedAction *at = action >= 0 ? (allActionTables + action) : 0; + + RedTransAp *trans = redFsm->allocateTrans( targState, at ); + newTrans( allStates + curState, lowKey, highKey, trans ); + } + else { + numConds = trans->tcap()->condList.length(); + outConds = new RedCondEl[numConds]; + int pos = 0; + for ( CondList::Iter cti = trans->tcap()->condList; cti.lte(); cti++, pos++ ) { + long targ = -1; + long action = -1; + + /* First reduce the action. */ + RedActionTable *actionTable = 0; + if ( cti->actionTable.length() > 0 ) + actionTable = actionTableMap.find( cti->actionTable ); + + if ( cti->toState != 0 ) + targ = cti->toState->alg.stateNum; + + if ( actionTable != 0 ) + action = actionTable->id; + + /* Make the new transitions. */ + RedStateAp *targState = targ >= 0 ? (allStates + targ) : redFsm->getErrorState(); + RedAction *at = action >= 0 ? (allActionTables + action) : 0; + RedCondAp *cond = redFsm->allocateCond( targState, at ); + + outConds[pos].key = cti->key; + outConds[pos].value = cond; + } + + GenCondSpace *condSpace = allCondSpaces + trans->condSpace->condSpaceId; + + /* If the cond list is not full then we need an error cond. */ + RedCondAp *errCond = 0; + if ( numConds < ( 1 << condSpace->condSet.length() ) ) + errCond = redFsm->getErrorCond(); + + RedTransAp *trans = redFsm->allocateTrans( + condSpace, outConds, numConds, errCond ); + + newTrans( allStates + curState, lowKey, highKey, trans ); + } +} + +void Reducer::makeEofTrans( StateAp *state ) +{ + /* EOF actions go out here only if the state has no eof target. If it has + * an eof target then an eof transition will be used instead. */ + RedActionTable *eofActions = 0; + if ( state->eofActionTable.length() > 0 ) + eofActions = actionTableMap.find( state->eofActionTable ); + + /* Add an EOF transition if we have conditions, a target, or actions, */ + if ( state->outCondSpace != 0 || state->eofTarget != 0 || eofActions != 0 ) + redFsm->bAnyEofActivity = true; + + long targ = state->alg.stateNum; + long action = -1; + + if ( state->eofTarget != 0 ) + targ = state->eofTarget->alg.stateNum; + + if ( eofActions != 0 ) + action = eofActions->id; + + + if ( state->outCondSpace == 0 ) { + // std::cerr << "setEofTrans( " << + // state->alg.stateNum << ", " << targ << ", " << action << " );" << endl; + + setEofTrans( state->alg.stateNum, targ, action ); + } + else { + int numConds = state->outCondKeys.length(); + RedCondEl *outConds = new RedCondEl[numConds]; + for ( int pos = 0; pos < numConds; pos++ ) { + /* Make the new transitions. */ + RedStateAp *targState = targ >= 0 ? (allStates + targ) : redFsm->getErrorState(); + RedAction *at = action >= 0 ? (allActionTables + action) : 0; + RedCondAp *cond = redFsm->allocateCond( targState, at ); + + outConds[pos].key = state->outCondKeys[pos]; + outConds[pos].value = cond; + } + + GenCondSpace *condSpace = allCondSpaces + state->outCondSpace->condSpaceId; + + /* If the cond list is not full then we need an error cond. */ + RedCondAp *errCond = 0; + if ( numConds < ( 1 << condSpace->condSet.length() ) ) + errCond = redFsm->getErrorCond(); + + setEofTrans( state->alg.stateNum, condSpace, outConds, numConds, errCond ); + } +} + + +void Reducer::makeTransList( StateAp *state ) +{ + TransListVect outList; + + /* If there is only are no ranges the task is simple. */ + if ( state->outList.length() > 0 ) { + /* Loop each source range. */ + for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) { + /* Reduce the transition. If it reduced to anything then add it. */ + appendTrans( outList, trans->lowKey, trans->highKey, trans ); + } + } + + initTransList( curState, outList.length() ); + + for ( TransListVect::Iter tvi = outList; tvi.lte(); tvi++ ) + makeTrans( tvi->lowKey, tvi->highKey, tvi->value ); + + finishTransList( curState ); +} + +void Reducer::makeStateList() +{ + /* Write the list of states. */ + long length = fsm->stateList.length(); + initStateList( length ); + curState = 0; + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + makeStateActions( st ); + makeEofTrans( st ); + makeTransList( st ); + + long id = st->alg.stateNum; + setId( curState, id ); + + if ( st->isFinState() ) + setFinal( curState ); + + if ( st->nfaOut != 0 ) { + RedStateAp *from = allStates + curState; + from->nfaTargs = new RedNfaTargs; + for ( NfaTransList::Iter targ = *st->nfaOut; targ.lte(); targ++ ) { + RedStateAp *rtarg = allStates + targ->toState->alg.stateNum; + + RedAction *pushRa = 0; + RedAction *popTestRa = 0; + + if ( targ->pushTable.length() > 0 ) { + RedActionTable *pushActions = + actionTableMap.find( targ->pushTable ); + pushRa = allActionTables + pushActions->id; + } + + if ( targ->popTest.length() > 0 ) { + RedActionTable *popActions = + actionTableMap.find( targ->popTest ); + popTestRa = allActionTables + popActions->id; + } + + + from->nfaTargs->append( RedNfaTarg( rtarg, pushRa, + popTestRa, targ->order ) ); + + MergeSort sort; + sort.sort( from->nfaTargs->data, from->nfaTargs->length() ); + } + } + + curState += 1; + } +} + +void Reducer::makeMachine() +{ + createMachine(); + + /* Action tables. */ + reduceActionTables(); + + makeConditions(); + + /* Start State. */ + setStartState( fsm->startState->alg.stateNum ); + + /* Error state. */ + if ( fsm->errState != 0 ) + setErrorState( fsm->errState->alg.stateNum ); + + makeEntryPoints(); + makeStateList(); + + resolveTargetStates(); +} + +void Reducer::make( const HostLang *hostLang, const HostType *alphType ) +{ + /* Alphabet type. */ + setAlphType( hostLang, alphType->internalName ); + + /* Getkey expression. */ + if ( fsmCtx->getKeyExpr != 0 ) { + getKeyExpr = new GenInlineList; + makeGenInlineList( getKeyExpr, fsmCtx->getKeyExpr ); + } + + /* Access expression. */ + if ( fsmCtx->accessExpr != 0 ) { + accessExpr = new GenInlineList; + makeGenInlineList( accessExpr, fsmCtx->accessExpr ); + } + + /* PrePush expression. */ + if ( fsmCtx->prePushExpr != 0 ) { + GenInlineList *il = new GenInlineList; + makeGenInlineList( il, fsmCtx->prePushExpr->inlineList ); + prePushExpr = new GenInlineExpr( fsmCtx->prePushExpr->loc, il ); + } + + /* PostPop expression. */ + if ( fsmCtx->postPopExpr != 0 ) { + GenInlineList *il = new GenInlineList; + makeGenInlineList( il, fsmCtx->postPopExpr->inlineList ); + postPopExpr = new GenInlineExpr( fsmCtx->postPopExpr->loc, il ); + } + + /* PrePush expression. */ + if ( fsmCtx->nfaPrePushExpr != 0 ) { + GenInlineList *il = new GenInlineList; + makeGenInlineList( il, fsmCtx->nfaPrePushExpr->inlineList ); + nfaPrePushExpr = new GenInlineExpr( fsmCtx->nfaPrePushExpr->loc, il ); + } + + /* PostPop expression. */ + if ( fsmCtx->nfaPostPopExpr != 0 ) { + GenInlineList *il = new GenInlineList; + makeGenInlineList( il, fsmCtx->nfaPostPopExpr->inlineList ); + nfaPostPopExpr = new GenInlineExpr( fsmCtx->nfaPostPopExpr->loc, il ); + } + + + /* + * Variable expressions. + */ + + if ( fsmCtx->pExpr != 0 ) { + pExpr = new GenInlineList; + makeGenInlineList( pExpr, fsmCtx->pExpr ); + } + + if ( fsmCtx->peExpr != 0 ) { + peExpr = new GenInlineList; + makeGenInlineList( peExpr, fsmCtx->peExpr ); + } + + if ( fsmCtx->eofExpr != 0 ) { + eofExpr = new GenInlineList; + makeGenInlineList( eofExpr, fsmCtx->eofExpr ); + } + + if ( fsmCtx->csExpr != 0 ) { + csExpr = new GenInlineList; + makeGenInlineList( csExpr, fsmCtx->csExpr ); + } + + if ( fsmCtx->topExpr != 0 ) { + topExpr = new GenInlineList; + makeGenInlineList( topExpr, fsmCtx->topExpr ); + } + + if ( fsmCtx->stackExpr != 0 ) { + stackExpr = new GenInlineList; + makeGenInlineList( stackExpr, fsmCtx->stackExpr ); + } + + if ( fsmCtx->actExpr != 0 ) { + actExpr = new GenInlineList; + makeGenInlineList( actExpr, fsmCtx->actExpr ); + } + + if ( fsmCtx->tokstartExpr != 0 ) { + tokstartExpr = new GenInlineList; + makeGenInlineList( tokstartExpr, fsmCtx->tokstartExpr ); + } + + if ( fsmCtx->tokendExpr != 0 ) { + tokendExpr = new GenInlineList; + makeGenInlineList( tokendExpr, fsmCtx->tokendExpr ); + } + + if ( fsmCtx->dataExpr != 0 ) { + dataExpr = new GenInlineList; + makeGenInlineList( dataExpr, fsmCtx->dataExpr ); + } + + makeExports(); + makeMachine(); + + /* Do this before distributing transitions out to singles and defaults + * makes life easier. */ + redFsm->maxKey = findMaxKey(); + + redFsm->assignActionLocs(); + + /* Find the first final state (The final state with the lowest id). */ + redFsm->findFirstFinState(); +} + +void Reducer::createMachine() +{ + redFsm = new RedFsmAp( fsm->ctx, machineId ); +} + +void Reducer::initActionList( unsigned long length ) +{ + allActions = new GenAction[length]; + for ( unsigned long a = 0; a < length; a++ ) + actionList.append( allActions+a ); +} + +void Reducer::newAction( int anum, std::string name, + const InputLoc &loc, GenInlineList *inlineList ) +{ + allActions[anum].actionId = anum; + allActions[anum].name = name; + allActions[anum].loc = loc; + allActions[anum].inlineList = inlineList; +} + +void Reducer::initActionTableList( unsigned long length ) +{ + allActionTables = new RedAction[length]; +} + +void Reducer::initStateList( unsigned long length ) +{ + redFsm->allStates = allStates = new RedStateAp[length]; + for ( unsigned long s = 0; s < length; s++ ) + redFsm->stateList.append( allStates+s ); + + /* We get the start state as an offset, set the pointer now. */ + if ( startState >= 0 ) + redFsm->startState = allStates + startState; + if ( errState >= 0 ) + redFsm->errState = allStates + errState; + for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ ) + redFsm->entryPoints.insert( allStates + *en ); + + /* The nextStateId is no longer used to assign state ids (they come in set + * from the frontend now), however generation code still depends on it. + * Should eventually remove this variable. */ + redFsm->nextStateId = redFsm->stateList.length(); +} + +void Reducer::setStartState( unsigned long _startState ) +{ + startState = _startState; +} + +void Reducer::setErrorState( unsigned long _errState ) +{ + errState = _errState; +} + +void Reducer::addEntryPoint( char *name, unsigned long entryState ) +{ + entryPointIds.append( entryState ); + entryPointNames.append( name ); +} + +void Reducer::initTransList( int snum, unsigned long length ) +{ + /* Could preallocate the out range to save time growing it. For now do + * nothing. */ +} + +void Reducer::newTrans( RedStateAp *state, Key lowKey, Key highKey, RedTransAp *trans ) +{ + /* Get the current state and range. */ + RedTransList &destRange = state->outRange; + + /* Reduced machines are complete. We need to fill any gaps with the error + * transitions. */ + if ( destRange.length() == 0 ) { + /* Range is currently empty. */ + if ( keyOps->lt( keyOps->minKey, lowKey ) ) { + /* The first range doesn't start at the low end. */ + Key fillHighKey = lowKey; + keyOps->decrement( fillHighKey ); + + /* Create the filler with the state's error transition. */ + RedTransEl newTel( fsm->ctx->keyOps->minKey, fillHighKey, + redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + } + else { + /* The range list is not empty, get the the last range. */ + RedTransEl *last = &destRange[destRange.length()-1]; + Key nextKey = last->highKey; + keyOps->increment( nextKey ); + if ( keyOps->lt( nextKey, lowKey ) ) { + /* There is a gap to fill. Make the high key. */ + Key fillHighKey = lowKey; + keyOps->decrement( fillHighKey ); + + /* Create the filler with the state's error transtion. */ + RedTransEl newTel( nextKey, fillHighKey, redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + } + + /* Filler taken care of. Append the range. */ + destRange.append( RedTransEl( lowKey, highKey, trans ) ); +} + +void Reducer::finishTransList( int snum ) +{ + /* Get the current state and range. */ + RedStateAp *curState = allStates + snum; + RedTransList &destRange = curState->outRange; + + if ( curState == redFsm->errState ) + return; + + /* We may need filler on the end. */ + /* Check if there are any ranges already. */ + if ( destRange.length() == 0 ) { + /* Fill with the whole alphabet. */ + /* Add the range on the lower and upper bound. */ + RedTransEl newTel( fsm->ctx->keyOps->minKey, + fsm->ctx->keyOps->maxKey, redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + else { + /* Get the last and check for a gap on the end. */ + RedTransEl *last = &destRange[destRange.length()-1]; + if ( keyOps->lt( last->highKey, fsm->ctx->keyOps->maxKey ) ) { + /* Make the high key. */ + Key fillLowKey = last->highKey; + keyOps->increment( fillLowKey ); + + /* Create the new range with the error trans and append it. */ + RedTransEl newTel( fillLowKey, fsm->ctx->keyOps->maxKey, + redFsm->getErrorTrans() ); + destRange.append( newTel ); + } + } +} + +void Reducer::setId( int snum, int id ) +{ + RedStateAp *curState = allStates + snum; + curState->id = id; +} + +void Reducer::setFinal( int snum ) +{ + RedStateAp *curState = allStates + snum; + curState->isFinal = true; +} + + +void Reducer::setStateActions( int snum, long toStateAction, + long fromStateAction, long eofAction ) +{ + RedStateAp *curState = allStates + snum; + if ( toStateAction >= 0 ) + curState->toStateAction = allActionTables + toStateAction; + if ( fromStateAction >= 0 ) + curState->fromStateAction = allActionTables + fromStateAction; + if ( eofAction >= 0 ) + curState->eofAction = allActionTables + eofAction; +} + +void Reducer::setEofTrans( int snum, long eofTarget, long actId ) +{ + RedStateAp *curState = allStates + snum; + RedStateAp *targState = allStates + eofTarget; + RedAction *eofAct = actId >= 0 ? allActionTables + actId : 0; + + RedTransAp *trans = redFsm->allocateTrans( targState, eofAct ); + curState->eofTrans = trans; +} + +void Reducer::setEofTrans( int snum, GenCondSpace *condSpace, + RedCondEl *outConds, int numConds, RedCondAp *errCond ) +{ + RedStateAp *curState = allStates + snum; + + RedTransAp *trans = redFsm->allocateTrans( condSpace, outConds, numConds, errCond ); + + curState->eofTrans = trans; +} + +void Reducer::resolveTargetStates( GenInlineList *inlineList ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case GenInlineItem::Goto: case GenInlineItem::Call: + case GenInlineItem::Ncall: case GenInlineItem::Next: + case GenInlineItem::Entry: + item->targState = allStates + item->targId; + break; + default: + break; + } + + if ( item->children != 0 ) + resolveTargetStates( item->children ); + } +} + +void Reducer::resolveTargetStates() +{ + for ( GenActionList::Iter a = actionList; a.lte(); a++ ) + resolveTargetStates( a->inlineList ); + +#if 0 + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofAction != 0 ) { + for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) + setLabelsNeeded( item->value->inlineList ); + } + + if ( st->eofTrans != 0 ) { + long condsFullSize = st->eofTrans->condFullSize(); + for ( int c = 0; c < condsFullSize; c++ ) { + RedCondPair *pair = st->eofTrans->outCond( c ); + setLabelsNeeded( pair ); + } + } +#endif +} + +bool Reducer::setAlphType( const HostLang *hostLang, const char *data ) +{ + HostType *alphType = findAlphTypeInternal( hostLang, data ); + if ( alphType == 0 ) + return false; + + return true; +} + +void Reducer::condSpaceItem( int cnum, long condActionId ) +{ + GenCondSpace *cond = allCondSpaces + cnum; + cond->condSet.append( allActions + condActionId ); +} + +void Reducer::initStateCondList( int snum, ulong length ) +{ + /* Could preallocate these, as we could with transitions. */ +} + +void Reducer::addStateCond( int snum, Key lowKey, Key highKey, long condNum ) +{ +} + +Key Reducer::findMaxKey() +{ + Key maxKey = fsm->ctx->keyOps->maxKey; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + assert( st->outSingle.length() == 0 ); + assert( st->defTrans == 0 ); + + long rangeLen = st->outRange.length(); + if ( rangeLen > 0 ) { + Key highKey = st->outRange[rangeLen-1].highKey; + if ( keyOps->gt( highKey, maxKey ) ) + maxKey = highKey; + } + } + return maxKey; +} + +void Reducer::actionActionRefs( RedAction *action ) +{ + action->numTransRefs += 1; + for ( GenActionTable::Iter item = action->key; item.lte(); item++ ) + item->value->numTransRefs += 1; +} + +void Reducer::transActionRefs( RedTransAp *trans ) +{ + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + if ( cond->action != 0 ) + actionActionRefs( cond->action ); + } + + if ( trans->condSpace != 0 ) + trans->condSpace->numTransRefs += 1; +} + +void Reducer::transListActionRefs( RedTransList &list ) +{ + for ( RedTransList::Iter rtel = list; rtel.lte(); rtel++ ) + transActionRefs( rtel->value ); +} + +void Reducer::findFinalActionRefs() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Rerence count out of single transitions. */ + transListActionRefs( st->outSingle ); + + /* Reference count out of range transitions. */ + transListActionRefs( st->outRange ); + + /* Reference count default transition. */ + if ( st->defTrans != 0 ) + transActionRefs( st->defTrans ); + + /* Reference count EOF transitions. */ + if ( st->eofTrans != 0 ) + transActionRefs( st->eofTrans ); + + /* Reference count to state actions. */ + if ( st->toStateAction != 0 ) { + st->toStateAction->numToStateRefs += 1; + for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) + item->value->numToStateRefs += 1; + } + + /* Reference count from state actions. */ + if ( st->fromStateAction != 0 ) { + st->fromStateAction->numFromStateRefs += 1; + for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ ) + item->value->numFromStateRefs += 1; + } + + /* Reference count EOF actions. */ + if ( st->eofAction != 0 ) { + st->eofAction->numEofRefs += 1; + for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) + item->value->numEofRefs += 1; + } + + if ( st->nfaTargs != 0 ) { + for ( RedNfaTargs::Iter nt = *st->nfaTargs; nt.lte(); nt++ ) { + + if ( nt->push != 0 ) { + nt->push->numNfaPushRefs += 1; + for ( GenActionTable::Iter item = nt->push->key; item.lte(); item++ ) + item->value->numNfaPushRefs += 1; + } + + if ( nt->popTest != 0 ) { + nt->popTest->numNfaPopTestRefs += 1; + for ( GenActionTable::Iter item = nt->popTest->key; item.lte(); item++ ) + item->value->numNfaPopTestRefs += 1; + } + } + } + } +} + +void Reducer::analyzeAction( GenAction *act, GenInlineList *inlineList ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + /* Only consider actions that are referenced. */ + if ( act->numRefs() > 0 ) { + if ( item->type == GenInlineItem::Goto || item->type == GenInlineItem::GotoExpr ) + { + redFsm->bAnyActionGotos = true; + } + else if ( item->type == GenInlineItem::Call || item->type == GenInlineItem::CallExpr ) { + redFsm->bAnyActionCalls = true; + } + else if ( item->type == GenInlineItem::Ncall || item->type == GenInlineItem::NcallExpr ) { + redFsm->bAnyActionCalls = true; + } + else if ( item->type == GenInlineItem::Ret ) + redFsm->bAnyActionRets = true; + else if ( item->type == GenInlineItem::Nret ) + redFsm->bAnyActionNrets = true; + else if ( item->type == GenInlineItem::LmInitAct || + item->type == GenInlineItem::LmSetActId || + item->type == GenInlineItem::LmSwitch ) + { + redFsm->bUsingAct = true; + } + + /* Any by value control in all actions? */ + if ( item->type == GenInlineItem::CallExpr || item->type == GenInlineItem::GotoExpr ) + redFsm->bAnyActionByValControl = true; + } + + /* Check for various things in regular actions. */ + if ( act->numTransRefs > 0 || act->numToStateRefs > 0 || act->numFromStateRefs > 0 ) { + /* Any returns in regular actions? */ + if ( item->type == GenInlineItem::Ret || item->type == GenInlineItem::Nret ) + redFsm->bAnyRegActionRets = true; + + /* Any next statements in the regular actions? */ + if ( item->type == GenInlineItem::Next || item->type == GenInlineItem::NextExpr || + item->type == GenInlineItem::Ncall || item->type == GenInlineItem::NcallExpr || + item->type == GenInlineItem::Nret ) + redFsm->bAnyRegNextStmt = true; + + /* Any by value control in regular actions? */ + if ( item->type == GenInlineItem::CallExpr || item->type == GenInlineItem::GotoExpr ) + redFsm->bAnyRegActionByValControl = true; + + /* Any references to the current state in regular actions? */ + if ( item->type == GenInlineItem::Curs ) + redFsm->bAnyRegCurStateRef = true; + + if ( item->type == GenInlineItem::Break ) + redFsm->bAnyRegBreak = true; + + if ( item->type == GenInlineItem::Nbreak ) + redFsm->bAnyRegNbreak = true; + } + + if ( item->children != 0 ) + analyzeAction( act, item->children ); + } +} + +void Reducer::analyzeActionList( RedAction *redAct, GenInlineList *inlineList ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + /* Any next statements in the action table? */ + if ( item->type == GenInlineItem::Next || item->type == GenInlineItem::NextExpr || + item->type == GenInlineItem::Ncall || item->type == GenInlineItem::NcallExpr || + item->type == GenInlineItem::Nret ) + redAct->bAnyNextStmt = true; + + /* Any references to the current state. */ + if ( item->type == GenInlineItem::Curs ) + redAct->bAnyCurStateRef = true; + + if ( item->type == GenInlineItem::Break ) + redAct->bAnyBreakStmt = true; + + if ( item->type == GenInlineItem::NfaWrapConds ) + item->condSpace->numNfaRefs += 1; + + if ( item->children != 0 ) + analyzeActionList( redAct, item->children ); + } +} + +/* Assign ids to referenced actions. */ +void Reducer::assignActionIds() +{ + int nextActionId = 0; + for ( GenActionList::Iter act = actionList; act.lte(); act++ ) { + /* Only ever interested in referenced actions. */ + if ( act->numRefs() > 0 ) + act->actionId = nextActionId++; + } +} + +void Reducer::setValueLimits() +{ + redFsm->maxSingleLen = 0; + redFsm->maxRangeLen = 0; + redFsm->maxKeyOffset = 0; + redFsm->maxIndexOffset = 0; + redFsm->maxActListId = 0; + redFsm->maxActionLoc = 0; + redFsm->maxActArrItem = 0; + redFsm->maxSpan = 0; + redFsm->maxFlatIndexOffset = 0; + redFsm->maxCondSpaceId = 0; + + /* In both of these cases the 0 index is reserved for no value, so the max + * is one more than it would be if they started at 0. */ + redFsm->maxIndex = redFsm->transSet.length(); + redFsm->maxCond = condSpaceList.length(); + + /* The nextStateId - 1 is the last state id assigned. */ + redFsm->maxState = redFsm->nextStateId - 1; + + for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) { + if ( csi->condSpaceId > redFsm->maxCondSpaceId ) + redFsm->maxCondSpaceId = csi->condSpaceId; + } + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Maximum single length. */ + if ( st->outSingle.length() > redFsm->maxSingleLen ) + redFsm->maxSingleLen = st->outSingle.length(); + + /* Maximum range length. */ + if ( st->outRange.length() > redFsm->maxRangeLen ) + redFsm->maxRangeLen = st->outRange.length(); + + /* The key offset index offset for the state after last is not used, skip it.. */ + if ( ! st.last() ) { + redFsm->maxKeyOffset += st->outSingle.length() + st->outRange.length()*2; + redFsm->maxIndexOffset += st->outSingle.length() + st->outRange.length() + 2; + } + + /* Max key span. */ + if ( st->transList != 0 ) { + unsigned long long span = fsm->ctx->keyOps->span( st->lowKey, st->highKey ); + if ( span > redFsm->maxSpan ) + redFsm->maxSpan = span; + } + + /* Max flat index offset. */ + if ( ! st.last() ) { + if ( st->transList != 0 ) + redFsm->maxFlatIndexOffset += fsm->ctx->keyOps->span( st->lowKey, st->highKey ); + redFsm->maxFlatIndexOffset += 1; + } + } + + for ( GenActionTableMap::Iter at = redFsm->actionMap; at.lte(); at++ ) { + /* Maximum id of action lists. */ + if ( at->actListId+1 > redFsm->maxActListId ) + redFsm->maxActListId = at->actListId+1; + + /* Maximum location of items in action array. */ + if ( at->location+1 > redFsm->maxActionLoc ) + redFsm->maxActionLoc = at->location+1; + + /* Maximum values going into the action array. */ + if ( at->key.length() > redFsm->maxActArrItem ) + redFsm->maxActArrItem = at->key.length(); + for ( GenActionTable::Iter item = at->key; item.lte(); item++ ) { + if ( item->value->actionId > redFsm->maxActArrItem ) + redFsm->maxActArrItem = item->value->actionId; + } + } +} + +/* Gather various info on the machine. */ +void Reducer::analyzeMachine() +{ + /* Find the true count of action references. */ + findFinalActionRefs(); + + /* Check if there are any calls in action code. */ + for ( GenActionList::Iter act = actionList; act.lte(); act++ ) { + /* Record the occurrence of various kinds of actions. */ + if ( act->numToStateRefs > 0 ) + redFsm->bAnyToStateActions = true; + if ( act->numFromStateRefs > 0 ) + redFsm->bAnyFromStateActions = true; + if ( act->numEofRefs > 0 ) + redFsm->bAnyEofActions = true; + if ( act->numTransRefs > 0 ) + redFsm->bAnyRegActions = true; + + if ( act->numNfaPushRefs > 0 ) { + redFsm->bAnyNfaPushPops = true; + redFsm->bAnyNfaPushes = true; + } + + if ( act->numNfaPopActionRefs > 0 ) { + redFsm->bAnyNfaPushPops = true; + redFsm->bAnyNfaPops = true; + } + + if ( act->numNfaPopTestRefs > 0 ) { + redFsm->bAnyNfaPushPops = true; + redFsm->bAnyNfaPops = true; + } + + /* Recurse through the action's parse tree looking for various things. */ + analyzeAction( act, act->inlineList ); + } + + /* Analyze reduced action lists. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + for ( GenActionTable::Iter act = redAct->key; act.lte(); act++ ) + if ( act->value->inlineList != 0 ) + analyzeActionList( redAct, act->value->inlineList ); + } + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) + redFsm->bAnyNfaStates = true; + } + + /* Find states that have transitions with actions that have next + * statements. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Check any actions out of outSinge. */ + for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + if ( cond->action != 0 && cond->action->anyCurStateRef() ) + st->bAnyRegCurStateRef = true; + } + } + + /* Check any actions out of outRange. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + if ( cond->action != 0 && cond->action->anyCurStateRef() ) + st->bAnyRegCurStateRef = true; + } + } + + /* Check any action out of default. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + if ( cond->action != 0 && cond->action->anyCurStateRef() ) + st->bAnyRegCurStateRef = true; + } + } + + if ( st->eofTrans != 0 ) + redFsm->bAnyEofTrans = true; + } + + for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) { + GenCondSpace *condSpace = csi; + + if ( condSpace->numTransRefs > 0 ) + redFsm->bAnyTransCondRefs = true; + + if ( condSpace->numNfaRefs > 0 ) + redFsm->bAnyNfaCondRefs = true; + } + + /* Assign ids to actions that are referenced. */ + assignActionIds(); + + /* Set the maximums of various values used for deciding types. */ + setValueLimits(); +} + +void CodeGenData::genOutputLineDirective( std::ostream &out ) const +{ + std::streambuf *sbuf = out.rdbuf(); + output_filter *filter = dynamic_cast(sbuf); + if ( filter != 0 ) + (*genLineDirective)( out, lineDirectives, filter->line + 1, filter->fileName ); +} + +void CodeGenData::write_option_error( InputLoc &loc, std::string arg ) +{ + red->id->warning(loc) << "unrecognized write option \"" << arg << "\"" << std::endl; +} + +void CodeGenData::writeClear() +{ + clear(); + + /* Delete all the nodes in the action list. Will cause all the + * string data that represents the actions to be deallocated. */ + red->fsm->ctx->actionList.empty(); + + delete red->fsm; + red->fsm = 0; + + // red->pd->graphDict.empty(); + + cleared = true; +} + +void CodeGenData::collectReferences() +{ + /* Do this once only. */ + if ( !referencesCollected ) { + referencesCollected = true; + + /* Nullify the output and execute the write. We use this pass to collect references. */ + nullbuf nb; + std::streambuf *filt = out.rdbuf( &nb ); + writeExec(); + + /* Restore the output for whatever writing comes next. */ + out.rdbuf( filt ); + } +} + +void CodeGenData::writeStatement( InputLoc &loc, int nargs, + std::vector &args, bool generateDot, const HostLang *hostLang ) +{ + /* Start write generation on a fresh line. */ + out << '\n'; + + if ( cleared ) { + red->id->error(loc) << "write statement following a clear is invalid" << std::endl; + return; + } + + genOutputLineDirective( out ); + + if ( args[0] == "data" ) { + for ( int i = 1; i < nargs; i++ ) { + if ( args[i] == "noerror" ) + noError = true; + else if ( args[i] == "noprefix" ) + noPrefix = true; + else if ( args[i] == "nofinal" ) + noFinal = true; + else + write_option_error( loc, args[i] ); + } + + if ( red->id->printStatistics ) { + red->id->stats() << "fsm-name\t" << fsmName << std::endl; + red->id->stats() << "fsm-states\t" << redFsm->stateList.length() << std::endl; + } + + collectReferences(); + writeData(); + statsSummary(); + } + else if ( args[0] == "init" ) { + for ( int i = 1; i < nargs; i++ ) { + if ( args[i] == "nocs" ) + noCS = true; + else + write_option_error( loc, args[i] ); + } + writeInit(); + } + else if ( args[0] == "exec" ) { + for ( int i = 1; i < nargs; i++ ) { + if ( args[i] == "noend" ) + noEnd = true; + else + write_option_error( loc, args[i] ); + } + collectReferences(); + writeExec(); + } + else if ( args[0] == "exports" ) { + for ( int i = 1; i < nargs; i++ ) + write_option_error( loc, args[i] ); + writeExports(); + } + else if ( args[0] == "start" ) { + for ( int i = 1; i < nargs; i++ ) + write_option_error( loc, args[i] ); + writeStart(); + } + else if ( args[0] == "first_final" ) { + for ( int i = 1; i < nargs; i++ ) + write_option_error( loc, args[i] ); + writeFirstFinal(); + } + else if ( args[0] == "error" ) { + for ( int i = 1; i < nargs; i++ ) + write_option_error( loc, args[i] ); + writeError(); + } + else if ( args[0] == "clear" ) { + for ( int i = 1; i < nargs; i++ ) + write_option_error( loc, args[i] ); + writeClear(); + } + else { + /* EMIT An error here. */ + red->id->error(loc) << "unrecognized write command \"" << + args[0] << "\"" << std::endl; + } +} diff --git a/src/libfsm/gendata.h b/src/libfsm/gendata.h new file mode 100644 index 00000000..f34f2629 --- /dev/null +++ b/src/libfsm/gendata.h @@ -0,0 +1,477 @@ +/* + * Copyright 2005-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _GENDATA_H +#define _GENDATA_H + +#include +#include +#include +#include "config.h" +#include "redfsm.h" +#include "common.h" +#include "fsmgraph.h" + +/* Forwards. */ +struct TransAp; +struct FsmAp; +struct PdBase; +struct InputData; +struct FsmGbl; +struct GenInlineList; +struct InlineItem; + +struct RedActionTable +: + public AvlTreeEl +{ + RedActionTable( const ActionTable &key ) + : + key(key), + id(0) + { } + + const ActionTable &getKey() + { return key; } + + ActionTable key; + int id; +}; + +typedef AvlTree ActionTableMap; + +struct NextRedTrans +{ + Key lowKey, highKey; + TransAp *trans; + TransAp *next; + + void load() { + if ( trans != 0 ) { + next = trans->next; + lowKey = trans->lowKey; + highKey = trans->highKey; + } + } + + NextRedTrans( TransAp *t ) { + trans = t; + load(); + } + + void increment() { + trans = next; + load(); + } +}; + +struct RedBase +{ + RedBase( FsmGbl *id, FsmCtx *fsmCtx, FsmAp *fsm, std::string fsmName, int machineId ) + : + id(id), + fsmCtx(fsmCtx), + fsm(fsm), + fsmName(fsmName), + machineId(machineId), + keyOps(fsm->ctx->keyOps), + nextActionTableId(0) + { + } + + FsmGbl *id; + FsmCtx *fsmCtx; + FsmAp *fsm; + std::string fsmName; + int machineId; + + KeyOps *keyOps; + + ActionTableMap actionTableMap; + int nextActionTableId; +}; + +struct NameInst; +typedef DList GenActionList; + +typedef unsigned long ulong; + +void openHostBlock( char opener, InputData *id, std::ostream &out, const char *fileName, int line ); + +string itoa( int i ); + +struct Reducer + : public RedBase +{ + Reducer( FsmGbl *id, FsmCtx *fsmCtx, FsmAp *fsm, std::string fsmName, int machineId ) + : + RedBase( id, fsmCtx, fsm, fsmName, machineId ), + redFsm(0), + allActions(0), + allActionTables(0), + allConditions(0), + allCondSpaces(0), + allStates(0), + nameIndex(0), + startState(-1), + errState(-1), + getKeyExpr(0), + accessExpr(0), + prePushExpr(0), + postPopExpr(0), + nfaPrePushExpr(0), + nfaPostPopExpr(0), + pExpr(0), + peExpr(0), + eofExpr(0), + csExpr(0), + topExpr(0), + stackExpr(0), + actExpr(0), + tokstartExpr(0), + tokendExpr(0), + dataExpr(0), + hasLongestMatch(false) + { + } + + ~Reducer() + { + if ( redFsm != 0 ) + delete redFsm; + + delete[] allActions; + delete[] allActionTables; + delete[] allConditions; + delete[] allCondSpaces; + + actionTableMap.empty(); + + if ( getKeyExpr != 0 ) + delete getKeyExpr; + if ( accessExpr != 0 ) + delete accessExpr; + if ( prePushExpr != 0 ) + delete prePushExpr; + if ( postPopExpr != 0 ) + delete postPopExpr; + if ( nfaPrePushExpr != 0 ) + delete nfaPrePushExpr; + if ( nfaPostPopExpr != 0 ) + delete nfaPostPopExpr; + if ( pExpr != 0 ) + delete pExpr; + if ( peExpr != 0 ) + delete peExpr; + if ( eofExpr != 0 ) + delete eofExpr; + if ( csExpr != 0 ) + delete csExpr; + if ( topExpr != 0 ) + delete topExpr; + if ( stackExpr != 0 ) + delete stackExpr; + if ( actExpr != 0 ) + delete actExpr; + if ( tokstartExpr != 0 ) + delete tokstartExpr; + if ( tokendExpr != 0 ) + delete tokendExpr; + if ( dataExpr != 0 ) + delete dataExpr; + } + +protected: + /* Collected during parsing. */ + int curAction; + int curActionTable; + int curState; + + void makeKey( GenInlineList *outList, Key key ); + void makeText( GenInlineList *outList, InlineItem *item ); + void makeLmOnLast( GenInlineList *outList, InlineItem *item ); + void makeLmOnNext( GenInlineList *outList, InlineItem *item ); + void makeLmOnLagBehind( GenInlineList *outList, InlineItem *item ); + void makeLmSwitch( GenInlineList *outList, InlineItem *item ); + void makeLmNfaOnLast( GenInlineList *outList, InlineItem *item ); + void makeLmNfaOnNext( GenInlineList *outList, InlineItem *item ); + void makeLmNfaOnEof( GenInlineList *outList, InlineItem *item ); + void makeActionExec( GenInlineList *outList, InlineItem *item ); + void makeSetTokend( GenInlineList *outList, long offset ); + void makeSetAct( GenInlineList *outList, long lmId ); + void makeSubList( GenInlineList *outList, InlineList *inlineList, + GenInlineItem::Type type ); + void makeTargetItem( GenInlineList *outList, NameInst *nameTarg, + GenInlineItem::Type type ); + void makeExecGetTokend( GenInlineList *outList ); + void makeActionList(); + void makeAction( Action *action ); + void makeActionTableList(); + void makeConditions(); + void makeEntryPoints(); + bool makeNameInst( std::string &out, NameInst *nameInst ); + void makeStateList(); + + void makeStateActions( StateAp *state ); + void makeEofTrans( StateAp *state ); + void makeTransList( StateAp *state ); + void makeTrans( Key lowKey, Key highKey, TransAp *trans ); + void newTrans( RedStateAp *state, Key lowKey, Key highKey, RedTransAp *trans ); + + void makeSubList( GenInlineList *outList, const InputLoc &loc, + InlineList *inlineList, GenInlineItem::Type type ); + + void createMachine(); + void initActionList( unsigned long length ); + void newAction( int anum, std::string name, + const InputLoc &loc, GenInlineList *inlineList ); + void initActionTableList( unsigned long length ); + void initStateList( unsigned long length ); + void setStartState( unsigned long startState ); + void setErrorState( unsigned long errState ); + void addEntryPoint( char *name, unsigned long entryState ); + void setId( int snum, int id ); + void setFinal( int snum ); + void initTransList( int snum, unsigned long length ); + + void newTrans( int snum, int tnum, Key lowKey, Key highKey, + GenCondSpace *gcs, RedTransAp *trans ); + + void finishTransList( int snum ); + void setStateActions( int snum, long toStateAction, + long fromStateAction, long eofAction ); + void setEofTrans( int snum, long targ, long eofAction ); + void setEofTrans( int snum, GenCondSpace *condSpace, + RedCondEl *outConds, int numConds, RedCondAp *errCond ); + void setForcedErrorState() + { redFsm->forcedErrorState = true; } + + void condSpaceItem( int cnum, long condActionId ); + void newCondSpace( int cnum, int condSpaceId ); + + void initStateCondList( int snum, ulong length ); + void addStateCond( int snum, Key lowKey, Key highKey, long condNum ); + + + void resolveTargetStates( GenInlineList *inlineList ); + void resolveTargetStates(); + + + /* Gather various info on the machine. */ + void analyzeActionList( RedAction *redAct, GenInlineList *inlineList ); + void analyzeAction( GenAction *act, GenInlineList *inlineList ); + void actionActionRefs( RedAction *action ); + void transListActionRefs( RedTransList &list ); + void transActionRefs( RedTransAp *trans ); + void findFinalActionRefs(); + + void setValueLimits(); + void assignActionIds(); + + + void appendTrans( TransListVect &outList, Key lowKey, Key highKey, TransAp *trans ); + void reduceActionTables(); + +public: + + Key findMaxKey(); + void makeMachine(); + void makeExports(); + void makeGenInlineList( GenInlineList *outList, InlineList *inList ); + bool setAlphType( const HostLang *hostLang, const char *data ); + void analyzeMachine(); + void make( const HostLang *hostLang, const HostType *alphType ); + + /* + * Collecting the machine. + */ + + RedFsmAp *redFsm; + GenAction *allActions; + RedAction *allActionTables; + Condition *allConditions; + GenCondSpace *allCondSpaces; + RedStateAp *allStates; + NameInst **nameIndex; + int startState; + int errState; + GenActionList actionList; + CondSpaceList condSpaceList; + + GenInlineList *getKeyExpr; + GenInlineList *accessExpr; + GenInlineExpr *prePushExpr; + GenInlineExpr *postPopExpr; + + GenInlineExpr *nfaPrePushExpr; + GenInlineExpr *nfaPostPopExpr; + + /* Overriding variables. */ + GenInlineList *pExpr; + GenInlineList *peExpr; + GenInlineList *eofExpr; + GenInlineList *csExpr; + GenInlineList *topExpr; + GenInlineList *stackExpr; + GenInlineList *actExpr; + GenInlineList *tokstartExpr; + GenInlineList *tokendExpr; + GenInlineList *dataExpr; + + EntryIdVect entryPointIds; + EntryNameVect entryPointNames; + bool hasLongestMatch; + ExportList exportList; + Action *curInlineAction; +}; + +struct CodeGenArgs +{ + CodeGenArgs( FsmGbl *id, Reducer *red, HostType *alphType, + int machineId, std::string sourceFileName, + std::string fsmName, std::ostream &out, + CodeStyle codeStyle ) + : + id(id), + red(red), + alphType(alphType), + machineId(machineId), + sourceFileName(sourceFileName), + fsmName(fsmName), + out(out), + codeStyle(codeStyle), + lineDirectives(true), + forceVar(false), + loopLabels(false) + {} + + FsmGbl *id; + Reducer *red; + HostType *alphType; + int machineId; + std::string sourceFileName; + std::string fsmName; + std::ostream &out; + CodeStyle codeStyle; + bool lineDirectives; + GenLineDirectiveT genLineDirective; + bool forceVar; + bool loopLabels; +}; + +struct CodeGenData +{ + CodeGenData( const CodeGenArgs &args ) + : + red(args.red), + redFsm(args.red->redFsm), + sourceFileName(args.sourceFileName), + fsmName(args.fsmName), + keyOps(red->keyOps), + alphType(args.alphType), + out(args.out), + noEnd(false), + noPrefix(false), + noFinal(false), + noError(false), + noCS(false), + lineDirectives(args.lineDirectives), + cleared(false), + referencesCollected(false), + genLineDirective(args.id->hostLang->genLineDirective) + { + } + + /* + * The interface to the code generator. + */ + virtual void genAnalysis() = 0; + + /* These are invoked by writeStatement and are normally what are used to + * implement the code generators. */ + virtual void writeData() {}; + virtual void writeInit() {}; + virtual void writeExec() {}; + virtual void writeExports() {}; + virtual void writeStart() {}; + virtual void writeFirstFinal() {}; + virtual void writeError() {}; + virtual void writeClear(); + + /* Show some stats after a write data. */ + virtual void statsSummary() = 0; + + /* This can also be overridden to modify the processing of write + * statements. */ + virtual void writeStatement( InputLoc &loc, int nargs, + std::vector &args, bool generateDot, const HostLang *hostLang ); + + /********************/ + + virtual ~CodeGenData() + { + } + + void clear() + { + delete red->redFsm; + red->redFsm = 0; + } + + void collectReferences(); + +protected: + + Reducer *red; + RedFsmAp *redFsm; + std::string sourceFileName; + std::string fsmName; + KeyOps *keyOps; + HostType *alphType; + ostream &out; + + /* Write options. */ + bool noEnd; + bool noPrefix; + bool noFinal; + bool noError; + bool noCS; + + void write_option_error( InputLoc &loc, std::string arg ); + + bool lineDirectives; + bool cleared; + + bool referencesCollected; + + void genOutputLineDirective( std::ostream &out ) const; + GenLineDirectiveT genLineDirective; +}; + +/* Selects and constructs the codegen based on the output options. */ +CodeGenData *makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ); +CodeGenData *asm_makeCodeGen( const HostLang *hostLang, const CodeGenArgs &args ); + +typedef AvlMap CodeGenMap; +typedef AvlMapEl CodeGenMapEl; + +#endif diff --git a/src/libfsm/goto.cc b/src/libfsm/goto.cc new file mode 100644 index 00000000..610f44d1 --- /dev/null +++ b/src/libfsm/goto.cc @@ -0,0 +1,978 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "goto.h" +#include "redfsm.h" +#include "bstmap.h" +#include "gendata.h" + +#include + +using std::ostringstream; + +IpLabel *Goto::allocateLabels( IpLabel *labels, IpLabel::Type type, int n ) +{ + if ( labels == 0 ) { + labels = new IpLabel[n]; + for ( int id = 0; id < n; id++ ) { + labels[id].type = type; + labels[id].stid = id; + } + } + + return labels; +} + +void Goto::setTableState( TableArray::State state ) +{ + for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { + TableArray *tableArray = *i; + tableArray->setState( state ); + } +} + +/* Emit the goto to take for a given transition. */ +std::ostream &Goto::COND_GOTO( RedCondPair *cond ) +{ + out << "goto " << ctrLabel[cond->id].reference() << ";"; + return out; +} + +/* Emit the goto to take for a given transition. */ +std::ostream &Goto::TRANS_GOTO( RedTransAp *trans ) +{ + if ( trans->condSpace == 0 || trans->condSpace->condSet.length() == 0 ) { + /* Existing. */ + assert( trans->numConds() == 1 ); + RedCondPair *cond = trans->outCond( 0 ); + + /* Go to the transition which will go to the state. */ + out << "goto " << ctrLabel[cond->id].reference() << ";"; + } + else { + out << ck << " = 0;\n"; + for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { + out << "if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + out << " )\n" << ck << " += " << condValOffset << ";\n"; + } + CondKey lower = 0; + CondKey upper = trans->condFullSize() - 1; + COND_B_SEARCH( trans, lower, upper, 0, trans->numConds()-1 ); + + if ( trans->errCond() != 0 ) { + COND_GOTO( trans->errCond() ) << "\n"; + } + } + + return out; +} + +/* Write out the array of actions. */ +void Goto::taActions() +{ + actions.start(); + + actions.value( 0 ); + + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Write out the length, which will never be the last character. */ + actions.value( act->key.length() ); + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) + actions.value( item->value->actionId ); + } + + actions.finish(); +} + +void Goto::GOTO_HEADER( RedStateAp *state ) +{ + /* Label the state. */ + out << "case " << state->id << ":\n"; +} + + +void Goto::SINGLE_SWITCH( RedStateAp *state ) +{ + /* Load up the singles. */ + int numSingles = state->outSingle.length(); + RedTransEl *data = state->outSingle.data; + + if ( numSingles == 1 ) { + /* If there is a single single key then write it out as an if. */ + out << "if ( " << GET_KEY() << " == " << + KEY(data[0].lowKey) << " ) {\n"; + + /* Virtual function for writing the target of the transition. */ + TRANS_GOTO(data[0].value) << "\n"; + out << "}\n"; + } + else if ( numSingles > 1 ) { + /* Write out single keys in a switch if there is more than one. */ + out << "switch( " << GET_KEY() << " ) {\n"; + + /* Write out the single indices. */ + for ( int j = 0; j < numSingles; j++ ) { + out << "case " << KEY(data[j].lowKey) << ": {\n"; + TRANS_GOTO(data[j].value) << "\n"; + out << "}\n"; + } + + /* Close off the transition switch. */ + out << "}\n"; + } +} + +void Goto::RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ) +{ + /* Get the mid position, staying on the lower end of the range. */ + int mid = (low + high) >> 1; + RedTransEl *data = state->outRange.data; + + /* Determine if we need to look higher or lower. */ + bool anyLower = mid > low; + bool anyHigher = mid < high; + + /* Determine if the keys at mid are the limits of the alphabet. */ + bool limitLow = keyOps->eq( data[mid].lowKey, lower ); + bool limitHigh = keyOps->eq( data[mid].highKey, upper ); + + if ( anyLower && anyHigher ) { + /* Can go lower and higher than mid. */ + out << "if ( " << GET_KEY() << " < " << + KEY(data[mid].lowKey) << " ) {\n"; + RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); + out << "} else if ( " << GET_KEY() << " > " << + KEY(data[mid].highKey) << " ) {\n"; + RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); + out << "} else {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + else if ( anyLower && !anyHigher ) { + /* Can go lower than mid but not higher. */ + out << "if ( " << GET_KEY() << " < " << + KEY(data[mid].lowKey) << " ) {\n"; + RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); + + /* if the higher is the highest in the alphabet then there is no + * sense testing it. */ + if ( limitHigh ) { + out << "} else {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + else { + out << "} else if ( " << GET_KEY() << " <= " << + KEY(data[mid].highKey) << " ) {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + } + else if ( !anyLower && anyHigher ) { + /* Can go higher than mid but not lower. */ + out << "if ( " << GET_KEY() << " > " << + KEY(data[mid].highKey) << " ) {\n"; + RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); + + /* If the lower end is the lowest in the alphabet then there is no + * sense testing it. */ + if ( limitLow ) { + out << "} else {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + else { + out << "} else if ( " << GET_KEY() << " >= " << + KEY(data[mid].lowKey) << " ) {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + } + else { + /* Cannot go higher or lower than mid. It's mid or bust. What + * tests to do depends on limits of alphabet. */ + if ( !limitLow && !limitHigh ) { + out << "if ( " << KEY(data[mid].lowKey) << " <= " << + GET_KEY() << " && " << GET_KEY() << " <= " << + KEY(data[mid].highKey) << " ) {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + else if ( limitLow && !limitHigh ) { + out << "if ( " << GET_KEY() << " <= " << + KEY(data[mid].highKey) << " ) {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + else if ( !limitLow && limitHigh ) { + out << "if ( " << KEY(data[mid].lowKey) << " <= " << + GET_KEY() << " ) {\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + else { + /* Both high and low are at the limit. No tests to do. */ + out << "{\n"; + TRANS_GOTO(data[mid].value) << "\n"; + out << "}\n"; + } + } +} + +/* Write out a key from the fsm code gen. Depends on wether or not the key is + * signed. */ +string Goto::CKEY( CondKey key ) +{ + ostringstream ret; + ret << key.getVal(); + return ret.str(); +} + +void Goto::COND_B_SEARCH( RedTransAp *trans, CondKey lower, + CondKey upper, int low, int high ) +{ + /* Get the mid position, staying on the lower end of the range. */ + int mid = (low + high) >> 1; +// RedCondEl *data = trans->outCond(0); + + /* Determine if we need to look higher or lower. */ + bool anyLower = mid > low; + bool anyHigher = mid < high; + + CondKey midKey = trans->outCondKey( mid ); + RedCondPair *midTrans = trans->outCond( mid ); + + /* Determine if the keys at mid are the limits of the alphabet. */ + bool limitLow = midKey == lower; + bool limitHigh = midKey == upper; + + if ( anyLower && anyHigher ) { + /* Can go lower and higher than mid. */ + out << "if ( " << ck << " < " << + CKEY(midKey) << " ) {\n"; + COND_B_SEARCH( trans, lower, midKey-1, low, mid-1 ); + out << "} else if ( " << ck << " > " << + CKEY(midKey) << " ) {\n"; + COND_B_SEARCH( trans, midKey+1, upper, mid+1, high ); + out << "} else {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + else if ( anyLower && !anyHigher ) { + /* Can go lower than mid but not higher. */ + out << "if ( " << ck << " < " << + CKEY(midKey) << " ) {\n"; + COND_B_SEARCH( trans, lower, midKey-1, low, mid-1); + + /* if the higher is the highest in the alphabet then there is no + * sense testing it. */ + if ( limitHigh ) { + out << "} else {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + else { + out << "} else if ( " << ck << " <= " << + CKEY(midKey) << " ) {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + } + else if ( !anyLower && anyHigher ) { + /* Can go higher than mid but not lower. */ + out << "if ( " << ck << " > " << + CKEY(midKey) << " ) {\n"; + COND_B_SEARCH( trans, midKey+1, upper, mid+1, high ); + + /* If the lower end is the lowest in the alphabet then there is no + * sense testing it. */ + if ( limitLow ) { + out << "} else {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + else { + out << "} else if ( " << ck << " >= " << + CKEY(midKey) << " ) {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + } + else { + /* Cannot go higher or lower than mid. It's mid or bust. What + * tests to do depends on limits of alphabet. */ + if ( !limitLow && !limitHigh ) { + out << "if ( " << ck << " == " << + CKEY(midKey) << " ) {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + else if ( limitLow && !limitHigh ) { + out << "if ( " << ck << " <= " << + CKEY(midKey) << " ) {\n"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + else if ( !limitLow && limitHigh ) { + out << "if ( " << CKEY(midKey) << " <= " << ck << " )\n {"; + COND_GOTO(midTrans) << "\n"; + out << "}\n"; + } + else { + /* Both high and low are at the limit. No tests to do. */ + COND_GOTO(midTrans) << "\n"; + } + } +} + +void Goto::STATE_GOTO_ERROR() +{ + /* Bail out immediately. */ + out << " goto " << _again << ";\n"; +} + +void Goto::FROM_STATE_ACTION_EMIT( RedStateAp *state ) +{ + if ( state->fromStateAction != 0 ) { + /* Write every action in the list. */ + for ( GenActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( state->id, false, + state->fromStateAction->anyNextStmt() ) ); + out << "\n"; + } + } +} + +std::ostream &Goto::STATE_CASES() +{ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Writing code above state gotos. */ + GOTO_HEADER( st ); + + FROM_STATE_ACTION_EMIT( st ); + + if ( !noEnd && eof ) { + out << + "if ( " << P() << " == " << vEOF() << " ) {\n"; + + if ( st->eofTrans != 0 ) + TRANS_GOTO( st->eofTrans ); + + out << + " goto " << _again << ";\n" + "}\n" + "else {\n"; + } + + if ( st == redFsm->errState ) + STATE_GOTO_ERROR(); + else { + /* Try singles. */ + if ( st->outSingle.length() > 0 ) + SINGLE_SWITCH( st ); + + /* Default case is to binary search for the ranges, if that fails then */ + if ( st->outRange.length() > 0 ) { + RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey, + 0, st->outRange.length() - 1 ); + } + + /* Write the default transition. */ + TRANS_GOTO( st->defTrans ) << "\n"; + } + + if ( !noEnd && eof ) { + out << + "}\n"; + } + } + return out; +} + +std::ostream &Goto::TRANSITION( RedCondPair *pair ) +{ + /* Write the label for the transition so it can be jumped to. */ + if ( ctrLabel[pair->id].isReferenced ) + out << "_ctr" << pair->id << ": "; + + /* Destination state. */ + if ( pair->action != 0 && pair->action->anyCurStateRef() ) + out << ps << " = " << vCS() << ";"; + out << vCS() << " = " << pair->targ->id << "; "; + + if ( pair->action != 0 ) { + /* Write out the transition func. */ + out << "goto f" << pair->action->actListId << ";\n"; + } + else { + /* No code to execute, just loop around. */ + out << "goto " << _again << ";\n"; + } + return out; +} + +std::ostream &Goto::TRANSITIONS() +{ + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + if ( trans->condSpace == 0 ) + TRANSITION( &trans->p ); + } + + for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) + TRANSITION( &cond->p ); + + return out; +} + +unsigned int Goto::TO_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->toStateAction != 0 ) + act = state->toStateAction->location+1; + return act; +} + +unsigned int Goto::FROM_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->fromStateAction != 0 ) + act = state->fromStateAction->location+1; + return act; +} + +unsigned int Goto::EOF_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->eofAction != 0 ) + act = state->eofAction->location+1; + return act; +} + +void Goto::taToStateActions() +{ + toStateActions.start(); + + /* Take one off for the psuedo start state. */ + int numStates = redFsm->stateList.length(); + unsigned int *vals = new unsigned int[numStates]; + memset( vals, 0, sizeof(unsigned int)*numStates ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + vals[st->id] = TO_STATE_ACTION(st); + + for ( int st = 0; st < redFsm->nextStateId; st++ ) { + /* Write any eof action. */ + toStateActions.value( vals[st] ); + } + delete[] vals; + + toStateActions.finish(); +} + +void Goto::taFromStateActions() +{ + fromStateActions.start(); + + /* Take one off for the psuedo start state. */ + int numStates = redFsm->stateList.length(); + unsigned int *vals = new unsigned int[numStates]; + memset( vals, 0, sizeof(unsigned int)*numStates ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + vals[st->id] = FROM_STATE_ACTION(st); + + for ( int st = 0; st < redFsm->nextStateId; st++ ) { + /* Write any eof action. */ + fromStateActions.value( vals[st] ); + } + delete[] vals; + + fromStateActions.finish(); +} + +void Goto::taEofActions() +{ + eofActions.start(); + + /* Take one off for the psuedo start state. */ + int numStates = redFsm->stateList.length(); + unsigned int *vals = new unsigned int[numStates]; + memset( vals, 0, sizeof(unsigned int)*numStates ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + vals[st->id] = EOF_ACTION(st); + + for ( int st = 0; st < redFsm->nextStateId; st++ ) { + /* Write any eof action. */ + eofActions.value( vals[st] ); + } + delete[] vals; + + eofActions.finish(); +} + +void Goto::taNfaOffsets() +{ + nfaOffsets.start(); + + /* Offset of zero means no NFA targs, real targs start at 1. */ + long offset = 1; + + /* Take one off for the psuedo start state. */ + int numStates = redFsm->stateList.length(); + unsigned int *vals = new unsigned int[numStates]; + memset( vals, 0, sizeof(unsigned int)*numStates ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs == 0 ) { + vals[st->id] = 0; + //nfaOffsets.value( 0 ); + } + else { + vals[st->id] = offset; + //nfaOffsets.value( offset ); + offset += 1 + st->nfaTargs->length(); + } + } + + for ( int st = 0; st < redFsm->nextStateId; st++ ) + nfaOffsets.value( vals[st] ); + delete[] vals; + + nfaOffsets.finish(); +} + +void Goto::taNfaTargs() +{ + nfaTargs.start(); + + /* Offset of zero means no NFA targs, put a filler there. */ + nfaTargs.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaTargs.value( st->nfaTargs->length() ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + nfaTargs.value( targ->state->id ); + } + } + + nfaTargs.finish(); +} + +/* These need to mirror nfa targs. */ +void Goto::taNfaPushActions() +{ + nfaPushActions.start(); + + nfaPushActions.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaPushActions.value( 0 ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_PUSH_ACTION( targ ); + } + } + + nfaPushActions.finish(); +} + +void Goto::taNfaPopTrans() +{ + nfaPopTrans.start(); + + nfaPopTrans.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaPopTrans.value( 0 ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_POP_TEST( targ ); + } + } + + nfaPopTrans.finish(); +} + +void Goto::EOF_CHECK( ostream &ret ) +{ + ret << + " if ( " << P() << " == " << PE() << " )\n" + " goto " << _test_eof << ";\n"; +} + +void Goto::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << "; "; + + if ( inFinish && !noEnd ) + EOF_CHECK( ret ); + + ret << "goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void Goto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + if ( inFinish && !noEnd ) + EOF_CHECK( ret ); + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void Goto::CURS( ostream &ret, bool inFinish ) +{ + ret << "(" << ps << ")"; +} + +void Goto::TARGS( ostream &ret, bool inFinish, int targState ) +{ + ret << "(" << vCS() << ")"; +} + +void Goto::NEXT( ostream &ret, int nextDest, bool inFinish ) +{ + ret << vCS() << " = " << nextDest << ";"; +} + +void Goto::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << vCS() << " = ("; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ");"; +} + +void Goto::CALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << + TOP() << " += 1;" << vCS() << " = " << + callDest << ";"; + + if ( inFinish && !noEnd ) + EOF_CHECK( ret ); + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void Goto::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << + TOP() << " += 1;" << vCS() << " = " << + callDest << "; " << CLOSE_GEN_BLOCK(); +} + +void Goto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << + vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + if ( inFinish && !noEnd ) + EOF_CHECK( ret ); + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void Goto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << + vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); +} + +void Goto::RET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + if ( inFinish && !noEnd ) + EOF_CHECK( ret ); + + ret << "goto " << _again << ";" << CLOSE_GEN_BLOCK(); +} + +void Goto::NRET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << CLOSE_GEN_BLOCK(); +} + +void Goto::BREAK( ostream &ret, int targState, bool csForced ) +{ + ret << OPEN_GEN_BLOCK() << P() << " += 1; " << "goto " << _out << "; " << CLOSE_GEN_BLOCK(); +} + +void Goto::NBREAK( ostream &ret, int targState, bool csForced ) +{ + ret << OPEN_GEN_BLOCK() << P() << " += 1; " << nbreak << " = 1; " << CLOSE_GEN_BLOCK(); +} + +void Goto::tableDataPass() +{ + if ( type == Loop ) + taActions(); + + taToStateActions(); + taFromStateActions(); + taEofActions(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void Goto::genAnalysis() +{ + /* For directly executable machines there is no required state + * ordering. Choose a depth-first ordering to increase the + * potential for fall-throughs. */ + redFsm->depthFirstOrdering(); + + /* Choose default transitions and the single transition. */ + redFsm->chooseDefaultSpan(); + + /* Choose single. */ + redFsm->moveSelectTransToSingle(); + + /* If any errors have occured in the input file then don't write anything. */ + if ( red->id->errorCount > 0 ) + return; + + /* Anlayze Machine will find the final action reference counts, among other + * things. We will use these in reporting the usage of fsm directives in + * action code. */ + red->analyzeMachine(); + + /* Run the analysis pass over the table data. */ + setTableState( TableArray::AnalyzePass ); + tableDataPass(); + + /* Switch the tables over to the code gen mode. */ + setTableState( TableArray::GeneratePass ); +} + +void Goto::writeData() +{ + if ( type == Loop ) { + if ( redFsm->anyActions() ) + taActions(); + } + + if ( redFsm->anyToStateActions() ) + taToStateActions(); + + if ( redFsm->anyFromStateActions() ) + taFromStateActions(); + + if ( redFsm->anyEofActions() ) + taEofActions(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); + + STATE_IDS(); +} + +void Goto::writeExec() +{ + int maxCtrId = redFsm->nextCondId > redFsm->nextTransId ? redFsm->nextCondId : redFsm->nextTransId; + ctrLabel = allocateLabels( ctrLabel, IpLabel::Ctr, maxCtrId ); + + out << "{\n"; + + DECLARE( INT(), cpc ); + DECLARE( INT(), ck ); + DECLARE( INT(), pop_test ); + DECLARE( INT(), nbreak ); + DECLARE( INT(), ps, " = 0" ); + DECLARE( INT(), new_recs ); + DECLARE( INT(), alt ); + DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); + DECLARE( UINT(), nacts ); + + out << "\n"; + + out << EMIT_LABEL( _resume ); + + /* Do we break out on no more input. */ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + if ( !noEnd ) { + if ( eof ) { + out << + " if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" + " goto " << _out << ";\n"; + } + else { + out << + " if ( " << P() << " == " << PE() << " )\n" + " goto " << _out << ";\n"; + } + } + + NFA_PUSH( vCS() ); + + out << + " switch ( " << vCS() << " ) {\n"; + STATE_CASES() << + " }\n" + "\n"; + TRANSITIONS() << + "\n"; + + if ( redFsm->anyRegActions() ) + EXEC_FUNCS() << "\n"; + + out << EMIT_LABEL( _again ); + + if ( !noEnd && eof ) { + out << + " if ( " << P() << " == " << vEOF() << " ) {\n" + " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" + " goto " << _out << ";\n" + " }\n" + " else {\n"; + } + + TO_STATE_ACTIONS(); + + if ( redFsm->errState != 0 ) { + out << + " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; + } + + out << + " " << P() << " += 1;\n" + " goto " << _resume << ";\n"; + + if ( redFsm->errState != 0 ) { + out << + " }\n"; + } + + if ( !noEnd && eof ) { + out << + " }\n"; + } + + if ( redFsm->anyNfaStates() ) { + out << + " if ( nfa_len == 0 )\n" + " goto " << _out << ";\n" + "\n" + " nfa_count += 1;\n" + " nfa_len -= 1;\n" + " " << P() << " = nfa_bp[nfa_len].p;\n" + ; + + if ( redFsm->bAnyNfaPops ) { + NFA_FROM_STATE_ACTION_EXEC(); + + NFA_POP_TEST_EXEC(); + + out << + " if ( " << pop_test << " )\n" + " " << vCS() << " = nfa_bp[nfa_len].state;\n" + " else\n" + " " << vCS() << " = " << ERROR_STATE() << ";\n"; + } + else { + out << + " " << vCS() << " = nfa_bp[nfa_len].state;\n"; + + } + + NFA_POST_POP(); + + out << "goto " << _resume << ";\n"; + } + + out << EMIT_LABEL( _out ); + + out << "}\n"; +} diff --git a/src/libfsm/goto.h b/src/libfsm/goto.h new file mode 100644 index 00000000..dcf13448 --- /dev/null +++ b/src/libfsm/goto.h @@ -0,0 +1,226 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _C_GOTO_H +#define _C_GOTO_H + +#include +#include "codegen.h" + +/* Forwards. */ +struct CodeGenData; +struct NameInst; +struct RedTransAp; +struct RedStateAp; +struct GenStateCond; + +struct IpLabel +{ + IpLabel() + : + type(None), + stid(0), + isReferenced(false) + {} + + enum Type + { + None = 1, + TestEof, + Ctr, + St, + Out, + Pop + }; + + std::string reference() + { + isReferenced = true; + return define(); + } + + std::string define() + { + std::stringstream ss; + switch ( type ) { + case None: break; + case TestEof: + ss << "_test_eof" << stid; + break; + case Ctr: + ss << "_ctr" << stid; + break; + case St: + ss << "_st" << stid; + break; + case Out: + ss << "_out" << stid; + break; + case Pop: + ss << "_pop" << stid; + break; + } + + return ss.str(); + } + + Type type; + int stid; + bool isReferenced; +}; + + +/* + * Goto driven fsm. + */ +class Goto + : public CodeGen +{ +public: + enum Type { + Loop = 1, + Exp, + Ip + }; + + Goto( const CodeGenArgs &args, Type type ) + : + CodeGen( args ), + type(type), + acts( "_acts" ), + nacts( "_nacts" ), + ck( "_ck" ), + nbreak( "_nbreak" ), + ps( "_ps" ), + _out("_out"), + _pop("_pop"), + _again("_again"), + _resume("_resume"), + _test_eof("_test_eof"), + actions( "actions", *this ), + toStateActions( "to_state_actions", *this ), + fromStateActions( "from_state_actions", *this ), + eofActions( "eof_actions", *this ), + ctrLabel(0) + {} + + void tableDataPass(); + virtual void genAnalysis(); + virtual void writeData(); + virtual void writeExec(); + + std::ostream &TRANSITION( RedCondPair *pair ); + + void FROM_STATE_ACTION_EMIT( RedStateAp *state ); + + std::ostream &STATE_CASES(); + std::ostream &TRANSITIONS(); + + Type type; + + Variable acts; + Variable nacts; + Variable ck; + Variable nbreak; + Variable ps; + + GotoLabel _out; + GotoLabel _pop; + GotoLabel _again; + GotoLabel _resume; + GotoLabel _test_eof; + + TableArray actions; + TableArray toStateActions; + TableArray fromStateActions; + TableArray eofActions; + + IpLabel *ctrLabel; + + void taActions(); + void taToStateActions(); + void taFromStateActions(); + void taEofActions(); + void taNfaTargs(); + void taNfaOffsets(); + void taNfaPushActions(); + void taNfaPopTrans(); + + void EOF_CHECK( ostream &ret ); + + void GOTO( ostream &ret, int gotoDest, bool inFinish ); + void CALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NEXT( ostream &ret, int nextDest, bool inFinish ); + void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void CURS( ostream &ret, bool inFinish ); + void TARGS( ostream &ret, bool inFinish, int targState ); + void RET( ostream &ret, bool inFinish ); + void NRET( ostream &ret, bool inFinish ); + void BREAK( ostream &ret, int targState, bool csForced ); + void NBREAK( ostream &ret, int targState, bool csForced ); + + virtual unsigned int TO_STATE_ACTION( RedStateAp *state ); + virtual unsigned int FROM_STATE_ACTION( RedStateAp *state ); + virtual unsigned int EOF_ACTION( RedStateAp *state ); + + virtual std::ostream &EXEC_FUNCS() = 0; + virtual std::ostream &TO_STATE_ACTION_SWITCH() = 0; + virtual std::ostream &FROM_STATE_ACTION_SWITCH() = 0; + virtual std::ostream &EOF_ACTION_SWITCH() = 0; + + std::ostream &ACTIONS_ARRAY(); + + void setTableState( TableArray::State ); + + virtual std::ostream &COND_GOTO( RedCondPair *trans ); + + string CKEY( CondKey key ); + void COND_B_SEARCH( RedTransAp *trans, CondKey lower, CondKey upper, int low, int high); + + virtual std::ostream &TRANS_GOTO( RedTransAp *trans ); + + void SINGLE_SWITCH( RedStateAp *state ); + void RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ); + + /* Called from STATE_GOTOS just before writing the gotos */ + virtual void GOTO_HEADER( RedStateAp *state ); + virtual void STATE_GOTO_ERROR(); + + virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ) = 0; + virtual void NFA_POP_TEST( RedNfaTarg *targ ) {} + virtual void NFA_FROM_STATE_ACTION_EXEC() = 0; + + void NFA_POP() {} + + virtual void FROM_STATE_ACTIONS() = 0; + virtual void TO_STATE_ACTIONS() = 0; + virtual void REG_ACTIONS() = 0; + virtual void EOF_ACTIONS() = 0; + + IpLabel *allocateLabels( IpLabel *labels, IpLabel::Type type, int n ); +}; + +#endif diff --git a/src/libfsm/gotoexp.cc b/src/libfsm/gotoexp.cc new file mode 100644 index 00000000..fe695401 --- /dev/null +++ b/src/libfsm/gotoexp.cc @@ -0,0 +1,207 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "gotoexp.h" +#include "redfsm.h" +#include "gendata.h" +#include "bstmap.h" +#include "parsedata.h" + +std::ostream &GotoExp::EXEC_FUNCS() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numTransRefs > 0 ) { + /* We are at the start of a glob, write the case. */ + out << "f" << redAct->actListId << ":\n"; + + if ( redFsm->anyRegNbreak() ) + out << nbreak << " = 0;\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, false, false ) ); + + if ( redFsm->anyRegNbreak() ) { + out << + " if ( " << nbreak << " == 1 )\n" + " goto " << _out << ";\n"; + } + + + out << "goto " << _again << ";\n"; + } + } + return out; +} + +/* Write out the function switch. This switch is keyed on the values + * of the func index. */ +std::ostream &GotoExp::TO_STATE_ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numToStateRefs > 0 ) { + /* Write the entry label. */ + out << "\t" << CASE( STR( redAct->actListId+1 ) ) << "{\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, false, false ) ); + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +/* Write out the function switch. This switch is keyed on the values + * of the func index. */ +std::ostream &GotoExp::FROM_STATE_ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numFromStateRefs > 0 ) { + /* Write the entry label. */ + out << "\t" << CASE( STR( redAct->actListId+1 ) ) << "{\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, false, false ) ); + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &GotoExp::EOF_ACTION_SWITCH() +{ + /* Loop the actions. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numEofRefs > 0 ) { + /* Write the entry label. */ + out << "\t" << CASE( STR( redAct->actListId+1 ) ) << "{\n"; + + /* Write each action in the list of action items. */ + for ( GenActionTable::Iter item = redAct->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, true, false ) ); + + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +unsigned int GotoExp::TO_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->toStateAction != 0 ) + act = state->toStateAction->actListId+1; + return act; +} + +unsigned int GotoExp::FROM_STATE_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->fromStateAction != 0 ) + act = state->fromStateAction->actListId+1; + return act; +} + +unsigned int GotoExp::EOF_ACTION( RedStateAp *state ) +{ + int act = 0; + if ( state->eofAction != 0 ) + act = state->eofAction->actListId+1; + return act; +} + +void GotoExp::NFA_PUSH_ACTION( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->push != 0 ) + act = targ->push->actListId+1; + nfaPushActions.value( act ); +} + +void GotoExp::NFA_POP_TEST( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->popTest != 0 ) + act = targ->popTest->actListId+1; + nfaPopTrans.value( act ); +} + + +void GotoExp::NFA_FROM_STATE_ACTION_EXEC() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " switch ( " << ARR_REF( fromStateActions ) << "[nfa_bp[nfa_len].state] ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + "\n"; + } +} + +void GotoExp::FROM_STATE_ACTIONS() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " switch ( " << ARR_REF( fromStateActions ) << "[" << vCS() << "] ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + "\n"; + } +} + +void GotoExp::TO_STATE_ACTIONS() +{ + if ( redFsm->anyToStateActions() ) { + out << + " switch ( " << ARR_REF( toStateActions ) << "[" << vCS() << "] ) {\n"; + TO_STATE_ACTION_SWITCH() << + " }\n" + "\n"; + } +} + +void GotoExp::REG_ACTIONS() +{ + +} + +void GotoExp::EOF_ACTIONS() +{ + if ( redFsm->anyEofActions() ) { + out << + " switch ( " << ARR_REF( eofActions ) << "[" << vCS() << "] ) {\n"; + EOF_ACTION_SWITCH() << + " }\n"; + } + +} diff --git a/src/libfsm/gotoexp.h b/src/libfsm/gotoexp.h new file mode 100644 index 00000000..ddb3f138 --- /dev/null +++ b/src/libfsm/gotoexp.h @@ -0,0 +1,75 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SWITCH_GOTO_EXP_H +#define SWITCH_GOTO_EXP_H + +#include +#include "goto.h" + +/* Forwards. */ +struct CodeGenData; + +/* + * class GotoExp + */ +class GotoExp + : public Goto +{ +public: + GotoExp( const CodeGenArgs &args ) + : Goto(args, Exp) {} + + virtual std::ostream &EXEC_FUNCS(); + virtual std::ostream &TO_STATE_ACTION_SWITCH(); + virtual std::ostream &FROM_STATE_ACTION_SWITCH(); + virtual std::ostream &EOF_ACTION_SWITCH(); + + unsigned int TO_STATE_ACTION( RedStateAp *state ); + unsigned int FROM_STATE_ACTION( RedStateAp *state ); + unsigned int EOF_ACTION( RedStateAp *state ); + + virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); + virtual void NFA_POP_TEST( RedNfaTarg *targ ); + virtual void NFA_FROM_STATE_ACTION_EXEC(); + + virtual void FROM_STATE_ACTIONS(); + virtual void TO_STATE_ACTIONS(); + virtual void REG_ACTIONS(); + virtual void EOF_ACTIONS(); +}; + +namespace C +{ + class GotoExp + : + public ::GotoExp + { + public: + GotoExp( const CodeGenArgs &args ) + : ::GotoExp( args ) + {} + }; +} + + +#endif diff --git a/src/libfsm/gotoloop.cc b/src/libfsm/gotoloop.cc new file mode 100644 index 00000000..1db3f2cd --- /dev/null +++ b/src/libfsm/gotoloop.cc @@ -0,0 +1,226 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "gotoloop.h" +#include "redfsm.h" +#include "bstmap.h" +#include "gendata.h" +#include "parsedata.h" + +std::ostream &GotoLoop::ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numTransRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; + ACTION( out, act, IlOpts( 0, false, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &GotoLoop::EOF_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numEofRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; + ACTION( out, act, IlOpts( 0, true, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &GotoLoop::FROM_STATE_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numFromStateRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; + ACTION( out, act, IlOpts( 0, false, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +std::ostream &GotoLoop::TO_STATE_ACTION_SWITCH() +{ + /* Walk the list of functions, printing the cases. */ + for ( GenActionList::Iter act = red->actionList; act.lte(); act++ ) { + /* Write out referenced actions. */ + if ( act->numToStateRefs > 0 ) { + /* Write the case label, the action and the case break. */ + out << "\t" << CASE( STR( act->actionId ) ) << "{\n"; + ACTION( out, act, IlOpts( 0, false, false ) ); + out << "\n\t" << CEND() << "\n}\n"; + } + } + + return out; +} + +void GotoLoop::NFA_PUSH_ACTION( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->push != 0 ) + act = targ->push->actListId+1; + nfaPushActions.value( act ); +} + +void GotoLoop::NFA_POP_TEST( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->popTest != 0 ) + act = targ->popTest->actListId+1; + nfaPopTrans.value( act ); +} + +std::ostream &GotoLoop::EXEC_FUNCS() +{ + /* Make labels that set acts and jump to execFuncs. Loop func indices. */ + for ( GenActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) { + if ( redAct->numTransRefs > 0 ) { + out << " f" << redAct->actListId << ": " << + "" << acts << " = " << OFFSET( ARR_REF( actions ), itoa( redAct->location+1 ) ) << ";" + " goto execFuncs;\n"; + } + } + + out << + "\n" + "execFuncs:\n"; + + if ( redFsm->anyRegNbreak() ) + out << nbreak << " = 0;\n"; + + out << + " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; + ACTION_SWITCH() << + " }\n" + " " << acts << " += 1;\n" + " " << nacts << " -= 1;\n" + " }\n" + "\n"; + + if ( redFsm->anyRegNbreak() ) { + out << + " if ( " << nbreak << " == 1 )\n" + " goto " << _out << ";\n"; + } + + out << + " goto _again;\n"; + return out; +} + +void GotoLoop::NFA_FROM_STATE_ACTION_EXEC() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), ARR_REF( fromStateActions ) + "[nfa_bp[nfa_len].state]" ) << ";\n" + " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << ";\n" + " " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + " " << nacts << " -= 1;\n" + " " << acts << " += 1;\n" + " }\n" + "\n"; + } +} + +void GotoLoop::FROM_STATE_ACTIONS() +{ + if ( redFsm->anyFromStateActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), + ARR_REF( fromStateActions ) + "[" + vCS() + "]" ) << ";\n" + " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << "; " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; + FROM_STATE_ACTION_SWITCH() << + " }\n" + " " << acts << " += 1;\n" + " " << nacts << " -= 1;\n" + " }\n" + "\n"; + } +} + +void GotoLoop::TO_STATE_ACTIONS() +{ + if ( redFsm->anyToStateActions() ) { + out << + " " << acts << " = " << OFFSET( ARR_REF( actions ), + ARR_REF( toStateActions ) + "[" + vCS() + "]" ) << ";\n" + " " << nacts << " = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << "; " << acts << " += 1;\n" + " while ( " << nacts << " > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "" + acts.ref() + "" ) << " ) {\n"; + TO_STATE_ACTION_SWITCH() << + " }\n" + " " << acts << " += 1;\n" + " " << nacts << " -= 1;\n" + " }\n" + "\n"; + } +} + +void GotoLoop::REG_ACTIONS() +{ +} + +void GotoLoop::EOF_ACTIONS() +{ + if ( redFsm->anyEofActions() ) { + out << + " " << INDEX( ARR_TYPE( actions ), "__acts" ) << ";\n" + " " << UINT() << " __nacts;\n" + " __acts = " << OFFSET( ARR_REF( actions ), + ARR_REF( eofActions ) + "[" + vCS() + "]" ) << ";\n" + " __nacts = " << CAST( UINT() ) << DEREF( ARR_REF( actions ), "__acts" ) << "; __acts += 1;\n" + " while ( __nacts > 0 ) {\n" + " switch ( " << DEREF( ARR_REF( actions ), "__acts" ) << " ) {\n"; + EOF_ACTION_SWITCH() << + " }\n" + " __acts += 1;\n" + " __nacts -= 1;\n" + " }\n"; + } +} diff --git a/src/libfsm/gotoloop.h b/src/libfsm/gotoloop.h new file mode 100644 index 00000000..68c43ce2 --- /dev/null +++ b/src/libfsm/gotoloop.h @@ -0,0 +1,72 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SWITCH_GOTO_LOOP_H +#define SWITCH_GOTO_LOOP_H + +#include +#include "goto.h" + +/* Forwards. */ +struct CodeGenData; +struct NameInst; +struct RedTransAp; +struct RedStateAp; +struct GenStateCond; + +class GotoLoop + : public Goto +{ +public: + GotoLoop( const CodeGenArgs &args ) + : Goto(args, Loop) {} + + virtual std::ostream &ACTION_SWITCH(); + virtual std::ostream &EXEC_FUNCS(); + virtual std::ostream &TO_STATE_ACTION_SWITCH(); + virtual std::ostream &FROM_STATE_ACTION_SWITCH(); + virtual std::ostream &EOF_ACTION_SWITCH(); + + virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ); + virtual void NFA_POP_TEST( RedNfaTarg *targ ); + virtual void NFA_FROM_STATE_ACTION_EXEC(); + + virtual void FROM_STATE_ACTIONS(); + virtual void TO_STATE_ACTIONS(); + virtual void REG_ACTIONS(); + virtual void EOF_ACTIONS(); +}; + +namespace C +{ + class GotoLoop + : + public ::GotoLoop + { + public: + GotoLoop( const CodeGenArgs &args ) + : ::GotoLoop( args ) + {} + }; +} + +#endif diff --git a/src/libfsm/idbase.cc b/src/libfsm/idbase.cc new file mode 100644 index 00000000..38850b56 --- /dev/null +++ b/src/libfsm/idbase.cc @@ -0,0 +1,421 @@ +/* + * Copyright 2008-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "fsmgraph.h" +#include "parsedata.h" +#include "action.h" + +/* Error reporting format. */ +ErrorFormat errorFormat = ErrorFormatGNU; + +void FsmCtx::finalizeInstance( FsmAp *graph ) +{ + /* Resolve any labels that point to multiple states. Any labels that are + * still around are referenced only by gotos and calls and they need to be + * made into deterministic entry points. */ + graph->deterministicEntry(); + + /* + * All state construction is now complete. + */ + + /* Transfer actions from the out action tables to eof action tables. */ + for ( StateSet::Iter state = graph->finStateSet; state.lte(); state++ ) + graph->transferOutActions( *state ); + + /* Transfer global error actions. */ + for ( StateList::Iter state = graph->stateList; state.lte(); state++ ) + graph->transferErrorActions( state, 0 ); + + if ( fsmGbl->wantDupsRemoved ) + graph->removeActionDups(); + + /* Remove unreachable states. There should be no dead end states. The + * subtract and intersection operators are the only places where they may + * be created and those operators clean them up. */ + graph->removeUnreachableStates(); + + /* No more fsm operations are to be done. Action ordering numbers are + * no longer of use and will just hinder minimization. Clear them. */ + graph->nullActionKeys(); + + /* Transition priorities are no longer of use. We can clear them + * because they will just hinder minimization as well. Clear them. */ + graph->clearAllPriorities(); + + if ( graph->ctx->minimizeOpt != MinimizeNone ) { + /* Minimize here even if we minimized at every op. Now that function + * keys have been cleared we may get a more minimal fsm. */ + switch ( graph->ctx->minimizeLevel ) { + #ifdef TO_UPGRADE_CONDS + case MinimizeApprox: + graph->minimizeApproximate(); + break; + #endif + #ifdef TO_UPGRADE_CONDS + case MinimizeStable: + graph->minimizeStable(); + break; + #endif + case MinimizePartition1: + graph->minimizePartition1(); + break; + case MinimizePartition2: + graph->minimizePartition2(); + break; + } + } + + graph->compressTransitions(); + + createNfaActions( graph ); +} + +void FsmCtx::analyzeAction( Action *action, InlineList *inlineList ) +{ + /* FIXME: Actions used as conditions should be very constrained. */ + for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) { + if ( item->type == InlineItem::Call || item->type == InlineItem::CallExpr || + item->type == InlineItem::Ncall || item->type == InlineItem::NcallExpr ) + { + action->anyCall = true; + } + + /* Need to recurse into longest match items. */ + if ( item->type == InlineItem::LmSwitch ) { + FsmLongestMatch *lm = item->longestMatch; + for ( FsmLmPartList::Iter lmi = *lm->longestMatchList; lmi.lte(); lmi++ ) { + if ( lmi->action != 0 ) + analyzeAction( action, lmi->action->inlineList ); + } + } + + if ( item->type == InlineItem::LmOnLast || + item->type == InlineItem::LmOnNext || + item->type == InlineItem::LmOnLagBehind ) + { + FsmLongestMatchPart *lmi = item->longestMatchPart; + if ( lmi->action != 0 ) + analyzeAction( action, lmi->action->inlineList ); + } + + if ( item->children != 0 ) + analyzeAction( action, item->children ); + } +} + +/* Check actions for bad uses of fsm directives. We don't go inside longest + * match items in actions created by ragel, since we just want the user + * actions. */ +void FsmCtx::checkInlineList( Action *act, InlineList *inlineList ) +{ + for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) { + /* EOF checks. */ + if ( act->numEofRefs > 0 ) { + switch ( item->type ) { + /* Currently no checks. */ + default: + break; + } + } + + /* Recurse. */ + if ( item->children != 0 ) + checkInlineList( act, item->children ); + } +} + +void FsmCtx::checkAction( Action *action ) +{ + /* Check for actions with calls that are embedded within a longest match + * machine. */ + if ( !action->isLmAction && action->numRefs() > 0 && action->anyCall ) { + for ( NameInstVect::Iter ar = action->embedRoots; ar.lte(); ar++ ) { + NameInst *check = *ar; + while ( check != 0 ) { + if ( check->isLongestMatch ) { + fsmGbl->error(action->loc) << "within a scanner, fcall and fncall are permitted" + " only in pattern actions" << endl; + break; + } + check = check->parent; + } + } + } + + checkInlineList( action, action->inlineList ); +} + +void FsmCtx::analyzeGraph( FsmAp *graph ) +{ + for ( ActionList::Iter act = actionList; act.lte(); act++ ) + analyzeAction( act, act->inlineList ); + + for ( StateList::Iter st = graph->stateList; st.lte(); st++ ) { + /* The transition list. */ + for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) { + //if ( trans->condSpace != 0 ) { + // for ( CondSet::Iter sci = trans->condSpace->condSet; sci.lte(); sci++ ) + // (*sci)->numCondRefs += 1; + //} + + if ( trans->plain() ) { + for ( ActionTable::Iter at = trans->tdap()->actionTable; at.lte(); at++ ) + at->value->numTransRefs += 1; + } + else { + for ( CondList::Iter cond = trans->tcap()->condList; cond.lte(); cond++ ) { + for ( ActionTable::Iter at = cond->actionTable; at.lte(); at++ ) + at->value->numTransRefs += 1; + } + } + } + + for ( ActionTable::Iter at = st->toStateActionTable; at.lte(); at++ ) + at->value->numToStateRefs += 1; + + for ( ActionTable::Iter at = st->fromStateActionTable; at.lte(); at++ ) + at->value->numFromStateRefs += 1; + + for ( ActionTable::Iter at = st->eofActionTable; at.lte(); at++ ) + at->value->numEofRefs += 1; + + //for ( OutCondSet::Iter oci = st->outCondSet; oci.lte(); oci++ ) + // oci->action->numCondRefs += 1; + + if ( st->nfaOut != 0 ) { + for ( NfaTransList::Iter n = *st->nfaOut; n.lte(); n++ ) { + for ( ActionTable::Iter ati = n->pushTable; ati.lte(); ati++ ) + ati->value->numNfaRefs += 1; + + for ( ActionTable::Iter ati = n->restoreTable; ati.lte(); ati++ ) + ati->value->numNfaRefs += 1; + + for ( ActionTable::Iter ati = n->popAction; ati.lte(); ati++ ) + ati->value->numNfaRefs += 1; + + for ( ActionTable::Iter ati = n->popTest; ati.lte(); ati++ ) + ati->value->numNfaRefs += 1; + } + } + } + + /* Can't count on cond references in transitions, since we don't refcount + * the spaces. FIXME: That would be the proper solution. */ + for ( CondSpaceMap::Iter cs = condData->condSpaceMap; cs.lte(); cs++ ) { + for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ ) + (*csi)->numCondRefs += 1; + } + + /* Checks for bad usage of directives in action code. */ + for ( ActionList::Iter act = actionList; act.lte(); act++ ) + checkAction( act ); +} + +/* This create an action that refs the original embed roots, if the optWrap arg + * is supplied. */ +Action *FsmCtx::newNfaWrapAction( const char *name, InlineList *inlineList, Action *optWrap ) +{ + InputLoc loc; + loc.line = 1; + loc.col = 1; + loc.fileName = "NONE"; + + Action *action = new Action( loc, name, inlineList, nextCondId++ ); + + if ( optWrap != 0 ) + action->embedRoots.append( optWrap->embedRoots ); + + actionList.append( action ); + return action; +} + +void FsmCtx::createNfaActions( FsmAp *fsm ) +{ + for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) { + if ( st->nfaOut != 0 ) { + for ( NfaTransList::Iter n = *st->nfaOut; n.lte(); n++ ) { + /* Move pop restore actions into poptest. Wrap to override the + * condition-like testing. */ + for ( ActionTable::Iter ati = n->restoreTable; ati.lte(); ati++ ) { + n->popTest.setAction( ati->key, ati->value ); + } + + /* Move pop actions into pop test. Wrap to override the + * condition-like testing. */ + for ( ActionTable::Iter ati = n->popFrom; ati.lte(); ati++ ) { + + InlineList *il1 = new InlineList; + il1->append( new InlineItem( InputLoc(), + ati->value, InlineItem::NfaWrapAction ) ); + Action *wrap = newNfaWrapAction( "action_wrap", il1, ati->value ); + n->popTest.setAction( ORD_COND2, wrap ); + } + + /* Move condition evaluation into pop test. Wrap with condition + * execution. */ + if ( n->popCondSpace != 0 ) { + InlineList *il1 = new InlineList; + il1->append( new InlineItem( InputLoc(), + n->popCondSpace, n->popCondKeys, + InlineItem::NfaWrapConds ) ); + Action *wrap = newNfaWrapAction( "cond_wrap", il1, 0 ); + n->popTest.setAction( ORD_COND, wrap ); + } + + /* Move pop actions into pop test. Wrap to override the + * condition-like testing. */ + for ( ActionTable::Iter ati = n->popAction; ati.lte(); ati++ ) { + + InlineList *il1 = new InlineList; + il1->append( new InlineItem( InputLoc(), + ati->value, InlineItem::NfaWrapAction ) ); + Action *wrap = newNfaWrapAction( "action_wrap", il1, ati->value ); + n->popTest.setAction( ati->key, wrap ); + } + } + } + } +} + +void FsmCtx::prepareReduction( FsmAp *sectionGraph ) +{ + /* Decide if an error state is necessary. + * 1. There is an error transition + * 2. There is a gap in the transitions + * 3. The longest match operator requires it. */ + if ( lmRequiresErrorState || sectionGraph->hasErrorTrans() ) + sectionGraph->errState = sectionGraph->addState(); + + /* State numbers need to be assigned such that all final states have a + * larger state id number than all non-final states. This enables the + * first_final mechanism to function correctly. We also want states to be + * ordered in a predictable fashion. So we first apply a depth-first + * search, then do a stable sort by final state status, then assign + * numbers. */ + + sectionGraph->depthFirstOrdering(); + sectionGraph->sortStatesByFinal(); + sectionGraph->setStateNumbers( 0 ); +} + + +void translatedHostData( ostream &out, const std::string &data ) +{ + const char *p = data.c_str(); + for ( const char *c = p; *c != 0; ) { + if ( c[0] == '}' && ( c[1] == '@' || c[1] == '$' || c[1] == '=' ) ) { + out << "@}@" << c[1]; + c += 2; + } + else if ( c[0] == '@' ) { + out << "@@"; + c += 1; + } + // Have some escaping issues that these fix, but they lead to other problems. + // Can be reproduced by passing "={}" through ragel and adding --colm-backend + // else if ( c[0] == '=' ) { + // out << "@="; + // c += 1; + //} + // else if ( c[0] == '$' ) { + // out << "@$"; + // c += 1; + //} + else { + out << c[0]; + c += 1; + } + } +} + + +void FsmGbl::abortCompile( int code ) +{ + throw AbortCompile( code ); +} + +/* Print the opening to a warning in the input, then return the error ostream. */ +ostream &FsmGbl::warning( const InputLoc &loc ) +{ + ostream &err = std::cerr; + err << loc << ": warning: "; + return err; +} + +/* Print the opening to a program error, then return the error stream. */ +ostream &FsmGbl::error() +{ + errorCount += 1; + ostream &err = std::cerr; + err << PROGNAME ": "; + return err; +} + +ostream &FsmGbl::error( const InputLoc &loc ) +{ + errorCount += 1; + ostream &err = std::cerr; + err << loc << ": "; + return err; +} + +ostream &FsmGbl::error_plain() +{ + errorCount += 1; + ostream &err = std::cerr; + return err; +} + + +std::ostream &FsmGbl::stats() +{ + return std::cout; +} + +/* Requested info. */ +std::ostream &FsmGbl::info() +{ + return std::cout; +} + +ostream &operator<<( ostream &out, const InputLoc &loc ) +{ + assert( loc.fileName != 0 ); + switch ( errorFormat ) { + case ErrorFormatMSVC: + out << loc.fileName << "(" << loc.line; + if ( loc.col ) + out << "," << loc.col; + out << ")"; + break; + + default: + out << loc.fileName << ":" << loc.line; + if ( loc.col ) + out << ":" << loc.col; + break; + } + return out; +} + diff --git a/src/libfsm/idbase.h b/src/libfsm/idbase.h new file mode 100644 index 00000000..a34837a8 --- /dev/null +++ b/src/libfsm/idbase.h @@ -0,0 +1,7 @@ +#ifndef _IDBASE_H +#define _IDBASE_H + +void translatedHostData( ostream &out, const std::string &data ); + +#endif + diff --git a/src/libfsm/ipgoto.cc b/src/libfsm/ipgoto.cc new file mode 100644 index 00000000..3718ea91 --- /dev/null +++ b/src/libfsm/ipgoto.cc @@ -0,0 +1,764 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "ipgoto.h" +#include "redfsm.h" +#include "gendata.h" +#include "bstmap.h" +#include "parsedata.h" + +#include + +using std::ostringstream; + +void IpGoto::tableDataPass() +{ + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void IpGoto::genAnalysis() +{ + /* For directly executable machines there is no required state + * ordering. Choose a depth-first ordering to increase the + * potential for fall-throughs. */ + redFsm->depthFirstOrdering(); + + /* Choose default transitions and the single transition. */ + redFsm->chooseDefaultSpan(); + + /* Choose single. */ + redFsm->moveSelectTransToSingle(); + + /* If any errors have occured in the input file then don't write anything. */ + if ( red->id->errorCount > 0 ) + return; + + redFsm->setInTrans(); + + /* Anlayze Machine will find the final action reference counts, among other + * things. We will use these in reporting the usage of fsm directives in + * action code. */ + red->analyzeMachine(); + + /* Run the analysis pass over the table data. */ + setTableState( TableArray::AnalyzePass ); + tableDataPass(); + + /* Switch the tables over to the code gen mode. */ + setTableState( TableArray::GeneratePass ); +} + +bool IpGoto::useAgainLabel() +{ + return redFsm->anyActionRets() || + redFsm->anyActionByValControl() || + redFsm->anyRegNextStmt(); +} + +void IpGoto::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + ret << "goto " << stLabel[gotoDest].reference() << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::CALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << targState << + "; " << TOP() << "+= 1; "; + + ret << "goto " << stLabel[callDest].reference() << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << targState << + "; " << TOP() << "+= 1; " << vCS() << " = " << callDest << "; " << + CLOSE_GEN_BLOCK(); +} + +void IpGoto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << targState << "; " << TOP() << "+= 1;" << + vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + ret << " goto " << _again << ";"; + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << targState << "; " << TOP() << "+= 1;" << + vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); +} + +void IpGoto::RET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " + << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << "goto " << _again << ";" << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NRET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " + << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NEXT( ostream &ret, int nextDest, bool inFinish ) +{ + ret << vCS() << " = " << nextDest << ";"; +} + +void IpGoto::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << vCS() << " = ("; + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << ");"; +} + +void IpGoto::CURS( ostream &ret, bool inFinish ) +{ + ret << "(" << ps << ")"; +} + +void IpGoto::TARGS( ostream &ret, bool inFinish, int targState ) +{ + ret << targState; +} + +void IpGoto::BREAK( ostream &ret, int targState, bool csForced ) +{ + ret << OPEN_GEN_BLOCK() << P() << "+= 1; "; + if ( !csForced ) + ret << vCS() << " = " << targState << "; "; + ret << "goto " << _out << ";" << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NBREAK( ostream &ret, int targState, bool csForced ) +{ + ret << OPEN_GEN_BLOCK() << P() << "+= 1; "; + if ( !csForced ) + ret << vCS() << " = " << targState << "; "; + ret << nbreak << " = 1;" << CLOSE_GEN_BLOCK(); +} + +void IpGoto::NFA_PUSH_ACTION( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->push != 0 ) + act = targ->push->actListId+1; + nfaPushActions.value( act ); +} + +void IpGoto::NFA_POP_TEST( RedNfaTarg *targ ) +{ + int act = 0; + if ( targ->popTest != 0 ) + act = targ->popTest->actListId+1; + nfaPopTrans.value( act ); +} + + +bool IpGoto::IN_TRANS_ACTIONS( RedStateAp *state ) +{ + bool anyWritten = false; + + /* Emit any transitions that have actions and that go to this state. */ + for ( int it = 0; it < state->numInConds; it++ ) { + RedCondPair *trans = state->inConds[it]; + if ( trans->action != 0 ) { + /* Remember that we wrote an action so we know to write the + * line directive for going back to the output. */ + anyWritten = true; + + /* Write the label for the transition so it can be jumped to. */ + if ( ctrLabel[trans->id].isReferenced ) + out << "_ctr" << trans->id << ":\n"; + + /* If the action contains a next, then we must preload the current + * state since the action may or may not set it. */ + if ( trans->action->anyNextStmt() ) + out << " " << vCS() << " = " << trans->targ->id << ";\n"; + + if ( redFsm->anyRegNbreak() ) + out << nbreak << " = 0;\n"; + + /* Write each action in the list. */ + for ( GenActionTable::Iter item = trans->action->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( trans->targ->id, false, + trans->action->anyNextStmt() ) ); + out << "\n"; + } + + if ( redFsm->anyRegNbreak() ) { + out << + "if ( " << nbreak << " == 1 )\n" + " goto " << _out << ";\n"; + } + + + /* If the action contains a next then we need to reload, otherwise + * jump directly to the target state. */ + if ( trans->action->anyNextStmt() ) + out << "goto " << _again << ";\n"; + else + out << "goto " << stLabel[trans->targ->id].reference() << ";\n"; + } + } + + + return anyWritten; +} + +void IpGoto::GOTO_HEADER( RedStateAp *state ) +{ +} + +void IpGoto::STATE_GOTO_ERROR() +{ +} + + +/* Emit the goto to take for a given transition. */ +std::ostream &IpGoto::TRANS_GOTO( RedTransAp *trans ) +{ + if ( trans->condSpace == 0 || trans->condSpace->condSet.length() == 0 ) { + /* Existing. */ + assert( trans->numConds() == 1 ); + RedCondPair *cond = trans->outCond( 0 ); + if ( cond->action != 0 ) { + /* Go to the transition which will go to the state. */ + out << "goto " << ctrLabel[trans->p.id].reference() << ";"; + } + else { + /* Go directly to the target state. */ + out << "goto " << stLabel[cond->targ->id].reference() << ";"; + } + } + else { + out << ck << " = 0;\n"; + for ( GenCondSet::Iter csi = trans->condSpace->condSet; csi.lte(); csi++ ) { + out << "if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + out << " )\n" << ck << " += " << condValOffset << ";\n"; + } + CondKey lower = 0; + CondKey upper = trans->condFullSize() - 1; + COND_B_SEARCH( trans, lower, upper, 0, trans->numConds() - 1 ); + + if ( trans->errCond() != 0 ) { + COND_GOTO( trans->errCond() ) << "\n"; + } + } + + return out; +} + +/* Emit the goto to take for a given transition. */ +std::ostream &IpGoto::COND_GOTO( RedCondPair *cond ) +{ + /* Existing. */ + if ( cond->action != 0 ) { + /* Go to the transition which will go to the state. */ + out << "goto " << ctrLabel[cond->id].reference() << ";"; + } + else { + /* Go directly to the target state. */ + out << "goto " << stLabel[cond->targ->id].reference() << ";"; + } + + return out; +} + +std::ostream &IpGoto::EXIT_STATES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( outLabel[st->id].isReferenced ) { + out << outLabel[st->id].define() << ": " << vCS() << " = " << + st->id << "; goto " << _out << "; \n"; + } + if ( popLabel[st->id].isReferenced ) { + out << popLabel[st->id].define() << ": " << vCS() << " = " << + st->id << "; goto " << _pop << "; \n"; + } + } + return out; +} + +std::ostream &IpGoto::AGAIN_CASES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + out << + "case " << st->id << ": goto " << stLabel[st->id].reference() << ";\n"; + } + return out; +} + +std::ostream &IpGoto::STATE_GOTO_CASES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + out << "case " << st->id << ":\n"; + out << "goto st_case_" << st->id << ";\n"; + } + return out; +} + +void IpGoto::NFA_PUSH_ST( RedStateAp *state ) +{ + std::stringstream ss; + ss << state->id; + std::string _state = ss.str(); + + if ( redFsm->anyNfaStates() ) { + + if ( state->nfaTargs != 0 ) { + out << + "if ( " << ARR_REF( nfaOffsets ) << "[" << _state << "] != 0 ) {\n"; + + if ( red->nfaPrePushExpr != 0 ) { + out << + new_recs << " = " << state->nfaTargs->length() << ";\n"; + } + + if ( red->nfaPrePushExpr != 0 ) { + out << OPEN_HOST_BLOCK( red->nfaPrePushExpr ); + INLINE_LIST( out, red->nfaPrePushExpr->inlineList, 0, false, false ); + out << CLOSE_HOST_BLOCK(); + } + + int alt = 0; + for ( RedNfaTargs::Iter nt = *state->nfaTargs; nt.lte(); nt++ ) { + out << + " nfa_bp[nfa_len].state = " << nt->state->id << ";\n" + " nfa_bp[nfa_len].p = " << P() << ";\n"; + + if ( nt->popTest != 0 ) { + out << + " nfa_bp[nfa_len].popTrans = " << (nt->popTest->actListId+1) << ";\n"; + } + else if ( redFsm->bAnyNfaPops ) { + out << + " nfa_bp[nfa_len].popTrans = 0;\n"; + } + + if ( nt->push != 0 ) { + for ( GenActionTable::Iter item = nt->push->key; item.lte(); item++ ) + ACTION( out, item->value, IlOpts( 0, false, false ) ); + } + + out << + " nfa_len += 1;\n"; + + alt += 1; + } + + out << + "}\n"; + } + } +} + +std::ostream &IpGoto::STATE_GOTOS() +{ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + IN_TRANS_ACTIONS( st ); + + if ( stLabel[st->id].isReferenced ) + out << "_st" << st->id << ":\n"; + + /* Need to do this if the transition is an eof transition, or if + * the action contains fexec. Otherwise, no need. */ + if ( eof ) { + out << + "if ( " << P() << " == " << vEOF() << " )\n"; + + if ( st->isFinal || !redFsm->anyNfaStates() ) + out << "goto " << outLabel[st->id].reference() << ";\n"; + else + out << "goto " << popLabel[st->id].reference() << ";\n"; + } + + if ( st->toStateAction != 0 ) { + /* Write every action in the list. */ + for ( GenActionTable::Iter item = st->toStateAction->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( st->id, false, + st->toStateAction->anyNextStmt() ) ); + out << "\n"; + } + } + + if ( st == redFsm->errState ) { + out << "st_case_" << st->id << ":\n"; + + /* Break out here. */ + if ( !redFsm->anyNfaStates() ) + out << "goto " << outLabel[st->id].reference() << ";\n"; + else + out << "goto " << popLabel[st->id].reference() << ";\n"; + } + else { + + /* Advance and test buffer pos. */ + if ( st->labelNeeded ) { + out << + P() << "+= 1;\n"; + } + + /* Give the st a switch case. */ + out << "st_case_" << st->id << ":\n"; + + if ( !noEnd ) { + if ( eof ) { + out << + "if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" + " goto " << outLabel[st->id].reference() << ";\n"; + } + else { + out << + "if ( " << P() << " == " << PE() << " )\n" + " goto " << outLabel[st->id].reference() << ";\n"; + } + } + + + NFA_PUSH_ST( st ); + + if ( st->fromStateAction != 0 ) { + /* Write every action in the list. */ + for ( GenActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ ) { + ACTION( out, item->value, IlOpts( st->id, false, + st->fromStateAction->anyNextStmt() ) ); + out << "\n"; + } + } + + if ( !noEnd && eof ) { + out << + "if ( " << P() << " == " << vEOF() << " ) {\n"; + + if ( st->eofTrans != 0 ) + TRANS_GOTO( st->eofTrans ); + else { + if ( st->isFinal || !redFsm->anyNfaStates() ) + out << "goto " << outLabel[st->id].reference() << ";\n"; + else + out << "goto " << popLabel[st->id].reference() << ";\n"; + } + + out << + "}\n" + "else {\n"; + } + + /* Record the prev st if necessary. */ + if ( st->anyRegCurStateRef() ) + out << ps << " = " << st->id << ";\n"; + + /* Try singles. */ + if ( st->outSingle.length() > 0 ) + SINGLE_SWITCH( st ); + + /* Default case is to binary search for the ranges, if that fails then */ + if ( st->outRange.length() > 0 ) { + RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey, + 0, st->outRange.length() - 1 ); + } + + /* Write the default transition. */ + TRANS_GOTO( st->defTrans ) << "\n"; + + if ( !noEnd && eof ) { + out << + "}\n"; + } + } + } + return out; +} + +std::ostream &IpGoto::FINISH_CASES() +{ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + out << + "case " << st->id << ":\n"; + + TRANS_GOTO( st->eofTrans ); + } + } + + return out; +} + +void IpGoto::setLabelsNeeded( GenInlineList *inlineList ) +{ + for ( GenInlineList::Iter item = *inlineList; item.lte(); item++ ) { + switch ( item->type ) { + case GenInlineItem::Goto: + case GenInlineItem::Call: + case GenInlineItem::Ncall: { + /* Mark the target as needing a label. */ + item->targState->labelNeeded = true; + break; + } + default: break; + } + + if ( item->children != 0 ) + setLabelsNeeded( item->children ); + } +} + +void IpGoto::setLabelsNeeded( RedCondPair *pair ) +{ + /* If there is no action with a next statement, then the label will be + * needed. */ + if ( pair->action == 0 || !pair->action->anyNextStmt() ) + pair->targ->labelNeeded = true; + + /* Need labels for states that have goto or calls in action code + * invoked on characters (ie, not from out action code). */ + if ( pair->action != 0 ) { + /* Loop the actions. */ + for ( GenActionTable::Iter act = pair->action->key; act.lte(); act++ ) { + /* Get the action and walk it's tree. */ + setLabelsNeeded( act->value->inlineList ); + } + } +} + +/* Set up labelNeeded flag for each state. */ +void IpGoto::setLabelsNeeded() +{ + /* If we use the _again label, then we generate the _again switch, which + * uses all labels. */ + if ( useAgainLabel() ) { + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->labelNeeded = true; + } + else { + /* Do not use all labels by default, init all labelNeeded vars to false. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + st->labelNeeded = false; + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + if ( trans->condSpace == 0 ) + setLabelsNeeded( &trans->p ); + } + + for ( CondApSet::Iter cond = redFsm->condSet; cond.lte(); cond++ ) + setLabelsNeeded( &cond->p ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofAction != 0 ) { + for ( GenActionTable::Iter item = st->eofAction->key; item.lte(); item++ ) + setLabelsNeeded( item->value->inlineList ); + } + } + } +} + +void IpGoto::writeData() +{ + STATE_IDS(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void IpGoto::NFA_FROM_STATE_ACTION_EXEC() +{ +// if ( redFsm->anyFromStateActions() ) { +// /* Unimplemented feature. Don't have the from state actions array in +// * this mode. Need to add it, or to alter the NFA pop codegen to be +// * consistent with the mode. */ +// assert( false ); +// } +} + +void IpGoto::writeExec() +{ + int maxCtrId = redFsm->nextCondId > redFsm->nextTransId ? redFsm->nextCondId : redFsm->nextTransId; + + stLabel = allocateLabels( stLabel, IpLabel::St, redFsm->nextStateId ); + ctrLabel = allocateLabels( ctrLabel, IpLabel::Ctr, maxCtrId ); + outLabel = allocateLabels( outLabel, IpLabel::Out, redFsm->nextStateId ); + popLabel = allocateLabels( popLabel, IpLabel::Pop, redFsm->nextStateId ); + + /* Must set labels immediately before writing because we may depend on the + * noend write option. */ + setLabelsNeeded(); + + out << "{\n"; + + DECLARE( INT(), cpc ); + DECLARE( INT(), ck ); + DECLARE( INT(), pop_test ); + DECLARE( INT(), nbreak ); + DECLARE( INT(), ps ); + DECLARE( INT(), new_recs ); + DECLARE( INT(), alt ); + + if ( _again.isReferenced ) { + out << + " goto " << _resume << ";\n" + "\n"; + + out << EMIT_LABEL( _again ); + + out << + " switch ( " << vCS() << " ) {\n"; + AGAIN_CASES() << + " }\n" + "\n"; + + } + + out << EMIT_LABEL( _resume ); + + out << "switch ( " << vCS() << " ) {\n"; + + STATE_GOTO_CASES(); + + out << "}\n"; + + STATE_GOTOS(); + + EXIT_STATES(); + + out << EMIT_LABEL( _pop ); + + if ( redFsm->anyNfaStates() ) { + out << + "if ( nfa_len == 0 )\n" + " goto " << _out << ";\n" + "\n"; + + out << + "nfa_count += 1;\n" + "nfa_len -= 1;\n" << + P() << " = nfa_bp[nfa_len].p;\n"; + + if ( redFsm->bAnyNfaPops ) { + NFA_FROM_STATE_ACTION_EXEC(); + + NFA_POP_TEST_EXEC(); + + out << + "if ( " << pop_test << " )\n" + " " << vCS() << " = nfa_bp[nfa_len].state;\n" + "else\n" + " " << vCS() << " = " << ERROR_STATE() << ";\n"; + } + else { + out << + vCS() << " = nfa_bp[nfa_len].state;\n"; + + } + + NFA_POST_POP(); + + out << "goto " << _resume << ";\n"; + } + + out << EMIT_LABEL( _out ); + + out << + "}\n"; +} diff --git a/src/libfsm/ipgoto.h b/src/libfsm/ipgoto.h new file mode 100644 index 00000000..1ec51bbf --- /dev/null +++ b/src/libfsm/ipgoto.h @@ -0,0 +1,129 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IPGOTO_H +#define IPGOTO_H + +#include +#include "goto.h" + +/* Forwards. */ +struct CodeGenData; + +/* + * class FGotoCodeGen + */ +class IpGoto + : public Goto +{ +public: + IpGoto( const CodeGenArgs &args ) + : + Goto( args, Ip ), + stLabel(0), + ctrLabel(0), + outLabel(0), + popLabel(0) + {} + + std::ostream &EXIT_STATES(); + std::ostream &TRANS_GOTO( RedTransAp *trans ); + std::ostream &COND_GOTO( RedCondPair *trans ); + std::ostream &FINISH_CASES(); + std::ostream &AGAIN_CASES(); + std::ostream &STATE_GOTOS(); + std::ostream &STATE_GOTO_CASES(); + + /* unused. */ + virtual std::ostream &ACTION_SWITCH() { return out; } + virtual std::ostream &EXEC_FUNCS() { return out; } + virtual std::ostream &TO_STATE_ACTION_SWITCH() { return out; } + virtual std::ostream &FROM_STATE_ACTION_SWITCH() { return out; } + virtual std::ostream &EOF_ACTION_SWITCH() { return out; } + + /* Unused */ + virtual void FROM_STATE_ACTIONS() {} + virtual void TO_STATE_ACTIONS() {} + virtual void REG_ACTIONS() {} + virtual void EOF_ACTIONS() {} + + void GOTO( ostream &ret, int gotoDest, bool inFinish ); + void CALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NEXT( ostream &ret, int nextDest, bool inFinish ); + void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void RET( ostream &ret, bool inFinish ); + void NRET( ostream &ret, bool inFinish ); + void CURS( ostream &ret, bool inFinish ); + void TARGS( ostream &ret, bool inFinish, int targState ); + void BREAK( ostream &ret, int targState, bool csForced ); + void NBREAK( ostream &ret, int targState, bool csForced ); + + virtual void genAnalysis(); + virtual void writeData(); + virtual void writeExec(); + +protected: + bool useAgainLabel(); + + /* Called from Goto::STATE_GOTOS just before writing the gotos for + * each state. */ + bool IN_TRANS_ACTIONS( RedStateAp *state ); + void GOTO_HEADER( RedStateAp *state ); + void STATE_GOTO_ERROR(); + + /* Set up labelNeeded flag for each state. */ + void setLabelsNeeded( RedCondPair *pair ); + void setLabelsNeeded( GenInlineList *inlineList ); + void setLabelsNeeded(); + + void NFA_PUSH_ACTION( RedNfaTarg *targ ); + void NFA_POP_TEST( RedNfaTarg *targ ); + virtual void NFA_FROM_STATE_ACTION_EXEC(); + + void NFA_PUSH_ST( RedStateAp *state ); + + void tableDataPass(); + + IpLabel *stLabel; + IpLabel *ctrLabel; + IpLabel *outLabel; + IpLabel *popLabel; +}; + +namespace C +{ + class IpGoto + : + public ::IpGoto + { + public: + IpGoto( const CodeGenArgs &args ) + : ::IpGoto( args ) + {} + }; +} + +#endif diff --git a/src/libfsm/parsedata.h b/src/libfsm/parsedata.h new file mode 100644 index 00000000..4d11b1c7 --- /dev/null +++ b/src/libfsm/parsedata.h @@ -0,0 +1,27 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _PARSEDATA_H +#define _PARSEDATA_H + + +#endif diff --git a/src/libfsm/ragel-config.cmake.in b/src/libfsm/ragel-config.cmake.in new file mode 100644 index 00000000..8de5d2cb --- /dev/null +++ b/src/libfsm/ragel-config.cmake.in @@ -0,0 +1,3 @@ +# @_PACKAGE_NAME@-config.cmake Generated from ragel-config.cmake.in by cmake + +include("${CMAKE_CURRENT_LIST_DIR}/@_PACKAGE_NAME@-targets.cmake") diff --git a/src/libfsm/ragel.h b/src/libfsm/ragel.h new file mode 100644 index 00000000..c3fd6f22 --- /dev/null +++ b/src/libfsm/ragel.h @@ -0,0 +1,108 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _RAGEL_H +#define _RAGEL_H + +#include +#include +#include +#include +#include "vector.h" +#include "config.h" +#include "common.h" + +#define PROGNAME "ragel" + +#define MAIN_MACHINE "main" + +/* Target output style. */ +enum CodeStyle +{ + GenBinaryLoop, + GenBinaryExp, + GenFlatLoop, + GenFlatExp, + GenGotoLoop, + GenGotoExp, + GenSwitchLoop, + GenSwitchExp, + GenIpGoto +}; + +/* To what degree are machine minimized. */ +enum MinimizeLevel { + #ifdef TO_UPGRADE_CONDS + MinimizeApprox, + #endif + #ifdef TO_UPGRADE_CONDS + MinimizeStable, + #endif + MinimizePartition1, + MinimizePartition2 +}; + +enum MinimizeOpt { + MinimizeNone, + MinimizeEnd, + MinimizeMostOps, + MinimizeEveryOp +}; + +/* Target implementation */ +enum RubyImplEnum +{ + MRI, + Rubinius +}; + +/* Error reporting format. */ +enum ErrorFormat { + ErrorFormatGNU, + ErrorFormatMSVC, +}; + +extern ErrorFormat errorFormat; + + +struct colm_location; + +InputLoc makeInputLoc( const char *fileName, int line = 0, int col = 0 ); +InputLoc makeInputLoc( const struct colm_location *loc ); +std::ostream &operator<<( std::ostream &out, const InputLoc &loc ); + +void xmlEscapeHost( std::ostream &out, const char *data, long len ); + + +using std::endl; + +extern const char mainMachine[]; + +struct AbortCompile +{ + AbortCompile( int code ) + : code(code) {} + + int code; +}; + +#endif diff --git a/src/libfsm/redfsm.cc b/src/libfsm/redfsm.cc new file mode 100644 index 00000000..1b83e5b5 --- /dev/null +++ b/src/libfsm/redfsm.cc @@ -0,0 +1,1192 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "redfsm.h" +#include "avlmap.h" +#include "mergesort.h" +#include "fsmgraph.h" +#include +#include +#include + +using std::ostringstream; + +GenInlineItem::~GenInlineItem() +{ + if ( children != 0 ) { + children->empty(); + delete children; + } +} + +string GenAction::nameOrLoc() +{ + if ( name.empty() ) { + ostringstream ret; + ret << loc.line << ":" << loc.col; + return ret.str(); + } + else { + return name; + } +} + +RedFsmAp::RedFsmAp( FsmCtx *fsmCtx, int machineId ) +: + keyOps(fsmCtx->keyOps), + fsmCtx(fsmCtx), + machineId(machineId), + forcedErrorState(false), + nextActionId(0), + nextTransId(0), + nextCondId(0), + startState(0), + errState(0), + errTrans(0), + errCond(0), + firstFinState(0), + numFinStates(0), + bAnyToStateActions(false), + bAnyFromStateActions(false), + bAnyRegActions(false), + bAnyEofActions(false), + bAnyEofTrans(false), + bAnyEofActivity(false), + bAnyActionGotos(false), + bAnyActionCalls(false), + bAnyActionNcalls(false), + bAnyActionRets(false), + bAnyActionNrets(false), + bAnyActionByValControl(false), + bAnyRegActionRets(false), + bAnyRegActionByValControl(false), + bAnyRegNextStmt(false), + bAnyRegCurStateRef(false), + bAnyRegBreak(false), + bAnyRegNbreak(false), + bUsingAct(false), + bAnyNfaStates(false), + bAnyNfaPushPops(false), + bAnyNfaPushes(false), + bAnyNfaPops(false), + bAnyTransCondRefs(false), + bAnyNfaCondRefs(false), + nextClass(0), + classMap(0) +{ +} + +RedFsmAp::~RedFsmAp() +{ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + delete[] st->transList; + if ( st->nfaTargs != 0 ) + delete st->nfaTargs; + if ( st->inConds != 0 ) + delete[] st->inConds; + if ( st->inCondTests != 0 ) + delete[] st->inCondTests; + } + + delete[] allStates; + if ( classMap != 0 ) + delete[] classMap; + + for ( TransApSet::Iter ti = transSet; ti.lte(); ti++ ) { + if ( ti->condSpace != 0 ) + delete[] ti->v.outConds; + } + + condSet.empty(); + transSet.empty(); +} + +/* Does the machine have any actions. */ +bool RedFsmAp::anyActions() +{ + return actionMap.length() > 0; +} + +void RedFsmAp::depthFirstOrdering( RedStateAp *state ) +{ + /* Nothing to do if the state is already on the list. */ + if ( state->onStateList ) + return; + + /* Doing depth first, put state on the list. */ + state->onStateList = true; + stateList.append( state ); + + /* At this point transitions should only be in ranges. */ + assert( state->outSingle.length() == 0 ); + assert( state->defTrans == 0 ); + + /* Recurse on everything ranges. */ + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + for ( int c = 0; c < rtel->value->numConds(); c++ ) { + RedCondPair *cond = rtel->value->outCond( c ); + if ( cond->targ != 0 ) + depthFirstOrdering( cond->targ ); + } + } + + if ( state->nfaTargs ) { + for ( RedNfaTargs::Iter s = *state->nfaTargs; s.lte(); s++ ) + depthFirstOrdering( s->state ); + } +} + +/* Ordering states by transition connections. */ +void RedFsmAp::depthFirstOrdering() +{ + /* Init on state list flags. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) + st->onStateList = false; + + /* Clear out the state list, we will rebuild it. */ + int stateListLen = stateList.length(); + stateList.abandon(); + + /* Add back to the state list from the start state and all other entry + * points. */ + if ( startState != 0 ) + depthFirstOrdering( startState ); + for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) + depthFirstOrdering( *en ); + if ( forcedErrorState ) + depthFirstOrdering( errState ); + + /* Make sure we put everything back on. */ + assert( stateListLen == stateList.length() ); +} + +void RedFsmAp::breadthFirstAdd( RedStateAp *state ) +{ + if ( state->onStateList ) + return; + + state->onStateList = true; + stateList.append( state ); +} + +void RedFsmAp::breadthFirstOrdering() +{ + /* Init on state list flags. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) + st->onStateList = false; + + /* Clear out the state list, we will rebuild it. */ + int stateListLen = stateList.length(); + stateList.abandon(); + + if ( startState != 0 ) + breadthFirstAdd( startState ); + + int depth = 0; + int nextLevel = stateList.length(); + int pos = 0; + + /* To implement breadth-first we traverse the current list (assuming a + * start state) and add children. */ + RedStateAp *cur = stateList.head; + while ( cur != 0 ) { + /* Recurse on everything ranges. */ + for ( RedTransList::Iter rtel = cur->outRange; rtel.lte(); rtel++ ) { + for ( int c = 0; c < rtel->value->numConds(); c++ ) { + RedCondPair *cond = rtel->value->outCond( c ); + if ( cond->targ != 0 ) + breadthFirstAdd( cond->targ ); + } + } + + if ( cur->nfaTargs ) { + for ( RedNfaTargs::Iter s = *cur->nfaTargs; s.lte(); s++ ) + breadthFirstAdd( s->state ); + } + + cur = cur->next; + pos += 1; + + if ( pos == nextLevel ) { + depth += 1; + nextLevel = stateList.length(); + } + } + + for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) + depthFirstOrdering( *en ); + if ( forcedErrorState ) + depthFirstOrdering( errState ); + + assert( stateListLen == stateList.length() ); +} + +#ifdef SCORE_ORDERING +void RedFsmAp::readScores() +{ + /* + * Reads processed transitions logged by ASM codegen when LOG_TRANS is + * enabled. Process with: + * + * cat trans-log | sort -n -k 1 -k 2 -k 3 | uniq -c | sort -r -n -k1 -r > scores + */ + FILE *sfn = fopen( "scores", "r" ); + + scores = new long*[nextStateId]; + for ( int i = 0; i < nextStateId; i++ ) { + scores[i] = new long[256]; + memset( scores[i], 0, sizeof(long) * 256 ); + } + + long score, m, state, ch; + while ( true ) { + int n = fscanf( sfn, "%ld %ld %ld %ld\n", &score, &m, &state, &ch ); + if ( n != 4 ) + break; + if ( m == machineId ) + scores[state][ch] = score; + } + fclose( sfn ); + + /* Init on state list flags. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + RedTransList::Iter rtel = st->outRange; + int chi = 0; + while ( rtel.lte() ) { + /* 1. Bring chi up to lower end of out range. */ + while ( chi < rtel->lowKey.getVal() ) { + chi++; + } + + /* 2. While inside lower, add in score. */ + while ( chi <= rtel->highKey.getVal() ) { + rtel->score += scores[st->id][chi]; + chi++; + } + + /* 3. Next range. */ + rtel++; + } + } +} + +/* This second pass will collect any states that didn't make it in the first + * pass. Used for depth-first and breadth-first passes. */ +void RedFsmAp::scoreSecondPass( RedStateAp *state ) +{ + /* Nothing to do if the state is already on the list. */ + if ( state->onListRest ) + return; + + /* Doing depth first, put state on the list. */ + state->onListRest = true; + + if ( !state->onStateList ) { + state->onStateList = true; + stateList.append( state ); + } + + /* At this point transitions should only be in ranges. */ + assert( state->outSingle.length() == 0 ); + assert( state->defTrans == 0 ); + + /* Recurse on everything ranges. */ + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + for ( int c = 0; c < rtel->value->numConds(); c++ ) { + RedCondPair *cond = rtel->value->outCond( c ); + if ( cond->targ != 0 ) + scoreSecondPass( cond->targ ); + } + } + + if ( state->nfaTargs ) { + for ( RedNfaTargs::Iter s = *state->nfaTargs; s.lte(); s++ ) + scoreSecondPass( s->state ); + } +} + +void RedFsmAp::scoreOrderingDepth( RedStateAp *state ) +{ + /* Nothing to do if the state is already on the list. */ + if ( state->onStateList ) + return; + + /* Doing depth first, put state on the list. */ + state->onStateList = true; + stateList.append( state ); + + /* At this point transitions should only be in ranges. */ + assert( state->outSingle.length() == 0 ); + assert( state->defTrans == 0 ); + + /* Recurse on everything ranges. */ + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + if ( rtel->score > 10 ) { + for ( int c = 0; c < rtel->value->numConds(); c++ ) { + RedCondPair *cond = rtel->value->outCond( c ); + if ( cond->targ != 0 ) + scoreOrderingDepth( cond->targ ); + } + } + } +} + +void RedFsmAp::scoreOrderingDepth() +{ + readScores(); + + /* Init on state list flags. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + st->onStateList = false; + st->onListRest = false; + } + + /* Clear out the state list, we will rebuild it. */ + int stateListLen = stateList.length(); + stateList.abandon(); + + scoreOrderingDepth( startState ); + + scoreSecondPass( startState ); + for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) + scoreSecondPass( *en ); + if ( forcedErrorState ) + scoreSecondPass( errState ); + + assert( stateListLen == stateList.length() ); +} + +void RedFsmAp::scoreOrderingBreadth() +{ + readScores(); + + /* Init on state list flags. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + st->onStateList = false; + st->onListRest = false; + } + + /* Clear out the state list, we will rebuild it. */ + int stateListLen = stateList.length(); + stateList.abandon(); + + if ( startState != 0 ) + breadthFirstAdd( startState ); + + int depth = 0; + int nextLevel = stateList.length(); + int pos = 0; + + /* To implement breadth-first we traverse the current list (assuming a + * start state) and add children. */ + RedStateAp *cur = stateList.head; + while ( cur != 0 ) { + /* Recurse on everything ranges. */ + for ( RedTransList::Iter rtel = cur->outRange; rtel.lte(); rtel++ ) { + if ( rtel->score > 100 ) { + for ( int c = 0; c < rtel->value->numConds(); c++ ) { + RedCondPair *cond = rtel->value->outCond( c ); + if ( cond->targ != 0 ) + breadthFirstAdd( cond->targ ); + } + } + } + + cur = cur->next; + pos += 1; + + if ( pos == nextLevel ) { + depth += 1; + nextLevel = stateList.length(); + } + } + + scoreSecondPass( startState ); + for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ ) + scoreSecondPass( *en ); + if ( forcedErrorState ) + scoreSecondPass( errState ); + + assert( stateListLen == stateList.length() ); +} +#endif + +void RedFsmAp::randomizedOrdering() +{ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) + st->onStateList = false; + + /* Clear out the state list, we will rebuild it. */ + int stateListLen = stateList.length(); + stateList.abandon(); + + srand( time( 0 ) ); + + for ( int i = nextStateId; i > 0; i-- ) { + /* Pick one from 0 ... i (how many are left). */ + int nth = rand() % i; + + /* Go forward through the list adding the nth. Need to scan because + * there are items already added in the list. */ + for ( int j = 0; j < nextStateId; j++ ) { + if ( !allStates[j].onStateList ) { + if ( nth == 0 ) { + /* Add. */ + allStates[j].onStateList = true; + stateList.append( &allStates[j] ); + break; + } + else { + nth -= 1; + } + } + } + } + assert( stateListLen == stateList.length() ); +} + +/* Assign state ids by appearance in the state list. */ +void RedFsmAp::sequentialStateIds() +{ + /* Table based machines depend on the state numbers starting at zero. */ + nextStateId = 0; + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) + st->id = nextStateId++; +} + +/* Stable sort the states by final state status. */ +void RedFsmAp::sortStatesByFinal() +{ + /* Move forward through the list and move final states onto the end. */ + RedStateAp *state = 0; + RedStateAp *next = stateList.head; + RedStateAp *last = stateList.tail; + while ( state != last ) { + /* Move forward and load up the next. */ + state = next; + next = state->next; + + /* Throw to the end? */ + if ( state->isFinal ) { + stateList.detach( state ); + stateList.append( state ); + } + } +} + +/* Assign state ids by final state state status. */ +void RedFsmAp::sortStateIdsByFinal() +{ + /* Table based machines depend on this starting at zero. */ + nextStateId = 0; + + /* First pass to assign non final ids. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( ! st->isFinal ) + st->id = nextStateId++; + } + + /* Second pass to assign final ids. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->isFinal ) + st->id = nextStateId++; + } +} + +struct CmpStateById +{ + static int compare( RedStateAp *st1, RedStateAp *st2 ) + { + if ( st1->id < st2->id ) + return -1; + else if ( st1->id > st2->id ) + return 1; + else + return 0; + } +}; + +void RedFsmAp::sortByStateId() +{ + /* Make the array. */ + int pos = 0; + RedStateAp **ptrList = new RedStateAp*[stateList.length()]; + for ( RedStateList::Iter st = stateList; st.lte(); st++, pos++ ) + ptrList[pos] = st; + + MergeSort mergeSort; + mergeSort.sort( ptrList, stateList.length() ); + + stateList.abandon(); + for ( int st = 0; st < pos; st++ ) + stateList.append( ptrList[st] ); + + delete[] ptrList; +} + +/* Find the final state with the lowest id. */ +void RedFsmAp::findFirstFinState() +{ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->isFinal && (firstFinState == 0 || st->id < firstFinState->id) ) + firstFinState = st; + } +} + +void RedFsmAp::assignActionLocs() +{ + int nextLocation = 0; + for ( GenActionTableMap::Iter act = actionMap; act.lte(); act++ ) { + /* Store the loc, skip over the array and a null terminator. */ + act->location = nextLocation; + nextLocation += act->key.length() + 1; + } +} + +/* Check if we can extend the current range by displacing any ranges + * ahead to the singles. */ +bool RedFsmAp::canExtend( const RedTransList &list, int pos ) +{ + /* Get the transition that we want to extend. */ + RedTransAp *extendTrans = list[pos].value; + + /* Look ahead in the transition list. */ + for ( int next = pos + 1; next < list.length(); pos++, next++ ) { + /* If they are not continuous then cannot extend. */ + Key nextKey = list[next].lowKey; + keyOps->decrement( nextKey ); + if ( keyOps->ne( list[pos].highKey, nextKey ) ) + break; + + /* Check for the extenstion property. */ + if ( extendTrans == list[next].value ) + return true; + + /* If the span of the next element is more than one, then don't keep + * checking, it won't be moved to single. */ + unsigned long long nextSpan = keyOps->span( list[next].lowKey, list[next].highKey ); + if ( nextSpan > 1 ) + break; + } + return false; +} + +/* Move ranges to the singles list if it means we can extend some ranges, or if + * the spans are of length one. */ +void RedFsmAp::moveSelectTransToSingle( RedStateAp *state ) +{ + RedTransList &range = state->outRange; + RedTransList &single = state->outSingle; + for ( int rpos = 0; rpos < range.length(); ) { + /* Check if this is a range we can extend. */ + if ( canExtend( range, rpos ) ) { + /* Transfer singles over. */ + while ( range[rpos].value != range[rpos+1].value ) { + /* Transfer the range to single. */ + single.append( range[rpos+1] ); + range.remove( rpos+1 ); + } + + /* Extend. */ + range[rpos].highKey = range[rpos+1].highKey; + range.remove( rpos+1 ); + } + /* Maybe move it to the singles. */ + else if ( keyOps->span( range[rpos].lowKey, range[rpos].highKey ) == 1 ) { + single.append( range[rpos] ); + range.remove( rpos ); + } + else { + /* Keeping it in the ranges. */ + rpos += 1; + } + } +} + +void RedFsmAp::moveAllTransToSingle( RedStateAp *state ) +{ + RedTransList &range = state->outRange; + RedTransList &single = state->outSingle; + for ( int rpos = 0; rpos < range.length(); rpos++ ) { + + RedTransEl el = range[rpos]; + unsigned long long span = keyOps->span( el.lowKey, el.highKey ); + + Key key = el.lowKey; + for ( unsigned long long pos = 0; pos < span; pos++ ) { + el.lowKey = el.highKey = key; + single.append( el ); + keyOps->increment( key ); + } + } + range.empty(); +} + +/* Look through ranges and choose suitable single character transitions. */ +void RedFsmAp::moveSelectTransToSingle() +{ + /* Loop the states. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + /* Rewrite the transition list taking out the suitable single + * transtions. */ + moveSelectTransToSingle( st ); + } +} + +void RedFsmAp::moveAllTransToSingle() +{ + /* Loop the states. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + /* Rewrite the transition list taking out the suitable single + * transtions. */ + moveAllTransToSingle( st ); + } +} + +void RedFsmAp::makeFlat() +{ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->outRange.length() == 0 ) { + st->lowKey = st->highKey = 0; + st->transList = 0; + } + else { + st->lowKey = st->outRange[0].lowKey; + st->highKey = st->outRange[st->outRange.length()-1].highKey; + unsigned long long span = keyOps->span( st->lowKey, st->highKey ); + st->transList = new RedTransAp*[ span ]; + memset( st->transList, 0, sizeof(RedTransAp*)*span ); + + for ( RedTransList::Iter trans = st->outRange; trans.lte(); trans++ ) { + unsigned long long base, trSpan; + base = keyOps->span( st->lowKey, trans->lowKey )-1; + trSpan = keyOps->span( trans->lowKey, trans->highKey ); + for ( unsigned long long pos = 0; pos < trSpan; pos++ ) + st->transList[base+pos] = trans->value; + } + + /* Fill in the gaps with the default transition. */ + for ( unsigned long long pos = 0; pos < span; pos++ ) { + if ( st->transList[pos] == 0 ) + st->transList[pos] = st->defTrans; + } + } + } +} + +void RedFsmAp::characterClass( EquivList &equiv ) +{ + /* Find the global low and high keys. */ + bool anyTrans = false; + Key lowKey = keyOps->maxKey; + Key highKey = keyOps->minKey; + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->outRange.length() == 0 ) + continue; + + st->lowKey = st->outRange[0].lowKey; + st->highKey = st->outRange[st->outRange.length()-1].highKey; + + if ( keyOps->lt( st->lowKey, lowKey ) ) + lowKey = st->lowKey; + + if ( keyOps->gt( st->highKey, highKey ) ) + highKey = st->highKey; + + anyTrans = true; + } + + if ( ! anyTrans ) { + this->lowKey = lowKey; + this->highKey = highKey; + this->classMap = 0; + this->nextClass = 1; + return; + } + + long long next = 1; + equiv.append( new EquivClass( lowKey, highKey, next++ ) ); + + /* Start with a single equivalence class and break it up using range + * boundaries of each state. This will tell us what the equivalence class + * ranges are. These are the ranges that always go to the same state, + * across all states. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->outRange.length() == 0 ) + continue; + + EquivList newList; + PairKeyMap uniqPairs; + + /* What is the set of unique transitions (*for this state) */ + EquivAlloc uniqTrans; + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + if ( ! uniqTrans.find( rtel->value ) ) + uniqTrans.insert( rtel->value, next++ ); + } + + /* Merge with whole-machine equiv classes. */ + typedef RangePairIter< PiList, PiVector > RangePairIterPiListEquivClassPiVectorRedTransEl; + for ( RangePairIterPiListEquivClassPiVectorRedTransEl + pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ ) + { + switch ( pair.userState ) { + + case RangePairIterPiListEquivClassPiVectorRedTransEl::RangeOverlap: { + /* Look up the char for s2. */ + EquivAllocEl *s2El = uniqTrans.find( pair.s2Tel.trans->value ); + + /* Can't use either equiv classes, find uniques. */ + PairKey pairKey( pair.s1Tel.trans->value, s2El->value ); + PairKeyMapEl *pairEl = uniqPairs.find( pairKey ); + if ( ! pairEl ) + pairEl = uniqPairs.insert( pairKey, next++ ); + + EquivClass *equivClass = new EquivClass( + pair.s1Tel.lowKey, pair.s1Tel.highKey, + pairEl->value ); + newList.append( equivClass ); + break; + } + + case RangePairIterPiListEquivClassPiVectorRedTransEl::RangeInS1: { + EquivClass *equivClass = new EquivClass( + pair.s1Tel.lowKey, pair.s1Tel.highKey, + pair.s1Tel.trans->value ); + newList.append( equivClass ); + break; + } + + case RangePairIterPiListEquivClassPiVectorRedTransEl::RangeInS2: { + /* Look up the char for s2. */ + EquivAllocEl *s2El = uniqTrans.find( pair.s2Tel.trans->value ); + + EquivClass *equivClass = new EquivClass( + pair.s2Tel.lowKey, pair.s2Tel.highKey, + s2El->value ); + newList.append( equivClass ); + break; + } + + case RangePairIterPiListEquivClassPiVectorRedTransEl::BreakS1: + case RangePairIterPiListEquivClassPiVectorRedTransEl::BreakS2: + break; + } + } + + equiv.empty(); + equiv.transfer( newList ); + } + + /* Reduce to sequential. */ + next = 0; + BstMap map; + for ( EquivClass *c = equiv.head; c != 0; c = c->next ) { + BstMapEl *el = map.find( c->value ); + if ( ! el ) + el = map.insert( c->value, next++ ); + c->value = el->value; + } + + /* Build the map and emit arrays from the range-based equiv classes. Will + * likely crash if there are no transitions in the FSM. */ + long long maxSpan = keyOps->span( lowKey, highKey ); + long long *dest = new long long[maxSpan]; + memset( dest, 0, sizeof(long long) * maxSpan ); + + for ( EquivClass *c = equiv.head; c != 0; c = c->next ) { + long long base = keyOps->span( lowKey, c->lowKey ) - 1; + long long span = keyOps->span( c->lowKey, c->highKey ); + for ( long long s = 0; s < span; s++ ) + dest[base + s] = c->value; + } + + this->lowKey = lowKey; + this->highKey = highKey; + this->classMap = dest; + this->nextClass = next; + +} + +void RedFsmAp::makeFlatClass() +{ + EquivList equiv; + characterClass( equiv ); + + /* Expand the transitions. This uses the equivalence classes. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + if ( st->outRange.length() == 0 ) { + st->lowKey = st->highKey = 0; + st->low = st->high = 0; + st->transList = 0; + } + else { + st->lowKey = st->outRange[0].lowKey; + st->highKey = st->outRange[st->outRange.length()-1].highKey; + + /* Compute low and high in class space. Use a pair iter to find all + * the clases. Alleviates the need to iterate the whole input + * alphabet. */ + st->low = nextClass; + st->high = -1; + for ( RangePairIter< PiList, PiVector > + pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ ) + { + if ( pair.userState == RangePairIter, PiVector >::RangeOverlap || + pair.userState == RangePairIter, PiVector >::RangeInS2 ) + { + long long off = keyOps->span( lowKey, pair.s2Tel.lowKey ) - 1; + if ( classMap[off] < st->low ) + st->low = classMap[off]; + if ( classMap[off] > st->high ) + st->high = classMap[off]; + } + } + + long long span = st->high - st->low + 1; + st->transList = new RedTransAp*[ span ]; + memset( st->transList, 0, sizeof(RedTransAp*)*span ); + + for ( RangePairIter< PiList, PiVector > + pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ ) + { + if ( pair.userState == RangePairIter< PiList, PiVector >::RangeOverlap || + pair.userState == RangePairIter< PiList, PiVector >::RangeInS2 ) + { + long long off = keyOps->span( lowKey, pair.s2Tel.lowKey ) - 1; + st->transList[ classMap[off] - st->low ] = pair.s2Tel.trans->value; + } + } + + /* Fill in the gaps with the default transition. */ + for ( long long pos = 0; pos < span; pos++ ) { + if ( st->transList[pos] == 0 ) + st->transList[pos] = st->defTrans; + } + } + } + + equiv.empty(); +} + + +/* A default transition has been picked, move it from the outRange to the + * default pointer. */ +void RedFsmAp::moveToDefault( RedTransAp *defTrans, RedStateAp *state ) +{ + /* Rewrite the outRange, omitting any ranges that use + * the picked default. */ + RedTransList outRange; + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + /* If it does not take the default, copy it over. */ + if ( rtel->value != defTrans ) + outRange.append( *rtel ); + } + + /* Save off the range we just created into the state's range. */ + state->outRange.transfer( outRange ); + + /* Store the default. */ + state->defTrans = defTrans; +} + +bool RedFsmAp::alphabetCovered( RedTransList &outRange ) +{ + /* Cannot cover without any out ranges. */ + if ( outRange.length() == 0 ) + return false; + + /* If the first range doesn't start at the the lower bound then the + * alphabet is not covered. */ + RedTransList::Iter rtel = outRange; + if ( keyOps->lt( keyOps->minKey, rtel->lowKey ) ) + return false; + + /* Check that every range is next to the previous one. */ + rtel.increment(); + for ( ; rtel.lte(); rtel++ ) { + Key highKey = rtel[-1].highKey; + keyOps->increment( highKey ); + if ( keyOps->ne( highKey, rtel->lowKey ) ) + return false; + } + + /* The last must extend to the upper bound. */ + RedTransEl *last = &outRange[outRange.length()-1]; + if ( keyOps->lt( last->highKey, keyOps->maxKey ) ) + return false; + + return true; +} + +RedTransAp *RedFsmAp::chooseDefaultSpan( RedStateAp *state ) +{ + /* Make a set of transitions from the outRange. */ + RedTransSet stateTransSet; + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) + stateTransSet.insert( rtel->value ); + + /* For each transition in the find how many alphabet characters the + * transition spans. */ + unsigned long long *span = new unsigned long long[stateTransSet.length()]; + memset( span, 0, sizeof(unsigned long long) * stateTransSet.length() ); + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + /* Lookup the transition in the set. */ + RedTransAp **inSet = stateTransSet.find( rtel->value ); + int pos = inSet - stateTransSet.data; + span[pos] += keyOps->span( rtel->lowKey, rtel->highKey ); + } + + /* Find the max span, choose it for making the default. */ + RedTransAp *maxTrans = 0; + unsigned long long maxSpan = 0; + for ( RedTransSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) { + if ( span[rtel.pos()] > maxSpan ) { + maxSpan = span[rtel.pos()]; + maxTrans = *rtel; + } + } + + delete[] span; + return maxTrans; +} + +/* Pick default transitions from ranges for the states. */ +void RedFsmAp::chooseDefaultSpan() +{ + /* Loop the states. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + /* Only pick a default transition if the alphabet is covered. This + * avoids any transitions in the out range that go to error and avoids + * the need for an ERR state. */ + if ( alphabetCovered( st->outRange ) ) { + /* Pick a default transition by largest span. */ + RedTransAp *defTrans = chooseDefaultSpan( st ); + + /* Rewrite the transition list taking out the transition we picked + * as the default and store the default. */ + moveToDefault( defTrans, st ); + } + } +} + +RedTransAp *RedFsmAp::chooseDefaultGoto( RedStateAp *state ) +{ + /* Make a set of transitions from the outRange. */ + RedTransSet stateTransSet; + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + for ( int c = 0; c < rtel->value->numConds(); c++ ) { + RedCondPair *cond = rtel->value->outCond(c); + if ( cond->targ == state->next ) + return rtel->value; + } + } + return 0; +} + +void RedFsmAp::chooseDefaultGoto() +{ + /* Loop the states. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + /* Pick a default transition. */ + RedTransAp *defTrans = chooseDefaultGoto( st ); + if ( defTrans == 0 ) + defTrans = chooseDefaultSpan( st ); + + /* Rewrite the transition list taking out the transition we picked + * as the default and store the default. */ + moveToDefault( defTrans, st ); + } +} + +RedTransAp *RedFsmAp::chooseDefaultNumRanges( RedStateAp *state ) +{ + /* Make a set of transitions from the outRange. */ + RedTransSet stateTransSet; + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) + stateTransSet.insert( rtel->value ); + + /* For each transition in the find how many ranges use the transition. */ + int *numRanges = new int[stateTransSet.length()]; + memset( numRanges, 0, sizeof(int) * stateTransSet.length() ); + for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) { + /* Lookup the transition in the set. */ + RedTransAp **inSet = stateTransSet.find( rtel->value ); + numRanges[inSet - stateTransSet.data] += 1; + } + + /* Find the max number of ranges. */ + RedTransAp *maxTrans = 0; + int maxNumRanges = 0; + for ( RedTransSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) { + if ( numRanges[rtel.pos()] > maxNumRanges ) { + maxNumRanges = numRanges[rtel.pos()]; + maxTrans = *rtel; + } + } + + delete[] numRanges; + return maxTrans; +} + +void RedFsmAp::chooseDefaultNumRanges() +{ + /* Loop the states. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + /* Pick a default transition. */ + RedTransAp *defTrans = chooseDefaultNumRanges( st ); + + /* Rewrite the transition list taking out the transition we picked + * as the default and store the default. */ + moveToDefault( defTrans, st ); + } +} + +RedCondAp *RedFsmAp::getErrorCond() +{ + return allocateCond( getErrorState(), 0 ); +} + +RedTransAp *RedFsmAp::getErrorTrans() +{ + return allocateTrans( getErrorState(), 0 ); +} + +RedStateAp *RedFsmAp::getErrorState() +{ + /* Something went wrong. An error state is needed but one was not supplied + * by the frontend. */ + assert( errState != 0 ); + return errState; +} + +/* Makes a plain transition. */ +RedTransAp *RedFsmAp::allocateTrans( RedStateAp *targ, RedAction *action ) +{ + /* Create a reduced trans and look for it in the transiton set. */ + RedTransAp redTrans( 0, 0, targ, action ); + RedTransAp *inDict = transSet.find( &redTrans ); + if ( inDict == 0 ) { + inDict = new RedTransAp( nextTransId++, nextCondId++, targ, action ); + transSet.insert( inDict ); + } + return inDict; +} + +/* Makes a cond list transition. */ +RedTransAp *RedFsmAp::allocateTrans( GenCondSpace *condSpace, + RedCondEl *outConds, int numConds, RedCondAp *errCond ) +{ + /* Create a reduced trans and look for it in the transiton set. */ + RedTransAp redTrans( 0, condSpace, outConds, numConds, errCond ); + RedTransAp *inDict = transSet.find( &redTrans ); + if ( inDict == 0 ) { + inDict = new RedTransAp( nextTransId++, condSpace, outConds, numConds, errCond ); + transSet.insert( inDict ); + } + else { + /* Need to free the out cond vector. */ + delete[] outConds; + } + return inDict; +} + +RedCondAp *RedFsmAp::allocateCond( RedStateAp *targ, RedAction *action ) +{ + /* Create a reduced trans and look for it in the transiton set. */ + RedCondAp redCond( targ, action, 0 ); + RedCondAp *inDict = condSet.find( &redCond ); + if ( inDict == 0 ) { + inDict = new RedCondAp( targ, action, nextCondId++ ); + condSet.insert( inDict ); + } + return inDict; +} + +void RedFsmAp::partitionFsm( int nparts ) +{ + /* At this point the states are ordered by a depth-first traversal. We + * will allocate to partitions based on this ordering. */ + this->nParts = nparts; + int partSize = stateList.length() / nparts; + int remainder = stateList.length() % nparts; + int numInPart = partSize; + int partition = 0; + if ( remainder-- > 0 ) + numInPart += 1; + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + st->partition = partition; + + numInPart -= 1; + if ( numInPart == 0 ) { + partition += 1; + numInPart = partSize; + if ( remainder-- > 0 ) + numInPart += 1; + } + } +} + +void RedFsmAp::setInTrans() +{ + /* First pass counts the number of transitions. */ + for ( CondApSet::Iter trans = condSet; trans.lte(); trans++ ) + trans->p.targ->numInConds += 1; + + for ( TransApSet::Iter trans = transSet; trans.lte(); trans++ ) { + if ( trans->condSpace == 0 ) + trans->p.targ->numInConds += 1; + else { + /* We have a placement choice here, but associate it with the + * first. */ + RedCondPair *pair = trans->outCond( 0 ); + pair->targ->numInCondTests += 1; + } + } + + /* Allocate. Reset the counts so we can use them as the current size. */ + for ( RedStateList::Iter st = stateList; st.lte(); st++ ) { + st->inConds = new RedCondPair*[st->numInConds]; + st->numInConds = 0; + + st->inCondTests = new RedTransAp*[st->numInCondTests]; + st->numInCondTests = 0; + } + + /* Fill the arrays. */ + for ( CondApSet::Iter trans = condSet; trans.lte(); trans++ ) { + RedStateAp *targ = trans->p.targ; + targ->inConds[targ->numInConds++] = &trans->p; + } + + for ( TransApSet::Iter trans = transSet; trans.lte(); trans++ ) { + if ( trans->condSpace == 0 ) { + RedStateAp *targ = trans->p.targ; + targ->inConds[targ->numInConds++] = &trans->p; + } + else { + RedCondPair *pair = trans->outCond( 0 ); + RedStateAp *targ = pair->targ; + targ->inCondTests[targ->numInCondTests++] = trans; + } + } +} diff --git a/src/libfsm/redfsm.h b/src/libfsm/redfsm.h new file mode 100644 index 00000000..392b1a9c --- /dev/null +++ b/src/libfsm/redfsm.h @@ -0,0 +1,889 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _REDFSM_H +#define _REDFSM_H + +#include +#include +#include +#include "config.h" +#include "common.h" +#include "vector.h" +#include "dlist.h" +#include "compare.h" +#include "bstmap.h" +#include "bstset.h" +#include "avlmap.h" +#include "avltree.h" +#include "avlbasic.h" +#include "mergesort.h" +#include "sbstmap.h" +#include "sbstset.h" +#include "sbsttable.h" + +#define TRANS_ERR_TRANS 0 +#define STATE_ERR_STATE 0 +#define FUNC_NO_FUNC 0 + +// #define SCORE_ORDERING 1 + +using std::string; + +struct RedStateAp; +struct GenInlineList; +struct GenAction; +struct FsmCtx; +struct GenCondSpace; +typedef BstSet RedCondKeySet; + +/* + * Inline code tree + */ +struct GenInlineItem +{ + enum Type + { + Text, Goto, Call, Ncall, Next, GotoExpr, CallExpr, + NcallExpr, NextExpr, Ret, Nret, + PChar, Char, Hold, Curs, Targs, Entry, Exec, Break, Nbreak, + LmSwitch, LmExec, LmSetActId, LmSetTokEnd, LmGetTokEnd, + LmInitAct, LmInitTokStart, LmSetTokStart, NfaClear, + HostStmt, HostExpr, HostText, + GenStmt, GenExpr, LmCase, LmHold, + NfaWrapAction, NfaWrapConds + }; + + GenInlineItem( const InputLoc &loc, Type type ) : + loc(loc), targId(0), targState(0), + lmId(0), children(0), offset(0), + wrappedAction(0), type(type) { } + + ~GenInlineItem(); + + InputLoc loc; + std::string data; + int targId; + RedStateAp *targState; + int lmId; + GenInlineList *children; + int offset; + GenAction *wrappedAction; + GenCondSpace *condSpace; + RedCondKeySet condKeySet; + Type type; + + GenInlineItem *prev, *next; +}; + +/* Normally this would be atypedef, but that would entail including DList from + * ptreetypes, which should be just typedef forwards. */ +struct GenInlineList : public DList { }; + +struct GenInlineExpr +{ + GenInlineExpr( const InputLoc &loc, GenInlineList *inlineList ) + : loc(loc), inlineList( inlineList ) {} + + ~GenInlineExpr() + { + if ( inlineList != 0 ) { + inlineList->empty(); + delete inlineList; + } + } + + InputLoc loc; + GenInlineList *inlineList; +}; + +/* Element in list of actions. Contains the string for the code to exectute. */ +struct GenAction +: + public DListEl +{ + GenAction( ) + : + inlineList(0), + actionId(0), + numTransRefs(0), + numToStateRefs(0), + numFromStateRefs(0), + numEofRefs(0), + numNfaPushRefs(0), + numNfaRestoreRefs(0), + numNfaPopActionRefs(0), + numNfaPopTestRefs(0) + { + } + + ~GenAction() + { + if ( inlineList != 0 ) { + inlineList->empty(); + delete inlineList; + } + } + + /* Data collected during parse. */ + InputLoc loc; + std::string name; + GenInlineList *inlineList; + int actionId; + + string nameOrLoc(); + + /* Number of references in the final machine. */ + int numRefs() + { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; } + int numTransRefs; + int numToStateRefs; + int numFromStateRefs; + int numEofRefs; + int numNfaPushRefs; + int numNfaRestoreRefs; + int numNfaPopActionRefs; + int numNfaPopTestRefs; +}; + + +/* Forwards. */ +struct RedStateAp; +struct StateAp; + +/* Transistion GenAction Element. */ +typedef SBstMapEl< int, GenAction* > GenActionTableEl; + +/* Transition GenAction Table. */ +struct GenActionTable + : public SBstMap< int, GenAction*, CmpOrd > +{ + void setAction( int ordering, GenAction *action ); + void setActions( int *orderings, GenAction **actions, int nActs ); + void setActions( const GenActionTable &other ); +}; + +/* Compare of a whole action table element (key & value). */ +struct CmpGenActionTableEl +{ + static int compare( const GenActionTableEl &action1, + const GenActionTableEl &action2 ) + { + if ( action1.key < action2.key ) + return -1; + else if ( action1.key > action2.key ) + return 1; + else if ( action1.value < action2.value ) + return -1; + else if ( action1.value > action2.value ) + return 1; + return 0; + } +}; + +/* Compare for GenActionTable. */ +typedef CmpSTable< GenActionTableEl, CmpGenActionTableEl > CmpGenActionTable; + +/* Set of states. */ +typedef BstSet RedStateSet; +typedef BstSet IntSet; + +/* Reduced action. */ +struct RedAction +: + public AvlTreeEl +{ + RedAction( ) + : + key(), + eofRefs(0), + numTransRefs(0), + numToStateRefs(0), + numFromStateRefs(0), + numEofRefs(0), + numNfaPushRefs(0), + numNfaRestoreRefs(0), + numNfaPopActionRefs(0), + numNfaPopTestRefs(0), + bAnyNextStmt(false), + bAnyCurStateRef(false), + bAnyBreakStmt(false), + bUsingAct(false) + { } + + const GenActionTable &getKey() + { return key; } + + GenActionTable key; + int actListId; + int location; + IntSet *eofRefs; + + /* Number of references in the final machine. */ + int numRefs() + { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; } + int numTransRefs; + int numToStateRefs; + int numFromStateRefs; + int numEofRefs; + int numNfaPushRefs; + int numNfaRestoreRefs; + int numNfaPopActionRefs; + int numNfaPopTestRefs; + + bool anyNextStmt() { return bAnyNextStmt; } + bool anyCurStateRef() { return bAnyCurStateRef; } + bool anyBreakStmt() { return bAnyBreakStmt; } + bool usingAct() { return bUsingAct; } + + bool bAnyNextStmt; + bool bAnyCurStateRef; + bool bAnyBreakStmt; + bool bUsingAct; +}; + +typedef AvlTree GenActionTableMap; + +struct RedCondPair +{ + int id; + RedStateAp *targ; + RedAction *action; +}; + +struct RedCondAp +: + public AvlTreeEl +{ + RedCondAp( RedStateAp *targ, RedAction *action, int id ) + { + p.id = id; + p.targ = targ; + p.action = action; + } + + RedCondPair p; +}; + +struct RedCondEl +{ + CondKey key; + RedCondAp *value; +}; + +struct CmpRedCondEl +{ + static int compare( const RedCondEl &el1, const RedCondEl &el2 ) + { + if ( el1.key < el2.key ) + return -1; + else if ( el1.key > el2.key ) + return 1; + else if ( el1.value < el2.value ) + return -1; + else if ( el1.value > el2.value ) + return 1; + else + return 0; + } +}; + +typedef Vector< GenAction* > GenCondSet; + +struct GenCondSpace +{ + GenCondSpace() + : + numTransRefs(0), + numNfaRefs(0) + {} + + Key baseKey; + GenCondSet condSet; + int condSpaceId; + + long fullSize() + { return ( 1 << condSet.length() ); } + + long numTransRefs; + long numNfaRefs; + + GenCondSpace *next, *prev; +}; + +typedef DList CondSpaceList; + +struct RedCondVect +{ + int numConds; + RedCondEl *outConds; + RedCondAp *errCond; +}; + +/* Reduced transition. */ +struct RedTransAp +: + public AvlTreeEl +{ + RedTransAp( int id, GenCondSpace *condSpace, + RedCondEl *outConds, int numConds, RedCondAp *errCond ) + : + id(id), + condSpace(condSpace) + { + v.outConds = outConds; + v.numConds = numConds; + v.errCond = errCond; + } + + RedTransAp( int id, int condId, RedStateAp *targ, RedAction *action ) + : + id(id), + condSpace(0) + { + p.id = condId; + p.targ = targ; + p.action = action; + } + + long condFullSize() + { + return condSpace == 0 ? 1 : condSpace->fullSize(); + } + + CondKey outCondKey( int off ) + { + return condSpace == 0 ? CondKey(0) : v.outConds[off].key; + } + + RedCondPair *outCond( int off ) + { + return condSpace == 0 ? &p : &v.outConds[off].value->p; + } + + int numConds() + { + return condSpace == 0 ? 1 : v.numConds; + } + + RedCondPair *errCond() + { + return condSpace == 0 ? 0 : ( v.errCond != 0 ? &v.errCond->p : 0 ); + } + + int id; + GenCondSpace *condSpace; + + /* Either a pair or a vector of conds. */ + union + { + RedCondPair p; + RedCondVect v; + }; +}; + +/* Compare of transitions for the final reduction of transitions. Comparison + * is on target and the pointer to the shared action table. It is assumed that + * when this is used the action tables have been reduced. */ +struct CmpRedTransAp +{ + static int compare( const RedTransAp &t1, const RedTransAp &t2 ) + { + if ( t1.condSpace < t2.condSpace ) + return -1; + else if ( t1.condSpace > t2.condSpace ) + return 1; + else { + if ( t1.condSpace == 0 ) { + if ( t1.p.targ < t2.p.targ ) + return -1; + else if ( t1.p.targ > t2.p.targ ) + return 1; + else if ( t1.p.action < t2.p.action ) + return -1; + else if ( t1.p.action > t2.p.action ) + return 1; + else + return 0; + + } + else { + if ( t1.v.numConds < t2.v.numConds ) + return -1; + else if ( t1.v.numConds > t2.v.numConds ) + return 1; + else + { + RedCondEl *i1 = t1.v.outConds, *i2 = t2.v.outConds; + long len = t1.v.numConds, cmpResult; + for ( long pos = 0; pos < len; + pos += 1, i1 += 1, i2 += 1 ) + { + cmpResult = CmpRedCondEl::compare(*i1, *i2); + if ( cmpResult != 0 ) + return cmpResult; + } + return 0; + } + } + } + } +}; + +struct CmpRedCondAp +{ + static int compare( const RedCondAp &t1, const RedCondAp &t2 ) + { + if ( t1.p.targ < t2.p.targ ) + return -1; + else if ( t1.p.targ > t2.p.targ ) + return 1; + else if ( t1.p.action < t2.p.action ) + return -1; + else if ( t1.p.action > t2.p.action ) + return 1; + else + return 0; + } +}; + +typedef AvlBasic TransApSet; +typedef AvlBasic CondApSet; + +/* Element in out range. */ +struct RedTransEl +{ + /* Constructors. */ + RedTransEl( Key lowKey, Key highKey, RedTransAp *value ) + : + lowKey(lowKey), + highKey(highKey), + value(value) +#ifdef SCORE_ORDERING + , score(0) +#endif + { } + + Key lowKey, highKey; + RedTransAp *value; +#ifdef SCORE_ORDERING + long long score; +#endif +}; + +typedef Vector RedTransList; +typedef Vector RedStateVect; + +typedef BstMapEl RedSpanMapEl; +typedef BstMap RedSpanMap; + +/* Compare used by span map sort. Reverse sorts by the span. */ +struct CmpRedSpanMapEl +{ + static int compare( const RedSpanMapEl &smel1, const RedSpanMapEl &smel2 ) + { + if ( smel1.value > smel2.value ) + return -1; + else if ( smel1.value < smel2.value ) + return 1; + else + return 0; + } +}; + +/* Sorting state-span map entries by span. */ +typedef MergeSort RedSpanMapSort; + +/* Set of entry ids that go into this state. */ +typedef Vector EntryIdVect; +typedef Vector EntryNameVect; + +struct Condition +{ + Condition( ) + : key(0), baseKey(0) {} + + Key key; + Key baseKey; + GenCondSet condSet; + + Condition *next, *prev; +}; +typedef DList ConditionList; + +struct GenStateCond +{ + Key lowKey; + Key highKey; + + GenCondSpace *condSpace; + + GenStateCond *prev, *next; +}; +typedef DList GenStateCondList; +typedef Vector StateCondVect; + +struct RedNfaTarg +{ + RedNfaTarg( RedStateAp *state, RedAction *push, + RedAction *popTest, int order ) + : + id(0), + state(state), + push(push), + popTest(popTest), + order(order) + {} + + long id; + RedStateAp *state; + RedAction *push; + RedAction *popTest; + int order; +}; + +struct RedNfaTargCmp +{ + static inline long compare( const RedNfaTarg &k1, const RedNfaTarg &k2 ) + { + if ( k1.order < k2.order ) + return -1; + else if ( k1.order > k2.order ) + return 1; + return 0; + } +}; + +typedef Vector RedNfaTargs; + +/* Reduced state. */ +struct RedStateAp +{ + RedStateAp() + : + defTrans(0), + transList(0), + isFinal(false), + labelNeeded(false), + outNeeded(false), + onStateList(false), + onListRest(false), + toStateAction(0), + fromStateAction(0), + eofAction(0), + eofTrans(0), + id(0), + bAnyRegCurStateRef(false), + partitionBoundary(false), + inConds(0), + numInConds(0), + inCondTests(0), + numInCondTests(0), + nfaTargs(0), + outCondSpace(0) + { } + + /* Transitions out. */ + RedTransList outSingle; + RedTransList outRange; + RedTransAp *defTrans; + + /* For flat keys. */ + Key lowKey, highKey; + RedTransAp **transList; + long long low, high; + + /* The list of states that transitions from this state go to. */ + RedStateVect targStates; + + bool isFinal; + bool labelNeeded; + bool outNeeded; + bool onStateList; + bool onListRest; + RedAction *toStateAction; + RedAction *fromStateAction; + RedAction *eofAction; + RedTransAp *eofTrans; + int id; + + /* Pointers for the list of states. */ + RedStateAp *prev, *next; + + bool anyRegCurStateRef() { return bAnyRegCurStateRef; } + bool bAnyRegCurStateRef; + + int partition; + bool partitionBoundary; + + RedCondPair **inConds; + int numInConds; + + RedTransAp **inCondTests; + int numInCondTests; + + RedNfaTargs *nfaTargs; + GenCondSpace *outCondSpace; + RedCondKeySet outCondKeys; +}; + +/* List of states. */ +typedef DList RedStateList; + +/* Set of reduced transitons. Comparison is by pointer. */ +typedef BstSet< RedTransAp*, CmpOrd > RedTransSet; + +/* Next version of the fsm machine. */ +struct RedFsmAp +{ + RedFsmAp( FsmCtx *fsmCtx, int machineId ); + ~RedFsmAp(); + + KeyOps *keyOps; + FsmCtx *fsmCtx; + int machineId; + + bool forcedErrorState; + + int nextActionId; + int nextTransId; + int nextCondId; + + /* Next State Id doubles as the total number of state ids. */ + int nextStateId; + + TransApSet transSet; + CondApSet condSet; + GenActionTableMap actionMap; + RedStateList stateList; + RedStateSet entryPoints; + RedStateAp *startState; + RedStateAp *errState; + RedTransAp *errTrans; + RedCondAp *errCond; + RedTransAp *errActionTrans; + RedStateAp *firstFinState; + RedStateAp *allStates; + int numFinStates; + int nParts; + + bool bAnyToStateActions; + bool bAnyFromStateActions; + bool bAnyRegActions; + bool bAnyEofActions; + bool bAnyEofTrans; + bool bAnyEofActivity; + bool bAnyActionGotos; + bool bAnyActionCalls; + bool bAnyActionNcalls; + bool bAnyActionRets; + bool bAnyActionNrets; + bool bAnyActionByValControl; + bool bAnyRegActionRets; + bool bAnyRegActionByValControl; + bool bAnyRegNextStmt; + bool bAnyRegCurStateRef; + bool bAnyRegBreak; + bool bAnyRegNbreak; + bool bUsingAct; + bool bAnyNfaStates; + bool bAnyNfaPushPops; + bool bAnyNfaPushes; + bool bAnyNfaPops; + bool bAnyTransCondRefs; + bool bAnyNfaCondRefs; + + int maxState; + int maxSingleLen; + int maxRangeLen; + int maxKeyOffset; + int maxIndexOffset; + int maxIndex; + int maxActListId; + int maxActionLoc; + int maxActArrItem; + unsigned long long maxSpan; + int maxFlatIndexOffset; + Key maxKey; + int maxCondSpaceId; + int maxCond; + + bool anyActions(); + bool anyToStateActions() { return bAnyToStateActions; } + bool anyFromStateActions() { return bAnyFromStateActions; } + bool anyRegActions() { return bAnyRegActions; } + bool anyEofActions() { return bAnyEofActions; } + bool anyEofTrans() { return bAnyEofTrans; } + bool anyEofActivity() { return bAnyEofActivity; } + bool anyActionGotos() { return bAnyActionGotos; } + bool anyActionCalls() { return bAnyActionCalls; } + bool anyActionNcalls() { return bAnyActionNcalls; } + bool anyActionRets() { return bAnyActionRets; } + bool anyActionNrets() { return bAnyActionNrets; } + bool anyActionByValControl() { return bAnyActionByValControl; } + bool anyRegActionRets() { return bAnyRegActionRets; } + bool anyRegActionByValControl() { return bAnyRegActionByValControl; } + bool anyRegNextStmt() { return bAnyRegNextStmt; } + bool anyRegCurStateRef() { return bAnyRegCurStateRef; } + bool anyRegBreak() { return bAnyRegBreak; } + bool usingAct() { return bUsingAct; } + bool anyRegNbreak() { return bAnyRegNbreak; } + bool anyNfaStates() { return bAnyNfaStates; } + + /* Is is it possible to extend a range by bumping ranges that span only + * one character to the singles array. */ + bool canExtend( const RedTransList &list, int pos ); + + /* Pick single transitions from the ranges. */ + void moveSelectTransToSingle( RedStateAp *state ); + void moveAllTransToSingle( RedStateAp *state ); + + void moveSelectTransToSingle(); + void moveAllTransToSingle(); + + void makeFlat(); + + /* State low/high, in key space and class space. */ + Key lowKey; + Key highKey; + long long nextClass; + long long *classMap; + + /* Support structs for equivalence class computation. */ + struct EquivClass + { + EquivClass( Key lowKey, Key highKey, long long value ) + : lowKey(lowKey), highKey(highKey), value(value) {} + + Key lowKey, highKey; + long long value; + EquivClass *prev, *next; + }; + + typedef DList EquivList; + typedef BstMap EquivAlloc; + typedef BstMapEl EquivAllocEl; + + struct PairKey + { + PairKey( long long k1, long long k2 ) + : k1(k1), k2(k2) {} + + long long k1; + long long k2; + }; + + struct PairKeyCmp + { + static inline long compare( const PairKey &k1, const PairKey &k2 ) + { + if ( k1.k1 < k2.k1 ) + return -1; + else if ( k1.k1 > k2.k1 ) + return 1; + if ( k1.k2 < k2.k2 ) + return -1; + else if ( k1.k2 > k2.k2 ) + return 1; + else + return 0; + } + }; + + typedef BstMap< PairKey, long long, PairKeyCmp > PairKeyMap; + typedef BstMapEl< PairKey, long long > PairKeyMapEl; + + void characterClass( EquivList &equiv ); + void makeFlatClass(); + + /* Move a selected transition from ranges to default. */ + void moveToDefault( RedTransAp *defTrans, RedStateAp *state ); + + /* Pick a default transition by largest span. */ + RedTransAp *chooseDefaultSpan( RedStateAp *state ); + void chooseDefaultSpan(); + + /* Pick a default transition by most number of ranges. */ + RedTransAp *chooseDefaultNumRanges( RedStateAp *state ); + void chooseDefaultNumRanges(); + + /* Pick a default transition tailored towards goto driven machine. */ + RedTransAp *chooseDefaultGoto( RedStateAp *state ); + void chooseDefaultGoto(); + + /* Ordering states by transition connections. */ + void optimizeStateOrdering( RedStateAp *state ); + void optimizeStateOrdering(); + + /* Ordering states by transition connections. */ + void depthFirstOrdering( RedStateAp *state ); + void depthFirstOrdering(); + + void breadthFirstAdd( RedStateAp *state ); + void breadthFirstOrdering(); + + void randomizedOrdering(); + +#ifdef SCORE_ORDERING + long **scores; + void scoreSecondPass( RedStateAp *state ); + void scoreOrderingBreadth(); + void readScores(); + void scoreOrderingDepth( RedStateAp *state ); + void scoreOrderingDepth(); +#endif + + /* Set state ids. */ + void sequentialStateIds(); + void sortStateIdsByFinal(); + + /* Arrange states in by final id. This is a stable sort. */ + void sortStatesByFinal(); + + /* Sorting states by id. */ + void sortByStateId(); + + /* Locating the first final state. This is the final state with the lowest + * id. */ + void findFirstFinState(); + + void assignActionLocs(); + + RedCondAp *getErrorCond(); + RedTransAp *getErrorTrans(); + RedStateAp *getErrorState(); + + /* Is every char in the alphabet covered? */ + bool alphabetCovered( RedTransList &outRange ); + + RedTransAp *allocateTrans( RedStateAp *targ, RedAction *action ); + RedTransAp *allocateTrans( GenCondSpace *condSpace, + RedCondEl *outConds, int numConds, RedCondAp *errCond ); + + RedCondAp *allocateCond( RedStateAp *targState, RedAction *actionTable ); + + void partitionFsm( int nParts ); + + void setInTrans(); +}; + +#endif diff --git a/src/libfsm/switch.cc b/src/libfsm/switch.cc new file mode 100644 index 00000000..076f3585 --- /dev/null +++ b/src/libfsm/switch.cc @@ -0,0 +1,1036 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ragel.h" +#include "switch.h" +#include "redfsm.h" +#include "gendata.h" + +#include + +std::ostream &Switch::TRANS_GOTO( int off, RedTransAp *trans ) +{ + out << "_trans = " << off << ";\n"; + return out; +} + +void Switch::RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ) +{ + /* Get the mid position, staying on the lower end of the range. */ + int mid = (low + high) >> 1; + RedTransEl *data = state->outRange.data; + + /* Determine if we need to look higher or lower. */ + bool anyLower = mid > low; + bool anyHigher = mid < high; + + /* Determine if the keys at mid are the limits of the alphabet. */ + bool limitLow = keyOps->eq( data[mid].lowKey, lower ); + bool limitHigh = keyOps->eq( data[mid].highKey, upper ); + + if ( anyLower && anyHigher ) { + /* Can go lower and higher than mid. */ + out << "if ( " << GET_KEY() << " < " << + KEY(data[mid].lowKey) << " ) {\n"; + RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); + out << "} else if ( " << GET_KEY() << " > " << + KEY(data[mid].highKey) << " ) {\n"; + RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); + out << "} else {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value ) << "\n"; + out << "}\n"; + } + else if ( anyLower && !anyHigher ) { + /* Can go lower than mid but not higher. */ + out << "if ( " << GET_KEY() << " < " << + KEY(data[mid].lowKey) << " ) {\n"; + RANGE_B_SEARCH( state, lower, keyOps->sub( data[mid].lowKey, 1 ), low, mid-1 ); + + /* if the higher is the highest in the alphabet then there is no + * sense testing it. */ + if ( limitHigh ) { + out << "} else {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + } + else { + out << "} else if ( " << GET_KEY() << " <= " << + KEY(data[mid].highKey) << " ) {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + + out << "else {\n"; + DEFAULT( state ); + out << "}\n"; + } + } + else if ( !anyLower && anyHigher ) { + /* Can go higher than mid but not lower. */ + out << "if ( " << GET_KEY() << " > " << + KEY(data[mid].highKey) << " ) {\n"; + RANGE_B_SEARCH( state, keyOps->add( data[mid].highKey, 1 ), upper, mid+1, high ); + + /* If the lower end is the lowest in the alphabet then there is no + * sense testing it. */ + if ( limitLow ) { + out << "} else {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + } + else { + out << "} else if ( " << GET_KEY() << " >= " << + KEY(data[mid].lowKey) << " ) {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + + out << "else {\n"; + DEFAULT( state ); + out << "}\n"; + } + } + else { + /* Cannot go higher or lower than mid. It's mid or bust. What + * tests to do depends on limits of alphabet. */ + if ( !limitLow && !limitHigh ) { + out << "if ( " << KEY(data[mid].lowKey) << " <= " << + GET_KEY() << " && " << GET_KEY() << " <= " << + KEY(data[mid].highKey) << " ) {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + + out << "else {\n"; + DEFAULT( state ); + out << "}\n"; + } + else if ( limitLow && !limitHigh ) { + out << "if ( " << GET_KEY() << " <= " << + KEY(data[mid].highKey) << " ) {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + + out << "else {\n"; + DEFAULT( state ); + out << "}\n"; + } + else if ( !limitLow && limitHigh ) { + out << "if ( " << KEY(data[mid].lowKey) << " <= " << + GET_KEY() << " ) {\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + + out << "else {\n"; + DEFAULT( state ); + out << "}\n"; + } + else { + /* Both high and low are at the limit. No tests to do. */ + out << "{\n"; + TRANS_GOTO(transBase + state->outSingle.length() + (mid), data[mid].value) << "\n"; + out << "}\n"; + } + } +} + +void Switch::SINGLE_SWITCH( RedStateAp *st ) +{ + /* Load up the singles. */ + int numSingles = st->outSingle.length(); + RedTransEl *data = st->outSingle.data; + + if ( numSingles == 1 ) { + /* If there is a single single key then write it out as an if. */ + out << "\tif ( " << GET_KEY() << " == " << + KEY(data[0].lowKey) << " ) {\n\t\t"; + + /* Virtual function for writing the target of the transition. */ + TRANS_GOTO(transBase, data[0].value) << "\n"; + out << "\t}\n"; + + out << "else {\n"; + NOT_SINGLE( st ); + out << "}\n"; + } + else if ( numSingles > 1 ) { + /* Write out single keys in a switch if there is more than one. */ + out << "\tswitch( " << GET_KEY() << " ) {\n"; + + /* Write out the single indices. */ + for ( int j = 0; j < numSingles; j++ ) { + out << CASE( KEY(data[j].lowKey) ) << " {\n"; + TRANS_GOTO(transBase + j, data[j].value) << "\n"; + out << CEND() << "\n}\n"; + } + + out << CodeGen::DEFAULT() << " {\n"; + NOT_SINGLE( st ); + out << CEND() << "\n}\n"; + + /* Close off the transition switch. */ + out << "\t}\n"; + } +} + +void Switch::DEFAULT( RedStateAp *st ) +{ + if ( st->defTrans != 0 ) { + TRANS_GOTO( transBase + st->outSingle.length() + st->outRange.length(), st->defTrans ) << "\n"; + } +} + +void Switch::NOT_SINGLE( RedStateAp *st ) +{ + if ( st->outRange.length() > 0 ) { + RANGE_B_SEARCH( st, keyOps->minKey, keyOps->maxKey, + 0, st->outRange.length() - 1 ); + } + else { + DEFAULT( st ); + } +} + +void Switch::LOCATE_TRANS() +{ + transBase = 0; + + out << + " switch ( " << vCS() << " ) {\n"; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st == redFsm->errState ) { + out << CASE( STR( st->id ) ) << " {\n"; + out << CEND() << "\n}\n"; + } + else { + /* Label the state. */ + out << CASE( STR( st->id ) ) << " {\n"; + + /* Try singles. */ + if ( st->outSingle.length() > 0 ) { + SINGLE_SWITCH( st ); + } + else { + NOT_SINGLE( st ); + } + + out << CEND() << "\n}\n"; + } + + transBase += st->outSingle.length() + + st->outRange.length() + + ( st->defTrans != 0 ? 1 : 0 ); + } + + out << + " }\n" + "\n"; +} + +void Switch::genAnalysis() +{ + redFsm->sortByStateId(); + + /* Choose default transitions and the single transition. */ + redFsm->chooseDefaultSpan(); + + /* Choose the singles. */ + redFsm->moveSelectTransToSingle(); + + if ( redFsm->errState != 0 ) + redFsm->getErrorCond(); + + /* If any errors have occured in the input file then don't write anything. */ + if ( red->id->errorCount > 0 ) + return; + + /* Anlayze Machine will find the final action reference counts, among other + * things. We will use these in reporting the usage of fsm directives in + * action code. */ + red->analyzeMachine(); + + setKeyType(); + + /* Run the analysis pass over the table data. */ + setTableState( TableArray::AnalyzePass ); + tableDataPass(); + + /* Switch the tables over to the code gen mode. */ + setTableState( TableArray::GeneratePass ); +} + + +void Switch::tableDataPass() +{ + if ( type == Loop ) + taActions(); + + taKeyOffsets(); + taSingleLens(); + taRangeLens(); + taIndexOffsets(); + taIndices(); + + taTransCondSpacesWi(); + taTransOffsetsWi(); + taTransLengthsWi(); + + taTransCondSpaces(); + taTransOffsets(); + taTransLengths(); + + taCondTargs(); + taCondActions(); + + taToStateActions(); + taFromStateActions(); + taEofActions(); + taEofConds(); + taEofTrans(); + + taKeys(); + taCondKeys(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); +} + +void Switch::writeData() +{ + if ( type == Loop ) { + /* If there are any transtion functions then output the array. If there + * are none, don't bother emitting an empty array that won't be used. */ + if ( redFsm->anyActions() ) + taActions(); + } + + taKeyOffsets(); + taKeys(); + taSingleLens(); + taRangeLens(); + taIndexOffsets(); + + taTransCondSpaces(); + taTransOffsets(); + taTransLengths(); + + taCondKeys(); + taCondTargs(); + taCondActions(); + + if ( redFsm->anyToStateActions() ) + taToStateActions(); + + if ( redFsm->anyFromStateActions() ) + taFromStateActions(); + + if ( redFsm->anyEofActions() ) + taEofActions(); + + taEofConds(); + + if ( redFsm->anyEofTrans() ) + taEofTrans(); + + taNfaTargs(); + taNfaOffsets(); + taNfaPushActions(); + taNfaPopTrans(); + + STATE_IDS(); +} + + +void Switch::setKeyType() +{ + transKeys.setType( ALPH_TYPE(), alphType->size, alphType->isChar ); + transKeys.isSigned = keyOps->isSigned; +} + +void Switch::setTableState( TableArray::State state ) +{ + for ( ArrayVector::Iter i = arrayVector; i.lte(); i++ ) { + TableArray *tableArray = *i; + tableArray->setState( state ); + } +} + +void Switch::taKeyOffsets() +{ + keyOffsets.start(); + + int curKeyOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + keyOffsets.value( curKeyOffset ); + curKeyOffset += st->outSingle.length() + st->outRange.length() * 2; + } + + keyOffsets.finish(); +} + + +void Switch::taSingleLens() +{ + singleLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + singleLens.value( st->outSingle.length() ); + + singleLens.finish(); +} + + +void Switch::taRangeLens() +{ + rangeLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + rangeLens.value( st->outRange.length() ); + + rangeLens.finish(); +} + +void Switch::taIndexOffsets() +{ + indexOffsets.start(); + + int curIndOffset = 0; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Write the index offset. */ + indexOffsets.value( curIndOffset ); + + /* Move the index offset ahead. */ + curIndOffset += st->outSingle.length() + st->outRange.length(); + if ( st->defTrans != 0 ) + curIndOffset += 1; + } + + indexOffsets.finish(); +} + +void Switch::taToStateActions() +{ + toStateActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + TO_STATE_ACTION(st); + + toStateActions.finish(); +} + +void Switch::taFromStateActions() +{ + fromStateActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + FROM_STATE_ACTION(st); + + fromStateActions.finish(); +} + +void Switch::taEofActions() +{ + eofActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) + EOF_ACTION( st ); + + eofActions.finish(); +} + +void Switch::taEofConds() +{ + /* + * EOF Cond Spaces + */ + eofCondSpaces.start(); + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->outCondSpace != 0 ) + eofCondSpaces.value( st->outCondSpace->condSpaceId ); + else + eofCondSpaces.value( -1 ); + } + eofCondSpaces.finish(); + + /* + * EOF Cond Key Indixes + */ + eofCondKeyOffs.start(); + + int curOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long off = 0; + if ( st->outCondSpace != 0 ) { + off = curOffset; + curOffset += st->outCondKeys.length(); + } + eofCondKeyOffs.value( off ); + } + + eofCondKeyOffs.finish(); + + /* + * EOF Cond Key Lengths. + */ + eofCondKeyLens.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long len = 0; + if ( st->outCondSpace != 0 ) + len = st->outCondKeys.length(); + eofCondKeyLens.value( len ); + } + + eofCondKeyLens.finish(); + + /* + * EOF Cond Keys + */ + eofCondKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->outCondSpace != 0 ) { + for ( int c = 0; c < st->outCondKeys.length(); c++ ) { + CondKey key = st->outCondKeys[c]; + eofCondKeys.value( key.getVal() ); + } + } + } + + eofCondKeys.finish(); +} + +void Switch::taEofTrans() +{ + eofTrans.start(); + + /* Need to compute transition positions. */ + int totalTrans = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + totalTrans += st->outSingle.length(); + totalTrans += st->outRange.length(); + if ( st->defTrans != 0 ) + totalTrans += 1; + } + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + long trans = 0; + if ( st->eofTrans != 0 ) { + trans = totalTrans + 1; + totalTrans += 1; + } + + eofTrans.value( trans ); + } + + eofTrans.finish(); +} + +void Switch::taKeys() +{ + transKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Loop the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + transKeys.value( stel->lowKey.getVal() ); + } + + /* Loop the state's transitions. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + /* Lower key. */ + transKeys.value( rtel->lowKey.getVal() ); + + /* Upper key. */ + transKeys.value( rtel->highKey.getVal() ); + } + } + + transKeys.finish(); +} + +void Switch::taIndices() +{ + indices.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) + indices.value( stel->value->id ); + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) + indices.value( rtel->value->id ); + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) + indices.value( st->defTrans->id ); + } + + indices.finish(); +} + +void Switch::taTransCondSpaces() +{ + transCondSpaces.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + if ( trans->condSpace != 0 ) + transCondSpaces.value( trans->condSpace->condSpaceId ); + else + transCondSpaces.value( -1 ); + } + } + + transCondSpaces.finish(); +} + +void Switch::taTransOffsets() +{ + transOffsets.start(); + + int curOffset = 0; + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + transOffsets.value( curOffset ); + curOffset += trans->numConds(); + } + } + + errCondOffset = curOffset; + + transOffsets.finish(); +} + +void Switch::taTransLengths() +{ + transLengths.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + transLengths.value( trans->numConds() ); + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + transLengths.value( trans->numConds() ); + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + transLengths.value( trans->numConds() ); + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + transLengths.value( trans->numConds() ); + } + } + + transLengths.finish(); +} + +void Switch::taTransCondSpacesWi() +{ + transCondSpacesWi.start(); + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + /* Cond Space id. */ + if ( trans->condSpace != 0 ) + transCondSpacesWi.value( trans->condSpace->condSpaceId ); + else + transCondSpacesWi.value( -1 ); + } + + transCondSpacesWi.finish(); +} + +void Switch::taTransOffsetsWi() +{ + transOffsetsWi.start(); + + int curOffset = 0; + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + transOffsetsWi.value( curOffset ); + + TransApSet::Iter next = trans; + next.increment(); + + curOffset += trans->numConds(); + } + + transOffsetsWi.finish(); +} + +void Switch::taTransLengthsWi() +{ + transLengthsWi.start(); + + for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) { + transLengthsWi.value( trans->numConds() ); + + TransApSet::Iter next = trans; + next.increment(); + } + + transLengthsWi.finish(); +} + +void Switch::taCondKeys() +{ + condKeys.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + CondKey key = trans->outCondKey( c ); + condKeys.value( key.getVal() ); + } + } + } + + condKeys.finish(); +} + +void Switch::taCondTargs() +{ + condTargs.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + condTargs.value( cond->targ->id ); + } + } + } + + if ( redFsm->errCond != 0 ) { + RedCondPair *cond = &redFsm->errCond->p; + condTargs.value( cond->targ->id ); + } + + condTargs.finish(); +} + +void Switch::taCondActions() +{ + condActions.start(); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + /* Walk the singles. */ + for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) { + RedTransAp *trans = stel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + COND_ACTION( cond ); + } + } + + /* Walk the ranges. */ + for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) { + RedTransAp *trans = rtel->value; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond( c ); + COND_ACTION( cond ); + } + } + + /* The state's default index goes next. */ + if ( st->defTrans != 0 ) { + RedTransAp *trans = st->defTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + COND_ACTION( cond ); + } + } + } + + /* Add any eof transitions that have not yet been written out above. */ + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->eofTrans != 0 ) { + RedTransAp *trans = st->eofTrans; + for ( int c = 0; c < trans->numConds(); c++ ) { + RedCondPair *cond = trans->outCond(c); + COND_ACTION( cond ); + } + } + } + + if ( redFsm->errCond != 0 ) { + RedCondPair *cond = &redFsm->errCond->p; + COND_ACTION( cond ); + } + + condActions.finish(); +} + +void Switch::taNfaTargs() +{ + nfaTargs.start(); + + /* Offset of zero means no NFA targs, put a filler there. */ + nfaTargs.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaTargs.value( st->nfaTargs->length() ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + nfaTargs.value( targ->state->id ); + } + } + + nfaTargs.finish(); +} + +/* These need to mirror nfa targs. */ +void Switch::taNfaPushActions() +{ + nfaPushActions.start(); + + nfaPushActions.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + nfaPushActions.value( 0 ); + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_PUSH_ACTION( targ ); + } + } + + nfaPushActions.finish(); +} + +void Switch::taNfaPopTrans() +{ + nfaPopTrans.start(); + + nfaPopTrans.value( 0 ); + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs != 0 ) { + + nfaPopTrans.value( 0 ); + + for ( RedNfaTargs::Iter targ = *st->nfaTargs; targ.lte(); targ++ ) + NFA_POP_TEST( targ ); + } + } + + nfaPopTrans.finish(); +} + +void Switch::taNfaOffsets() +{ + nfaOffsets.start(); + + /* Offset of zero means no NFA targs, real targs start at 1. */ + long offset = 1; + + for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) { + if ( st->nfaTargs == 0 ) { + nfaOffsets.value( 0 ); + } + else { + nfaOffsets.value( offset ); + offset += 1 + st->nfaTargs->length(); + } + } + + nfaOffsets.finish(); +} + + +/* Write out the array of actions. */ +std::ostream &Switch::ACTIONS_ARRAY() +{ + out << "\t0, "; + int totalActions = 1; + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Write out the length, which will never be the last character. */ + out << act->key.length() << ", "; + /* Put in a line break every 8 */ + if ( totalActions++ % 8 == 7 ) + out << "\n\t"; + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) { + out << item->value->actionId; + if ( ! (act.last() && item.last()) ) + out << ", "; + + /* Put in a line break every 8 */ + if ( totalActions++ % 8 == 7 ) + out << "\n\t"; + } + } + out << "\n"; + return out; +} + +void Switch::taActions() +{ + actions.start(); + + /* Put "no-action" at the beginning. */ + actions.value( 0 ); + + for ( GenActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) { + /* Write out the length, which will never be the last character. */ + actions.value( act->key.length() ); + + for ( GenActionTable::Iter item = act->key; item.lte(); item++ ) + actions.value( item->value->actionId ); + } + + actions.finish(); +} + + + + diff --git a/src/libfsm/switch.h b/src/libfsm/switch.h new file mode 100644 index 00000000..7f23778b --- /dev/null +++ b/src/libfsm/switch.h @@ -0,0 +1,106 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _C_SWITCH_H +#define _C_SWITCH_H + +#include +#include "codegen.h" +#include "tables.h" + +/* Forwards. */ +struct CodeGenData; +struct NameInst; +struct RedTransAp; +struct RedStateAp; + +class Switch + : public virtual Tables +{ +protected: + enum Type { + Loop = 1, Exp + }; + +public: + Switch( const CodeGenArgs &args, Type type ) + : + Tables( args ), + type(type) + {} + + std::ostream &TRANS_GOTO( int off, RedTransAp *trans ); + void RANGE_B_SEARCH( RedStateAp *state, Key lower, Key upper, int low, int high ); + void SINGLE_SWITCH( RedStateAp *st ); + void DEFAULT( RedStateAp *st ); + void NOT_SINGLE( RedStateAp *st ); + void LOCATE_TRANS(); + +protected: + Type type; + int transBase; + + std::ostream &COND_KEYS_v1(); + std::ostream &COND_SPACES_v1(); + std::ostream &INDICES(); + std::ostream &INDEX_OFFSETS(); + std::ostream &SINGLE_LENS(); + std::ostream &RANGE_LENS(); + std::ostream &TRANS_TARGS_WI(); + std::ostream &ACTIONS_ARRAY(); + + void taKeyOffsets(); + void taSingleLens(); + void taRangeLens(); + void taIndexOffsets(); + void taIndices(); + void taTransCondSpacesWi(); + void taTransOffsetsWi(); + void taTransLengthsWi(); + void taTransCondSpaces(); + void taTransOffsets(); + void taTransLengths(); + void taCondTargs(); + void taCondActions(); + void taToStateActions(); + void taFromStateActions(); + void taEofTrans(); + void taEofConds(); + void taEofActions(); + void taKeys(); + void taActions(); + void taCondKeys(); + void taNfaTargs(); + void taNfaOffsets(); + void taNfaPushActions(); + void taNfaPopTrans(); + + void setKeyType(); + + void setTableState( TableArray::State ); + + virtual void writeData(); + virtual void tableDataPass(); + virtual void genAnalysis(); +}; + +#endif diff --git a/src/libfsm/switchbreak.cc b/src/libfsm/switchbreak.cc new file mode 100644 index 00000000..567dfbc4 --- /dev/null +++ b/src/libfsm/switchbreak.cc @@ -0,0 +1,75 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "switchbreak.h" + +void SwitchBreak::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + std::stringstream success, error; + + out << + " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + if ( red->condSpaceList.length() > 0 ) + COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); + + success << + cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; + + error << + cond << " = " << errCondOffset << ";\n"; + + out << + " {\n" + " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" + " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" + " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << error.str() << "\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << success.str() << "\n" + " break;\n" + " }\n" + " }\n" + " }\n" + ; + } + + out << EMIT_LABEL( _match_cond ); +} + diff --git a/src/libfsm/switchbreak.h b/src/libfsm/switchbreak.h new file mode 100644 index 00000000..fdbac68c --- /dev/null +++ b/src/libfsm/switchbreak.h @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_SWITCHBREAK_H +#define RAGEL_SWITCHBREAK_H + +#include "switch.h" +#include "actloop.h" +#include "actexp.h" + +struct SwitchBreak +: + public Switch, public TabBreak +{ + SwitchBreak( const CodeGenArgs &args, Switch::Type type ) + : + Tables( args ), + Switch( args, type ), + TabBreak( args ) + {} + + void LOCATE_COND(); +}; + +class SwitchBreakLoop + : public SwitchBreak, public ActLoop +{ +public: + SwitchBreakLoop( const CodeGenArgs &args ) + : + Tables( args ), + SwitchBreak( args, Loop ), + ActLoop( args ) + {} +}; + + +class SwitchBreakExp + : public SwitchBreak, public ActExp +{ +public: + SwitchBreakExp( const CodeGenArgs &args ) + : + Tables( args ), + SwitchBreak( args, Exp ), + ActExp( args ) + {} +}; + + +#endif diff --git a/src/libfsm/switchgoto.cc b/src/libfsm/switchgoto.cc new file mode 100644 index 00000000..3b293c70 --- /dev/null +++ b/src/libfsm/switchgoto.cc @@ -0,0 +1,73 @@ +/* + * Copyright 2001-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "switchgoto.h" + +void SwitchGoto::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + std::stringstream success, error; + + out << + " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + if ( red->condSpaceList.length() > 0 ) + COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); + + success << + cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; + + error << + cond << " = " << errCondOffset << ";\n"; + + out << + " {\n" + " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" + " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" + " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" + " while ( " << TRUE() << " ) {\n" + " if ( _upper < _lower ) {\n" + " " << error.str() << "\n" + " break;\n" + " }\n" + "\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << success.str() << "\n" + " break;\n" + " }\n" + " }\n" + " }\n" + ; + } +} + diff --git a/src/libfsm/switchgoto.h b/src/libfsm/switchgoto.h new file mode 100644 index 00000000..d8207325 --- /dev/null +++ b/src/libfsm/switchgoto.h @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_SWITCHGOTO_H +#define RAGEL_SWITCHGOTO_H + +#include "switch.h" +#include "actloop.h" +#include "actexp.h" + +struct SwitchGoto +: + public Switch, public TabGoto +{ + SwitchGoto( const CodeGenArgs &args, Switch::Type type ) + : + Tables( args ), + Switch( args, type ), + TabGoto( args ) + {} + + void LOCATE_COND(); +}; + +class SwitchGotoLoop + : public SwitchGoto, public ActLoop +{ +public: + SwitchGotoLoop( const CodeGenArgs &args ) + : + Tables( args ), + SwitchGoto( args, Loop ), + ActLoop( args ) + {} +}; + + +class SwitchGotoExp + : public SwitchGoto, public ActExp +{ +public: + SwitchGotoExp( const CodeGenArgs &args ) + : + Tables( args ), + SwitchGoto( args, Exp ), + ActExp( args ) + {} +}; + + +#endif diff --git a/src/libfsm/switchvar.cc b/src/libfsm/switchvar.cc new file mode 100644 index 00000000..5382bc04 --- /dev/null +++ b/src/libfsm/switchvar.cc @@ -0,0 +1,76 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "switchvar.h" +#include "parsedata.h" + +void SwitchVar::LOCATE_COND() +{ + if ( red->condSpaceList.length() > 0 ) { + std::stringstream success, error; + + out << + " " << ckeys << " = " << OFFSET( ARR_REF( condKeys ), ARR_REF( transOffsets ) + "[" + trans.ref() + "]" ) << ";\n" + " " << klen << " = " << CAST( "int" ) << ARR_REF( transLengths ) << "[" << trans << "];\n" + " " << cond << " = " << CAST( UINT() ) << ARR_REF( transOffsets ) << "[" << trans << "];\n" + "\n"; + + out << + " " << cpc << " = 0;\n"; + + if ( red->condSpaceList.length() > 0 ) + COND_EXEC( ARR_REF( transCondSpaces ) + "[" + trans.ref() + "]" ); + + success << + cond << " += " << CAST( UINT() ) << "(_mid - " << ckeys << ");\n"; + + error << + cond << " = " << errCondOffset << ";\n"; + + out << + " {\n" + " " << INDEX( ARR_TYPE( condKeys ), "_lower" ) << " = " << ckeys << ";\n" + " " << INDEX( ARR_TYPE( condKeys ), "_upper" ) << " = " << ckeys << " + " << klen << " - 1;\n" + " " << INDEX( ARR_TYPE( condKeys ), "_mid" ) << ";\n" + " _bsc = 1;\n" + " while ( _bsc == 1 ) {\n" + " if ( _upper < _lower ) {\n" + " " << error.str() << "\n" + " _bsc = 0;\n" + " }\n" + " else {\n" + " _mid = _lower + ((_upper-_lower) >> 1);\n" + " if ( " << cpc << " < " << CAST("int") << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _upper = _mid - 1;\n" + " else if ( " << cpc << " > " << CAST( "int" ) << DEREF( ARR_REF( condKeys ), "_mid" ) << " )\n" + " _lower = _mid + 1;\n" + " else {\n" + " " << success.str() << "\n" + " _bsc = 0;\n" + " }\n" + " }\n" + " }\n" + " }\n" + ; + } +} + diff --git a/src/libfsm/switchvar.h b/src/libfsm/switchvar.h new file mode 100644 index 00000000..220963a4 --- /dev/null +++ b/src/libfsm/switchvar.h @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAGEL_SWITCHVAR_H +#define RAGEL_SWITCHVAR_H + +#include "switch.h" +#include "actloop.h" +#include "actexp.h" + +struct SwitchVar +: + public Switch, public TabVar +{ + SwitchVar( const CodeGenArgs &args, Switch::Type type ) + : + Tables( args ), + Switch( args, type ), + TabVar( args ) + {} + + void VAR_COND_BIN_SEARCH( Variable &var, TableArray &keys, std::string ok, std::string error ); + + //void LOCATE_TRANS(); + void LOCATE_COND(); +}; + +class SwitchVarLoop + : public SwitchVar, public ActLoop +{ +public: + SwitchVarLoop( const CodeGenArgs &args ) + : + Tables( args ), + SwitchVar( args, Loop ), + ActLoop( args ) + {} +}; + +class SwitchVarExp +: + public SwitchVar, public ActExp +{ +public: + SwitchVarExp( const CodeGenArgs &args ) + : + Tables( args ), + SwitchVar( args, Exp ), + ActExp( args ) + {} +}; + +#endif diff --git a/src/libfsm/tabbreak.cc b/src/libfsm/tabbreak.cc new file mode 100644 index 00000000..ee82cc0c --- /dev/null +++ b/src/libfsm/tabbreak.cc @@ -0,0 +1,379 @@ +/* + * Copyright 2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tables.h" +#include "binary.h" +#include "flat.h" + +std::string TabBreak::BREAK( GotoLabel &label ) +{ + string ret = "break"; + if ( loopLabels ) { + ret += " "; + ret += label.ref(); + } + return ret; +} + +std::string TabBreak::CONTINUE( GotoLabel &label ) +{ + string ret = "continue"; + if ( loopLabels ) { + ret += " "; + ret += label.ref(); + } + return ret; +} + +std::string TabBreak::BREAK_LABEL( GotoLabel &label ) +{ + if ( loopLabels ) { + if ( label.isReferenced ) + return std::string(label.name) + "::\n"; + } + return ""; +} + +void TabBreak::CONTROL_JUMP( ostream &ret, bool inFinish ) +{ + ret << "if ( " << TRUE() << " ) break " << _again << ";"; +} + +void TabBreak::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << ";"; + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabBreak::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabBreak::CALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << + vCS() << "; " << TOP() << " += 1;" << vCS() << " = " << + callDest << ";"; + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabBreak::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << + TOP() << " += 1;" << vCS() << " = " << + callDest << "; " << CLOSE_GEN_BLOCK(); +} + +void TabBreak::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << + vCS() << "; " << TOP() << " += 1;" << vCS() << + " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabBreak::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << vCS() << + " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); +} + +void TabBreak::RET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabBreak::NRET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << CLOSE_GEN_BLOCK(); +} + +void TabBreak::BREAK( ostream &ret, int targState, bool csForced ) +{ + ret << + OPEN_GEN_BLOCK() << + P() << " += 1; " << + "break " << _resume << "; " << + CLOSE_GEN_BLOCK(); +} + +void TabBreak::NBREAK( ostream &ret, int targState, bool csForced ) +{ + ret << + OPEN_GEN_BLOCK() << + P() << " += 1; " << + nbreak << " = 1;" << + CLOSE_GEN_BLOCK(); +} + +void TabBreak::writeExec() +{ + out << + " {\n"; + + DECLARE( INT(), ps ); + DECLARE( INT(), cpc ); + DECLARE( INT(), nbreak ); + DECLARE( INT(), klen ); + DECLARE( INDEX( ARR_TYPE( condKeys ) ), ckeys ); + DECLARE( INDEX( ARR_TYPE( eofCondKeys ) ), cekeys ); + DECLARE( UINT(), trans, " = 0" ); + DECLARE( UINT(), cond, " = 0" ); + DECLARE( INDEX( ALPH_TYPE() ), keys ); + DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); + DECLARE( INDEX( ARR_TYPE( indices ) ), inds ); + DECLARE( UINT(), nacts ); + DECLARE( INT(), have ); + DECLARE( INT(), pop_test ); + DECLARE( INT(), new_recs ); + DECLARE( INT(), alt ); + DECLARE( INT(), ic ); + + out << BREAK_LABEL( _resume ); + + /* Do we break out on no more input. */ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + if ( !noEnd ) { + if ( eof ) { + out << + " while ( " << P() << " != " << PE() << " || " << P() << " == " << vEOF() << " ) {\n"; + } + else { + out << + " while ( " << P() << " != " << PE() << " ) {\n"; + } + } + else { + out << + " while ( " << TRUE() << " ) {\n"; + + } + + NFA_PUSH( vCS() ); + + if ( loopLabels ) { + out << BREAK_LABEL( _again ); + out << "while ( " << TRUE() << " ) {\n"; + } + + FROM_STATE_ACTIONS(); + + if ( !noEnd && eof ) { + out << + "if ( " << P() << " == " << vEOF() << " ) {\n"; + + if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) { + if ( redFsm->anyEofTrans() ) { + out << + " if ( " << ARR_REF( eofTrans ) << "[" << vCS() << "] > 0 ) {\n" + " " << trans << " = " << + CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n" + " }\n"; + } + } + + out << + "}\n" + "else {\n"; + } + + LOCATE_TRANS(); + + if ( !noEnd && eof ) { + out << + "}\n"; + } + + LOCATE_COND(); + + if ( redFsm->anyRegCurStateRef() ) + out << " " << ps << " = " << vCS() << ";\n"; + + string condVar = + red->condSpaceList.length() != 0 ? cond.ref() : trans.ref(); + + out << + " " << vCS() << " = " << CAST(INT()) << ARR_REF( condTargs ) << "[" << condVar << "];\n\n"; + + if ( redFsm->anyRegActions() ) { + out << + " if ( " << ARR_REF( condActions ) << "[" << condVar << "] != 0 ) {\n" + "\n"; + + if ( redFsm->anyRegNbreak() ) + out << " " << nbreak << " = 0;\n"; + + REG_ACTIONS( condVar ); + + if ( redFsm->anyRegNbreak() ) { + out << + " if ( " << nbreak << " == 1 )\n" + " " << BREAK( _resume ) << ";\n"; + } + + out << "}\n"; + } + + + if ( loopLabels ) { + out << BREAK( _again ) << ";\n}\n"; + } + else { + out << "\n" << EMIT_LABEL( _again ); + } + + if ( !noEnd && eof ) { + out << + " if ( " << P() << " == " << vEOF() << " ) {\n" + " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" + " " << BREAK( _resume ) << ";\n" + " }\n" + " else {\n"; + } + + TO_STATE_ACTIONS(); + + if ( redFsm->errState != 0 ) { + out << + " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; + } + + out << + " " << P() << " += 1;\n" + " " << CONTINUE( _resume ) << ";\n"; + + if ( redFsm->errState != 0 ) { + out << + " }\n"; + } + + if ( !noEnd && eof ) { + out << + " }\n"; + } + + if ( redFsm->anyNfaStates() ) { + out << + " if ( nfa_len == 0 )\n" + " " << BREAK ( _resume ) << ";\n" + "\n" + " nfa_count += 1;\n" + " nfa_len -= 1;\n" + " " << P() << " = nfa_bp[nfa_len].p;\n" + ; + + if ( redFsm->bAnyNfaPops ) { + NFA_FROM_STATE_ACTION_EXEC(); + + NFA_POP_TEST_EXEC(); + + out << + " if ( " << pop_test << " )\n" + " " << vCS() << " = nfa_bp[nfa_len].state;\n" + " else\n" + " " << vCS() << " = " << ERROR_STATE() << ";\n"; + } + else { + out << + " " << vCS() << " = nfa_bp[nfa_len].state;\n"; + + } + + NFA_POST_POP(); + } + else { + out << + " " << BREAK( _resume ) << ";\n"; + } + + out << + "}\n"; + + out << EMIT_LABEL( _out ); + + out << " }\n"; +} + diff --git a/src/libfsm/tabgoto.cc b/src/libfsm/tabgoto.cc new file mode 100644 index 00000000..ca90cb9d --- /dev/null +++ b/src/libfsm/tabgoto.cc @@ -0,0 +1,330 @@ +/* + * Copyright 2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tables.h" +#include "binary.h" +#include "flat.h" + +void TabGoto::CONTROL_JUMP( ostream &ret, bool inFinish ) +{ + ret << "goto " << _again << ";"; +} + +void TabGoto::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << ";"; + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabGoto::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabGoto::CALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << + vCS() << "; " << TOP() << " += 1;" << vCS() << " = " << + callDest << ";"; + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabGoto::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << + TOP() << " += 1;" << vCS() << " = " << + callDest << "; " << CLOSE_GEN_BLOCK(); +} + +void TabGoto::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << + vCS() << "; " << TOP() << " += 1;" << vCS() << + " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";"; + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabGoto::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << vCS() << "; " << TOP() << " += 1;" << vCS() << + " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << "; " << CLOSE_GEN_BLOCK(); +} + +void TabGoto::RET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + CONTROL_JUMP( ret, inFinish ); + ret << CLOSE_GEN_BLOCK(); +} + +void TabGoto::NRET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << " -= 1;" << vCS() << " = " << STACK() << "[" << TOP() << "];"; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << CLOSE_GEN_BLOCK(); +} + +void TabGoto::BREAK( ostream &ret, int targState, bool csForced ) +{ + ret << + OPEN_GEN_BLOCK() << + P() << " += 1; " << + "goto " << _out << "; " << + CLOSE_GEN_BLOCK(); +} + +void TabGoto::NBREAK( ostream &ret, int targState, bool csForced ) +{ + ret << + OPEN_GEN_BLOCK() << + P() << " += 1; " << + nbreak << " = 1;" << + CLOSE_GEN_BLOCK(); +} + +void TabGoto::writeExec() +{ + out << + " {\n"; + + DECLARE( INT(), ps ); + DECLARE( INT(), cpc ); + DECLARE( INT(), nbreak ); + DECLARE( INT(), klen ); + DECLARE( INDEX( ARR_TYPE( condKeys ) ), ckeys ); + DECLARE( INDEX( ARR_TYPE( eofCondKeys ) ), cekeys ); + DECLARE( UINT(), trans, " = 0" ); + DECLARE( UINT(), cond, " = 0" ); + DECLARE( INDEX( ALPH_TYPE() ), keys ); + DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); + DECLARE( INDEX( ARR_TYPE( indices ) ), inds ); + DECLARE( UINT(), nacts ); + DECLARE( INT(), pop_test ); + DECLARE( INT(), new_recs ); + DECLARE( INT(), alt ); + DECLARE( INT(), ic ); + + out << EMIT_LABEL( _resume ); + + /* Do we break out on no more input. */ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + if ( !noEnd ) { + if ( eof ) { + out << + " if ( " << P() << " == " << PE() << " && " << P() << " != " << vEOF() << " )\n" + " goto " << _out << ";\n"; + } + else { + out << + " if ( " << P() << " == " << PE() << " )\n" + " goto " << _out << ";\n"; + } + } + + NFA_PUSH( vCS() ); + + FROM_STATE_ACTIONS(); + + if ( !noEnd && eof ) { + out << + "if ( " << P() << " == " << vEOF() << " ) {\n"; + + if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) { + if ( redFsm->anyEofTrans() ) { + out << + " if ( " << ARR_REF( eofTrans ) << "[" << vCS() << "] > 0 ) {\n" + " " << trans << " = " << + CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n" + " }\n"; + } + } + + out << + "}\n" + "else {\n"; + } + + LOCATE_TRANS(); + + if ( !noEnd && eof ) { + out << + "}\n"; + } + + LOCATE_COND(); + + if ( redFsm->anyRegCurStateRef() ) + out << " " << ps << " = " << vCS() << ";\n"; + + string condVar = + red->condSpaceList.length() != 0 ? cond.ref() : trans.ref(); + + out << + " " << vCS() << " = " << CAST(INT()) << ARR_REF( condTargs ) << "[" << condVar << "];\n\n"; + + if ( redFsm->anyRegActions() ) { + out << + " if ( " << ARR_REF( condActions ) << "[" << condVar << "] != 0 ) {\n" + "\n"; + + if ( redFsm->anyRegNbreak() ) + out << " " << nbreak << " = 0;\n"; + + REG_ACTIONS( condVar ); + + if ( redFsm->anyRegNbreak() ) { + out << + " if ( " << nbreak << " == 1 )\n" + " goto " << _out << ";\n"; + } + + out << "}\n"; + } + + out << "\n" << EMIT_LABEL( _again ); + + if ( !noEnd && eof ) { + out << + " if ( " << P() << " == " << vEOF() << " ) {\n" + " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" + " goto " << _out << ";\n" + " }\n" + " else {\n"; + } + + TO_STATE_ACTIONS(); + + if ( redFsm->errState != 0 ) { + out << + " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; + } + + out << + " " << P() << " += 1;\n" + " goto " << _resume << ";\n"; + + if ( redFsm->errState != 0 ) { + out << + " }\n"; + } + + if ( !noEnd && eof ) { + out << + " }\n"; + } + + if ( redFsm->anyNfaStates() ) { + out << + " if ( nfa_len == 0 )\n" + " goto " << _out << ";\n" + "\n" + " nfa_count += 1;\n" + " nfa_len -= 1;\n" + " " << P() << " = nfa_bp[nfa_len].p;\n" + ; + + if ( redFsm->bAnyNfaPops ) { + NFA_FROM_STATE_ACTION_EXEC(); + + NFA_POP_TEST_EXEC(); + + out << + " if ( " << pop_test << " )\n" + " " << vCS() << " = nfa_bp[nfa_len].state;\n" + " else\n" + " " << vCS() << " = " << ERROR_STATE() << ";\n"; + } + else { + out << + " " << vCS() << " = nfa_bp[nfa_len].state;\n"; + + } + + NFA_POST_POP(); + + out << "goto " << _resume << ";\n"; + } + + out << EMIT_LABEL( _out ); + + out << " }\n"; +} + diff --git a/src/libfsm/tables.cc b/src/libfsm/tables.cc new file mode 100644 index 00000000..40edd93e --- /dev/null +++ b/src/libfsm/tables.cc @@ -0,0 +1,81 @@ +/* + * Copyright 2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tables.h" + +void Tables::CURS( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_EXPR() << ps << CLOSE_GEN_EXPR(); +} + +void Tables::TARGS( ostream &ret, bool inFinish, int targState ) +{ + ret << OPEN_GEN_EXPR() << vCS() << CLOSE_GEN_EXPR(); +} + +void Tables::NEXT( ostream &ret, int nextDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << nextDest << ";" << CLOSE_GEN_BLOCK(); +} + +void Tables::NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << "" << vCS() << " = " << OPEN_HOST_EXPR(); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";" << CLOSE_GEN_BLOCK(); +} + +void Tables::EOF_TRANS() +{ + out << + "" << trans << " = " << CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n"; + + if ( red->condSpaceList.length() > 0 ) { + out << + "" << cond << " = " << CAST(UINT()) << ARR_REF( transOffsets ) << "[" << trans << "];\n"; + } +} + +void Tables::COND_EXEC( std::string expr ) +{ + out << + " switch ( " << expr << " ) {\n" + "\n"; + + for ( CondSpaceList::Iter csi = red->condSpaceList; csi.lte(); csi++ ) { + GenCondSpace *condSpace = csi; + out << " " << CASE( STR( condSpace->condSpaceId ) ) << " {\n"; + for ( GenCondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) { + out << "if ( "; + CONDITION( out, *csi ); + Size condValOffset = (1 << csi.pos()); + out << " ) " << cpc << " += " << condValOffset << ";\n"; + } + + out << + " " << CEND() << "\n}\n"; + } + + out << + " }\n"; +} + diff --git a/src/libfsm/tables.h b/src/libfsm/tables.h new file mode 100644 index 00000000..258f869e --- /dev/null +++ b/src/libfsm/tables.h @@ -0,0 +1,265 @@ +/* + * Copyright 2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TABLES_H +#define _TABLES_H + +#include +#include "codegen.h" + +struct Tables +: + public CodeGen +{ + Tables( const CodeGenArgs &args ) + : + CodeGen( args ), + + pa( "_pa" ), + klen( "_klen" ), + ckeys( "_ckeys" ), + cekeys( "_cekeys" ), + trans( "_trans" ), + cond( "_cond" ), + keys( "_keys" ), + acts( "_acts" ), + nacts( "_nacts" ), + inds( "_inds" ), + + cont( "_cont" ), + nfa_repeat( "_nfa_repeat" ), + nfa_test( "_nfa_test" ), + ps( "_ps" ), + nbreak( "_nbreak" ), + have( "__have" ), + ic( "_ic" ), + + _out("_out"), + _pop("_pop"), + _test_eof( "_test_eof" ), + _resume( "_resume" ), + _match_cond( "_match_cond" ), + _again( "_again" ), + _match( "_match" ), + _eof_goto( "_eof_goto" ), + + actions( "actions", *this ), + transKeys( "trans_keys", *this ), + charClass( "char_class", *this ), + flatIndexOffset( "index_offsets", *this ), + indices( "indices", *this ), + indexDefaults( "index_defaults", *this ), + transCondSpaces( "trans_cond_spaces", *this ), + transOffsets( "trans_offsets", *this ), + condTargs( "cond_targs", *this ), + condActions( "cond_actions", *this ), + toStateActions( "to_state_actions", *this ), + fromStateActions( "from_state_actions", *this ), + eofCondSpaces( "eof_cond_spaces", *this ), + eofCondKeyOffs( "eof_cond_key_offs", *this ), + eofCondKeyLens( "eof_cond_key_lens", *this ), + eofCondKeys( "eof_cond_keys", *this ), + eofActions( "eof_actions", *this ), + eofTrans( "eof_trans", *this ), + + keyOffsets( "key_offsets", *this ), + singleLens( "single_lengths", *this ), + rangeLens( "range_lengths", *this ), + indexOffsets( "index_offsets", *this ), + transCondSpacesWi( "trans_cond_spaces_wi", *this ), + transOffsetsWi( "trans_offsets_wi", *this ), + transLengthsWi( "trans_lengths_wi", *this ), + transLengths( "trans_lengths", *this ), + condKeys( "cond_keys", *this ) + {} + + Variable pa; + Variable klen; + Variable ckeys; + Variable cekeys; + Variable trans; + Variable cond; + Variable keys; + Variable acts; + Variable nacts; + Variable inds; + Variable cont; + Variable nfa_repeat; + Variable nfa_test; + Variable ps; + Variable nbreak; + Variable have; + Variable ic; + + GotoLabel _out; + GotoLabel _pop; + GotoLabel _test_eof; + GotoLabel _resume; + GotoLabel _match_cond; + GotoLabel _again; + GotoLabel _match; + GotoLabel _eof_goto; + + TableArray actions; + TableArray transKeys; + TableArray charClass; + TableArray flatIndexOffset; + TableArray indices; + TableArray indexDefaults; + TableArray transCondSpaces; + TableArray transOffsets; + TableArray condTargs; + TableArray condActions; + TableArray toStateActions; + TableArray fromStateActions; + TableArray eofCondSpaces; + TableArray eofCondKeyOffs; + TableArray eofCondKeyLens; + TableArray eofCondKeys; + TableArray eofActions; + TableArray eofTrans; + + TableArray keyOffsets; + TableArray singleLens; + TableArray rangeLens; + TableArray indexOffsets; + TableArray transCondSpacesWi; + TableArray transOffsetsWi; + TableArray transLengthsWi; + TableArray transLengths; + TableArray condKeys; + + int errCondOffset; + + virtual void TO_STATE_ACTION( RedStateAp *state ) = 0; + virtual void FROM_STATE_ACTION( RedStateAp *state ) = 0; + virtual void EOF_ACTION( RedStateAp *state ) = 0; + virtual void COND_ACTION( RedCondPair *cond ) = 0; + + virtual void NFA_PUSH_ACTION( RedNfaTarg *targ ) = 0; + virtual void NFA_POP_TEST( RedNfaTarg *targ ) = 0; + virtual void NFA_FROM_STATE_ACTION_EXEC() = 0; + + virtual void FROM_STATE_ACTIONS() = 0; + virtual void REG_ACTIONS( std::string cond ) = 0; + virtual void TO_STATE_ACTIONS() = 0; + virtual void EOF_ACTIONS() = 0; + + void CURS( ostream &ret, bool inFinish ); + void TARGS( ostream &ret, bool inFinish, int targState ); + void NEXT( ostream &ret, int nextDest, bool inFinish ); + void NEXT_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void EOF_TRANS(); + void COND_EXEC( std::string expr ); +}; + +struct TabGoto +: + public virtual Tables +{ + TabGoto( const CodeGenArgs &args ) + : + Tables( args ) + {} + + void CONTROL_JUMP( ostream &ret, bool inFinish ); + + void GOTO( ostream &ret, int gotoDest, bool inFinish ); + void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void CALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); + void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void RET( ostream &ret, bool inFinish ); + void NRET( ostream &ret, bool inFinish ); + void BREAK( ostream &ret, int targState, bool csForced ); + void NBREAK( ostream &ret, int targState, bool csForced ); + + void NFA_POP() {} + + void writeExec(); +}; + +struct TabBreak +: + public virtual Tables +{ + TabBreak( const CodeGenArgs &args ) + : + Tables( args ), + loopLabels( args.loopLabels ) + {} + + void CONTROL_JUMP( ostream &ret, bool inFinish ); + + void GOTO( ostream &ret, int gotoDest, bool inFinish ); + void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void CALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); + void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void RET( ostream &ret, bool inFinish ); + void NRET( ostream &ret, bool inFinish ); + void BREAK( ostream &ret, int targState, bool csForced ); + void NBREAK( ostream &ret, int targState, bool csForced ); + + void NFA_POP() {} + + void writeExec(); + + bool loopLabels; + std::string BREAK( GotoLabel &label ); + std::string CONTINUE( GotoLabel &label ); + std::string BREAK_LABEL( GotoLabel &label ); +}; + +struct TabVar +: + public virtual Tables +{ + TabVar( const CodeGenArgs &args ) + : + Tables( args ) + {} + + void GOTO( ostream &ret, int gotoDest, bool inFinish ); + void GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ); + void CALL( ostream &ret, int callDest, int targState, bool inFinish ); + void NCALL( ostream &ret, int callDest, int targState, bool inFinish ); + void CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ); + void RET( ostream &ret, bool inFinish ); + void NRET( ostream &ret, bool inFinish ); + void BREAK( ostream &ret, int targState, bool csForced ); + void NBREAK( ostream &ret, int targState, bool csForced ); + + void NFA_POP() {} + + std::string BREAK( GotoLabel &label ); + std::string CONTINUE( GotoLabel &label ); + std::string BREAK_LABEL( GotoLabel &label ); + + void writeExec(); +}; + + +#endif diff --git a/src/libfsm/tabvar.cc b/src/libfsm/tabvar.cc new file mode 100644 index 00000000..02bd7b55 --- /dev/null +++ b/src/libfsm/tabvar.cc @@ -0,0 +1,332 @@ +/* + * Copyright 2018 Adrian Thurston + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tables.h" +#include "flatvar.h" +#include "binvar.h" + +std::string TabVar::BREAK( GotoLabel &label ) +{ + return "{ _cont = 0; _again = 0; }"; +} + +std::string TabVar::CONTINUE( GotoLabel &label ) +{ + return "{ _cont = 0; _again = 1; }"; +} + +std::string TabVar::BREAK_LABEL( GotoLabel &label ) +{ + return ""; +} + +void TabVar::GOTO( ostream &ret, int gotoDest, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << gotoDest << ";" << CLOSE_GEN_BLOCK(); +} + +void TabVar::GOTO_EXPR( ostream &ret, GenInlineItem *ilItem, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << vCS() << " = " << OPEN_HOST_EXPR( "-", 1 ); + INLINE_LIST( ret, ilItem->children, 0, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";" << CLOSE_GEN_BLOCK(); +} + +void TabVar::CALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + red->id->error() << "cannot use fcall in -B mode" << std::endl; + red->id->abortCompile( 1 ); +} + +void TabVar::NCALL( ostream &ret, int callDest, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << + vCS() << "; " << TOP() << " += 1;" << vCS() << " = " << + callDest << ";" << CLOSE_GEN_BLOCK(); +} + +void TabVar::CALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + red->id->error() << "cannot use fcall in -B mode" << std::endl; + red->id->abortCompile( 1 ); +} + +void TabVar::NCALL_EXPR( ostream &ret, GenInlineItem *ilItem, int targState, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK(); + + if ( red->prePushExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->prePushExpr ); + INLINE_LIST( ret, red->prePushExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << STACK() << "[" << TOP() << "] = " << + vCS() << "; " << TOP() << " += 1;" << vCS() << + " = " << OPEN_HOST_EXPR( "-", 1 ); + INLINE_LIST( ret, ilItem->children, targState, inFinish, false ); + ret << CLOSE_HOST_EXPR() << ";" << CLOSE_GEN_BLOCK(); +} + +void TabVar::RET( ostream &ret, bool inFinish ) +{ + red->id->error() << "cannot use fret in -B mode" << std::endl; + red->id->abortCompile( 1 ); +} + +void TabVar::NRET( ostream &ret, bool inFinish ) +{ + ret << OPEN_GEN_BLOCK() << TOP() << "-= 1;" << vCS() << " = " << + STACK() << "[" << TOP() << "]; "; + + if ( red->postPopExpr != 0 ) { + ret << OPEN_HOST_BLOCK( red->postPopExpr ); + INLINE_LIST( ret, red->postPopExpr->inlineList, 0, false, false ); + ret << CLOSE_HOST_BLOCK(); + } + + ret << CLOSE_GEN_BLOCK(); +} + +void TabVar::BREAK( ostream &ret, int targState, bool csForced ) +{ + red->id->error() << "cannot use fbreak in -B mode" << std::endl; + red->id->abortCompile( 1 ); +} + +void TabVar::NBREAK( ostream &ret, int targState, bool csForced ) +{ + ret << + OPEN_GEN_BLOCK() << + P() << "+= 1;\n" << + nbreak << " = 1;" << + CLOSE_GEN_BLOCK(); +} + +void TabVar::writeExec() +{ + out << + "{\n"; + + DECLARE( INT(), ps ); + DECLARE( INT(), cpc ); + DECLARE( INT(), nbreak ); + DECLARE( INT(), klen ); + DECLARE( INDEX( ARR_TYPE( condKeys ) ), ckeys ); + DECLARE( INDEX( ARR_TYPE( eofCondKeys ) ), cekeys ); + DECLARE( UINT(), trans, " = 0" ); + DECLARE( UINT(), cond, " = 0" ); + DECLARE( INDEX( ALPH_TYPE() ), keys ); + DECLARE( INDEX( ARR_TYPE( actions ) ), acts ); + DECLARE( INDEX( ARR_TYPE( indices ) ), inds ); + DECLARE( UINT(), nacts ); + DECLARE( INT(), have ); + DECLARE( INT(), pop_test ); + DECLARE( INT(), new_recs ); + DECLARE( INT(), alt ); + DECLARE( INT(), ic ); + + out << UINT() << " _have = 0;\n"; + out << UINT() << " _cont = 1;\n"; + out << UINT() << " _again = 1;\n"; + out << UINT() << " _bsc = 1;\n"; + + out << BREAK_LABEL( _resume ); + + /* Do we break out on no more input. */ + bool eof = redFsm->anyEofActivity() || redFsm->anyNfaStates(); + if ( !noEnd ) { + if ( eof ) { + out << + " while ( _again == 1 && ( " << P() << " != " << PE() << " || " << P() << " == " << vEOF() << " ) ) {\n"; + } + else { + out << + " while ( _again == 1 && " << P() << " != " << PE() << " ) {\n"; + } + } + else { + out << + " while ( _again == 1 ) {\n"; + + } + + out << "_cont = 1;\n"; + out << "_again = 1;\n"; + + NFA_PUSH( vCS() ); + + FROM_STATE_ACTIONS(); + + if ( !noEnd && eof ) { + out << + "if ( " << P() << " == " << vEOF() << " ) {\n"; + + if ( redFsm->anyEofTrans() || redFsm->anyEofActions() ) { + if ( redFsm->anyEofTrans() ) { + out << + " if ( " << ARR_REF( eofTrans ) << "[" << vCS() << "] > 0 ) {\n" + " " << trans << " = " << + CAST(UINT()) << ARR_REF( eofTrans ) << "[" << vCS() << "] - 1;\n" + " }\n"; + } + } + + out << + "}\n" + "else {\n"; + } + + LOCATE_TRANS(); + + if ( !noEnd && eof ) { + out << + "}\n"; + } + + LOCATE_COND(); + + if ( redFsm->anyRegCurStateRef() ) + out << " " << ps << " = " << vCS() << ";\n"; + + string condVar = + red->condSpaceList.length() != 0 ? cond.ref() : trans.ref(); + + out << + " " << vCS() << " = " << CAST(INT()) << ARR_REF( condTargs ) << "[" << condVar << "];\n\n"; + + if ( redFsm->anyRegActions() ) { + out << + " if ( " << ARR_REF( condActions ) << "[" << condVar << "] != 0 ) {\n" + "\n"; + + if ( redFsm->anyRegNbreak() ) + out << " " << nbreak << " = 0;\n"; + + REG_ACTIONS( condVar ); + + if ( redFsm->anyRegNbreak() ) { + out << + " if ( " << nbreak << " == 1 )\n" + " " << BREAK( _resume ) << "\n"; + } + + out << "}\n"; + } + + out << "if ( _cont == 1 ) {\n"; + + out << "\n" << EMIT_LABEL( _again ); + + if ( !noEnd && eof ) { + out << + " if ( " << P() << " == " << vEOF() << " ) {\n" + " if ( " << vCS() << " >= " << FIRST_FINAL_STATE() << " )\n" + " " << BREAK( _resume ) << "\n" + " }\n" + " else {\n"; + } + + TO_STATE_ACTIONS(); + + if ( redFsm->errState != 0 ) { + out << + " if ( " << vCS() << " != " << redFsm->errState->id << " ) {\n"; + } + + out << + " " << P() << " += 1;\n" + " " << CONTINUE( _resume ) << "\n"; + + if ( redFsm->errState != 0 ) { + out << + " }\n"; + } + + if ( !noEnd && eof ) { + out << + " }\n"; + } + + out << "if ( _cont == 1 ) {\n"; + + if ( redFsm->anyNfaStates() ) { + out << + " if ( nfa_len == 0 )\n" + " " << BREAK ( _resume ) << "\n" + "\n"; + + out << "if ( _cont == 1 ) {\n"; + + out << + " nfa_count += 1;\n" + " nfa_len -= 1;\n" + " " << P() << " = nfa_bp[nfa_len].p;\n" + ; + + if ( redFsm->bAnyNfaPops ) { + NFA_FROM_STATE_ACTION_EXEC(); + + NFA_POP_TEST_EXEC(); + + out << + " if ( " << pop_test << " )\n" + " " << vCS() << " = nfa_bp[nfa_len].state;\n" + " else\n" + " " << vCS() << " = " << ERROR_STATE() << ";\n"; + } + else { + out << + " " << vCS() << " = nfa_bp[nfa_len].state;\n"; + + } + + NFA_POST_POP(); + + /* cont */ + out << "}\n"; + } + else { + out << + " " << BREAK( _resume ) << "\n"; + } + + /* cont */ + out << "}}\n"; + + /* P loop. */ + out << "}\n"; + + out << EMIT_LABEL( _out ); + + /* Variable decl. */ + out << "}\n"; +} + -- cgit v1.2.1