packages feed

regexdot (empty) → 0.10.2.1

raw patch · 26 files changed

+3951/−0 lines, 26 filesdep +basedep +deepseqdep +parallelsetup-changed

Dependencies added: base, deepseq, parallel, parsec, toolshed

Files

+ LICENSE view
@@ -0,0 +1,675 @@+                    GNU GENERAL PUBLIC LICENSE+                       Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++                            Preamble++  The GNU General Public License is a free, copyleft license for+software and other kinds of works.++  The licenses for most software and other practical works are designed+to take away your freedom to share and change the works.  By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users.  We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors.  You can apply it to+your programs, too.++  When we speak of free software, we are referring to freedom, not+price.  Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++  To protect your rights, we need to prevent others from denying you+these rights or asking you to surrender the rights.  Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++  For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received.  You must make sure that they, too, receive+or can get the source code.  And you must show them these terms so they+know their rights.++  Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++  For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software.  For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++  Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so.  This is fundamentally incompatible with the aim of+protecting users' freedom to change the software.  The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable.  Therefore, we+have designed this version of the GPL to prohibit the practice for those+products.  If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++  Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary.  To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++  The precise terms and conditions for copying, distribution and+modification follow.++                       TERMS AND CONDITIONS++  0. Definitions.++  "This License" refers to version 3 of the GNU General Public License.++  "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++  "The Program" refers to any copyrightable work licensed under this+License.  Each licensee is addressed as "you".  "Licensees" and+"recipients" may be individuals or organizations.++  To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy.  The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++  A "covered work" means either the unmodified Program or a work based+on the Program.++  To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy.  Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++  To "convey" a work means any kind of propagation that enables other+parties to make or receive copies.  Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++  An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License.  If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++  1. Source Code.++  The "source code" for a work means the preferred form of the work+for making modifications to it.  "Object code" means any non-source+form of a work.++  A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++  The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form.  A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++  The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities.  However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work.  For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++  The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++  The Corresponding Source for a work in source code form is that+same work.++  2. Basic Permissions.++  All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met.  This License explicitly affirms your unlimited+permission to run the unmodified Program.  The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work.  This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++  You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force.  You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright.  Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++  Conveying under any other circumstances is permitted solely under+the conditions stated below.  Sublicensing is not allowed; section 10+makes it unnecessary.++  3. Protecting Users' Legal Rights From Anti-Circumvention Law.++  No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++  When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++  4. Conveying Verbatim Copies.++  You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++  You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++  5. Conveying Modified Source Versions.++  You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++    a) The work must carry prominent notices stating that you modified+    it, and giving a relevant date.++    b) The work must carry prominent notices stating that it is+    released under this License and any conditions added under section+    7.  This requirement modifies the requirement in section 4 to+    "keep intact all notices".++    c) You must license the entire work, as a whole, under this+    License to anyone who comes into possession of a copy.  This+    License will therefore apply, along with any applicable section 7+    additional terms, to the whole of the work, and all its parts,+    regardless of how they are packaged.  This License gives no+    permission to license the work in any other way, but it does not+    invalidate such permission if you have separately received it.++    d) If the work has interactive user interfaces, each must display+    Appropriate Legal Notices; however, if the Program has interactive+    interfaces that do not display Appropriate Legal Notices, your+    work need not make them do so.++  A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit.  Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++  6. Conveying Non-Source Forms.++  You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++    a) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by the+    Corresponding Source fixed on a durable physical medium+    customarily used for software interchange.++    b) Convey the object code in, or embodied in, a physical product+    (including a physical distribution medium), accompanied by a+    written offer, valid for at least three years and valid for as+    long as you offer spare parts or customer support for that product+    model, to give anyone who possesses the object code either (1) a+    copy of the Corresponding Source for all the software in the+    product that is covered by this License, on a durable physical+    medium customarily used for software interchange, for a price no+    more than your reasonable cost of physically performing this+    conveying of source, or (2) access to copy the+    Corresponding Source from a network server at no charge.++    c) Convey individual copies of the object code with a copy of the+    written offer to provide the Corresponding Source.  This+    alternative is allowed only occasionally and noncommercially, and+    only if you received the object code with such an offer, in accord+    with subsection 6b.++    d) Convey the object code by offering access from a designated+    place (gratis or for a charge), and offer equivalent access to the+    Corresponding Source in the same way through the same place at no+    further charge.  You need not require recipients to copy the+    Corresponding Source along with the object code.  If the place to+    copy the object code is a network server, the Corresponding Source+    may be on a different server (operated by you or a third party)+    that supports equivalent copying facilities, provided you maintain+    clear directions next to the object code saying where to find the+    Corresponding Source.  Regardless of what server hosts the+    Corresponding Source, you remain obligated to ensure that it is+    available for as long as needed to satisfy these requirements.++    e) Convey the object code using peer-to-peer transmission, provided+    you inform other peers where the object code and Corresponding+    Source of the work are being offered to the general public at no+    charge under subsection 6d.++  A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++  A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling.  In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage.  For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product.  A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++  "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source.  The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++  If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information.  But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++  The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed.  Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++  Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++  7. Additional Terms.++  "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law.  If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++  When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it.  (Additional permissions may be written to require their own+removal in certain cases when you modify the work.)  You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++  Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++    a) Disclaiming warranty or limiting liability differently from the+    terms of sections 15 and 16 of this License; or++    b) Requiring preservation of specified reasonable legal notices or+    author attributions in that material or in the Appropriate Legal+    Notices displayed by works containing it; or++    c) Prohibiting misrepresentation of the origin of that material, or+    requiring that modified versions of such material be marked in+    reasonable ways as different from the original version; or++    d) Limiting the use for publicity purposes of names of licensors or+    authors of the material; or++    e) Declining to grant rights under trademark law for use of some+    trade names, trademarks, or service marks; or++    f) Requiring indemnification of licensors and authors of that+    material by anyone who conveys the material (or modified versions of+    it) with contractual assumptions of liability to the recipient, for+    any liability that these contractual assumptions directly impose on+    those licensors and authors.++  All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10.  If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term.  If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++  If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++  Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++  8. Termination.++  You may not propagate or modify a covered work except as expressly+provided under this License.  Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++  However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++  Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++  Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License.  If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++  9. Acceptance Not Required for Having Copies.++  You are not required to accept this License in order to receive or+run a copy of the Program.  Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance.  However,+nothing other than this License grants you permission to propagate or+modify any covered work.  These actions infringe copyright if you do+not accept this License.  Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++  10. Automatic Licensing of Downstream Recipients.++  Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License.  You are not responsible+for enforcing compliance by third parties with this License.++  An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations.  If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++  You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License.  For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++  11. Patents.++  A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based.  The+work thus licensed is called the contributor's "contributor version".++  A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version.  For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++  Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++  In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement).  To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++  If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients.  "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++  If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++  A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License.  You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++  Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++  12. No Surrender of Others' Freedom.++  If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License.  If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all.  For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++  13. Use with the GNU Affero General Public License.++  Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work.  The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++  14. Revised Versions of this License.++  The Free Software Foundation may publish revised and/or new versions of+the GNU General Public License from time to time.  Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++  Each version is given a distinguishing version number.  If the+Program specifies that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation.  If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++  If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++  Later license versions may give you additional or different+permissions.  However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++  15. Disclaimer of Warranty.++  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++  16. Limitation of Liability.++  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++  17. Interpretation of Sections 15 and 16.++  If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++                     END OF TERMS AND CONDITIONS++            How to Apply These Terms to Your New Programs++  If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++  To do so, attach the following notices to the program.  It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++    <one line to give the program's name and a brief idea of what it does.>+    Copyright (C) <year>  <name of author>++    This program is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This program is distributed in the hope that it will be useful,+    but WITHOUT ANY WARRANTY; without even the implied warranty of+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+    GNU General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this program.  If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++  If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++    <program>  Copyright (C) <year>  <name of author>+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+    This is free software, and you are welcome to redistribute it+    under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License.  Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++  You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<http://www.gnu.org/licenses/>.++  The GNU General Public License does not permit incorporating your program+into proprietary programs.  If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library.  If this is what you want to do, use the GNU Lesser General+Public License instead of this License.  But first, please read+<http://www.gnu.org/philosophy/why-not-lgpl.html>.+
+ Setup.hs view
@@ -0,0 +1,5 @@+#!/usr/bin/env runhaskell++import qualified	Distribution.Simple++main	= Distribution.Simple.defaultMain
+ changelog view
@@ -0,0 +1,25 @@+2010-11-18 Dr. Alistair Ward <regexdot at functionalley dot eu>++0.9.0.0+	* First version of the package.+0.10.0.0+	* Reacted to major-number changes in the package 'ToolShed-0.10.0.0'.+	* Created "src/RegExDot/" sub-directory & then modified module-names accordingly.+	* Pacified 'hlint'.+	* Added a makefile.+	* Removed awkward unary operators from 'Repeatable.hs'.+	* Qualified identifiers used in error-messages.+0.10.0.1+	* Ported to ghc-7.0.1:+		Used definitions of 'NFData' & 'rnf' from new package 'deepseq', rather than 'parallel'.+		Replaced reference to the deprecated function 'Control.Parallel.Strategies.parFlatMap' with @concat . Control.Parallel.Strategies.parMap@.+0.10.1.0+	* Set @threaded@ flag to @True@ by default.+	* Replaced use of @threaded@-flag in source-code, with CABAL CPP-macro.+	* Relocated "RegExDot.Options" to "ToolShed.Options".+0.10.2.0+	* Renamed package from 'RegExDot' to 'regexdot', for compatibility with Debian's ".deb"-format.+0.10.2.1+	* Added manually controlled "llvm" flag to "regexdot.cabal".+	* Changed identifier for type-parameters, to better reflect its role.+	* Reacted to the creation of module "ToolShed.Defaultable".
@@ -0,0 +1,11 @@+Author:+	Dr. Alistair Ward <regexdot at functionalley dot eu>.++Copyright:+	Copyright (C) 2010-2011 Dr. Alistair Ward. All Rights Reserved.++Home-page:+	http://functionalley.eu++License:+	GNU GENERAL PUBLIC LICENSE Version 3; see '/usr/share/common-licenses/GPL-3' or '/usr/share/doc/licenses/gpl-3.0.txt' where available, or the local packaged file 'LICENSE'.
+ makefile view
@@ -0,0 +1,55 @@+# Copyright (C) 2010 Dr. Alistair Ward+# +# This program is free software: you can redistribute it and/or modify+# it under the terms of the GNU General Public License as published by+# the Free Software Foundation, either version 3 of the License, or+# (at your option) any later version.+# +# This program is distributed in the hope that it will be useful,+# but WITHOUT ANY WARRANTY; without even the implied warranty of+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+# GNU General Public License for more details.+# +# You should have received a copy of the GNU General Public License+# along with this program.  If not, see <http://www.gnu.org/licenses/>.+ +.PHONY: all build clean configure copy haddock help hlint install prof sdist++all: install++install: build haddock+	@[ -z "$$CABAL_INSTALL_OPTIONS" ] || echo "INFO: CABAL_INSTALL_OPTIONS='$$CABAL_INSTALL_OPTIONS'"+	runhaskell Setup.hs $@ $$CABAL_INSTALL_OPTIONS++prof:+	CABAL_CONFIGURE_OPTIONS="--enable-library-profiling $$CABAL_CONFIGURE_OPTIONS" make install++copy: build+	@[ -z "$$CABAL_COPY_OPTIONS" ] || echo "INFO: CABAL_COPY_OPTIONS='$$CABAL_COPY_OPTIONS'"+	runhaskell Setup.hs $@ $$CABAL_COPY_OPTIONS++build: configure+	@[ -z "$$CABAL_BUILD_OPTIONS" ] || echo "INFO: CABAL_BUILD_OPTIONS='$$CABAL_BUILD_OPTIONS'"+	runhaskell Setup.hs $@ $$CABAL_BUILD_OPTIONS++configure: regexdot.cabal Setup.hs+	@[ -z "$$CABAL_CONFIGURE_OPTIONS" ] || echo "INFO: CABAL_CONFIGURE_OPTIONS='$$CABAL_CONFIGURE_OPTIONS'"+	runhaskell Setup.hs $@ $$CABAL_CONFIGURE_OPTIONS	#--user++haddock: configure+	PATH=~/.cabal/bin:$$PATH runhaskell Setup.hs $@ --hyperlink-source	#Amend path to find 'HsColour', as required for 'hyperlink-source'.++hlint:+	@$@ src/++sdist: configure+	runhaskell Setup.hs $@++clean:+	runhaskell Setup.hs $@++help:+	@grep '^[a-zA-Z].*:' makefile | sed -e 's/:.*//'++rpm: regexdot.rpm build+
+ regexdot.cabal view
@@ -0,0 +1,80 @@+--Package-properties+Name:			regexdot+Version:		0.10.2.1+Cabal-Version:		>= 1.6+Copyright:		(C) 2010 Dr. Alistair Ward+License:		GPL+License-file:		LICENSE+Author:			Dr. Alistair Ward+Stability:		Unstable interface, incomplete features.+Synopsis:		A /polymorphic/, POSIX, extended regular-expression engine.+Build-Type:		Simple+Description:		Provides a portable, POSIX, extended regex-engine, designed to process a list of /arbitrary/ objects.+Category:		Search, Regex+Tested-With:		GHC == 6.10, GHC == 6.12, GHC == 7.0+Homepage:		http://functionalley.eu+Maintainer:		regexdot <at> functionalley <dot> eu+Bug-reports:		regexdot <at> functionalley <dot> eu+Extra-Source-Files:	changelog, copyright, makefile++flag haveDeepSeq+    Description:	Use 'Control.DeepSeq' rather than the older module 'Control.Parallel.Strategies'.+    Default:		True++flag llvm+    Description:	Whether the 'llvm' backend has been installed and is required for code-generation.+    manual:		True+    default:		False++flag threaded+    Description:	Enable parallelized code.+    default:		True++Library+    hs-source-dirs:	src++    Exposed-modules:+        RegExDot.Anchor+        RegExDot.BracketExpression+        RegExDot.BracketExpressionMember+        RegExDot.CompilationOptions+        RegExDot.Consumer+        RegExDot.ConsumptionBounds+        RegExDot.ConsumptionProfile+        RegExDot.DataSpan+        RegExDot.DataSpanTree+        RegExDot.DSL+        RegExDot.ExecutionOptions+        RegExDot.InstanceInt+        RegExDot.Meta+        RegExDot.RegEx+        RegExDot.RegExOpts+        RegExDot.Repeatable+        RegExDot.Result+        RegExDot.ShowablePredicate+        RegExDot.Span+        RegExDot.Tree++    Build-depends:+        base == 4.*,+        parsec == 3.*,+        toolshed == 0.11.*++    GHC-options:	-Wall -O2+    GHC-prof-options:	-prof -auto-all -caf-all++    if flag(haveDeepSeq)+        Build-depends:	deepseq >= 1.1+        CPP-options:	-DHAVE_DEEPSEQ+    else+        Build-depends:	parallel == 1.*++    if flag(threaded)+        Build-depends:	parallel >= 3.0+        GHC-options:	-feager-blackholing+    else+        Build-depends:	parallel++    if impl(ghc >= 7.0) && flag(llvm)+        GHC-options:	-fllvm+
+ src/RegExDot/Anchor.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* A type of /zero-width assertion/:+	the 'Bow'-predicate is 'True' if no input-data has yet been consumed; the 'Stern'-predicate is 'True' if no input-data remains to be consumed.++	* Whilst at the top-level of the regex, the /absence/ of an anchor is equivalent to a non-capturing @.*?@,+	tempting one to implement them using this property,+	it doesn't apply to those anchors encountered at either end of a sub-expression.+	So in all cases, an anchor constrains the permissible consumption of input-data,+	but at the top level it also prevents the regex drifting away from either end of the input-data.+-}++module RegExDot.Anchor(+-- * Types+-- ** Data-types+	Anchor(..),+-- * Constants+	bowToken,+	sternToken,+	tokens+) where++#ifdef HAVE_DEEPSEQ+import	Control.DeepSeq(NFData, rnf)+#else+import	Control.Parallel.Strategies(NFData, rnf)+#endif++-- | Defines the types on /anchor/ by which a /regex/ can be moored to a part of the input-data.+data Anchor =+	Bow	-- ^ Matches only if no input data has yet been consumed. Can only exist at the start of the entire regex, or (in theory) the start of any /alternative/.+	| Stern	-- ^ Matches only if no input data remains to be consumed. Can only exist at the end of the entire regex, or (in theory) the end of any /alternative/.+	deriving (+		Eq+--		Read,	--See specialisation below.+--		Show	--See specialisation below.+	)++instance Show Anchor	where+	showsPrec _ Bow		= showChar bowToken+	showsPrec _ Stern	= showChar sternToken++instance Read Anchor	where+	readsPrec _ []		= []	--No parse.+	readsPrec _ (' ' : s)	= reads s	--Consume white-space.+	readsPrec _ ('\t' : s)	= reads s	--Consume white-space.+	readsPrec _ (c : s)	= case c `lookup` [(bowToken, Bow), (sternToken, Stern)] of+		Just anchor	-> [(anchor, s)]+		_		-> []	--No parse.++instance NFData Anchor	where+	rnf _	= ()++-- | The conventional token used to denote a 'Bow'-anchor, when in 'String'-form.+bowToken :: Char+bowToken	= '^'++-- | The conventional token used to denote a 'Stern'-anchor, when in 'String'-form.+sternToken :: Char+sternToken	= '$'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [bowToken, sternToken]+
+ src/RegExDot/BracketExpression.hs view
@@ -0,0 +1,63 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	This concept was traditionally known as a /Character-class/,+	but to distinguish it from the more recently introduced & narrower /POSIX Character-class/, it has been renamed /Bracket-expression/.+-}++module RegExDot.BracketExpression(+-- * Types+-- ** Type-synonyms+	BracketExpression,+-- * Constants+	delimiterTokens,+	negationToken,+	tokens,+-- * Functions+-- ** Predicates+	containsMatch+) where++import			RegExDot.BracketExpressionMember((=~))+import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember++-- | A /Bracket-expression/ consists of a list of 'BracketExpressionMember.Member's.+type BracketExpression m	= [BracketExpressionMember.Member m]++-- | 'True' if the specified datum matches any 'BracketExpressionMember.Member' of the 'BracketExpression'.+containsMatch :: Eq m+	=> m			-- ^ The input datum.+	-> BracketExpression m	-- ^ The list of bracket-expression members within which to find a match.+	-> Bool+containsMatch datum	= any (datum =~)++-- | The delimiters of a /bracket-expression/, when in 'String'-form.+delimiterTokens :: (Char, Char)+delimiterTokens	= ('[', ']')++-- | Used to denote a negated /bracket-expression/, when in 'String'-form.+negationToken :: Char+negationToken	= '^'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [fst delimiterTokens, snd delimiterTokens, negationToken]+
+ src/RegExDot/BracketExpressionMember.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	Permits /Perl-style shortcut/s to be canned & assigned a single-'Char' mnemonic for subsequent reference;+	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'ShowablePredicate.ShowablePredicate'.++ [@CAVEATS@]++	Since the underlying polymorphic data-type isn't required to implement neither 'Enum' nor 'Ord', the implementation of 'Read' can't cope with range-specifications.+	Lacking this, Bracket-expression members must be enumerated exhaustively.+-}++module RegExDot.BracketExpressionMember(+-- * Type-classes+	ShortcutExpander(..),+-- * Types+-- ** Data-types+	Member(..),+-- * Functions+-- ** Operators+	(=~)+) where++import qualified	Control.Arrow+import qualified	RegExDot.ShowablePredicate	as ShowablePredicate++#ifdef HAVE_DEEPSEQ+import			Control.DeepSeq(NFData, rnf)+#else+import			Control.Parallel.Strategies(NFData, rnf)+#endif++infix 4 =~	--Same as (==).++{- |+	* The interface via which /Perl-style shortcut/s are expanded (when they occur within a /bracket-expression/), in a manner appropriate to the chosen type-parameter.++	* The expansion of /Perl-style shortcut/s, is more restricted inside than outside, a /bracket-expression/,+	& consequently are merely represented here by a 'ShowablePredicate.ShowablePredicate', rather than providing a more general form suitable also for those /Perl-style shortcuts/ found outside /bracket-expression/s.++	* This interface is implemented elsewhere, where the specific type-parameter & consequently the appropriate set of /Perl-style shortcut/s, are defined.+-}+class ShortcutExpander a	where+	findPredicate	:: Char -> Maybe (ShowablePredicate.ShowablePredicate a)	-- ^ Attempt to find the appropriate 'ShowablePredicate.ShowablePredicate' to implement this /Perl-style shortcut/.++{- |+	* A /BracketExpression/ can contain either a literal, a range of literals given @(Enum a, Ord a)@, a /Perl-style shortcut/, or when 'Char' is the type-parameter, a /POSIX Character-class/.++	* This data-type reduces the representation of all these possibilities to either a predicate or a literal.+-}+data Member m	=+	Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ This 'Member' is described using a /predicate/, which is run to determine whether the datum conforms & is a member of the "BracketExpression".+	| Literal m						-- ^ This 'Member' is defined literally, using an item of the polymorphic type.+	deriving Eq++instance Show m => Show (Member m)	where+	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate+	showsPrec _ (Literal literal)			= shows literal++instance (ShortcutExpander m, Read m) => Read (Member m)	where+	readsPrec _ []				= []		--No parse.+	readsPrec _ (' ' : s)			= reads s	--Consume white-space.+	readsPrec _ ('\t' : s)			= reads s	--Consume white-space.+	readsPrec _ ('\\' : shortcut : s)	= case findPredicate shortcut of+		Just showablePredicate	-> [(Predicate showablePredicate, s)]+		_			-> error $ "readsPrec RegExDot.BracketExpressionMember.Member:\tfindPredicate failed for shortcut " ++ show shortcut+	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal++instance NFData m => NFData (Member m)	where+	rnf (Predicate showablePredicate)	= rnf showablePredicate+	rnf (Literal literal)			= rnf literal++-- | Match-operator.+(=~) :: Eq m+	=> m		-- ^ The input datum.+	-> Member m	-- ^ The member of the bracket-expression against which the input-datum is to be matched.+	-> Bool		-- ^ The result of the match-operation.+datum =~ Predicate showablePredicate	= ShowablePredicate.predicate showablePredicate datum+datum =~ Literal literal		= datum == literal+
+ src/RegExDot/CompilationOptions.hs view
@@ -0,0 +1,48 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines those options relating to compile-time.++	* These tend to be options which affect the result, rather than the means by which it is achieved.++	* Whilst similar structures are present in other regex-implementations, there's no standardisation of the fields.+-}++module RegExDot.CompilationOptions(+-- * Types+-- ** Data-types+	CompilationOptions(..)+) where++import qualified	ToolShed.Defaultable	as Defaultable+import qualified	ToolShed.Options	as Options++-- | The switch(es) used to control compilation of the /regex/-engine.+data CompilationOptions	= MkCompilationOptions {+	complyStrictlyWithPosix	:: Bool		-- ^ Define the offset of captured data, corresponding to a sub-expression which matched zero times, as the artificial value @-1@ specified by POSIX.+} deriving (Eq, Show)++instance Defaultable.Defaultable CompilationOptions	where+	defaultValue	= Options.blankValue { complyStrictlyWithPosix	= True }++instance Options.Options CompilationOptions	where+	blankValue	= MkCompilationOptions { complyStrictlyWithPosix = undefined }+
+ src/RegExDot/Consumer.hs view
@@ -0,0 +1,105 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Define the requirements & capabilities of a data-type which is designed to consume data.++	* This module also facilitates investigation of group-behaviour, by defining operations on either concatenations or alternations of 'Consumer's.++	* This module is specifically for use in a /regex/-engine,+	in which the meta-data from which the /regex/ is composed, are considered to /consume/ input data,+	whilst concatenation & alternation of such elements, behave as a different types of groups of consumer.+-}++module RegExDot.Consumer(+-- * Type-classes+	Consumer(..),+-- * Types+-- ** Type-synonyms+--	StarHeight,+-- * Functions+	accumulateConsumptionProfiles,+--	accumulateConsumptionProfilesFrom,+	aggregateConsumptionProfilesFromAlternatives,+	aggregateConsumptionProfilesFromConcatenation,+-- ** Query+	getConsumptionBounds,+	getFewest,+	getHasSpecificRequirement+) where++import			RegExDot.ConsumptionProfile((<>), (|+|))+import qualified	Data.List+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile++{- |+	* A measure of the complexity of a /regex/, which has some baring on either the time-complexity or the space-complexity of the solution.++	* <http://en.wikipedia.org/wiki/Star_height>+-}+type StarHeight	= Int++{- |+	* The interface, to which types, which have a data-requirement, may conform.++	* Regrettably, methods requiring reference to the type of the consumable, would require multi-parameter type-classes.++	* 'consumptionProfile' defines the quantity-range of data which may be consumed, & any specific data-requirements.++	* 'starHeight' measures the complexity of the 'Consumer'.+-}+class Consumer c	where+	consumptionProfile	:: c -> ConsumptionProfile.ConsumptionProfile+	starHeight		:: c -> StarHeight++instance Consumer c => Consumer [c] where+	consumptionProfile	= undefined+	starHeight		= Data.List.foldl' (\l -> max l . starHeight) 0++-- | Convenience-function, to query the 'ConsumptionBounds.ConsumptionBounds' of a 'Consumer'.+getConsumptionBounds :: Consumer c => c -> ConsumptionBounds.ConsumptionBounds+getConsumptionBounds	= ConsumptionProfile.consumptionBounds . consumptionProfile++-- | Determine the minimum acceptable quantity of data.+getFewest :: Consumer c => c -> ConsumptionBounds.DataLength+getFewest	= fst . getConsumptionBounds++-- | Determine whether the specified 'Consumer', has a specific requirement.+getHasSpecificRequirement :: Consumer c => c -> Bool+getHasSpecificRequirement	= ConsumptionProfile.hasSpecificRequirement . consumptionProfile++-- | The aggregate of the specified alternation of 'ConsumptionProfile.ConsumptionProfile's.+aggregateConsumptionProfilesFromAlternatives :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile+aggregateConsumptionProfilesFromAlternatives []	= error "RegExDot.Consumer.aggregateConsumptionProfilesFromAlternatives:\tnull list"+aggregateConsumptionProfilesFromAlternatives l	= Data.List.foldl1' (<>) $ consumptionProfile `map` l++-- | Get the 'ConsumptionProfile.ConsumptionProfile's for the specified list of 'Consumer's, then find the net effect of concatenating them.+aggregateConsumptionProfilesFromConcatenation :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile+aggregateConsumptionProfilesFromConcatenation	= Data.List.foldl' (\acc -> (acc |+|) . consumptionProfile) ConsumptionProfile.zero {-initial value-}++-- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them, from the specified initial value.+accumulateConsumptionProfilesFrom :: Consumer c => ConsumptionProfile.ConsumptionProfile -> [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles+accumulateConsumptionProfilesFrom	= scanr ((|+|) . consumptionProfile)++-- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them.+accumulateConsumptionProfiles :: Consumer c => [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles+accumulateConsumptionProfiles	= {-init .-} accumulateConsumptionProfilesFrom ConsumptionProfile.zero	--It's useful to leave the initial value at the end of the list.+
+ src/RegExDot/ConsumptionBounds.hs view
@@ -0,0 +1,48 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Describes the bounds of data-consumption.+-}++module RegExDot.ConsumptionBounds(+-- * Types+-- ** Type-synonyms+	ConsumptionBounds,+	DataLength,+-- * Constants+	zero,+-- * Functions+-- ** Predicates+	isPrecise+) where++-- | A measure of the quantity of an unspecified type of data.+type DataLength	= Int++-- | The minimum & maximum bounds of potential data-consumption.+type ConsumptionBounds	= (DataLength, Maybe DataLength)++-- | Exactly zero consumption.+zero :: ConsumptionBounds+zero	= (0, Just 0)++-- | Predicate which is 'True' if only a precise quantity of data is consumable; no more, no less.+isPrecise :: ConsumptionBounds -> Bool+isPrecise (fewest, most)	= Just fewest == most+
+ src/RegExDot/ConsumptionProfile.hs view
@@ -0,0 +1,146 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	A 'RegExDot.ConsumptionProfile' is composed from both a capacity to consume, & an ability to discriminate based on the specific data.+	The former is described by 'consumptionBounds' & the latter by both 'hasSpecificRequirement' & 'canConsumeAnything'.+-}++module RegExDot.ConsumptionProfile(+-- * Types+-- ** Type-synonyms+	AccumulatedConsumptionProfiles,+-- ** Data-types+	ConsumptionProfile(..),+-- * Constants+	zero,+-- * Functions+	accumulateFrom,+	aggregateFromConcatenation,+-- ** Operators+	(|+|),+	(<>),+-- ** Predicates+--	isPrecise,+	withinConsumptionBounds+) where++import			Control.Applicative((<$>), (<*>))+import qualified	Data.List+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	ToolShed.SelfValidate		as SelfValidate++infixr 5 |+|	--Same as (++).+infixr 2 <>	--Same as (||).++{- |+	* A 'Consumer' is considered to have a 'ConsumptionProfile' composed from both a capacity to consume, & an ability to discriminate.++	* Whilst 'hasSpecificRequirement' & 'canConsumeAnything' look like opposites, they can assume independent values; the instance can be both, but only be neither when empty.+-}+data ConsumptionProfile	= MkConsumptionProfile {+	consumptionBounds	:: ConsumptionBounds.ConsumptionBounds,	-- ^ The permissible quantity of data, which can be consumed.+	hasSpecificRequirement	:: Bool,				-- ^ Whether at least one specific input datum is required.+	canConsumeAnything	:: Bool					-- ^ Whether at least one arbitrary input datum can be consumed.+} deriving (Eq, Read, Show)++instance SelfValidate.SelfValidator ConsumptionProfile	where+	isValid c@(+		MkConsumptionProfile {+			consumptionBounds	= (fewest, most),+			hasSpecificRequirement	= hasSpecificRequirement',+			canConsumeAnything	= canConsumeAnything'+		}+	 ) = and [+		fewest >= 0,+		case most of+			Nothing	-> True+			Just m	-> m >= fewest,+		or [c == zero, hasSpecificRequirement', canConsumeAnything']+	 ]++{-+-- | 'True' if there's no choice in the quantity of data to consume.+isPrecise :: ConsumptionProfile -> Bool+isPrecise	= ConsumptionBounds.isPrecise . consumptionBounds+-}++-- | Predicate, which is 'True' if the specified data-length, falls within the specified 'ConsumptionBounds.ConsumptionBounds'.+withinConsumptionBounds ::+	ConsumptionBounds.DataLength	-- ^ The actual quantity of data consumed.+	-> ConsumptionProfile		-- ^ The bounds within which data-consumption is required to fall. +	-> Bool+withinConsumptionBounds dataLength MkConsumptionProfile {+	consumptionBounds	= (minConsumption, maxConsumption)+} = dataLength >= minConsumption && case maxConsumption of+	Nothing	-> True+	Just m	-> dataLength <= m++-- | Both minimum & maximum set to zero; which can be used as the initial value when accumulating the sum of a list.+zero :: ConsumptionProfile+zero	= MkConsumptionProfile {+	consumptionBounds	= ConsumptionBounds.zero,+	hasSpecificRequirement	= False,+	canConsumeAnything	= False+}++-- | The net effect of two concatenated 'ConsumptionProfile's.+(|+|) :: ConsumptionProfile -> ConsumptionProfile -> ConsumptionProfile+MkConsumptionProfile {+	consumptionBounds	= (lf, ls),+	hasSpecificRequirement	= lh,+	canConsumeAnything	= lc+} |+| MkConsumptionProfile {+	consumptionBounds	= (rf, rs),+	hasSpecificRequirement	= rh,+	canConsumeAnything	= rc+} = MkConsumptionProfile {+	consumptionBounds	= (lf + rf, (+) <$> ls <*> rs),	--The sum of those of the concatenation.+	hasSpecificRequirement	= lh || rh,			--The concatenation mandates consumption of at least one specific input datum, if either 'ConsumptionProfile' does.+	canConsumeAnything	= lc || rc			--The concatenation can consume at least one arbitrary input datum, if either 'ConsumptionProfile' can.+}++-- | The net effect of two alternative 'ConsumptionProfile's.+(<>) :: ConsumptionProfile -> ConsumptionProfile -> ConsumptionProfile+MkConsumptionProfile {+	consumptionBounds	= (lf, ls),+	hasSpecificRequirement	= lh,+	canConsumeAnything	= lc+} <> MkConsumptionProfile {+	consumptionBounds	= (rf, rs),+	hasSpecificRequirement	= rh,+	canConsumeAnything	= rc+} = MkConsumptionProfile {+	consumptionBounds	= (lf `min` rf, max <$> ls <*> rs),	--Stretched to envelope alternatives.+	hasSpecificRequirement	= lh && rh,				--The alternation mandates consumption of at least one specific input datum, if both 'ConsumptionProfile's do.+	canConsumeAnything	= lc || rc				--The alternation can consume at least one arbitrary input datum, if either 'ConsumptionProfile' can.+}++-- | The aggregate of the specified concatenation of 'ConsumptionProfile's.+aggregateFromConcatenation :: [ConsumptionProfile] -> ConsumptionProfile+aggregateFromConcatenation	= Data.List.foldl' (|+|) zero {-initial value-}++-- | The 'ConsumptionProfile's of all the aggregated tails of a list of 'Consumer's.+type AccumulatedConsumptionProfiles	= [ConsumptionProfile]++-- | Accumulate the specified list of 'ConsumptionProfile's, from the specified initial value.+accumulateFrom :: ConsumptionProfile -> [ConsumptionProfile] -> AccumulatedConsumptionProfiles+accumulateFrom	= scanr (|+|)+
+ src/RegExDot/DSL.hs view
@@ -0,0 +1,117 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines /binary/ operators, to form a /Domain-specific Language/, by which to compose 'RegEx.Concatenations' from 'RegEx.Pattern's.++	* Each operator quantifies the specified 'RegEx.Pattern', & prepends it to the specified 'RegEx.Concatenation'.++ [@TODO@]	Could @[]@ & 'Meta' be instances of a type-class which has methods (-:) etc.,+		allowing the former to prepend 'RegEx.captureGroup' & the latter 'RegEx.Require' ?++-}++module RegExDot.DSL(+-- * Functions+-- ** Operators+	(-:),+	(?:),+	(??:),+	(*:),+	(*?:),+	(+:),+	(+?:),+	( #->#:),+	( #->#?:),+	( #->:),+	( #->?:),+	( #:),+	(<~>)+) where++import			RegExDot.Repeatable((^#->#), (^#->#?), (^#->), (^#->?), (^#))+import qualified	RegExDot.Anchor		as Anchor+import qualified	RegExDot.RegEx		as RegEx+import qualified	RegExDot.Repeatable	as Repeatable++infixr 5 -:, ?:, ??:, *:, *?:, +:, +?:,#->#:, #->#?:, #->:, #->?:, #:, <~>	--Same as for ':', & lower than Repeatable's operators.++-- | Prepend an unrepeated 'RegEx.Pattern', to the specified 'RegEx.Concatenation'.+(-:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(-:) pattern	= (Repeatable.one pattern :)++-- | Prepend an optional 'RegEx.Pattern', to the specified 'RegEx.Concatenation'.+(?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(?:) pattern	= (Repeatable.zeroOrOne pattern :)++-- | A /non-greedy/ version of '?:'.+(??:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(??:) pattern	= (Repeatable.zeroOrOne' pattern :)++-- | Prepend a 'RegEx.Pattern', repeatable zero or more times, to the specified 'RegEx.Concatenation'.+(*:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(*:) pattern	= (Repeatable.zeroOrMore pattern :)++-- | A /non-greedy/ version of '*:'.+(*?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(*?:) pattern	= (Repeatable.zeroOrMore' pattern :)++-- | Prepend a 'RegEx.Pattern', repeatable one or more times, to the specified 'RegEx.Concatenation'.+(+:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(+:) pattern	= (Repeatable.oneOrMore pattern :)++-- | A /non-greedy/ version of '+:'.+(+?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(+?:) pattern	= (Repeatable.oneOrMore' pattern :)++-- | Prepend a 'RegEx.Pattern', repeated a range of times, to the specified 'RegEx.Concatenation'.+( #->#:) :: (RegEx.Pattern a, Repeatable.RepetitionBounds) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->#:) (pattern, bounds)	= (pattern ^#-># bounds :)++-- | A /non-greedy/ version of '#->#:'.+( #->#?:) :: (RegEx.Pattern a, Repeatable.RepetitionBounds) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->#?:) (pattern, bounds)	= (pattern ^#->#? bounds :)++-- | Prepend a 'RegEx.Pattern', repeated at least a specified number of times, to the specified 'RegEx.Concatenation'.+( #->:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->:) (pattern, fewest)	= (pattern ^#-> fewest :)++-- | A /non-greedy/ version of '#->:'.+( #->?:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->?:) (pattern, fewest)	= (pattern ^#->? fewest :)++-- | Prepend a 'RegEx.Pattern', repeated a precise number of times, to the specified 'RegEx.Concatenation'.+( #:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #:) (pattern, r)	= (pattern ^# r :)++{- |+	* Sandwiches a 'RegEx.Concatenation' between optional 'Anchor.Anchor's to construct a 'RegEx.ExtendedRegEx'.++	* Accounts for the unusual requirement to place an 'Anchor.Stern' at the start of the 'RegEx.Concatenation', or an 'Anchor.Bow' at the end.+-}+(<~>) ::+	(Maybe Anchor.Anchor, Maybe Anchor.Anchor)	-- ^ A specification for the anchors required around the expression.+	-> RegEx.Concatenation a			-- ^ The bare list of repeatable patterns to anchor.+	-> RegEx.ExtendedRegEx a			-- ^ The completed regex.+(bow, stern) <~> concatenation = RegEx.MkExtendedRegEx {+	RegEx.bowAnchor		= bow,+	RegEx.concatenation	= concatenation,+	RegEx.sternAnchor	= stern+}
+ src/RegExDot/DataSpan.hs view
@@ -0,0 +1,61 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Augments a 'Span.Span', with the specific data concerned.+-}++module RegExDot.DataSpan(+-- * Types+-- ** Type-synonyms+	DataSpan,+-- * Functions+	after,+	empty,+	join+) where++import			Control.Arrow((***))+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.Span			as Span++{- |+	* Augment 'Span.Span' with a copy of the data to which it refers.++	* Similar to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html#t%3AMatchText>.+-}+type DataSpan a	= ([a], Span.Span)++-- | Constructs an empty instance, located at the specified offset.+empty :: ConsumptionBounds.DataLength -> DataSpan a+empty offset	= ([], Span.empty offset)++-- | Returns the furthest extent, i.e. the offset just after the end.+after :: DataSpan a -> ConsumptionBounds.DataLength+after	= Span.after . snd++{- |+	* Condenses a list into a single value.++	* Expects a gap-free, sorted list.+-}+join ::+	ConsumptionBounds.DataLength	-- ^ The offset at which the concatenated match is considered to have occurred.+	-> [DataSpan a]			-- ^ The list to be concatenated.+	-> DataSpan a+join offset	= (concat *** Span.join offset) . unzip
+ src/RegExDot/DataSpanTree.hs view
@@ -0,0 +1,109 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Permits transformation of 'RegEx.MatchList', to facilitate standardisation.+-}++module RegExDot.DataSpanTree(+-- * Types+-- ** Type-synonyms+--	DataSpanTree,+--	DataSpanTreeList,+-- * Functions+	extractCaptureGroups,+	flattenTreeList,+	toTreeList+) where++import			Control.Applicative((<$>))+import qualified	Data.Foldable+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.DataSpan		as DataSpan+import qualified	RegExDot.RegEx			as RegEx+import qualified	RegExDot.Tree			as Tree++-- | Defines a similar 'Tree.Tree' to 'RegEx.Match', but with different 'Tree.Leaf's.+type DataSpanTree a	= Tree.Tree (DataSpan.DataSpan a)++-- | Defines a similar structure to 'RegEx.MatchList'.+type DataSpanTreeList a	= [DataSpanTree a]++-- | Converts a 'RegEx.MatchList' into a 'DataSpanTreeList', by transforming the 'Tree.Leaf's.+toTreeList :: RegEx.MatchList a -> DataSpanTreeList a+toTreeList	= map toTree	where+	toTree :: RegEx.Match a -> DataSpanTree a+	toTree	= (toDataSpan <$>)	where+		toDataSpan :: RegEx.MatchedData a -> DataSpan.DataSpan a+		toDataSpan (_, inputDataOffset, inputData)	= (inputData, (inputDataOffset, length inputData))++-- | Condenses a 'DataSpanTreeList's into a list of 'DataSpan.DataSpan's, using 'DataSpan.join'.+flattenTreeList ::+	ConsumptionBounds.DataLength	-- ^ The offset into the input-data at which a match occurred.+	-> DataSpanTreeList a		-- ^ The tree to flatten.+	-> [DataSpan.DataSpan a]+flattenTreeList _ []				= []+flattenTreeList offset (tree : treeList)	= flattenedTree : flattenTreeList (DataSpan.after flattenedTree) treeList	where+--	flattenedTree :: DataSpan.DataSpan a+	flattenedTree	= DataSpan.join offset $ Data.Foldable.toList tree++{- |+	* POSIX describes the contents of /capture-groups/, as summarised in <http://www2.research.att.com/~gsf/testregex/>.++	* 'RegEx.Result', is a complete description of the match between 'RegEx.InputData' & RegEx.ExtendedRegEx'; this function extracts a POSIX-conformant list from it.++	* The major differences are, that:++		Only data from parenthesized sub-expressions ('RegEx.Alternatives') is captured.++		Only the /last/ repetition of a repeated sub-expression is returned.+		<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.++		The data captured within each parenthesized sub-expression, is summarised as a single 'DataSpan.DataSpan'.++		POSIX specifies a 'Span.Span'-offset of @-1@, for sub-expressions which match zero times; cf sub-expressions which consume nothing, once.+		<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.+		@+			("ace" Text.Regex.Posix.=~ "a(b)*c(d)?e") :: Text.Regex.Base.RegexLike.MatchArray+			array (0,2) [(0,(0,3)),(1,(-1,0)),(2,(-1,0))]++			("ace" Text.Regex.Posix.=~ "a(b*)c(d?)e") :: Text.Regex.Base.RegexLike.MatchArray+			array (0,2) [(0,(0,3)),(1,(1,0)),(2,(2,0))]+		@+		I consider this a poor convention, resulting from the focus of POSIX on C, which makes subsequent calculation from the list of 'DataSpan's difficult & error-prone.+-}+extractCaptureGroups ::+	Bool			-- ^ Whether to strictly comply with /POSIX/.+	-> DataSpanTreeList a	-- ^ The tree-structure from which to extract the capture-groups.+	-> [DataSpan.DataSpan a]+extractCaptureGroups complyStrictlyWithPosix	= extractCaptureGroups' 0	where+	extractCaptureGroups' :: ConsumptionBounds.DataLength -> DataSpanTreeList a -> [DataSpan.DataSpan a]+	extractCaptureGroups' _ []			= []+	extractCaptureGroups' offset (tree : treeList)	= let+--		recurseHorizontallyFrom :: ConsumptionBounds.DataLength -> [DataSpan.DataSpan a]+		recurseHorizontallyFrom	= (`extractCaptureGroups'` treeList)+	 in case tree of+		Tree.Leaf dataSpan	-> recurseHorizontallyFrom $ DataSpan.after dataSpan+		Tree.Node []		-> DataSpan.empty (if complyStrictlyWithPosix then -1 else offset) : recurseHorizontallyFrom offset	--POSIX specifies an Span-offset of -1, for sub-expressions which match 0 times; cf sub-expressions which consumes nothing, once.+		Tree.Node treeLists	-> joinedFlattenedTreeList : (extractCaptureGroups' offset lastMatch {-recurse vertically-} ++ recurseHorizontallyFrom (DataSpan.after joinedFlattenedTreeList))	where+--			lastMatch :: DataSpanTreeList a+			lastMatch	= last treeLists	-- <http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.++--			joinedFlattenedTreeList :: DataSpan.DataSpan a+			joinedFlattenedTreeList	= DataSpan.join offset $ flattenTreeList offset lastMatch+
+ src/RegExDot/ExecutionOptions.hs view
@@ -0,0 +1,102 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines those options relating to the "RegExDot.RegEx"-runtime.++	* These tend to be options which don't affect the result, only the means by which it is achieved.++	* Whilst similar structures are present in other regex-implementations, there's no standardisation of the fields.+-}++module RegExDot.ExecutionOptions(+-- * Types+-- ** Data-types+	ExecutionOptions(..),+-- * Functions+-- ** Mutators+	setVerbose+) where++import qualified	ToolShed.Defaultable	as Defaultable+import qualified	ToolShed.Options	as Options++-- | The switches used to control execution of the /regex/-engine.+data ExecutionOptions	= MkExecutionOptions {+	abortTrialRepetitionsOnInherentFailure	:: Bool,	-- ^ If an /alternative/ can't match, irrespective of the subsequent /RegExDot.RegEx.Concatenation/, then avoid futile trial /RegExDot.Repeatable.Repetitions/. The converse of 'checkForUnconsumableData'.+	abortTrialRepetitionsOnZeroConsumption	:: Bool,	-- ^ Check for zero data-consumption by the @n@-th /RegExDot.Repeatable.Repeatable RegExDot.RegEx.CaptureGroup/, before attempting @n+1@.+	bypassInputDataForLiberalConsumer	:: Bool,	-- ^ Whether to bypass reading of the input data, if the mapping to specific /RegExDot.RegEx.RepeatablePattern/s isn't required, & the /RegExDot.RegEx.ExtendedRegEx/ can consume the required quantity of anything.+	catchIncompatibleAnchors		:: Bool,	-- ^ Avoid futile trial solutions, involving repetitions of anchored alternatives, which must consume data.+	checkExistenceOfInelasticTail		:: Bool,	-- ^ If the /RegExDot.RegEx.ExtendedRegEx/ ends in an inelastic (zero /Star-height/) tail, confirm its existence at the end of the /RegExDot.RegEx.InputData/.+	checkForUnconsumableData		:: Bool,	-- ^ Check whether there's no possibility of consuming some of the input data. The converse of 'abortTrialRepetitionsOnInherentFailure'.+	moderateGreed				:: Bool,	-- ^ Greedily consume data, only up to the limit beyond which, future requirements would be compromised.+	permitReorderingOfAlternatives		:: Bool,	-- ^ Permit /RegExDot.RegEx.Alternatives/ to be re-ordered, in an attempt to more quickly locate a result.+	preferAlternativesWhichFeedTheGreedy	:: Bool,	-- ^ Within the /RegExDot.RegEx.MatchedData/ from which each candidate /RegExDot.RegEx.Match/ amongst sequences of /RegExDot.RegEx.Alternatives/, is ultimately composed, prefer /RegExDot.ConsumptionBounds.ConsumptionBounds/ of /RegExDot.RegEx.InputData/, beyond /RegExDot.Repeatable.getFewest/, by /RegExDot.Repeatable.isGreedy RegExDot.RegEx.RepeatablePattern/s.+	preferAlternativesWhichMimickUnrolling	:: Bool,	-- ^ Compare /RegExDot.ConsumptionBounds.ConsumptionBounds/ on successive /RegExDot.Repeatable.Repetitions/ of /RegExDot.RegEx.CaptureGroup/, between candidate /RegExDot.RegEx.Match/es, to mimic the behaviour of the unrolled /RegExDot.Repeatable.Repetitions/.+	preferFewerRepeatedAlternatives		:: Bool,	-- ^ Prefer fewer /RegExDot.Repeatable.Repetitions/ of /RegExDot.RegEx.Alternatives/, to discourage the capture of null lists of /RegExDot.RegEx.InputData/.+	requireMatchList			:: Bool,	-- ^ If merely interested in a 'Bool' result, rather than the optimal mapping of input data to /RegExDot.RegEx.RepeatablePattern/s, avoid unnecessary evaluation of the /RegEx.Match/.+	unrollRepeatedSingletonAlternative	:: Bool,	-- ^ Check whether /RegExDot.RegEx.Alternatives/ consists of just a singleton /RegExDot.RegEx.ExtendedRegEx/, & has therefore been used merely as a capture-group. Though this doesn't affect the result, it vastly improves efficiency.+	useFirstMatchAmongAlternatives		:: Bool,	-- ^ Rather than performing an exhaustive search for the optimal choice amongst /RegExDot.RegEx.Alternatives/, merely select the first that matches; conform to /Perl/ rather than /POSIX/.+	validateMinConsumptionOfAlternatives	:: Bool		-- ^ When the number of repetitions of a /RegExDot.RegEx.CaptureGroup/ is precisely specified, check whether the resulting minimum data-requirement is available.+} deriving (Eq, Show)++instance Defaultable.Defaultable ExecutionOptions	where+	defaultValue	= setVerbose False $ Options.blankValue {+		abortTrialRepetitionsOnInherentFailure	= True,		--Regrettably, this slightly reduces performance for most non-pathological patterns.+		catchIncompatibleAnchors		= True,+		checkExistenceOfInelasticTail		= True,+		checkForUnconsumableData		= True,		--Expensive, particularly when (not requireMatchList), & only typically useful in failure-scenarios.+		moderateGreed				= True,		--Cost may exceed benefit. TODO: confirm.+		preferAlternativesWhichFeedTheGreedy	= True,+		preferAlternativesWhichMimickUnrolling	= True,+		preferFewerRepeatedAlternatives		= True,+		unrollRepeatedSingletonAlternative	= True,		--Affects only efficiency, not the result.+		useFirstMatchAmongAlternatives		= False,	--Perl-style matching may be faster, but may also yield a sub-optimal Match.+		validateMinConsumptionOfAlternatives	= False		--The cost outweighs the small infrequent dividend.+	}++instance Options.Options ExecutionOptions	where+	blankValue	= MkExecutionOptions {+		abortTrialRepetitionsOnInherentFailure	= undefined,+		abortTrialRepetitionsOnZeroConsumption	= undefined,+		bypassInputDataForLiberalConsumer	= undefined,+		catchIncompatibleAnchors		= undefined,+		checkExistenceOfInelasticTail		= undefined,+		checkForUnconsumableData		= undefined,+		moderateGreed				= undefined,+		permitReorderingOfAlternatives		= undefined,+		preferAlternativesWhichFeedTheGreedy	= undefined,+		preferAlternativesWhichMimickUnrolling	= undefined,+		preferFewerRepeatedAlternatives		= undefined,+		requireMatchList			= undefined,+		unrollRepeatedSingletonAlternative	= undefined,+		useFirstMatchAmongAlternatives		= undefined,+		validateMinConsumptionOfAlternatives	= undefined+	}++-- | Sets those fields which depend crucially on whether the caller wants to retrieve any /RegExDot.RegEx.MatchList/ from the /RegExDot.RegEx.Result/, or just query whether there is one.+setVerbose :: Bool -> ExecutionOptions -> ExecutionOptions+setVerbose verbose e	= e {+	abortTrialRepetitionsOnZeroConsumption	= verbose,	--The corresponding check, involves evaluation of a /RegExDot.RegEx.MatchList/, which is too expensive if the /RegExDot.RegEx.Matchlist/ isn't otherwise required.+	bypassInputDataForLiberalConsumer	= not verbose,	--Potentially bypasses reading of /RegExDot.RegEx.InputData/, which is inappropriate if the mapping into a /RegExDot.RegEx.Result/ is required.+	permitReorderingOfAlternatives		= not verbose,	--Doesn't help when 'requireMatchList', since an exhaustive search of /RegExDot.RegEx.Alternatives/, for the optimal solution, is performed.+	requireMatchList			= verbose+}+
+ src/RegExDot/InstanceInt.hs view
@@ -0,0 +1,43 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Provides a default implementation of the type-classes, required for 'RegEx.ExtendedRegEx' Int.++ [@CAVEATS@]++	In contrast to the traditional regex, there's no reason to escape a 'Char', other than when referring to a /shortcut/.+	Since no /shortcuts/ are defined in this minimal definition, it's always an error to escape a character.+-}++module RegExDot.InstanceInt() where++import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember+import qualified	RegExDot.Meta				as Meta+import qualified	RegExDot.RegEx				as RegEx++instance BracketExpressionMember.ShortcutExpander Int	where+	findPredicate _	= Nothing++instance Meta.ShortcutExpander Int	where+	expand c	= error $ "RegExDot.Meta.ShortcutExpander.expand RegExDot.InstanceInt:\tunrecognised shortcut '" ++ show c ++ "'."	-- A shortcut is the only legitimate reason to escape a character.++instance RegEx.ShortcutExpander Int	where+	expand c	= error $ "RegExDot.RegEx.ShortcutExpander.expand RegExDot.InstanceInt:\tunrecognised shortcut '" ++ show c ++ "'."	-- A shortcut is the only legitimate reason to escape a character.+
+ src/RegExDot/Meta.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Describes the set of polymorphic data, which can be matched.++	* Only permits a match against exactly one polymorphic datum, which distinguishes it from a /zero-width assertion/, like an /anchor/, /word-boundary/, or /look-ahead assertion/.++	* Designed to be used by a polymorphic /regex/-engine, to implement the traditional meta-characters; @. [] [^]@.++	* Permits /Perl-style shortcuts/ for commonly used 'Meta'-data, to be canned & assigned a single-'Char' mnemonic for subsequent reference;+	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'Meta'.+	Since this class is polymorphic, it has no knowledge of what shortcuts might be appropriate for the chosen type-parameter,+	so the expansion from the back-slashed 'Char' to corresponding 'Meta'-data, is performed through the 'expand' interface of the 'ShortcutExpander' class, which should be implemented elsewhere.+-}++module RegExDot.Meta(+-- * Type-classes+	ShortcutExpander(..),+-- * Types+-- ** Data-types+	Meta(..),+-- * Constants+	shortcutToken,+	anyToken,+	tokens,+-- * Functions+-- ** Predicates+	isMatch+) where++import qualified	Control.Arrow+import qualified	RegExDot.BracketExpression		as BracketExpression+import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember+import qualified	RegExDot.Consumer			as Consumer+import qualified	RegExDot.ConsumptionProfile		as ConsumptionProfile+import qualified	RegExDot.ShowablePredicate		as ShowablePredicate+import qualified	ToolShed.SelfValidate			as SelfValidate++#ifdef HAVE_DEEPSEQ+import			Control.DeepSeq(NFData, rnf)+#else+import			Control.Parallel.Strategies(NFData, rnf)+#endif++{- |+	* The interface via which /Perl-style shortcut/s are expanded, in a manner appropriate to the chosen type-parameter.++	* Since the expansion of /Perl-style shortcut/s, is more restricted inside than outside a 'BracketExpression.BracketExpression',+	the former is considered to be a superclass, providing a base from which to build alternative implementations.+-}+class BracketExpressionMember.ShortcutExpander m => ShortcutExpander m	where+	expand	:: Char -> Meta m	-- ^ Expand a /Perl-style shortcut/.++-- | Declares a polymorphic data-type.+data Meta m =+	Any							-- ^ Any datum matches. Equivalent to @NoneOf []@, but more efficient. CAVEAT: independent of the type-parameter @a@.+	| Literal m						-- ^ The datum matches, if it's equal to the specified value. Equivalent to @AnyOf [BracketExpression.Literal x]@, but more efficient.+	| AnyOf (BracketExpression.BracketExpression m)		-- ^ The datum matches, if 'BracketExpression.containsMatch'.+	| NoneOf (BracketExpression.BracketExpression m)	-- ^ The datum matches, if @not BracketExpression.containsMatch@.+	| Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ The datum matches if 'ShowablePredicate.ShowablePredicate'.+	deriving (+		Eq+--		Read,	--Specialised below.+--		Show	--Specialised below.+	)++instance SelfValidate.SelfValidator (Meta m)	where+	isValid _	= True++instance Show m => Show (Meta m)	where+	showsPrec _ Any					= showChar anyToken+	showsPrec _ (Literal m)				= shows m+	showsPrec _ (AnyOf bracketExpression)		= shows bracketExpression+	showsPrec _ (NoneOf bracketExpression)		= showChar x . showChar BracketExpression.negationToken . showString xs	where (x : xs)	= show $ AnyOf bracketExpression+	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate++instance (ShortcutExpander m, Read m) => Read (Meta m)	where+	readsPrec _ []				= []		--No parse.+	readsPrec _ (' ' : s)			= reads s	--Consume white-space.+	readsPrec _ ('\t' : s)			= reads s	--Consume white-space.+	readsPrec _ ('.' : s)			= [(Any, s)]+	readsPrec _ ('[' : '^' : noneOf)	= Control.Arrow.first NoneOf `map` reads (fst BracketExpression.delimiterTokens : noneOf) {-Reconstruct without negation, & recurse-}+	readsPrec _ anyOf@('[' : _)		= Control.Arrow.first AnyOf `map` reads anyOf+	readsPrec _ ('\\' : c : s)		= [(expand c, s)]+	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal++instance Consumer.Consumer (Meta m)	where+	consumptionProfile meta	= let+		hasSpecificDataRequirement :: Bool+		hasSpecificDataRequirement	= case meta of+			Any		-> False+			NoneOf []	-> False+			_		-> True+	 in ConsumptionProfile.MkConsumptionProfile {+		ConsumptionProfile.consumptionBounds		= (1, Just 1),+		ConsumptionProfile.hasSpecificRequirement	= hasSpecificDataRequirement,+		ConsumptionProfile.canConsumeAnything		= not hasSpecificDataRequirement+	 }++	starHeight _	= 0++instance NFData m => NFData (Meta m)	where+	rnf Any					= ()+	rnf (Literal m)				= rnf m+	rnf (AnyOf bracketExpression)		= rnf bracketExpression+	rnf (NoneOf bracketExpression)		= rnf bracketExpression+	rnf (Predicate showablePredicate)	= rnf showablePredicate++-- | 'True' if the specified datum matches.+isMatch :: Eq m+	=> m		-- ^ The input datum.+	-> Meta m	-- ^ The meta-entity against which the input datum is to be matched.+	-> Bool		-- ^ The result of the match-operation.+isMatch _ Any					= True+isMatch datum (Literal literal)			= datum == literal+isMatch datum (AnyOf bracketExpression)		= datum `BracketExpression.containsMatch` bracketExpression+isMatch datum (NoneOf bracketExpression)	= not $ datum `isMatch` AnyOf bracketExpression	--This implementation leverages future enhancements to 'AnyOf'.+isMatch datum (Predicate showablePredicate)	= ShowablePredicate.predicate showablePredicate datum++-- | The token used to precede a /Perl-style shortcut/, when in the 'String'-form.+shortcutToken :: Char+shortcutToken	= '\\'++-- | The token used to denote 'Any', when in the 'String'-form.+anyToken :: Char+anyToken	= '.'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [shortcutToken, anyToken] ++ BracketExpression.tokens+
+ src/RegExDot/RegEx.hs view
@@ -0,0 +1,1194 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* This implementation of extended /regex/es, generalises the familiar concept of pattern-matching of character-strings, to matching lists composed from an arbitrary data-type.+	The polymorphic data, from which the input data-list is composed, need only support @Eq@ & @Show@.++	* Because of the unknown stringified form of the underlying polymorphic data,+	the /regex/ must be described by a comparatively verbose bracketed & comma-separated list, rather than the traditional /String/ containing Meta-characters.+	Each element of this 'Concatenation' is a 'RepeatablePattern', which describes a permissible match against 'InputData'.++	* 'RepeatablePattern' can take one of two forms.+	In the simplest case, it matches just a single item of the underlying polymorphic type, perhaps literally, though looser specifications also exist:+	@.@ matches any input datum; @[x, y, z]@ matches any of @x@, @y@, or @z@; @[^x, y, z]@ matches anything but @x@, @y@, or @z@.+	To support POSIX /ERE/s, 'RepeatablePattern' can also be a list 'Alternatives', each of which is recursively defined as an 'ExtendedRegEx', to form a tree-structure.++	* Each 'Pattern', can optionally be /quantified/ by either a traditional /greedy/, or a /Perl/-style /non-greedy/, suffix, e.g.; @[*, +, ?, {n, m}, {n,}, {n}, *?, +?, ??, {n, m}?, {n,}?]@.++	* For convenience, common specifications can be canned & assigned a single 'Char' mnemonic, for subsequent reference.+	Since 'ExtendedRegEx' is polymorphic, the set of abbreviations appropriate in the context of the unspecified base-type,+	must be implemented externally through the 'Meta.ShortcutExpander' interface.+	This permits the use, when the type-parameter is 'Char', of /Perl-style shortcuts/ @[\\d\\D\\s\\S\\w\\W]@.++	* The algorithm, is the classic /back-tracking/ one, rather than either a /DFA/ or /NFA/.+	This permits construction of 'Result' via which one can discover the deep mapping of 'InputData' into 'ExtendedRegEx',+	& provides the flexibility to add the features now expected by modern /regex/-engines.+	Since the type-parameter is unknown, & may represent a large object, the exponential space-complexity of creating a /DFA/ may present additional problems.+	The exponential time-complexity of the /back-tracking/ algorithm is partially tamed by targeting obvious inefficiencies with specific optimisations.++	* Char-based regexen, traditionally overload the delimiters of a set of 'Alternatives' (parentheses), as a request for data-capture.+	Here, in contrast, all 'RepeatablePattern's capture data, & repeated sub-expressions capture a list of data,+	rather than arbitrarily recording just the last (<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>) item.++ [@REFERENCES@]++	* <http://en.wikipedia.org/wiki/Regular_expression>++	* <http://swtch.com/~rsc/regexp/regexp1.html>++	* <http://docstore.mik.ua/orelly/perl/prog/ch02_04.htm#PERL2-CH-2-SECT-4.1.2>++	* <http://www.macs.hw.ac.uk/~dsg/gph/papers/html/Strategies/strategies.html>++	* <http://hackage.haskell.org/packages/archive/regex-posix/latest/doc/html/Text-Regex-Posix.html>++	* <http://www.haskell.org/haskellwiki/Regular_expressions>++	* <http://www2.research.att.com/~gsf/testregex/re-interpretation.html>++ [@CAVEATS@]++	* Because of the definition of mutually recursive data-types, it is difficult to split this annoyingly large module, & preserve compatibility across compilers,+	but it may be possible to break this cyclic dependency, by defining an interface to which one of the data-types defined here conforms.++	* Doesn't implement /Back-references/, making the definition of the 'ExtendedRegEx' context-free.++	* There's no integration with the type-classes defined in "Text.Regex.Base.RegexLike", which assumes 'Char'-based 'InputData';+	though this could be added to a specialised instance.++	* When 'Alternatives' are defined, 'Result' becomes a tree-like structure.+	Unless the alternative is a singleton, the specific alternative selected in the solution is typically unknown, & therefore the /structure/ of the branch of this tree is also unknown.+	This lack of clarity is compounded when the 'Alternatives' are 'Repeatable.Repeatable', since a different one may be selected on each successive repetition.+	Consequently, the user can't navigate this portion of the structure in a statically defined manner, to acquire the captured data.+	Despite this, & in contrast to other /regex/-engines, access to the whole data-structure is available, since it doesn't seem advantage to hide it.+	The user can then either use 'extractDataFromMatch' for that element of 'Result', thus aggregating the data from sections of unknown structure, or 'show' it, as an aid to debugging.++ [@TODO@]++	* Test parallel-operation, on a 3 or more processor machine.+	If 'rnf' is less effective than 'rwhnf',+	then the 'NFData' context can be removed,+	reducing the requirements imposed on the type-parameter 'a'.++	* Try 'Data.List.Stream' (stream-fusion), a faster drop-in replacement for 'Data.List'; possibly integrated in GHC-6.12.++	* 'ExecutionOptions.bypassInputDataForLiberalConsumer' is too restrictive.+	More generally, we can test whether the set of different 'a' in 'InputData', is a subset of those common to all remaining terms in the 'ExtendedRegEx'.+	Using this rule, we can infer @"aaa ..." =~ MkExtendedRegEx [a,a+,a?,[ab]{2,3}]@, given compatible 'ConsumptionProfile.consumptionBounds'.++	* Nested repetitions, where nothing has been added to the expression, result in repeated trials of the same expression,+	e.g.; @"(x{i,}){j,}"@ results in the same expansion for @(i, j) in [(2, 3), (3, 2), (6, 1), (1, 6)]@.+	The resulting 'MatchList' may be different, but if the first such trial fails, so will all the remainder.++	* Should cope with empty sets of 'Alternatives' & zero repetitions, neither of which can ever match, but the wider pattern can, e.g. @(()|x{0}|y)@.++	* By removing 'RepeatablePattern' from 'Match', it can be isolated in a new module.+	This would result in a significant loss of discoverability.++	* Expand repeated 'Anchor.Bow' with @fewest - 1@ null matches followed by recursive 'findMatch'-call with @repetitions = 1@.+-}++module RegExDot.RegEx(+-- * Type-classes+	ShortcutExpander(..),+-- * Types+-- ** Type-synonyms+--	BasicRegEx,+	Concatenation,+	ExternalMatch,+	InputData,+	MatchedData,+	MatchList,+--	MetaDataList,+	RepeatablePattern,+--	Transformation,+-- ** Data-types+	Alternatives(..),+	Match,+	ExtendedRegEx(..),+	Pattern(..),+	Result,+-- * Constants+	alternativeExtendedRegExSeparatorToken,+--	anyDatum,+	captureGroupDelimiters,+	tokens,+-- * Functions+--	accumulateDistinctInputData,+--	accumulateDistinctMetaDataFrom,+	dock,+--	drift,+--	findMatch,+	captureGroup,+--	mkNullMatchFromConcatenation,+--	mkNullMatchFromExtendedRegEx,+--	mkNullMatchFromRepeatablePattern,+--	safeReciprocal,+--	shiftMatchedData,+--	shiftMatch,+	shiftMatchList,+	showsMaybeAnchor,+	simply,+	transformExtendedRegEx,+-- ** Accessors (Deconstructors)+	deconstructAlternatives,+--	getInputData,+-- ** Operators+	(+~),+	(=~),+	(/~),+	(.*),+	(.*?),+--	(/+)+-- ** Predicates+--	hasBowAnchor,+--	hasSternAnchor,+	isDefined,+	isCaptureGroup,+	isSingletonAlternatives,+--	isUnconsumableByAnyOf,+-- ** Query+--	getDistinctMetaDataFromAlternatives,+--	getDistinctMetaDataFromConcatenation,+--	getDistinctMetaDataFromExtendedRegEx,+--	getDistinctMetaDataFromPattern,+--	getDistinctMetaDataFromRepeatablePattern,+	externalMatchLength,+	extractDataFromMatch,+	extractDataFromMatch',+	extractDataFromMatchList+) where++import			Control.Applicative((<$>))+import			Control.Arrow((&&&))+import			RegExDot.Repeatable((^#->))+import qualified	Control.Arrow+import qualified	Data.Char+import qualified	Data.Foldable+import qualified	Data.List+import qualified	Data.Maybe+import qualified	Data.Ord+import qualified	RegExDot.Anchor			as Anchor+import qualified	RegExDot.Consumer		as Consumer+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile+import qualified	RegExDot.ExecutionOptions	as ExecutionOptions+import qualified	RegExDot.Meta			as Meta+import qualified	RegExDot.RegExOpts		as RegExOpts+import qualified	RegExDot.Repeatable		as Repeatable+import qualified	RegExDot.Tree			as Tree+import qualified	ToolShed.ListPlus		as ListPlus+import qualified	ToolShed.SelfValidate		as SelfValidate++#if MIN_VERSION_parallel(3,0,0)+import qualified	Control.Parallel.Strategies+#endif++#ifdef HAVE_DEEPSEQ+import			Control.DeepSeq(NFData, rnf)+#else+import			Control.Parallel.Strategies(NFData, rnf)+#endif++infix 4 +~, =~, /~	--Same as (==) & (/=).++-- | The type of a /regex/, in which there's no provision for either 'Alternatives' or 'Anchor.Anchor's.+type BasicRegEx m	= [Repeatable.Repeatable (Meta.Meta m)]++{- |+	* Defines the method required to expand a mnemonic into an 'ExtendedRegEx'.++	* CAVEAT: this interface must be declared locally, since it references 'ExtendedRegEx', & 'ExtendedRegEx' references it.+-}+class ShortcutExpander m	where+	expand	:: Char -> ExtendedRegEx m	-- ^ Expand a single-'Char' mnemonic into the corresponding 'ExtendedRegEx'.++{- |+	* Represents the /alternation/ feature of 'ExtendedRegEx's.++	* One could amalgamate this with 'Pattern', since it seems to exist merely as a peg to hang /instance/-declarations from.+-}+newtype Alternatives m	= MkAlternatives [ExtendedRegEx m]	deriving Eq++-- | Accessor, to expose the guts.+deconstructAlternatives :: Alternatives m -> [ExtendedRegEx m]+deconstructAlternatives (MkAlternatives extendedRegExList)	= extendedRegExList++-- | Similar to 'fmap', but operates on ['ExtendedRegEx'], rather than just @a@.+transformAlternatives ::+	([ExtendedRegEx m] -> [ExtendedRegEx m])	-- ^ The transformation function to apply to the list of data behind the constructor.+	-> Alternatives m+	-> Alternatives m+transformAlternatives f	= MkAlternatives . f . deconstructAlternatives++instance (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => Read (Alternatives m)	where+	readsPrec _ (' ' : s)	= reads s	--Consume white-space.+	readsPrec _ ('\t' : s)	= reads s	--Consume white-space.+	readsPrec _ s		= case reads s of+		[(extendedRegEx, s1)]	-> case dropWhile Data.Char.isSpace s1 of+			('|' : s2)	-> Control.Arrow.first (transformAlternatives (extendedRegEx :)) `map` reads s2+			_		-> [(MkAlternatives [extendedRegEx], s1)]+		_			-> []	--No parse.++instance Show m => Show (Alternatives m)	where+	showsPrec _	= foldl (.) (showString "") . Data.List.intersperse (showChar alternativeExtendedRegExSeparatorToken) . map shows . deconstructAlternatives	--Replace the default list-format, with 'egrep'-syntax.++instance Consumer.Consumer (Alternatives m)	where+	consumptionProfile	= Consumer.aggregateConsumptionProfilesFromAlternatives . deconstructAlternatives+	starHeight		= Consumer.starHeight . deconstructAlternatives	--Must evaluate all Alternatives to determine best.++instance SelfValidate.SelfValidator (Alternatives m)	where+	isValid	= SelfValidate.isValid . deconstructAlternatives++instance NFData m => NFData (Alternatives m)	where+	rnf	= rnf . deconstructAlternatives++-- | 'Alternatives' can be employed as a simple /capture-group/ as well as a switch, under which circumstances there's no choice amongst multiple 'Alternatives'.+isSingletonAlternatives :: Alternatives m -> Bool+isSingletonAlternatives	= (== 1) . length . deconstructAlternatives++-- | A set of 'Meta.Meta' which can be consumed.+type MetaDataList m	= [Meta.Meta m]++{- |+	* 'True' if there's no possibility, that the specified input datum, can be consumed by any of the specified 'MetaDataList'.++	* Because data may not be consumed, perhaps because of unsuitable 'ConsumptionBounds.ConsumptionBounds', the converse doesn't hold,+	ie @not isUnconsumableByAnyOf@ /doesn't/ imply that the input datum can be consumed, just that it hasn't been proven that it can't.+-}+isUnconsumableByAnyOf :: Eq m+	=> m			-- ^ The input datum.+	-> MetaDataList m	-- ^ The list of meta-data against any of which a match can occur.+	-> Bool+isUnconsumableByAnyOf i	= not . any (Meta.isMatch i)++-- | The set of distinct 'Meta.Meta', in the specified 'Alternatives'.+getDistinctMetaDataFromAlternatives :: Eq m => Alternatives m -> MetaDataList m+getDistinctMetaDataFromAlternatives	= foldr (Data.List.union . getDistinctMetaDataFromExtendedRegEx) [] . deconstructAlternatives++-- | Defines either a simple 'Meta.Meta', which can match exactly one datum, or a set of 'Alternatives', each of which is recursively defined above, as an 'ExtendedRegEx'.+data Pattern m	=+	Require (Meta.Meta m)		-- ^ Describes a requirement for a simple scalar datum of the polymorphic type.+	| CaptureGroup (Alternatives m)	-- ^ A sub-expression containing a selection of recursively defined alternatives, thus forming a tree-structure.+	deriving Eq++instance (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => Read (Pattern m)	where+	readsPrec _ (' ' : s)	= reads s	--Consume white-space.+	readsPrec _ ('\t' : s)	= reads s	--Consume white-space.+	readsPrec _ ('(' : s)	= case {-Alternatives.-} reads s of+		[(alternatives, s1)]	-> case dropWhile Data.Char.isSpace s1 of+			(')' : s2)	-> [(CaptureGroup alternatives, s2)]+			_		-> []	--No parse.+		_			-> []	--No parse.+	readsPrec _ s		= case reads s of+		[pair]	-> [Control.Arrow.first Require pair]+		_	-> []	--No parse.++instance Show m => Show (Pattern m)	where+	showsPrec _ (Require meta)		= shows meta+	showsPrec _ (CaptureGroup alternatives)	= showChar (fst captureGroupDelimiters) . shows alternatives . showChar (snd captureGroupDelimiters)++instance Consumer.Consumer (Pattern m)	where+	consumptionProfile (Require meta)		= Consumer.consumptionProfile meta+	consumptionProfile (CaptureGroup alternatives)	= Consumer.consumptionProfile alternatives++	starHeight (Require meta)		= Consumer.starHeight meta+	starHeight (CaptureGroup alternatives)	= Consumer.starHeight alternatives++instance SelfValidate.SelfValidator (Pattern m)	where+	isValid (Require meta)			= SelfValidate.isValid meta+	isValid (CaptureGroup alternatives)	= SelfValidate.isValid alternatives++instance NFData m => NFData (Pattern m)	where+	rnf (Require meta)		= rnf meta+	rnf (CaptureGroup alternatives)	= rnf alternatives++-- | Convenience-function to build a 'CaptureGroup' from a list of alternative 'ExtendedRegEx's.+captureGroup :: [ExtendedRegEx m] -> Pattern m+captureGroup	= CaptureGroup . MkAlternatives++-- | 'True' if the 'Pattern' was constructed via 'CaptureGroup'.+isCaptureGroup :: Pattern m -> Bool+isCaptureGroup (CaptureGroup _)	= True+isCaptureGroup _		= False++-- | The set of distinct 'Meta.Meta', in the specified 'Pattern'.+getDistinctMetaDataFromPattern :: Eq m => Pattern m -> MetaDataList m+getDistinctMetaDataFromPattern pattern	= case pattern of+	Require meta			-> [meta]+	CaptureGroup alternatives	-> getDistinctMetaDataFromAlternatives alternatives++-- | Constant pattern, representing a lax 'Require'ment.+anyDatum :: Pattern a+anyDatum	= Require Meta.Any++-- | Make 'Pattern's, 'Repeatable.Repeatable'.+type RepeatablePattern m	= Repeatable.Repeatable (Pattern m)++-- | Construct a null 'Match' by assuming that the specified 'RepeatablePattern' consumes zero 'InputData'; of which the caller should ensure it's capable.+mkNullMatchFromRepeatablePattern ::+	ConsumptionBounds.DataLength	-- ^ The offset into the list of input data at which the zero-length match occurred.+	-> RepeatablePattern m		-- ^ The pattern that the input data matched.+	-> Match m			-- ^ The resulting match-structure.+mkNullMatchFromRepeatablePattern offset r	= case Repeatable.base r of+	Require _	-> Tree.Leaf (r, offset, [])+	CaptureGroup _	-> Tree.Node $ Repeatable.getFewest r `replicate` []++-- | The set of distinct 'Meta.Meta', in the specified 'RepeatablePattern'.+getDistinctMetaDataFromRepeatablePattern :: Eq m => RepeatablePattern m -> MetaDataList m+getDistinctMetaDataFromRepeatablePattern	= getDistinctMetaDataFromPattern . Repeatable.base++{- |+	* Represents a black hole, which will greedily consume all data.++	* CAVEAT: nullary, ie a constant.+-}+(.*) :: RepeatablePattern m+(.*)	= Repeatable.zeroOrMore anyDatum++-- | A /non-greedy/ version of '.*'.+(.*?) :: RepeatablePattern m+(.*?)	= Repeatable.zeroOrMore' anyDatum++-- | Convenience-function, to build a 'RepeatablePattern' from an unrepeated instance of the specified 'Meta.Meta'-datum.+simply :: Meta.Meta m -> RepeatablePattern m+simply	= Repeatable.one . Require++-- | Represents the /concatenation/ aspect of 'ExtendedRegEx's.+type Concatenation m	= [RepeatablePattern m]++-- | Construct a null 'Match' by assuming that the specified 'Concatenation' consumes zero 'InputData'.+mkNullMatchFromConcatenation ::+	ConsumptionBounds.DataLength	-- ^ The offset into the list of input data at which the zero-length match occurred.+	-> Concatenation m		-- ^ The list of repeatable patterns that the input data matched.+	-> MatchList m			-- ^ The resulting match-structure.+mkNullMatchFromConcatenation offset	= map $ mkNullMatchFromRepeatablePattern offset++-- | The set of distinct 'Meta.Meta', in the specified 'Concatenation'.+getDistinctMetaDataFromConcatenation :: Eq m => Concatenation m -> MetaDataList m+getDistinctMetaDataFromConcatenation	= foldr (Data.List.union . getDistinctMetaDataFromRepeatablePattern) []++-- | The accumulating set of 'MetaDataList', resulting from each successive 'RepeatablePattern' in the specified 'Concatenation'.+accumulateDistinctMetaDataFrom :: Eq m => MetaDataList m -> Concatenation m -> [MetaDataList m]+accumulateDistinctMetaDataFrom	= scanr (Data.List.union . getDistinctMetaDataFromRepeatablePattern)++-- | Constructs an 'ExtendedRegEx', by surrounding a 'Concatenation' with optional 'Anchor.Anchor's.+data ExtendedRegEx m	= MkExtendedRegEx {+	bowAnchor	:: Maybe Anchor.Anchor,	-- ^ An option to anchor the /regex/ to the start of the 'InputData'.+	concatenation	:: Concatenation m,	-- ^ The sequence of 'RepeatablePattern's defining the 'Require'ments that the 'InputData' must meet.+	sternAnchor	:: Maybe Anchor.Anchor	-- ^ An option to anchor the /regex/ to the end of the 'InputData'.+} deriving Eq++-- | The type of a function which transforms an 'ExtendedRegEx'.+type Transformation m	= ExtendedRegEx m -> ExtendedRegEx m++-- | Similar to 'fmap', but operates on 'Concatenation', rather than just @a@.+transformExtendedRegEx ::+	(Concatenation m -> Concatenation m)	-- ^ The function used to transform the data behind the constructor.+	-> Transformation m+transformExtendedRegEx f extendedRegEx	= extendedRegEx { concatenation	= f $ concatenation extendedRegEx }++instance (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => Read (ExtendedRegEx m)	where+	readsPrec _ []			= []+	readsPrec _ (' ' : s)		= reads s	--Consume white-space.+	readsPrec _ ('\t' : s)		= reads s	--Consume white-space.+	readsPrec _ ('\\' : c : s)	= [(expand c, s)]+	readsPrec _ s			= (SelfValidate.isValid . fst) `filter` [+		(+			MkExtendedRegEx {+				bowAnchor	= maybeBowAnchor,+				concatenation	= concatenation',+				sternAnchor	= maybeSternAnchor+			},+			remainder+		) |+			(maybeBowAnchor, s1)		<- readsMaybeAnchor s,+			(concatenation', s2)		<- reads s1,+			(maybeSternAnchor, remainder)	<- readsMaybeAnchor s2++	 ] where+		readsMaybeAnchor :: ReadS (Maybe Anchor.Anchor)+		readsMaybeAnchor s'	= return {-to List-monad-} $ case reads s' of+			[pair]	-> Control.Arrow.first Just pair+			_	-> (Nothing, s')++-- | Shows either the specified 'Anchor.Anchor', or a null string where 'Nothing' is specified.+showsMaybeAnchor :: Maybe Anchor.Anchor -> String -> String+showsMaybeAnchor maybeAnchor+	| Data.Maybe.isJust maybeAnchor	= shows $ Data.Maybe.fromJust maybeAnchor+	| otherwise			= id++instance Show m => Show (ExtendedRegEx m)	where+	showsPrec _ MkExtendedRegEx {+		bowAnchor	= maybeBowAnchor,	--CAVEAT: this could be 'Nothing' or perversely an 'Anchor.Stern'.+		concatenation	= concatenation',+		sternAnchor	= maybeSternAnchor	--CAVEAT: this could be 'Nothing' or perversely an 'Anchor.Bow'.+	} = showsMaybeAnchor maybeBowAnchor . shows concatenation' . showsMaybeAnchor maybeSternAnchor++instance Consumer.Consumer (ExtendedRegEx m)	where+	consumptionProfile	= Consumer.aggregateConsumptionProfilesFromConcatenation . concatenation+	starHeight		= Consumer.starHeight . concatenation++instance SelfValidate.SelfValidator (ExtendedRegEx m)	where+	isValid	= SelfValidate.isValid {-delegate-} . concatenation	--NB: null 'Concatenation' is OK.++instance NFData m => NFData (ExtendedRegEx m)	where+	rnf MkExtendedRegEx {+		bowAnchor	= maybeBowAnchor,+		concatenation	= concatenation',+		sternAnchor	= maybeSternAnchor+	} = rnf (maybeBowAnchor, concatenation', maybeSternAnchor)++-- | Drop 'Anchor.Anchor's at both bow & stern of the specified 'ExtendedRegEx'.+dock :: Transformation m+dock e	= e {+	bowAnchor	= Just Anchor.Bow,+	sternAnchor	= Just Anchor.Stern+}++-- | Amend the 'Concatenation' in the specified 'ExtendedRegEx', by prepending '.*' where there's a missing top-level 'Anchor.Bow' & appending '.*?' where there's a missing top-level 'Anchor.Stern'.+drift :: Transformation m+drift extendedRegEx	= transformExtendedRegEx (+	(+		if hasBowAnchor extendedRegEx then id else {-no anchor-} ((.*?) :)+	) . (+		if hasSternAnchor extendedRegEx then id else {-no anchor-} (++ [(.*)])+	)+ ) extendedRegEx++-- | 'True' if the specified 'ExtendedRegEx' has an initial 'Anchor.Bow'. CAVEAT: though typically the alternative is no 'Anchor.Anchor', there may perversely be an initial 'Anchor.Stern', but neither qualify.+hasBowAnchor :: ExtendedRegEx m -> Bool+hasBowAnchor	= (== Just Anchor.Bow) . bowAnchor++-- | 'True' if the specified 'ExtendedRegEx' has a terminal 'Anchor.Stern'. CAVEAT: though typically the alternative is no 'Anchor.Anchor', there may perversely be a terminal 'Anchor.Bow', but neither qualify.+hasSternAnchor :: ExtendedRegEx m -> Bool+hasSternAnchor	= (== Just Anchor.Stern) . sternAnchor++-- | 'True' if there's at least one 'RepeatablePattern' in the 'Concatenation', ie that it's non-null.+isDefined :: ExtendedRegEx m -> Bool+isDefined	= not . null . concatenation++-- | Construct a null 'Match' by assuming that the specified 'ExtendedRegEx' consumes zero 'InputData'.+mkNullMatchFromExtendedRegEx :: ConsumptionBounds.DataLength -> ExtendedRegEx m -> MatchList m+mkNullMatchFromExtendedRegEx offset	= mkNullMatchFromConcatenation offset . concatenation++-- | The set of distinct (ie unique) 'Meta.Meta', in the specified 'ExtendedRegEx'.+getDistinctMetaDataFromExtendedRegEx :: Eq m => ExtendedRegEx m -> MetaDataList m+getDistinctMetaDataFromExtendedRegEx	= getDistinctMetaDataFromConcatenation . concatenation++{- |+	* The input-data is just a list.++	* Whilst typically this list is also a 'String', & could therefore be more efficiently implemented using "Data.ByteString",+	we can't assume that the polymorphic base-type is always 'Char'.+-}+type InputData m = [m]++-- | The accumulating sets of distinct input data.+accumulateDistinctInputData :: Eq m => InputData m -> [InputData m]+accumulateDistinctInputData	= scanr (Data.List.union . return) []++-- | Tag the 'InputData' with the 'RepeatablePattern' it matched (which unfortunately confines the definition to this (bloated) module), & the offset from the start of the data;+type MatchedData m	= (RepeatablePattern m, ConsumptionBounds.DataLength, InputData m)++-- | Shifts the offset of the specified 'MatchedData'.+shiftMatchedData ::+	ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a match occurred.+	-> MatchedData m		-- ^ The match-structure whose offset is to be shifted.+	-> MatchedData m+shiftMatchedData i (r, offset, d)	= (r, offset + i, d)++-- | Accessor.+getInputData :: MatchedData m -> InputData m+getInputData (_, _, inputData)	= inputData++-- | Describes the manner in which a 'RepeatablePattern' successfully consumed 'InputData'.+type Match m	= Tree.Tree (MatchedData m)++-- | Shifts the offsets of all the 'MatchedData' contained in the specified 'Match'.+shiftMatch ::+	ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a match occurred.+	-> Match m			-- ^ The match-structure whose offset is to be shifted.+	-> Match m+shiftMatch i	= (shiftMatchedData i <$>)++-- | Extract & concatenate, the 'InputData' from a 'Match'.+extractDataFromMatch :: Match m -> InputData m+extractDataFromMatch	= Data.Foldable.foldMap getInputData	--Uses the List-monoid's associative binary operator (++), to concatenate the values returned by the specified function.++-- | Extract & concatenate, the 'InputData' from a 'Match'; null if it didn't match any.+extractDataFromMatch' :: Maybe (Match m) -> InputData m+extractDataFromMatch' (Just match)	= extractDataFromMatch match+extractDataFromMatch' _			= []++-- | Describes the manner in which a 'Concatenation' successfully consumed 'InputData'.+type MatchList m	= [Match m]++-- | Shifts the offsets of all the 'MatchedData' contained in the specified 'MatchList'.+shiftMatchList ::+	ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a each listed match occurred.+	-> MatchList m			-- ^ The list of match-structures, each of whose offsets are to be shifted.+	-> MatchList m+shiftMatchList i	= map (shiftMatch i)++-- | Extract & concatenate, the 'InputData', from the 'MatchList'.+extractDataFromMatchList :: MatchList m -> InputData m+extractDataFromMatchList	= concatMap extractDataFromMatch	--CAVEAT: too fine-grain for effective data-parallelism.++-- | At the top-level of an 'ExtendedRegEx', the lack of an 'Anchor.Anchor' allows the 'ExtendedRegEx' to drift away from the corresponding end of the input-data; this data-gap is captured here.+type ExternalMatch m	= Maybe (Match m)++-- | Captures the list of input-data consumed by the 'Concatenation', bracketed by any data-prefix or data-suffix.+type Result m	= (ExternalMatch m, Maybe (MatchList m), ExternalMatch m)++-- | Returns the length of data consumed by the specified 'ExternalMatch'.+externalMatchLength :: ExternalMatch m -> ConsumptionBounds.DataLength+externalMatchLength	= length . extractDataFromMatch'++{- |+	* Similar to '+~', but exposes 'ExecutionOptions.ExecutionOptions', permitting greater control; & enough rope to hang yourself.++	* The parameter-order facilitates partial application, to acquire a matcher-function for a specific 'ExtendedRegEx', which can be applied repeatedly to different 'InputData'.++	* Performs some one-off @O(n)@ time-complexity preparation, then delegates the grunt-work to a private recursive function.++	* One could waste a lot of time trying to consume 'InputData' from the head of a high-'Consumer.starHeight' 'ExtendedRegEx',+	only to find that there's a low-'Consumer.starHeight' tail that can't ever match the 'InputData'; checks this first before delegating.+-}+findMatch :: (Eq m, NFData m, Show m)+	=> RegExOpts.RegExOpts (ExtendedRegEx m)	-- ^ The match-options parameterised by the regex against which to match the input data.+	-> InputData m					-- ^ The input data within which to locate a match.+	-> Maybe (MatchList m)+findMatch regExOpts@RegExOpts.MkRegExOpts {+	RegExOpts.executionOptions	= executionOptions,+	RegExOpts.regEx			= extendedRegEx+} originalInputData+	| let+		extractLowStarHeightTail :: ExtendedRegEx m -> Concatenation m+		extractLowStarHeightTail	= {-#SCC "extractLowStarHeightTail" #-} fromConcatenation . reverse . concatenation	where+			fromConcatenation :: Concatenation m -> Concatenation m+			fromConcatenation []					= []+			fromConcatenation (repeatablePattern : concatenation')+				| Consumer.starHeight repeatablePattern == 0	= repeatablePattern : fromConcatenation {-recurse-} concatenation'+				| Repeatable.getFewest repeatablePattern > 0	= case Repeatable.base repeatablePattern of+					CaptureGroup alternatives+						| isSingletonAlternatives alternatives	-> extractLowStarHeightTail {-recurse-} . head $ deconstructAlternatives alternatives+						| otherwise				-> {-non-singleton-} []+					_				-> []	--Zero 'Consumer.StarHeight' would have been detected by 'fromConcatenation'.+				| otherwise					= []++--		lowStarHeightTail :: Concatenation a+		lowStarHeightTail	= extractLowStarHeightTail extendedRegEx+	in and [+		ExecutionOptions.checkExistenceOfInelasticTail executionOptions,+		not $ null lowStarHeightTail,	--Prevent infinite recursion.+		reverse originalInputData /~ RegExOpts.mkRegEx MkExtendedRegEx {+			bowAnchor	= Just Anchor.Bow,+			concatenation	= lowStarHeightTail,+			sternAnchor	= Nothing+		} --Check for mismatch with the corresponding tail of 'InputData'.+	]		= {-Unsafe.printShow "Tail-mismatch:\t"-} Nothing+	| otherwise	= findMatchSlave originalConcatenation (+		Consumer.accumulateConsumptionProfiles originalConcatenation+	) (+		accumulateDistinctMetaDataFrom [] originalConcatenation+	) originalInputData originalInputDataLength (+		accumulateDistinctInputData originalInputData+	)+	where+--		originalConcatenation :: Concatenation a+		originalConcatenation	= concatenation extendedRegEx++		originalInputDataLength :: ConsumptionBounds.DataLength+		originalInputDataLength	= length originalInputData+{-+ This beast takes a lot of parameters.+ Whilst the only strictly necessary parameters are the lists 'Concatenation' & 'InputData', many ancillary lists derived from them, are also passed by parameter.+ These are proportionally reduced on recursion, to avoid the requirement to regenerate them each time they're required.+-}+		findMatchSlave :: (+			Eq m,+			NFData m,+			Show m+		 )	=> Concatenation m					--The list of 'RepeatablePattern's from which the regex is constructed.+			-> ConsumptionProfile.AccumulatedConsumptionProfiles	--The capacity-bounds for the consumption of 'InputData', that the 'Concatenation' extending right from any given 'RepeatablePattern', can consume.+			-> [MetaDataList m]					--The set of distinct 'Meta.Meta'-data, in the 'Concatenation extending right from any given 'RepeatablePattern'.+			-> InputData m						--The input data, which will be fed to the 'Concatenation'.+			-> ConsumptionBounds.DataLength				--The length of the previously specified 'InputData'.+			-> [InputData m]					--The set of distinct input data, extending right from any given point.+			-> Maybe (MatchList m)+		findMatchSlave [] _ _ [] _ _	= {-Unsafe.printShow "Dead heat:\t" $-} Just []	--Simultaneous exhaustion of the 'Concatenation' of 'RepeatablePattern's & the 'InputData' => success.+		findMatchSlave [] _ _ _ _ _	= {-Unsafe.printShow "Excess data:\t"-} Nothing+		findMatchSlave concatenation'@(repeatablePatternHead : concatenationTail) (accumulatedConsumptionProfileHead : accumulatedConsumptionProfilesTail) (distinctMetaDataHead : distinctMetaDataTail) inputData inputDataLength distinctInputData@(distinctInputDataHead : _)+			| not $ inputDataLength `ConsumptionProfile.withinConsumptionBounds` accumulatedConsumptionProfileHead	= {-Unsafe.printShow "outside ConsumptionBounds:\t"-} Nothing+			| null inputData	= Just $ mkNullMatchFromConcatenation inputDataOffset concatenation'	--Build a 'Match', of null 'InputData'.+			| and [+				ExecutionOptions.bypassInputDataForLiberalConsumer executionOptions,			--We may be able to establish success, without evaluating 'inputData' any further than required to determine its length.+				not $ ExecutionOptions.requireMatchList executionOptions,				--Otherwise the precise mapping of the 'inputData' to 'RepeatablePattern's must be determined.+				not $ ConsumptionProfile.hasSpecificRequirement accumulatedConsumptionProfileHead	--Otherwise a match for the specific 'Meta'-data must be found.+			] = Just undefined {-shouldn't be evaluated according to 'ExecutionOptions.requireMatchList'-}+			| and [+				ExecutionOptions.checkForUnconsumableData executionOptions,+				not $ ConsumptionProfile.canConsumeAnything accumulatedConsumptionProfileHead,	--Otherwise the subsequent test will always fail.+				(`isUnconsumableByAnyOf` distinctMetaDataHead) `any` distinctInputDataHead	--Occasionally failure is both inevitable & obvious.+			] = {-Unsafe.printShow ("Unconsumable data=" ++ show inputData ++ ":\t")-} Nothing+			| otherwise	= {-#SCC "findMatchSlave" #-} let+				tailConsumptionProfile :: ConsumptionProfile.ConsumptionProfile+				tailConsumptionProfile@(+					ConsumptionProfile.MkConsumptionProfile {+						ConsumptionProfile.consumptionBounds	= (minConsumptionConcatenationTail, maybeMaxConsumptionConcatenationTail)+					}+				 ) = {-Unsafe.printShow "tailConsumptionProfile:\t" $-} head accumulatedConsumptionProfilesTail	--Extract the aggregate consumption-profile, of the tail of the 'Concatenation'.++				maxDataAvailable :: ConsumptionBounds.DataLength+				maxDataAvailable	= inputDataLength - minConsumptionConcatenationTail	--The maximum data available to match 'repeatablePatternHead'.+			in if maxDataAvailable < 0+				then {-Unsafe.printShow ("Excess Concatenation=" ++ show concatenation' ++ ":\t")-} Nothing+				else {-inputData is sufficient for concatenationTail-} let+					Repeatable.MkRepeatable {+						Repeatable.base			= base,+						Repeatable.repetitionBounds	= (fewest, most),+						Repeatable.isGreedy		= isGreedy+					} = {-Unsafe.printShow "repeatablePatternHead:\t"-} repeatablePatternHead	--Completely deconstruct the 'Repeatable' at the head of the 'Concatenation'.+{-+ Find the maximum sequence of inputData, preceding the first of the mandatory requirements of 'concatenationTail'.+ This enables one to more tightly constrain the maximum number of repetitions of a 'RepeatablePattern',+ which consequently reduces the subsequent requirement to back-track.+-}+--					maximumDataBeforePegs :: Maybe (InputData m)+					maximumDataBeforePegs	= {-#SCC "maximumDataBeforePegs" #-} reverse (+						{-Unsafe.printShow "minimumRequirement:\t" $-} extractMinimumRequirementFromConcatenation concatenationTail+					 ) `maximumDataAfterPegs` reverse inputData	where+{-+ Extract those 'RepeatablePattern's which must match at least once.+ Singleton 'Alternatives' are flattened by replication, & non-singleton 'Alternatives' removed, allowing a reduction of the list's base-type to just 'Repeatable.Repeatable' 'Meta.Meta'.+ The resulting 'BasicRegEx', with /Kleene Star/s weeded-out & 'Alternatives' pruned, can be matched in O(n)-time, to verify minimal requirements.+-}+						extractMinimumRequirementFromConcatenation :: Concatenation m -> BasicRegEx m+						extractMinimumRequirementFromConcatenation	= concatMap (+							\repeatablePattern -> case Repeatable.base repeatablePattern of+								Require datum	-> return {-to List-monad-} repeatablePattern { Repeatable.base = datum }+								CaptureGroup a+									| isSingletonAlternatives a	-> concat . replicate (Repeatable.getFewest repeatablePattern) . extractMinimumRequirementFromConcatenation {-recurse-} . concatenation . head $ deconstructAlternatives a+									| otherwise			-> []+						 ) . filter ((> 0) . Repeatable.getFewest)++						maximumDataAfterPegs :: Eq m => BasicRegEx m -> InputData m -> Maybe (InputData m)+						maximumDataAfterPegs pegList@(+							Repeatable.MkRepeatable {+								Repeatable.base			= metaPeg,+								Repeatable.repetitionBounds	= (fewestPeg, _)+							} : pegListTail+						 ) inputData'+							| null remainingInputData && length candidateMatchedInputData < fewestPeg		= Nothing							--Insufficient data to ever match.+							| fewestPeg == 1 || (`Meta.isMatch` metaPeg) `all` tail candidateMatchedInputData	= pegListTail `maximumDataAfterPegs` remainingInputData		--Success => recurse.+							| otherwise										= pegList `maximumDataAfterPegs` tail candidateInputData	--Failed candidate => recurse.+							where+--								candidateInputData, candidateMatchedInputData, remainingInputData :: InputData a+								candidateInputData				= dropWhile (not . (`Meta.isMatch` metaPeg)) inputData'+								(candidateMatchedInputData, remainingInputData)	= fewestPeg `splitAt` candidateInputData+						maximumDataAfterPegs [] inputData'	= Just inputData'	--All the pegs have been matched.+{-+ Each 'repeatablePatternHead' can either be a 'Require' or a 'CaptureGroup'.+ The former case needs further reduction & we can proceed to construct a 'Tree.Leaf' from any matching 'inputData'.+ We attempt to reduce the latter case to an instance of the former, by expanding all combinations of repetitions of various 'Alternatives', constructing a new 'ExtendedRegEx' from each, & recursing.+-}+				in case base of+					Require meta+						| null maybeMatchList	-> Nothing+						| otherwise		-> head {-most suitable-} maybeMatchList+						where+{-+ Attempt to narrow the repetition-range, from the potentially unbounded specification, to that which is possible according to,+ the 'inputDataLength', & the specified consumption-bounds of the remainder of the /regex/.+ Once the repetition-range has been narrowed, iterate from the appropriate end of the range (depending on 'isGreedy'), extract the corresponding length from inputData, & check for a match.+-}+--							maybeMatchList :: [Maybe (MatchList m)]+							maybeMatchList	= {-#SCC "maybeMatchList" #-} dropWhile Data.Maybe.isNothing . {-Unsafe.printShow "maybeMatchList:\t" .-} map (+								\(consumedInputData, unconsumedInputData)	-> let+									consumedInputDataLength :: ConsumptionBounds.DataLength+									consumedInputDataLength	= {-Unsafe.printShow "consumedInputDataLength:\t" $-} length consumedInputData+								in (+									Tree.Leaf (repeatablePatternHead, inputDataOffset, {-Unsafe.printShow "consumedInputData:\t"-} consumedInputData) :+								) <$> {-apply to Maybe Functor-} findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail unconsumedInputData (+									inputDataLength - consumedInputDataLength+								) (+									consumedInputDataLength `drop` distinctInputData+								) --Recurse, to check whether the 'unconsumedInputData' tail also matches.+							 ) $ let+								fewestData, mostData, mostData' :: Repeatable.Repetitions+								fewestData	= {-#SCC "fewestData" #-} {-Unsafe.printShow "fewestData:\t" $-} case maybeMaxConsumptionConcatenationTail of+									Just m	-> (inputDataLength - m) {- :: ConsumptionBounds.DataLength-} `max` fewest {- :: Repeatable.Repetitions-}	--CAVEAT: conceptually different types.+									_	-> fewest++--								mostData	= length . takeWhile (~= meta) $ (`take` inputData) maxData	--As slow as it is concise.+								mostData	= {-#SCC "mostData" #-} {-Unsafe.printShow "mostData:\t" $-} maxData - measureUnmatchableTail inputData maxData	where+									maxData :: ConsumptionBounds.DataLength+									maxData	= case most of+										Just cap	-> cap {-:: Repeatable.Repetitions-} `min` maxDataAvailable {-:: ConsumptionBounds.DataLength-}	--CAVEAT: conceptually different types.+										_		-> maxDataAvailable++--									measureUnmatchableTail :: InputData m -> ConsumptionBounds.DataLength -> ConsumptionBounds.DataLength+									measureUnmatchableTail _ 0	= 0						--Have matched all the data.+									measureUnmatchableTail (a : as) unmatched+										| a `Meta.isMatch` meta	= measureUnmatchableTail as $ unmatched - 1	--Recurse.+										| otherwise		= unmatched					--Return the unmatchable tail-length.+									measureUnmatchableTail [] _	= error "RegExDot.RegEx.findMatch.findMatchSlave.maybeMatchList.mostData.measureUnmatchableTail:\tdata unexpectedly exhausted."+{-+ 'most' has been extracted from 'repeatablePatternHead',+ & reduced according to the length of 'inputData' which actually matches, to form 'mostData',+ but if it still exceeds 'fewestData', the resulting repetition-range may trigger backtracking.+ So, attempt to further reduce this ill-defined repetition-range, according to the competing requirements of 'concatenationTail'.+-}+								mostData'+									| and [+										ExecutionOptions.moderateGreed executionOptions,+										isGreedy,		--Otherwise, since the search proceeds from 'fewest' to 'most', the optimal solution is located before backtracking.+										mostData > fewestData	--Otherwise, there's no unbridled greed to moderate.+									] = case maximumDataBeforePegs of+										Just maximumDataBeforePegs'	-> {-Unsafe.printShow ("Reducing mostData=" ++ show mostData ++ " ->\t") $-} mostData `min` length maximumDataBeforePegs'	--Cap upper bound.+										_				-> {-Unsafe.printShow "Peg match-failure:\t" $-} negate 1	--Guaranteed to be < 'fewestData'.+									| otherwise {-no requirement for this optimisation-}	= mostData+							 in (+								1 + {-fence-post-} mostData' - fewestData	--CAVEAT: possibly <= 0.+							 ) `take` (+								if isGreedy+									then ListPlus.splitsLeftFrom mostData'+									else ListPlus.splitsRightFrom fewestData+							 ) inputData++					CaptureGroup alternatives+						| and [+							not isGreedy,+							fewest <= 0,	--Zero repetitions permissible.+							Data.Maybe.isJust tailMatch+						] {-Unsafe.printShow "Trying zero alternatives:\t"-}	-> zeroRepetitions	--Zero repetitions is the optimal solution, rendering the choice of Alternative irrelevant.+						| and [+							ExecutionOptions.catchIncompatibleAnchors executionOptions,+							fewest > 1,			--Multiple repetitions.+							minConsumptionAlternatives > 0,	--InputData required.+							($ extendedRegExAlternatives) `any` [(hasBowAnchor `all`), (hasSternAnchor `all`)]+						]							-> Nothing	--'extendedRegExFromAlternative' requires 'InputData', therefore 'Anchor.Bow' can only pass on the 1st repetition & 'Anchor.Stern' can only pass on the last.+						| otherwise						-> let+{-+ We're about to try all repetitions of one Alternative before progressing to the next.+ One could alternatively transpose this search-pattern, & investigate all 'Alternatives' permissible at n repetitions, before trying (n + 1); I've not pursued this option.+ Either way, if the user requires data-capture, then for POSIX-compliance we must perform an exhaustive search of the O(Alternatives ^ Repetitions) permutations for the optimal solution; though the search may be narrowed as we proceed.+-}+--							matchPairList :: [(Match m, MatchList m)]+							matchPairList	= {-#SCC "matchPairList" #-} Data.Maybe.catMaybes . {-Unsafe.printShow "matchPairList:\t" .-}+#if MIN_VERSION_parallel(3,0,0)+							 concat . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rseq	--Particularly effective when 'not ExecutionOptions.requireMatchList'.+#else+							 concatMap+#endif+							 (+--								:: ExtendedRegEx m -> [Maybe (Match m, MatchList m)]+								\extendedRegExFromAlternative@MkExtendedRegEx {+									concatenation	= concatenationFromAlternative+								} -> let+									components :: Int+									components	= length concatenationFromAlternative++									consumptionProfileConcatenationFromAlternative :: [ConsumptionProfile.ConsumptionProfile]+									consumptionProfileConcatenationFromAlternative	= map Consumer.consumptionProfile concatenationFromAlternative++									minConsumptionConcatenationFromAlternative	:: ConsumptionBounds.DataLength+									maybeMaxConsumptionConcatenationFromAlternative	:: Maybe ConsumptionBounds.DataLength+									(minConsumptionConcatenationFromAlternative, maybeMaxConsumptionConcatenationFromAlternative)	= ConsumptionProfile.consumptionBounds $ ConsumptionProfile.aggregateFromConcatenation consumptionProfileConcatenationFromAlternative+								in (+									if ExecutionOptions.abortTrialRepetitionsOnZeroConsumption executionOptions && Data.Maybe.isNothing most {-constrained only by 'inputDataLength'-}+										then {-#SCC "abortTrialRepetitionsOnZeroConsumption" #-} ListPlus.takeUntil $ \maybeMatchPair -> case maybeMatchPair of+											Just (match, _)	-> ($ Tree.pop match) `all` [+												(>= fewest) . length,			--Must achieve 'Repeatable.getFewest', regardless of data-consumption.+												null . extractDataFromMatchList . last	--If the n-th repetition consumed nothing, so will the (n + 1)-th.+											 ]+											_		-> False			--Failure with n repetitions, doesn't preclude success with n + 1.+										else {-optimisation not required-} id+								) . (+--									:: Maybe (Match m, MatchList m) -> Maybe (Match m, MatchList m)+									\maybeMatchPairList	-> if ExecutionOptions.abortTrialRepetitionsOnInherentFailure executionOptions+{-+ We're iterating through the identified range of possible repetitions, of one possible 'concatenationFromAlternative'.+ If failure can be proven, for an arbitrary number of repetitions, then we can bypass the futile remainder.+ If the first trial succeeded, then clearly there's no such inherent problem with 'concatenationFromAlternative', & it's necessary to evaluate all subsequent trial-repetitions.+ Otherwise, assuming there're subsequent trials to be bypassed, attempt to prove that failure is inevitable, by launching a pilot-study,+ using a single repetition of 'concatenationFromAlternative' followed by a replacement infallible 'concatenationTail'.+-}+										then {-#SCC "abortTrialRepetitionsOnInherentFailure" #-} case maybeMatchPairList of+											(Nothing {-failed attempt-} : _ {-subsequent attempt worth bypassing-} : _)	-> if (+												minConsumptionConcatenationFromAlternative == 0	--Can delegate consumption of unconsumable data to the infallible tail => unprovable culpability.+											 ) || inputData =~ RegExOpts.mkRegEx MkExtendedRegEx {+												bowAnchor	= Just Anchor.Bow,+												concatenation	= concatenationFromAlternative ++ [anyDatum ^#-> minConsumptionConcatenationTail],	--Infallible tail.+												sternAnchor	= Just Anchor.Stern+											 }+												then maybeMatchPairList+												else {-Unsafe.printShow ("Bypassing " ++ show (length maybeMatchPairList) ++ " repetitions; ")-} []	--Abandon further repetitions of 'Alternatives'.+											_ {-Alternative is OK, or there's no subsequent attempt to bypass-}		-> maybeMatchPairList+										else {-optimisation not required-} maybeMatchPairList+								) . map (+--									:: Repeatable.Repetitions -> Maybe (Match m, MatchList m)+									\repetitions	-> if hasSternAnchor extendedRegExFromAlternative+										then if (repetitions == 1 || minConsumptionConcatenationFromAlternative == 0) && minConsumptionConcatenationTail == 0+											then (+												shiftMatch inputDataOffset . Tree.Node . (+													: replicate (+														repetitions - 1	--Typically degenerates to zero, since anchored sub-expressions aren't normally repeatable.+													) (+														mkNullMatchFromExtendedRegEx 0 extendedRegExFromAlternative	--Other Alternatives may be available & suitable.+													) --Repeated null match may, lacking other suitable Alternatives, correspond to this stern-anchored ExtendedRegEx, so data must be consumed before them.+												) &&& const (mkNullMatchFromConcatenation inputDataOffset concatenationTail)+											) <$> findMatch regExOpts { RegExOpts.regEx = extendedRegExFromAlternative } inputData	--Recurse, either consuming all inputData or failing.+											else Nothing+										else {-no Anchor.Stern-} let+{-+ Expand the 'Repeatable CaptureGroup', so that after recursion it is ultimately reduced in complexity, to just a 'Concatenation' of 'Repeatable Require', & can be handled above as a POSIX BRE.++ There're two special cases; one repetition of n 'Alternatives' & n repetitions of one Alternative.+ If there's only one repetition, we can reduce the implementation-logic substantially, to the point where the implementation's independent of whether there's a choice of 'Alternatives'.+ If there only one Alternative, then the entire repetition-sequence can be immediately expanded, vastly reducing the required number of recursions,+ otherwise we can only expand the first repetition, leaving the option of matching a different Alternative on subsequent repetitions.++ In all cases, we must subsequently re-compose any 'MatchList' resulting of the recursive call, into one corresponding to the structure of original ExtendedRegEx.+ The expansion-process & corresponding subsequent collation, are associated in a tuple, in which the consumption-profile of the expansion is also contained.+-}+--											collater					:: MatchList m -> (Match m, MatchList m)+											consumptionProfileExpandedConcatenationPrefix	:: [ConsumptionProfile.ConsumptionProfile]+--											expandedConcatenationPrefix			:: Concatenation a+											(collater, consumptionProfileExpandedConcatenationPrefix, expandedConcatenationPrefix)+												| repetitions == 1	= (+													Control.Arrow.first (+														Tree.Node . return {-to List-monad-}+													) . splitAt components,	--Bisect the 'MatchList', into the part resulting from the Alternative, & that for 'concatenationTail'.+													consumptionProfileConcatenationFromAlternative,+													concatenationFromAlternative+												) --Both tuples below, can degenerate to this simple case; it isn't a fundamentally different algorithm.+												| and [+													ExecutionOptions.unrollRepeatedSingletonAlternative executionOptions,+													isSingletonAlternative,+													not $ hasBowAnchor extendedRegExFromAlternative+												] = (+													Control.Arrow.first (+														Tree.Node . ListPlus.chunk components+													) . splitAt (+														components * repetitions+													), --Bisect the 'MatchList', into the part resulting from (Alternative){n}, & that for 'concatenationTail'.+													concat $ replicate repetitions consumptionProfileConcatenationFromAlternative,+													concat $ replicate repetitions concatenationFromAlternative	--Expand all repetitions of the single alternative.+												)+												| otherwise {-choice of Alternatives or has Anchor.Bow-}	=  let+													remainingRepetitions :: Repeatable.Repetitions+													remainingRepetitions	= repetitions - 1	--Expand just the first repetition of the set of 'Alternatives'.++--													singletonRepeatable :: RepeatablePattern a+													singletonRepeatable	= Repeatable.toSingleton repeatablePatternHead+												in (+													Control.Arrow.first (+														Tree.Node . uncurry (:) . Control.Arrow.second (+															concatMap Tree.pop		--Amalgamate the repeated singleton 'MatchList's.+														) . splitAt components			--Bisect first 'MatchList', into the part resulting from 'concatenationFromAlternative', & that for 'replicate remainingRepetitions'.+													) . splitAt (+														components + remainingRepetitions	--Bisect the 'MatchList', into the part resulting from (Alternatives){n}, & that for 'concatenationTail'.+													),+													consumptionProfileConcatenationFromAlternative ++ replicate remainingRepetitions (Consumer.consumptionProfile singletonRepeatable),+													concatenationFromAlternative {-expand 1st repetition-} ++ replicate remainingRepetitions {-potentially zero-} singletonRepeatable	--Enumerate all remaining repetitions of ANY Alternative; a different Alternative can match for each repetition.+												)+									in {-#SCC "collater" #-} collater <$> {-apply to Maybe Functor-} findMatchSlave (+										{-Unsafe.printShow ("Expanded Alternatives for repetitions=" ++ show repetitions ++ ":\t") $-} expandedConcatenationPrefix ++ concatenationTail+									) (+										ConsumptionProfile.accumulateFrom tailConsumptionProfile {-initial value-} consumptionProfileExpandedConcatenationPrefix ++ tail accumulatedConsumptionProfilesTail+									) (+										{-Unsafe.printShow "MetaDataList:\t" $-} accumulateDistinctMetaDataFrom (head distinctMetaDataTail) {-initial value-} expandedConcatenationPrefix ++ tail distinctMetaDataTail+									) inputData inputDataLength distinctInputData	--Recurse to get 'Maybe (MatchList a)'.+								) {-. Unsafe.printShow ("Range for " ++ show concatenationFromAlternative ++ ":\t")-} $ if Repeatable.isPrecise repeatablePatternHead	--These are frequently generated by the previous recursion, as a trial expansion of an Alternative.+									then if ExecutionOptions.validateMinConsumptionOfAlternatives executionOptions && maxDataAvailable < fewest * minConsumptionAlternatives+										then {-Unsafe.printShow "Insufficient data:\t"-} []	--Failure is inevitable.+										else [fewest]+									else {-imprecise range-} let+{-+ Attempt to narrow the repetition-range, from the potentially unbounded specification, to that which is possible according to (data available / consumption-requirement),+ where there're bounds for both numerator & denominator.+ Additionally, one must account for the possibility of the denominator being zero, & if so, select the optimal value if the numerator is also zero.+ The range can be narrowed more aggressively, for the special case of a singleton 'Alternative', as used in a capture-group.+-}+										fewestAlternatives, mostAlternatives, mostAlternatives' :: Repeatable.Repetitions+										fewestAlternatives	= {-#SCC "fewestAlternatives" #-} {-Unsafe.printShow "fewestAlternatives:\t" $-} 1 {-zero is handled later-} `max` if any Data.Maybe.isNothing [maybeMaxConsumptionConcatenationTail, maybeMaxConsumptionAlternatives]+											then fewest+											else {-no unlimited capacities-} let+												minDataAvailable, maxConsumptionAlternatives :: ConsumptionBounds.DataLength+												minDataAvailable		= inputDataLength - Data.Maybe.fromJust maybeMaxConsumptionConcatenationTail	--CAVEAT: can be negative.+												maxConsumptionAlternatives	= Data.Maybe.fromJust maybeMaxConsumptionAlternatives+											in if isSingletonAlternative+												then if maxConsumptionAlternatives == 0							--Denominator.+													then {-sole Alternative can't consume anything ?!-} if minDataAvailable <= 0	--Numerator.+														then {-tail may consume all data-} fewest	--Repeat to meet the minimum requirement.+														else {-unconsumable data-} maxBound		--Failure is inevitable.+													else {-non-zero => can divide-} max fewest $ minDataAvailable /+ maxConsumptionAlternatives+												else {-choice of Alternatives-} let+													minDataAvailable' :: ConsumptionBounds.DataLength+													minDataAvailable'	= minDataAvailable - Data.Maybe.fromJust maybeMaxConsumptionConcatenationFromAlternative	--CAVEAT: potentially negative.+												in if maxConsumptionAlternatives == 0						--Denominator.+													then {-no Alternative can consume anything-} if minDataAvailable' <= 0	--Numerator.+														then {-zero unconsumable data-} {-Unsafe.printShow "0/0 => fewestAlternatives:\t"-} fewest	--Repeat to meet the minimum requirement.+														else {-unconsumable data-} maxBound	--Failure is inevitable.+													else {-non-zero => can divide-} max fewest . succ {-account for expanded instance-} $ minDataAvailable' /+ maxConsumptionAlternatives++										mostAlternatives	= {-#SCC "mostAlternatives" #-} {-Unsafe.printShow "mostAlternatives:\t" $-} case most of+											Just cap	-> cap `min` mostPermissibleRepetitions	--Create a ceiling above which the calculated number of permissible repetitions can't rise.+											_		-> mostPermissibleRepetitions+											where+												mostPermissibleRepetitions :: Repeatable.Repetitions+												mostPermissibleRepetitions+													| isSingletonAlternative		= if minConsumptionAlternatives == 0							--Denominator.+														then {-sole Alternative can consume zero data-} if maxDataAvailable == 0	--Numerator.+															then {-zero data available-} fewest		--Repeat to meet the minimum requirement.+															else {-data available-} maxDataAvailable	--Either each repetition consumes something, or (n - 1) repetitions is a better solution.+														else {-non-zero => can divide-} maxDataAvailable `div` minConsumptionAlternatives	--Divide & round down.+													| otherwise {-choice of Alternatives-}	= 1 {-account for instance expanded as 'concatenationFromAlternative'-} + let+														maxDataAvailable' :: ConsumptionBounds.DataLength+														maxDataAvailable'	= maxDataAvailable - minConsumptionConcatenationFromAlternative	--CAVEAT: potentially negative.+													in if minConsumptionAlternatives == 0							--Denominator.+														then {-@ least one Alternative can consume zero-} if maxDataAvailable' == 0	--Numerator.+															then {-zero data available $-} fewestAlternatives - 1	--Repeat as required. CAVEAT: an annoying dependency, which prevents parallel-evaluation.+															else {-data available-} if and [+																False,	--The cost outweighs the small infrequent dividend.+																minConsumptionConcatenationFromAlternative == 0,+																maxDataAvailable > fewest+															] --If 'fewest' can be met, without unconsuming repetitions of 'concatenationFromAlternative', then stop short of permitting it.+																then maxDataAvailable - 1	--Any greater, & one repetition (possibly that currently expanded) must needlessly consume nothing.+																else maxDataAvailable'		--Either all n repetitions consumes something, or (n - 1) is a better solution.+														else {-non-zero => can divide-} maxDataAvailable' `div` minConsumptionAlternatives	--Divide & round down.+{-+ 'most' has been extracted from 'repeatablePatternHead',+ & reduced according to the number of times it can fit into the maximum available data, to form 'mostAlternatives',+ but if it still exceeds 'fewestAlternatives', the resulting repetition-range may trigger backtracking.+ So, attempt to further reduce this ill-defined repetition-range, according to the competing requirements of 'concatenationTail'.+ CAVEAT: this pays-off infrequently, but not necessarily insignificantly.+-}+										mostAlternatives'	= {-#SCC "mostAlternatives'" #-} if and [+											ExecutionOptions.moderateGreed executionOptions,+											mostAlternatives > fewestAlternatives,	--Otherwise, there's no unbridled greed to moderate.+											minConsumptionAlternatives > 0		--Otherwise, any number of repetitions can occur without necessarily triggering back-tracking.+										 ]+											then case maximumDataBeforePegs of+												Just maximumDataBeforePegs'	-> {-Unsafe.printShow ("Reducing mostAlternatives=" ++ show mostAlternatives ++ "\t-> ") $-} mostAlternatives `min` (+													length maximumDataBeforePegs' `div` minConsumptionAlternatives+												 ) --Cap upper bound.+												_				-> {-Unsafe.printShow "Peg match-failure:\t" $-} negate 1	--Guaranteed to be < 'fewestAlternatives'.+											else {-no requirement for this optimisation-} mostAlternatives+									in [fewestAlternatives .. mostAlternatives']	--The permissible repetition-range of any Alternative, constrained by the amount of data available, after subtracting fixed consumption-requirements.+							 ) $ {-Unsafe.printShow "extendedRegExList:\t" .-} (+								if ExecutionOptions.permitReorderingOfAlternatives executionOptions+{-+ The order in which 'Alternatives' are evaluated, should make no difference to the final result; unless 'ExecutionOptions.useFirstMatchAmongAlternatives'.+ This order can be manipulated in an attempt to more quickly locate any 'Just MatchList', though if 'ExecutionOptions.requireMatchList', an exhaustive search for the optimal solution must be performed anyway.+ Tackling lower-complexity 'Alternatives' first makes sense, in the hope of finding any easy solution before becoming entrenched fighting some hideous monster.+ Preferring 'Alternatives' which require more data also makes sense, regardless of 'Repeatable.isGreedy' which just a preference for a type of solution, because fewer repetitions are then permissible;+ this is rather like trying to pack a box & preferring to try the biggest items first.+-}+									then Data.List.sortBy $ Data.Ord.comparing (+										Consumer.starHeight &&& safeReciprocal . (fromIntegral :: ConsumptionBounds.DataLength -> Rational) . Consumer.getFewest+									) --Firstly increasing complexity, then decreasing minimum data-capacity, otherwise stable.+									else id+							 ) $ filter (+								\e	-> (+									not (hasBowAnchor e) || inputDataOffset == 0			--Necessary & sufficient.+								) && (+									not (hasSternAnchor e) || minConsumptionConcatenationTail == 0	--Necessary but insufficient, since though minimum consumption is zero, maximum isn't necessarily.+								)+							 ) extendedRegExAlternatives+						in if null matchPairList+							then if fewest <= 0+								then {-Unsafe.printShow "Trying zero Alternatives:\t"-} zeroRepetitions	--Which might still fail, depending on 'tailMatch'.+								else {-zero repetitions isn't permissible-} {-Unsafe.printShow "Alternatives-mismatch:\t"-} Nothing+							else {-at least one Alternative matched-} {-#SCC "selectAlternative" #-} Just . uncurry (:) {-re-join head & tail-} $ (+{-+ At least one 'Match' has been found amongst the combinations of repetitions of various 'Alternatives',+ & if the caller reduces the 'Just MatchList' to 'True', that'd be sufficient, but otherwise one must identify the optimal 'MatchList'.+ The primary selection-criterion used is the POSIX one, based on total maximum (or minimum for non-'Repeatable.isGreedy' capture-groups) data-consumption.+ /Perl/'s "first-past-the-post" policy is available as an alternative.+-}+								if ExecutionOptions.useFirstMatchAmongAlternatives executionOptions || length matchPairList == 1+									then head+									else snd {-remove prepended selection-criterion-} . Data.List.maximumBy {-select the best match-} (+										Data.Ord.comparing fst	--Compare using only the criterion, not the result from which it was derived.+									) . {-Unsafe.printShow "Ranked matchPairList:\t" .-} map (+										(+{-+ If the primary selection-criterion doesn't resolve the choice between candidate 'Match'es, I employ these ad-hoc criteria.+* Fewer repetitions are preferred, which discourages the capture of null lists of 'InputData'.++* Within the 'MatchedData', from which a candidate 'Match' is ultimately composed, consumption of 'InputData' beyond 'Repeatable.getFewest' by 'Repeatable.isGreedy' 'RepeatablePattern's, is preferred to non-greedy ones.++* The consumption on each successive repetition is compared between candidate 'Match'es (which are now known to have used an equal number of repetitions);+this causes the data-consumption to flow towards earlier repetitions for greedy capture-groups, & towards later repetitions in non-greedy ones,+thus mimicking the behaviour of the unrolled repetition.++* PS: more precise criteria are defined in <http://www2.research.att.com/~gsf/testregex/re-interpretation.html>.+-}+											\match -> let+												dataLengthCriterion :: InputData m -> Rational+												dataLengthCriterion	= (+													if isGreedy+														then id+														else safeReciprocal	--Prefer less data.+												 ) . fromIntegral . length++--												matchLists :: [MatchList m]+												matchLists	= Tree.pop match+											in (+												dataLengthCriterion $ extractDataFromMatch match,+												if ExecutionOptions.preferFewerRepeatedAlternatives executionOptions+													then safeReciprocal . fromIntegral $ length matchLists :: Rational+													else 0,+												if ExecutionOptions.preferAlternativesWhichFeedTheGreedy executionOptions+													then let+														excessConsumption :: MatchedData m -> ConsumptionBounds.DataLength+														excessConsumption (+															Repeatable.MkRepeatable {+																Repeatable.repetitionBounds	= (f, _),+																Repeatable.isGreedy		= g+															},+															_,+															consumedInputData+														 )+															| g		= l - f+															| otherwise	= f - l+															where+																l :: ConsumptionBounds.DataLength+																l	= length consumedInputData+													in Data.Foldable.foldl' (\l -> (l +) . excessConsumption) 0 match+													else 0,+												if ExecutionOptions.preferAlternativesWhichMimickUnrolling executionOptions+													then map (dataLengthCriterion . extractDataFromMatchList) matchLists+													else []+											) --Create a tuple of selection-criteria, for simultaneous assessment.+										) . fst {-focus the choice on the Alternatives-} &&& id	--Prepend a selection-criterion to each result.+									)+							) matchPairList+						where+--							tailMatch, zeroRepetitions :: Maybe (MatchList m)+							tailMatch	= findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail inputData inputDataLength distinctInputData	--Recurse.+							zeroRepetitions	= (Tree.Node [] :) <$> {-apply to Maybe Functor-} tailMatch	--Prepend a null 'MatchList'.++--							extendedRegExAlternatives :: [ExtendedRegEx m]+							extendedRegExAlternatives	= Data.List.nub $ deconstructAlternatives alternatives++							isSingletonAlternative :: Bool+							isSingletonAlternative	= length extendedRegExAlternatives == 1++							minConsumptionAlternatives	:: ConsumptionBounds.DataLength+							maybeMaxConsumptionAlternatives	:: Maybe ConsumptionBounds.DataLength+							(minConsumptionAlternatives, maybeMaxConsumptionAlternatives)	= Consumer.getConsumptionBounds alternatives	--Independent of the choice of Alternative.+			where+				inputDataOffset :: ConsumptionBounds.DataLength+				inputDataOffset	= originalInputDataLength - inputDataLength+		findMatchSlave _ _ _ _ _ _	= error "RegExDot.RegEx.findMatch.findMatchSlave:\tunexpected parameter-pattern."++{- |+	* Operator's name was chosen to suggest something more than '=~'.++	* CAVEAT: much more expensive then '=~': in /ghci/, 'Just' can be observed to be printed /long/ before the 'MatchList' from which 'Result' is constructed,+	as the lazy algorithm finds the first solution, but not yet necessarily the optimal solution, amongst 'Alternatives'.+-}+(+~) :: (Eq m, NFData m, Show m)+	=> InputData m					-- ^ The input data within which to locate a match.+	-> RegExOpts.RegExOpts (ExtendedRegEx m)	-- ^ The match-options parameterised by the regex against which to match the input data.+	-> Result m+inputData +~ regExOpts	= (+	if hasBowAnchor' then Nothing else head <$> maybeMatchList,	--Record the first 'Match', consumed in the absence of a top-level 'Anchor.Bow'.+	(+		if hasBowAnchor' then id else tail			--Remove the first 'Match', since this wasn't consumed by the 'Concatenation'.+	) . (+		if hasSternAnchor' then id else init			--Remove the last 'Match', since this wasn't consumed by the 'Concatenation'.+	) <$> maybeMatchList,+	if hasSternAnchor' then Nothing else last <$> maybeMatchList	--Record the last 'Match', consumed in the absence of a top-level 'Anchor.Stern'.+ ) where+--	extendedRegEx :: ExtendedRegEx a+	extendedRegEx	= RegExOpts.regEx regExOpts++	hasBowAnchor', hasSternAnchor' :: Bool+	hasBowAnchor'	= hasBowAnchor extendedRegEx+	hasSternAnchor'	= hasSternAnchor extendedRegEx++--	maybeMatchList :: Maybe (MatchList m)+	maybeMatchList	= fmap drift (RegExOpts.setVerbose True regExOpts) `findMatch` inputData++{- |+	* Pattern-match operator.++	* Identifier & parameter-order follow the lead of /Perl/'s pattern-match operator.++	* Considerably more efficient than '+~', since even though they are both implemented via 'findMatch',+	the discovery of /any/ solution is sufficient to generate the return-value;+	lazy-evaluation avoids the requirement to identify the irrelevant optimal solution.+-}+(=~) :: (Eq m, NFData m, Show m)+	=> InputData m					-- ^ The input data within which to locate a match.+	-> RegExOpts.RegExOpts (ExtendedRegEx m)	-- ^ The match-options parameterised by the regex against which to match the input data.+	-> Bool+inputData =~ regExOpts	= Data.Maybe.isJust $ fmap drift (RegExOpts.setVerbose False regExOpts) `findMatch` inputData++-- | Pattern-mismatch operator.+(/~) :: (Eq m, NFData m, Show m)+	=> InputData m					-- ^ The input data within which to locate a match.+	-> RegExOpts.RegExOpts (ExtendedRegEx m)	-- ^ The match-options parameterised by the regex against which to match the input data.+	-> Bool+(/~) inputData	= not . (inputData =~)++-- | The delimiters of 'Alternatives', when in the 'String'-form.+captureGroupDelimiters :: (Char, Char)+captureGroupDelimiters	= ('(', ')')++-- | The token used to separate alternative 'ExtendedRegEx's, when in the 'String'-form.+alternativeExtendedRegExSeparatorToken :: Char+alternativeExtendedRegExSeparatorToken	= '|'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [fst captureGroupDelimiters, snd captureGroupDelimiters, alternativeExtendedRegExSeparatorToken]++{- |+	* Invert the specified fraction, but return 'maxBound' if the result would otherwise be infinite.++	* CAVEAT: this is an awful concept, and therefore intended for internal use only.+-}+safeReciprocal :: Fractional f => f -> f+safeReciprocal 0	= fromIntegral (maxBound :: Int)	--Handle divide-by-zero error.+safeReciprocal f	= recip f++infixl 7 /+	--Same as (/).++{- |+	* Integral division, with any fractional remainder rounded-up.++	* A rather dubious requirement, so internal use only.+-}+--(/+) :: Integral i => i -> i -> i+(/+) ::+	Int	-- ^ Numerator.+	-> Int	-- ^ Denominator.+	-> Int	--10% faster in unoptimised code, & more in optimised.+_ /+ 0				= error "RegExDot.RegEx.(/+):\tzero denominator => infinity"+--numerator /+ denominator	= ceiling ((fromIntegral numerator / fromIntegral denominator) :: Double)+numerator /+ denominator	= uncurry (+) . Control.Arrow.second signum $ quotRem numerator denominator	--Slightly faster.
+ src/RegExDot/RegExOpts.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Provides a complete job-specification.++	* 'regEx' is polymorphic, allowing use of either /RegExDot.RegEx.ExtendedRegEx/ (which is also polymorphic) or /RegExChar.ExtendedRegExChar.ExtendedRegExChar/.+-}++module RegExDot.RegExOpts(+-- * Types+-- ** Data-types+	RegExOpts(..),+-- * Functions+	mkRegEx,+	setVerbose+) where++import qualified	RegExDot.CompilationOptions	as CompilationOptions+import qualified	RegExDot.ExecutionOptions	as ExecutionOptions+import qualified	ToolShed.Defaultable		as Defaultable++-- | Aggregates both 'ExecutionOptions.ExecutionOptions' & 'CompilationOptions.CompilationOptions' with a polymorphic /regex/, to form a complete job-description.+data RegExOpts a	= MkRegExOpts {+	compilationOptions	:: CompilationOptions.CompilationOptions,	-- ^ Parameters governing the result, rather than the implementation.+	executionOptions	:: ExecutionOptions.ExecutionOptions,		-- ^ Parameters governing the implementation, rather than the result.+	regEx			:: a						-- ^ Polymorphic, to permit specialisation either of the type of /regex/ or the type of input-data.+} deriving Show++instance Functor RegExOpts	where+	fmap f regExOpts	= regExOpts { regEx = f $ regEx regExOpts }++-- | Build using 'Defaultable.defaultValue'.+mkRegEx :: a -> RegExOpts a+mkRegEx	r	= MkRegExOpts {+	compilationOptions	= Defaultable.defaultValue,+	executionOptions	= Defaultable.defaultValue,+	regEx			= r+}++-- | Mutator, which sets an appropriate 'ExecutionOptions.ExecutionOptions', for the depth to which the caller wants to probe the resulting match.+setVerbose :: Bool -> RegExOpts a -> RegExOpts a+setVerbose verbose regExOpts	= regExOpts { executionOptions = ExecutionOptions.setVerbose verbose $ executionOptions regExOpts }+
+ src/RegExDot/Repeatable.hs view
@@ -0,0 +1,398 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* A data-type, which represents the permissible range of instances, of the underlying polymorphic datum.++	* Designed for use in a polymorphic /regex/-engine, which specifies patterns composed of repeated /greedy/ & /non-greedy/ sequences of /Meta/-data;++ >		*	+	?	{fewest, most}	{fewest,}	{fewest}+ >		*?	+?	??	{fewest, most}?	{fewest,}?++	* In the context of /regex/es, this concept is known as /Quantification/.++	* /regex/es evolved from the minimal ability to optionally qualify the datum with a <http://en.wikipedia.org/wiki/Kleene_star> suffix.+	More exotic repetition-specifications could be composed by concatenating these atomic building-blocks.+	Here, I've taken the contrary top-down view, & assumed that all data are qualified by a full 'RepetitionBounds', which in most cases will degenerate into a simpler form.++	* The type of entity which is being repeated, isn't the domain of this data-type; it's polymorphic.+-}++module RegExDot.Repeatable(+-- * Types+-- ** Type-synonyms+	Repetitions,+	RepetitionBounds,+-- ** Data-types+	Repeatable(..),+-- * Constants+--	nonGreedyToken,+	oneOrMoreToken,+	rangeDelimiters,+	rangeSeparatorToken,+	tokens,+	zeroOrMoreToken,+	zeroOrOneToken,+-- * Functions+	one,+	oneOrMore,+	oneOrMore',+	zeroOrMore,+	zeroOrMore',+	zeroOrOne,+	zeroOrOne',+--	precisely,+	repeatableParser,+	showSuffix,+-- ** Accessors+	getFewest,+	getMost,+-- ** Mutators+	focus,+--	setNonGreedy,+	toSingleton,+-- ** Operators+	(^#->#),+	(^#->#?),+	(^#->),+	(^#->?),+	(^#),+-- ** Predicates+	isPrecise,+	hasPreciseBounds+) where++import			Control.Applicative((<$>), (<*>))+import			Control.Arrow((***))+import			Text.ParserCombinators.Parsec((<?>))+import qualified	Control.Monad+import qualified	Data.List+import qualified	RegExDot.Consumer		as Consumer+import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile+import qualified	Text.ParserCombinators.Parsec	as Parsec+import qualified	ToolShed.SelfValidate		as SelfValidate++#ifdef HAVE_DEEPSEQ+import			Control.DeepSeq(NFData, rnf)+#else+import			Control.Parallel.Strategies(NFData, rnf)+#endif++infix 6 ^#->#, ^#->#?, ^#->, ^#->?, ^#	--A notch tighter than "DSL"s binary operators.++-- | A number of repetitions.+type Repetitions	= Int++-- | Defines the bounds of a range of permissible repetitions.+type RepetitionBounds	= (Repetitions, Maybe Repetitions)++-- | Creates a precise 'RepetitionBounds', i.e. both lower & upper bounds on the number of 'Repetitions' are equal to the same value.+precisely :: Repetitions -> RepetitionBounds+precisely i	= (i, Just i)++-- | Predicate which is 'True' if exactly one value is permissible, ie lower & upper bounds on the number of 'Repetitions' are identical.+hasPreciseBounds :: RepetitionBounds -> Bool+hasPreciseBounds (fewest, most)	= Just fewest == most++-- | Declares a polymorphic data-type, which augments the underlying 'base' datum, with the range of times it may be used.+data Repeatable a	= MkRepeatable {+	base			:: a,			-- ^ The underlying polymorphic datum.+	repetitionBounds	:: RepetitionBounds,	-- ^ The bounds delimiting the range of permissible repetitions, of 'base'.+	isGreedy		:: Bool			-- ^ Whether to demand as many matching instances of 'base' as possible; or as few (AKA /lazy quantification/).+} deriving Eq++instance Functor Repeatable	where+	fmap f repeatable	= repeatable { base = f $ base repeatable }++-- | 'True' if there's no choice in the number of repetitions; implemented via 'isPrecise'.+isPrecise :: Repeatable a -> Bool+isPrecise	= hasPreciseBounds . repetitionBounds++-- | Builds a parser for a specification of the number of permissible instances of the specified polymorphic parameter.+repeatableParser :: a -> Parsec.Parser (Repeatable a)+repeatableParser b	= Parsec.option (+	one b	--The default; there's no concept of greediness here.+ ) $ do+	repeatable	<- Parsec.choice [+		(Parsec.char oneOrMoreToken <?> "Repeatable.oneOrMoreToken " ++ show oneOrMoreToken)	>> return {-to GenParser-monad-} (oneOrMore b),+		(Parsec.char zeroOrOneToken <?> "Repeatable.zeroOrOneToken " ++ show zeroOrOneToken)	>> return {-to GenParser-monad-} (zeroOrOne b),+		(Parsec.char zeroOrMoreToken <?> "Repeatable.zeroOrMoreToken " ++ show zeroOrMoreToken)	>> return {-to GenParser-monad-} (zeroOrMore b),+		(b ^#->#) {-arbitrarily greedy for now-} <$> uncurry Parsec.between (+			Parsec.char *** Parsec.char $ rangeDelimiters+		) (+			do+				fewest	<- Parsec.skipMany Parsec.space >> (read <$> Parsec.many1 Parsec.digit <?> "Repetition-range minimum")+				most	<- Parsec.skipMany Parsec.space >> Parsec.option (+					Just fewest	--The default.+				 ) (+					do+						i	<- (+							Parsec.char rangeSeparatorToken			<?> "Repeatable.rangeSeparatorToken " ++ show rangeSeparatorToken+						 ) >> Parsec.skipMany Parsec.space >> Parsec.option Nothing {-default to open-ended range-} (+							Just . read <$> Parsec.many1 Parsec.digit	<?> "Repetition-range maximum"+						 )++						Parsec.skipMany Parsec.space >> return {-to GenParser-monad-} i+				 )++				return {-to GenParser-monad-} (fewest, most)+		) <?> "Repeatable.rangeDelimiters " ++ show rangeDelimiters+	 ]++	g	<- Parsec.option True {-the default-} $ (Parsec.char nonGreedyToken <?> "Repeatable.nonGreedyToken " ++ show nonGreedyToken) >> return {-to GenParser-monad-} False++	return {-to GenParser-monad-} repeatable { isGreedy = g }	--Correct prior assumption.++instance Read a => Read (Repeatable a)	where+	readsPrec _ s	= case reads s {-first, read the base-type-} of+		[(base', s1)]	-> (error . ("readsPrec Repeatable:\tparse-error; " ++) . show) `either` return $ Parsec.parse (Control.Monad.liftM2 (,) (repeatableParser base') Parsec.getInput) "Repeatable" s1+		_		-> []	--No parse.++{- |+	* A 'ShowS'-function for the suffix, denoting the permissible repetitions, of 'base'.++	* This function converts the internal, into the tradition /greedy/ & /non-greedy/ quantifiers of various specific varieties.+-}+showSuffix :: Repeatable a -> ShowS+showSuffix repeatable	= let+	showRange :: ShowS -> ShowS+	showRange x	= (\(begin, end) -> begin . x . end) $ (showChar *** showChar) rangeDelimiters+ in (+	case repetitionBounds repeatable of+		(0, Nothing)		-> showChar zeroOrMoreToken+		(1, Nothing)		-> showChar oneOrMoreToken+		(fewest, Nothing)	-> showRange $ shows fewest . showChar rangeSeparatorToken+		(0, Just 1)		-> showChar zeroOrOneToken+		(1, Just 1)		-> id	--CAVEAT: since there's no explicit repetition-operator, the non-greedy modifier can't be appended.+		(fewest, Just most)	-> showRange $ if fewest == most+			then shows fewest	--Single-valued range.+			else shows fewest . showChar rangeSeparatorToken . shows most+ ) . if ($ repeatable) `any` [isGreedy, isPrecise] {-without a range of possibilities, non-greediness is irrelevant-}+	then id+	else showChar nonGreedyToken	--This can only be appended, if there a previous repetition-operator for it to modify.++--Replicate the syntax, for repetition, as used in a POSIX-standard /regex/.+instance Show a => Show (Repeatable a)	where+	showsPrec _ repeatable	= shows (base repeatable) . showSuffix repeatable++instance Consumer.Consumer a => Consumer.Consumer (Repeatable a)	where+	consumptionProfile MkRepeatable {+		base			= b,+		repetitionBounds	= (fewest, most)+	} = baseConsumptionProfile {+		ConsumptionProfile.consumptionBounds	= (fewest *) *** ((*) <$> most <*>) $ ConsumptionProfile.consumptionBounds baseConsumptionProfile	--CAVEAT: special cases exist, where one or both halves of this calculation degenerate to a simpler form, but special treatment, in an attempt to improve performance, proved counterproductive.+	} where+		baseConsumptionProfile :: ConsumptionProfile.ConsumptionProfile+		baseConsumptionProfile	= Consumer.consumptionProfile b++	starHeight MkRepeatable {+		base			= b,+		repetitionBounds	= r+	} = Consumer.starHeight b + if hasPreciseBounds r then 0 else 1++instance SelfValidate.SelfValidator a => SelfValidate.SelfValidator (Repeatable a)	where+	isValid MkRepeatable {+		base			= b,+		repetitionBounds	= (fewest, most),+		isGreedy		= g+	} = and [+		SelfValidate.isValid b,	--Delegate.+		fewest >= 0,+		case most of+			Just m	-> m >= fewest	--Exactly zero instances is perverse, but legal.+			_	-> True,+		g || case most of+			Just m	-> fewest < m	--There ought to be potential for non-greediness, where specified: the converse isn't true, since greediness isn't explicit, & may not have been wanted.+			_	-> True+	 ]++instance NFData a => NFData (Repeatable a)	where+	rnf MkRepeatable {+		base			= b,+		repetitionBounds	= r,+		isGreedy		= g+	} = rnf (b, r, g)++-- | Mutator.+setNonGreedy :: Repeatable a -> Repeatable a+setNonGreedy r	= r { isGreedy = False }++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with the specified range of permissible instances.++	* The /#/s in the identifier represent the two bounds.++	* /a{f, m}/+-}+(^#->#) ::+	a			-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> RepetitionBounds	-- ^ The permissible repetition-bounds for the polymorphic data.+	-> Repeatable a+b ^#-># bounds	= MkRepeatable {+	base			= b,+	repetitionBounds	= bounds,+	isGreedy		= True+}++{- |+	* Construct a non-greedy version of '^#->#'.++	* /a{f, m}?/+-}+(^#->#?) ::+	a			-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> RepetitionBounds	-- ^ The permissible repetition-bounds for the polymorphic data.+	-> Repeatable a+b ^#->#? bounds	= setNonGreedy (b ^#-># bounds)++{- |+	* Construct a greedy 'Repeatable', tailored for data repeated at least the specified number of times.++	* The /#/ in the identifier represents the single bound.++	* /a{f,}/+-}+(^#->) ::+	a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> Repetitions	-- ^ The minimum permissible repetitions of the polymorphic data.+	-> Repeatable a+b ^#-> fewest	= b ^#-># (fewest, Nothing)++{- |+	* Construct a non-greedy version of '^#->'.++	* /a{f,}?/+-}+(^#->?) ::+	a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> Repetitions	-- ^ The minimum permissible repetitions of the polymorphic data.+	-> Repeatable a+b ^#->? fewest	= setNonGreedy (b ^#-> fewest)++{- |+	* Construct a 'Repeatable', tailored for data repeated a precise number of times.++	* The /#/ in the identifier represents the single bound.++	* /a{f}/+-}+(^#) ::+	a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> Repetitions	-- ^ The precise number of repetitions of the polymorphic data which is required.+	-> Repeatable a+b ^# r	= b ^#-># precisely r++{- |+	* Construct a 'Repeatable', tailored for unrepeated data.++	* A degenerate case of '^#'.+-}+one :: a -> Repeatable a+one	= (^# 1)++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with 'fewest' == 0 & 'most' == 1.++	* A specific case of '^#->#'.+-}+zeroOrOne :: a -> Repeatable a+zeroOrOne	= (^#-># (0, Just 1))++-- | Construct a non-greedy version of 'zeroOrOne'.+zeroOrOne' :: a -> Repeatable a+zeroOrOne'	= setNonGreedy . zeroOrOne++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with 'fewest' == 0.++	* A specific case of '^#->'.+-}+zeroOrMore :: a -> Repeatable a+zeroOrMore 	= (^#-> 0)++-- | Construct a non-greedy version of 'zeroOrMore'.+zeroOrMore' :: a -> Repeatable a+zeroOrMore'	= setNonGreedy . zeroOrMore++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with lower 'RepetitionBounds' == one.++	* A specific case of '^#->'.+-}+oneOrMore :: a -> Repeatable a+oneOrMore	= (^#-> 1)++-- | Construct a non-greedy version of 'oneOrMore'.+oneOrMore' :: a -> Repeatable a+oneOrMore'	= setNonGreedy . oneOrMore++-- | Reduces a 'Repeatable', with a range of 'RepetitionBounds', to a precise number of repetitions.+focus :: Repeatable a -> Repetitions -> Repeatable a+focus r i	= r { repetitionBounds = precisely i }++{- |+	* Reduces a 'Repeatable', with a range of 'RepetitionBounds', to a singleton.++	* A degenerate case of 'focus'.+-}+toSingleton :: Repeatable a -> Repeatable a+toSingleton	= (`focus` 1)++-- | Accessor.+getFewest :: Repeatable a -> Repetitions+getFewest MkRepeatable { repetitionBounds = (f, _) }	= f++-- | Accessor.+getMost :: Repeatable a -> Maybe Repetitions+getMost MkRepeatable { repetitionBounds = (_, m) }	= m++-- | The token used to denote /non-greedy/, when in the 'String'-form.+nonGreedyToken :: Char+nonGreedyToken	= '?'++{- |+	* The token used to denote 'zeroOrMore', when in the 'String'-form.++	* AKA /Kleene Star/.+-}+zeroOrMoreToken :: Char+zeroOrMoreToken	= '*'++-- | The token used to denote 'zeroOrOne', when in the 'String'-form.+zeroOrOneToken :: Char+zeroOrOneToken	= '?'++-- | The token used to denote 'oneOrMore', when in the 'String'-form.+oneOrMoreToken :: Char+oneOrMoreToken	= '+'++-- | The delimiters of '^#->#', when in the 'String'-form.+rangeDelimiters :: (Char, Char)+rangeDelimiters	= ('{', '}')++-- | The token used to separate 'RepetitionBounds', when in the 'String'-form.+rangeSeparatorToken :: Char+rangeSeparatorToken	= ','++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= Data.List.nub [nonGreedyToken, zeroOrMoreToken, zeroOrOneToken, oneOrMoreToken, fst rangeDelimiters, snd rangeDelimiters, rangeSeparatorToken]+
+ src/RegExDot/Result.hs view
@@ -0,0 +1,60 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Tools to manipulate a 'RegEx.Result'.++ [@CAVEAT@]	The data-definition remains in "RegEx", since it references 'RegEx.Match'.+-}++module RegExDot.Result(+-- * Functions+-- ** Accessors (Deconstructors)+	getMatchList,+	getPreMatch,+	getPostMatch,+-- ** Predicates+	isMatch,+-- ** Query+	countMatches+) where++import qualified	Data.Maybe+import qualified	RegExDot.RegEx	as RegEx++-- | Accessor.+getPreMatch :: RegEx.Result a -> RegEx.ExternalMatch a+getPreMatch (externalMatch, _, _)	= externalMatch++-- | Accessor.+getPostMatch :: RegEx.Result a -> RegEx.ExternalMatch a+getPostMatch (_, _, externalMatch)	= externalMatch++-- | Accessor.+getMatchList :: RegEx.Result a -> Maybe (RegEx.MatchList a)+getMatchList (_, maybeMatchList, _)	= maybeMatchList++-- | 'True' if the 'RegEx.InputData' matched the 'RegEx.ExtendedRegEx'.+isMatch :: RegEx.Result a -> Bool+isMatch	= Data.Maybe.isJust . getMatchList++-- | Counts the number of top-level 'RegEx.Match'es.+countMatches :: RegEx.Result a -> Int+countMatches (_, Just matchList, _)	= length matchList+countMatches _				= 0+
+ src/RegExDot/ShowablePredicate.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Used to implement /Perl-shortcut/s, /BracketExpression/-ranges & /POSIX Character-classes/.+-}++module RegExDot.ShowablePredicate (+-- * Types+	Predicate,+-- ** Data-types+	ShowablePredicate(+		MkShowablePredicate,+		name,+		predicate+	)+) where++#ifdef HAVE_DEEPSEQ+import	Control.DeepSeq(NFData, rnf)+#else+import	Control.Parallel.Strategies(NFData, rnf)+#endif++-- | An arbitrary polymorphic predicate function.+type Predicate a	= a -> Bool++-- | Container for both the /predicate/, & the name used in the implementation of 'Show'.+data ShowablePredicate a	= MkShowablePredicate {+	name		:: String,	-- ^ The identifier of this predicate when in string-form.+	predicate	:: Predicate a	-- ^ The function used to determine whether an input datum matches.+}++instance Show (ShowablePredicate a)	where+	showsPrec _	= showString . name++instance Eq (ShowablePredicate a)	where+	l == r	= name l == name r	--Ignore 'predicate'.++instance NFData (ShowablePredicate a)	where+	rnf	= rnf . name	--Ignore 'predicate'.+
+ src/RegExDot/Span.hs view
@@ -0,0 +1,64 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Delimits a data-sequence, via its offset & length, within a wider list.+-}++module RegExDot.Span(+-- * Types+-- ** Type-synonyms+	Span,+-- * Functions+	after,+	empty,+	join+) where++import			Control.Arrow((***))+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds++{- |+	* The offset & length of a specific data-sequence, within a wider list.++	* Similar to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html#t%3AMatchArray>.+-}+type Span	= (ConsumptionBounds.DataLength {-offset-}, ConsumptionBounds.DataLength)++-- | An empty instance, located at the specified offset.+empty :: ConsumptionBounds.DataLength -> Span+empty offset	= (offset, 0)++-- | Returns the furthest extent, i.e. the offset just after the end.+after :: Span -> ConsumptionBounds.DataLength+after	= uncurry (+)++{- |+	* Condenses a list into a single value.++	* Expects a gap-free, sorted list.+-}+join ::+	ConsumptionBounds.DataLength	-- ^ The offset into the list of input-data to use when a null list of spans is received.+	-> [Span]+	-> Span+join offset []		= empty offset		--The offset can't be deduced from a null list, so use the value provided.+join _ [singleton]	= singleton		--Merely for efficiency.+join _ spanList		= head *** sum $ unzip spanList++
+ src/RegExDot/Tree.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines a specific type of tree-structure, which is composed from either a datum, or a list of Tree-lists.++	* This more general tree-structure has the shape of a regular-expression match.++	* This contrasts with the typical binary-tree, which is either empty, or contains a (left Tree, datum, right Tree).+-}++module RegExDot.Tree(+-- * Types+-- ** Type-synonyms+--	TreeList,+-- ** Data-types+	Tree(..),+-- * Functions+	pop+) where++import qualified	Control.Arrow+import qualified	Data.Foldable+import qualified	Data.List+import qualified	Data.Monoid++#ifdef HAVE_DEEPSEQ+import			Control.DeepSeq(NFData, rnf)+#else+import			Control.Parallel.Strategies(NFData, rnf)+#endif++-- | A general purpose tree-type structure.+data Tree a	=+	Leaf a			-- ^ The payload.+	| Node [TreeList a]	-- ^ Recurse. NB: a list of lists is required to contain the /MatchLists/ resulting from repeated /Alternatives/.+	deriving Eq++-- | A list of 'Tree's; significant only because it is the essence of the recursive nature of 'Tree'.+type TreeList a	= [Tree a]++instance Show a => Show (Tree a)	where+	showsPrec _ (Leaf a)		= shows a+	showsPrec _ (Node treeLists)	= showList treeLists++instance Read a => Read (Tree a)	where+	readsPrec _ node@('[' : _)	= Control.Arrow.first Node `map` readList node+	readsPrec _ leaf		= Control.Arrow.first Leaf `map` reads leaf++instance NFData a => NFData (Tree a)	where+	rnf (Leaf a)	= rnf a+	rnf (Node l)	= rnf l++instance Functor Tree	where+	fmap f (Leaf a)		= Leaf $ f a+	fmap f (Node treeLists)	= Node $ map (fmap {-recurse-} f `map`) treeLists++instance Data.Foldable.Foldable Tree where+	foldMap f (Leaf a)		= f a	--CAVEAT: 'f' should be Associative, as required by a Monoid.+	foldMap f (Node treeLists)	= Data.List.foldl' (Data.List.foldl' (\monoid -> (monoid `Data.Monoid.mappend`) . Data.Foldable.foldMap f)) Data.Monoid.mempty treeLists++-- | Deconstruct the specified 'Node'; i.e. lop the apex from the 'Tree', leaving a flat top.+pop ::+	Tree a	-- ^ The tree from which to extract the list of trees hanging immediately beneath its apex.+	-> [TreeList a]+pop (Node treeLists)	= treeLists+pop _			= error "RegExDot.Tree.pop:\tunexpected Leaf"+