cpphs-1.20.8/0000755000000000000000000000000013122646212011104 5ustar0000000000000000cpphs-1.20.8/CHANGELOG0000644000000000000000000001752613122646212012331 0ustar0000000000000000Version 1.20 ------------ * bugfixes for #if defined(FOO) && FOO(a,b) * (1.20.1): fix version number * (1.20.2): ensure all input/output is UTF8, regardless of locale * (1.20.3): detect an absolute windows path with a drive letter in a #include * (1.20.4): more windows path handling * (1.20.5): revert change in 1.20.4 * (1.20.6): minor bugfix for crash in obscure corner case * (1.20.7): bugfix for windows drive letter in #include * (1.20.8): another bugfix for windows drive letter in #include Version 1.19 ------------ * expose more of the cpphs API * allow the static linking exception to the LGPL * (1.19.1): don't warn about trailing comments in #ifdefs * (1.19.2): fix build error * (1.19.3): bugfix for hlint ticket #161 - interaction of --unlit/--linepragma Version 1.18 ------------ * better lexing of Template Haskell single quotes (thanks to Stefan Wehr) * (1.18.1): fix incomplete pattern match * (1.18.2): bugfix for erroneous boolean intepretation of some macro expansions in #if clauses * (1.18.3): further rewrites of the #if expression parser * (1.18.4): fix the accidental flipping of comment-stripping behaviour with --cpp -traditional flags * (1.18.5): fix a bug with windows filepath directory separators in a #include * (1.18.6): bugfix to reject a macro usage with different arity than its definition * (1.18.7): bugfix to accept a #include with absolute filepath * (1.18.8): fix version number * (1.18.9): accept #if defined foo as well as #if defined(foo) Version 1.17 ------------ * recursively evaluate #if expressions after macro expansion (fix) * (1.17.1): report the right version with cpphs --version Version 1.16 ------------ * fix interaction of runCpphsReturningSymTab with --nomacro Version 1.15 ------------ * Fix the interaction of --nomacro with --strip. * Fix the error message received when # appears without a command. Version 1.14 ------------ * New API to return symbol table after processing. Version 1.13 ------------ * Accept -U cmdline option for compatibility with cpp. Version 1.12 ------------ * Allow it to build with ghc-7.2. Version 1.11 ------------ * API change: runCpphs, cppIfdef, and macroPass are now in the IO monad. Version 1.10 ----------- * New command-line option: "--linepragma" It converts #line droppings into {-# LINE #-}. Version 1.9 ----------- * Bugfix for #undef. Version 1.8 ----------- * Bugfix for off-by-one error in line numbers with --include=file. Version 1.7 ----------- * Bugfix in interaction of --unlit with \end{code} Version 1.6 ----------- * New command-line option: "--include=filename". * New command-line option: "--strip-eol" for comment-stripping. * Line pragmas can have filenames containing spaces. Version 1.5 ----------- * Parametrised macro-calls now permitted in #ifdef's. * Recursive textual expansion now permitted in #ifdef's. * Better options-handling when used as a library. * Various small bugfixes Version 1.4 ----------- * Added a "--pragma" option to retain #pragma in the output. * Fixed a number of obscure corner cases involving the interaction of multiple features e.g. foo##__LINE__. * Added the "--nowarn" option. Version 1.3 ----------- * Added a "--cpp" option for drop-in compatibility with standard cpp. It causes cpphs to accept standard cpp flags and translate them to cpphs equivalents. Compatibility options include: -o, -ansi, -traditional, -stdc, -x, -include, -P, -C, -CC, -A. The file behaviour is different too - if two filenames are given on the commandline, then the second is treated as the output location. * Fixed a corner-case bug in evaluating chained and overlapping #ifdefs. Version 1.2 ----------- * Re-arranged the source files into hierarchical libraries. * Exposed the library interface as an installable Cabal package, with Haddock documentation. * Added the --unlit option, for removing literate-style comments. Version 1.1 ----------- * Fix the .cabal way of building cpphs. * Update the --version reported (forgotten in 1.0, which still reports 0.9) * No longer throws an error when given an empty file as input. Version 1.0 ----------- * Add a compatibility script cpphs.compat, allowing cpphs to act as a drop-in replacement for cpp, e.g. ghc -cpp -pgmP cpphs.compat * Place quotes around replacements for special macros __FILE__, __DATE__, and __TIME__. * If no files are specified, read from stdin. * Ignore #! lines (e.g. in scripts) * Parse -D commandline options once only, and consistently with cpp, i.e. -Dfoo means foo=1 * Fix compatibility with preprocessors like hsc2hs, which use non-cpp directives like #def. They are now passed through to the output with a warning to stderr. Version 0.9 ----------- * Bugfix for ghc-6.4 -O: flush the output buffer. Version 0.8 ----------- * Added the --text option, to signify the input should not be lexed as Haskell. This causes macros to be defined or expanded regardless of their location within comments, string delimiters, etc. * Shuffle a few files around to make it easier to say 'hmake cpphs'. There is also now a runhugs script to invoke cpphs nicely. Version 0.7 ----------- * Enable the __FILE__, __LINE__, __DATE__, and __TIME__ specials, which can be useful for creating DIY error messages. Version 0.6 ----------- * Recognise and ignore the #pragma cpp directive. * Fix beginning-of-file bug, where in --noline mode, a # cpp directive at the top of the file appeared in the output. * Fix chained parenthesised boolean exprs in #if, e.g. #if ( foo ) && ( bar ) * Fix precedence in chained unparenthesised boolean exprs in #if, e.g. #if foo && bar || baz && frob * For better compatibility with cpp, and because otherwise there are certain constructs that cannot be expressed, we no longer permit whitespace in a #define between the symbolname and an opening parenthesis, e.g. #define f (f' id) Previously, this was interpreted as a parametrised macro, with arguments in the parens, and no expansion. Now, the space indicates that this is a textual replacement, and the parenthesised expression is in fact the replacement. Version 0.5 ----------- * Added a --version flag to report the version number. * Renamed --stringise to --hashes, and use it to turn on ## catenation as well. * Bugfix for #if 1, previously taken as false. * Bugfix for --nolines: it no longer adds extra spurious newlines. * File inclusion now looks in the directory of the calling file. * Failure to find an include file is now merely a warning to stderr rather than an error. * Added a --layout flag. Previously, line continuations in a macro definition were always preserved in the output, permitting use of the Haskell layout rule even inside a macro. The default is now to remove line continuations for conformance with cpp, but the option of using --layout is still possible. Version 0.4 ----------- * New flag -Ofile to redirect output * Bugfix for precedence in #if !False && False * Bugfix for whitespace between # and if * Bugfix for #define F "blah"; #include F Version 0.3 ----------- * Bugfix for recursive macro expansion. * New flag --strip to remove C comments even outside cpp directives. * New flag --stringise to recognise the # stringise operator in macros. Version 0.2 ----------- * New flag --noline to eliminate #line directives from output. * Add symbol-replacement and macro-expansion. * New flag --nomacro to turn off symbol/macro-expansion. 2004-Apr-21 ----------- * Now accept multi-line # commands via the \ line continuation operator. The original file line numbering is preserved in the output by some tricky acrobatics. Version 0.1 ----------- * Initial release. cpphs-1.20.8/LICENCE-GPL0000644000000000000000000004311113122646212012511 0ustar0000000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 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 convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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 Library General Public License instead of this License. cpphs-1.20.8/LICENCE-LGPL0000644000000000000000000006363313122646212012640 0ustar0000000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cpphs-1.20.8/LICENCE-commercial0000644000000000000000000000250313122646212014202 0ustar0000000000000000Commercial licence for cpphs. Copyright 2004-2010, Malcolm Wallace (malcolm.wallace@me.com) All rights reserved. * This software, built from original unmodified sources, may be used for any purpose whatsoever, without restriction. * Redistribution in binary form, without modification, is permitted provided that the above copyright notice, these conditions and the following disclaimer are reproduced in the documentation and/or other materials provided with the distribution. * Redistribution in source form, with or without modification, is not permitted under this license. THIS SOFTWARE IS PROVIDED BY Malcolm Wallace AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Malcolm Wallace OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cpphs-1.20.8/README0000644000000000000000000000273113122646212011767 0ustar0000000000000000This directory contains 'cpphs', a simplified but robust re-implementation of cpp, the C pre-processor, in Haskell. TO BUILD -------- Just use hmake cpphs [-package base] or ghc --make cpphs [-o cpphs] # -o needed for ghc <= 6.4.1 ] or runhugs cpphs # or rename the script cpphs.hugs to cpphs USAGE ----- cpphs [filename | -Dsym | -Dsym=val | -Ipath]+ [-Ofile] [ --include=file ]* [ --nomacro | --noline | --nowarn | --strip | --strip-eol | --pragma | --text | --hashes | --layout | --unlit | --linepragma ]* [ --cpp compatopts ] For fuller details, see docs/index.html If you want to use cpphs as a completely drop-in replacement for the real cpp, that is, to accept the same arguments, and have broadly the same behaviour in response to them, then use the --cpp compatibility option. COPYRIGHT --------- Copyright (c) 2004-2017 Malcolm Wallace (Malcolm.Wallace@me.com) LICENCE ------- These library modules are distributed under the terms of the LGPL. The application module 'cpphs.hs' is GPL. This software comes with no warranty. Use at your own risk. If you have a commercial use for cpphs, and feel the terms of the (L)GPL are too onerous, you have the option of distributing unmodified binaries (only, not sources) under the terms of a different licence (see LICENCE-commercial). WEBSITE ------- http://projects.haskell.org/cpphs/ darcs get http://code.haskell.org/~malcolm/cpphs cpphs-1.20.8/Setup.hs0000644000000000000000000000005613122646212012541 0ustar0000000000000000import Distribution.Simple main = defaultMain cpphs-1.20.8/cpphs.cabal0000644000000000000000000000444013122646212013207 0ustar0000000000000000Name: cpphs Version: 1.20.8 Copyright: 2004-2017, Malcolm Wallace License: LGPL License-File: LICENCE-LGPL Cabal-Version: >= 1.6 Author: Malcolm Wallace Maintainer: Malcolm Wallace Homepage: http://projects.haskell.org/cpphs/ bug-reports: https://github.com/malcolmwallace/cpphs/issues Synopsis: A liberalised re-implementation of cpp, the C pre-processor. Description: Cpphs is a re-implementation of the C pre-processor that is both more compatible with Haskell, and itself written in Haskell so that it can be distributed with compilers. . This version of the C pre-processor is pretty-much feature-complete and compatible with traditional (K&R) pre-processors. Additional features include: a plain-text mode; an option to unlit literate code files; and an option to turn off macro-expansion. Category: Development Build-type: Simple Extra-Source-Files: README, LICENCE-GPL, LICENCE-commercial, CHANGELOG, docs/cpphs.1, docs/index.html Library Build-Depends: base>3&&<6, old-locale, old-time, directory, polyparse>=1.9 Exposed-Modules: Language.Preprocessor.Cpphs Language.Preprocessor.Unlit Other-Modules: Language.Preprocessor.Cpphs.CppIfdef Language.Preprocessor.Cpphs.HashDefine Language.Preprocessor.Cpphs.MacroPass Language.Preprocessor.Cpphs.Options Language.Preprocessor.Cpphs.Position Language.Preprocessor.Cpphs.ReadFirst Language.Preprocessor.Cpphs.RunCpphs Language.Preprocessor.Cpphs.SymTab Language.Preprocessor.Cpphs.Tokenise Executable cpphs Build-Depends: base>3&&<6, old-locale, old-time, directory, polyparse>=1.9 Main-Is: cpphs.hs Other-Modules: Language.Preprocessor.Cpphs Language.Preprocessor.Unlit Language.Preprocessor.Cpphs.CppIfdef Language.Preprocessor.Cpphs.HashDefine Language.Preprocessor.Cpphs.MacroPass Language.Preprocessor.Cpphs.Options Language.Preprocessor.Cpphs.Position Language.Preprocessor.Cpphs.ReadFirst Language.Preprocessor.Cpphs.RunCpphs Language.Preprocessor.Cpphs.SymTab Language.Preprocessor.Cpphs.Tokenise Source-Repository head Type: darcs Location: http://code.haskell.org/cpphs cpphs-1.20.8/cpphs.hs0000644000000000000000000001066413122646212012564 0ustar0000000000000000{- -- The main program wrapper for cpphs, a simple C pre-processor -- written in Haskell. -- Author: Malcolm Wallace, 2004 -- This file is licensed under the GPL. Note however, that all other -- modules used by it are either distributed under the LGPL, or are Haskell'98. -- -- Thus, when compiled as a standalone executable, this program will fall -- under the GPL. -} module Main where import System.Environment ( getArgs, getProgName) import System.Exit ( exitWith, ExitCode(..) ) import Data.Maybe import Language.Preprocessor.Cpphs ( runCpphs, CpphsOptions(..), parseOptions ) import Language.Preprocessor.Cpphs.ReadFirst ( readFileUTF8, writeFileUTF8 ) import System.IO ( stdin, stdout, stderr, hFlush, hSetEncoding, utf8 ) import Control.Monad ( when ) import Data.List ( isPrefixOf ) version :: String version = "1.20.8" main :: IO () main = do hSetEncoding stdin utf8 hSetEncoding stdout utf8 hSetEncoding stderr utf8 args <- getArgs args <- return $ if "--cpp" `elem` args then convertArgs args else args prog <- getProgName when ("--version" `elem` args) (do putStrLn (prog++" "++version) exitWith ExitSuccess) when ("--help" `elem` args) (do putStrLn ("Usage: "++prog ++" [file ...] [ -Dsym | -Dsym=val | -Ipath ]* [-Ofile]\n" ++"\t\t[--nomacro] [--noline] [--linepragma] [--pragma] [--text]\n" ++"\t\t[--strip] [--strip-eol] [--hashes] [--layout] [--unlit]\n" ++"\t\t[ --cpp std-cpp-options ] [--include=filename]") exitWith ExitSuccess) let parsedArgs = parseOptions args options = fromRight parsedArgs ins = infiles options outs = outfiles options out = listToMaybe outs when (isLeft parsedArgs) (do putStrLn $ "Unknown option "++fromLeft parsedArgs ++", for valid options try "++prog++" --help\n" exitWith (ExitFailure 1)) when (length outs > 1) (do putStrLn $ "At most one output file (-O) can be specified" exitWith (ExitFailure 2)) if null ins then execute options out Nothing else mapM_ (execute options out) (map Just ins) -- | Execute the preprocessor. -- If the filepath is Nothing then default to stdout\/stdin as appropriate. execute :: CpphsOptions -> Maybe FilePath -> Maybe FilePath -> IO () execute opts ofile infile = let (filename, readIt) = case infile of Just x -> (x, readFileUTF8 x) Nothing -> ("stdin", getContents) output Nothing x = do putStr x; hFlush stdout output (Just f) x = writeFileUTF8 f x in do contents <- readIt transformed <- runCpphs opts filename contents output ofile transformed isLeft (Left _) = True isLeft _ = False fromLeft (Left x) = x fromRight (Right x) = x -- | Convert commandline options to remain compatible with cpp. -- Based on a shell script cpphs.compat data ConvertArgs = ConvertArgs { traditional, strip :: Bool , infile, outfile :: String } convertArgs :: [String] -> [String] convertArgs xs = f (ConvertArgs False True "-" "-") xs where flg = "DUI" f e (['-',r]:x:xs) | r `elem` flg = ('-':r:x) : f e xs f e (x@('-':r:_):xs) | r `elem` flg = x : f e xs f e ("-o":x:xs) = ('-':'O':x) : f e xs f e (('-':'o':x):xs) = ('-':'O':drop 2 x) : f e xs f e (('-':x):xs) | "ansi" `isPrefixOf` x = f e{traditional=False} xs | "traditional" `isPrefixOf` x = f e{traditional=True} xs | "std" `isPrefixOf` x = f e xs -- ignore language spec f e ("-x":x:xs) = f e xs -- ignore language spec f e ("-include":x:xs) = ("--include="++x) : f e xs f e ("-P":xs) = "--noline" : f e xs f e (x:xs) | x == "-C" || x == "-CC" = f e{strip=False} xs f e ("-A":x:xs) = f e xs -- strip assertions f e ("--help":xs) = "--help" : f e xs f e ("--version":xs) = "--version" : f e xs f e ("-version":xs) = "--version" : f e xs f e (('-':x):xs) = f e xs -- strip all other flags f e (x:xs) = f (if infile e == "-" then e{infile=x} else e{outfile=x}) xs f e [] = ["--hashes" | not (traditional e)] ++ ["--strip" | traditional e && strip e] ++ ["--strip-eol" | not (traditional e) && strip e] ++ [infile e] ++ ["-O" ++ outfile e | outfile e /= "-"] cpphs-1.20.8/Language/0000755000000000000000000000000013122646212012627 5ustar0000000000000000cpphs-1.20.8/Language/Preprocessor/0000755000000000000000000000000013122646212015315 5ustar0000000000000000cpphs-1.20.8/Language/Preprocessor/Cpphs.hs0000644000000000000000000000262713122646212016735 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Language.Preprocessor.Cpphs -- Copyright : 2000-2006 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- Include the interface that is exported ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs ( runCpphs, runCpphsPass1, runCpphsPass2, runCpphsReturningSymTab , cppIfdef, tokenise, WordStyle(..) , macroPass, macroPassReturningSymTab , CpphsOptions(..), BoolOptions(..) , parseOptions, defaultCpphsOptions, defaultBoolOptions , module Language.Preprocessor.Cpphs.Position ) where import Language.Preprocessor.Cpphs.CppIfdef(cppIfdef) import Language.Preprocessor.Cpphs.MacroPass(macroPass ,macroPassReturningSymTab) import Language.Preprocessor.Cpphs.RunCpphs(runCpphs ,runCpphsPass1 ,runCpphsPass2 ,runCpphsReturningSymTab) import Language.Preprocessor.Cpphs.Options (CpphsOptions(..), BoolOptions(..), parseOptions ,defaultCpphsOptions,defaultBoolOptions) import Language.Preprocessor.Cpphs.Position import Language.Preprocessor.Cpphs.Tokenise cpphs-1.20.8/Language/Preprocessor/Unlit.hs0000644000000000000000000000677313122646212016761 0ustar0000000000000000-- | Part of this code is from "Report on the Programming Language Haskell", -- version 1.2, appendix C. module Language.Preprocessor.Unlit (unlit) where import Data.Char import Data.List (isPrefixOf) data Classified = Program String | Blank | Comment | Include Int String | Pre String classify :: [String] -> [Classified] classify [] = [] classify (('\\':x):xs) | x == "begin{code}" = Blank : allProg xs where allProg [] = [] -- Should give an error message, -- but I have no good position information. allProg (('\\':x):xs) | "end{code}"`isPrefixOf`x = Blank : classify xs allProg (x:xs) = Program x:allProg xs classify (('>':x):xs) = Program (' ':x) : classify xs classify (('#':x):xs) = (case words x of (line:rest) | all isDigit line -> Include (read line) (unwords rest) _ -> Pre x ) : classify xs --classify (x:xs) | "{-# LINE" `isPrefixOf` x = Program x: classify xs classify (x:xs) | all isSpace x = Blank:classify xs classify (x:xs) = Comment:classify xs unclassify :: Classified -> String unclassify (Program s) = s unclassify (Pre s) = '#':s unclassify (Include i f) = '#':' ':show i ++ ' ':f unclassify Blank = "" unclassify Comment = "" -- | 'unlit' takes a filename (for error reports), and transforms the -- given string, to eliminate the literate comments from the program text. unlit :: FilePath -> String -> String unlit file lhs = (unlines . map unclassify . adjacent file (0::Int) Blank . classify) (inlines lhs) adjacent :: FilePath -> Int -> Classified -> [Classified] -> [Classified] adjacent file 0 _ (x :xs) = x : adjacent file 1 x xs -- force evaluation of line number adjacent file n y@(Program _) (x@Comment :xs) = error (message file n "program" "comment") adjacent file n y@(Program _) (x@(Include i f):xs) = x: adjacent f i y xs adjacent file n y@(Program _) (x@(Pre _) :xs) = x: adjacent file (n+1) y xs adjacent file n y@Comment (x@(Program _) :xs) = error (message file n "comment" "program") adjacent file n y@Comment (x@(Include i f):xs) = x: adjacent f i y xs adjacent file n y@Comment (x@(Pre _) :xs) = x: adjacent file (n+1) y xs adjacent file n y@Blank (x@(Include i f):xs) = x: adjacent f i y xs adjacent file n y@Blank (x@(Pre _) :xs) = x: adjacent file (n+1) y xs adjacent file n _ (x@next :xs) = x: adjacent file (n+1) x xs adjacent file n _ [] = [] message :: String -> Int -> String -> String -> String message "\"\"" n p c = "Line "++show n++": "++p++ " line before "++c++" line.\n" message [] n p c = "Line "++show n++": "++p++ " line before "++c++" line.\n" message file n p c = "In file " ++ file ++ " at line "++show n++": "++p++ " line before "++c++" line.\n" -- Re-implementation of 'lines', for better efficiency (but decreased laziness). -- Also, importantly, accepts non-standard DOS and Mac line ending characters. inlines :: String -> [String] inlines s = lines' s id where lines' [] acc = [acc []] lines' ('\^M':'\n':s) acc = acc [] : lines' s id -- DOS lines' ('\^M':s) acc = acc [] : lines' s id -- MacOS lines' ('\n':s) acc = acc [] : lines' s id -- Unix lines' (c:s) acc = lines' s (acc . (c:)) cpphs-1.20.8/Language/Preprocessor/Cpphs/0000755000000000000000000000000013122646212016372 5ustar0000000000000000cpphs-1.20.8/Language/Preprocessor/Cpphs/CppIfdef.hs0000644000000000000000000003722013122646212020412 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : CppIfdef -- Copyright : 1999-2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- Perform a cpp.first-pass, gathering \#define's and evaluating \#ifdef's. -- and \#include's. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.CppIfdef ( cppIfdef -- :: FilePath -> [(String,String)] -> [String] -> Options -- -> String -> IO [(Posn,String)] ) where import Text.Parse import Language.Preprocessor.Cpphs.SymTab import Language.Preprocessor.Cpphs.Position (Posn,newfile,newline,newlines ,cppline,cpp2hask,newpos) import Language.Preprocessor.Cpphs.ReadFirst (readFirst) import Language.Preprocessor.Cpphs.Tokenise (linesCpp,reslash) import Language.Preprocessor.Cpphs.Options (BoolOptions(..)) import Language.Preprocessor.Cpphs.HashDefine(HashDefine(..),parseHashDefine ,expandMacro) import Language.Preprocessor.Cpphs.MacroPass (preDefine,defineMacro) import Data.Char (isDigit,isSpace,isAlphaNum) import Data.List (intercalate,isPrefixOf) import Numeric (readHex,readOct,readDec) import System.IO.Unsafe (unsafeInterleaveIO) import System.IO (hPutStrLn,stderr) import Control.Monad (when) -- | Run a first pass of cpp, evaluating \#ifdef's and processing \#include's, -- whilst taking account of \#define's and \#undef's as we encounter them. cppIfdef :: FilePath -- ^ File for error reports -> [(String,String)] -- ^ Pre-defined symbols and their values -> [String] -- ^ Search path for \#includes -> BoolOptions -- ^ Options controlling output style -> String -- ^ The input file content -> IO [(Posn,String)] -- ^ The file after processing (in lines) cppIfdef fp syms search options = cpp posn defs search options (Keep []) . initial . linesCpp where posn = newfile fp defs = preDefine options syms initial = if literate options then id else (cppline posn:) -- Previous versions had a very simple symbol table mapping strings -- to strings. Now the #ifdef pass uses a more elaborate table, in -- particular to deal with parameterised macros in conditionals. -- | Internal state for whether lines are being kept or dropped. -- In @Drop n b ps@, @n@ is the depth of nesting, @b@ is whether -- we have already succeeded in keeping some lines in a chain of -- @elif@'s, and @ps@ is the stack of positions of open @#if@ contexts, -- used for error messages in case EOF is reached too soon. data KeepState = Keep [Posn] | Drop Int Bool [Posn] -- | Return just the list of lines that the real cpp would decide to keep. cpp :: Posn -> SymTab HashDefine -> [String] -> BoolOptions -> KeepState -> [String] -> IO [(Posn,String)] cpp _ _ _ _ (Keep ps) [] | not (null ps) = do hPutStrLn stderr $ "Unmatched #if: positions of open context are:\n"++ unlines (map show ps) return [] cpp _ _ _ _ _ [] = return [] cpp p syms path options (Keep ps) (l@('#':x):xs) = let ws = words x cmd = if null ws then "" else head ws line = if null ws then [] else tail ws sym = if null line then "" else head line rest = if null line then [] else tail line def = defineMacro options (sym++" "++ maybe "1" id (un rest)) un v = if null v then Nothing else Just (unwords v) keepIf b = if b then Keep (p:ps) else Drop 1 False (p:ps) skipn syms' retain ud xs' = let n = 1 + length (filter (=='\n') l) in (if macros options && retain then emitOne (p,reslash l) else emitMany (replicate n (p,""))) $ cpp (newlines n p) syms' path options ud xs' in case cmd of "define" -> skipn (insertST def syms) True (Keep ps) xs "undef" -> skipn (deleteST sym syms) True (Keep ps) xs "ifndef" -> skipn syms False (keepIf (not (definedST sym syms))) xs "ifdef" -> skipn syms False (keepIf (definedST sym syms)) xs "if" -> do b <- gatherDefined p syms (unwords line) skipn syms False (keepIf b) xs "else" -> skipn syms False (Drop 1 False ps) xs "elif" -> skipn syms False (Drop 1 True ps) xs "endif" | null ps -> do hPutStrLn stderr $ "Unmatched #endif at "++show p return [] "endif" -> skipn syms False (Keep (tail ps)) xs "pragma" -> skipn syms True (Keep ps) xs ('!':_) -> skipn syms False (Keep ps) xs -- \#!runhs scripts "include"-> do (inc,content) <- readFirst (file syms (unwords line)) p path (warnings options) cpp p syms path options (Keep ps) (("#line 1 "++show inc): linesCpp content ++ cppline (newline p): xs) "warning"-> if warnings options then do hPutStrLn stderr (l++"\nin "++show p) skipn syms False (Keep ps) xs else skipn syms False (Keep ps) xs "error" -> error (l++"\nin "++show p) "line" | all isDigit sym -> (if locations options && hashline options then emitOne (p,l) else if locations options then emitOne (p,cpp2hask l) else id) $ cpp (newpos (read sym) (un rest) p) syms path options (Keep ps) xs n | all isDigit n && not (null n) -> (if locations options && hashline options then emitOne (p,l) else if locations options then emitOne (p,cpp2hask l) else id) $ cpp (newpos (read n) (un (tail ws)) p) syms path options (Keep ps) xs | otherwise -> do when (warnings options) $ hPutStrLn stderr ("Warning: unknown directive #"++n ++"\nin "++show p) emitOne (p,l) $ cpp (newline p) syms path options (Keep ps) xs cpp p syms path options (Drop n b ps) (('#':x):xs) = let ws = words x cmd = if null ws then "" else head ws delse | n==1 && b = Drop 1 b ps | n==1 = Keep ps | otherwise = Drop n b ps dend | n==1 = Keep (tail ps) | otherwise = Drop (n-1) b (tail ps) delif v | n==1 && not b && v = Keep ps | otherwise = Drop n b ps skipn ud xs' = let n' = 1 + length (filter (=='\n') x) in emitMany (replicate n' (p,"")) $ cpp (newlines n' p) syms path options ud xs' in if cmd == "ifndef" || cmd == "if" || cmd == "ifdef" then skipn (Drop (n+1) b (p:ps)) xs else if cmd == "elif" then do v <- gatherDefined p syms (unwords (tail ws)) skipn (delif v) xs else if cmd == "else" then skipn delse xs else if cmd == "endif" then if null ps then do hPutStrLn stderr $ "Unmatched #endif at "++show p return [] else skipn dend xs else skipn (Drop n b ps) xs -- define, undef, include, error, warning, pragma, line cpp p syms path options (Keep ps) (x:xs) = let p' = newline p in seq p' $ emitOne (p,x) $ cpp p' syms path options (Keep ps) xs cpp p syms path options d@(Drop _ _ _) (_:xs) = let p' = newline p in seq p' $ emitOne (p,"") $ cpp p' syms path options d xs -- | Auxiliary IO functions emitOne :: a -> IO [a] -> IO [a] emitMany :: [a] -> IO [a] -> IO [a] emitOne x io = do ys <- unsafeInterleaveIO io return (x:ys) emitMany xs io = do ys <- unsafeInterleaveIO io return (xs++ys) ---- gatherDefined :: Posn -> SymTab HashDefine -> String -> IO Bool gatherDefined p st inp = case runParser (preExpand st) inp of (Left msg, _) -> error ("Cannot expand #if directive in file "++show p ++":\n "++msg) (Right s, xs) -> do -- hPutStrLn stderr $ "Expanded #if at "++show p++" is:\n "++s when (any (not . isSpace) xs) $ hPutStrLn stderr ("Warning: trailing characters after #if" ++" macro expansion in file "++show p++": "++xs) case runParser parseBoolExp s of (Left msg, _) -> error ("Cannot parse #if directive in file "++show p ++":\n "++msg) (Right b, xs) -> do when (any (not . isSpace) xs && notComment xs) $ hPutStrLn stderr ("Warning: trailing characters after #if" ++" directive in file "++show p++": "++xs) return b notComment = not . ("//"`isPrefixOf`) . dropWhile isSpace -- | The preprocessor must expand all macros (recursively) before evaluating -- the conditional. preExpand :: SymTab HashDefine -> TextParser String preExpand st = do eof return "" <|> do a <- many1 (satisfy notIdent) commit $ pure (a++) `apply` preExpand st <|> do b <- expandSymOrCall st commit $ pure (b++) `apply` preExpand st -- | Expansion of symbols. expandSymOrCall :: SymTab HashDefine -> TextParser String expandSymOrCall st = do sym <- parseSym if sym=="defined" then do arg <- skip parseSym; convert sym [arg] <|> do arg <- skip $ parenthesis (do x <- skip parseSym; skip (return x)) convert sym [arg] <|> convert sym [] else ( do args <- parenthesis (commit $ fragment `sepBy` skip (isWord ",")) args' <- flip mapM args $ \arg-> case runParser (preExpand st) arg of (Left msg, _) -> fail msg (Right s, _) -> return s convert sym args' <|> convert sym [] ) where fragment = many1 (satisfy (`notElem`",)")) convert "defined" [arg] = case lookupST arg st of Nothing | all isDigit arg -> return arg Nothing -> return "0" Just (a@AntiDefined{}) -> return "0" Just (a@SymbolReplacement{}) -> return "1" Just (a@MacroExpansion{}) -> return "1" convert sym args = case lookupST sym st of Nothing -> if null args then return sym else return "0" -- else fail (disp sym args++" is not a defined macro") Just (a@SymbolReplacement{}) -> do reparse (replacement a) return "" Just (a@MacroExpansion{}) -> do reparse (expandMacro a args False) return "" Just (a@AntiDefined{}) -> if null args then return sym else return "0" -- else fail (disp sym args++" explicitly undefined with -U") disp sym args = let len = length args chars = map (:[]) ['a'..'z'] in sym ++ if null args then "" else "("++intercalate "," (take len chars)++")" parseBoolExp :: TextParser Bool parseBoolExp = do a <- parseExp1 bs <- many (do skip (isWord "||") commit $ skip parseBoolExp) return $ foldr (||) a bs parseExp1 :: TextParser Bool parseExp1 = do a <- parseExp0 bs <- many (do skip (isWord "&&") commit $ skip parseExp1) return $ foldr (&&) a bs parseExp0 :: TextParser Bool parseExp0 = do skip (isWord "!") a <- commit $ parseExp0 return (not a) <|> do val1 <- parseArithExp1 op <- parseCmpOp val2 <- parseArithExp1 return (val1 `op` val2) <|> do sym <- parseArithExp1 case sym of 0 -> return False _ -> return True <|> do parenthesis (commit parseBoolExp) parseArithExp1 :: TextParser Integer parseArithExp1 = do val1 <- parseArithExp0 ( do op <- parseArithOp1 val2 <- parseArithExp1 return (val1 `op` val2) <|> return val1 ) <|> do parenthesis parseArithExp1 parseArithExp0 :: TextParser Integer parseArithExp0 = do val1 <- parseNumber ( do op <- parseArithOp0 val2 <- parseArithExp0 return (val1 `op` val2) <|> return val1 ) <|> do parenthesis parseArithExp0 parseNumber :: TextParser Integer parseNumber = fmap safeRead $ skip parseSym where safeRead s = case s of '0':'x':s' -> number readHex s' '0':'o':s' -> number readOct s' _ -> number readDec s number rd s = case rd s of [] -> 0 :: Integer ((n,_):_) -> n :: Integer parseCmpOp :: TextParser (Integer -> Integer -> Bool) parseCmpOp = do skip (isWord ">=") return (>=) <|> do skip (isWord ">") return (>) <|> do skip (isWord "<=") return (<=) <|> do skip (isWord "<") return (<) <|> do skip (isWord "==") return (==) <|> do skip (isWord "!=") return (/=) parseArithOp1 :: TextParser (Integer -> Integer -> Integer) parseArithOp1 = do skip (isWord "+") return (+) <|> do skip (isWord "-") return (-) parseArithOp0 :: TextParser (Integer -> Integer -> Integer) parseArithOp0 = do skip (isWord "*") return (*) <|> do skip (isWord "/") return (div) <|> do skip (isWord "%") return (rem) -- | Return the expansion of the symbol (if there is one). parseSymOrCall :: SymTab HashDefine -> TextParser String parseSymOrCall st = do sym <- skip parseSym args <- parenthesis (commit $ parseSymOrCall st `sepBy` skip (isWord ",")) return $ convert sym args <|> do sym <- skip parseSym return $ convert sym [] where convert sym args = case lookupST sym st of Nothing -> sym Just (a@SymbolReplacement{}) -> recursivelyExpand st (replacement a) Just (a@MacroExpansion{}) -> recursivelyExpand st (expandMacro a args False) Just (a@AntiDefined{}) -> name a recursivelyExpand :: SymTab HashDefine -> String -> String recursivelyExpand st inp = case runParser (parseSymOrCall st) inp of (Left msg, _) -> inp (Right s, _) -> s parseSym :: TextParser String parseSym = many1 (satisfy (\c-> isAlphaNum c || c`elem`"'`_")) `onFail` do xs <- allAsString fail $ "Expected an identifier, got \""++xs++"\"" notIdent :: Char -> Bool notIdent c = not (isAlphaNum c || c`elem`"'`_") skip :: TextParser a -> TextParser a skip p = many (satisfy isSpace) >> p -- | The standard "parens" parser does not work for us here. Define our own. parenthesis :: TextParser a -> TextParser a parenthesis p = do isWord "(" x <- p isWord ")" return x -- | Determine filename in \#include file :: SymTab HashDefine -> String -> String file st name = case name of ('"':ns) -> init ns ('<':ns) -> init ns _ -> let ex = recursivelyExpand st name in if ex == name then name else file st ex cpphs-1.20.8/Language/Preprocessor/Cpphs/HashDefine.hs0000644000000000000000000001275213122646212020733 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : HashDefine -- Copyright : 2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- What structures are declared in a \#define. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.HashDefine ( HashDefine(..) , ArgOrText(..) , expandMacro , parseHashDefine , simplifyHashDefines ) where import Data.Char (isSpace) import Data.List (intercalate) data HashDefine = LineDrop { name :: String } | Pragma { name :: String } | AntiDefined { name :: String , linebreaks :: Int } | SymbolReplacement { name :: String , replacement :: String , linebreaks :: Int } | MacroExpansion { name :: String , arguments :: [String] , expansion :: [(ArgOrText,String)] , linebreaks :: Int } deriving (Eq,Show) -- | 'smart' constructor to avoid warnings from ghc (undefined fields) symbolReplacement :: HashDefine symbolReplacement = SymbolReplacement { name=undefined, replacement=undefined, linebreaks=undefined } -- | Macro expansion text is divided into sections, each of which is classified -- as one of three kinds: a formal argument (Arg), plain text (Text), -- or a stringised formal argument (Str). data ArgOrText = Arg | Text | Str deriving (Eq,Show) -- | Expand an instance of a macro. -- Precondition: got a match on the macro name. expandMacro :: HashDefine -> [String] -> Bool -> String expandMacro macro parameters layout = let env = zip (arguments macro) parameters replace (Arg,s) = maybe ("") id (lookup s env) replace (Str,s) = maybe (str "") str (lookup s env) replace (Text,s) = if layout then s else filter (/='\n') s str s = '"':s++"\"" checkArity | length (arguments macro) == 1 && length parameters <= 1 || length (arguments macro) == length parameters = id | otherwise = error ("macro "++name macro++" expected "++ show (length (arguments macro))++ " arguments, but was given "++ show (length parameters)) in checkArity $ concatMap replace (expansion macro) -- | Parse a \#define, or \#undef, ignoring other \# directives parseHashDefine :: Bool -> [String] -> Maybe HashDefine parseHashDefine ansi def = (command . skip) def where skip xss@(x:xs) | all isSpace x = skip xs | otherwise = xss skip [] = [] command ("line":xs) = Just (LineDrop ("#line"++concat xs)) command ("pragma":xs) = Just (Pragma ("#pragma"++concat xs)) command ("define":xs) = Just (((define . skip) xs) { linebreaks=count def }) command ("undef":xs) = Just (((undef . skip) xs)) command _ = Nothing undef (sym:_) = AntiDefined { name=sym, linebreaks=0 } define (sym:xs) = case {-skip-} xs of ("(":ys) -> (macroHead sym [] . skip) ys ys -> symbolReplacement { name=sym , replacement = concatMap snd (classifyRhs [] (chop (skip ys))) } macroHead sym args (",":xs) = (macroHead sym args . skip) xs macroHead sym args (")":xs) = MacroExpansion { name =sym , arguments = reverse args , expansion = classifyRhs args (skip xs) , linebreaks = undefined } macroHead sym args (var:xs) = (macroHead sym (var:args) . skip) xs macroHead sym args [] = error ("incomplete macro definition:\n" ++" #define "++sym++"(" ++intercalate "," args) classifyRhs args ("#":x:xs) | ansi && x `elem` args = (Str,x): classifyRhs args xs classifyRhs args ("##":xs) | ansi = classifyRhs args xs classifyRhs args (s:"##":s':xs) | ansi && all isSpace s && all isSpace s' = classifyRhs args xs classifyRhs args (word:xs) | word `elem` args = (Arg,word): classifyRhs args xs | otherwise = (Text,word): classifyRhs args xs classifyRhs _ [] = [] count = length . filter (=='\n') . concat chop = reverse . dropWhile (all isSpace) . reverse -- | Pretty-print hash defines to a simpler format, as key-value pairs. simplifyHashDefines :: [HashDefine] -> [(String,String)] simplifyHashDefines = concatMap simp where simp hd@LineDrop{} = [] simp hd@Pragma{} = [] simp hd@AntiDefined{} = [] simp hd@SymbolReplacement{} = [(name hd, replacement hd)] simp hd@MacroExpansion{} = [(name hd++"("++intercalate "," (arguments hd) ++")" ,concatMap snd (expansion hd))] cpphs-1.20.8/Language/Preprocessor/Cpphs/MacroPass.hs0000644000000000000000000002161113122646212020617 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : MacroPass -- Copyright : 2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- Perform a cpp.second-pass, accumulating \#define's and \#undef's, -- whilst doing symbol replacement and macro expansion. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.MacroPass ( macroPass , preDefine , defineMacro , macroPassReturningSymTab ) where import Language.Preprocessor.Cpphs.HashDefine (HashDefine(..), expandMacro , simplifyHashDefines) import Language.Preprocessor.Cpphs.Tokenise (tokenise, WordStyle(..) , parseMacroCall) import Language.Preprocessor.Cpphs.SymTab (SymTab, lookupST, insertST , emptyST, flattenST) import Language.Preprocessor.Cpphs.Position (Posn, newfile, filename, lineno) import Language.Preprocessor.Cpphs.Options (BoolOptions(..)) import System.IO.Unsafe (unsafeInterleaveIO) import Control.Monad ((=<<)) import System.Time (getClockTime, toCalendarTime, formatCalendarTime) import System.Locale (defaultTimeLocale) noPos :: Posn noPos = newfile "preDefined" -- | Walk through the document, replacing calls of macros with the expanded RHS. macroPass :: [(String,String)] -- ^ Pre-defined symbols and their values -> BoolOptions -- ^ Options that alter processing style -> [(Posn,String)] -- ^ The input file content -> IO String -- ^ The file after processing macroPass syms options = fmap (safetail -- to remove extra "\n" inserted below . concat . onlyRights) . macroProcess (pragma options) (layout options) (lang options) (preDefine options syms) . tokenise (stripEol options) (stripC89 options) (ansi options) (lang options) . ((noPos,""):) -- ensure recognition of "\n#" at start of file where safetail [] = [] safetail (_:xs) = xs -- | auxiliary onlyRights :: [Either a b] -> [b] onlyRights = concatMap (\x->case x of Right t-> [t]; Left _-> [];) -- | Walk through the document, replacing calls of macros with the expanded RHS. -- Additionally returns the active symbol table after processing. macroPassReturningSymTab :: [(String,String)] -- ^ Pre-defined symbols and their values -> BoolOptions -- ^ Options that alter processing style -> [(Posn,String)] -- ^ The input file content -> IO (String,[(String,String)]) -- ^ The file and symbol table after processing macroPassReturningSymTab syms options = fmap (mapFst (safetail -- to remove extra "\n" inserted below . concat) . walk) . macroProcess (pragma options) (layout options) (lang options) (preDefine options syms) . tokenise (stripEol options) (stripC89 options) (ansi options) (lang options) . ((noPos,""):) -- ensure recognition of "\n#" at start of file where safetail [] = [] safetail (_:xs) = xs walk (Right x: rest) = let (xs, foo) = walk rest in (x:xs, foo) walk (Left x: []) = ( [] , simplifyHashDefines (flattenST x) ) walk (Left x: rest) = walk rest mapFst f (a,b) = (f a, b) -- | Turn command-line definitions (from @-D@) into 'HashDefine's. preDefine :: BoolOptions -> [(String,String)] -> SymTab HashDefine preDefine options defines = foldr (insertST . defineMacro options . (\ (s,d)-> s++" "++d)) emptyST defines -- | Turn a string representing a macro definition into a 'HashDefine'. defineMacro :: BoolOptions -> String -> (String,HashDefine) defineMacro opts s = let (Cmd (Just hd):_) = tokenise True True (ansi opts) (lang opts) [(noPos,"\n#define "++s++"\n")] in (name hd, hd) -- | Trundle through the document, one word at a time, using the WordStyle -- classification introduced by 'tokenise' to decide whether to expand a -- word or macro. Encountering a \#define or \#undef causes that symbol to -- be overwritten in the symbol table. Any other remaining cpp directives -- are discarded and replaced with blanks, except for \#line markers. -- All valid identifiers are checked for the presence of a definition -- of that name in the symbol table, and if so, expanded appropriately. -- (Bool arguments are: keep pragmas? retain layout? haskell language?) -- The result lazily intersperses output text with symbol tables. Lines -- are emitted as they are encountered. A symbol table is emitted after -- each change to the defined symbols, and always at the end of processing. macroProcess :: Bool -> Bool -> Bool -> SymTab HashDefine -> [WordStyle] -> IO [Either (SymTab HashDefine) String] macroProcess _ _ _ st [] = return [Left st] macroProcess p y l st (Other x: ws) = emit x $ macroProcess p y l st ws macroProcess p y l st (Cmd Nothing: ws) = emit "\n" $ macroProcess p y l st ws macroProcess p y l st (Cmd (Just (LineDrop x)): ws) = emit "\n" $ emit x $ macroProcess p y l st ws macroProcess pragma y l st (Cmd (Just (Pragma x)): ws) | pragma = emit "\n" $ emit x $ macroProcess pragma y l st ws | otherwise = emit "\n" $ macroProcess pragma y l st ws macroProcess p layout lang st (Cmd (Just hd): ws) = let n = 1 + linebreaks hd newST = insertST (name hd, hd) st in emit (replicate n '\n') $ emitSymTab newST $ macroProcess p layout lang newST ws macroProcess pr layout lang st (Ident p x: ws) = case x of "__FILE__" -> emit (show (filename p))$ macroProcess pr layout lang st ws "__LINE__" -> emit (show (lineno p)) $ macroProcess pr layout lang st ws "__DATE__" -> do w <- return . formatCalendarTime defaultTimeLocale "\"%d %b %Y\"" =<< toCalendarTime =<< getClockTime emit w $ macroProcess pr layout lang st ws "__TIME__" -> do w <- return . formatCalendarTime defaultTimeLocale "\"%H:%M:%S\"" =<< toCalendarTime =<< getClockTime emit w $ macroProcess pr layout lang st ws _ -> case lookupST x st of Nothing -> emit x $ macroProcess pr layout lang st ws Just hd -> case hd of AntiDefined {name=n} -> emit n $ macroProcess pr layout lang st ws SymbolReplacement {replacement=r} -> let r' = if layout then r else filter (/='\n') r in -- one-level expansion only: -- emit r' $ macroProcess layout st ws -- multi-level expansion: macroProcess pr layout lang st (tokenise True True False lang [(p,r')] ++ ws) MacroExpansion {} -> case parseMacroCall p ws of Nothing -> emit x $ macroProcess pr layout lang st ws Just (args,ws') -> if length args /= length (arguments hd) then emit x $ macroProcess pr layout lang st ws else do args' <- mapM (fmap (concat.onlyRights) . macroProcess pr layout lang st) args -- one-level expansion only: -- emit (expandMacro hd args' layout) $ -- macroProcess layout st ws' -- multi-level expansion: macroProcess pr layout lang st (tokenise True True False lang [(p,expandMacro hd args' layout)] ++ ws') -- | Useful helper function. emit :: a -> IO [Either b a] -> IO [Either b a] emit x io = do xs <- unsafeInterleaveIO io return (Right x:xs) -- | Useful helper function. emitSymTab :: b -> IO [Either b a] -> IO [Either b a] emitSymTab x io = do xs <- unsafeInterleaveIO io return (Left x:xs) cpphs-1.20.8/Language/Preprocessor/Cpphs/Options.hs0000644000000000000000000001344013122646212020363 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Options -- Copyright : 2006 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- This module deals with Cpphs options and parsing them ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.Options ( CpphsOptions(..) , BoolOptions(..) , parseOptions , defaultCpphsOptions , defaultBoolOptions , trailing ) where import Data.Maybe import Data.List (isPrefixOf) -- | Cpphs options structure. data CpphsOptions = CpphsOptions { infiles :: [FilePath] , outfiles :: [FilePath] , defines :: [(String,String)] , includes :: [String] , preInclude:: [FilePath] -- ^ Files to \#include before anything else , boolopts :: BoolOptions } deriving (Show) -- | Default options. defaultCpphsOptions :: CpphsOptions defaultCpphsOptions = CpphsOptions { infiles = [], outfiles = [] , defines = [], includes = [] , preInclude = [] , boolopts = defaultBoolOptions } -- | Options representable as Booleans. data BoolOptions = BoolOptions { macros :: Bool -- ^ Leave \#define and \#undef in output of ifdef? , locations :: Bool -- ^ Place \#line droppings in output? , hashline :: Bool -- ^ Write \#line or {-\# LINE \#-} ? , pragma :: Bool -- ^ Keep \#pragma in final output? , stripEol :: Bool -- ^ Remove C eol (\/\/) comments everywhere? , stripC89 :: Bool -- ^ Remove C inline (\/**\/) comments everywhere? , lang :: Bool -- ^ Lex input as Haskell code? , ansi :: Bool -- ^ Permit stringise \# and catenate \#\# operators? , layout :: Bool -- ^ Retain newlines in macro expansions? , literate :: Bool -- ^ Remove literate markup? , warnings :: Bool -- ^ Issue warnings? } deriving (Show) -- | Default settings of boolean options. defaultBoolOptions :: BoolOptions defaultBoolOptions = BoolOptions { macros = True, locations = True , hashline = True, pragma = False , stripEol = False, stripC89 = False , lang = True, ansi = False , layout = False, literate = False , warnings = True } -- | Raw command-line options. This is an internal intermediate data -- structure, used during option parsing only. data RawOption = NoMacro | NoLine | LinePragma | Pragma | Text | Strip | StripEol | Ansi | Layout | Unlit | SuppressWarnings | Macro (String,String) | Path String | PreInclude FilePath | IgnoredForCompatibility deriving (Eq, Show) flags :: [(String, RawOption)] flags = [ ("--nomacro", NoMacro) , ("--noline", NoLine) , ("--linepragma", LinePragma) , ("--pragma", Pragma) , ("--text", Text) , ("--strip", Strip) , ("--strip-eol", StripEol) , ("--hashes", Ansi) , ("--layout", Layout) , ("--unlit", Unlit) , ("--nowarn", SuppressWarnings) ] -- | Parse a single raw command-line option. Parse failure is indicated by -- result Nothing. rawOption :: String -> Maybe RawOption rawOption x | isJust a = a where a = lookup x flags rawOption ('-':'D':xs) = Just $ Macro (s, if null d then "1" else tail d) where (s,d) = break (=='=') xs rawOption ('-':'U':xs) = Just $ IgnoredForCompatibility rawOption ('-':'I':xs) = Just $ Path $ trailing "/\\" xs rawOption xs | "--include="`isPrefixOf`xs = Just $ PreInclude (drop 10 xs) rawOption _ = Nothing -- | Trim trailing elements of the second list that match any from -- the first list. Typically used to remove trailing forward\/back -- slashes from a directory path. trailing :: (Eq a) => [a] -> [a] -> [a] trailing xs = reverse . dropWhile (`elem`xs) . reverse -- | Convert a list of RawOption to a BoolOptions structure. boolOpts :: [RawOption] -> BoolOptions boolOpts opts = BoolOptions { macros = not (NoMacro `elem` opts) , locations = not (NoLine `elem` opts) , hashline = not (LinePragma `elem` opts) , pragma = Pragma `elem` opts , stripEol = StripEol`elem` opts , stripC89 = StripEol`elem` opts || Strip `elem` opts , lang = not (Text `elem` opts) , ansi = Ansi `elem` opts , layout = Layout `elem` opts , literate = Unlit `elem` opts , warnings = not (SuppressWarnings `elem` opts) } -- | Parse all command-line options. parseOptions :: [String] -> Either String CpphsOptions parseOptions xs = f ([], [], []) xs where f (opts, ins, outs) (('-':'O':x):xs) = f (opts, ins, x:outs) xs f (opts, ins, outs) (x@('-':_):xs) = case rawOption x of Nothing -> Left x Just a -> f (a:opts, ins, outs) xs f (opts, ins, outs) (x:xs) = f (opts, normalise x:ins, outs) xs f (opts, ins, outs) [] = Right CpphsOptions { infiles = reverse ins , outfiles = reverse outs , defines = [ x | Macro x <- reverse opts ] , includes = [ x | Path x <- reverse opts ] , preInclude=[ x | PreInclude x <- reverse opts ] , boolopts = boolOpts opts } normalise ('/':'/':filepath) = normalise ('/':filepath) normalise (x:filepath) = x:normalise filepath normalise [] = [] cpphs-1.20.8/Language/Preprocessor/Cpphs/Position.hs0000644000000000000000000000700613122646212020535 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Position -- Copyright : 2000-2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- Simple file position information, with recursive inclusion points. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.Position ( Posn(..) , newfile , addcol, newline, tab, newlines, newpos , cppline, haskline, cpp2hask , filename, lineno, directory , cleanPath ) where import Data.List (isPrefixOf) -- | Source positions contain a filename, line, column, and an -- inclusion point, which is itself another source position, -- recursively. data Posn = Pn String !Int !Int (Maybe Posn) deriving (Eq) instance Show Posn where showsPrec _ (Pn f l c i) = showString f . showString " at line " . shows l . showString " col " . shows c . ( case i of Nothing -> id Just p -> showString "\n used by " . shows p ) -- | Constructor. Argument is filename. newfile :: String -> Posn newfile name = Pn (cleanPath name) 1 1 Nothing -- | Increment column number by given quantity. addcol :: Int -> Posn -> Posn addcol n (Pn f r c i) = Pn f r (c+n) i -- | Increment row number, reset column to 1. newline :: Posn -> Posn --newline (Pn f r _ i) = Pn f (r+1) 1 i newline (Pn f r _ i) = let r' = r+1 in r' `seq` Pn f r' 1 i -- | Increment column number, tab stops are every 8 chars. tab :: Posn -> Posn tab (Pn f r c i) = Pn f r (((c`div`8)+1)*8) i -- | Increment row number by given quantity. newlines :: Int -> Posn -> Posn newlines n (Pn f r _ i) = Pn f (r+n) 1 i -- | Update position with a new row, and possible filename. newpos :: Int -> Maybe String -> Posn -> Posn newpos r Nothing (Pn f _ c i) = Pn f r c i newpos r (Just ('"':f)) (Pn _ _ c i) = Pn (init f) r c i newpos r (Just f) (Pn _ _ c i) = Pn f r c i -- | Project the line number. lineno :: Posn -> Int -- | Project the filename. filename :: Posn -> String -- | Project the directory of the filename. directory :: Posn -> FilePath lineno (Pn _ r _ _) = r filename (Pn f _ _ _) = f directory (Pn f _ _ _) = dirname f -- | cpp-style printing of file position cppline :: Posn -> String cppline (Pn f r _ _) = "#line "++show r++" "++show f -- | haskell-style printing of file position haskline :: Posn -> String haskline (Pn f r _ _) = "{-# LINE "++show r++" "++show f++" #-}" -- | Conversion from a cpp-style "#line" to haskell-style pragma. cpp2hask :: String -> String cpp2hask line | "#line" `isPrefixOf` line = "{-# LINE " ++unwords (tail (words line)) ++" #-}" | otherwise = line -- | Strip non-directory suffix from file name (analogous to the shell -- command of the same name). dirname :: String -> String dirname = reverse . safetail . dropWhile (not.(`elem`"\\/")) . reverse where safetail [] = [] safetail (_:x) = x -- | Sigh. Mixing Windows filepaths with unix is bad. Make sure there is a -- canonical path separator. cleanPath :: FilePath -> FilePath cleanPath [] = [] cleanPath ('\\':cs) = '/': cleanPath cs cleanPath (c:cs) = c: cleanPath cs cpphs-1.20.8/Language/Preprocessor/Cpphs/ReadFirst.hs0000644000000000000000000000617313122646212020620 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : ReadFirst -- Copyright : 2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- Read the first file that matches in a list of search paths. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.ReadFirst ( readFirst , readFileUTF8 , writeFileUTF8 ) where import System.IO import System.Directory (doesFileExist) import Data.List (intersperse) import Control.Exception as E import Control.Monad (when) import Language.Preprocessor.Cpphs.Position (Posn,directory,cleanPath) -- | Attempt to read the given file from any location within the search path. -- The first location found is returned, together with the file content. -- (The directory of the calling file is always searched first, then -- the current directory, finally any specified search path.) readFirst :: String -- ^ filename -> Posn -- ^ inclusion point -> [String] -- ^ search path -> Bool -- ^ report warnings? -> IO ( FilePath , String ) -- ^ discovered filepath, and file contents readFirst name demand path warn = case name of -- Windows drive in absolute path c:':':'\\':nm-> try nm (Just (c:':':[])) [""] c:':':'/':nm -> try nm (Just (c:':':[])) [""] -- Windows drive in relative path c:':':nm -> try nm (Just (c:':':[])) (cons dd (".":path)) -- unix-like absolute path '/':nm -> try nm Nothing [""] -- any relative path _ -> try name Nothing (cons dd (".":path)) where dd = directory demand cons x xs = if null x then xs else x:xs try name _ [] = do when warn $ hPutStrLn stderr ("Warning: Can't find file \""++name ++"\" in directories\n\t" ++concat (intersperse "\n\t" (cons dd (".":path))) ++"\n Asked for by: "++show demand) return ("missing file: "++name,"") try name drive (p:ps) = do let file = (maybe id (++) drive) $ cleanPath p++'/':cleanPath name ok <- doesFileExist file if not ok then try name drive ps else do content <- readFileUTF8 file return (file,content) readFileUTF8 :: FilePath -> IO String readFileUTF8 file = do h <- openFile file ReadMode (do utf8r <- mkTextEncoding "UTF-8//ROUNDTRIP" hSetEncoding h utf8r hGetContents h) `E.onException` (hClose h) writeFileUTF8 :: FilePath -> String -> IO () writeFileUTF8 f txt = withFile f WriteMode $ \hdl-> do utf8r <- mkTextEncoding "UTF-8//ROUNDTRIP" hSetEncoding hdl utf8r hPutStr hdl txt `E.onException` (hClose hdl) cpphs-1.20.8/Language/Preprocessor/Cpphs/RunCpphs.hs0000644000000000000000000000740613122646212020477 0ustar0000000000000000{- -- The main program for cpphs, a simple C pre-processor written in Haskell. -- Copyright (c) 2004 Malcolm Wallace -- This file is LGPL (relicensed from the GPL by Malcolm Wallace, October 2011). -} module Language.Preprocessor.Cpphs.RunCpphs ( runCpphs , runCpphsPass1 , runCpphsPass2 , runCpphsReturningSymTab ) where import Language.Preprocessor.Cpphs.CppIfdef (cppIfdef) import Language.Preprocessor.Cpphs.MacroPass(macroPass,macroPassReturningSymTab) import Language.Preprocessor.Cpphs.Options (CpphsOptions(..), BoolOptions(..) ,trailing) import Language.Preprocessor.Cpphs.Tokenise (deWordStyle, tokenise) import Language.Preprocessor.Cpphs.Position (cleanPath, Posn) import Language.Preprocessor.Unlit as Unlit (unlit) runCpphs :: CpphsOptions -> FilePath -> String -> IO String runCpphs options filename input = do pass1 <- runCpphsPass1 options filename input runCpphsPass2 (boolopts options) (defines options) filename pass1 runCpphsPass1 :: CpphsOptions -> FilePath -> String -> IO [(Posn,String)] runCpphsPass1 options' filename input = do let options= options'{ includes= map (trailing "\\/") (includes options') } let bools = boolopts options preInc = case preInclude options of [] -> "" is -> concatMap (\f->"#include \""++f++"\"\n") is ++ "#line 1 \""++cleanPath filename++"\"\n" pass1 <- cppIfdef filename (defines options) (includes options) bools (preInc++input) return pass1 runCpphsPass2 :: BoolOptions -> [(String,String)] -> FilePath -> [(Posn,String)] -> IO String runCpphsPass2 bools defines filename pass1 = do pass2 <- macroPass defines bools pass1 let result= if not (macros bools) then if stripC89 bools || stripEol bools then concatMap deWordStyle $ tokenise (stripEol bools) (stripC89 bools) (ansi bools) (lang bools) pass1 else unlines (map snd pass1) else pass2 pass3 = if literate bools then Unlit.unlit filename else id return (pass3 result) runCpphsReturningSymTab :: CpphsOptions -> FilePath -> String -> IO (String,[(String,String)]) runCpphsReturningSymTab options' filename input = do let options= options'{ includes= map (trailing "\\/") (includes options') } let bools = boolopts options preInc = case preInclude options of [] -> "" is -> concatMap (\f->"#include \""++f++"\"\n") is ++ "#line 1 \""++cleanPath filename++"\"\n" (pass2,syms) <- if macros bools then do pass1 <- cppIfdef filename (defines options) (includes options) bools (preInc++input) macroPassReturningSymTab (defines options) bools pass1 else do pass1 <- cppIfdef filename (defines options) (includes options) bools{macros=True} (preInc++input) (_,syms) <- macroPassReturningSymTab (defines options) bools pass1 pass1 <- cppIfdef filename (defines options) (includes options) bools (preInc++input) let result = if stripC89 bools || stripEol bools then concatMap deWordStyle $ tokenise (stripEol bools) (stripC89 bools) (ansi bools) (lang bools) pass1 else init $ unlines (map snd pass1) return (result,syms) let pass3 = if literate bools then Unlit.unlit filename else id return (pass3 pass2, syms) cpphs-1.20.8/Language/Preprocessor/Cpphs/SymTab.hs0000644000000000000000000000515713122646212020135 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : SymTab -- Copyright : 2000-2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : Stable -- Portability : All -- -- Symbol Table, based on index trees using a hash on the key. -- Keys are always Strings. Stored values can be any type. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.SymTab ( SymTab , emptyST , insertST , deleteST , lookupST , definedST , flattenST , IndTree ) where -- | Symbol Table. Stored values are polymorphic, but the keys are -- always strings. type SymTab v = IndTree [(String,v)] emptyST :: SymTab v insertST :: (String,v) -> SymTab v -> SymTab v deleteST :: String -> SymTab v -> SymTab v lookupST :: String -> SymTab v -> Maybe v definedST :: String -> SymTab v -> Bool flattenST :: SymTab v -> [v] emptyST = itgen maxHash [] insertST (s,v) ss = itiap (hash s) ((s,v):) ss id deleteST s ss = itiap (hash s) (filter ((/=s).fst)) ss id lookupST s ss = let vs = filter ((==s).fst) ((itind (hash s)) ss) in if null vs then Nothing else (Just . snd . head) vs definedST s ss = let vs = filter ((==s).fst) ((itind (hash s)) ss) in (not . null) vs flattenST ss = itfold (map snd) (++) ss ---- -- | Index Trees (storing indexes at nodes). data IndTree t = Leaf t | Fork Int (IndTree t) (IndTree t) deriving Show itgen :: Int -> a -> IndTree a itgen 1 x = Leaf x itgen n x = let n' = n `div` 2 in Fork n' (itgen n' x) (itgen (n-n') x) itiap :: --Eval a => Int -> (a->a) -> IndTree a -> (IndTree a -> b) -> b itiap _ f (Leaf x) k = let fx = f x in {-seq fx-} (k (Leaf fx)) itiap i f (Fork n lt rt) k = if i k (Fork n lt' rt) else itiap (i-n) f rt $ \rt' -> k (Fork n lt rt') itind :: Int -> IndTree a -> a itind _ (Leaf x) = x itind i (Fork n lt rt) = if ib) -> (b->b->b) -> IndTree a -> b itfold leaf _fork (Leaf x) = leaf x itfold leaf fork (Fork _ l r) = fork (itfold leaf fork l) (itfold leaf fork r) ---- -- Hash values maxHash :: Int -- should be prime maxHash = 101 class Hashable a where hashWithMax :: Int -> a -> Int hash :: a -> Int hash = hashWithMax maxHash instance Enum a => Hashable [a] where hashWithMax m = h 0 where h a [] = a h a (c:cs) = h ((17*(fromEnum c)+19*a)`rem`m) cs ---- cpphs-1.20.8/Language/Preprocessor/Cpphs/Tokenise.hs0000644000000000000000000003475213122646212020522 0ustar0000000000000000----------------------------------------------------------------------------- -- | -- Module : Tokenise -- Copyright : 2004 Malcolm Wallace -- Licence : LGPL -- -- Maintainer : Malcolm Wallace -- Stability : experimental -- Portability : All -- -- The purpose of this module is to lex a source file (language -- unspecified) into tokens such that cpp can recognise a replaceable -- symbol or macro-use, and do the right thing. ----------------------------------------------------------------------------- module Language.Preprocessor.Cpphs.Tokenise ( linesCpp , reslash , tokenise , WordStyle(..) , deWordStyle , parseMacroCall ) where import Data.Char import Language.Preprocessor.Cpphs.HashDefine import Language.Preprocessor.Cpphs.Position -- | A Mode value describes whether to tokenise a la Haskell, or a la Cpp. -- The main difference is that in Cpp mode we should recognise line -- continuation characters. data Mode = Haskell | Cpp -- | linesCpp is, broadly speaking, Prelude.lines, except that -- on a line beginning with a \#, line continuation characters are -- recognised. In a line continuation, the newline character is -- preserved, but the backslash is not. linesCpp :: String -> [String] linesCpp [] = [] linesCpp (x:xs) | x=='#' = tok Cpp ['#'] xs | otherwise = tok Haskell [] (x:xs) where tok Cpp acc ('\\':'\n':ys) = tok Cpp ('\n':acc) ys tok _ acc ('\n':'#':ys) = reverse acc: tok Cpp ['#'] ys tok _ acc ('\n':ys) = reverse acc: tok Haskell [] ys tok _ acc [] = reverse acc: [] tok mode acc (y:ys) = tok mode (y:acc) ys -- | Put back the line-continuation characters. reslash :: String -> String reslash ('\n':xs) = '\\':'\n':reslash xs reslash (x:xs) = x: reslash xs reslash [] = [] ---- -- | Submodes are required to deal correctly with nesting of lexical -- structures. data SubMode = Any | Pred (Char->Bool) (Posn->String->WordStyle) | String Char | LineComment | NestComment Int | CComment | CLineComment -- | Each token is classified as one of Ident, Other, or Cmd: -- * Ident is a word that could potentially match a macro name. -- * Cmd is a complete cpp directive (\#define etc). -- * Other is anything else. data WordStyle = Ident Posn String | Other String | Cmd (Maybe HashDefine) deriving (Eq,Show) other :: Posn -> String -> WordStyle other _ s = Other s deWordStyle :: WordStyle -> String deWordStyle (Ident _ i) = i deWordStyle (Other i) = i deWordStyle (Cmd _) = "\n" -- | tokenise is, broadly-speaking, Prelude.words, except that: -- * the input is already divided into lines -- * each word-like "token" is categorised as one of {Ident,Other,Cmd} -- * \#define's are parsed and returned out-of-band using the Cmd variant -- * All whitespace is preserved intact as tokens. -- * C-comments are converted to white-space (depending on first param) -- * Parens and commas are tokens in their own right. -- * Any cpp line continuations are respected. -- No errors can be raised. -- The inverse of tokenise is (concatMap deWordStyle). tokenise :: Bool -> Bool -> Bool -> Bool -> [(Posn,String)] -> [WordStyle] tokenise _ _ _ _ [] = [] tokenise stripEol stripComments ansi lang ((pos,str):pos_strs) = (if lang then haskell else plaintext) Any [] pos pos_strs str where -- rules to lex Haskell haskell :: SubMode -> String -> Posn -> [(Posn,String)] -> String -> [WordStyle] haskell Any acc p ls ('\n':'#':xs) = emit acc $ -- emit "\n" $ cpp Any haskell [] [] p ls xs -- warning: non-maximal munch on comment haskell Any acc p ls ('-':'-':xs) = emit acc $ haskell LineComment "--" p ls xs haskell Any acc p ls ('{':'-':xs) = emit acc $ haskell (NestComment 0) "-{" p ls xs haskell Any acc p ls ('/':'*':xs) | stripComments = emit acc $ haskell CComment " " p ls xs haskell Any acc p ls ('/':'/':xs) | stripEol = emit acc $ haskell CLineComment " " p ls xs haskell Any acc p ls ('"':xs) = emit acc $ haskell (String '"') ['"'] p ls xs haskell Any acc p ls ('\'':'\'':xs) = emit acc $ -- TH type quote haskell Any "''" p ls xs haskell Any acc p ls ('\'':xs@('\\':_)) = emit acc $ -- escaped char literal haskell (String '\'') "'" p ls xs haskell Any acc p ls ('\'':x:'\'':xs) = emit acc $ -- character literal emit ['\'', x, '\''] $ haskell Any [] p ls xs haskell Any acc p ls ('\'':xs) = emit acc $ -- TH name quote haskell Any "'" p ls xs haskell Any acc p ls (x:xs) | single x = emit acc $ emit [x] $ haskell Any [] p ls xs haskell Any acc p ls (x:xs) | space x = emit acc $ haskell (Pred space other) [x] p ls xs haskell Any acc p ls (x:xs) | symbol x = emit acc $ haskell (Pred symbol other) [x] p ls xs -- haskell Any [] p ls (x:xs) | ident0 x = id $ haskell Any acc p ls (x:xs) | ident0 x = emit acc $ haskell (Pred ident1 Ident) [x] p ls xs haskell Any acc p ls (x:xs) = haskell Any (x:acc) p ls xs haskell pre@(Pred pred ws) acc p ls (x:xs) | pred x = haskell pre (x:acc) p ls xs haskell (Pred _ ws) acc p ls xs = ws p (reverse acc): haskell Any [] p ls xs haskell (String c) acc p ls ('\\':x:xs) | x=='\\' = haskell (String c) ('\\':'\\':acc) p ls xs | x==c = haskell (String c) (c:'\\':acc) p ls xs haskell (String c) acc p ls (x:xs) | x==c = emit (c:acc) $ haskell Any [] p ls xs | otherwise = haskell (String c) (x:acc) p ls xs haskell LineComment acc p ls xs@('\n':_) = emit acc $ haskell Any [] p ls xs haskell LineComment acc p ls (x:xs) = haskell LineComment (x:acc) p ls xs haskell (NestComment n) acc p ls ('{':'-':xs) = haskell (NestComment (n+1)) ("-{"++acc) p ls xs haskell (NestComment 0) acc p ls ('-':'}':xs) = emit ("}-"++acc) $ haskell Any [] p ls xs haskell (NestComment n) acc p ls ('-':'}':xs) = haskell (NestComment (n-1)) ("}-"++acc) p ls xs haskell (NestComment n) acc p ls (x:xs) = haskell (NestComment n) (x:acc) p ls xs haskell CComment acc p ls ('*':'/':xs) = emit (" "++acc) $ haskell Any [] p ls xs haskell CComment acc p ls (x:xs) = haskell CComment (white x:acc) p ls xs haskell CLineComment acc p ls xs@('\n':_)= emit acc $ haskell Any [] p ls xs haskell CLineComment acc p ls (_:xs) = haskell CLineComment (' ':acc) p ls xs haskell mode acc _ ((p,l):ls) [] = haskell mode acc p ls ('\n':l) haskell _ acc _ [] [] = emit acc $ [] -- rules to lex Cpp cpp :: SubMode -> (SubMode -> String -> Posn -> [(Posn,String)] -> String -> [WordStyle]) -> String -> [String] -> Posn -> [(Posn,String)] -> String -> [WordStyle] cpp mode next word line pos remaining input = lexcpp mode word line remaining input where lexcpp Any w l ls ('/':'*':xs) = lexcpp (NestComment 0) "" (w*/*l) ls xs lexcpp Any w l ls ('/':'/':xs) = lexcpp LineComment " " (w*/*l) ls xs lexcpp Any w l ((p,l'):ls) ('\\':[]) = cpp Any next [] ("\n":w*/*l) p ls l' lexcpp Any w l ls ('\\':'\n':xs) = lexcpp Any [] ("\n":w*/*l) ls xs lexcpp Any w l ls xs@('\n':_) = Cmd (parseHashDefine ansi (reverse (w*/*l))): next Any [] pos ls xs -- lexcpp Any w l ls ('"':xs) = lexcpp (String '"') ['"'] (w*/*l) ls xs -- lexcpp Any w l ls ('\'':xs) = lexcpp (String '\'') "'" (w*/*l) ls xs lexcpp Any w l ls ('"':xs) = lexcpp Any [] ("\"":(w*/*l)) ls xs lexcpp Any w l ls ('\'':xs) = lexcpp Any [] ("'": (w*/*l)) ls xs lexcpp Any [] l ls (x:xs) | ident0 x = lexcpp (Pred ident1 Ident) [x] l ls xs -- lexcpp Any w l ls (x:xs) | ident0 x = lexcpp (Pred ident1 Ident) [x] (w*/*l) ls xs lexcpp Any w l ls (x:xs) | single x = lexcpp Any [] ([x]:w*/*l) ls xs | space x = lexcpp (Pred space other) [x] (w*/*l) ls xs | symbol x = lexcpp (Pred symbol other) [x] (w*/*l) ls xs | otherwise = lexcpp Any (x:w) l ls xs lexcpp pre@(Pred pred _) w l ls (x:xs) | pred x = lexcpp pre (x:w) l ls xs lexcpp (Pred _ _) w l ls xs = lexcpp Any [] (w*/*l) ls xs lexcpp (String c) w l ls ('\\':x:xs) | x=='\\' = lexcpp (String c) ('\\':'\\':w) l ls xs | x==c = lexcpp (String c) (c:'\\':w) l ls xs lexcpp (String c) w l ls (x:xs) | x==c = lexcpp Any [] ((c:w)*/*l) ls xs | otherwise = lexcpp (String c) (x:w) l ls xs lexcpp LineComment w l ((p,l'):ls) ('\\':[]) = cpp LineComment next [] (('\n':w)*/*l) pos ls l' lexcpp LineComment w l ls ('\\':'\n':xs) = lexcpp LineComment [] (('\n':w)*/*l) ls xs lexcpp LineComment w l ls xs@('\n':_) = lexcpp Any w l ls xs lexcpp LineComment w l ls (_:xs) = lexcpp LineComment (' ':w) l ls xs lexcpp (NestComment _) w l ls ('*':'/':xs) = lexcpp Any [] (w*/*l) ls xs lexcpp (NestComment n) w l ls (x:xs) = lexcpp (NestComment n) (white x:w) l ls xs lexcpp mode w l ((p,l'):ls) [] = cpp mode next w l p ls ('\n':l') lexcpp _ _ _ [] [] = [] -- rules to lex non-Haskell, non-cpp text plaintext :: SubMode -> String -> Posn -> [(Posn,String)] -> String -> [WordStyle] plaintext Any acc p ls ('\n':'#':xs) = emit acc $ -- emit "\n" $ cpp Any plaintext [] [] p ls xs plaintext Any acc p ls ('/':'*':xs) | stripComments = emit acc $ plaintext CComment " " p ls xs plaintext Any acc p ls ('/':'/':xs) | stripEol = emit acc $ plaintext CLineComment " " p ls xs plaintext Any acc p ls (x:xs) | single x = emit acc $ emit [x] $ plaintext Any [] p ls xs plaintext Any acc p ls (x:xs) | space x = emit acc $ plaintext (Pred space other) [x] p ls xs plaintext Any acc p ls (x:xs) | ident0 x = emit acc $ plaintext (Pred ident1 Ident) [x] p ls xs plaintext Any acc p ls (x:xs) = plaintext Any (x:acc) p ls xs plaintext pre@(Pred pred ws) acc p ls (x:xs) | pred x = plaintext pre (x:acc) p ls xs plaintext (Pred _ ws) acc p ls xs = ws p (reverse acc): plaintext Any [] p ls xs plaintext CComment acc p ls ('*':'/':xs) = emit (" "++acc) $ plaintext Any [] p ls xs plaintext CComment acc p ls (x:xs) = plaintext CComment (white x:acc) p ls xs plaintext CLineComment acc p ls xs@('\n':_) = emit acc $ plaintext Any [] p ls xs plaintext CLineComment acc p ls (_:xs)= plaintext CLineComment (' ':acc) p ls xs plaintext mode acc _ ((p,l):ls) [] = plaintext mode acc p ls ('\n':l) plaintext _ acc _ [] [] = emit acc $ [] -- predicates for lexing Haskell. ident0 x = isAlpha x || x `elem` "_`" ident1 x = isAlphaNum x || x `elem` "'_`" symbol x = x `elem` ":!#$%&*+./<=>?@\\^|-~" single x = x `elem` "(),[];{}" space x = x `elem` " \t" -- conversion of comment text to whitespace white '\n' = '\n' white '\r' = '\r' white _ = ' ' -- emit a token (if there is one) from the accumulator emit "" = id emit xs = (Other (reverse xs):) -- add a reversed word to the accumulator "" */* l = l w */* l = reverse w : l -- help out broken Haskell compilers which need balanced numbers of C -- comments in order to do import chasing :-) -----> */* -- | Parse a possible macro call, returning argument list and remaining input parseMacroCall :: Posn -> [WordStyle] -> Maybe ([[WordStyle]],[WordStyle]) parseMacroCall p = call . skip where skip (Other x:xs) | all isSpace x = skip xs skip xss = xss call (Other "(":xs) = (args (0::Int) [] [] . skip) xs call _ = Nothing args 0 w acc ( Other ")" :xs) = Just (reverse (addone w acc), xs) args 0 w acc ( Other "," :xs) = args 0 [] (addone w acc) (skip xs) args n w acc (x@(Other "("):xs) = args (n+1) (x:w) acc xs args n w acc (x@(Other ")"):xs) = args (n-1) (x:w) acc xs args n w acc ( Ident _ v :xs) = args n (Ident p v:w) acc xs args n w acc (x@(Other _) :xs) = args n (x:w) acc xs args _ _ _ _ = Nothing addone w acc = reverse (skip w): acc cpphs-1.20.8/docs/0000755000000000000000000000000013122646212012034 5ustar0000000000000000cpphs-1.20.8/docs/cpphs.10000644000000000000000000001714113122646212013237 0ustar0000000000000000.TH CPPHS 1 2004-10-01 "cpphs version 0.9" "User Manual" .SH NAME cpphs \- liberalised cpp-a-like preprocessor for Haskell .SH SYNOPSIS .B cpphs [\fIFILENAME\fR|\fIOPTION\fR]... .SH DESCRIPTION .ds c \fIcpphs\fP \*c is a liberalised re-implementation of .B cpp (1), the C pre-processor, in and for Haskell. .PP Why re-implement cpp? Rightly or wrongly, the C pre-processor is widely used in Haskell source code. It enables conditional compilation for different compilers, different versions of the same compiler, and different OS platforms. It is also occasionally used for its macro language, which can enable certain forms of platform-specific detail-filling, such as the tedious boilerplate generation of instance definitions and FFI declarations. However, there are two problems with cpp, aside from the obvious aesthetic ones: .IP For some Haskell systems, notably Hugs on Windows, a true cpp is not available by default. .IP Even for the other Haskell systems, the common cpp provided by the gcc 3.x series is changing subtly in ways that are incompatible with Haskell's syntax. There have always been problems with, for instance, string gaps, and prime characters in identifiers. These problems are only going to get worse. .PP So, it seemed right to attempt to provide an alternative to cpp, both more compatible with Haskell, and itself written in Haskell so that it can be distributed with compilers. .PP \*c is pretty-much feature-complete, and compatible with the .B \-traditional style of cpp. It has two modes: .IP conditional compilation only (\fB\-\-nomacro\fR), .IP and full macro-expansion (default). .PP In .B \-\-nomacro mode, \*c performs only conditional compilation actions, i.e. \fB#include\fR's, \fB#if\fR's, and \fB#ifdef\fR's are processed according to text-replacement definitions (both command-line and internal), but no parameterised macro expansion is performed. In full compatibility mode (the default), textual replacements and macro expansions are also processed in the remaining body of non-cpp text. .PP Working Features: .TP .B #ifdef simple conditional compilation .TP .B #if the full boolean language of defined(), &&, ||, ==, etc. .TP .B #elif chained conditionals .TP .B #define in-line definitions (text replacements and macros) .TP .B #undef in-line revocation of definitions .TP .B #include file inclusion .TP .B #line line number directives .TP .B \\\\n line continuations within all # directives .TP .B /**/ token catenation within a macro definition .TP .B ## ANSI-style token catenation .TP .B # ANSI-style token stringisation .TP .B __FILE__ special text replacement for DIY error messages .TP .B __LINE__ special text replacement for DIY error messages .TP .B __DATE__ special text replacement .TP .B __TIME__ special text replacement .PP Macro expansion is recursive. Redefinition of a macro name does not generate a warning. Macros can be defined on the command-line with .B \-D just like textual replacements. Macro names are permitted to be Haskell identifiers e.g. with the prime \(ga and backtick \(aa characters, which is slightly looser than in C, but they still may not include operator symbols. .PP Numbering of lines in the output is preserved so that any later processor can give meaningful error messages. When a file is \fB#include\fR'd, \*c inserts .B #line directives for the same reason. Numbering should be correct even in the presence of line continuations. If you don't want .B #line directives in the final output, use the .B \-\-noline option. .PP Any syntax errors in cpp directives gives a message to stderr and halts the program. Failure to find a #include'd file produces a warning to stderr, but processing continues. .PP You can give any number of filenames on the command-line. The results are catenated on standard output. .TP .B \-D\fIsym\fR define a textual replacement (default value is 1) .TP .B \-Dsym=\fIval\fR define a textual replacement with a specific value .TP .B \-I\fIpath\fR add a directory to the search path for #include's .TP .B \-O\fIfile\fR specify a file for output (default is stdout) .TP .B \-\-nomacro only process #ifdef's and #include's, do not expand macros .TP .B \-\-noline remove #line droppings from the output .TP .B \-\-strip convert C-style comments to whitespace, even outside cpp directives .TP .B \-\-hashes recognise the ANSI # stringise operator, and ## for token catenation, within macros .TP .B \-\-text treat the input as plain text, not Haskell code .TP .B \-\-layout preserve newlines within macro expansions .TP .B \-\-unlit remove literate-style comments .TP .B \-\-version report version number of cpphs and stop .PP There are NO textual replacements defined by default. (Normal cpp usually has definitions for machine, OS, etc. These could easily be added to the cpphs source code if you wish.) The search path is searched in order of the .B \-I options, except that the directory of the calling file, then the current directory, are always searched first. Again, there is no default search path (and again, this could easily be changed). .SH "DIFFERENCES FROM CPP" .PP In general, cpphs is based on the .B \-traditional behaviour, not ANSI C, and has the following main differences from the standard cpp. .B General .PP The .B # that introduces any cpp directive must be in the first column of a line (whereas ANSI permits whitespace before the .B # ). .PP Generates the .B "#line \fIn\fR \(dq\fIfilename\fR\(dq" syntax, not the .B "# \fIn\fR \(dq\fIfilename\fR\(dq" variant. .PP C comments are only removed from within cpp directives. They are not stripped from other text. Consider for instance that in Haskell, all of the following are valid operator symbols: .B /* */ */* However, you can turn on C-comment removal with the .B \-\-strip option. .B Macro language .PP Accepts .B /**/ for token-pasting in a macro definition. However, .B /* */ (with any text between the open/close comment) inserts whitespace. .PP The ANSI .B ## token-pasting operator is available with the .B \-\-hashes flag. This is to avoid misinterpreting any valid Haskell operator of the same name. .PP Replaces a macro formal parameter with the actual, even inside a string (double or single quoted). This is \-traditional behaviour, not supported in ANSI. .PP Recognises the .B # stringisation operator in a macro definition only if you use the .B \-\-hashes option. (It is an ANSI addition, only needed because quoted stringisation (above) is prohibited by ANSI.) .PP Preserves whitespace within a textual replacement definition exactly (modulo newlines), but leading and trailing space is eliminated. .PP Preserves whitespace within a macro definition (and trailing it) exactly (modulo newlines), but leading space is eliminated. .PP Preserves whitespace within macro call arguments exactly (including newlines), but leading and trailing space is eliminated. .PP With the .B \-\-layout option, line continuations in a textual replacement or macro definition are preserved as line-breaks in the macro call. (Useful for layout-sensitive code in Haskell.) .SH BUGS Bug reports, and any other feedback, should be sent to Malcolm Wallace .SH COPYRIGHT Copyright \(co 2004-2005 Malcolm Wallace, except for ParseLib (Copyright \(co 1995 Graham Hutton and Erik Meijer). .PP The library modules in cpphs are distributed under the terms of the LGPL. If that's a problem for you, contact me to make other arrangements. The application module .B Main.hs itself is GPL. .SH "SEE ALSO" .BR cpp (1) .SH AUTHOR This manual page was written, based on \fBindex.html\fR, by Ian Lynagh for the Debian system (but may be used by others). cpphs-1.20.8/docs/index.html0000644000000000000000000007173113122646212014042 0ustar0000000000000000 cpphs

