summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdrian Thurston <thurston@colm.net>2020-10-18 11:44:43 -0700
committerAdrian Thurston <thurston@colm.net>2020-10-18 11:44:43 -0700
commit85b76476de71f43d3eb25d6bef4ee6d84cb71f6c (patch)
tree65c127fbcf70e62d8a4848be2c9c4ff7d74d86a1 /src
parent86bb5882a70224a29650ccfa1e46c9b023c2a3ef (diff)
downloadcolm-85b76476de71f43d3eb25d6bef4ee6d84cb71f6c.tar.gz
lift all source code into src/ dirinto-src
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/aapl/.gitignore2
-rw-r--r--src/aapl/COPYING20
-rw-r--r--src/aapl/ChangeLog562
-rw-r--r--src/aapl/Makefile.am7
-rw-r--r--src/aapl/README6
-rw-r--r--src/aapl/astring.h862
-rw-r--r--src/aapl/avlbasic.h66
-rw-r--r--src/aapl/avlcommon.h1637
-rw-r--r--src/aapl/avlibasic.h68
-rw-r--r--src/aapl/avlikeyless.h65
-rw-r--r--src/aapl/avlimap.h78
-rw-r--r--src/aapl/avlimel.h80
-rw-r--r--src/aapl/avlimelkey.h77
-rw-r--r--src/aapl/avliset.h76
-rw-r--r--src/aapl/avlitree.h79
-rw-r--r--src/aapl/avlkeyless.h59
-rw-r--r--src/aapl/avlmap.h75
-rw-r--r--src/aapl/avlmel.h75
-rw-r--r--src/aapl/avlmelkey.h72
-rw-r--r--src/aapl/avlset.h71
-rw-r--r--src/aapl/avltree.h74
-rw-r--r--src/aapl/bstcommon.h815
-rw-r--r--src/aapl/bstmap.h114
-rw-r--r--src/aapl/bstset.h87
-rw-r--r--src/aapl/bsttable.h85
-rw-r--r--src/aapl/bubblesort.h95
-rw-r--r--src/aapl/buffer.h60
-rw-r--r--src/aapl/compare.h269
-rw-r--r--src/aapl/dlcommon.h791
-rw-r--r--src/aapl/dlist.h65
-rw-r--r--src/aapl/dlistmel.h72
-rw-r--r--src/aapl/dlistval.h72
-rw-r--r--src/aapl/insertsort.h95
-rw-r--r--src/aapl/mergesort.h141
-rw-r--r--src/aapl/quicksort.h186
-rw-r--r--src/aapl/resize.h345
-rw-r--r--src/aapl/rope.h237
-rw-r--r--src/aapl/sbstmap.h122
-rw-r--r--src/aapl/sbstset.h95
-rw-r--r--src/aapl/sbsttable.h94
-rw-r--r--src/aapl/svector.h1351
-rw-r--r--src/aapl/table.h253
-rw-r--r--src/aapl/vector.h1190
-rw-r--r--src/aapl/version.mk2
-rw-r--r--src/cgil/.gitignore2
-rw-r--r--src/cgil/Makefile.am9
-rw-r--r--src/cgil/ragel.lm1027
-rw-r--r--src/cgil/ril.lm284
-rw-r--r--src/cgil/rlhc-c.lm462
-rw-r--r--src/cgil/rlhc-crack.lm536
-rw-r--r--src/cgil/rlhc-csharp.lm480
-rw-r--r--src/cgil/rlhc-d.lm511
-rw-r--r--src/cgil/rlhc-go.lm454
-rw-r--r--src/cgil/rlhc-java.lm504
-rw-r--r--src/cgil/rlhc-js.lm504
-rw-r--r--src/cgil/rlhc-julia.lm561
-rw-r--r--src/cgil/rlhc-main.lm19
-rw-r--r--src/cgil/rlhc-ocaml.lm609
-rw-r--r--src/cgil/rlhc-ruby.lm527
-rw-r--r--src/cgil/rlhc-rust.lm534
-rw-r--r--src/libfsm/.exrc28
-rw-r--r--src/libfsm/.gitignore57
-rw-r--r--src/libfsm/CMakeLists.txt154
-rw-r--r--src/libfsm/Makefile.am43
-rw-r--r--src/libfsm/actexp.cc218
-rw-r--r--src/libfsm/actexp.h62
-rw-r--r--src/libfsm/action.h119
-rw-r--r--src/libfsm/actloop.cc229
-rw-r--r--src/libfsm/actloop.h63
-rw-r--r--src/libfsm/allocgen.cc136
-rw-r--r--src/libfsm/asm.cc2046
-rw-r--r--src/libfsm/asm.h248
-rw-r--r--src/libfsm/binary.cc819
-rw-r--r--src/libfsm/binary.h98
-rw-r--r--src/libfsm/binbreak.cc132
-rw-r--r--src/libfsm/binbreak.h71
-rw-r--r--src/libfsm/bingoto.cc131
-rw-r--r--src/libfsm/bingoto.h71
-rw-r--r--src/libfsm/binvar.cc137
-rw-r--r--src/libfsm/binvar.h72
-rw-r--r--src/libfsm/codegen.cc1202
-rw-r--r--src/libfsm/codegen.h460
-rw-r--r--src/libfsm/common.cc352
-rw-r--r--src/libfsm/common.h504
-rw-r--r--src/libfsm/config.h.cmake.in13
-rw-r--r--src/libfsm/dot.cc392
-rw-r--r--src/libfsm/dot.h53
-rw-r--r--src/libfsm/flat.cc576
-rw-r--r--src/libfsm/flat.h94
-rw-r--r--src/libfsm/flatbreak.cc118
-rw-r--r--src/libfsm/flatbreak.h72
-rw-r--r--src/libfsm/flatgoto.cc118
-rw-r--r--src/libfsm/flatgoto.h72
-rw-r--r--src/libfsm/flatvar.cc117
-rw-r--r--src/libfsm/flatvar.h70
-rw-r--r--src/libfsm/fsmap.cc1200
-rw-r--r--src/libfsm/fsmattach.cc857
-rw-r--r--src/libfsm/fsmbase.cc854
-rw-r--r--src/libfsm/fsmcond.cc520
-rw-r--r--src/libfsm/fsmgraph.cc1948
-rw-r--r--src/libfsm/fsmgraph.h2694
-rw-r--r--src/libfsm/fsmmin.cc934
-rw-r--r--src/libfsm/fsmnfa.cc660
-rw-r--r--src/libfsm/fsmstate.cc603
-rw-r--r--src/libfsm/gendata.cc1732
-rw-r--r--src/libfsm/gendata.h477
-rw-r--r--src/libfsm/goto.cc978
-rw-r--r--src/libfsm/goto.h226
-rw-r--r--src/libfsm/gotoexp.cc207
-rw-r--r--src/libfsm/gotoexp.h75
-rw-r--r--src/libfsm/gotoloop.cc226
-rw-r--r--src/libfsm/gotoloop.h72
-rw-r--r--src/libfsm/idbase.cc421
-rw-r--r--src/libfsm/idbase.h7
-rw-r--r--src/libfsm/ipgoto.cc764
-rw-r--r--src/libfsm/ipgoto.h129
-rw-r--r--src/libfsm/parsedata.h27
-rw-r--r--src/libfsm/ragel-config.cmake.in3
-rw-r--r--src/libfsm/ragel.h108
-rw-r--r--src/libfsm/redfsm.cc1192
-rw-r--r--src/libfsm/redfsm.h889
-rw-r--r--src/libfsm/switch.cc1036
-rw-r--r--src/libfsm/switch.h106
-rw-r--r--src/libfsm/switchbreak.cc75
-rw-r--r--src/libfsm/switchbreak.h70
-rw-r--r--src/libfsm/switchgoto.cc73
-rw-r--r--src/libfsm/switchgoto.h70
-rw-r--r--src/libfsm/switchvar.cc76
-rw-r--r--src/libfsm/switchvar.h72
-rw-r--r--src/libfsm/tabbreak.cc379
-rw-r--r--src/libfsm/tabgoto.cc330
-rw-r--r--src/libfsm/tables.cc81
-rw-r--r--src/libfsm/tables.h265
-rw-r--r--src/libfsm/tabvar.cc332
135 files changed, 47688 insertions, 1 deletions
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 <thurston@colm.net> 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 <thurston@colm.net>
+ *
+ * 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 <new>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <iostream>
+#include <assert.h>
+#include <string.h>
+
+#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 T> 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 <class FT> friend StrTmpl<FT> operator+(
+ const StrTmpl<FT> &s1, const char *s2 );
+ template <class FT> friend StrTmpl<FT> operator+(
+ const char *s1, const StrTmpl<FT> &s2 );
+ template <class FT> friend StrTmpl<FT> operator+(
+ const StrTmpl<FT> &s1, const StrTmpl<FT> &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<class T> StrTmpl<T>::~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<class T> StrTmpl<T>::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<class T> StrTmpl<T>::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<class T> StrTmpl<T>::StrTmpl( const Fresh &, long length )
+{
+ /* Init space for the data. */
+ initSpace( length );
+ data[length] = 0;
+}
+
+/* Create from another string class. */
+template<class T> StrTmpl<T>::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<class T> StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> StrTmpl<T> &StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> StrTmpl<T> &StrTmpl<T>::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<class T> StrTmpl<T> &StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> StrTmpl<T> &StrTmpl<T>::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<class T> void StrTmpl<T>::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<class T> StrTmpl<T> &StrTmpl<T>::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<class T> StrTmpl<T> &StrTmpl<T>::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<class T> char *StrTmpl<T>::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<class T> StrTmpl<T> operator+( const StrTmpl<T> &s1, const char *s2 )
+{
+ /* Find s2 length and alloc the space for the result. */
+ long str1Len = (((typename StrTmpl<T>::Head*)(s1.data)) - 1)->length;
+ long str2Len = strlen( s2 );
+
+ typename StrTmpl<T>::Head *head = (typename StrTmpl<T>::Head*)
+ malloc( sizeof(typename StrTmpl<T>::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<T>( data, typename StrTmpl<T>::DisAmbig() );
+}
+
+/* Concatenate a c-style string and a String. */
+template<class T> StrTmpl<T> operator+( const char *s1, const StrTmpl<T> &s2 )
+{
+ /* Find s2 length and alloc the space for the result. */
+ long str1Len = strlen( s1 );
+ long str2Len = (((typename StrTmpl<T>::Head*)(s2.data)) - 1)->length;
+
+ typename StrTmpl<T>::Head *head = (typename StrTmpl<T>::Head*)
+ malloc( sizeof(typename StrTmpl<T>::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<T>( data, typename StrTmpl<T>::DisAmbig() );
+}
+
+/* Add two StrTmpl strings. */
+template<class T> StrTmpl<T> operator+( const StrTmpl<T> &s1, const StrTmpl<T> &s2 )
+{
+ /* Find s2 length and alloc the space for the result. */
+ long str1Len = (((typename StrTmpl<T>::Head*)(s1.data)) - 1)->length;
+ long str2Len = (((typename StrTmpl<T>::Head*)(s2.data)) - 1)->length;
+ typename StrTmpl<T>::Head *head = (typename StrTmpl<T>::Head*)
+ malloc( sizeof(typename StrTmpl<T>::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<T>( data, typename StrTmpl<T>::DisAmbig() );
+}
+
+/* Operator used in case the compiler does not support the conversion. */
+template <class T> inline std::ostream &operator<<( std::ostream &o, const StrTmpl<T> &s )
+{
+ return o.write( s.data, s.length() );
+}
+
+typedef StrTmpl<char> 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+
+#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<class SubClassEl> 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<class SubClassEl> 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 <class Key, class Value> struct AvliMapEl :
+ public AvliTreeEl< AvliMapEl<Key, Value> >
+{
+ 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 <class Key, class Value> struct AvlMapEl :
+ public AvlTreeEl< AvlMapEl<Key, Value> >
+{
+ 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 <class Key> struct AvliSetEl :
+ public AvliTreeEl< AvliSetEl<Key> >
+{
+ 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 <class Key> struct AvlSetEl :
+ public AvlTreeEl< AvlSetEl<Key> >
+{
+ 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 <AVLMEL_TEMPDEF> AvlTree<AVLMEL_TEMPUSE>::
+ AvlTree(const AvlTree<AVLMEL_TEMPUSE> &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 <AVLMEL_TEMPDEF> AvlTree<AVLMEL_TEMPUSE> &AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ transfer(AvlTree<AVLMEL_TEMPUSE> &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 <AVLMEL_TEMPDEF> AvlTree<AVLMEL_TEMPUSE> &AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ transfer(AvlTree<AVLMEL_TEMPUSE> &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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::Iter::
+ operator++()
+{
+ return ptr = findNext( ptr );
+}
+
+/* Postfix ++ */
+template <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::Iter::
+ operator++(int)
+{
+ Element *rtn = ptr;
+ ptr = findNext( ptr );
+ return rtn;
+}
+
+/* increment */
+template <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::Iter::
+ increment()
+{
+ return ptr = findNext( ptr );
+}
+
+/* Prefix -- */
+template <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::Iter::
+ operator--()
+{
+ return ptr = findPrev( ptr );
+}
+
+/* Postfix -- */
+template <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::Iter::
+ operator--(int)
+{
+ Element *rtn = ptr;
+ ptr = findPrev( ptr );
+ return rtn;
+}
+
+/* decrement */
+template <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::Iter::
+ decrement()
+{
+ return ptr = findPrev( ptr );
+}
+
+#ifndef WALKABLE
+
+/* Move ahead one. */
+template <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> bool AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::abandon()
+{
+ root = 0;
+ treeSize = 0;
+
+#ifdef WALKABLE
+ BASELIST::abandon();
+#else
+ head = tail = 0;
+#endif
+}
+
+/* Recursively delete all the children of a element. */
+template <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> Element *AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ 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 <AVLMEL_TEMPDEF> void AvlTree<AVLMEL_TEMPUSE>::
+ 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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<Element> >
+#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 <thurston@colm.net>
+ *
+ * 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<Key,Value> >
+#define AVLMEL_CLASSDEF class Key, class Value, class Compare = CmpOrd<Key>
+#define AVLMEL_TEMPDEF class Key, class Value, class Compare
+#define AVLMEL_TEMPUSE Key, Value, Compare
+#define AvlTree AvliMap
+#define Element AvliMapEl<Key,Value>
+#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 <thurston@colm.net>
+ *
+ * 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<Key>
+#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 <thurston@colm.net>
+ *
+ * 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<Key>
+#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 <thurston@colm.net>
+ *
+ * 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<Key> >
+#define AVLMEL_CLASSDEF class Key, class Compare = CmpOrd<Key>
+#define AVLMEL_TEMPDEF class Key, class Compare
+#define AVLMEL_TEMPUSE Key, Compare
+#define AvlTree AvliSet
+#define Element AvliSetEl<Key>
+#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 <thurston@colm.net>
+ *
+ * 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<Element> >
+#define AVLMEL_CLASSDEF class Element, class Key, class Compare = CmpOrd<Key>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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<Key>
+#define AVLMEL_TEMPDEF class Key, class Value, class Compare
+#define AVLMEL_TEMPUSE Key, Value, Compare
+#define AvlTree AvlMap
+#define Element AvlMapEl<Key,Value>
+
+#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 <thurston@colm.net>
+ *
+ * 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<Key>
+#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 <thurston@colm.net>
+ *
+ * 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<Key>
+#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 <thurston@colm.net>
+ *
+ * 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<Key>
+#define AVLMEL_TEMPDEF class Key, class Compare
+#define AVLMEL_TEMPUSE Key, Compare
+#define AvlTree AvlSet
+#define Element AvlSetEl<Key>
+
+#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 <thurston@colm.net>
+ *
+ * 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<Key>
+#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 <thurston@colm.net>
+ *
+ * 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<Element, Resize> BaseVector;
+ typedef Table<Element> 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 <BST_TEMPL_DEF> BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> bool BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> bool BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> long BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> long BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> bool BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> bool BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> void BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <BST_TEMPL_DEF> Element *BstTable<BST_TEMPL_USE>::
+ 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 <thurston@colm.net>
+ *
+ * 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 <class Key, class Value> 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<Key>, 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<Key, Value>
+#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<Key, Value> **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 <thurston@colm.net>
+ *
+ * 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<Key>, \
+ 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 <thurston@colm.net>
+ *
+ * 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<Key>, 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 <thurston@colm.net>
+ *
+ * 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 T, class Compare> class BubbleSort
+ : public Compare
+{
+public:
+ /* Sorting interface routine. */
+ void sort(T *data, long len);
+};
+
+
+/**
+ * \brief Bubble sort an array of data.
+ */
+template <class T, class Compare> void BubbleSort<T,Compare>::
+ 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <string>
+#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 <class T> 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<T> > struct CmpTable
+ : public CompareT
+{
+ /**
+ * \brief Compare two tables storing type T.
+ */
+ static inline long compare(const Table<T> &t1, const Table<T> &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<T> > struct CmpTableNs
+ : public CompareT
+{
+ /**
+ * \brief Compare two tables storing type T.
+ */
+ inline long compare(const Table<T> &t1, const Table<T> &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<T> > struct CmpSTable : public CompareT
+{
+ /**
+ * \brief Compare two tables storing type T.
+ */
+ static inline long compare(const STable<T> &t1, const STable<T> &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<T> > struct CmpSTableNs
+ : public CompareT
+{
+ /**
+ * \brief Compare two tables storing type T.
+ */
+ inline long compare(const STable<T> &t1, const STable<T> &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 <thurston@colm.net>
+ *
+ * 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 <class T> 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<T> *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<T> *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 <class Element> 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 <DLMEL_TEMPDEF> 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 <DLMEL_TEMPDEF> DList<DLMEL_TEMPUSE>::
+ DList(const DList<DLMEL_TEMPUSE> &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 <DLMEL_TEMPDEF> DList<DLMEL_TEMPUSE> &DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> DList<DLMEL_TEMPUSE> &DList<DLMEL_TEMPUSE>::
+ operator=(const DList &other)
+{
+ Element *el = other.head;
+ while( el != 0 ) {
+ append( new Element(*el) );
+ el = el->BASE_EL(next);
+ }
+ return *this;
+}
+
+template <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ prepend(const T &item)
+{
+ addBefore(head, new Element(item));
+}
+
+/* Append a new item. Inlining this bloats the caller with the new overhead. */
+template <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ addBefore(Element *next_el, const T &item)
+{
+ addBefore(next_el, new Element(item));
+}
+
+#endif
+
+/*
+ * The larger iterator operators.
+ */
+
+/* Postfix ++ */
+template <DLMEL_TEMPDEF> Element *DList<DLMEL_TEMPUSE>::Iter::
+ operator++(int)
+{
+ Element *rtn = ptr;
+ ptr = ptr->BASE_EL(next);
+ return rtn;
+}
+
+/* Postfix -- */
+template <DLMEL_TEMPDEF> Element *DList<DLMEL_TEMPUSE>::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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ addAfter( Element *prev_el, DList<DLMEL_TEMPUSE> &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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::
+ addBefore( Element *next_el, DList<DLMEL_TEMPUSE> &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 <DLMEL_TEMPDEF> Element *DList<DLMEL_TEMPUSE>::
+ 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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::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 <DLMEL_TEMPDEF> void DList<DLMEL_TEMPUSE>::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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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<T>
+#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 <thurston@colm.net>
+ *
+ * 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 T, class Compare> class InsertSort
+ : public Compare
+{
+public:
+ /* Sorting interface routine. */
+ void sort(T *data, long len);
+};
+
+
+/**
+ * \brief Insertion sort an array of data.
+ */
+template <class T, class Compare>
+ void InsertSort<T,Compare>::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 <thurston@colm.net>
+ *
+ * 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 T, class Compare> class MergeSort
+ : public BubbleSort<T, Compare>
+{
+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<T,Compare>::
+ doSort(T *tmpStor, T *data, long len)
+{
+ if ( len <= 1 )
+ return;
+
+ if ( len <= _MS_BUBBLE_THRESH ) {
+ BubbleSort<T, Compare>::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<T,Compare>::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 <thurston@colm.net>
+ *
+ * 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 T, class Compare> class QuickSort :
+ public InsertSort<T, Compare>
+{
+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 <class T, class Compare> T *QuickSort<T,Compare>::
+ 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 <class T, class Compare> T *QuickSort<T,Compare>::
+ 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<T,Compare>::
+ 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<T, Compare>::sort( start, len );
+ }
+}
+
+/**
+ * \brief Quick sort an array of data.
+ */
+template< class T, class Compare>
+ void QuickSort<T,Compare>::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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+
+#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 <long step> 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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <iostream>
+#include <stdio.h>
+
+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 <thurston@colm.net>
+ *
+ * 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 <class Key, class Value> 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<Key>, 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<Key, Value>
+#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<Key, Value> **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 <thurston@colm.net>
+ *
+ * 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<Key>, \
+ 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 <thurston@colm.net>
+ *
+ * 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<Key>, 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 <thurston@colm.net>
+ *
+ * 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 <new>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#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<T>, public Resize
+{
+private:
+ typedef STable<T> 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 <class T, class Resize> SVector<T, Resize>::
+ SVector(const SVector<T, Resize> &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 <class T, class Resize> SVector<T, Resize> &
+ SVector<T, Resize>:: 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 <class T, class Resize> SVector<T, Resize>::
+ 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 <class T, class Resize> SVector<T, Resize>::
+ 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 <class T, class Resize> SVector<T, Resize>::
+ 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 <class T, class Resize> SVector<T, Resize>::
+ 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 <class T, class Resize> SVector<T, Resize>::
+ 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 <class T, class Resize> typename SVector<T, Resize>::Iter &
+ SVector<T, Resize>::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 <class T, class Resize> typename SVector<T, Resize>::Iter &
+ SVector<T, Resize>::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 <class T, class Resize> typename SVector<T, Resize>::Iter &
+ SVector<T, Resize>::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 <class T, class Resize> typename SVector<T, Resize>::Iter &
+ SVector<T, Resize>::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 <class T, class Resize> typename SVector<T, Resize>::Iter &
+ SVector<T, Resize>::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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> long SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> long SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <class T, class Resize> void SVector<T, Resize>::
+ 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 <thurston@colm.net>
+ *
+ * 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 T> 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 <class T> inline Table<T>::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 T> 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 <class T> inline STable<T>::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 <thurston@colm.net>
+ *
+ * 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 <new>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#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<T>, public Resize
+{
+private:
+ typedef Table<T> 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 <class T, class Resize> Vector<T, Resize>::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 <class T, class Resize> Vector<T, Resize>::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 <class T, class Resize> Vector<T, Resize>::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 <class T, class Resize> Vector<T, Resize>::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 <class T, class Resize> Vector<T, Resize>::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 <class T, class Resize> typename Vector<T, Resize>::Iter &
+ Vector<T, Resize>::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 <class T, class Resize> typename Vector<T, Resize>::Iter &
+ Vector<T, Resize>::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 <class T, class Resize> typename Vector<T, Resize>::Iter &
+ Vector<T, Resize>::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 <class T, class Resize> typename Vector<T, Resize>::Iter &
+ Vector<T, Resize>::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 <class T, class Resize> typename Vector<T, Resize>::Iter &
+ Vector<T, Resize>::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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> Vector<T, Resize> &Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> Vector<T, Resize>::
+ Vector(const Vector<T, Resize> &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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<class T, class Resize> void Vector<T, Resize>::
+ 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<str, machine> = new map<str, machine>()
+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<str> = new list<str>()
+
+struct saved_globals
+ FileName: str
+ TargetMachine: str
+ SearchMachine: str
+ WantSection: bool
+ IncludeDepth: int
+ Import: bool
+end
+
+global GblSavedGlobals: list<saved_globals> = new list<saved_globals>()
+
+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<str, str>
+ IncludeHistory: list<include_history_item>
+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<ign_want>, '' ) )
+ else
+ input->push( make_token( typeid<ign_ignore>, '' ) )
+ }
+
+ 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<mn_word>, 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<str, str>()
+ Machine->IncludeHistory = new list<include_history_item>()
+ 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 | [`</] :NotStart1 | [`$/] :All1
+ | [`%/] :Final1 | [`@/] :NotFinal1 | [`<>/] :Middle1
+ | [`> `eof] :Start2 | [`< `eof] :NotStart2 | [`$ `eof] :All2
+ | [`% `eof] :Final2 | [`@ `eof] :NotFinal2 | [`<> `eof] :Middle2
+
+ def aug_gbl_error
+ [`>!] :Start1 | [`<!] :NotStart1 | [`$!] :All1
+ | [`%!] :Final1 | [`@!] :NotFinal1 | [`<>!] :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<str> makeIncludePathChecks( CurFileName: str, IncFileName: str )
+{
+ new L: list<str>()
+
+ 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<str>
+ if IncFileName
+ Checks = makeIncludePathChecks( GblFileName, IncFileName )
+ else {
+ Checks = new list<str>()
+ 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<str>
+ if IncFileName
+ Checks = makeIncludePathChecks( GblFileName, IncFileName )
+ else {
+ Checks = new list<str>()
+ 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<c_out::c_out>
+
+ 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<c_out::c_out>()
+
+ 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<crack_out::crack_out>
+
+ 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<crack_out::crack_out>()
+
+ 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<csharp_out::csharp_out>
+
+ 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<csharp_out::csharp_out>()
+
+ 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<d_out::d_out>
+
+ global HasDefault: list<int> = new list<int>()
+
+ 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<d_out::d_out>()
+
+ 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<out_go::out_go>
+
+ 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<out_go::out_go>()
+ 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<java_out::java_out>
+
+ 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<java_out::java_out>()
+
+ 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<js_out::js_out>
+
+ 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<js_out::js_out>()
+
+ 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<julia_out::julia_out>
+
+ 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<julia_out::julia_out>()
+
+ 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<str, str> = new map<str, str>()
+ global Parser: parser<ocaml_out::ocaml_out>
+
+ 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<ocaml_out::ocaml_out>()
+
+ 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<ruby_out::ruby_out>
+
+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<ruby_out::ruby_out>()
+
+ 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<rust_out::rust_out>
+
+ 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<rust_out::rust_out>()
+
+ 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 <NL> j
+map  k
+map Q gq
+nmap gx <Plug>NetrwBrowseX
+nnoremap <silent> <Plug>NetrwBrowseX :call netrw#NetrwBrowseX(expand("<cWORD>"),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
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../src>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/../aapl>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+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
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/..>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/..>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/../aapl>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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<InlineItem> { };
+
+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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <sstream>
+
+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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#include <string>
+#include <iomanip>
+#include <stdio.h>
+
+#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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+
+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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <sstream>
+#include <string>
+#include <assert.h>
+#include <iomanip>
+
+
+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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#include <string>
+#include <stdio.h>
+#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<TableArray*> 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<TableArray*> 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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <assert.h>
+#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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#include <fstream>
+#include <climits>
+#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<Export> 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+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<CondAp> > ValPairIterPiListCondAp;
+ ValPairIterPiListCondAp outPair( condList1, condList2 );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+ case ValPairIterPiListCondAp::RangeInS1: {
+ int compareRes = FsmAp::compareCondBitElimPtr<CondAp>( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+ }
+ case ValPairIterPiListCondAp::RangeInS2: {
+ int compareRes = FsmAp::compareCondBitElimPtr<CondAp>( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+ }
+ case ValPairIterPiListCondAp::RangeOverlap: {
+ int compareRes = FsmAp::compareCondBitElimPtr<CondAp>(
+ 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<CondAp> > ValPairIterPiListCondAp;
+ ValPairIterPiListCondAp outPair( trans1->tcap()->condList,
+ trans2->tcap()->condList );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+ case ValPairIterPiListCondAp::RangeInS1: {
+ int compareRes = FsmAp::compareCondDataPtr<CondAp>( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+ }
+ case ValPairIterPiListCondAp::RangeInS2: {
+ int compareRes = FsmAp::compareCondDataPtr<CondAp>( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+ }
+ case ValPairIterPiListCondAp::RangeOverlap: {
+ int compareRes = FsmAp::compareCondDataPtr<CondAp>(
+ 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<int>::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<FsmLongestMatchPart*>::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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include "fsmgraph.h"
+
+#include <iostream>
+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<CondAp> > 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<TransAp> > 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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <iostream>
+
+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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+#include <iostream>
+
+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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+#include <iostream>
+
+#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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+
+/* 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<NfaRound> 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<NameInst*> NameInstVect;
+
+struct ActionParam
+{
+ ActionParam( std::string name )
+ : name(name) {}
+
+ std::string name;
+};
+
+typedef Vector<ActionParam*> ActionParamList;
+
+typedef Vector<Action*> ActionArgList;
+
+struct CmpActionArgList
+{
+ static inline int compare( const ActionArgList *list1, const ActionArgList *list2 )
+ {
+ return CmpTable<Action*>::compare( *list1, *list2 );
+ }
+};
+
+typedef BstMap<ActionArgList*, Action*, CmpActionArgList> ActionArgListMap;
+typedef BstMapEl<ActionArgList*, Action*> ActionArgListMapEl;
+
+/* Element in list of actions. Contains the string for the code to exectute. */
+struct Action
+:
+ public DListEl<Action>,
+ public AvlTreeEl<Action>
+{
+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<Action> ActionList;
+typedef AvlTree<Action, std::string, CmpString> ActionDict;
+
+/* Structure for reverse action mapping. */
+struct RevActionMapEl
+{
+ char *name;
+ InputLoc location;
+};
+
+
+/* Transition Action Table. */
+struct ActionTable
+ : public SBstMap< int, Action*, CmpOrd<int> >
+{
+ 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<Action*> > ActionSet;
+typedef CmpSTable< Action*, CmpOrd<Action*> > CmpActionSet;
+
+/* Transistion Action Element. */
+typedef SBstMapEl< int, FsmLongestMatchPart* > LmActionTableEl;
+
+/* Transition Action Table. */
+struct LmActionTable
+ : public SBstMap< int, FsmLongestMatchPart*, CmpOrd<int> >
+{
+ 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<int> >
+{
+ 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<PriorDesc> 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<PriorEl, CmpPriorEl> CmpPriorTable;
+
+/* Plain action list that imposes no ordering. */
+typedef Vector<int> TransFuncList;
+
+/* Comparison for TransFuncList. */
+typedef CmpTable< int, CmpOrd<int> > 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 <class Element> 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<CondAp> 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<TransCondAp*>( this ) : 0; }
+
+inline TransDataAp *TransAp::tdap()
+ { return this->condSpace == 0 ? static_cast<TransDataAp*>( this ) : 0; }
+
+typedef DList<TransAp> TransList;
+
+/* Need the base vector type for accessing underlying remove function. */
+typedef BstSet<int> CondKeySet;
+typedef Vector<int> 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<StateAp*, NfaActions> NfaStateMap;
+typedef BstMapEl<StateAp*, NfaActions> NfaStateMapEl;
+
+typedef DList<NfaTrans> NfaTransList;
+typedef InList<NfaTrans> 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<int>::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<StateAp*> StateSet;
+typedef DList<StateAp> StateList;
+
+/* A element in a state dict. */
+struct StateDictEl
+:
+ public AvlTreeEl<StateDictEl>
+{
+ 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<StateAp*> > 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<Key, CmpKey>
+{
+ 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<int> 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<EptVectEl> EptVect;
+
+/* Set of entry ids that go into this state. */
+typedef BstSet<int> EntryIdSet;
+
+/* Set of longest match items that may be active in a given state. */
+typedef BstSet<FsmLongestMatchPart*> 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>
+{
+ CondSpace( const CondSet &condSet )
+ : condSet(condSet) {}
+
+ const CondSet &getKey() { return condSet; }
+
+ long fullSize()
+ { return ( 1 << condSet.length() ); }
+
+ CondSet condSet;
+ long condSpaceId;
+};
+
+typedef Vector<CondSpace*> CondSpaceVect;
+
+typedef AvlTree<CondSpace, CondSet, CmpCondSet> CondSpaceMap;
+
+typedef Vector<long> 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<CondAp> CondInList;
+typedef InList<TransDataAp> TransInList;
+
+struct NfaStateEl
+{
+ StateAp *prev, *next;
+};
+
+typedef DListMel<StateAp, NfaStateEl> 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 <class Item> struct PiList
+{
+ PiList()
+ : ptr(0) {}
+
+ PiList( const DList<Item> &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 <class Item> 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 <class Item> struct PiVector
+{
+ PiVector()
+ : ptr(0), length(0) {}
+
+ PiVector( const Vector<Item> &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 <class ItemIter1, class ItemIter2 = ItemIter1> 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 <class ItemIter> 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<ItemIter1> s1Tel;
+ NextTrans<ItemIter2> s2Tel;
+ Key bottomLow, bottomHigh;
+ ItemIter1 *bottomTrans1;
+ ItemIter2 *bottomTrans2;
+
+private:
+ void findNext();
+};
+
+/* Init the iterator by advancing to the first item. */
+template <class ItemIter1, class ItemIter2>
+ ValPairIter<ItemIter1, ItemIter2>::
+ 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 <class ItemIter1, class ItemIter2>
+ void ValPairIter<ItemIter1, ItemIter2>::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 <class ItemIter1, class ItemIter2 = ItemIter1> 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 <class ItemIter> 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<ItemIter1> s1Tel;
+ NextTrans<ItemIter2> s2Tel;
+ Key bottomLow, bottomHigh;
+ ItemIter1 bottomTrans1;
+ ItemIter2 bottomTrans2;
+
+private:
+ void findNext();
+};
+
+/* Init the iterator by advancing to the first item. */
+template <class ItemIter1, class ItemIter2> RangePairIter<ItemIter1, ItemIter2>::
+ 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 <class ItemIter1, class ItemIter2>
+ void RangePairIter<ItemIter1, ItemIter2>::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<int> > 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<TransEl> 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<EntryMapEl> 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<BreadthCost> 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<std::string> {};
+typedef Vector<NameRef*> NameRefList;
+typedef Vector<NameInst*> 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<FsmLongestMatchPart> {};
+
+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<NameInst*> vals;
+};
+
+/* Tree of instantiated names. */
+typedef AvlMapEl<std::string, NameMapVal*> NameMapEl;
+typedef AvlMap<std::string, NameMapVal*, CmpString> NameMap;
+typedef Vector<NameInst*> 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<NameInst> 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 <thurston@colm.net>
+ *
+ * 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<StateAp*, InitPartitionCompare>
+{
+ MergeSortInitPartition( FsmCtx *ctx )
+ {
+ InitPartitionCompare::ctx = ctx;
+ }
+};
+
+struct MergeSortPartition
+ : public MergeSort<StateAp*, PartitionCompare>
+{
+ MergeSortPartition( FsmCtx *ctx )
+ {
+ PartitionCompare::ctx = ctx;
+ }
+};
+
+struct MergeSortApprox
+ : public MergeSort<StateAp*, ApproxCompare>
+{
+ 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<Action*>::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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+#include <iostream>
+
+#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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <iostream>
+
+/* 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<TransAp> outPair( ctx, state1->outList.head, state2->outList.head );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case RangePairIter<TransAp>::RangeInS1:
+ compareRes = FsmAp::compareFullPtr( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangePairIter<TransAp>::RangeInS2:
+ compareRes = FsmAp::compareFullPtr( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangePairIter<TransAp>::RangeOverlap:
+ compareRes = FsmAp::compareFullPtr(
+ outPair.s1Tel.trans, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangePairIter<TransAp>::BreakS1:
+ case RangePairIter<TransAp>::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<TransAp> > 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<TransAp> > 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<TransAp> outPair( ctx, state1->outList.head, state2->outList.head );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case RangePairIter<TransAp>::RangeInS1:
+ if ( FsmAp::shouldMarkPtr( markIndex, outPair.s1Tel.trans, 0 ) )
+ return true;
+ break;
+
+ case RangePairIter<TransAp>::RangeInS2:
+ if ( FsmAp::shouldMarkPtr( markIndex, 0, outPair.s2Tel.trans ) )
+ return true;
+ break;
+
+ case RangePairIter<TransAp>::RangeOverlap:
+ if ( FsmAp::shouldMarkPtr( markIndex,
+ outPair.s1Tel.trans, outPair.s2Tel.trans ) )
+ return true;
+ break;
+
+ case RangePairIter<TransAp>::BreakS1:
+ case RangePairIter<TransAp>::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<CondAp> > ValPairIterPiListCondAp;
+ ValPairIterPiListCondAp outPair( trans1->tcap()->condList,
+ trans2->tcap()->condList );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case ValPairIterPiListCondAp::RangeInS1: {
+ int compareRes = FsmAp::compareCondPartPtr<CondAp>( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+ }
+
+ case ValPairIterPiListCondAp::RangeInS2: {
+ int compareRes = FsmAp::compareCondPartPtr<CondAp>( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+ }
+
+ case ValPairIterPiListCondAp::RangeOverlap: {
+ int compareRes = FsmAp::compareCondPartPtr<CondAp>(
+ 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 <thurston@colm.net>
+ *
+ * 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 <string.h>
+#include <iostream>
+
+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 <exec> 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<RedNfaTarg, RedNfaTargCmp> 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<output_filter*>(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<std::string> &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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#include <string>
+#include <vector>
+#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>
+{
+ RedActionTable( const ActionTable &key )
+ :
+ key(key),
+ id(0)
+ { }
+
+ const ActionTable &getKey()
+ { return key; }
+
+ ActionTable key;
+ int id;
+};
+
+typedef AvlTree<RedActionTable, ActionTable, CmpActionTable> 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<GenAction> 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<std::string> &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<char *, CodeGenData*, CmpStr> CodeGenMap;
+typedef AvlMapEl<char *, CodeGenData*> 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 <thurston@colm.net>
+ *
+ * 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 <sstream>
+
+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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <sstream>
+
+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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <string>
+#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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#include <sstream>
+#include <ctime>
+
+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<RedStateAp*, CmpStateById> 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<EquivClass>, PiVector<RedTransEl> > 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<long long, long long> map;
+ for ( EquivClass *c = equiv.head; c != 0; c = c->next ) {
+ BstMapEl<long long, long long> *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<EquivClass>, PiVector<RedTransEl> >
+ pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ )
+ {
+ if ( pair.userState == RangePairIter<PiList<EquivClass>, PiVector<RedTransEl> >::RangeOverlap ||
+ pair.userState == RangePairIter<PiList<EquivClass>, PiVector<RedTransEl> >::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<EquivClass>, PiVector<RedTransEl> >
+ pair( fsmCtx, equiv, st->outRange ); !pair.end(); pair++ )
+ {
+ if ( pair.userState == RangePairIter< PiList<EquivClass>, PiVector<RedTransEl> >::RangeOverlap ||
+ pair.userState == RangePairIter< PiList<EquivClass>, PiVector<RedTransEl> >::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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+#include <string.h>
+#include <string>
+#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<int> 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<GenInlineItem> { };
+
+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>
+{
+ 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<int> >
+{
+ 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<RedStateAp*> RedStateSet;
+typedef BstSet<int> IntSet;
+
+/* Reduced action. */
+struct RedAction
+:
+ public AvlTreeEl<RedAction>
+{
+ 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<RedAction, GenActionTable, CmpGenActionTable> GenActionTableMap;
+
+struct RedCondPair
+{
+ int id;
+ RedStateAp *targ;
+ RedAction *action;
+};
+
+struct RedCondAp
+:
+ public AvlTreeEl<RedCondAp>
+{
+ 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<GenCondSpace> CondSpaceList;
+
+struct RedCondVect
+{
+ int numConds;
+ RedCondEl *outConds;
+ RedCondAp *errCond;
+};
+
+/* Reduced transition. */
+struct RedTransAp
+:
+ public AvlTreeEl<RedTransAp>
+{
+ 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<RedTransAp, CmpRedTransAp> TransApSet;
+typedef AvlBasic<RedCondAp, CmpRedCondAp> 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<RedTransEl> RedTransList;
+typedef Vector<RedStateAp*> RedStateVect;
+
+typedef BstMapEl<RedStateAp*, unsigned long long> RedSpanMapEl;
+typedef BstMap<RedStateAp*, unsigned long long> 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<RedSpanMapEl, CmpRedSpanMapEl> RedSpanMapSort;
+
+/* Set of entry ids that go into this state. */
+typedef Vector<int> EntryIdVect;
+typedef Vector<char*> EntryNameVect;
+
+struct Condition
+{
+ Condition( )
+ : key(0), baseKey(0) {}
+
+ Key key;
+ Key baseKey;
+ GenCondSet condSet;
+
+ Condition *next, *prev;
+};
+typedef DList<Condition> ConditionList;
+
+struct GenStateCond
+{
+ Key lowKey;
+ Key highKey;
+
+ GenCondSpace *condSpace;
+
+ GenStateCond *prev, *next;
+};
+typedef DList<GenStateCond> GenStateCondList;
+typedef Vector<GenStateCond*> 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<RedNfaTarg> 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<RedStateAp> RedStateList;
+
+/* Set of reduced transitons. Comparison is by pointer. */
+typedef BstSet< RedTransAp*, CmpOrd<RedTransAp*> > 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<EquivClass> EquivList;
+ typedef BstMap<RedTransAp*, int> EquivAlloc;
+ typedef BstMapEl<RedTransAp*, int> 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 <thurston@colm.net>
+ *
+ * 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 <assert.h>
+
+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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <thurston@colm.net>
+ *
+ * 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 <iostream>
+#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 <thurston@colm.net>
+ *
+ * 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";
+}
+