├── .gitignore ├── .travis-ci.sh ├── .travis.yml ├── LICENSE.md ├── Makefile ├── Readme.md ├── jbuild ├── jbuild-workspace.dev ├── pumping.ml ├── pumping.opam └── test ├── jbuild ├── main.ml ├── test.expected └── test.ml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | _build/ 3 | *.byte 4 | *.native 5 | 6 | # generated by jbuilder 7 | *.merlin 8 | *.install 9 | -------------------------------------------------------------------------------- /.travis-ci.sh: -------------------------------------------------------------------------------- 1 | bash -ex .travis-opam.sh 2 | ## Add more stuff here 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: required 3 | install: wget https://raw.githubusercontent.com/ocaml/ocaml-travisci-skeleton/master/.travis-opam.sh 4 | script: bash -ex .travis-ci.sh 5 | env: 6 | global: 7 | - PACKAGE=pumping 8 | matrix: 9 | - OCAML_VERSION=4.03 10 | - OCAML_VERSION=4.04 11 | - OCAML_VERSION=4.05 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This Software is distributed under the terms of the GNU Lesser 2 | General Public License version 2.1 (included below), or (at your 3 | option) any later version. 4 | 5 | As a special exception to the GNU Library General Public License, you 6 | may link, statically or dynamically, a "work that uses the Library" 7 | with a publicly distributed version of the Library to produce an 8 | executable file containing portions of the Library, and distribute 9 | that executable file under terms of your choice, without any of the 10 | additional requirements listed in clause 6 of the GNU Library General 11 | Public License. By "a publicly distributed version of the Library", 12 | we mean either the unmodified Library, or a modified version of the 13 | Library that is distributed under the conditions defined in clause 3 14 | of the GNU Library General Public License. This exception does not 15 | however invalidate any other reasons why the executable file might be 16 | covered by the GNU Library General Public License. 17 | 18 | ### GNU LESSER GENERAL PUBLIC LICENSE 19 | 20 | Version 2.1, February 1999 21 | 22 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 23 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | 25 | Everyone is permitted to copy and distribute verbatim copies 26 | of this license document, but changing it is not allowed. 27 | 28 | [This is the first released version of the Lesser GPL. It also counts 29 | as the successor of the GNU Library Public License, version 2, hence 30 | the version number 2.1.] 31 | 32 | ### Preamble 33 | 34 | The licenses for most software are designed to take away your 35 | freedom to share and change it. By contrast, the GNU General Public 36 | Licenses are intended to guarantee your freedom to share and change 37 | free software--to make sure the software is free for all its users. 38 | 39 | This license, the Lesser General Public License, applies to some 40 | specially designated software packages--typically libraries--of the 41 | Free Software Foundation and other authors who decide to use it. You 42 | can use it too, but we suggest you first think carefully about whether 43 | this license or the ordinary General Public License is the better 44 | strategy to use in any particular case, based on the explanations below. 45 | 46 | When we speak of free software, we are referring to freedom of use, 47 | not price. Our General Public Licenses are designed to make sure that 48 | you have the freedom to distribute copies of free software (and charge 49 | for this service if you wish); that you receive source code or can get 50 | it if you want it; that you can change the software and use pieces of 51 | it in new free programs; and that you are informed that you can do 52 | these things. 53 | 54 | To protect your rights, we need to make restrictions that forbid 55 | distributors to deny you these rights or to ask you to surrender these 56 | rights. These restrictions translate to certain responsibilities for 57 | you if you distribute copies of the library or if you modify it. 58 | 59 | For example, if you distribute copies of the library, whether gratis 60 | or for a fee, you must give the recipients all the rights that we gave 61 | you. You must make sure that they, too, receive or can get the source 62 | code. If you link other code with the library, you must provide 63 | complete object files to the recipients, so that they can relink them 64 | with the library after making changes to the library and recompiling 65 | it. And you must show them these terms so they know their rights. 66 | 67 | We protect your rights with a two-step method: (1) we copyright the 68 | library, and (2) we offer you this license, which gives you legal 69 | permission to copy, distribute and/or modify the library. 70 | 71 | To protect each distributor, we want to make it very clear that 72 | there is no warranty for the free library. Also, if the library is 73 | modified by someone else and passed on, the recipients should know 74 | that what they have is not the original version, so that the original 75 | author's reputation will not be affected by problems that might be 76 | introduced by others. 77 | 78 | Finally, software patents pose a constant threat to the existence of 79 | any free program. We wish to make sure that a company cannot 80 | effectively restrict the users of a free program by obtaining a 81 | restrictive license from a patent holder. Therefore, we insist that 82 | any patent license obtained for a version of the library must be 83 | consistent with the full freedom of use specified in this license. 84 | 85 | Most GNU software, including some libraries, is covered by the 86 | ordinary GNU General Public License. This license, the GNU Lesser 87 | General Public License, applies to certain designated libraries, and 88 | is quite different from the ordinary General Public License. We use 89 | this license for certain libraries in order to permit linking those 90 | libraries into non-free programs. 91 | 92 | When a program is linked with a library, whether statically or using 93 | a shared library, the combination of the two is legally speaking a 94 | combined work, a derivative of the original library. The ordinary 95 | General Public License therefore permits such linking only if the 96 | entire combination fits its criteria of freedom. The Lesser General 97 | Public License permits more lax criteria for linking other code with 98 | the library. 99 | 100 | We call this license the "Lesser" General Public License because it 101 | does Less to protect the user's freedom than the ordinary General 102 | Public License. It also provides other free software developers Less 103 | of an advantage over competing non-free programs. These disadvantages 104 | are the reason we use the ordinary General Public License for many 105 | libraries. However, the Lesser license provides advantages in certain 106 | special circumstances. 107 | 108 | For example, on rare occasions, there may be a special need to 109 | encourage the widest possible use of a certain library, so that it becomes 110 | a de-facto standard. To achieve this, non-free programs must be 111 | allowed to use the library. A more frequent case is that a free 112 | library does the same job as widely used non-free libraries. In this 113 | case, there is little to gain by limiting the free library to free 114 | software only, so we use the Lesser General Public License. 115 | 116 | In other cases, permission to use a particular library in non-free 117 | programs enables a greater number of people to use a large body of 118 | free software. For example, permission to use the GNU C Library in 119 | non-free programs enables many more people to use the whole GNU 120 | operating system, as well as its variant, the GNU/Linux operating 121 | system. 122 | 123 | Although the Lesser General Public License is Less protective of the 124 | users' freedom, it does ensure that the user of a program that is 125 | linked with the Library has the freedom and the wherewithal to run 126 | that program using a modified version of the Library. 127 | 128 | The precise terms and conditions for copying, distribution and 129 | modification follow. Pay close attention to the difference between a 130 | "work based on the library" and a "work that uses the library". The 131 | former contains code derived from the library, whereas the latter must 132 | be combined with the library in order to run. 133 | 134 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 135 | 136 | **0.** This License Agreement applies to any software library or other 137 | program which contains a notice placed by the copyright holder or 138 | other authorized party saying it may be distributed under the terms of 139 | this Lesser General Public License (also called "this License"). 140 | Each licensee is addressed as "you". 141 | 142 | A "library" means a collection of software functions and/or data 143 | prepared so as to be conveniently linked with application programs 144 | (which use some of those functions and data) to form executables. 145 | 146 | The "Library", below, refers to any such software library or work 147 | which has been distributed under these terms. A "work based on the 148 | Library" means either the Library or any derivative work under 149 | copyright law: that is to say, a work containing the Library or a 150 | portion of it, either verbatim or with modifications and/or translated 151 | straightforwardly into another language. (Hereinafter, translation is 152 | included without limitation in the term "modification".) 153 | 154 | "Source code" for a work means the preferred form of the work for 155 | making modifications to it. For a library, complete source code means 156 | all the source code for all modules it contains, plus any associated 157 | interface definition files, plus the scripts used to control compilation 158 | and installation of the library. 159 | 160 | Activities other than copying, distribution and modification are not 161 | covered by this License; they are outside its scope. The act of 162 | running a program using the Library is not restricted, and output from 163 | such a program is covered only if its contents constitute a work based 164 | on the Library (independent of the use of the Library in a tool for 165 | writing it). Whether that is true depends on what the Library does 166 | and what the program that uses the Library does. 167 | 168 | **1.** You may copy and distribute verbatim copies of the Library's 169 | complete source code as you receive it, in any medium, provided that 170 | you conspicuously and appropriately publish on each copy an 171 | appropriate copyright notice and disclaimer of warranty; keep intact 172 | all the notices that refer to this License and to the absence of any 173 | warranty; and distribute a copy of this License along with the 174 | Library. 175 | 176 | You may charge a fee for the physical act of transferring a copy, 177 | and you may at your option offer warranty protection in exchange for a 178 | fee. 179 | 180 | **2.** You may modify your copy or copies of the Library or any portion 181 | of it, thus forming a work based on the Library, and copy and 182 | distribute such modifications or work under the terms of Section 1 183 | above, provided that you also meet all of these conditions: 184 | 185 | - **a)** The modified work must itself be a software library. 186 | 187 | - **b)** You must cause the files modified to carry prominent notices 188 | stating that you changed the files and the date of any change. 189 | 190 | - **c)** You must cause the whole of the work to be licensed at no 191 | charge to all third parties under the terms of this License. 192 | 193 | - **d)** If a facility in the modified Library refers to a function or a 194 | table of data to be supplied by an application program that uses 195 | the facility, other than as an argument passed when the facility 196 | is invoked, then you must make a good faith effort to ensure that, 197 | in the event an application does not supply such function or 198 | table, the facility still operates, and performs whatever part of 199 | its purpose remains meaningful. 200 | 201 | (For example, a function in a library to compute square roots has 202 | a purpose that is entirely well-defined independent of the 203 | application. Therefore, Subsection 2d requires that any 204 | application-supplied function or table used by this function must 205 | be optional: if the application does not supply it, the square 206 | root function must still compute square roots.) 207 | 208 | These requirements apply to the modified work as a whole. If 209 | identifiable sections of that work are not derived from the Library, 210 | and can be reasonably considered independent and separate works in 211 | themselves, then this License, and its terms, do not apply to those 212 | sections when you distribute them as separate works. But when you 213 | distribute the same sections as part of a whole which is a work based 214 | on the Library, the distribution of the whole must be on the terms of 215 | this License, whose permissions for other licensees extend to the 216 | entire whole, and thus to each and every part regardless of who wrote 217 | it. 218 | 219 | Thus, it is not the intent of this section to claim rights or contest 220 | your rights to work written entirely by you; rather, the intent is to 221 | exercise the right to control the distribution of derivative or 222 | collective works based on the Library. 223 | 224 | In addition, mere aggregation of another work not based on the Library 225 | with the Library (or with a work based on the Library) on a volume of 226 | a storage or distribution medium does not bring the other work under 227 | the scope of this License. 228 | 229 | **3.** You may opt to apply the terms of the ordinary GNU General Public 230 | License instead of this License to a given copy of the Library. To do 231 | this, you must alter all the notices that refer to this License, so 232 | that they refer to the ordinary GNU General Public License, version 2, 233 | instead of to this License. (If a newer version than version 2 of the 234 | ordinary GNU General Public License has appeared, then you can specify 235 | that version instead if you wish.) Do not make any other change in 236 | these notices. 237 | 238 | Once this change is made in a given copy, it is irreversible for 239 | that copy, so the ordinary GNU General Public License applies to all 240 | subsequent copies and derivative works made from that copy. 241 | 242 | This option is useful when you wish to copy part of the code of 243 | the Library into a program that is not a library. 244 | 245 | **4.** You may copy and distribute the Library (or a portion or 246 | derivative of it, under Section 2) in object code or executable form 247 | under the terms of Sections 1 and 2 above provided that you accompany 248 | it with the complete corresponding machine-readable source code, which 249 | must be distributed under the terms of Sections 1 and 2 above on a 250 | medium customarily used for software interchange. 251 | 252 | If distribution of object code is made by offering access to copy 253 | from a designated place, then offering equivalent access to copy the 254 | source code from the same place satisfies the requirement to 255 | distribute the source code, even though third parties are not 256 | compelled to copy the source along with the object code. 257 | 258 | **5.** 5. A program that contains no derivative of any portion of the 259 | Library, but is designed to work with the Library by being compiled or 260 | linked with it, is called a "work that uses the Library". Such a 261 | work, in isolation, is not a derivative work of the Library, and 262 | therefore falls outside the scope of this License. 263 | 264 | However, linking a "work that uses the Library" with the Library 265 | creates an executable that is a derivative of the Library (because it 266 | contains portions of the Library), rather than a "work that uses the 267 | library". The executable is therefore covered by this License. 268 | Section 6 states terms for distribution of such executables. 269 | 270 | When a "work that uses the Library" uses material from a header file 271 | that is part of the Library, the object code for the work may be a 272 | derivative work of the Library even though the source code is not. 273 | Whether this is true is especially significant if the work can be 274 | linked without the Library, or if the work is itself a library. The 275 | threshold for this to be true is not precisely defined by law. 276 | 277 | If such an object file uses only numerical parameters, data 278 | structure layouts and accessors, and small macros and small inline 279 | functions (ten lines or less in length), then the use of the object 280 | file is unrestricted, regardless of whether it is legally a derivative 281 | work. (Executables containing this object code plus portions of the 282 | Library will still fall under Section 6.) 283 | 284 | Otherwise, if the work is a derivative of the Library, you may 285 | distribute the object code for the work under the terms of Section 6. 286 | Any executables containing that work also fall under Section 6, 287 | whether or not they are linked directly with the Library itself. 288 | 289 | **6.** As an exception to the Sections above, you may also combine or 290 | link a "work that uses the Library" with the Library to produce a 291 | work containing portions of the Library, and distribute that work 292 | under terms of your choice, provided that the terms permit 293 | modification of the work for the customer's own use and reverse 294 | engineering for debugging such modifications. 295 | 296 | You must give prominent notice with each copy of the work that the 297 | Library is used in it and that the Library and its use are covered by 298 | this License. You must supply a copy of this License. If the work 299 | during execution displays copyright notices, you must include the 300 | copyright notice for the Library among them, as well as a reference 301 | directing the user to the copy of this License. Also, you must do one 302 | of these things: 303 | 304 | - **a)** Accompany the work with the complete corresponding 305 | machine-readable source code for the Library including whatever 306 | changes were used in the work (which must be distributed under 307 | Sections 1 and 2 above); and, if the work is an executable linked 308 | with the Library, with the complete machine-readable "work that 309 | uses the Library", as object code and/or source code, so that the 310 | user can modify the Library and then relink to produce a modified 311 | executable containing the modified Library. (It is understood 312 | that the user who changes the contents of definitions files in the 313 | Library will not necessarily be able to recompile the application 314 | to use the modified definitions.) 315 | 316 | - **b)** Use a suitable shared library mechanism for linking with the 317 | Library. A suitable mechanism is one that (1) uses at run time a 318 | copy of the library already present on the user's computer system, 319 | rather than copying library functions into the executable, and (2) 320 | will operate properly with a modified version of the library, if 321 | the user installs one, as long as the modified version is 322 | interface-compatible with the version that the work was made with. 323 | 324 | - **c)** Accompany the work with a written offer, valid for at 325 | least three years, to give the same user the materials 326 | specified in Subsection 6a, above, for a charge no more 327 | than the cost of performing this distribution. 328 | 329 | - **d)** If distribution of the work is made by offering access to copy 330 | from a designated place, offer equivalent access to copy the above 331 | specified materials from the same place. 332 | 333 | - **e)** Verify that the user has already received a copy of these 334 | materials or that you have already sent this user a copy. 335 | 336 | For an executable, the required form of the "work that uses the 337 | Library" must include any data and utility programs needed for 338 | reproducing the executable from it. However, as a special exception, 339 | the materials to be distributed need not include anything that is 340 | normally distributed (in either source or binary form) with the major 341 | components (compiler, kernel, and so on) of the operating system on 342 | which the executable runs, unless that component itself accompanies 343 | the executable. 344 | 345 | It may happen that this requirement contradicts the license 346 | restrictions of other proprietary libraries that do not normally 347 | accompany the operating system. Such a contradiction means you cannot 348 | use both them and the Library together in an executable that you 349 | distribute. 350 | 351 | **7.** You may place library facilities that are a work based on the 352 | Library side-by-side in a single library together with other library 353 | facilities not covered by this License, and distribute such a combined 354 | library, provided that the separate distribution of the work based on 355 | the Library and of the other library facilities is otherwise 356 | permitted, and provided that you do these two things: 357 | 358 | - **a)** Accompany the combined library with a copy of the same work 359 | based on the Library, uncombined with any other library 360 | facilities. This must be distributed under the terms of the 361 | Sections above. 362 | 363 | - **b)** Give prominent notice with the combined library of the fact 364 | that part of it is a work based on the Library, and explaining 365 | where to find the accompanying uncombined form of the same work. 366 | 367 | **8.** You may not copy, modify, sublicense, link with, or distribute 368 | the Library except as expressly provided under this License. Any 369 | attempt otherwise to copy, modify, sublicense, link with, or 370 | distribute the Library is void, and will automatically terminate your 371 | rights under this License. However, parties who have received copies, 372 | or rights, from you under this License will not have their licenses 373 | terminated so long as such parties remain in full compliance. 374 | 375 | **9.** You are not required to accept this License, since you have not 376 | signed it. However, nothing else grants you permission to modify or 377 | distribute the Library or its derivative works. These actions are 378 | prohibited by law if you do not accept this License. Therefore, by 379 | modifying or distributing the Library (or any work based on the 380 | Library), you indicate your acceptance of this License to do so, and 381 | all its terms and conditions for copying, distributing or modifying 382 | the Library or works based on it. 383 | 384 | **10.** Each time you redistribute the Library (or any work based on the 385 | Library), the recipient automatically receives a license from the 386 | original licensor to copy, distribute, link with or modify the Library 387 | subject to these terms and conditions. You may not impose any further 388 | restrictions on the recipients' exercise of the rights granted herein. 389 | You are not responsible for enforcing compliance by third parties with 390 | this License. 391 | 392 | **11.** If, as a consequence of a court judgment or allegation of patent 393 | infringement or for any other reason (not limited to patent issues), 394 | conditions are imposed on you (whether by court order, agreement or 395 | otherwise) that contradict the conditions of this License, they do not 396 | excuse you from the conditions of this License. If you cannot 397 | distribute so as to satisfy simultaneously your obligations under this 398 | License and any other pertinent obligations, then as a consequence you 399 | may not distribute the Library at all. For example, if a patent 400 | license would not permit royalty-free redistribution of the Library by 401 | all those who receive copies directly or indirectly through you, then 402 | the only way you could satisfy both it and this License would be to 403 | refrain entirely from distribution of the Library. 404 | 405 | If any portion of this section is held invalid or unenforceable under any 406 | particular circumstance, the balance of the section is intended to apply, 407 | and the section as a whole is intended to apply in other circumstances. 408 | 409 | It is not the purpose of this section to induce you to infringe any 410 | patents or other property right claims or to contest validity of any 411 | such claims; this section has the sole purpose of protecting the 412 | integrity of the free software distribution system which is 413 | implemented by public license practices. Many people have made 414 | generous contributions to the wide range of software distributed 415 | through that system in reliance on consistent application of that 416 | system; it is up to the author/donor to decide if he or she is willing 417 | to distribute software through any other system and a licensee cannot 418 | impose that choice. 419 | 420 | This section is intended to make thoroughly clear what is believed to 421 | be a consequence of the rest of this License. 422 | 423 | **12.** If the distribution and/or use of the Library is restricted in 424 | certain countries either by patents or by copyrighted interfaces, the 425 | original copyright holder who places the Library under this License may add 426 | an explicit geographical distribution limitation excluding those countries, 427 | so that distribution is permitted only in or among countries not thus 428 | excluded. In such case, this License incorporates the limitation as if 429 | written in the body of this License. 430 | 431 | **13.** The Free Software Foundation may publish revised and/or new 432 | versions of the Lesser General Public License from time to time. 433 | Such new versions will be similar in spirit to the present version, 434 | but may differ in detail to address new problems or concerns. 435 | 436 | Each version is given a distinguishing version number. If the Library 437 | specifies a version number of this License which applies to it and 438 | "any later version", you have the option of following the terms and 439 | conditions either of that version or of any later version published by 440 | the Free Software Foundation. If the Library does not specify a 441 | license version number, you may choose any version ever published by 442 | the Free Software Foundation. 443 | 444 | **14.** If you wish to incorporate parts of the Library into other free 445 | programs whose distribution conditions are incompatible with these, 446 | write to the author to ask for permission. For software which is 447 | copyrighted by the Free Software Foundation, write to the Free 448 | Software Foundation; we sometimes make exceptions for this. Our 449 | decision will be guided by the two goals of preserving the free status 450 | of all derivatives of our free software and of promoting the sharing 451 | and reuse of software generally. 452 | 453 | **NO WARRANTY** 454 | 455 | **15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 456 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 457 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 458 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 459 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 460 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 461 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 462 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 463 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 464 | 465 | **16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 466 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 467 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 468 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 469 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 470 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 471 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 472 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 473 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 474 | DAMAGES. 475 | 476 | ### END OF TERMS AND CONDITIONS 477 | 478 | ### How to Apply These Terms to Your New Libraries 479 | 480 | If you develop a new library, and you want it to be of the greatest 481 | possible use to the public, we recommend making it free software that 482 | everyone can redistribute and change. You can do so by permitting 483 | redistribution under these terms (or, alternatively, under the terms of the 484 | ordinary General Public License). 485 | 486 | To apply these terms, attach the following notices to the library. It is 487 | safest to attach them to the start of each source file to most effectively 488 | convey the exclusion of warranty; and each file should have at least the 489 | "copyright" line and a pointer to where the full notice is found. 490 | 491 | 492 | Copyright (C) 493 | 494 | This library is free software; you can redistribute it and/or 495 | modify it under the terms of the GNU Lesser General Public 496 | License as published by the Free Software Foundation; either 497 | version 2 of the License, or (at your option) any later version. 498 | 499 | This library is distributed in the hope that it will be useful, 500 | but WITHOUT ANY WARRANTY; without even the implied warranty of 501 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 502 | Lesser General Public License for more details. 503 | 504 | You should have received a copy of the GNU Lesser General Public 505 | License along with this library; if not, write to the Free Software 506 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 507 | 508 | Also add information on how to contact you by electronic and paper mail. 509 | 510 | You should also get your employer (if you work as a programmer) or your 511 | school, if any, to sign a "copyright disclaimer" for the library, if 512 | necessary. Here is a sample; alter the names: 513 | 514 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 515 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 516 | 517 | , 1 April 1990 518 | Ty Coon, President of Vice 519 | 520 | That's all there is to it! 521 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | jbuilder build @install --dev 3 | 4 | test: 5 | jbuilder build @runtest --dev 6 | 7 | clean: 8 | rm -rf _build 9 | 10 | .PHONY: all test clean 11 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Pumping 2 | 3 | Pumping is a library to enrich the OCaml type system in order to conveniently check regular languages. 4 | 5 | The goal of type systems is to check that some expression respect some properties. More powerful type systems enrich the kind of properties that can be expressed. 6 | On this regard, the OCaml type system is [quite powerful][generic-ppx]. 7 | Indeed, jacques Garrigue showed that [arbitrary Turing machines can be encoded 8 | in GADTs][GADTexhaustiveness]. 9 | These methods allow to check very expressive properties, but are quite inconvenient to use. The *pumping* library allows to use properties that covers regular languages 10 | by leveraging a well known tool: regular expressions. 11 | 12 | [generic-ppx]: http://www.ocamlpro.com/2014/04/01/the-generic-syntax-extension/ 13 | [GADTexhaustiveness]: http://eptcs.web.cse.unsw.edu.au/paper.cgi?ML2015.2 14 | 15 | ## Regular expressions in types 16 | 17 | Pumping introduces a new ppx annotation `re` (also available as `pumping.re`) that 18 | allows to write regular expressions in types: 19 | ```ocaml 20 | type t = [%re? Star (`a | `b)] 21 | ``` 22 | 23 | We can then check if a word is in this regular language. Words here are sequence of polymorphic variants terminated by `` `End``. 24 | ```ocaml 25 | let x : t = `a (`a (`b `End)) 26 | ``` 27 | 28 | Types produced by pumping are structural: we don't even need a type declaration! 29 | ```ocaml 30 | let x : [%re? `a, (`b | `c)] = `a (`c `End) 31 | ``` 32 | 33 | Most of the usual regular expression operators are available, such as star and ranges. 34 | ```ocaml 35 | let x 36 | : [%re? Star ('a'..'z'), (`Foo | `Bar)] 37 | = `f (`o (`o (`Bar `End))) 38 | ``` 39 | 40 | Intersection is also available: 41 | ```ocaml 42 | (`B `End : [%re? (`A | `B) :: (`B | `C)]) 43 | ``` 44 | 45 | ## Convenient notation for regular expressions 46 | 47 | Unfortunately, PPXs are limited by the OCaml syntax. The usual syntax for regular expressions 48 | is short and well known by most people, but not easily encoded in the OCaml syntax. 49 | In order to be able to express regular properties in an even more terse manner, pumping 50 | also allows to use the POSIX notation for regular expressions: 51 | ```ocaml 52 | let x : [%re"foo|bar"] = `foo `End 53 | ``` 54 | 55 | Most of the POSIX syntaxes are supported, including charsets, ranges and bounded repetition: 56 | ```ocaml 57 | let x : [%re"[a-z]{2,6}"] = `p (`o (`t (`a (`t (`o `End))))) 58 | ``` 59 | 60 | A new operator, `&`, is introduced for the intersection: 61 | ```ocaml 62 | let x : [%re"[ab]&[bc]"] = `b `End 63 | ``` 64 | 65 | ## Equivalence of regular expressions 66 | 67 | Since types defined by pumping are structural, it is possible to use the typechecker 68 | to test the equivalence of regular expressions: 69 | ```ocaml 70 | let f : [%re"aa*a"] -> [%re"a{2,}"] = fun x -> x 71 | ``` 72 | 73 | We can also leverage subtyping to test the inclusion of regular expressions: 74 | ```ocaml 75 | let f x = (x : [%re"a*"] :> [%re"[ab]*"]) 76 | ``` 77 | 78 | ## Implementation 79 | 80 | The implementation is a fairly simple adaption of [Brzozowski derivative][deriv]. In 81 | particular, it follows the technique described in the article "[Regular-expression derivatives reexamined][deriv2]". 82 | Recursive types with (polymorphic) variants naturally describes a graph, thanks to 83 | unification type variables and the `as` construction. 84 | 85 | Indeed, `[%re"(OC|oc|c)(a)*ml"]` is simply: 86 | 87 | ```ocaml 88 | [ `oc of 'v2 89 | | `c of 'v2 90 | | `OC of [ `ml of [ `End ] as 'v3 | `a of 'v2 ] as 'v2 91 | ] as 'v1 92 | ``` 93 | 94 | The typechecker then does a very good job at simplifying the types. Unfortunately, it also 95 | unfolds the sub-parts of non-recursive types, which sometimes causes 96 | an exponential blowup. 97 | 98 | [deriv]: https://en.wikipedia.org/wiki/Brzozowski_derivative 99 | [deriv2]: https://www.cl.cam.ac.uk/~so294/documents/jfp09.pdf 100 | 101 | ## Going further 102 | 103 | Several features are yet missing in this implementation. Indeed, the expressivity of 104 | regular languages is well known, but limited. 105 | 106 | - In the style of PERL named regular expressions, it would be interesting to introduce 107 | annotation on types, in order to describe mutually dependent regular expressions: 108 | ```ocaml 109 | type%re t1 = [%re? Star `Foo, t2] 110 | and t2 = [%re? (`Bar | `Baz | t2)] 111 | ``` 112 | - It is unclear how to handle grouping, backreferences and lookaround constructions. 113 | - Deterministic Pushdown automatons are fairly easy to encode (using a "counter" type), but 114 | a new convenient syntax would need to be proposed. 115 | -------------------------------------------------------------------------------- /jbuild: -------------------------------------------------------------------------------- 1 | (jbuild_version 1) 2 | 3 | ;; Library 4 | (library 5 | ((name pumping) 6 | (public_name pumping) 7 | (synopsis "Pumping - Regular languages in types") 8 | (libraries (compiler-libs.common 9 | ocaml-migrate-parsetree 10 | str fmt)) 11 | (modules pumping) 12 | (kind ppx_rewriter) 13 | (flags (:standard 14 | -safe-string 15 | -keep-locs 16 | -short-paths 17 | -w -6-9+58+59 18 | )) 19 | )) 20 | -------------------------------------------------------------------------------- /jbuild-workspace.dev: -------------------------------------------------------------------------------- 1 | ;; Install the following opam switches 2 | ;; 3 | ;; $ jbuilder build @runtest --workspace jbuild-workspace.dev 4 | 5 | (context ((switch 4.03.0))) 6 | (context ((switch 4.04.0))) 7 | -------------------------------------------------------------------------------- /pumping.ml: -------------------------------------------------------------------------------- 1 | open Migrate_parsetree 2 | 3 | (* Define the rewriter on OCaml 4.05 AST *) 4 | open Ast_405 5 | let ocaml_version = Versions.ocaml_405 6 | 7 | type atom = string 8 | 9 | module CSet = Set.Make(Char) 10 | 11 | (** Regular expressions *) 12 | module Re = struct 13 | 14 | type t = 15 | | Epsilon 16 | | Chars of CSet.t 17 | | Atom of atom 18 | | Concat of t list 19 | | Alt of t * t 20 | | Inter of t * t 21 | | Rep of int * int option * t 22 | 23 | let compare = Pervasives.compare 24 | let equal x y = compare x y = 0 25 | 26 | let epsilon = Epsilon 27 | let void = Chars CSet.empty 28 | let atom s = Atom s 29 | let char c = atom (String.make 1 c) 30 | let charset cs = Chars (CSet.of_list cs) 31 | let enumerate c1 c2 = 32 | if c1 > c2 then None 33 | else 34 | let rec aux i m = 35 | if i > m then [] 36 | else Char.chr i :: aux (i+1) m 37 | in 38 | Some (aux (Char.code c1) (Char.code c2)) 39 | 40 | let concat l = 41 | let rec aux = function 42 | | [] -> [] 43 | | Concat l :: l' -> aux (l @ l') 44 | | Epsilon :: l -> aux l 45 | | Chars cs :: _ when CSet.is_empty cs -> [void] 46 | | x :: l -> x :: aux l 47 | in 48 | match aux l with 49 | | [] -> Epsilon 50 | | [x] -> x 51 | | l -> Concat l 52 | 53 | let alt re re' = match re, re' with 54 | | x, x' when equal x x' -> x 55 | | Chars cs, x when CSet.is_empty cs -> x 56 | | x, Chars cs when CSet.is_empty cs -> x 57 | | Chars cs1, Chars cs2 -> Chars (CSet.union cs1 cs2) 58 | | a, b -> 59 | let i = compare a b in 60 | if i >= 0 then Alt (a, b) else Alt (b, a) 61 | 62 | let inter re re' = match re, re' with 63 | | x, x' when equal x x' -> x 64 | | _, Chars cs when CSet.is_empty cs -> void 65 | | Chars cs, _ when CSet.is_empty cs -> void 66 | | a, b -> 67 | let i = compare a b in 68 | if i >= 0 then Inter (a, b) else Inter (b, a) 69 | 70 | let rec rep i j x = match i, j, x with 71 | | 0, Some 0, _ -> Epsilon 72 | | 1, Some 1, x -> x 73 | | _, _, Epsilon -> epsilon 74 | | _, _, Chars cs when CSet.is_empty cs -> epsilon 75 | | _, _, Rep (i', None, x) 76 | | _, None, Rep (i', Some _, x) -> rep (i * i') None x 77 | | _, Some j, Rep (i', Some j', x) -> rep (i * i') (Some (j * j')) x 78 | | i, j, x -> Rep (i, j, x) 79 | 80 | let star x = rep 0 None x 81 | let plus x = rep 1 None x 82 | let opt x = rep 0 (Some 1) x 83 | 84 | module Infix = struct 85 | let ( + ) = alt 86 | let ( * ) x y = concat [ x ; y ] 87 | let ( ! ) = atom 88 | end 89 | end 90 | module ReMap = Map.Make(Re) 91 | module SMap = Map.Make(String) 92 | 93 | module Var : sig 94 | type t 95 | val pp : t Fmt.t 96 | val gen : unit -> t 97 | val name : t -> string 98 | end = struct 99 | type t = string 100 | let pp fmt x = Fmt.pf fmt "'%s" x 101 | let gen = 102 | let r = ref 0 in 103 | fun () -> 104 | incr r ; 105 | "v" ^ string_of_int !r 106 | let name x = x 107 | end 108 | 109 | (** Type grammar *) 110 | 111 | type ty = 112 | | Alt of transition list 113 | | As of ty * Var.t 114 | | Alias of Var.t 115 | and transition = 116 | | A of atom * ty 117 | | E 118 | 119 | let rec pp_transition ppf = function 120 | | A (a, ty) -> 121 | Fmt.pf ppf "`%s of %a" a pp_ty ty 122 | | E -> 123 | Fmt.pf ppf "`End" 124 | and pp_ty ppf = function 125 | | Alias v -> Var.pp ppf v 126 | | As (ty, v) -> Fmt.pf ppf "(%a as %a)" pp_ty ty Var.pp v 127 | | Alt l -> 128 | Fmt.pf ppf "@[[@ %a@ ]@]" 129 | Fmt.(list ~sep:(unit "@ | ") pp_transition) 130 | l 131 | 132 | (** Derivatives *) 133 | 134 | let rec has_epsilon = function 135 | | Re.Epsilon -> true 136 | | Atom _ -> false 137 | | Concat el -> 138 | List.for_all has_epsilon el 139 | | Alt (e1, e2) -> 140 | has_epsilon e1 || has_epsilon e2 141 | | Rep (0, _, _) -> true 142 | | Rep (_, _, _) -> false 143 | | Inter (e1, e2) -> has_epsilon e1 && has_epsilon e2 144 | | Chars _ -> false 145 | 146 | let prefix l re = 147 | let f re_c = Re.concat [re_c; re] in 148 | SMap.map f l 149 | 150 | let union = 151 | SMap.union 152 | (fun _c re1 re2 -> Some (Re.alt re1 re2)) 153 | 154 | let inter = 155 | SMap.merge @@ fun _c re1 re2 -> match re1, re2 with 156 | | Some re1, Some re2 -> Some (Re.inter re1 re2) 157 | | _, _ -> None 158 | 159 | let charset cs = 160 | CSet.fold 161 | (fun c m -> SMap.add (String.make 1 c) Re.epsilon m) 162 | cs 163 | SMap.empty 164 | 165 | let rec heads = function 166 | | Re.Epsilon -> SMap.empty 167 | | Atom a -> SMap.singleton a Re.epsilon 168 | | Concat el -> 169 | let rec aux = function 170 | | [] -> SMap.empty 171 | | e :: t -> 172 | let h = prefix (heads e) (Re.concat t) in 173 | if has_epsilon e 174 | then union h (aux t) 175 | else h 176 | in 177 | aux el 178 | | Alt (e1, e2) -> union (heads e1) (heads e2) 179 | | Rep (i, None, e) -> 180 | prefix (heads e) (Re.rep (max 0 (i-1)) None e) 181 | | Rep (i, Some j, e) -> 182 | prefix (heads e) (Re.rep (max 0 (i-1)) (Some (max 0 (j-1))) e) 183 | | Inter (e1,e2) -> inter (heads e1) (heads e2) 184 | | Chars cs -> charset cs 185 | 186 | let add_new_ty re m = 187 | let v = Var.gen () in 188 | let m = ReMap.add re (Alias v) m in 189 | m, v 190 | 191 | let rec goto c re (map,l) = 192 | if ReMap.mem re map then 193 | (map, A (c, ReMap.find re map) :: l) 194 | else 195 | let map, var = add_new_ty re map in 196 | let map, ty = explore map re in 197 | (map, A (c, As (ty, var)) :: l) 198 | 199 | and explore map re = 200 | let l = heads re in 201 | let init = if has_epsilon re then [E] else [] in 202 | let map, alts = SMap.fold goto l (map, init) in 203 | map, Alt alts 204 | 205 | let make_type re = 206 | let map, var = add_new_ty re ReMap.empty in 207 | let _, ty = explore map re in 208 | As (ty, var) 209 | 210 | (** Posix parser, borrowed from Re *) 211 | module Posix = struct 212 | 213 | exception Parse_error 214 | exception Not_supported 215 | 216 | let parse s = 217 | let i = ref 0 in 218 | let l = String.length s in 219 | let eos () = !i = l in 220 | let test c = not (eos ()) && s.[!i] = c in 221 | let accept c = let r = test c in if r then incr i; r in 222 | let get () = let r = s.[!i] in incr i; r in 223 | let unget () = decr i in 224 | 225 | let rec regexp () = regexp' (branch ()) 226 | and regexp' left = 227 | if accept '|' then regexp' (Re.alt left (branch ())) 228 | else if accept '&' then regexp' (Re.inter left (branch ())) 229 | else left 230 | and branch () = branch' [] 231 | and branch' left = 232 | if eos () || test '|' || test '&' || test ')' then Re.concat (List.rev left) 233 | else branch' (piece () :: left) 234 | and piece () = 235 | let r = atom () in 236 | if accept '*' then Re.star r else 237 | if accept '+' then Re.plus r else 238 | if accept '?' then Re.opt r else 239 | if accept '{' then 240 | match integer () with 241 | Some i -> 242 | let j = if accept ',' then integer () else Some i in 243 | if not (accept '}') then raise Parse_error; 244 | begin match j with 245 | Some j when j < i -> raise Parse_error | _ -> () 246 | end; 247 | Re.rep i j r 248 | | None -> 249 | unget (); r 250 | else 251 | r 252 | and atom () = 253 | if accept '.' then begin 254 | raise Not_supported 255 | (* if newline then Re.notnl else Re.any *) 256 | end else if accept '(' then begin 257 | let r = regexp () in 258 | if not (accept ')') then raise Parse_error; 259 | r 260 | end else 261 | if accept '^' then begin 262 | raise Not_supported 263 | (* if newline then Re.bol else Re.bos *) 264 | end else if accept '$' then begin 265 | raise Not_supported 266 | (* if newline then Re.eol else Re.eos *) 267 | end else if accept '[' then begin 268 | if accept '^' then 269 | raise Not_supported 270 | (* Re.diff (Re.compl (bracket [])) (Re.char '\n') *) 271 | else 272 | Re.charset (bracket []) 273 | end else 274 | if accept '\\' then begin 275 | if eos () then raise Parse_error; 276 | match get () with 277 | '|' | '&' | '(' | ')' | '*' | '+' | '?' 278 | | '[' | '.' | '^' | '$' | '{' | '\\' as c -> Re.char c 279 | | _ -> raise Parse_error 280 | end else begin 281 | if eos () then raise Parse_error; 282 | match get () with 283 | '*' | '+' | '?' | '{' | '\\' -> raise Parse_error 284 | | c -> Re.char c 285 | end 286 | and integer () = 287 | if eos () then None else 288 | match get () with 289 | '0'..'9' as d -> integer' (Char.code d - Char.code '0') 290 | | _ -> unget (); None 291 | and integer' i = 292 | if eos () then Some i else 293 | match get () with 294 | '0'..'9' as d -> 295 | let i' = 10 * i + (Char.code d - Char.code '0') in 296 | if i' < i then raise Parse_error; 297 | integer' i' 298 | | _ -> 299 | unget (); Some i 300 | and bracket s = 301 | if s <> [] && accept ']' then s else begin 302 | let c = char () in 303 | if accept '-' then begin 304 | if accept ']' then c :: '-' :: s else begin 305 | let c' = char () in 306 | match Re.enumerate c c' with 307 | | None -> raise Parse_error 308 | | Some l -> bracket (l @ s) 309 | end 310 | end else 311 | bracket (c :: s) 312 | end 313 | and char () = 314 | if eos () then raise Parse_error; 315 | let c = get () in 316 | if c = '[' then begin 317 | if accept '=' then raise Not_supported 318 | else if accept ':' then begin 319 | raise Not_supported (*XXX*) 320 | end else if accept '.' then begin 321 | if eos () then raise Parse_error; 322 | let c = get () in 323 | if not (accept '.') then raise Not_supported; 324 | if not (accept ']') then raise Parse_error; 325 | c 326 | end else 327 | c 328 | end else 329 | c 330 | in 331 | let res = regexp () in 332 | if not (eos ()) then raise Parse_error; 333 | res 334 | 335 | let rec simplify (re : Re.t) = match re with 336 | | Re.Concat l -> 337 | let rec aux = function 338 | | [] -> [] 339 | | Re.Atom s :: Re.Atom s' :: l -> 340 | aux (Re.atom (s ^ s') :: l) 341 | | x :: l -> simplify x :: aux l 342 | in 343 | Re.concat (aux l) 344 | | Re.Rep (i, j, re) -> Re.rep i j (simplify re) 345 | | Re.Alt (re1, re2) -> Re.alt (simplify re1) (simplify re2) 346 | | Re.Inter (re1, re2) -> Re.inter (simplify re1) (simplify re2) 347 | | Re.Atom _ 348 | | Re.Epsilon 349 | | Re.Chars _ as re -> re 350 | 351 | let make loc x = 352 | try 353 | simplify @@ parse x 354 | with 355 | | Parse_error -> 356 | Location.raise_errorf ~loc "This posix regular expression is invalid." 357 | | Not_supported -> 358 | Location.raise_errorf ~loc "This posix regular expression uses unsuported features." 359 | end 360 | 361 | (** Syntax *) 362 | 363 | open Parsetree 364 | module A = Ast_helper 365 | 366 | let rec re_of_parsetree pat = 367 | let loc = pat.ppat_loc in 368 | match pat.ppat_desc with 369 | | Ppat_tuple l -> 370 | Re.concat @@ List.map re_of_parsetree l 371 | | Ppat_or (p1, p2) -> 372 | Re.alt (re_of_parsetree p1) (re_of_parsetree p2) 373 | 374 | | Ppat_construct ({txt=Longident.Lident "Star"}, None) -> 375 | Location.raise_errorf ~loc:pat.ppat_loc 376 | "Star constructors take an argument." 377 | | Ppat_construct ({txt=Longident.Lident "Star"}, Some p) -> 378 | Re.star (re_of_parsetree p) 379 | 380 | | Ppat_construct ({txt=Longident.Lident ("Eps"|"Epsilon")}, None) -> 381 | Re.epsilon 382 | | Ppat_construct ({txt=Longident.Lident ("Eps"|"Epsilon")}, Some _) -> 383 | Location.raise_errorf ~loc:pat.ppat_loc 384 | "Epsilon constructors take no arguments." 385 | 386 | | Ppat_construct ({txt=Longident.Lident ("()")}, None) -> 387 | Re.void 388 | | Ppat_construct ({txt=Longident.Lident ("()")}, Some _) -> 389 | Location.raise_errorf ~loc:pat.ppat_loc 390 | "Void constructors take no arguments." 391 | 392 | | Ppat_construct ({txt=Longident.Lident ("Inter"|"::")}, 393 | Some {ppat_desc = Ppat_tuple [p1; p2]}) -> 394 | Re.inter (re_of_parsetree p1) (re_of_parsetree p2) 395 | | Ppat_construct ({txt=Longident.Lident ("Inter"|"::")}, _) -> 396 | Location.raise_errorf ~loc:pat.ppat_loc 397 | "Inter constructors take exactly two arguments." 398 | 399 | | Ppat_variant (s, None) -> 400 | Re.atom s 401 | | Ppat_variant (_, Some _) -> 402 | Location.raise_errorf ~loc:pat.ppat_loc 403 | "Symbols can not have arguments." 404 | 405 | | Ppat_interval (Pconst_char c1, Pconst_char c2) -> 406 | begin match Re.enumerate c1 c2 with 407 | | Some l -> Re.charset l 408 | | None -> 409 | Location.raise_errorf ~loc "This character range is ill-formed." 410 | end 411 | | _ -> 412 | Location.raise_errorf ~loc:pat.ppat_loc 413 | "This is not a valid regular expression constructor." 414 | 415 | let make_variant ~loc dir l = match dir with 416 | | `Eq -> A.Typ.variant ~loc l Closed None 417 | | `GEq -> A.Typ.variant ~loc l Open None 418 | | `LEq -> A.Typ.variant ~loc l Closed (Some []) 419 | 420 | let rec parsetree_of_ty loc dir ty = match ty with 421 | | Alt l -> 422 | let aux = function 423 | | E -> Rtag ("End", [], true, []) 424 | | A (c, ty) -> Rtag (c, [], false, [parsetree_of_ty loc dir ty]) 425 | in 426 | make_variant ~loc dir (List.map aux l) 427 | | As (ty,v) -> 428 | A.Typ.alias ~loc (parsetree_of_ty loc dir ty) (Var.name v) 429 | | Alias v -> 430 | A.Typ.var ~loc (Var.name v) 431 | 432 | let get_extension s = match Str.split (Str.regexp_string ".") s with 433 | | ["pumping";"re"] | ["re"] -> Some `Eq 434 | | ["pumping";"re";("le"|"less") ] | ["re";("le"|"less")] -> Some `LEq 435 | | ["pumping";"re";("ge"|"greater") ] | ["re";("ge"|"greater")] -> Some `GEq 436 | | _ -> None 437 | 438 | let mapper = 439 | let module AM = Ast_mapper in 440 | let typ mapper x = 441 | let loc = x.ptyp_loc in 442 | match x.ptyp_desc with 443 | | Ptyp_extension ({txt}, payload) when get_extension txt <> None -> 444 | let dir = match get_extension txt with Some x -> x | None -> assert false in 445 | let p = match payload with 446 | | PStr [{pstr_desc = Pstr_eval 447 | ({pexp_loc=loc; pexp_desc = Pexp_constant (Pconst_string (s,_))},_)}] 448 | | PPat ({ppat_loc=loc; ppat_desc = Ppat_constant (Pconst_string (s,_))}, None) 449 | -> Posix.make loc s 450 | | PPat (p,None) -> re_of_parsetree p 451 | | PStr _ | PSig _ | PTyp _ | PPat (_, Some _) -> 452 | Location.raise_errorf ~loc 453 | "The payload of this extension should be either a pattern or a string." 454 | in 455 | parsetree_of_ty loc dir @@ make_type p 456 | | _ -> AM.default_mapper.typ mapper x 457 | in 458 | { AM.default_mapper with typ } 459 | 460 | 461 | (* Register the rewriter in the driver *) 462 | let () = 463 | Driver.register 464 | ~name:"pumping" 465 | ocaml_version 466 | (fun _config _cookies -> mapper) 467 | 468 | (* 469 | Pumping - Regular languages in types 470 | 471 | Copyright (C) 2017 Gabriel Radanne 472 | 473 | This library is free software; you can redistribute it and/or 474 | modify it under the terms of the GNU Lesser General Public 475 | License as published by the Free Software Foundation; either 476 | version 2 of the License, or (at your option) any later version. 477 | 478 | This library is distributed in the hope that it will be useful, 479 | but WITHOUT ANY WARRANTY; without even the implied warranty of 480 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 481 | Lesser General Public License for more details. 482 | 483 | You should have received a copy of the GNU Lesser General Public 484 | License along with this library; if not, write to the Free Software 485 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 486 | *) 487 | -------------------------------------------------------------------------------- /pumping.opam: -------------------------------------------------------------------------------- 1 | opam-version: "1.2" 2 | name: "pumping" 3 | maintainer: "Gabriel Radanne " 4 | authors: [ "Gabriel Radanne" ] 5 | homepage: "https://github.com/Drup/pumping" 6 | bug-reports: "https://github.com/Drup/pumping/issues" 7 | dev-repo: "https://github.com/Drup/pumping.git" 8 | synopsis: "Regular languages in types" 9 | license: "LGPL with OCaml linking exception" 10 | version: "dev" 11 | tags: [ "regex" "types" ] 12 | build: [ 13 | ["jbuilder" "build" "--only-packages" "pumping" "--root" "." "-j" jobs "@install"] 14 | ] 15 | run-test: [ 16 | ["jbuilder" "build" "--only-packages" "pumping" "--root" "." "-j" jobs "@runtest"] 17 | ] 18 | depends: [ 19 | "ocamlfind" {build} 20 | "jbuilder" {build} 21 | "ocaml-migrate-parsetree" 22 | "fmt" 23 | ] 24 | available: [ ocaml-version >= "4.03.0" ] -------------------------------------------------------------------------------- /test/jbuild: -------------------------------------------------------------------------------- 1 | (jbuild_version 1) 2 | 3 | (executable 4 | ((name main) 5 | (libraries (pumping ocaml-migrate-parsetree)))) 6 | 7 | (rule 8 | ((targets (test.result)) 9 | (deps (test.ml)) 10 | (action (system "TERM=dumb ${OCAML} -noinit -noprompt -ppx ./${exe:main.exe} < ${<} 2>&1 | tail -n +2 > ${@}")))) 11 | 12 | (alias 13 | ((name runtest) 14 | (deps (test.result test.expected)) 15 | (action (run diff -dEbZBt test.result test.expected)))) 16 | -------------------------------------------------------------------------------- /test/main.ml: -------------------------------------------------------------------------------- 1 | Migrate_parsetree.Driver.run_as_ppx_rewriter () 2 | -------------------------------------------------------------------------------- /test/test.expected: -------------------------------------------------------------------------------- 1 | 2 | type t = [ `A of [ `B of 'a | `C of 'a | `End ] as 'a ] 3 | - : t = `A `End 4 | - : t = `A (`B `End) 5 | - : t = `A (`B (`B `End)) 6 | - : t = `A (`C (`B `End)) 7 | Characters 16-18: 8 | (`A : t) ;; 9 | ^^ 10 | Error: This expression has type [> `A ] 11 | but an expression was expected of type t 12 | Types for tag `A are incompatible 13 | Characters 1-8: 14 | (`B `End : t) ;; 15 | ^^^^^^^ 16 | Error: This expression has type [> `B of [> `End ] ] 17 | but an expression was expected of type t 18 | The second variant type does not allow tag(s) `B 19 | Characters 4-13: 20 | (`A (`A `End) : t) ;; 21 | ^^^^^^^^^ 22 | Error: This expression has type [> `A of [> `End ] ] 23 | but an expression was expected of type 24 | [ `B of 'a | `C of 'a | `End ] as 'a 25 | The second variant type does not allow tag(s) `A 26 | Characters 8-17: 27 | (`A (`C (`A `End)) : t) ;; 28 | ^^^^^^^^^ 29 | Error: This expression has type [> `A of [> `End ] ] 30 | but an expression was expected of type 31 | [ `B of 'a | `C of 'a | `End ] as 'a 32 | The second variant type does not allow tag(s) `A 33 | - : [ `A of [ `End ] 34 | | `B of [ `End ] 35 | | `C of [ `End ] 36 | | `D of [ `End ] 37 | | `E of [ `End ] 38 | | `F of [ `End ] 39 | | `G of [ `End ] 40 | | `H of [ `End ] 41 | | `I of [ `End ] 42 | | `J of [ `End ] 43 | | `K of [ `End ] 44 | | `L of [ `End ] 45 | | `M of [ `End ] 46 | | `N of [ `End ] 47 | | `O of [ `End ] 48 | | `P of [ `End ] 49 | | `Q of [ `End ] 50 | | `R of [ `End ] 51 | | `S of [ `End ] 52 | | `T of [ `End ] 53 | | `U of [ `End ] 54 | | `V of [ `End ] 55 | | `W of [ `End ] 56 | | `X of [ `End ] 57 | | `Y of [ `End ] 58 | | `Z of [ `End ] ] 59 | = `A `End 60 | - : [ `C of [ `End ] ] = `C `End 61 | - : [ `C of [ `End ] ] = `C `End 62 | - : [ `End ] = `End 63 | - : [ `a of [ `End ] ] = `a `End 64 | - : [ `abc of [ `End ] ] = `abc `End 65 | - : [ `a of [ `End ] | `b of [ `End ] ] = `a `End 66 | Characters 1-8: 67 | (`c `End :[%re"[ab]"]) ;; (*fail*) 68 | ^^^^^^^ 69 | Error: This expression has type [> `c of [> `End ] ] 70 | but an expression was expected of type 71 | [ `a of [ `End ] | `b of [ `End ] ] 72 | The second variant type does not allow tag(s) `c 73 | - : [ `a of [ `End ] | `b of [ `End ] | `c of [ `End ] | `d of [ `End ] ] = 74 | `a `End 75 | Characters 1-8: 76 | (`x `End :[%re"[a-d]"]) ;; (*fail*) 77 | ^^^^^^^ 78 | Error: This expression has type [> `x of [> `End ] ] 79 | but an expression was expected of type 80 | [ `a of [ `End ] | `b of [ `End ] | `c of [ `End ] | `d of [ `End ] ] 81 | The second variant type does not allow tag(s) `x 82 | - : [ `End | `a of 'a ] as 'a = `a (`a (`a `End)) 83 | - : [ `End | `a of 'a ] as 'a = `End 84 | - : [ `a of [ `End | `a of 'a ] as 'a ] = `a (`a (`a `End)) 85 | Characters 1-5: 86 | (`End :[%re"a+"]) ;; (*fail*) 87 | ^^^^ 88 | Error: This expression has type [> `End ] 89 | but an expression was expected of type 90 | [ `a of [ `End | `a of 'a ] as 'a ] 91 | The second variant type does not allow tag(s) `End 92 | - : [ `a of [ `a of [ `a of [ `End | `a of 'a ] as 'a ] ] ] = 93 | `a (`a (`a `End)) 94 | - : [ `a of [ `a of [ `a of [ `End | `a of 'a ] as 'a ] ] ] = 95 | `a (`a (`a (`a `End))) 96 | Characters 8-12: 97 | (`a (`a `End) :[%re"a{3,}"]) ;; (*fail*) 98 | ^^^^ 99 | Error: This expression has type [> `End ] 100 | but an expression was expected of type 101 | [ `a of [ `End | `a of 'a ] as 'a ] 102 | The second variant type does not allow tag(s) `End 103 | - : [ `a of [ `a of [ `a of [ `End | `a of [ `End ] ] ] ] ] = 104 | `a (`a (`a `End)) 105 | - : [ `a of [ `a of [ `a of [ `End | `a of [ `End ] ] ] ] ] = 106 | `a (`a (`a (`a `End))) 107 | Characters 8-12: 108 | (`a (`a `End) :[%re"a{3,4}"]) ;; (*fail*) 109 | ^^^^ 110 | Error: This expression has type [> `End ] 111 | but an expression was expected of type 112 | [ `a of [ `End | `a of [ `End ] ] ] 113 | The second variant type does not allow tag(s) `End 114 | Characters 16-25: 115 | (`a (`a (`a (`a (`a `End)))) :[%re"a{3,4}"]) ;; (*fail*) 116 | ^^^^^^^^^ 117 | Error: This expression has type [> `a of [> `End ] ] 118 | but an expression was expected of type [ `End ] 119 | The second variant type does not allow tag(s) `a 120 | - : [ `b of [ `End ] ] = `b `End 121 | Characters 1-8: 122 | (`a `End :[%re"[ab]&[bc]"]) ;; (*fail*) 123 | ^^^^^^^ 124 | Error: This expression has type [> `a of [> `End ] ] 125 | but an expression was expected of type [ `b of [ `End ] ] 126 | The second variant type does not allow tag(s) `a 127 | val id : 'a -> 'a = 128 | - : [ `a of [ `a of [ `End | `a of 'a ] as 'a ] ] -> [ `a of [ `a of 'a ] ] = 129 | 130 | - : [ `b of [ `End ] ] -> [ `b of [ `End ] ] = 131 | - : [ `a of [ `c of [ `End ] | `d of [ `End ] ] 132 | | `b of [ `c of [ `End ] | `d of [ `End ] ] ] -> 133 | [ `a of [ `c of [ `End ] | `d of [ `End ] ] 134 | | `b of [ `c of [ `End ] | `d of [ `End ] ] ] 135 | = 136 | 137 | val f : ([ `End | `a of 'a ] as 'a) -> ([ `End | `a of 'b | `b of 'b ] as 'b) = 138 | -------------------------------------------------------------------------------- /test/test.ml: -------------------------------------------------------------------------------- 1 | type t = [%re? `A, Star (`B | `C)] ;; 2 | 3 | (* Success *) 4 | (`A `End : t) ;; 5 | (`A (`B `End) : t) ;; 6 | (`A (`B (`B `End)) : t) ;; 7 | (`A (`C (`B `End)) : t) ;; 8 | 9 | (* Failure *) 10 | (`A : t) ;; 11 | (`B `End : t) ;; 12 | (`A (`A `End) : t) ;; 13 | (`A (`C (`A `End)) : t) ;; 14 | 15 | 16 | (* pattern syntaxes *) 17 | (`A `End : [%re? 'A'..'Z']);; 18 | (`C `End : [%re? Inter ((`A | `C),(`B | `C))]);; 19 | (`C `End : [%re? (`A | `C) :: (`B | `C)]);; 20 | (`End : [%re? Epsilon]);; 21 | 22 | (* posix *) 23 | (`a `End :[%re"a"]) ;; 24 | (`abc `End :[%re"abc"]) ;; 25 | 26 | (`a `End :[%re"[ab]"]) ;; 27 | (`c `End :[%re"[ab]"]) ;; (*fail*) 28 | 29 | (`a `End :[%re"[a-d]"]) ;; 30 | (`x `End :[%re"[a-d]"]) ;; (*fail*) 31 | 32 | (`a (`a (`a `End)) :[%re"a*"]) ;; 33 | (`End :[%re"a*"]) ;; 34 | 35 | (`a (`a (`a `End)) :[%re"a+"]) ;; 36 | (`End :[%re"a+"]) ;; (*fail*) 37 | 38 | (`a (`a (`a `End)) :[%re"a{3,}"]) ;; 39 | (`a (`a (`a (`a `End))) :[%re"a{3,}"]) ;; 40 | (`a (`a `End) :[%re"a{3,}"]) ;; (*fail*) 41 | 42 | (`a (`a (`a `End)) :[%re"a{3,4}"]) ;; 43 | (`a (`a (`a (`a `End))) :[%re"a{3,4}"]) ;; 44 | (`a (`a `End) :[%re"a{3,4}"]) ;; (*fail*) 45 | (`a (`a (`a (`a (`a `End)))) :[%re"a{3,4}"]) ;; (*fail*) 46 | 47 | (`b `End :[%re"[ab]&[bc]"]) ;; 48 | (`a `End :[%re"[ab]&[bc]"]) ;; (*fail*) 49 | 50 | 51 | (* equivalences *) 52 | let id x = x ;; 53 | (id : [%re"aa*a"] -> [%re"a{2,}"]);; 54 | (id : [%re"[ab]&[bc]"] -> [%re"b"]);; 55 | (id : [%re"(a|b)(c|d)"] -> [%re"[a]c|[a]d|[b]c|[b]d"]);; 56 | 57 | let f x = (x : [%re"a*"] :> [%re"[ab]*"]) ;; 58 | --------------------------------------------------------------------------------