cpphs

What is cpphs?
How do I use it?
Downloads
Differences to cpp
cpphs as a library
Contacts

What is cpphs?

cpphs is a liberalised re-implementation of cpp, the C pre-processor, in Haskell.

Why re-implement cpp? Rightly or wrongly, the C pre-processor is widely used in Haskell source code. It enables conditional compilation for different compilers, different versions of the same compiler, and different OS platforms. It is also occasionally used for its macro language, which can enable certain forms of platform-specific detail-filling, such as the tedious boilerplate generation of instance definitions and FFI declarations. However, there are two problems with cpp, aside from the obvious aesthetic ones:

  • For some Haskell systems, notably Hugs on Windows, a true cpp is not available by default.
  • Even for the other Haskell systems, the common cpp provided by the gcc 3.x and 4.x series has changed subtly in ways that are incompatible with Haskell's syntax. There have always been problems with, for instance, string gaps, and prime characters in identifiers. These problems are only going to get worse.
So, it seemed right to provide an alternative to cpp, both more compatible with Haskell, and itself written in Haskell so that it can be distributed with compilers.

This version of the C pre-processor is pretty-much feature-complete, and compatible with the -traditional style. It has two main modes:

  • conditional compilation only (--nomacro),
  • and full macro-expansion (default).
In --nomacro mode, cpphs performs only conditional compilation actions, namely #include's, #if's, and #ifdef's are processed according to text-replacement definitions and macro expansions (both command-line and internal). In full compatibility mode (the default), textual replacements and macro expansions are also processed in the remaining body of non-cpp text.

Source language features:
#ifdef simple conditional compilation
#if the full boolean language of defined(), &&, ||, ==, etc.
#elif chained conditionals
#define in-line definitions (text replacements and macros)
#undef in-line revocation of definitions
#includefile inclusion
#line line number directives
#pragma cpp pragmas (ignored)
\\n line continuations within all # directives
/**/ token catenation within a macro definition
## ANSI-style token catenation
# ANSI-style token stringisation
__FILE__special text replacement for DIY error messages
__LINE__special text replacement for DIY error messages
__DATE__special text replacement
__TIME__special text replacement

Macro expansion is recursive. Redefinition of a macro name does not generate a warning. Macros can be defined on the command-line with -D just like textual replacements. Macro names are permitted to be Haskell identifiers e.g. with the prime ' and backtick ` characters, which is slightly looser than in C, but they still may not include operator symbols.

Numbering of lines in the output is preserved so that any later processor can give meaningful error messages. When a file is #include'd, cpphs inserts #line directives for the same reason. Numbering should be correct even in the presence of line continuations. If you don't want #line directives in the final output, use the --noline option, or if you would prefer them in {-# LINE #-} Haskell pragma format, use the --linepragma option.

Any syntax error in a cpp directive gives a warning message to stderr. Failure to find a #include'd file also produces a warning to stderr. In both cases, processing continues on the rest of the input.


How do I use it?

Usage: cpphs  [ filename | -Dsym | -Dsym=val | -Ipath ]+  [-Ofile]
              [--include=file]*
              [--nomacro] [--noline] [--linepragma] [--nowarn] [--pragma]
              [--strip] [--strip-eol]
              [--text] [--hashes] [--layout] [--unlit]
              [ --cpp compatopts ]
       cpphs --version                                             

You can give any number of filenames on the command-line. The results are catenated on standard output. (Macro definitions in one file do not carry over into the next.) If no filename is given, cpphs reads from standard input.

Note: if you wish to use cpphs as a replacement for gcc's cpp in conjunction with the ghc compiler then the extra options you need to give to ghc are these:

  -cpp  -pgmPcpphs  -optP--cpp

Options:
-Dsym define a textual replacement (default value is 1)
-Dsym=val define a textual replacement with a specific value
-Dsym(args)=val define a macro with arguments
-Ipath add a directory to the search path for #include's
-Ofile specify a file for output (default is stdout)
--include=file #include the given file at the start of the input
--nomacro only process #ifdef's and #include's, do not expand macros
--noline remove #line droppings from the output
--linepragma convert #line droppings into {-# LINE #-} format
--nowarn suppress messages from missing #include files, or #warning
--pragma retain #pragma in the output (normally removed)
--strip convert traditional C-style comments (not eol //) to whitespace, even outside cpp directives
--strip-eol convert modern C-style comments (including /**/ and //) to whitespace, even outside cpp directives
--hashes recognise the ANSI # stringise operator, and ## for token catenation, within macros
--text treat input as plain text, not Haskell code
--layout preserve newlines within macro expansions
--unlit unlit literate source code
--cpp compatopts accept standard cpp options: -o, -x, -ansi, -traditional, -P, -C, -A, etc
--version report version number of cpphs and stop

There are NO textual replacements defined by default. (Normal cpp usually has definitions for machine, OS, etc. You can easily create a wrapper script if you need these.) The search path is searched in order of the -I options, except that the directory of the calling file, then the current directory, are always searched first. Again, there is no default search path (unless you define one via a wrapper script).


Downloads

Current stable version:

cpphs-1.20.8, release date 2017-06-22
By HTTP: Hackage.

  • (1.20.8) another bugfix for windows drive letters in #includes.

Development:

The current darcs repository of cpphs is available at

    darcs get http://code.haskell.org/cpphs
(Users on Windows or MacOS filesystems may need to use the --partial flag.) What's new, over and above the latest stable release?
  • Nothing since last release.

Older versions:

cpphs-1.20.8, release date 2017-06-22
By HTTP: Hackage.

  • (1.20.8) another bugfix for windows drive letters in #includes.
  • (1.20.7) bugfix for windows drive letters in #includes.
  • (1.20.6) minor bugfix to avoid a crash in an obscure corner case

cpphs-1.20.5, release date 2017-04-11
By HTTP: Hackage.

  • (1.20.5) reverts the changes in 1.20.4

cpphs-1.20.4, release date 2017-02-27
By HTTP: Hackage.

  • (1.20.4) more windows path handling

cpphs-1.20.3, release date 2017-02-06
By HTTP: Hackage.

  • (1.20.3) detect an absolute windows path with a drive letter in a #include

cpphs-1.20.2, release date 2016-09-05
By HTTP: Hackage.

  • (1.20.2) ensure all input/output is UTF8, regardless of locale

cpphs-1.20, release date 2016-03-04
By HTTP: Hackage.

  • bugfix for #if defined(FOO) && FOO(a,b)
  • (1.20.1) fix version number

cpphs-1.19.3, release date 2015-08-23
By HTTP: Hackage.

  • bugfix for hlint ticket #161, for the interaction of --unlit and --linepragma options

cpphs-1.19.2, release date 2015-07-31
By HTTP: Hackage.

  • fix build error

cpphs-1.19.1, release date 2015-07-30
By HTTP: Hackage.

  • don't warn about trailing comments in #ifdefs

cpphs-1.19, release date 2015-03-30
By HTTP: Hackage.

  • expose more of the API

cpphs-1.18.9, release date 2015-02-19
By HTTP: Hackage.

  • accept "#if defined foo" as well as "#if defined(foo)"

cpphs-1.18.8, release date 2015-01-20
By HTTP: Hackage.

  • fix version number

cpphs-1.18.7, release date 2015.01.17
By HTTP: Hackage.

  • bugfix to accept a #include with absolute filepath

cpphs-1.18.6, release date 2014.10.17
By HTTP: Hackage.

  • bugfix to reject a macro usage with different arity than its definition

cpphs-1.18.5, release date 2014.06.26
By HTTP: Hackage.

  • fix a bug related to windows filepath directory separators in #includes

cpphs-1.18.4, release date 2014.03.22
By HTTP: Hackage.

  • fix the accidental flipping of comment-stripping behaviour with --cpp -traditional flags.

cpphs-1.18.3, release date 2014.03.03
By HTTP: Hackage.

  • further rewrites of the #if expression parser, for better error messages, support for integer operations, and fewer precedence bugs

cpphs-1.18.2, release date 2014.02.24
By HTTP: Hackage.

  • bugfix for erroneous boolean interpretation of some macro expansions in #if clauses

cpphs-1.18.1, release date 2014.02.18
By HTTP: Hackage.

  • fix for incomplete pattern-match

cpphs-1.18, release date 2014.02.04
By HTTP: Hackage.

  • Better lexing of Template Haskell single quotes. (Thanks to Stephan Wehr)

cpphs-1.17.1, release date 2013.08.18
By HTTP: Hackage.

  • (minor) Report the correct version number with cpphs --version

cpphs-1.17, release date 2013.08.16
By HTTP: .tar.gz, Hackage.

  • Recursively evaluate #if expressions after macro expansion (bugfix).

cpphs-1.16, release date 2013.01.22
By HTTP: .tar.gz, Hackage.

  • Fix the interaction of runCpphsReturningSymTab with --nomacro.

cpphs-1.15, release date 2012.11.30
By HTTP: .tar.gz, Hackage.

  • Fix the interaction of --nomacro with --strip.
  • Fix the error message received when # appears without a command.

cpphs-1.14, release date 2012.07.11
By HTTP: .tar.gz, Hackage.

  • New API to return symbol table after processing.

cpphs-1.13, release date 2011.09.26
By HTTP: .tar.gz, Hackage.

  • Accept the -U commandline option for compatibility with cpp.

cpphs-1.12, release date 2011.06.26
By HTTP: .tar.gz, Hackage.

  • Compatibility fixes for ghc-7.2.

cpphs-1.11, release date 2010.01.31
By HTTP: .tar.gz, .zip, Hackage.

  • Major API change: runCpphs, cppIfdef and macroPass are now in the IO monad.

cpphs-1.10, release date 2010.01.30
By HTTP: .tar.gz, .zip,

  • New command-line flag: --linepragma

cpphs-1.9, release date 2009.09.07
By HTTP: .tar.gz, .zip.

  • Bugfix for #undef.

cpphs-1.8, release date 2009.08.06
By HTTP: .tar.gz, .zip.

  • Bugfix for off-by-one error in line numbers with --include=file.

cpphs-1.7, release date 2009.06.22
By HTTP: .tar.gz, .zip.

  • Bugfix for --unlit interaction with \end{code}.

cpphs-1.6, release date 2008.10.09
By HTTP: .tar.gz, .zip.

  • New option --include=filename, compatible with cpp's -include filename.
  • New option --strip-eol now strips C eol // comments in addition to /**/.
  • Line pragmas can now have filenames containing spaces.
  • Bugfix for cpp directives within {- -} Haskell comments.

cpphs-1.5, release date 2007.06.05
By HTTP: .tar.gz, .zip. Windows binary,

  • Fixed some more obscure corner cases, involving parameterised macro expansion within conditionals e.g. #if FOO(BAR,QUUX)
  • Internal refactoring, affecting parts of the library API.

cpphs-1.4, release date 2007.04.17
By HTTP: .tar.gz, .zip.

  • Added a "--pragma" option to retain #pragma in the output.
  • Fixed a number of obscure corner cases involving the interaction of multiple features e.g. foo##__LINE__.
  • Added the "--nowarn" option.

cpphs-1.3, release date 2006.10.09
By HTTP: .tar.gz, .zip, Windows binary.

  • Added a "--cpp" option for drop-in compatibility with standard cpp. It causes cpphs to accept standard cpp flags and translate them to cpphs equivalents. Compatibility options include: -o, -ansi, -traditional, -stdc, -x, -include, -P, -C, -CC, -A. The file behaviour is different too - if two filenames are given on the commandline, then the second is treated as the output location.
  • Fixed a corner-case bug in evaluating chained and overlapping #ifdefs.

cpphs-1.2, release date 2006.05.04
By HTTP: .tar.gz, .zip, Windows binary.

  • Re-arranged the source files into hierarchical libraries.
  • Exposed the library interface as an installable Cabal package, with Haddock documentation.
  • Added the --unlit option, for removing literate-style comments.

cpphs-1.1, release date 2005.10.14
By HTTP: .tar.gz, .zip.

  • Fixed the .cabal way of building cpphs.
  • Update the --version reported (forgotten in 1.0, which still mistakenly reports 0.9).
  • No longer throws an error on an empty file.

cpphs-1.0, release date 2005.10.05
By HTTP: .tar.gz, .zip.

  • Included the cpphs.compat script for argument compatibility with the original cpp.
  • Placed quotes around replacements for special macros __FILE__, __DATE__, and __TIME__.
  • If no files are specified, read from stdin.
  • Ignore #! lines (e.g. in scripts)
  • Parse -D commandline options consistently with cpp, i.e. -Dfoo means foo=1
  • Fix compatibility with preprocessors like hsc2hs, which use non-cpp directives like #def. They are now passed through to the output with a warning to stderr.

cpphs-0.9, release date 2005.03.17
By HTTP: .tar.gz, .zip.

  • Bugfix for ghc-6.4 -O: flush output buffer.

cpphs-0.8, release date 2004.11.14
By HTTP: .tar.gz, .zip.

  • Added the --text option, to signify the input should not be lexed as Haskell. This causes macros to be defined or expanded regardless of their location within comments, string delimiters, etc.
  • Shuffled some source files around - there is now a runhugs script to invoke cpphs nicely.

cpphs-0.7, release date 2004.09.01
By HTTP: .tar.gz, .zip.

  • Enable the __FILE__, __LINE__, __DATE__, and __TIME__ specials, which can be useful for creating DIY error messages.

cpphs-0.6, release date 2004.07.30
By HTTP: .tar.gz, .zip.

  • Recognise and ignore the #pragma cpp directive.
  • Fix beginning-of-file bug, where in --noline mode, a #line cpp directive appeared at the top of the output file.
  • Fix chained parenthesised boolean exprs in #if, e.g.
    #if ( foo ) && ( bar )
  • Fix precedence in chained unparenthesised boolean exprs in #if, e.g.
    #if foo && bar || baz && frob
  • For better compatibility with cpp, and because otherwise there are certain constructs that cannot be expressed, we no longer permit whitespace in a #define between the symbolname and an opening parenthesis, e.g.
    #define f (f' id)
    . Previously, this was interpreted as a parametrised macro, with arguments in the parens, and no expansion. Now, the space indicates that this is a textual replacement, and the parenthesised expression is in fact the replacement.

cpphs-0.5, release date 2004.06.07
By HTTP: .tar.gz, .zip.

  • Added a --version flag to report the version number.
  • Renamed --stringise to --hashes, and use it to turn on ## catenation as well.
  • Bugfix for #if 1, previously interpreted as false.
  • Bugfix for --nolines: it no longer adds extra spurious newlines.
  • File inclusion now looks in the directory of the calling file.
  • Failure to find an include file is now merely a warning to stderr rather than an error.
  • Added a --layout flag. Previously, line continuations in a macro definition were always preserved in the output, permitting use of the Haskell layout rule even inside a macro. The default is now to remove line continuations for conformance with cpp, but the option of using --layout is still possible.

cpphs-0.4, release date 2004.05.19
By HTTP: .tar.gz, .zip.

  • New flag -Ofile to redirect output
  • Bugfix for precedence of ! in #if !False && False
  • Bugfix for whitespace permitted between # and if
  • Bugfix for #define F "blah"; #include F

cpphs-0.3, release date 2004.05.18
By HTTP: .tar.gz, .zip.

Fix recursive macro expansion bug. Added option to strip C comments. Added option to recognise the # stringise operator.

cpphs-0.2, release date 2004.05.15
By HTTP: .tar.gz, .zip.

Implements textual replacement and macro expansion.

cpphs-0.1, release date 2004.04.07
By HTTP: .tar.gz, .zip.

Initial release: implements conditional compilation and file inclusion only.

Building instructions

To build cpphs, use

    hmake cpphs [-package base]
or
    ghc --make cpphs [-o cpphs]
or
    mv cpphs.hugs cpphs	# a simple runhugs script

You will notice that the command-line arguments for cpphs are not the same as for the original cpp. If you want to use cpphs as a completely drop-in replacement for the real cpp, that is, to accept the same arguments, and have broadly the same behaviour in response to them, then use the --cpp compatibility option as the first commandline flag.


Differences from cpp

In general, cpphs is based on the -traditional behaviour, not ANSI C, and has the following main differences from the standard cpp.

General

  • The # that introduces any cpp directive must be in the first column of a line (whereas ANSI permits whitespace before the #).
  • Generates the #line n "filename" syntax, not the # n "filename" variant.
  • C comments are only removed from within cpp directives. They are not stripped from other text. Consider for instance that in Haskell, all of the following are valid operator symbols: /* */ */* However, you can turn on C-comment removal with the --strip option.
  • Macros are never expanded within Haskell comments, strings, or character constants, unless you give the --text option to disable lexing the input as Haskell.
  • Macros are always expanded recursively, unlike ANSI, which detects and prevents self-recursion. For instance, #define foo x:foo expands foo once only to x:foo in ANSI, but in cpphs it becomes an infinite list x:x:x:x:..., i.e. cpphs does not terminate.

Macro definition language

  • Accepts /**/ for token-pasting in a macro definition. However, /* */ (with any text between the open/close comment) inserts whitespace.
  • The ANSI ## token-pasting operator is available with the --hashes flag. This is to avoid misinterpreting any valid Haskell operator of the same name.
  • Replaces a macro formal parameter with the actual, even inside a string (double or single quoted). This is -traditional behaviour, not supported in ANSI.
  • Recognises the # stringisation operator in a macro definition only if you use the --hashes option. (It is an ANSI addition, only needed because quoted stringisation (above) is prohibited by ANSI.)
  • Preserves whitespace within a textual replacement definition exactly (modulo newlines), but leading and trailing space is eliminated.
  • Preserves whitespace within a macro definition (and trailing it) exactly (modulo newlines), but leading space is eliminated.
  • Preserves whitespace within macro call arguments exactly (including newlines), but leading and trailing space is eliminated.
  • With the --layout option, line continuations in a textual replacement or macro definition are preserved as line-breaks in the macro call. (Useful for layout-sensitive code in Haskell.)

cpphs as a library

You can use cpphs as a library from within a Haskell program. The main interface is in Language.Preprocessor.Cpphs. Haddock documentation is here. To make the library available to your haskell compiler, you must install the cpphs package using Cabal.


Contacts

I am interested in hearing your feedback on cpphs. Bug reports especially welcome. You can send feature requests too, but I won't guarantee to implement them if they depart much from the ordinary cpp's behaviour. Please mail

Copyright: © 2004-2016 Malcolm Wallace

License: The library modules in cpphs are distributed under the terms of the LGPL (see file LICENCE-LGPL for more details), with a special exception for static linking (see file COPYRIGHT. If that's a problem for you, contact me to make other arrangements. The application module 'cpphs.hs' itself is GPL (see file LICENCE-GPL). If you have a commercial use for cpphs and find the terms of the (L)GPL too onerous, you can instead choose to distribute unmodified binaries (not source), under the terms of LICENCE-commercial

This software comes with no warranty. Use at your own risk.