diff --git a/classic/test/bug_000008.cpp b/classic/test/bug_000008.cpp index fbe6116a5..e814928f5 100644 --- a/classic/test/bug_000008.cpp +++ b/classic/test/bug_000008.cpp @@ -11,7 +11,7 @@ // or https://sf.net/mailarchive/forum.php?thread_id=2692308&forum_id=1595 // for a description of the bug being tested for by this program // - // the problem should be solved with version 1.3 of phoenix/closures.hpp + // the problem should be solved with version 1.3 of phoenix/closures.hpp> #if defined(BOOST_SPIRIT_DEBUG) && defined(__GNUC__) && defined(__WIN32__) // It seems that MinGW has some problems with threads and iostream ? diff --git a/doc/Jamfile b/doc/Jamfile new file mode 100644 index 000000000..3ac28ebec --- /dev/null +++ b/doc/Jamfile @@ -0,0 +1,27 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2007 Hartmut Kaiser +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +project spirit/doc ; + +import boostbook : boostbook ; +using quickbook : quickbook ; + +boostbook spirit2 + : + spirit2.qbk + : + boost.root=../../../.. + boost.libraries=../../../libraries.htm + html.stylesheet=../../../../doc/html/boostbook.css + chunk.section.depth=5 + chunk.first.sections=1 + toc.section.depth=4 + toc.max.depth=4 + generate.section.toc.level=4 + ; diff --git a/doc/_concepts_template_.qbk b/doc/_concepts_template_.qbk new file mode 100644 index 000000000..e8009553c --- /dev/null +++ b/doc/_concepts_template_.qbk @@ -0,0 +1,46 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section XXX] + +[heading Description] + +Description of XXX concept + +[variablelist Notation + [[`xxx`] [An XXX]] +] + +[heading Valid Expressions] + +(For any Forward Sequence the following expressions must be valid:) + +In addition to the requirements defined in _XXX-Basic_concept_, for any +XXX the following must be met: + +[table + [[Expression] [Semantics] [Return type] [Complexity]] + [[`xxx`] [Semantics of `xxx`] [XXX] [Constant]] +] + +[heading Type Requirements] + +[table + [[Expression] [Requirements]] + [[`xxx`] [Requirements for `xxx`]] +] + +[heading Invariants] + +For any XXX xxx the following invariants always hold: + +[heading Models] + +Links to models of XXX concept + +[endsect] diff --git a/doc/_reference_template_.qbk b/doc/_reference_template_.qbk new file mode 100644 index 000000000..99665cef0 --- /dev/null +++ b/doc/_reference_template_.qbk @@ -0,0 +1,56 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section XXX] + +[heading Description] + +Description of XXX + +[heading Header] + + #include + +[heading Synopsis] + + template + struct XXX; + +[heading Template parameters] + +[table + [[Parameter] [Description] [Default]] + [[`T`] [What is T] []] +] + +[heading Model of] + +Link to concept + +[heading Objects] + +Objects provided by the library + +[variablelist Notation + [[`xxx`] [An XXX]] +] + +Semantics of an expression is defined only where it differs from, or is not +defined in _concept-of_XXX_. + +[table + [[Expression] [Semantics] [Return type] [Complexity]] + [[`xxx`] [Semantics of `xxx`] [XXX] [Constant]] +] + +[heading Example] + +Real example code. Use Quickbook import mechanism to link to actual +working code snippets here. + +[endsect] diff --git a/doc/acknowledgments.qbk b/doc/acknowledgments.qbk new file mode 100644 index 000000000..87f41dc8b --- /dev/null +++ b/doc/acknowledgments.qbk @@ -0,0 +1,147 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Acknowledgments] + +This version of Spirit is a complete rewrite of the /classic/ Spirit many +people have been contributing to (see below). But there are a couple of people +who already managed to help significantly during this rewrite. We would like to +express our special acknowledgement to: + +[*Eric Niebler] for writing Boost.Proto, without which this rewrite wouldn't +have been possible, and helping with examples, advices, and suggestions on +how to use Boost.Proto in the best possible way. + +[*Ben Hanson] for providing us with an early version of his Lexertl library, +which is proposed to be included into Boost (as Boost.Lexer), but at the time +of this writing the Boost review for this library is still pending. + +__fixme__: Add more people + + +[heading Acknowledgements from the Spirit V1 /classic/ Documentation] + +Special thanks for working on Spirit /classic/ to: + +[*Dan Nuffer] for his work on lexers, parse trees, ASTs, XML parsers, the +multi-pass iterator as well as administering Spirit's site, editing, +maintaining the CVS and doing the releases plus a zillion of other chores that +were almost taken for granted. + +[*Hartmut Kaiser] for his work on the C parser, the work on the C/C++ +preprocessor, utility parsers, the original port to Intel 5.0, various work on +Phoenix, porting to v1.5, the meta-parsers, the grouping-parsers, extensive +testing and painstaking attention to details. + +[*Martin Wille] who improved grammar multi thread safety, contributed the eol_p +parser, the dynamic parsers, documentation and for taking an active role in +almost every aspect from brainstorming and design to coding. And, as always, +helps keep the regression tests for g++ on Linux as green as ever :-). + +[*Martijn W. Van Der Lee] our Web site administrator and for contributing the +RFC821 parser. + +[*Giovanni Bajo] for last minute tweaks of Spirit 1.8.0 for CodeWarrior 8.3. +Actually, I'm ashamed Giovanni was not in this list already. He's done a lot +since Spirit 1.5, the first Boost.Spirit release. He's instrumental in the +porting of the Spirit iterators stuff to the new Boost Iterators Library +(version 2). He also did various bug fixes and wrote some tests here and there. + +[*Juan Carlos Arevalo-Baeza (JCAB)*] for his work on the C++ parser, the position +iterator, ports to v1.5 and keeping the mailing list discussions alive and +kicking. + +[*Vaclav Vesely], lots of stuff, the no\_actions directive, various patches +fixes, the distinct parsers, the lazy parser, some phoenix tweaks and add-ons +(e.g. new\_). Also, *Stefan Slapeta] and wife for editing Vaclav's distinct +parser doc. + +[*Raghavendra Satish] for doing the original v1.3 port to VC++ and his work on +Phoenix. + +[*Noah Stein] for following up and helping Ragav on the VC++ ports. + +[*Hakki Dogusan], for his original v1.0 Pascal parser. + +[*John (EBo) David] for his work on the VM and watching over my shoulder as I +code giving the impression of distance eXtreme programming. + +[*Chris Uzdavinis] for feeding in comments and valuable suggestions as well as +editing the documentation. + +[*Carsten Stoll], for his work on dynamic parsers. + +[*Andy Elvey] and his conifer parser. + +[*Bruce Florman], who did the original v1.0 port to VC++. + +[*Jeff Westfahl] for porting the loop parsers to v1.5 and contributing the file +iterator. + +[*Peter Simons] for the RFC date parser example and tutorial plus helping out +with some nitty gritty details. + +[*Markus Sch'''ö'''pflin] for suggesting the end_p parser and lots of other +nifty things and his active presence in the mailing list. + +[*Doug Gregor] for mentoring and his ability to see things that others don't. + +[*David Abrahams] for giving Joel a job that allows him to still work on Spirit, +plus countless advice and help on C++ and specifically template +metaprogramming. + +[*Aleksey Gurtovoy] for his MPL library from which we stole many metaprogramming +tricks especially for less conforming compilers such as Borland and VC6/7. + +[*Gustavo Guerra] for his last minute review of Spirit and constant feedback, +plus patches here and there (e.g. proposing the new dot behavior of the real +numerics parsers). + +[*Nicola Musatti], [*Paul Snively], [*Alisdair Meredith] and [*Hugo Duncan] for +testing and sending in various patches. + +[*Steve Rowe] for his splendid work on the TSTs that will soon be taken into +Spirit. + +[*Jonathan de Halleux] for his work on actors. + +[*Angus Leeming] for last minute editing work on the 1.8.0 release +documentation, his work on Phoenix and his active presence in the Spirit +mailing list. + +[*Joao Abecasis] for his active presence in the Spirit mailing list, providing +user support, participating in the discussions and so on. + +[*Guillaume Melquiond] for a last minute patch to multi_pass for 1.8.1. + +[*Peder Holt] for his porting work on Phoenix, Fusion and Spirit to VC6. + +To Joels wife Mariel who did the graphics in this document. + +My, there's a lot in this list! And it's a continuing list. We add people to +this list everytime. We hope we did not forget anyone. If we missed +someone you know who has helped in any way, please inform us. + +Special thanks also to people who gave feedback and valuable comments, +particularly members of Boost and Spirit mailing lists. This includes all those +who participated in the review: + +[*John Maddock], our review manager, [*Aleksey Gurtovoy], [*Andre Hentz], +[*Beman Dawes], [*Carl Daniel], [*Christopher Currie], [*Dan Gohman], +[*Dan Nuffer], [*Daryle Walker], [*David Abrahams], [*David B. Held], +[*Dirk Gerrits], [*Douglas Gregor], [*Hartmut Kaiser], [*Iain K.Hanson], +[*Juan Carlos Arevalo-Baeza], [*Larry Evans], [*Martin Wille], +[*Mattias Flodin], [*Noah Stein], [*Nuno Lucas], [*Peter Dimov], +[*Peter Simons], [*Petr Kocmid], [*Ross Smith], [*Scott Kirkwood], +[*Steve Cleary], [*Thorsten Ottosen], [*Tom Wenisch], [*Vladimir Prus] + +Finally thanks to SourceForge for hosting the Spirit project and Boost: a C++ +community comprised of extremely talented library authors who participate in +the discussion and peer review of well crafted C++ libraries. + +[endsect] diff --git a/doc/faq.qbk b/doc/faq.qbk new file mode 100644 index 000000000..aff508db6 --- /dev/null +++ b/doc/faq.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section FAQ] +[endsect] diff --git a/doc/html/images/FlowOfControl.png b/doc/html/images/FlowOfControl.png new file mode 100644 index 000000000..2e131c084 Binary files /dev/null and b/doc/html/images/FlowOfControl.png differ diff --git a/doc/html/images/Thumbs.db b/doc/html/images/Thumbs.db new file mode 100644 index 000000000..903c063e1 Binary files /dev/null and b/doc/html/images/Thumbs.db differ diff --git a/doc/html/images/TokenStructure.png b/doc/html/images/TokenStructure.png new file mode 100644 index 000000000..494badd9d Binary files /dev/null and b/doc/html/images/TokenStructure.png differ diff --git a/doc/introduction.qbk b/doc/introduction.qbk new file mode 100644 index 000000000..204861eee --- /dev/null +++ b/doc/introduction.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Introduction] +[endsect] diff --git a/doc/lex.qbk b/doc/lex.qbk new file mode 100644 index 000000000..73a69e286 --- /dev/null +++ b/doc/lex.qbk @@ -0,0 +1,50 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section __lex__] + +[include lex/introduction.qbk] + +[section __lex__ Tutorials] +[include lex/lexer_tutorials.qbk] +[include lex/lexer_quickstart1.qbk] +[include lex/lexer_quickstart2.qbk] +[include lex/lexer_quickstart3.qbk] +[endsect] + +[section Abstracts] +[section Lexer Primitives] +[include lex/lexer_primitives.qbk] +[include lex/tokens_values.qbk] +[include lex/token_definition.qbk] +[endsect] +[include lex/tokenizing.qbk] +[include lex/lexer_semantic_actions.qbk] +[include lex/lexer_static_model.qbk] +[include lex/parsing_using_a_lexer.qbk] +[include lex/lexer_attributes.qbk] +[include lex/lexer_states.qbk] +[endsect] + +[section Quick Reference] +[endsect] + +[section Reference] +[section Concepts] +[include reference/lex/lexer.qbk] +[include reference/lex/token.qbk] +[include reference/lex/tokendef.qbk] +[include reference/lex/tokenset.qbk] +[endsect] +[include reference/lex/lexer_class.qbk] +[include reference/lex/token_class.qbk] +[include reference/lex/tokendef_class.qbk] +[include reference/lex/tokenset_class.qbk] +[endsect] + +[endsect] diff --git a/doc/lex/introduction.qbk b/doc/lex/introduction.qbk new file mode 100644 index 000000000..c333552d9 --- /dev/null +++ b/doc/lex/introduction.qbk @@ -0,0 +1,137 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Introduction to __lex__] + +Lexical scanning is the process of analyzing the stream of input characters and +separating it into strings called tokens, separated by whitespace. +Most compiler texts start here, and devote several chapters to discussing +various ways to build scanners. __lex__ is a library built to take care of the +complexities of creating a lexer for your grammar (in this documentation we +will use the terms 'lexical analyzer', 'lexer' and 'scanner' interchangably). +All it needs to create a lexer is to know the set of patterns describing the +different tokens you want to recognize in the input. To make this a bit more +formal, here are some definitions: + +* A token is a sequence of consecutive characters having a collective meaning. + Tokens may have attributes specific to the token type, carrying additional + information about the matched character sequence. +* A pattern is a rule expressed as a regular expression and describing how a + particular token can be formed. For example, [^\[A-Za-z\]\[A-Za-z_0-9\]*] is + a pattern for a rule matching C++ identifiers. +* Characters between tokens are called whitespace; these include spaces, tabs, + newlines, and formfeeds. Many people also count comments as whitespace, + though since some tools such as lint look at comments, this conflation is not + perfect. + +[heading Why Using a Separate Lexer] + +Typically, lexical scanning is done in a separate module from the parser, +feeding the parser with a stream of input tokens only. Now, theoretically it is +not necessary to do this separation. In the end there is only one set of +syntactical rules defining the language, so in theory we could write the whole +parser in one module. In fact, __qi__ allows to write parsers without using a +lexer, parsing the input character stream directly, and for the most part this +is the way __spirit__ has been used since its invention. + +However, the separation has both practical and theoretical bases and proves to +be very useful in practical applications. In 1956, Noam Chomsky defined the +"Chomsky Hierarchy" of grammars: + +* Type 0: Unrestricted grammars (e.g., natural languages) +* Type 1: Context-Sensitive grammars +* Type 2: Context-Free grammars +* Type 3: Regular grammars + +The complexity of these grammars increases from regular grammars being the +simplest to unrestricted grammars being the most complex. Similarily, the +complexity of the recognizers for these grammars increases. Although, a few +features of some programming languages (such as C++) are Type 1, fortunately +for the most part programming languages can be described using only the Types 3 +and 2. The neat part about these two types is that they are well known and the +ways to parse them are well understood. It has been shown that any regular +grammar can be parsed using a state machine (finite automaton). Similarly, +context-free grammars can always be parsed using a push-down automaton +(essentially a state machine augmented by a stack). + +In real programming languages and practical grammars the parts that can be +handled as regular expressions tend to be the lower-level parts, such as the +definition of an identifier or of an integer value: + + letter := [a-zA-Z] + digit := [0-9] + + identifier := letter [ letter | digit ]* + integer := digit* + +Higher level parts of practical grammars tend to be more complex and can't be +implemented using plain regular expressions anymore. We need to store +information on the built-in hardware stack while recursing the grammar +hierarchy, and that in fact this is the preferred approach used for top-down +parsing. Since it takes a different kind of abstract machine to parse the two +types of grammars, it proved to be efficient to separate the lexical scanner +into a separate module which is built around the idea of a state machine. The +goal here is to use the simplest parsing technique needed for the job. + +Another, more practical reason for separating the scanner from the parser is +the need for backtracking during parsing. The input data is a stream of +characters, which is often thought to be processed left to right without any +backtracking. Unfortunately, in practice most of the time that isn't possible. +Almost every language has certain keywords such as IF, FOR, and WHILE. The +decision if a certain character sequence actually comprises a keyword or just +an identifier often can be made only after seeing the first delimiter /after/ +it. This already is a limited form of backtracking, since we need to store the +string long enough to be able to make the decision. The same is true for more +coarse grained language features such as nested IF/ELSE statements, where the +decision about to which IF belongs the last ELSE statement can be made only +after seeing the whole construct. + +So the structure of a conventional compiler often involves splitting up the +functions of the lower-level and higher-level parsing. The lexical scanner +deals with things at the character level, collecting characters into strings, +converting character sequence into different representations as integers, etc., +and passing them along to the parser proper as indivisible tokens. It's also +considered normal to let the scanner do additional jobs, such as identifying +keywords, storing identifiers in tables, etc. + +Now, __spirit__ follows this structure, where __lex__ can be used to implement +state machine based recognizers, while __qi__ can be used to build recognizers +for context free grammars. Since both modules are seemlessly integrated with +each other and with the C++ target language it is even possible to use the +provided functionality to build more complex grammar recognizers. + +[heading Advantages of using __lex__] + +The advantage of using __lex__ to create the lexical analyzer over using more +traditional tools such as __flex__ is its carefully crafted integration with +the __spirit__ library and the C++ host language. You don't need any external +tools to generate the code, your lexer will be perfectly integrated with the +rest of your program, making it possible to freely access any context +information and data structure. Since the C++ compiler sees all the code it +will generate optimal code nomatter what configuration options have been chosen +by the user. __lex__ gives you all the features you could get from a similar +__flex__ program without the need to leave C++ as a host language: + +* the definition of tokens is done using regular expressions (patterns) +* the token definitions can refer to special substitution string (pattern + macros) simplifying pattern definitions +* the generated lexical scanner may have multiple start states +* it is possible to attach code to any of the token definitions; this code gets + executed whenever the corresponding token pattern has been matched + +Even if it is possible to use __lex__ to generate C++ code representing +the lexical analyzer (we will refer to that as the /static/ model, described in +more detail in the section __sec_lex_static_model__) - a model +very similar to the way __flex__ operates - we will mainly focus on the +opposite, the /dynamic/ model. You can directly integrate the token definitions +into your C++ program, building the lexical analyzer dynamicly at runtime. The +dynamic model is something not supported by __flex__ or other lexical scanner +generators (such as __re2c__, __ragel__, etc.). But it is very flexible and +allows to speed up the development of your application. + +[endsect] diff --git a/doc/lex/lexer_attributes.qbk b/doc/lex/lexer_attributes.qbk new file mode 100644 index 000000000..517f28108 --- /dev/null +++ b/doc/lex/lexer_attributes.qbk @@ -0,0 +1,12 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Attributes] + + +[endsect] diff --git a/doc/lex/lexer_primitives.qbk b/doc/lex/lexer_primitives.qbk new file mode 100644 index 000000000..0cf96ad3e --- /dev/null +++ b/doc/lex/lexer_primitives.qbk @@ -0,0 +1,15 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Primitives] + +[/ Describe the primitive lexer constructs, such as token_def, token_set? ] +[/ Describe the primitive lexer constructs usable in parsers, such as + in_state[], set_state(), token(), etc. ] + +[endsect] diff --git a/doc/lex/lexer_quickstart1.qbk b/doc/lex/lexer_quickstart1.qbk new file mode 100644 index 000000000..03235ca4a --- /dev/null +++ b/doc/lex/lexer_quickstart1.qbk @@ -0,0 +1,97 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quickstart 1 - A word counter using __lex__] + +__lex__ is very modular, which follows the general building principle of the +__spirit__ libraries. You never pay for features you don't use. It is nicely +integrated with the other parts of __spirit__ but nevertheless can be used +separately to build standalone lexical analyzers. +The first quick start example describes a standalone application: +counting characters, words and lines in a file, very similar to what the well +known Unix command `wc` is doing (for the full example code see here: +[@../../example/lex/word_count_functor.cpp word_count_functor.cpp]). + +[import ../example/lex/word_count_functor.cpp] + + +[heading Prerequisites] + +The only required `#include` specific to /Spirit.Lex/ follows. It is a wrapper +for all necessary definitions to use /Spirit.Lex/ in a standalone fashion, and +on top of the __lexertl__ library. Additionally we `#include` two of the Boost +headers to define `boost::bind()` and `boost::ref()`. + +[wcf_includes] + +To make all the code below more readable we introduce the following namespaces. + +[wcf_namespaces] + + +[heading Defining Tokens] + +The most important step while creating a lexer using __lex__ is to define the +tokens to be recognized in the input sequence. This is normally done by +defining the regular expressions describing the matching character sequences, +and optionally their corresponding token ids. Additionally the defined tokens +need to be associated with an instance of a lexer object as provided by the +library. The following code snippet shows how this can be done using __lex__. + +[wcf_token_definition] + + +[heading Doing the Useful Work] + +We will use a setup, where we want the __lex__ library to invoke a given +function after any of of the generated tokens is recognized. For this reason +we need to implement a functor taking at least the generated token as an +argument and returning a boolean value allowing to stop the tokenization +process. The default token type used in this example carries a token value of +the type `iterator_range` pointing to the matched range in the +underlying input sequence. + +[wcf_functor] + +All what's left is to write some boilerplate code helping to tie together the +pieces described so far. To simplify this example we call the `lex::tokenize()` +function implemented in __lex__ (for a more detailed description of this +function see here: __fixme__), even if we could have written a loop to iterate +over the lexer iterators [`first`, `last`) as well. + + +[heading Pulling Everything Together] + +[wcf_main] + + +[heading Comparing __lex__ with __flex__] + +This example was deliberately chosen to be similar as much as possible to the +equivalent __flex__ program (see below), which isn't too different from what +has to be written when using __lex__. + +[note Interestingly enough, performance comparisons of lexical analyzers + written using __lex__ with equivalent programs generated by + __flex__ show that both have comparable execution speeds! + Generally, thanks to the highly optimized __lexertl__ library and + due its carefully designed integration with __spirit__ the + abstraction penalty to be paid for using __lex__ is neglectible. +] + +The remaining examples in this tutorial will use more sophisticated features +of __lex__, mainly to allow further simplification of the code to be written, +while maintaining the similarity with corresponding features of __flex__. +__lex__ has been designed to be as much as possible similar to __flex__, that +is why this documentation will provide the corresponding __flex__ code for the +shown __lex__ examples almost everywhere. So consequently, here is the __flex__ +code corresponding to the example as shown above. + +[wcf_flex_version] + +[endsect] diff --git a/doc/lex/lexer_quickstart2.qbk b/doc/lex/lexer_quickstart2.qbk new file mode 100644 index 000000000..1e32ab71a --- /dev/null +++ b/doc/lex/lexer_quickstart2.qbk @@ -0,0 +1,133 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quickstart 2 - A better word counter using __lex__] + +People knowing __flex__ will probably complain about the example from the +section __sec_lex_quickstart_1__ as being overly complex and not being +written to leverage the possibilities provided by this tool. In particular the +previous example did not directly use the lexer actions to count the lines, +words and characters. So the example provided in this step of the tutorial will +show how to use semantic actions in __lex__. Even if it still +will allow to count text elements only it introduces other new concepts and +configuration options along the lines (for the full example code +see here: [@../../example/lex/word_count_lexer.cpp word_count_lexer.cpp]). + +[import ../example/lex/word_count_lexer.cpp] + + +[heading Prerequisites] + +In addition to the only required `#include` specific to /Spirit.Lex/ this +example needs to include a couple of header files from the __phoenix2__ +library. This example shows how to attach functors to token definitions, which +could be done using any type of C++ technique resulting in a callable object. +Using __phoenix2__ for this task simplifies things and avoids adding +dependencies to other libraries (__phoenix2__ is already in use for +__spirit__ anyway). + +[wcl_includes] + +To make all the code below more readable we introduce the following namespaces. + +[wcl_namespaces] + +To give a preview at what to expect from this example, here is the flex program +which has been used as the starting point. The useful code is directly included +inside the actions associated with each of the token definitions. + +[wcl_flex_version] + + +[heading Semantic Actions in __lex__] + +__lex__ uses a very similar way of associating actions with the token +definitions (which should look familiar to anybody knowlegdeable with +__spirit__ as well): specifying the operations to execute inside of a pair of +`[]` brackets. In order to be able to attach semantic actions to token +definitions for each of them there is defined an instance of a `token_def<>`. + +[wcl_token_definition] + +The semantics of the shown code is as follows. The code inside the `[]` +brackets will be executed whenever the corresponding token has been matched by +the lexical analyzer. This is very similar to __flex__, where the action code +associated with a token definition gets executed after the recognition of a +matching input sequence. The code above uses functors constructed using +__phoenix2__, but it is possible to insert any C++ functor as long as it +exposes the interface: + + void f (Range r, std::size_t id, Context& ctx, bool& matched); + +[variablelist where: + [[`Range r`] [This is a `boost::iterator_range` holding two + iterators pointing to the matched range in the + underlying input sequence. The type of the + held iterators is the same as specified while + defining the type of the `lexertl_lexer<...>` + (its first template parameter).]] + [[`std::size_t id`] [This is the token id for the matched token.]] + [[`Context& ctx`] [This is a reference to a lexer specific, + unspecified type, providing the context for the + current lexer state. It can be used to access + different internal data items and is needed for + lexer state control from inside a semantic + action.]] + [[`bool& matched`] [This boolean value is pre/initialized to `true`. + If the functor sets it to `false` the lexer + stops calling any semantic actions attached to + this token and behaves as if the token have not + been matched in the first place.]] +] + +Even if it is possible to write your own functor implementations, the preferred +way of defining lexer semantic actions is to use __phoenix2__. In this case you +can access the three parameters described in the table above by using the +predefined __phoenix2__ placeholders: `_1` for the iterator range, `_2` for the +token id, `_3` for the reference to the lexer state, and `_4` for the reference +to the boolean value signaling the outcome of the semantic action. + +[important All placeholders (`_1`, `_2`, etc.) used in /lexer/ semantic + actions in conjunction with functors created based on __phoenix2__ + need to be imported from the `namespace boost::phoenix::arg_names` + (and *not* `namespace boost::spirit::arg_names`, which is + different from using placeholders in __qi__ or __karma__). + Using the wrong placeholders leads to subtle compilation errors + which are difficult to backtrack to their cause. +] + + +[heading Associating Token Definitions with the Lexer] + +If you compare the with the code from __sec_lex_quickstart_1__ with regard to +the way how token definitions are associated with the lexer, you will notice +a different syntax being used here. If in the previous example we have been +using the `self.add()` style of the API, then here we directly assign the token +definitions to `self`, combining the different token definitions using the `|` +operator. Here is the code snippet again: + + self = word [++ref(w), ref(c) += distance(_1)] + | eol [++ref(c), ++ref(l)] + | any [++ref(c)] + ; + +This way we have a very powerful and natural way of building the lexical +analyzer. If translated into English this may be read as: The lexical analyer +will recognize ('`=`') tokens as defined by any of ('`|`') the token +definitions `word`, `eol`, and `any`. + +A second difference to the previous example is that we do not explicitly +specify any token ids to use for the separate tokens. Using semantic actions to +trigger some useful work free'd us from the need to define these. To ensure +every token gets assigned a id the __lex__ library internally assigns unique +numbers to the token definitions, starting with the constant defined by +`boost::spirit::lex::min_token_id`. + + + +[endsect] diff --git a/doc/lex/lexer_quickstart3.qbk b/doc/lex/lexer_quickstart3.qbk new file mode 100644 index 000000000..5a9cc5cad --- /dev/null +++ b/doc/lex/lexer_quickstart3.qbk @@ -0,0 +1,151 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quickstart 3 - Counting Words Using a Parser] + +The whole purpose of integrating __lex__ as part of the __spirit__ library was +to add a library allowing to merge lexical analysis with the parsing +process as defined by a __spirit__ grammar. __spirit__ parsers read their input +from an input sequence accessed by iterators. So naturally, we chose iterators +to be used as the interface beween the lexer and the parser. A second goal of +the lexer/parser integration was to enable the usage of possibly different +lexical analyzer libraries. The utilization of iterators seemed to be the +right choice from this standpoint as well, mainly because these can be used as +an abstraction layer hiding implementation specifics of the used lexer +library. The [link spirit.lex.flowcontrol picture] below shows the common +flow control implemented while parsing combined with lexical analysis. + +[fig ./images/FlowOfControl.png..The common flow control implemented while parsing combined with lexical analysis..spirit.lex.flowcontrol] + +Another problem related to the integration of the lexical analyzer with the +parser was to find a way how the defined tokens syntactically could be blended +with the grammar definition syntax of __spirit__. For tokens defined as +instances of the `token_def<>` class the most natural way of integration was +to allow to directly use these as parser components. Semantically these parser +components succeed matching their input whenever the corresponding token type +has been matched by the lexer. This quick start example will demonstrate this +(and more) by counting words again, simply by adding up the numbers inside +of semantic actions of a parser (for the full example code see here: +[@../../example/lex/word_count.cpp word_count.cpp]). + + +[import ../example/lex/word_count.cpp] + + +[heading Prerequisites] + +This example uses two of the __spirit__ library components: __lex__ and __qi__, +consequently we have to `#include` the corresponding header files. Again, we +need to include a couple of header files from the __phoenix2__ library. This +example shows how to attach functors to parser components, which +could be done using any type of C++ technique resulting in a callable object. +Using __phoenix2__ for this task simplifies things and avoids adding +dependencies to other libraries (__phoenix2__ is already in use for +__spirit__ anyway). + +[wcp_includes] + +To make all the code below more readable we introduce the following namespaces. + +[wcp_namespaces] + + +[heading Defining Tokens] + +If compared to the two previous quick start examples (__sec_lex_quickstart_1__ +and __sec_lex_quickstart_2__) the token definition class for this example does +not reveal any surprises. However, it uses lexer token definition macros to +simplify the composition of the regular expressions, which will be described in +more detail in the section __fixme__. Generally, any token definition is usable +without modification either for a standalone lexical analyzer or in conjunction +with a parser. + +[wcp_token_definition] + + +[heading Using Token Definition Instances as Parsers] + +While the integration of lexer and parser in the control flow is achieved by +using special iterators wrapping the lexical analyzer, we still nead a means of +expressing in the grammar what tokens to match and where. The token definition +class above uses three different ways of defining a token: + +* Using an instance of a `token_def<>`, which is handy whenever you need to + specify a token attribute (for more information about lexer related + attributes please look here: __sec_lex_attributes__). +* Using a single character as the token, in this case the character represents + itself as a token, where the token id is the ASCII character value. +* Using a regular expression represented as a string, where the token id needs + to be specified explicitly to make the token accessible from the grammar + level. + +All three token definition methods require a different method of grammar +integration. But as you can see from the following code snippet, each of this +methods is straightforward and blends the corresponding token instance +naturally with the surrounding __qi__ grammar syntax. + +[table + [[Token definition] [Parser integration]] + [[`token_def<>`] [The `token_def<>` instance is directly usable as a + parser component. Parsing of this component will + succeed if the regular expression used to define + this has been matched successfully.]] + [[single character] [The single character is directly usable in the + grammar, under certain circumstances it needs to be + wrapped by a `char_()` parser component, though. + Parsing of this component will succeed if the + single character has been matched.]] + [[explicit token id] [To use an explicit token id in a __qi__ grammar you + are required to wrap it with the special `token()` + parser component. Parsing of this component will + succeed if the current token has the same token + id as specified in the expression `token()`.]] +] + +The grammar definition below uses each of the three types demonstrating their +usage. + +[wcp_grammar_definition] + +As already described (see: __sec_qi_karma_attributes__), the __qi__ parser +library builds upon a set of of fully attributed parser components. +Consequently, all the token definitions do support the this attribute model as +well. The most natural way of implementing this was to use the token values as +the attributes exposed by the parser component corresponding to the token +definition (you can read more about this topic here: __sec_lex_tokenvalues__). +The example above takes advantage of the full integration of the token values +as the `token_def<>`'s parser attributes: the `word` token definition is +declared as a `token_def`, making every instance of a `word` token +carry the string representation of the matched input sequence as its value. +The semantic action attached to `tok.word` receives this string (represented by +the `_1` placeholder) and uses it to calculate the number of matched +characters: `ref(c) += size(_1)`. + +[important All placeholders (`_1`, `_2`, etc.) used in /parser/ semantic + actions in conjunction with functors created based on __phoenix2__ + need to be imported from the `namespace boost::spirit::arg_names` + (and *not* `namespace boost::phoenix::arg_names`, which is + different from using placeholders in __lex__). + Using the wrong placeholders leads to subtle compilation errors + which are difficult to backtrack to their cause. +] + + +[heading Pulling Everything Together] + +The main function needs to implement a bit more logic now as we have to +initialize and start not only the lexical analysis but the parsing process as +well. The three type definitions (`typedef` statements) simplify the creation +of the lexical analyzer and the grammar. After reading the contents of the +given file into memory it calls the function __api_tokenize_and_parse__ to +initialize the lexical analysis and parsing processes. + +[wcp_main] + + +[endsect] diff --git a/doc/lex/lexer_semantic_actions.qbk b/doc/lex/lexer_semantic_actions.qbk new file mode 100644 index 000000000..48d95726e --- /dev/null +++ b/doc/lex/lexer_semantic_actions.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Semantic Actions] +[endsect] diff --git a/doc/lex/lexer_states.qbk b/doc/lex/lexer_states.qbk new file mode 100644 index 000000000..628e4b4c4 --- /dev/null +++ b/doc/lex/lexer_states.qbk @@ -0,0 +1,21 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer States] + + +[heading Controlling the Lexer State from Lexer Semantic Actions] + + +[heading Controlling the Lexer State from Parser Semantic Actions] + + +[heading Using a Lexer State for the Skip Parser] + + +[endsect] diff --git a/doc/lex/lexer_static_model.qbk b/doc/lex/lexer_static_model.qbk new file mode 100644 index 000000000..5f828706f --- /dev/null +++ b/doc/lex/lexer_static_model.qbk @@ -0,0 +1,119 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section The /Static/ Lexer Model] + +The documentation of __lex__ so far mostly was about describing the features of +the /dynamic/ model, where the tables needed for lexical analysis are generated +from the regular expressions at runtime. The big advantage of the dynamic model +is its flexibility, and its integration with the __spirit__ library and the C++ +host language. Its big disadvantage is the need to spend additional runtime to +generate the tables, which especially might be a limitation for larger lexical +analyers. The /static/ model strives to build upon the smooth integration with +__spirit__ and C++, and reuses large parts of the __lex__ library as described +so far, while overcoming the additional runtime requirements by using +pre-generated tables and tokenizer routines. To make the code generation as +simple as possible, it is possible reuse the token definition types developed +using the /dynamic/ model without any changes. As will be shown in this +section, building a code generator based on an existing token definition type +is a matter of writing 3 lines of code. + +Assuming you already built a dynamic lexer for your problem, there are two more +steps needed to create a static lexical analyzer using __lex__: + +# generating the C++ code for the static analyzer (including the tokenization + function and corresponding tables), and +# modifying the dynamic lexical anlyzer to use the generated code. + +Both steps are described in more detail in the two sections below (for the full +source code used in this example see the code here: +[@../../example/lex/static_lexer/word_count_tokens.hpp the common token definition], +[@../../example/lex/static_lexer/word_count_generate.cpp the code generator], +[@../../example/lex/static_lexer/word_count_static.hpp the generated code], and +[@../../example/lex/static_lexer/word_count_static.cpp the static lexical analyzer]). + +[import ../example/lex/static_lexer/word_count_tokens.hpp] +[import ../example/lex/static_lexer/word_count_static.cpp] +[import ../example/lex/static_lexer/word_count_generate.cpp] + +But first we provide the code snippets needed to understand the further +descriptions. Both, the definition of the used token identifier and the of the +token definition class in this example are put into a separate header file to +make these available to the code generator and the static lexical analyzer. + +[wc_static_tokenids] + +The important point here is, that the token definition class is not different +from a similar class to be used for a dynamic lexical analyzer. The library +has been designed in a way, that all components (dynamic lexical analyzer, code +generator, and static lexical analyzer) can reuse the very same token definition +syntax. + +[wc_static_tokendef] + +The only thing changing between the three different use cases is the template +parameter used to instantiate a concrete token definition. Fot the dynamic +model and the code generator you probably will use the __class_lexertl_lexer__ +template, where for the static model you will use the +__class_lexertl_static_lexer__ type as the template parameter. + +This example not only shows how to build a static lexer, but it additionally +demonstrates, how such a lexer can be used for parsing in conjunction with a +__qi__ grammar. For completeness we provide the simple grammar used in this +example. As you can see, this grammar does not have any dependencies on the +static lexical analyzer, and for this reason it is not different from a grammar +used either without a lexer or using a dynamic lexical analyzer as described +before. + +[wc_static_grammar] + + +[heading Generating the Static Analyzer] + +The first additional step to perform in order to create a static lexical +analyzer is to create a small standalone program for creating the lexer tables +and the corresponding tokenization function. For this purpose the __lex__ +library exposes a special API - the function __api_generate_static__. It +implements the whole code generator, no further code is needed. All what it +takes to invoke this function is to supply a token definition instance, an +output stream to use to generate the code to, and an optional string to be used +as a prefix for the name of the generated function. All in all just a couple +lines of code. + +[wc_static_generate_main] + +The shown code generator will generate output, which should be stored in a file +for later inclusion into the static lexical analzyer as shown in the next +topic (the full generated code can be viewed +[@../../example/lex/static_lexer/word_count_static.hpp here]). + + +[heading Modifying the Dynamic Analyzer] + +The second required step to convert an existing dynamic lexer into a static one +is to change your main program at two places. First, you need to change the +type of the used lexer (that is the template parameter used while instantiating +your token definition class). While in the dynamic model we have been using the +__class_lexertl_lexer__ template, we now need to change that to the +__class_lexertl_static_lexer__ type. The second change is tightly related to +the first one and involves correcting the corresponding `#include` statement to: + +[wc_static_include] + +Otherwise the main program is not different from an equivalent program using +the dynamic model. This feature makes it really easy for instance to develop +the lexer in dynamic mode and to switch to the static mode after the code has +been stabilized. The simple generator application showed above enables the +integration of the code generator into any existing build process. The +following code snippet provides the overall main function, highlighting +the code to be changed. + +[wc_static_main] + + +[endsect] diff --git a/doc/lex/lexer_tutorials.qbk b/doc/lex/lexer_tutorials.qbk new file mode 100644 index 000000000..8438bcf36 --- /dev/null +++ b/doc/lex/lexer_tutorials.qbk @@ -0,0 +1,59 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section __lex__ Tutorials Overview] + +The __lex__ library implements several components on top of possibly different +lexer generator libraries. It exposes a pair of iterators, which, when +dereferenced, return a stream of tokens generated from the underlying character +stream. The generated tokens are based on the token definitions supplied by the +user. + +Currently, __lex__ is built on top of Ben Hansons excellent __lexertl__ +library (which is a proposed Boost library). __lexertl__ provides the necessary +functionality to build state +machines based on a set of supplied regular expressions. But __lex__ is not +restricted to be used with __lexertl__. We expect it to be usable in +conjunction with any other lexical scanner generator library, all what needs +to be implemented is a set of wrapper objects exposing a well defined +interface as described in this documentation. + +[note For the sake of clarity all examples in this documentation assume + __lex__ to be used on top of __lexertl__.] + +Building a lexer using __lex__ is highly configurable, where most of this +configuration has to be done at compile time. Almost all of the configurable +parameters have generally useful default values, though, which means that +starting a project is easy and straightforward. Here is a (non-complete) list +of features you can tweak to adjust the generated lexer instance to the actual +needs: + +* Select and customize the token type to be generated by the lexer instance. +* Select and customize the token value types the generated token instances will + be able to hold. +* Select the iterator type of the underlying input stream, which will be used + as the source for the character stream to tokenize. +* Customize the iterator type returned by the lexer to enable debug support, + special handling of certain input sequences, etc. +* Select the /dynamic/ or the /static/ runtime model for the lexical + analyzer. + +Special care has been taken during the development of the library that +optimal code will be generated regardless of the configuration options +selected. + +The series of tutorial examples of this section will guide you through some +common use cases helping to understand the big picture. The first two quick +start examples (__sec_lex_quickstart_1__ and __sec_lex_quickstart_2__) +introduce the __lex__ library while building two standalone applications, not +being connected to or depending on any other part of __spirit__. The section +__sec_lex_quickstart_3__ demonstrates how to use a lexer in conjunction with a +parser (where certainly the parser is built using __qi__). + +[endsect] + diff --git a/doc/lex/parsing_using_a_lexer.qbk b/doc/lex/parsing_using_a_lexer.qbk new file mode 100644 index 000000000..a94a3267e --- /dev/null +++ b/doc/lex/parsing_using_a_lexer.qbk @@ -0,0 +1,15 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing using a Lexer] + +[/ write about integration of lexer component with __qi__] + +[/ write about iterator interface exposed by a __lex__ lexer] + +[endsect] diff --git a/doc/lex/token_definition.qbk b/doc/lex/token_definition.qbk new file mode 100644 index 000000000..5e64c3c5c --- /dev/null +++ b/doc/lex/token_definition.qbk @@ -0,0 +1,11 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Ways to define Tokens] + +[endsect] diff --git a/doc/lex/tokenizing.qbk b/doc/lex/tokenizing.qbk new file mode 100644 index 000000000..08fde93c9 --- /dev/null +++ b/doc/lex/tokenizing.qbk @@ -0,0 +1,15 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Tokenizing Input Data] + +[heading The tokenize() function] + +[heading The generate_static() function] + +[endsect] diff --git a/doc/lex/tokens_values.qbk b/doc/lex/tokens_values.qbk new file mode 100644 index 000000000..5c26d870f --- /dev/null +++ b/doc/lex/tokens_values.qbk @@ -0,0 +1,207 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section About Tokens and Token Values] + +As already discussed, lexical scanning is the process of analyzing the stream +of input characters and separating it into strings called tokens, most of the +time separated by whitespace. The different token types recognized by a lexical +analyzer often get assigned unique integer token identifiers (token ids). These +token ids arenormally used by the parser to identifiy the current token without +having to look at the matched string again. The __lex__ library is not +different with respect to this, as it uses the token ids as the main means of +identification of the different token types defined for a particular lexical +analyzer. However, it is different from commonly used lexical analyzers in the +sense that it returns (references to) instances of a (user defined) token class +to the user. The only real limitation posed on this token class is consequently, +that it has to carry at least the token id of the token it represents. For more +information about the interface a user defined token type has to expose please +look at the __sec_ref_lex_token__ reference. The library provides a default +token type based on the __lexertl__ library which should be sufficient in most +use cases: the __class_lexertl_token__ type. This section focusses on the +description of general features a token class may implement and how this +integrates with the other parts of the __lex__ library. + +[heading The Anatomy of a Token] + +It is very important to understand the difference between a token definition +(represented by the __class_token_def__ template) and a token itself (for +instance represented by the __class_lexertl_token__ template). + +The token definition is used to describe the main features of a particular +token type, especially: + +* to simplify the definition of a token type using a regular expression pattern + applied while matching this token type, +* to associate a token type with a particular lexer state, +* to optionally assign a token id to a token type, +* to optionally associate some code to execute whenever an instance of this + token type has been matched, +* and to optionally specify the attribute type of the token value. + +The token itself is a data structure returned by the lexer iterators. +Dereferencing a lexer iterator returns a reference to the last matched token +instance. It encapsulates the part of the underlying input sequence matched by +the regular expression used during the definiton of this token type. +Incrementing the lexer iterator invokes the lexical analyzer to +match the next token by advancing the underlying input stream. The token data +structure contains at least the token id of the matched token type, +allowing to identify the matched character sequence. Optionally, the token +instance may contain a token value and/or the lexer state this token instance +was matched in. The following [link spirit.lex.tokenstructure figure] shows the +schematic structure of a token. + +[fig ./images/TokenStructure.png..The structure of a token..spirit.lex.tokenstructure] + +The token value and the token state may be omitted for optimization reasons, +avoiding the token to carry more data than actually required. This +configuration can be achieved by supplying appropriate template parameters +for the __class_lexertl_token__ template while defining the token type. + +The lexer iterator returns the same token type for each of the different +matched token definitions. To accomodate for the possibly different token +/value/ types exposed by the various token types (token definitions), the +general type of the token value is a __boost_variant__. As a minimum (for the +default configuration) this token value variant will be configured to always +hold a __boost_iterator_range__ containing the pair of iterators pointing to +the matched input sequence for this token instance. + +[note If the lexical analyzer is used in conjunction with a __qi__ parser, the + stored __boost_iterator_range__ token value will be converted to the + requested token type (parser attribute) exactly once. This happens at the + time of the first access to the token value requiring the + corresponding type conversion. The converted token value will be stored + in the __boost_variant__ replacing the initially stored iterator range. + This avoids to convert the input sequence to the token value more than + once, thus optimizing the integration of the lexer with __qi__, even + during parser backtracking. +] + +Here is the template prototype of the __class_lexertl_token__ template: + + template < + typename Iterator = char const*, + typename AttributeTypes = mpl::vector0<>, + typename HasState = mpl::true_ + > + struct lexertl_token; + +[variablelist where: + [[Iterator] [This is the type of the iterator used to access the + underlying input stream. It defaults to a plain + `char const*`.]] + [[AttributeTypes] [This is either a mpl sequence containing all + attribute types used for the token definitions or the + type `omitted`. If the mpl sequence is empty (which is + the default), all token instances will store a + `boost::iterator_range` pointing to the start + and the end of the matched section in the input stream. + If the type is `omitted`, the generated tokens will + contain no token value (attribute) at all.]] + [[HasState] [This is either `mpl::true_` or `mpl::false_`, allowing + to control whether the generated token instances will + contain the lexer state they were generated in. The + default is mpl::true_, so all token instances will + contain the lexer state.]] +] + +Normally, during its construction, a token instance always holds the +__boost_iterator_range__ as its token value (except, if it has been defined +using the `omitted` token value type). This iterator range then is +converted in place to the requested token value type (attribute) when it is +requested for the first time. + + +[heading The Physiognomy of a Token Definition] + +The token definitions (represented by the __class_token_def__ template) are +normally used as part of the definition of the lexical analyzer. At the same +time a token definition instance may be used as a parser component in __qi__. + +The template prototype of this class is shown here: + + template< + typename Attribute = unused_type, + typename Char = char + > + class token_def; + +[variablelist where: + [[Attribute] [This is the type of the token value (attribute) + supported by token instances representing this token + type. This attribute type is exposed to the __qi__ + library, whenever this token definition is used as a + parser component. The default attribute type is + `unused_type`, which means the token instance holds a + __boost_iterator_range__ pointing to the start + and the end of the matched section in the input stream. + If the attribute is `omitted` the token instance will + expose no token type at all. Any other type will be + used directly as the token value type.]] + [[Char] [This is the value type of the iterator for the + underlying input sequence. It defaults to `char`.]] +] + +The semantics of the template parameters for the token type and the token +definition type are very similar and interdependent. As a rule of thumb you can +think of the token definition type as the means of specifying everything +related to a single specific token type (such as `identifier` or `integer`). +On the other hand the token type is used to define the general proerties of all +token instances generated by the __lex__ library. + +[important If you don't list any token value types in the token type definition + declaration (resulting in the usage of the default __boost_iterator_range__ + token type) everything will compile and work just fine, just a bit + less efficient. This is because the token value will be converted + from the matched input sequence every time it is requested. + + But as soon as you specify at least one token value type while + defining the token type you'll have to list all value types used for + __class_token_def__ declarations in the token definition class, + otherwise compilation errors will occur. +] + + +[heading Examples of using __class_lexertl_token__] + +Let's start with some examples. We refer to one of the __lex__ examples (for +the full source code of this example please see +[@../../example/lex/example4.cpp example4.cpp]). + +[import ../example/lex/example4.cpp] + +The first code snippet shows an excerpt of the token definition class, the +definition of a couple of token types. Some of the token types do not expose a +special token value (`if_`, `else_`, and `while_`). Their token value will +always hold the iterator range of the matched input sequence only. The token +definitions for the `identifier` and the integer `constant` are specialized +to expose an explicit token type each: `std::string` and `unsigned int`. + +[example4_token_def] + +As the parsers generated by __qi__ are fully attributed, any __qi__ parser +component needs to expose a certain type as its parser attribute. Naturally, +the __class_token_def__ exposes the token value type as its parser attribute, +enabling a smooth integration with __qi__. + +The next code snippet demonstrates how the required token value types are +specified while defining the token type to use. All of the token value types +used for at least one of the token definitions have to be re-iterated for the +token definition as well. + +[example4_token] + +To avoid the token to have a token value at all, the special tag `omitted` can +be used: `token_def` and `lexertl_token`. + + + + + + +[endsect] diff --git a/doc/notes.qbk b/doc/notes.qbk new file mode 100644 index 000000000..e581cba82 --- /dev/null +++ b/doc/notes.qbk @@ -0,0 +1,24 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Notes] + +[section Portability] +[endsect] + +[section Porting from Spirit 1.8.x] +[endsect] + +[section Style Guide] +[include notes/style_guide.qbk] +[endsect] + +[section Techniques] +[endsect] + +[endsect] diff --git a/doc/notes/style_guide.qbk b/doc/notes/style_guide.qbk new file mode 100644 index 000000000..bfd75ee9a --- /dev/null +++ b/doc/notes/style_guide.qbk @@ -0,0 +1,87 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +At some point, especially when there are lots of semantic actions attached to +various points, the grammar tends to be quite difficult to follow. In order to +keep an easy-to-read, consistent en aesthetically pleasing look to the Spirit +code, the following coding styleguide is advised. + +This coding style is adapted and extended from the ANTLR/PCCTS style (Terrence +Parr) and [@http://groups.yahoo.com/group/boost/files/coding_guidelines.html +Boost coding guidelines] (David Abrahams and Nathan Myers) and is the +combined work of Joel de Guzman, Chris Uzdavinis and Hartmut Kaiser. + +* Rule names use std C++ (Boost) convention. The rule name may be very long. +* The '=' is neatly indented 4 spaces below. Like in Boost, use spaces instead + of tabs. +* Breaking the operands into separate lines puts the semantic actions neatly + to the right. +* Semicolon at the last line terminates the rule. +* The adjacent parts of a sequence should be indented accordingly to have all, + what belongs to one level, at one indentation level. + + program + = program_heading [heading_action] + >> block [block_action] + >> '.' + | another_sequence + >> etc + ; + +* Prefer literals in the grammar instead of identifiers. e.g. `"program"` instead + of `PROGRAM`, `'>='` instead of `GTE` and `'.'` instead of `DOT`. This makes it much + easier to read. If this isn't possible (for instance where the used tokens + must be identified through integers) capitalized identifiers should be used + instead. +* Breaking the operands may not be needed for short expressions. + e.g. `*(',' >> file_identifier)` as long as the line does not + exceed 80 characters. +* If a sequence fits on one line, put spaces inside the parentheses + to clearly separate them from the rules. + + program_heading + = no_case["program"] + >> identifier + >> '(' + >> file_identifier + >> *( ',' >> file_identifier ) + >> ')' + >> ';' + ; + +* Nesting directives: If a rule does not fit on one line (80 characters) + it should be continued on the next line intended by one level. The brackets + of directives, semantic expressions (using Phoenix or LL lambda expressions) + or parsers should be placed as follows. + + identifier + = no_case + [ + lexeme + [ + alpha >> *(alnum | '_') [id_action] + ] + ] + ; + +* Nesting unary operators (e.g.Kleene star): Unary rule operators + (Kleene star, `'!'`, `'+'` etc.) should be moved out one space before + the corresponding indentation level, if this rule has a body or a + sequence after it, which does not fit on on line. This makes the + formatting more consistent and moves the rule 'body' at the same + indentation level as the rule itself, highlighting the unary operator. + + block + = *( label_declaration_part + | constant_definition_part + | type_definition_part + | variable_declaration_part + | procedure_and_function_declaration_part + ) + >> statement_part + ; diff --git a/doc/outline.txt b/doc/outline.txt new file mode 100644 index 000000000..7dad9ae6a --- /dev/null +++ b/doc/outline.txt @@ -0,0 +1,97 @@ +# Copyright (C) 2001-2008 Joel de Guzman +# Copyright (C) 2001-2008 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +Preface +What's New +Introduction +Qi and Karma + Tutorials + Abstracts + Parsing Expression Grammar + Parsing and Generating + Primitives + Operators + Attributes + Semantic Actions + Directives + Rules + Grammars + Debugging + Error Handling + Parse Trees and ASTs + Quick Reference + Reference + Concepts + Parser + Generator + Parser Director + Generator Director + Char + String + Numeric + Binary + Directive + Action + Nonterminal + Operator + Stream + Auxiliary + Debug +Lex + Introduction + Tutorials + Abstracts + Parsing using a Lexer + Lexer Primitives + Lexer States + Lexer Attributes + Lexer Semantic Actions + Quick Reference + Reference + Concepts + Lexer + Token + TokenDef + TokenSet + Lexer Class + Token Class + TokenDef Class + TokenSet Class +FAQ +Notes + Portability + Porting from Spirit 1.8.x + Style Guide + Techniques +Rationale +Acknowledgments +References + +----------------------------------------------------------------- + +Concepts Outline: + Description + Notation + Valid Expressions + Expression | Semantics | Return type | Complexity + Type Requirements + Expression | Requirements + Invariants + Models + +Reference Page Outline: + Description + Header + Synopsis + Template parameters + Model of + Objects + Expression Semantics + Expression | Semantics | Return type | Complexity + Example + + diff --git a/doc/preface.qbk b/doc/preface.qbk new file mode 100644 index 000000000..d1415b1c0 --- /dev/null +++ b/doc/preface.qbk @@ -0,0 +1,217 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Preface] + +[:['["Examples of designs that meet most of the criteria for +"goodness" (easy to understand, flexible, efficient) are a recursive- +descent parser, which is traditional procedural code. Another example +is the STL, which is a generic library of containers and algorithms +depending crucially on both traditional procedural code and on +parametric polymorphism.]] [*--Bjarne Stroustrup]] + +[heading History] + +[heading /80s/] + +In the Mid 80s, Joel wrote his first calculator in Pascal. It has been +an unforgettable coding experience. He was amazed how a mutually +recursive set of functions can model a grammar specification. In time, +the skills he acquired from that academic experience became very +practical. Periodically Joel was tasked to do some parsing. For +instance, whenever he needs to perform any form of I/O, even in +binary, he tries to approach the task somewhat formally by writing a +grammar using Pascal- like syntax diagrams and then write a +corresponding recursive-descent parser. This worked very well. + +[heading /90s/] + +The arrival of the Internet and the World Wide Web magnified this +thousand-fold. At one point Joel had to write an HTML parser for a Web +browser project. He got a recursive-descent HTML parser working based +on the W3C formal specifications easily. He was certainly glad that +HTML had a formal grammar specification. Because of the influence of +the Internet, Joel then had to do more parsing. RFC specifications +were everywhere. SGML, HTML, XML, even email addresses and those +seemingly trivial URLs were all formally specified using small EBNF- +style grammar specifications. This made him wish for a tool similar to +big- time parser generators such as YACC and ANTLR, where a parser is +built automatically from a grammar specification. Yet, he wants it to +be extremely small; small enough to fit in my pocket, yet scalable. + +It must be able to practically parse simple grammars such as email +addresses to moderately complex grammars such as XML and perhaps some +small to medium-sized scripting languages. Scalability is a prime +goal. You should be able to use it for small tasks such as parsing +command lines without incurring a heavy payload, as you do when you +are using YACC or PCCTS. Even now that it has evolved and matured to +become a multi-module library, true to its original intent, Spirit can +still be used for extreme micro-parsing tasks. You only pay for +features that you need. The power of Spirit comes from its modularity +and extensibility. Instead of giving you a sledgehammer, it gives you +the right ingredients to create a sledgehammer easily. + +The result was Spirit. Spirit was a personal project that was +conceived when Joel was doing R&D in Japan. Inspired by the GoF's +composite and interpreter patterns, he realized that he can model a +recursive-descent parser with hierarchical-object composition of +primitives (terminals) and composites (productions). The original +version was implemented with run-time polymorphic classes. A parser is +generated at run time by feeding in production rule strings such as: + + "prod ::= {'A' | 'B'} 'C';" + +A compile function compiled the parser, dynamically creating a +hierarchy of objects and linking semantic actions on the fly. A very +early text can be found here: __early_spirit__. + +[heading /2001 to 2006/] + +Version 1.0 to 1.8 was a complete rewrite of the original Spirit +parser using expression templates and static polymorphism, inspired by +the works of Todd Veldhuizen (__todd__exprtemplates__, C++ Report, +June 1995). Initially, the static-Spirit version was meant only to +replace the core of the original dynamic-Spirit. Dynamic-spirit +needed a parser to implement itself anyway. The original employed a +hand-coded recursive-descent parser to parse the input grammar +specification strings. Incidentially it was the time, when Hartmut +joined the Spirit development. + +After its initial "open-source" debut in May 2001, static-Spirit +became a success. At around November 2001, the Spirit website had an +activity percentile of 98%, making it the number one parser tool at +Source Forge at the time. Not bad for such a niche project such as a +parser library. The "static" portion of Spirit was forgotten and +static-Spirit simply became Spirit. The library soon evolved to +acquire more dynamic features. + +Spirit was formally accepted into __boost__ in October 2002. Boost is +a peer-reviewed, open collaborative development effort that is a +collection of free Open Source C++ libraries covering a wide range of +domains. The Boost Libraries have become widely known as an industry +standard for design and implementation quality, robustness, and +reusability. + +[heading /2007/] + +Over the years, especially after Spirit was accepted into Boost, +Spirit has served its purpose quite admirably. The focus of what we'll +now call [*/Classic-Spirit/] (versions prior to 2.0) was on +transduction parsing where the input string is merely translated to an +output string. A lot of parsers are of the transduction type. When the +time came to add attributes to the parser library, it was done rather +in an ad-hoc manner, with the goal being 100% backward compatible with +classic Spirit. Some parsers have attributes, some don't. + +Spirit V2 is another major rewrite. Spirit V2 grammars are fully +attributed (see __attr_grammar__). All parser components have +attributes. To do this efficiently and ellegantly, we had to use a +couple of infrastructure libraries. Some of which haven't been written +yet at the time, some were quite new when Spirit debuted, and some +needed work. __mpl__ is an important infrastructure library, yet is +not sufficient to implement Spirit V2. Another library had to be +written: __fusion__. Fusion sits between MPL and STL --between compile +time and runtime -- mapping types to values. Fusion is a direct +descendant of both MPL and __boost_tuples__ (Fusion is now a full +fledged __boost__ library). __phoenix__ also had to be beefed up to +support Spirit V2. The result is __phoenix2__. Last but not least, +Spirit V2 uses an __todd__exprtemplates__ library called +__boost_proto__. + +[heading New Ideas: Spirit V2] + +Just before the development of Spirit V2 began, Hartmut came across +the __string_template__ library which is a part of the ANTLR parser +framework. It is a Java template engine (with ports for C# and Python) +for generating source code, web pages, emails, or any other formatted +text output. With it, he got the the idea of using a formal notation +(a grammar) to describe the expected structure of an input character +sequence. The same grammar may be used to formalize the structure of a +corresponding output character sequence. This is possible because +parsing, most of the time, is implemented by comparing the input with +the patterns defined by the grammar. If we use the same patterns to +format a matching output, the generated sequence will follow the rules +of the grammar as well. + +This insight lead to the implementation of a grammar driven output generation +library compatibile with the Spirit parser library. As it turned out, parsing +and generation are tightly connected and have very similar concepts. The +duality of these two sides of the same medal is ubiquitous, which +allowed us to build the parser library __qi__ and the generator library +__karma__ using the same component infastructure. + +The idea of creating a lexer library well integrated with the Spirit parsers is +not new. This has been discussed almost for the whole time of the existence of +Classic-Spirit (pre V2) now. Several attempts to integrate existing lexer +libraries and frameworks with Spirit have been made and served as a proof of +concept and usability (for example see __wave__: The Boost C/C++ Preprocessor +Library, and __slex__: a fully dynamic C++ lexer implemented with Spirit). +Based on these experiences we added __lex__: a fully integrated lexer library +to the mix, allowing to take advantage of the power of regular expressions for +token matching, removing pressure from the parser components, simplifying +parser grammars. Again, Spirit's modular structure allowed us to reuse the same +underlying component library as for the parser and generator libraries. + + +[heading How to use this manual] + +Each major section (there are two: __sec_qi_and_karma__, and __sec_lex__) is +roughly divided into 3 parts: + +# Tutorials: A step by step guide with heavily annotated code. These + are meant to get the user acquainted with the library as quickly as + possible. The objective is to build the confidence of the user in + using the library using abundant examples and detailed instructions. + Examples speak volumes. + +# Abstracts: A high level summary of key topics. The objective is to + give the user a high level view of the library, the key concepts, + background and theories. + +# Reference: Detailed formal technical reference. We start with a quick + reference -- an easy to use table that maps into the reference proper. + The reference proper starts with C++ __cpp_concepts__ followed by + models of the concepts. + +Some icons are used to mark certain topics indicative of their relevance. +These icons precede some text to indicate: + +[table Icons + + [[Icon] [Name] [Meaning]] + + [[__note__] [Note] [Generally useful information (an aside that + doesn't fit in the flow of the text)]] + + [[__tip__] [Tip] [Suggestion on how to do something + (especially something that not be obvious)]] + + [[__important__] [Important] [Important note on something to take + particular notice of]] + + [[__caution__] [Caution] [Take special care with this - it may + not be what you expect and may cause bad + results]] + + [[__danger__] [Danger] [This is likely to cause serious + trouble if ignored]] +] + +This documentation is automatically generated by Boost QuickBook documentation +tool. QuickBook can be found in the __boost_tools__. + +[heading Support] + +Please direct all questions to Spirit's mailing list. You can subscribe to the +__spirit_list__. The mailing list has a searchable archive. A search link to +this archive is provided in __spirit__'s home page. You may also read and post +messages to the mailing list through __spirit_general__ (thanks to __gmane__). +The news group mirrors the mailing list. Here is a link to the archives: +__mlist_archive__. + +[endsect] [/ Preface] diff --git a/doc/qi_and_karma.qbk b/doc/qi_and_karma.qbk new file mode 100644 index 000000000..cfd7f5a09 --- /dev/null +++ b/doc/qi_and_karma.qbk @@ -0,0 +1,52 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Qi and Karma] + +[include qi_and_karma/tutorials.qbk] + +[section Abstracts] +[include qi_and_karma/peg.qbk] +[include qi_and_karma/parsing.qbk] +[include qi_and_karma/generating.qbk] +[include qi_and_karma/primitives.qbk] +[include qi_and_karma/operators.qbk] +[include qi_and_karma/attributes.qbk] +[include qi_and_karma/semantic_actions.qbk] +[include qi_and_karma/directives.qbk] +[include qi_and_karma/rules.qbk] +[include qi_and_karma/grammars.qbk] +[include qi_and_karma/debugging.qbk] +[include qi_and_karma/error_handling.qbk] +[include qi_and_karma/parse_trees_and_asts.qbk] +[endsect] + +[/section Quick Reference] +[include qi_and_karma/quick_reference.qbk] +[/endsect] + +[section Reference] +[section Concepts] +[include reference/qi_and_karma/parser.qbk] +[include reference/qi_and_karma/generator.qbk] +[endsect] +[include reference/qi_and_karma/char.qbk] +[include reference/qi_and_karma/string.qbk] +[include reference/qi_and_karma/numeric.qbk] +[include reference/qi_and_karma/binary.qbk] +[include reference/qi_and_karma/directive.qbk] +[include reference/qi_and_karma/action.qbk] +[include reference/qi_and_karma/nonterminal.qbk] +[include reference/qi_and_karma/operator.qbk] +[include reference/qi_and_karma/stream.qbk] +[include reference/qi_and_karma/auxiliary.qbk] +[include reference/qi_and_karma/debug.qbk] +[endsect] + +[endsect] + diff --git a/doc/qi_and_karma/attributes.qbk b/doc/qi_and_karma/attributes.qbk new file mode 100644 index 000000000..f4eab850e --- /dev/null +++ b/doc/qi_and_karma/attributes.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Attributes] +[endsect] diff --git a/doc/qi_and_karma/debugging.qbk b/doc/qi_and_karma/debugging.qbk new file mode 100644 index 000000000..adbbb84c1 --- /dev/null +++ b/doc/qi_and_karma/debugging.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Debugging] +[endsect] diff --git a/doc/qi_and_karma/directives.qbk b/doc/qi_and_karma/directives.qbk new file mode 100644 index 000000000..14ba5d9fc --- /dev/null +++ b/doc/qi_and_karma/directives.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Directives] +[endsect] diff --git a/doc/qi_and_karma/error_handling.qbk b/doc/qi_and_karma/error_handling.qbk new file mode 100644 index 000000000..39cfd5bae --- /dev/null +++ b/doc/qi_and_karma/error_handling.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Error Handling] +[endsect] diff --git a/doc/qi_and_karma/generating.qbk b/doc/qi_and_karma/generating.qbk new file mode 100644 index 000000000..1f9a01ab6 --- /dev/null +++ b/doc/qi_and_karma/generating.qbk @@ -0,0 +1,24 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing and Generating] + +[heading The API functions exposed by __qi__ ] + +[heading The parse() function] + +[heading The phrase_parse() function] + +[heading The tokenize_and_parse() function] + +[heading The tokenize_and_phrase_parse() function] + +[heading The make_parser() function] + +[endsect] + diff --git a/doc/qi_and_karma/grammars.qbk b/doc/qi_and_karma/grammars.qbk new file mode 100644 index 000000000..1ff8814ec --- /dev/null +++ b/doc/qi_and_karma/grammars.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Grammars] +[endsect] diff --git a/doc/qi_and_karma/operators.qbk b/doc/qi_and_karma/operators.qbk new file mode 100644 index 000000000..fbd635ce5 --- /dev/null +++ b/doc/qi_and_karma/operators.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Operators] +[endsect] diff --git a/doc/qi_and_karma/parse_trees_and_asts.qbk b/doc/qi_and_karma/parse_trees_and_asts.qbk new file mode 100644 index 000000000..b0028bc05 --- /dev/null +++ b/doc/qi_and_karma/parse_trees_and_asts.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parse Trees and ASTs] +[endsect] diff --git a/doc/qi_and_karma/parsing.qbk b/doc/qi_and_karma/parsing.qbk new file mode 100644 index 000000000..3fd1e7af4 --- /dev/null +++ b/doc/qi_and_karma/parsing.qbk @@ -0,0 +1,44 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing] + +Central to the library is the parser. The parser does the actual +work of recognizing a linear input stream of data read sequentially +from start to end by the supplied iterators. The parser attempts to +match the input following a well-defined set of specifications known +as grammar rules. The parser returns a `bool` to report the success or +failure. When successful, the parser calls a client-supplied semantic +action, if there is one. The semantic action extracts structural +information depending on the data passed by the parser and the +hierarchical context of the parser it is attached to. + +Parsers come in different flavors. The Spirit library comes bundled with an extensive set of pre-defined parsers that perform various parsing tasks from the trivial to the complex. The parser, as a concept, has a public conceptual interface contract. Following the contract, anyone can write a conforming parser that will play along well with the library's predefined components. We shall provide a blueprint detailing the conceptual interface of the parser later. + +Clients of the library generally do not need to write their own hand-coded parsers at all. Spirit has an immense repertoire of pre-defined parsers covering all aspects of syntax and semantic analysis. We shall examine this repertoire of parsers in the following sections. In the rare case where a specific functionality is not available, it is extremely easy to write a user-defined parser. The ease in writing a parser entity is the main reason for Spirit's extensibility. + + + + + + + +[heading The API functions exposed by __qi__ ] + +[heading The parse() function] + +[heading The phrase_parse() function] + +[heading The tokenize_and_parse() function] + +[heading The tokenize_and_phrase_parse() function] + +[heading The make_parser() function] + +[endsect] + diff --git a/doc/qi_and_karma/peg.qbk b/doc/qi_and_karma/peg.qbk new file mode 100644 index 000000000..2b4002119 --- /dev/null +++ b/doc/qi_and_karma/peg.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing Expression Grammar] +[endsect] diff --git a/doc/qi_and_karma/primitives.qbk b/doc/qi_and_karma/primitives.qbk new file mode 100644 index 000000000..78e07b53c --- /dev/null +++ b/doc/qi_and_karma/primitives.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Primitives] +[endsect] diff --git a/doc/qi_and_karma/quick_reference.qbk b/doc/qi_and_karma/quick_reference.qbk new file mode 100644 index 000000000..47aa8204b --- /dev/null +++ b/doc/qi_and_karma/quick_reference.qbk @@ -0,0 +1,43 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quick Reference] + +The following tables use some conventions to encode the attribute type exposed +by a component + +[variablelist + [[`attribute_of(P)`] [The component exposes the same attribute as the + component 'P' used as part of the overall + construct]] + [[`value_type(I)`] [The component exposes the value_type of the + underlying iterator 'I' as its attribute type]] +] + +[table Character Parsers + [[Component] [Description] [Attribute]] + [[`char_`] [] [`char`]] + [[`wchar`] [] [`wchar_t`]] + [[`lit`] [] [`unused`]] + [[`wlit`] [] [`unused`]] + [[`'x'`] [] [`unused`]] + [[`L'x'`] [] [`unused`]] + [[`alnum`] [] [`Char`]] + [[`alpha`] [] [`Char`]] + [[`blank`] [] [`Char`]] + [[`cntrl`] [] [`Char`]] + [[`digit`] [] [`Char`]] + [[`graph`] [] [`Char`]] + [[`print`] [] [`Char`]] + [[`punct`] [] [`Char`]] + [[`space`] [] [`Char`]] + [[`xdigit`] [] [`Char`]] + [[`~P`] [] [`attribute_of(P)`]] +] + +[endsect] diff --git a/doc/qi_and_karma/rules.qbk b/doc/qi_and_karma/rules.qbk new file mode 100644 index 000000000..cc4bf59c9 --- /dev/null +++ b/doc/qi_and_karma/rules.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Rules] +[endsect] diff --git a/doc/qi_and_karma/semantic_actions.qbk b/doc/qi_and_karma/semantic_actions.qbk new file mode 100644 index 000000000..b104760c1 --- /dev/null +++ b/doc/qi_and_karma/semantic_actions.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Semantic Actions] +[endsect] diff --git a/doc/qi_and_karma/tutorials.qbk b/doc/qi_and_karma/tutorials.qbk new file mode 100644 index 000000000..dfe8ffd2b --- /dev/null +++ b/doc/qi_and_karma/tutorials.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Tutorials] +[endsect] diff --git a/doc/rationale.qbk b/doc/rationale.qbk new file mode 100644 index 000000000..8bc4bba5b --- /dev/null +++ b/doc/rationale.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Rationale] +[endsect] diff --git a/doc/reference/lex/lexer.qbk b/doc/reference/lex/lexer.qbk new file mode 100644 index 000000000..25cee90d4 --- /dev/null +++ b/doc/reference/lex/lexer.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer] +[endsect] diff --git a/doc/reference/lex/lexer_class.qbk b/doc/reference/lex/lexer_class.qbk new file mode 100644 index 000000000..74c14db2a --- /dev/null +++ b/doc/reference/lex/lexer_class.qbk @@ -0,0 +1,19 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Class] + +[heading The lexertl_lexer Class Implementing the Dynamic Model] + +[heading The lexertl_actor_lexer Class Implementing the Dynamic Model] + +[heading The lexertl_static_lexer Class Implementing the Static Model] + +[heading The lexertl_static_actor_lexer Class Implementing the Static Model] + +[endsect] diff --git a/doc/reference/lex/token.qbk b/doc/reference/lex/token.qbk new file mode 100644 index 000000000..b6d20a676 --- /dev/null +++ b/doc/reference/lex/token.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Token] +[endsect] diff --git a/doc/reference/lex/token_class.qbk b/doc/reference/lex/token_class.qbk new file mode 100644 index 000000000..93d530b62 --- /dev/null +++ b/doc/reference/lex/token_class.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Token Class] +[endsect] diff --git a/doc/reference/lex/tokendef.qbk b/doc/reference/lex/tokendef.qbk new file mode 100644 index 000000000..5137889bb --- /dev/null +++ b/doc/reference/lex/tokendef.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenDef] +[endsect] diff --git a/doc/reference/lex/tokendef_class.qbk b/doc/reference/lex/tokendef_class.qbk new file mode 100644 index 000000000..c9f4056cd --- /dev/null +++ b/doc/reference/lex/tokendef_class.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenDef Class] +[endsect] diff --git a/doc/reference/lex/tokenset.qbk b/doc/reference/lex/tokenset.qbk new file mode 100644 index 000000000..d0737ec0a --- /dev/null +++ b/doc/reference/lex/tokenset.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenSet] +[endsect] diff --git a/doc/reference/lex/tokenset_class.qbk b/doc/reference/lex/tokenset_class.qbk new file mode 100644 index 000000000..18ac25767 --- /dev/null +++ b/doc/reference/lex/tokenset_class.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenSet Class] +[endsect] diff --git a/doc/reference/qi_and_karma/action.qbk b/doc/reference/qi_and_karma/action.qbk new file mode 100644 index 000000000..d61b3789f --- /dev/null +++ b/doc/reference/qi_and_karma/action.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Action] +[endsect] diff --git a/doc/reference/qi_and_karma/auxiliary.qbk b/doc/reference/qi_and_karma/auxiliary.qbk new file mode 100644 index 000000000..fc2465d66 --- /dev/null +++ b/doc/reference/qi_and_karma/auxiliary.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Auxiliary] +[endsect] diff --git a/doc/reference/qi_and_karma/binary.qbk b/doc/reference/qi_and_karma/binary.qbk new file mode 100644 index 000000000..171cf74f0 --- /dev/null +++ b/doc/reference/qi_and_karma/binary.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Binary] +[endsect] diff --git a/doc/reference/qi_and_karma/char.qbk b/doc/reference/qi_and_karma/char.qbk new file mode 100644 index 000000000..9af0c3882 --- /dev/null +++ b/doc/reference/qi_and_karma/char.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Char] +[endsect] diff --git a/doc/reference/qi_and_karma/debug.qbk b/doc/reference/qi_and_karma/debug.qbk new file mode 100644 index 000000000..3383f16d1 --- /dev/null +++ b/doc/reference/qi_and_karma/debug.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Debug] +[endsect] diff --git a/doc/reference/qi_and_karma/directive.qbk b/doc/reference/qi_and_karma/directive.qbk new file mode 100644 index 000000000..e17ee97ca --- /dev/null +++ b/doc/reference/qi_and_karma/directive.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Directive] +[endsect] diff --git a/doc/reference/qi_and_karma/generator.qbk b/doc/reference/qi_and_karma/generator.qbk new file mode 100644 index 000000000..9748c0456 --- /dev/null +++ b/doc/reference/qi_and_karma/generator.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Generator] +[endsect] diff --git a/doc/reference/qi_and_karma/nonterminal.qbk b/doc/reference/qi_and_karma/nonterminal.qbk new file mode 100644 index 000000000..db23dcff4 --- /dev/null +++ b/doc/reference/qi_and_karma/nonterminal.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Nonterminal] +[endsect] diff --git a/doc/reference/qi_and_karma/numeric.qbk b/doc/reference/qi_and_karma/numeric.qbk new file mode 100644 index 000000000..cb41e094e --- /dev/null +++ b/doc/reference/qi_and_karma/numeric.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Numeric] +[endsect] diff --git a/doc/reference/qi_and_karma/operator.qbk b/doc/reference/qi_and_karma/operator.qbk new file mode 100644 index 000000000..fbd635ce5 --- /dev/null +++ b/doc/reference/qi_and_karma/operator.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Operators] +[endsect] diff --git a/doc/reference/qi_and_karma/parser.qbk b/doc/reference/qi_and_karma/parser.qbk new file mode 100644 index 000000000..b8cb5d297 --- /dev/null +++ b/doc/reference/qi_and_karma/parser.qbk @@ -0,0 +1,43 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parser] + +[heading Description] + +Description of Parser concept + +[variablelist Notation + [[`p`] [A Parser]] +] + +[heading Valid Expressions] + +For any Parser the following expressions must be valid: + +[table + [[Expression] [Semantics] [Return type] [Complexity]] + [[`xxx`] [Semantics of `xxx`] [Parser] [Constant]] +] + +[heading Type Requirements] + +[table + [[Expression] [Requirements]] + [[`xxx`] [Requirements for `xxx`]] +] + +[heading Invariants] + +For any Parser xxx the following invariants always hold: + +[heading Models] + +Links to models of Parser concept + +[endsect] diff --git a/doc/reference/qi_and_karma/stream.qbk b/doc/reference/qi_and_karma/stream.qbk new file mode 100644 index 000000000..0437a451a --- /dev/null +++ b/doc/reference/qi_and_karma/stream.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Stream] +[endsect] diff --git a/doc/reference/qi_and_karma/string.qbk b/doc/reference/qi_and_karma/string.qbk new file mode 100644 index 000000000..13339d86e --- /dev/null +++ b/doc/reference/qi_and_karma/string.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section String] +[endsect] diff --git a/doc/references.qbk b/doc/references.qbk new file mode 100644 index 000000000..d64325f56 --- /dev/null +++ b/doc/references.qbk @@ -0,0 +1,91 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section References] + +[table +[[ ] [Authors] [Title, Publisher/link, Date Published]] +[[1.] [Todd Veldhuizen] [[@http://www.extreme.indiana.edu/%7Etveldhui/papers/Expression-Templates/exprtmpl.html + "Expression Templates"]. C++ Report, June 1995.]] +[[2.] [Peter Naur (ed.)] [[@http://www.masswerk.at/algol60/report.htm + "Report on the Algorithmic Language ALGOL 60"]. CACM, May 1960.]] +[[3.] [ISO/IEC] [[@http://www.cl.cam.ac.uk/%7Emgk25/iso-14977.pdf "ISO-EBNF"], + ISO/IEC 14977: 1996(E).]] +[[4.] [Richard J.Botting, Ph.D.] [[@http://www.csci.csusb.edu/dick/maths/intro_ebnf.html + "XBNF"] (citing Leu-Weiner, 1973). + California State University, San Bernardino, 1998.]] +[[5.] [James Coplien.] ["Curiously Recurring Template Pattern". + C++ Report, Feb. 1995.]] +[[6.] [Thierry Geraud and + Alexandre Duret-Lutz] [[@http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip + Generic Programming Redesign of Patterns] + Proceedings of the 5th European Conference on Pattern Languages + of Programs(EuroPLoP'2000) Irsee, Germany, July 2000.]] +[[7.] [Geoffrey Furnish] [[@http://www.adtmag.com/joop/carticle.aspx?ID=627 + "Disambiguated Glommable Expression Templates Reintroduced"] + C++ Report, May 2000]] +[[8.] [Erich Gamma, + Richard Helm, + Ralph Jhonson, + and John Vlissides] [Design Patterns, Elements of Reusable Object-Oriented Software. + Addison-Wesley, 1995.]] +[[9.] [Alfred V. Aho, + Revi Sethi, + Feffrey D. Ulman] [Compilers, Principles, Techniques and Tools + Addison-Wesley, June 1987.]] +[[10.] [Dick Grune and + Ceriel Jacobs] [[@http://www.cs.vu.nl/%7Edick/PTAPG.html + Parsing Techniques: A Practical Guide.] + Ellis Horwood Ltd.: West Sussex, England, 1990. + (electronic copy, 1998).]] +[[11.] [T. J. Parr, + H. G. Dietz, and + W. E. Cohen] [[@http://citeseer.ist.psu.edu/6885.html + PCCTS Reference Manual (Version 1.00)]. + School of Electrical Engineering, Purdue University, + West Lafayette, August 1991.]] +[[12.] [Adrian Johnstone and + Elizabeth Scott.] [[@ftp://ftp.cs.rhul.ac.uk/pub/rdp + RDP, A Recursive Descent Compiler Compiler]. + Technical Report CSD TR 97 25, Dept. of Computer Science, + Egham, Surrey, England, Dec. 20, 1997.]] +[[13.] [Adrian Johnstone] [[@http://www.cs.rhul.ac.uk/research/languages/projects/lookahead_backtrack.shtml + Languages and Architectures, + Parser generators with backtrack or extended lookahead capability] + Department of Computer Science, Royal Holloway, University of London, + Egham, Surrey, England]] +[[14.] [Damian Conway] [[@http://www.csse.monash.edu.au/%7Edamian/papers/#Embedded_Input_Parsing_for_C + Parsing with C++ Classes]. + ACM SIGPLAN Notices, 29:1, 1994.]] +[[15.] [Joel de Guzman] [[@http://spirit.sourceforge.net/distrib/spirit_1_8_5/libs/spirit/index.html + "Spirit Version 1.8"], 1998-2003.]] +[[16.] [S. Doaitse Swierstra and + Luc Duponcheel] [[@http://citeseer.ist.psu.edu/448665.html + Deterministic, Error-Correcting Combinator Parsers] + Dept. of Computer Science, Utrecht University P.O.Box 80.089, + 3508 TB Utrecht, The Netherland]] +[[17.] [Bjarne Stroustrup] [[@http://www.research.att.com/%7Ebs/whitespace98.pdf + Generalizing Overloading for C++2000] + Overload, Issue 25. April 1, 1998.]] +[[18.] [Dr. John Maddock] [[@http://www.boost.org/libs/regex/index.html + Regex++ Documentation] + http://www.boost.org/libs/regex/index.htm]] +[[19.] [Anonymous + Edited by Graham Hutton] [[@http://www.cs.nott.ac.uk/~gmh//faq.html + Frequently Asked Questions for comp.lang.functional]. + Edited by Graham Hutton, University of Nottingham.]] +[[20.] [Hewlett-Packard] [[@http://www.sgi.com/tech/stl/ + Standard Template Library Programmer's Guide.], Hewlett-Packard Company, 1994]] +[[21.] [Boost Libraries] [[@http://boost.org/libs/libraries.htm + Boost Libraries Documentation].]] +[[22.] [Brian McNamara and + Yannis Smaragdakis] [[@http://www.cc.gatech.edu/~yannis/fc++/ FC++:Functional Programming in C++].]] +[[23.] [Todd Veldhuizen] [[@ftp://ftp.cs.indiana.edu/pub/techreports/TR542.pdf Techniques for Scientic C++.]]] +] + +[endsect] diff --git a/doc/spirit2.qbk b/doc/spirit2.qbk new file mode 100644 index 000000000..0bf61af81 --- /dev/null +++ b/doc/spirit2.qbk @@ -0,0 +1,143 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[article Spirit + [quickbook 1.4] + [version 2.0] + [authors [de Guzman, Joel], [Kaiser, Hartmut]] + [copyright 2001 2002 2003 2004 2005 2006 2007 2008 Joel de Guzman, Hartmut Kaiser] + [purpose Parser and Generator Library] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + +[/ November 14, 2007 ] + +[/ Some links ] + +[def __spirit__ [@http://spirit.sourceforge.net Spirit]] +[def __phoenix__ [@http://boost.org/libs/spirit/phoenix/index.html Phoenix]] +[def __phoenix2__ [@http://spirit.sourceforge.net/dl_more/phoenix_v2/libs/spirit/phoenix/doc/html/index.html Phoenix2]] +[def __fusion__ [@http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/index.html Fusion]] +[def __mpl__ [@http://www.boost.org/libs/mpl/index.html MPL]] +[def __boost_tuples__ [@http://www.boost.org/libs/tuple/index.html Boost.Tuples]] +[def __boost_proto__ -Boost.Proto-] +[def __boost__ [@http://www.boost.org/ Boost]] +[def __boost_tools__ [@http://www.boost.org/tools/index.html Boost Tools]] +[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]] +[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]] +[def __gmane__ [@http://www.gmane.org Gmane]] +[def __mlist_archive__ [@http://news.gmane.org/gmane.comp.parsers.spirit.general]] + +[def __early_spirit__ [@http://spirit.sourceforge.net/dl_docs/pre-spirit.htm pre-Spirit]] +[def __todd__exprtemplates__ [@http://ubiety.uwaterloo.ca/~tveldhui/papers/Expression-Templates/exprtmpl.html Expression Templates]] +[def __cpp_concepts__ [@http://en.wikipedia.org/wiki/C%2B%2B0x#Concept Concepts]] +[def __attr_grammar__ [@http://en.wikipedia.org/wiki/Attribute_grammar Attribute Grammar]] +[def __string_template__ [@http://www.stringtemplate.org/ StringTemplate]] +[def __lexertl__ [@http://www.benhanson.net/lexertl.html Lexertl]] +[def __wave__ [@http://www.boost.org/libs/wave/index.html Wave]] +[def __slex__ [@http://spirit.sourceforge.net/repository/applications/slex.zip SLex]] +[def __flex__ [@http://flex.sourceforge.net/ Flex]] +[def __re2c__ [@http://re2c.sourceforge.net/ re2c]] +[def __ragel__ [@http://www.cs.queensu.ca/~thurston/ragel/ Ragel]] + +[def __boost_variant__ [@http://www.boost.org/doc/html/variant.html `boost::variant<>`]] +[def __boost_iterator_range__ [@http://www.boost.org/libs/range/doc/utility_class.html#iter_range `boost::iterator_range<>`]] + + +[def __qi__ /Spirit.Qi/] +[def __karma__ /Spirit.Karma/] +[def __lex__ /Spirit.Lex/] + + +[def __fixme__ *FIXME*] + + +[/ Sections ] + +[def __sec_qi_and_karma__ [link spirit.qi_and_karma Qi and Karma]] +[def __sec_qi_karma_attributes__ [link spirit.qi_and_karma.abstracts.attributes Attributes]] + +[def __sec_lex__ [link spirit.__lex__ Lex]] +[def __sec_lex_quickstart_1__ [link spirit.__lex__.__lex___tutorials.quickstart_1___a_word_counter_using___lex__ Lex Quickstart 1 - A word counter using __lex__]] +[def __sec_lex_quickstart_2__ [link spirit.__lex__.__lex___tutorials.quickstart_2___a_better_word_counter_using___lex__ Lex Quickstart 2 - A better word counter using __lex__]] +[def __sec_lex_quickstart_3__ [link spirit.__lex__.__lex___tutorials.quickstart_3___counting_words_using_a_parser Lex Quickstart 3 - Counting Words Using a Parser]] + +[def __sec_lex_static_model__ [link spirit.__lex__.abstracts.the__static__lexer_model The /Static/ Model]] +[def __sec_lex_primitives__ [link spirit.__lex__.abstracts.lexer_primitives Lexer Primitives]] +[def __sec_lex_tokenvalues__ [link spirit.__lex__.abstracts.lexer_primitives.about_tokens_and_token_values About Tokens and Token Values]] +[def __sec_lex_attributes__ [link spirit.__lex__.abstracts.lexer_attributes Lexer Attributes]] + +[def __sec_ref_lex_token__ [link spirit.__lex__.reference.concepts.token Token Reference]] +[def __sec_ref_lex_token_def__ [link spirit.__lex__.reference.concepts.tokendef TokenDef Reference]] + +[/ References to API descriptions ] + +[def __api_tokenize_and_parse__ [link spirit.qi_and_karma.abstracts.parsing_and_generating.the_tokenize_and_phrase_parse___function `tokenize_and_parse()`]] +[def __api_generate_static__ [link spirit.__lex__.abstracts.tokenizing_input_data.the_generate_static___function `generate_static()`]] + + +[/ References to classes ] + +[def __class_token_def__ [link spirit.__lex__.reference.tokendef_class `token_def<>`]] + +[def __class_lexertl_token__ [link spirit.__lex__.reference.token_class `lexertl_token<>`]] +[def __class_lexertl_lexer__ [link spirit.__lex__.reference.lexer_class.the_lexertl_lexer_class_implementing_the_dynamic_model `lexertl_lexer<>`]] +[def __class_lexertl_static_lexer__ [link spirit.__lex__.reference.lexer_class.the_lexertl_static_lexer_class_implementing_the_static_model `lexertl_static_lexer<>`]] + + +[/ Some images ] + +[def __note__ [$../../../../doc/html/images/adm_note.png]] +[def __tip__ [$../../../../doc/html/images/adm_tip.png]] +[def __important__ [$../../../../doc/html/images/adm_important.png]] +[def __caution__ [$../../../../doc/html/images/adm_caution.png]] +[def __danger__ [$../../../../doc/html/images/adm_danger.png]] + + +[/ some templates] + +[/ fig[ref title label] + Image element with a title. + + ref := Reference to the image file. + title := The title to associate with this figure. + label := the id to use to be able to reference this picture +] +[template fig[ref title label]''' +
+ '''[title]''' + + + + + + '''[title]''' + + +
+'''] + + +[/ Here we go ] + +[include preface.qbk] +[include what_s_new.qbk] +[include introduction.qbk] +[include qi_and_karma.qbk] +[include lex.qbk] +[include faq.qbk] +[include notes.qbk] +[include rationale.qbk] +[include acknowledgments.qbk] +[include references.qbk] + + diff --git a/doc/what_s_new.qbk b/doc/what_s_new.qbk new file mode 100644 index 000000000..067dd736b --- /dev/null +++ b/doc/what_s_new.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section What's New] +[endsect] diff --git a/example/karma/Jamfile b/example/karma/Jamfile new file mode 100644 index 000000000..6e45845d2 --- /dev/null +++ b/example/karma/Jamfile @@ -0,0 +1,12 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2007 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== +project spirit-karma-example ; + +exe basic_facilities : basic_facilities.cpp ; +exe functor_facilities : functor_facilities.cpp ; + diff --git a/example/karma/basic_facilities.cpp b/example/karma/basic_facilities.cpp new file mode 100644 index 000000000..98529d06c --- /dev/null +++ b/example/karma/basic_facilities.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The main purpose of this example is to show the uniform and easy way of +// output formatting for different container types. +// +// Since the 'stream' primitive used below uses the streaming operator defined +// for the container value_type, you must make sure to have a corresponding +// operator<<() available for this contained data type. OTOH this means, that +// the format descriptions used below will be usable for any contained type as +// long as this type has an associated streaming operator defined. + +// use a larger value for the alignment field width (default is 10) +#define BOOST_KARMA_DEFAULT_FIELD_LENGTH 25 + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::ascii; +namespace karma = boost::spirit::karma; + +/////////////////////////////////////////////////////////////////////////////// +// Output the given containers in list format +// Note: the format description does not depend on the type of the sequence +// nor does it depend on the type of the elements contained in the +// sequence +/////////////////////////////////////////////////////////////////////////////// +template +void output_container(std::ostream& os, Container const& c) +{ + // output the container as a space separated sequence + os << + karma::format_delimited( + *stream, // format description + c, // data + space // delimiter + ) << std::endl << std::endl; + + os << + karma::format_delimited( + '[' << *stream << ']', // format description + c, // data + space // delimiter + ) << std::endl << std::endl; + + // output the container as a comma separated list + os << + karma::format( + stream % ", ", // format description + c // data + ) << std::endl << std::endl; + + os << + karma::format( + '[' << (stream % ", ") << ']', // format description + c // data + ) << std::endl << std::endl; + + // output the container as a comma separated list of items enclosed in '()' + os << + karma::format( + ('(' << stream << ')') % ", ", // format description + c // data + ) << std::endl << std::endl; + + os << + karma::format( + '[' << ( + ('(' << stream << ')') % ", " + ) << ']', // format description + c // data + ) << std::endl << std::endl; + + // output the container as a HTML list + os << + karma::format_delimited( + "
    " << + *verbatim["
  1. " << stream << "
  2. "] + << "
", // format description + c, // data + '\n' // delimiter + ) << std::endl; + + // output the container as right aligned column + os << + karma::format_delimited( + *verbatim[ + "|" << right_align[stream] << "|" + ], // format description + c, // data + '\n' // delimiter + ) << std::endl; + + os << std::endl; +} + +int main() +{ + /////////////////////////////////////////////////////////////////////////// + // vector + std::vector v (8); + std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::vector" << std::endl; + output_container(std::cout, v); + + /////////////////////////////////////////////////////////////////////////// + // list + std::list l; + l.push_back('A'); + l.push_back('B'); + l.push_back('C'); + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::list" << std::endl; + output_container(std::cout, l); + + /////////////////////////////////////////////////////////////////////////// + // C-style array + int i[4] = { 3, 6, 9, 12 }; + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "int i[]" << std::endl; + output_container(std::cout, boost::make_iterator_range(i, i+4)); + + /////////////////////////////////////////////////////////////////////////// + // strings + std::string str("Hello world!"); + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::string" << std::endl; + output_container(std::cout, str); + + /////////////////////////////////////////////////////////////////////////// + // vector of boost::date objects + // Note: any registered facets get used! + using namespace boost::gregorian; + std::vector dates; + dates.push_back(date(2005, Jun, 25)); + dates.push_back(date(2006, Jan, 13)); + dates.push_back(date(2007, May, 03)); + + date_facet* facet(new date_facet("%A %B %d, %Y")); + std::cout.imbue(std::locale(std::cout.getloc(), facet)); + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::vector" << std::endl; + output_container(std::cout, dates); + + /////////////////////////////////////////////////////////////////////////// + // fusion tuples + // this will work in the future +// boost::fusion::vector fv(42, 'a', 45.8); +// +// std::cout << "boost::fusion::vector" << std::endl; +// output_container(std::cout, fv); + return 0; +} + diff --git a/example/karma/functor_facilities.cpp b/example/karma/functor_facilities.cpp new file mode 100644 index 000000000..523e1e852 --- /dev/null +++ b/example/karma/functor_facilities.cpp @@ -0,0 +1,202 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This examples demonstrate how to write functor based generators for special +// purposes. + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace boost::spirit; + +/////////////////////////////////////////////////////////////////////////////// +// The functor generator 'counter' can be used for output annotation with some +// item counting information. +/////////////////////////////////////////////////////////////////////////////// +struct counter_impl : boost::spirit::karma::functor_base +{ + template + bool operator()(Parameter const&, Context& ctx, OutputIterator& sink) const + { + namespace karma = boost::spirit::karma; + return karma::generate(sink, int_ << ": ", counter++); + } + + counter_impl(int& counter_) + : counter(counter_) {} + + int& counter; +}; + +inline boost::spirit::result_of::as_generator::type +counter(int& counter_) +{ + using namespace boost::spirit::karma; + return as_generator(counter_impl(counter_)); +} + +/////////////////////////////////////////////////////////////////////////////// +// The functor generator 'confix' allows a simple syntax for generating +// output wrapped inside a pair of a prefix and a suffix. +/////////////////////////////////////////////////////////////////////////////// +template +struct confix_impl : public boost::spirit::karma::functor_base +{ + template + struct apply + { + typedef boost::spirit::hold_any type; + }; + + template + bool operator()(Parameter const& v, Context& ctx, OutputIterator& sink) const + { + namespace karma = boost::spirit::karma; + return karma::generate(sink, open << xpr << close, v); + } + + confix_impl(char const* open_, char const* close_, Expr const& xpr_) + : open(open_), close(close_), xpr(xpr_) {} + + std::string open; + std::string close; + Expr xpr; +}; + +template +inline typename boost::spirit::result_of::as_generator >::type +confix(Expr const& xpr_, char const* open_ = "", char const* close_ = "") +{ + using namespace boost::spirit::karma; + return as_generator(confix_impl(open_, close_, xpr_)); +} + +/////////////////////////////////////////////////////////////////////////////// +// The functor generator 'list' allows a simple syntax for generating +// list formatted output. +// +// This example uses phoenix::bind to allow to omit the second argument from +// the operator() and to allow to switch the remaining two arguments. +/////////////////////////////////////////////////////////////////////////////// +template +struct list_impl : boost::spirit::karma::functor_base +{ + // this function will be called to generate the output + template + bool operator()(OutputIterator& sink, Parameter const& v) const + { + namespace karma = boost::spirit::karma; + return karma::generate(sink, xpr % delim, v); + } + + list_impl(Expr const& xpr_, char const* delim_) + : xpr(xpr_), delim(delim_) {} + + Expr xpr; + std::string delim; +}; + +// Supply the expected parameter type explicitly +struct list_impl_mf +{ + // the expected parameter type of a functor has to be defined using a + // embedded apply metafunction + template + struct apply + { + typedef boost::spirit::hold_any type; + }; +}; + +template +inline list_impl +list(Expr const& xpr, char const* delim) +{ + return list_impl(xpr, delim); +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace karma = boost::spirit::karma; + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + + /////////////////////////////////////////////////////////////////////////// + // Output the given containers in list format + // We use a special functor generator here to annotate the output with + // a integer counting the entries. + /////////////////////////////////////////////////////////////////////////// + std::vector v (8); + std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector + + int counter1 = 1; + std::cout << + karma::format( + (counter(counter1) << int_) % ", ", // format description + v // data + ) << std::endl; + + // Here we initialize the counter to 100 + int counter2 = 100; + std::cout << + karma::format( + '[' << ( + (counter(counter2) << int_) % ", " + ) << ']', // format description + v // data + ) << std::endl; + + /////////////////////////////////////////////////////////////////////////// + // list + // The output format description used below adds special item formatting + /////////////////////////////////////////////////////////////////////////// + std::list names; + names.push_back("Spirit"); + names.push_back("Qi"); + names.push_back("Karma"); + + // specifying a prefix item suffix scheme directly + std::cout << + karma::format( + ('{' << stream << '}') % ", ", // format description + names // data + ) << std::endl; + + // The confix generator nicely wraps the given expression with prefix and + // suffix strings + std::cout << + karma::format( + confix(stream % ", ", "[", "]"), // format description + names // data + ) << std::endl; + + /////////////////////////////////////////////////////////////////////////// + // Output the given container as a list + // We use a separate metafunction list_impl_mf to specify the expected + // parameter type of this functor generator. + // We use phoenix::bind to allow to omit the 2nd argument from the functor + // function operator and to change the sequence of the remaining two + // arguments. + /////////////////////////////////////////////////////////////////////////// + std::string str("Hello world!"); + std::cout << + karma::format( + karma::as_generator_mf(bind(list(stream, ", "), _3, _1)), + str + ) << std::endl; + + return 0; +} diff --git a/example/karma/quick_start1.cpp b/example/karma/quick_start1.cpp new file mode 100644 index 000000000..342892630 --- /dev/null +++ b/example/karma/quick_start1.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The main purpose of this example is to show how a single container type can +// be formatted using different output grammars. + +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::ascii; +namespace karma = boost::spirit::karma; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + /////////////////////////////////////////////////////////////////////////// + // vector + std::vector v (8); + std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector + + std::cout << "Output 8 integers from a std::vector..." << std::endl; + + // output the container as a sequence without any separation + std::cout << "...without any separation" << std::endl; + std::cout << + karma::format( + *int_, // format description + v // data + ) << std::endl << std::endl; + + // output the container as a space separated sequence + std::cout << "...as space delited list" << std::endl; + std::cout << + karma::format_delimited( + *int_, // format description + v, // data + space // delimiter + ) << std::endl << std::endl; + + std::cout << + karma::format_delimited( + '[' << *int_ << ']', // format description + v, // data + space // delimiter + ) << std::endl << std::endl; + + // output the container as a comma separated list + std::cout << "...as comma separated list" << std::endl; + std::cout << + karma::format( + int_ % ", ", // format description + v // data + ) << std::endl << std::endl; + + std::cout << + karma::format( + '[' << (int_ % ", ") << ']', // format description + v // data + ) << std::endl << std::endl; + + // output the container as a comma separated list of double's + std::cout << "...as comma separated list of doubles" << std::endl; + std::cout << + karma::format( + double_ % ", ", // format description + v // data + ) << std::endl << std::endl; + + // output the container as a comma separated list of items enclosed in '()' + std::cout << "..as list of ints enclosed in '()'" << std::endl; + std::cout << + karma::format( + ('(' << int_ << ')') % ", ", // format description + v // data + ) << std::endl << std::endl; + + std::cout << + karma::format( + '[' << ( + ('(' << int_ << ')') % ", " + ) << ']', // format description + v // data + ) << std::endl << std::endl; + + // output the container as a HTML list + std::cout << "...as HTML bullet list" << std::endl; + std::cout << + karma::format_delimited( + "
    " << + // no delimiting within verbatim + *verbatim["
  1. " << int_ << "
  2. "] + << "
", // format description + v, // data + '\n' // delimiter + ) << std::endl; + + // output the container as right aligned column + std::cout << "...right aligned in a column" << std::endl; + std::cout << + karma::format_delimited( + *verbatim[ + "|" << right_align[int_] << "|" + ], // format description + v, // data + '\n' // delimiter + ) << std::endl; + + std::cout << std::endl; + return 0; +} + diff --git a/example/lex/Jamfile b/example/lex/Jamfile new file mode 100644 index 000000000..b206c3753 --- /dev/null +++ b/example/lex/Jamfile @@ -0,0 +1,22 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2008 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +project spirit-lexer-example ; + +exe example1 : example1.cpp ; +exe example2 : example2.cpp ; +exe example3 : example3.cpp ; +exe example4 : example4.cpp ; +exe example5 : example5.cpp ; +exe example6 : example6.cpp ; +exe print_numbers : print_numbers.cpp ; +exe word_count : word_count.cpp ; +exe word_count_functor : word_count_functor.cpp ; +exe word_count_lexer : word_count_lexer.cpp ; +exe strip_comments : strip_comments.cpp ; + diff --git a/example/lex/example.hpp b/example/lex/example.hpp new file mode 100644 index 000000000..d5a772787 --- /dev/null +++ b/example/lex/example.hpp @@ -0,0 +1,26 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// Helper function reading a file into a string +/////////////////////////////////////////////////////////////////////////////// +inline std::string +read_from_file(char const* infile) +{ + std::ifstream instream(infile); + if (!instream.is_open()) { + std::cerr << "Couldn't open file: " << infile << std::endl; + exit(-1); + } + instream.unsetf(std::ios::skipws); // No white space skipping! + return std::string(std::istreambuf_iterator(instream.rdbuf()), + std::istreambuf_iterator()); +} + diff --git a/example/lex/example1.cpp b/example/lex/example1.cpp new file mode 100644 index 000000000..1eda85b04 --- /dev/null +++ b/example/lex/example1.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Simple lexer/parser to test the Spirit installation. +// +// This example shows, how to create a simple lexer recognizing 4 different +// tokens, and how to use a single token definition as the skip parser during +// the parsing. Additionally it demonstrates how to use one of the defined +// tokens as a parser component in the grammar. +// +// The grammar recognizes a simple input structure, for instance: +// +// { +// hello world, hello it is me +// } +// +// Any number of simple sentences (optionally comma separated) inside a pair +// of curly braces will be matched. + +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example1_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + self = token_def<>(',') | '{' | '}' | identifier; + + // any token definition to be used as the skip parser during parsing + // has to be associated with a separate lexer state (here 'WS') + white_space = "[ \\t\\n]+"; + self("WS") = white_space; + } + + token_def<> identifier, white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example1_grammar + : grammar_def > > +{ + template + example1_grammar(TokenDef const& tok) + { + start = '{' >> *(tok.identifier >> -char_(',')) >> '}'; + } + + rule > > start; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the token type to return from the lexer iterator + typedef lexertl_token token_type; + + // This is the lexer type to use to tokenize the input. + // We use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example1_tokens example1_tokens; + + // This is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // This is the type of the grammar to parse + typedef example1_grammar example1_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example1_tokens tokens; // Our token definition + example1_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def); // Our parser + + std::string str (read_from_file("example1.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_def defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + bool r = phrase_parse(iter, end, calc, in_state("WS")[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example2.cpp b/example/lex/example2.cpp new file mode 100644 index 000000000..328fcd323 --- /dev/null +++ b/example/lex/example2.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens and how to use this with a grammar. This example has a +// heavily backtracking grammar which makes it a candidate for lexer based +// parsing (all tokens are scanned and generated only once, even if +// backtracking is required) which speeds up the overall parsing process +// considerably, out-weighting the overhead needed for setting up the lexer. +// Additionally it demonstrates how to use one of the defined tokens as a +// parser component in the grammar. +// +// The grammar recognizes a simple input structure: any number of English +// simple sentences (statements, questions and commands) are recognized and +// are being counted separately. + +// #define BOOST_SPIRIT_DEBUG +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using boost::phoenix::ref; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example2_tokens : lexer_def +{ + template + void def (Self& self) + { + // A 'word' is comprised of one or more letters and an optional + // apostrophe. If it contains an apostrophe, there may only be one and + // the apostrophe must be preceded and succeeded by at least 1 letter. + // For example, "I'm" and "doesn't" meet the definition of 'word' we + // define below. + word = "[a-zA-Z]+('[a-zA-Z]+)?"; + + // associate the tokens and the token set with the lexer + self = token_def<>(',') | '!' | '.' | '?' | ' ' | '\n' | word; + } + + token_def<> word; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example2_grammar : grammar_def +{ + template + example2_grammar(TokenDef const& tok) + : paragraphs(0), commands(0), questions(0), statements(0) + { + story + = +paragraph + ; + + paragraph + = ( +( command [ ++ref(commands) ] + | question [ ++ref(questions) ] + | statement [ ++ref(statements) ] + ) + >> *char_(' ') >> +char_('\n') + ) + [ ++ref(paragraphs) ] + ; + + command + = +(tok.word | ' ' | ',') >> '!' + ; + + question + = +(tok.word | ' ' | ',') >> '?' + ; + + statement + = +(tok.word | ' ' | ',') >> '.' + ; + + BOOST_SPIRIT_DEBUG_NODE(story); + BOOST_SPIRIT_DEBUG_NODE(paragraph); + BOOST_SPIRIT_DEBUG_NODE(command); + BOOST_SPIRIT_DEBUG_NODE(question); + BOOST_SPIRIT_DEBUG_NODE(statement); + } + + rule story, paragraph, command, question, statement; + int paragraphs, commands, questions, statements; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the token type to return from the lexer iterator + typedef lexertl_token token_type; + + // This is the lexer type to use to tokenize the input. + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example2_tokens example2_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example2_grammar example2_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example2_tokens tokens; // Our token definition + example2_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.story); // Our grammar + + std::string str (read_from_file("example2.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + bool r = parse(iter, end, calc); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "There were " + << def.commands << " commands, " + << def.questions << " questions, and " + << def.statements << " statements.\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example3.cpp b/example/lex/example3.cpp new file mode 100644 index 000000000..e8a72d134 --- /dev/null +++ b/example/lex/example3.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens and how to use this with a grammar. This example has a +// heavily backtracking grammar which makes it a candidate for lexer based +// parsing (all tokens are scanned and generated only once, even if +// backtracking is required) which speeds up the overall parsing process +// considerably, out-weighting the overhead needed for setting up the lexer. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// This example recognizes couplets, which are sequences of numbers enclosed +// in matching pairs of parenthesis. See the comments below to for details +// and examples. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG +// #define BOOST_SPIRIT_DEBUG + +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example3_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + ellipses = "\\.\\.\\."; + number = "[0-9]+"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") // whitespace + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" // C style comments + ; + + // associate the tokens and the token set with the lexer + self = ellipses | '(' | ')' | number; + self("WS") = white_space; + } + + // these tokens expose the iterator_range of the matched input sequence + token_def<> ellipses, identifier, number; + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example3_grammar + : grammar_def > +{ + template + example3_grammar(TokenDef const& tok) + { + start + = +(couplet | tok.ellipses) + ; + + // A couplet matches nested left and right parenthesis. + // For example: + // (1) (1 2) (1 2 3) ... + // ((1)) ((1 2)(3 4)) (((1) (2 3) (1 2 (3) 4))) ... + // (((1))) ... + couplet + = tok.number + | '(' >> +couplet >> ')' + ; + + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_SPIRIT_DEBUG_NODE(couplet); + } + + typedef typename Lexer::token_set token_set; + rule > start, couplet; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the token type to return from the lexer iterator + typedef lexertl_token token_type; + + // This is the lexer type to use to tokenize the input. + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example3_tokens example3_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example3_grammar example3_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example3_tokens tokens; // Our token definition + example3_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def); // Our grammar + + std::string str (read_from_file("example3.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. + std::string ws("WS"); + bool r = phrase_parse(iter, end, calc, in_state(ws)[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example4.cpp b/example/lex/example4.cpp new file mode 100644 index 000000000..0f91989a0 --- /dev/null +++ b/example/lex/example4.cpp @@ -0,0 +1,239 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens aimed at a simple language and how to use this lexer with +// a grammar. It shows how to associate values to tokens and how to access the +// token values from inside the grammar. +// +// We use explicit token value types, making the corresponding token instances +// carry convert the matched input into an instance of that type. The token +// value is exposed as the parser attribute if this token is used as a +// parser component somewhere in a grammar. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// This example recognizes a very simple programming language having +// assignment statements and if and while control structures. Look at the file +// example4.input for an example. + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example4_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + constant = "[0-9]+"; + if_ = "if"; + else_ = "else"; + while_ = "while"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" + ; + + // associate the tokens and the token set with the lexer + self = token_def<>('(') | ')' | '{' | '}' | '=' | ';' | constant; + self += if_ | else_ | while_ | identifier; + self("WS") = white_space; + } + +//[example4_token_def + // these tokens expose the iterator_range of the matched input sequence + token_def<> if_, else_, while_; + + // The following two tokens have an associated value type, 'identifier' + // carries a string (the identifier name) and 'constant' carries the + // matched integer value. + // + // Note: any token value type specified explicitly during a token_def<> + // declaration needs to be listed during token type definition as + // well (see the typedef for the token_type below). + // + // The conversion of the matched input to an instance of this type occurs + // once (on first access), which makes token values as efficient as + // possible. Moreover, token instances are constructed once by the lexer + // library. From this point on tokens are passed by reference only, + // avoiding tokens being copied around. + token_def identifier; + token_def constant; +//] + + // token set to be used as the skip parser + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example4_grammar + : grammar_def > +{ + template + example4_grammar(TokenDef const& tok) + { + program + = +block + ; + + block + = '{' >> *statement >> '}' + ; + + statement + = assignment + | if_stmt + | while_stmt + ; + + assignment + = (tok.identifier >> '=' >> expression >> ';') + [ + std::cout << val("assignment statement to: ") << _1 << "\n" + ] + ; + + if_stmt + = ( tok.if_ >> '(' >> expression >> ')' >> block + >> -(tok.else_ >> block) + ) + [ + std::cout << val("if expression: ") << _2 << "\n" + ] + ; + + while_stmt + = (tok.while_ >> '(' >> expression >> ')' >> block) + [ + std::cout << val("while expression: ") << _2 << "\n" + ] + ; + + // since expression has a variant return type accommodating for + // std::string and unsigned integer, both possible values may be + // returned to the calling rule + expression + = tok.identifier [ _val = _1 ] + | tok.constant [ _val = _1 ] + ; + } + + typedef typename Lexer::token_set token_set; + typedef boost::variant expression_type; + + rule > program, block, statement; + rule > assignment, if_stmt; + rule > while_stmt; + + // the expression is the only rule having a return value + rule > expression; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + +//[example4_token + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used instead. + // + // If you don't list any token value types in the following declaration + // (or just use the default token type: lexertl_token) + // it will compile and work just fine, just a bit less efficient. This is + // because the token value will be generated from the matched input + // sequence every time it is requested. But as soon as you specify at + // least one token value type you'll have to list all value types used + // for token_def<> declarations in the token definition class above, + // otherwise compilation errors will occur. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; +//] + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example4_tokens example4_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example4_grammar example4_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example4_tokens tokens; // Our token definition + example4_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.program); // Our grammar + + std::string str (read_from_file("example4.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + bool r = phrase_parse(iter, end, calc, in_state("WS")[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example5.cpp b/example/lex/example5.cpp new file mode 100644 index 000000000..a5390c5bc --- /dev/null +++ b/example/lex/example5.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens aimed at a simple language and how to use this lexer with +// a grammar. It shows how to associate values to tokens and how to access the +// token values from inside the grammar. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// The main purpose of this example is to show, how inheritance can be used to +// overload parts of a base grammar and add token definitions to a base lexer. +// +// Further, it shows how you can use the 'omitted' attribute type specifier +// for token definitions to force the token to have no attribute (expose an +// unused attribute). +// +// This example recognizes a very simple programming language having +// assignment statements and if and while control structures. Look at the file +// example5.input for an example. + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition base, defines all tokens for the base grammar below +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_base_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + constant = "[0-9]+"; + if_ = "if"; + while_ = "while"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" + ; + + // associate the tokens and the token set with the lexer + self += token_def<>('(') | ')' | '{' | '}' | '=' | ';' | constant; + self += if_ | while_ | identifier; + self("WS") = white_space; + } + + // these tokens have no value + token_def if_, while_; + + // The following two tokens have an associated value type, identifier + // carries a string (the identifier name) and constant carries the matched + // integer value. + // + // Note: any explicitly token value type specified during a token_def<> + // declaration needs to be listed during token type definition as + // well (see the typedef for the token_type below). + // + // The conversion of the matched input to an instance of this type occurs + // once (on first access), which makes token values as efficient as + // possible. Moreover, token instances are constructed once by the lexer + // library. From this point on tokens are passed by reference only, + // avoiding tokens being copied around. + token_def identifier; + token_def constant; + + // token set to be used as the skip parser + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition base, defines a basic language +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_base_grammar + : grammar_def > +{ + template + example5_base_grammar(TokenDef const& tok) + { + program + = +block + ; + + block + = '{' >> *statement >> '}' + ; + + statement + = assignment + | if_stmt + | while_stmt + ; + + assignment + = (tok.identifier >> '=' >> expression >> ';') + [ + std::cout << val("assignment statement to: ") << _1 << "\n" + ] + ; + + if_stmt + = (tok.if_ >> '(' >> expression >> ')' >> block) + [ + std::cout << val("if expression: ") << _1 << "\n" + ] + ; + + while_stmt + = (tok.while_ >> '(' >> expression >> ')' >> block) + [ + std::cout << val("while expression: ") << _1 << "\n" + ] + ; + + // since expression has a variant return type accommodating for + // std::string and unsigned integer, both possible values may be + // returned to the calling rule + expression + = tok.identifier [ _val = _1 ] + | tok.constant [ _val = _1 ] + ; + } + + typedef + grammar_def > + base_type; + typedef typename base_type::skipper_type skipper_type; + + rule program, block, statement; + rule assignment, if_stmt; + rule while_stmt; + + // the expression is the only rule having a return value + typedef boost::variant expression_type; + rule expression; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition for derived lexer, defines additional tokens +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_tokens : example5_base_tokens +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the additional token to match + else_ = "else"; + + // associate the new token with the lexer, note we add 'else' before + // anything else to add it to the token set before the identifier + // token, otherwise "else" would be matched as an identifier + self = else_; + + // call the base class definition function + example5_base_tokens::def(self); + } + + // this token has no value + token_def else_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Derived grammar definition, defines a language extension +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_grammar : example5_base_grammar +{ + template + example5_grammar(TokenDef const& tok) + : example5_base_grammar(tok) + { + // we alter the if_stmt only + this->if_stmt + = this->if_stmt.copy() >> -(tok.else_ >> this->block) + ; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used instead. + // + // If you don't list any token value types in the following declaration + // (or just use the default token type: lexertl_token) + // it will compile and work just fine, just a bit less efficient. This is + // because the token value will be generated from the matched input + // sequence every time it is requested. But as soon as you specify at + // least one token value type you'll have to list all value types used + // for token_def<> declarations in the token definition class above, + // otherwise compilation errors will occur. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; + + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example5_tokens example5_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example5_grammar example5_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example5_tokens tokens; // Our token definition + example5_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.program); // Our grammar + + std::string str (read_from_file("example5.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + std::string ws("WS"); + bool r = phrase_parse(iter, end, calc, in_state(ws)[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example6.cpp b/example/lex/example6.cpp new file mode 100644 index 000000000..9a88af0fd --- /dev/null +++ b/example/lex/example6.cpp @@ -0,0 +1,263 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens aimed at a simple language and how to use this lexer with +// a grammar. It shows how to associate values to tokens and how to access the +// token values from inside the grammar. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// The example demonstrates how to use the add(...)(...) syntax to associate +// token definitions with the lexer and how token ids can be used in the +// parser to refer to a token, without having to directly reference its +// definition. +// +// This example recognizes a very simple programming language having +// assignment statements and if and while control structures. Look at the file +// example6.input for an example. +// +// This example is essentially identical to example4.cpp. The only difference +// is that we use the self.add() syntax to define tokens and to associate them +// with the lexer. + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; + +/////////////////////////////////////////////////////////////////////////////// +// Token id definitions +/////////////////////////////////////////////////////////////////////////////// +enum token_ids +{ + ID_CONSTANT = 1000, + ID_IF, + ID_ELSE, + ID_WHILE, + ID_IDENTIFIER +}; + +/////////////////////////////////////////////////////////////////////////////// +// Token definitions +/////////////////////////////////////////////////////////////////////////////// +template +struct example6_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + constant = "[0-9]+"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" + ; + + // associate the tokens and the token set with the lexer + self = token_def<>('(') | ')' | '{' | '}' | '=' | ';'; + + // Token definitions can be added by using some special syntactic + // construct as shown below. + // Note, that the token definitions added this way expose the iterator + // pair pointing to the matched input stream as their attribute. + self.add + (constant, ID_CONSTANT) + ("if", ID_IF) + ("else", ID_ELSE) + ("while", ID_WHILE) + (identifier, ID_IDENTIFIER) + ; + + // add whitespace tokens to another lexer state (here: "WS") + self("WS") = white_space; + } + + // The following two tokens have an associated value type, identifier + // carries a string (the identifier name) and constant carries the matched + // integer value. + // + // Note: any explicitly token value type specified during a token_def<> + // declaration needs to be listed during token type definition as + // well (see the typedef for the token_type below). + // + // The conversion of the matched input to an instance of this type occurs + // once (on first access), which makes token values as efficient as + // possible. Moreover, token instances are constructed once by the lexer + // library. From this point on tokens are passed by reference only, + // avoiding tokens being copied around. + token_def identifier; + token_def constant; + + // token set to be used as the skip parser + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example6_grammar + : grammar_def > +{ + template + example6_grammar(TokenDef const& tok) + { + program + = +block + ; + + block + = '{' >> *statement >> '}' + ; + + statement + = assignment + | if_stmt + | while_stmt + ; + + assignment + = (tok.identifier >> '=' >> expression >> ';') + [ + std::cout << val("assignment statement to: ") + << _1 << "\n" + ] + ; + + if_stmt + = ( token(ID_IF) >> '(' >> expression >> ')' >> block + >> -(token(ID_ELSE) >> block) + ) + [ + std::cout << val("if expression: ") + << _2 << "\n" + ] + ; + + while_stmt + = (token(ID_WHILE) >> '(' >> expression >> ')' >> block) + [ + std::cout << val("while expression: ") + << _2 << "\n" + ] + ; + + // since expression has a variant return type accommodating for + // std::string and unsigned integer, both possible values may be + // returned to the calling rule + expression + = tok.identifier [ _val = _1 ] + | tok.constant [ _val = _1 ] + ; + } + + typedef typename Lexer::token_set token_set; + typedef boost::variant expression_type; + + rule > program, block, statement; + rule > assignment, if_stmt; + rule > while_stmt; + + // the expression is the only rule having a return value + rule > expression; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used instead. + // + // If you don't list any token value types in the following declaration + // (or just use the default token type: lexertl_token) + // it will compile and work just fine, just a bit less efficient. This is + // because the token value will be generated from the matched input + // sequence every time it is requested. But as soon as you specify at + // least one token value type you'll have to list all value types used + // for token_def<> declarations in the token definition class above, + // otherwise compilation errors will occur. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; + + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example6_tokens example6_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example6_grammar example6_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example6_tokens tokens; // Our token definition + example6_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.program); // Our grammar + + std::string str (read_from_file("example6.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_def defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + std::string ws("WS"); + bool r = phrase_parse(iter, end, calc, in_state(ws)[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/print_numbers.cpp b/example/lex/print_numbers.cpp new file mode 100644 index 000000000..8d3a3e48d --- /dev/null +++ b/example/lex/print_numbers.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +// +// %{ +// #include +// %} +// %% +// [0-9]+ { printf("%s\n", yytext); } +// .|\n ; +// %% +// main() +// { +// yylex(); +// } +// +// Its purpose is to print all the (integer) numbers found in a file + +#include +#include +#include + +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +template +struct print_numbers_tokens : lexer_def +{ + // define tokens and associate it with the lexer + template + void def (Self& self) + { + self = token_def("[0-9]*") | ".|\n"; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct print_numbers_grammar : grammar_def +{ + print_numbers_grammar() + { + start = *( token(lex::min_token_id) [ std::cout << _1 << "\n" ] + | token(lex::min_token_id+1) + ) + ; + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char* argv[]) +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // the token type to be used, 'int' is available as the type of the token + // value and no lexer state is supported + typedef lexertl_token< + base_iterator_type, boost::mpl::vector, boost::mpl::false_ + > token_type; + + // lexer type + typedef lexertl_lexer lexer_type; + + // iterator type exposed by the lexer + typedef + lexer_iterator >::type + iterator_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + print_numbers_tokens print_tokens; // Our token definition + print_numbers_grammar def; // Our grammar definition + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + std::string str (read_from_file(1 == argc ? "print_numbers.input" : argv[1])); + base_iterator_type first = str.begin(); + bool r = tokenize_and_parse(first, str.end(), make_lexer(print_tokens), + make_parser(def)); + + if (r) { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else { + std::string rest(first, str.end()); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + + diff --git a/example/lex/static_lexer/Jamfile b/example/lex/static_lexer/Jamfile new file mode 100644 index 000000000..c9a35df49 --- /dev/null +++ b/example/lex/static_lexer/Jamfile @@ -0,0 +1,13 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2008 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +project spirit-static-lexer-example ; + +exe generate_tables : generate_tables.cpp ; +exe word_count_static : word_count_static.cpp ; + diff --git a/example/lex/static_lexer/word_count_generate.cpp b/example/lex/static_lexer/word_count_generate.cpp new file mode 100644 index 000000000..889de06eb --- /dev/null +++ b/example/lex/static_lexer/word_count_generate.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The purpose of this example is to show, how it is possible to use a lexer +// token definition for two purposes: +// +// . To generate C++ code implementing a static lexical analyzer allowing +// to recognize all defined tokens (this file) +// . To integrate the generated C++ lexer into the /Spirit/ framework. +// (see the file: word_count_static.cpp) + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include + +#include + +#include "word_count_tokens.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_generate_main +int main(int argc, char* argv[]) +{ + // create the lexer object instance needed to invoke the generator + word_count_tokens > word_count; // the token definition + + // open the output file, where the generated tokenizer function will be + // written to + std::ofstream out(argc < 2 ? "word_count_static.hpp" : argv[1]); + + // invoke the generator, passing the token definition, the output stream + // and the name prefix of the tokenizing function to be generated + char const* function_name = (argc < 3 ? "" : argv[2]); + return generate_static(make_lexer(word_count), out, function_name) ? 0 : -1; +} +//] diff --git a/example/lex/static_lexer/word_count_static.cpp b/example/lex/static_lexer/word_count_static.cpp new file mode 100644 index 000000000..d8fcc628f --- /dev/null +++ b/example/lex/static_lexer/word_count_static.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The purpose of this example is to show, how it is possible to use a lexer +// token definition for two purposes: +// +// . To generate C++ code implementing a static lexical analyzer allowing +// to recognize all defined tokens +// . To integrate the generated C++ lexer into the /Spirit/ framework. +// + +// #define BOOST_SPIRIT_LEXERTL_DEBUG +#define BOOST_VARIANT_MINIMIZE_SIZE + +#include +//[wc_static_include +#include +//] +#include +#include +#include + +#include +#include + +#include "../example.hpp" +#include "word_count_tokens.hpp" // token definition + +#include "word_count_static.hpp" // generated tokenizer + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_grammar +// This is an ordinary grammar definition following the rules defined by +// Spirit.Qi. There is nothing specific about it, except it gets the token +// definition class instance passed to the constructor to allow accessing the +// embedded token_def<> instances. +template +struct word_count_grammar : grammar_def +{ + template + word_count_grammar(TokenDef const& tok) + : c(0), w(0), l(0) + { + using boost::spirit::arg_names::_1; + using boost::phoenix::ref; + using boost::phoenix::size; + + // associate the defined tokens with the lexer, at the same time + // defining the actions to be executed + start = *( tok.word [++ref(w), ref(c) += size(_1)] + | char_('\n') [++ref(l), ++ref(c)] + | token(IDANY) [++ref(c)] + ) + ; + } + + std::size_t c, w, l; // counter for characters, words, and lines + rule start; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_main +int main(int argc, char* argv[]) +{ + // Define the token type to be used: 'std::string' is available as the type + // of the token value. + typedef lexertl_token< + char const*, boost::mpl::vector + > token_type; + + // Define the lexer type to be used as the base class for our token + // definition. + // + // This is the only place where the code is different from an equivalent + // dynamic lexical analyzer. We use the `lexertl_static_lexer<>` instead of + // the `lexertl_lexer<>` as the base class for our token defintion type. + // + typedef lexertl_static_lexer lexer_type; + + // Define the iterator type exposed by the lexer. + typedef lexer_iterator >::type iterator_type; + + // Now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process. + word_count_tokens word_count; // Our token definition + word_count_grammar def (word_count); // Our grammar definition + + // Read in the file into memory. + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + char const* first = str.c_str(); + char const* last = &first[str.size()]; + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + bool r = tokenize_and_parse(first, last, make_lexer(word_count), + make_parser(def)); + + if (r) { // success + std::cout << "lines: " << def.l << ", words: " << def.w + << ", characters: " << def.c << "\n"; + } + else { + std::string rest(first, last); + std::cerr << "Parsing failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] diff --git a/example/lex/static_lexer/word_count_static.hpp b/example/lex/static_lexer/word_count_static.hpp new file mode 100644 index 000000000..6aa402565 --- /dev/null +++ b/example/lex/static_lexer/word_count_static.hpp @@ -0,0 +1,111 @@ +// Copyright (c) 2008 Ben Hanson +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file licence_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Auto-generated by boost::lexer +#if !defined(BOOST_SPIRIT_LEXER_NEXT_TOKEN_Feb_13_2008_12_01_20) +#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_Feb_13_2008_12_01_20 + +#include +#include + +// the generated table of state names and the tokenizer have to be +// defined in the boost::spirit::lex::static namespace +namespace boost { namespace spirit { namespace lex { namespace static_ { + +// this table defines the names of the lexer states +char const* const lexer_state_names[1] = +{ + "INITIAL", +}; + +template +std::size_t next_token (std::size_t &start_state_, Iterator const& start_, + Iterator &start_token_, Iterator const& end_) +{ + enum {end_state_index, id_index, state_index, bol_index, eol_index, + dead_state_index, dfa_offset}; + static const std::size_t npos = static_cast(~0); + static const std::size_t lookup_[256] = {8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 6, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8}; + static const std::size_t dfa_alphabet_ = 9; + static const std::size_t dfa_[45] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, + 4, 2, 1, 65536, 0, 0, 0, 0, + 0, 0, 2, 1, 65537, 0, 0, 0, + 0, 0, 0, 0, 1, 65538, 0, 0, + 0, 0, 0, 0, 0}; + + if (start_token_ == end_) return 0; + + const std::size_t *ptr_ = dfa_ + dfa_alphabet_; + Iterator curr_ = start_token_; + bool end_state_ = *ptr_ != 0; + std::size_t id_ = *(ptr_ + id_index); + Iterator end_token_ = start_token_; + + while (curr_ != end_) + { + std::size_t const state_ = + ptr_[lookup_[static_cast + (*curr_++)]]; + + if (state_ == 0) break; + + ptr_ = &dfa_[state_ * dfa_alphabet_]; + + if (*ptr_) + { + end_state_ = true; + id_ = *(ptr_ + id_index); + end_token_ = curr_; + } + } + + if (end_state_) + { + // return longest match + start_token_ = end_token_; + } + else + { + id_ = npos; + } + + return id_; +} + +}}}} // namespace boost::spirit::lex::static_ + +#endif diff --git a/example/lex/static_lexer/word_count_tokens.hpp b/example/lex/static_lexer/word_count_tokens.hpp new file mode 100644 index 000000000..f73e7e236 --- /dev/null +++ b/example/lex/static_lexer/word_count_tokens.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(SPIRIT_LEXER_EXAMPLE_WORD_COUNT_TOKENS_FEB_10_2008_0739PM) +#define SPIRIT_LEXER_EXAMPLE_WORD_COUNT_TOKENS_FEB_10_2008_0739PM + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We keep the base class for the token definition as a +// template parameter to allow this class to be used for +// both: the code generation and the lexical analysis +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_tokenids +enum tokenids +{ + IDANY = boost::spirit::lex::min_token_id + 1, +}; +//] + +//[wc_static_tokendef +// This token definition class can be used without any change for all three +// possible use cases: a dynamic lexical analyzer, a code generator, and a +// static lexical analyzer. +template +struct word_count_tokens : boost::spirit::lex::lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + word = "[^ \t\n]+"; + self = word | '\n' | token_def<>(".", IDANY); + } + + boost::spirit::lex::token_def word; +}; +//] + +#endif diff --git a/example/lex/strip_comments.cpp b/example/lex/strip_comments.cpp new file mode 100644 index 000000000..a9e04124d --- /dev/null +++ b/example/lex/strip_comments.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +// +// %{ +// /* INITIAL is the default start state. COMMENT is our new */ +// /* state where we remove comments. */ +// %} +// +// %s COMMENT +// %% +// "//".* ; +// "/*" BEGIN COMMENT; +// . ECHO; +// [\n] ECHO; +// "*/" BEGIN INITIAL; +// . ; +// [\n] ; +// %% +// +// main() +// { +// yylex(); +// } +// +// Its purpose is to strip comments out of C code. +// +// Additionally this example demonstrates the use of lexer states to structure +// the lexer definition. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include +#include +#include + +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +enum tokenids +{ + IDANY = lex::min_token_id + 10 +}; + +template +struct strip_comments_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + cppcomment = "//.*\n"; + ccomment = "/\\*"; + endcomment = "\\*/"; + + // The following tokens are associated with the default lexer state + // (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is + // strictly optional. + self.add + (cppcomment) // no explicit token id is associated + (ccomment) + (".", IDANY) // IDANY is the token id associated with this token + // definition + ; + + // The following tokens are associated with the lexer state "COMMENT". + // We switch lexer states from inside the parsing process using the + // in_state("COMMENT")[] parser component as shown below. + self("COMMENT").add + (endcomment) + (".", IDANY) + ; + } + + token_def<> cppcomment, ccomment, endcomment; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct strip_comments_grammar : grammar_def +{ + template + strip_comments_grammar(TokenDef const& tok) + { + // The in_state("COMMENT")[...] parser component switches the lexer + // state to be 'COMMENT' during the matching of the embedded parser. + start = *( tok.ccomment + >> in_state("COMMENT") + [ + // the lexer is in the 'COMMENT' state during + // matching of the following parser components + *token(IDANY) >> tok.endcomment + ] + | tok.cppcomment + | token(IDANY) + ) + ; + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char* argv[]) +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // lexer type + typedef lexertl_lexer > lexer_type; + + // iterator type exposed by the lexer + typedef + lexer_iterator >::type + iterator_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + strip_comments_tokens strip_comments; // Our token definition + strip_comments_grammar def (strip_comments); // Our grammar definition + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1])); + base_iterator_type first = str.begin(); + bool r = tokenize_and_parse(first, str.end(), make_lexer(strip_comments), + make_parser(def)); + + if (r) { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else { + std::string rest(first, str.end()); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + + diff --git a/example/lex/strip_comments_lexer.cpp b/example/lex/strip_comments_lexer.cpp new file mode 100644 index 000000000..2eec0376d --- /dev/null +++ b/example/lex/strip_comments_lexer.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +// +// %{ +// /* INITIAL is the default start state. COMMENT is our new */ +// /* state where we remove comments. */ +// %} +// +// %s COMMENT +// %% +// "//".* ; +// "/*" BEGIN COMMENT; +// . ECHO; +// [\n] ECHO; +// "*/" BEGIN INITIAL; +// . ; +// [\n] ; +// %% +// +// main() +// { +// yylex(); +// } +// +// Its purpose is to strip comments out of C code. +// +// Additionally this example demonstrates the use of lexer states to structure +// the lexer definition. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include +#include +#include +#include + +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +enum tokenids +{ + IDANY = lex::min_token_id + 10, + IDEOL = lex::min_token_id + 11 +}; + +template +struct strip_comments_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + cppcomment = "//[^\n]*"; + ccomment = "/\\*"; + endcomment = "\\*/"; + any = "."; + eol = "\n"; + + // The following tokens are associated with the default lexer state + // (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is + // strictly optional. + self = cppcomment + | ccomment [ set_state("COMMENT") ] + | eol [ echo_input(std::cout) ] + | any [ echo_input(std::cout) ] + ; + + // The following tokens are associated with the lexer state 'COMMENT'. + self("COMMENT") + = endcomment [ set_state("INITIAL") ] + | eol + | any + ; + } + + token_def<> cppcomment, ccomment, endcomment, any, eol; +}; + + /////////////////////////////////////////////////////////////////////////////// +int main(int argc, char* argv[]) +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // lexer type + typedef lexertl_actor_lexer > lexer_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + strip_comments_tokens strip_comments; // Our token definition + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1])); + base_iterator_type first = str.begin(); + bool r = tokenize(first, str.end(), make_lexer(strip_comments)); + + if (!r) { + std::string rest(first, str.end()); + std::cerr << "Lexical analysis failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} + + + diff --git a/example/lex/word_count.cpp b/example/lex/word_count.cpp new file mode 100644 index 000000000..d5750f63f --- /dev/null +++ b/example/lex/word_count.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +/* +//[wcp_flex_version + %{ + int c = 0, w = 0, l = 0; + %} + word [^ \t\n]+ + eol \n + %% + {word} { ++w; c += yyleng; } + {eol} { ++c; ++l; } + . { ++c; } + %% + main() + { + yylex(); + printf("%d %d %d\n", l, w, c); + } +//] +*/ +// Its purpose is to do the word count function of the wc command in UNIX. It +// prints the number of lines, words and characters in a file. +// +// The example additionally demonstrates how to use the add_pattern(...)(...) +// syntax to define lexer patterns. These patterns are essentially parameter- +// less 'macros' for regular expressions, allowing to simplify their +// definition. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG +#define BOOST_VARIANT_MINIMIZE_SIZE + +//[wcp_includes +#include +#include +#include +#include +#include +//] + +#include +#include + +#include "example.hpp" + +//[wcp_namespaces +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +//[wcp_token_ids +enum tokenids +{ + IDANY = lex::min_token_id + 10 +}; +//] + +//[wcp_token_definition +template +struct word_count_tokens : lexer_def +{ + template + void def (Self& self) + { + // define patterns (lexer macros) to be used during token definition + // below + self.add_pattern + ("WORD", "[^ \t\n]+") + ; + + // define tokens and associate them with the lexer + word = "{WORD}"; // reference the pattern 'WORD' as defined above + + // this lexer will recognize 3 token types: words, newlines, and + // everything else + self.add + (word) // no token id is needed here + ('\n') // characters are usable as tokens as well + (".", IDANY) + ; + } + + token_def word; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +//[wcp_grammar_definition +template +struct word_count_grammar : grammar_def +{ + template + word_count_grammar(TokenDef const& tok) + : c(0), w(0), l(0) + { + using boost::phoenix::ref; + using boost::phoenix::size; + + // As documented in the Spirit.Qi documentation, any placeholders + // (_1 et.al.) used in semantic actions inside a grammar need to be + // imported from the namespace boost::spirit::arg_names, and not from + // the corresponding namespace in Phoenix. + using boost::spirit::arg_names::_1; + + start = *( tok.word [++ref(w), ref(c) += size(_1)] + | char_('\n') [++ref(c), ++ref(l)] + | token(IDANY) [++ref(c)] + ) + ; + } + + std::size_t c, w, l; + rule start; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wcp_main +int main(int argc, char* argv[]) +{ +/*< define the token type to be used: `std::string` is available as the + type of the token value +>*/ typedef lexertl_token< + char const*, boost::mpl::vector + > token_type; + +/*< define the lexer type to use implementing the state machine +>*/ typedef lexertl_lexer lexer_type; + +/*< define the iterator type exposed by the lexer type +>*/ typedef lexer_iterator >::type iterator_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + word_count_tokens word_count; // Our token definition + word_count_grammar def (word_count); // Our grammar definition + + // read in the file int memory + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + char const* first = str.c_str(); + char const* last = &first[str.size()]; + + // Parsing is done based on the the token stream, not the character + // stream read from the input. The function `tokenize_and_parse()` wraps + // the passed iterator range `[first, last)` by the lexical analyzer and + // uses its exposed iterators to parse the toke stream. + bool r = tokenize_and_parse(first, last, make_lexer(word_count), + make_parser(def)); + + if (r) { + std::cout << "lines: " << def.l << ", words: " << def.w + << ", characters: " << def.c << "\n"; + } + else { + std::string rest(first, last); + std::cerr << "Parsing failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] diff --git a/example/lex/word_count_functor.cpp b/example/lex/word_count_functor.cpp new file mode 100644 index 000000000..8169ca354 --- /dev/null +++ b/example/lex/word_count_functor.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following flex program: +/* +//[wcf_flex_version + %{ + #define ID_WORD 1000 + #define ID_EOL 1001 + #define ID_CHAR 1002 + int c = 0, w = 0, l = 0; + %} + %% + [^ \t\n]+ { return ID_WORD; } + \n { return ID_EOL; } + . { return ID_CHAR; } + %% + bool count(int tok) + { + switch (tok) { + case ID_WORD: ++w; c += yyleng; break; + case ID_EOL: ++l; ++c; break; + case ID_CHAR: ++c; break; + default: + return false; + } + return true; + } + void main() + { + int tok = EOF; + do { + tok = yylex(); + if (!count(tok)) + break; + } while (EOF != tok); + printf("%d %d %d\n", l, w, c); + } +//] +*/ +// Its purpose is to do the word count function of the wc command in UNIX. It +// prints the number of lines, words and characters in a file. +// +// This examples shows how to use the tokenize() function together with a +// simple functor, which gets executed whenever a token got matched in the +// input sequence. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +//[wcf_includes +#include +#include +#include +//] + +#include +#include + +#include "example.hpp" + +//[wcf_namespaces +using namespace boost::spirit; +using namespace boost::spirit::lex; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Token id definitions +/////////////////////////////////////////////////////////////////////////////// +//[wcf_token_ids +enum token_ids +{ + ID_WORD = 1000, + ID_EOL, + ID_CHAR +}; +//] + +//[wcf_token_definition +/*` The template `word_count_tokens` defines three different tokens: + `ID_WORD`, `ID_EOL`, and `ID_CHAR`, representing a word (anything except + a whitespace or a newline), a newline character, and any other character + (`ID_WORD`, `ID_EOL`, and `ID_CHAR` are enum values representing the token + ids, but could be anything else convertible to an integer as well). + The direct base class of any token definition class needs to be the + template `lexer_def<>`, where the corresponding template parameter (here: + `lexertl_lexer`) defines which underlying lexer engine has + to be used to provide the required state machine functionality. In this + example we use the Lexertl based lexer engine as the underlying lexer type. +*/ +template +struct word_count_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens (the regular expression to match and the corresponding + // token id) and add them to the lexer + self.add + ("[^ \t\n]+", ID_WORD) // words (anything except ' ', '\t' or '\n') + ("\n", ID_EOL) // newline characters + (".", ID_CHAR) // anything else is a plain character + ; + } +}; +//] + +//[wcf_functor +/*` In this example the struct 'counter' is used as a functor counting the + characters, words and lines in the analyzed input sequence by identifying + the matched tokens as passed from the /Spirit.Lex/ library. +*/ +struct counter +{ +//<- this is an implementation detail and doesn't show up in the documentation + typedef bool result_type; + +//-> + // the function operator gets called for each of the matched tokens + // c, l, w are references to the counters used to keep track of the numbers + template + bool operator()(Token const& t, std::size_t& c, std::size_t& w, std::size_t& l) const + { + switch (t.id()) { + case ID_WORD: // matched a word + // since we're using a default token type in this example, every + // token instance contains a `iterator_range` as its + // token value pointing to the matched character sequence in the input + ++w; c += t.value().size(); + break; + case ID_EOL: // matched a newline character + ++l; ++c; + break; + case ID_CHAR: // matched something else + ++c; + break; + } + return true; // always continue to tokenize + } +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wcf_main +/*` The main function simply loads the given file into memory (as a + `std::string`), instantiates an instance of the token definition template + using the correct iterator type (`word_count_tokens`), + and finally calls `lex::tokenize`, passing an instance of the counter functor + defined above. The return value of `lex::tokenize` will be `true` if the + whole input sequence has been successfully tokenized, and `false` otherwise. +*/ +int main(int argc, char* argv[]) +{ + // these variables are used to count characters, words and lines + std::size_t c = 0, w = 0, l = 0; + + // read input from the given file + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + + // create the token definition instance needed to invoke the lexical analyzer + word_count_tokens > word_count_functor; + + // tokenize the given string, the bound functor gets invoked for each of + // the matched tokens + char const* first = str.c_str(); + char const* last = &first[str.size()]; + bool r = lex::tokenize(first, last, make_lexer(word_count_functor), + boost::bind(counter(), _1, boost::ref(c), boost::ref(w), boost::ref(l))); + + // print results + if (r) { + std::cout << "lines: " << l << ", words: " << w + << ", characters: " << c << "\n"; + } + else { + std::string rest(first, last); + std::cout << "Lexical analysis failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] + diff --git a/example/lex/word_count_functor_flex.cpp b/example/lex/word_count_functor_flex.cpp new file mode 100644 index 000000000..d81b8ea86 --- /dev/null +++ b/example/lex/word_count_functor_flex.cpp @@ -0,0 +1,1571 @@ +#line 2 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include +#include + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#ifndef _WIN32 +#include +#endif + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 4 +#define YY_END_OF_BUFFER 5 +static yyconst short int yy_accept[9] = + { 0, + 0, 0, 5, 1, 3, 2, 1, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[4] = + { 0, + 1, 2, 2 + } ; + +static yyconst short int yy_base[10] = + { 0, + 0, 0, 5, 0, 6, 6, 0, 6, 3 + } ; + +static yyconst short int yy_def[10] = + { 0, + 8, 1, 8, 9, 8, 8, 9, 0, 8 + } ; + +static yyconst short int yy_nxt[10] = + { 0, + 4, 5, 6, 7, 8, 3, 8, 8, 8 + } ; + +static yyconst short int yy_chk[10] = + { 0, + 1, 1, 1, 9, 3, 8, 8, 8, 8 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +#define INITIAL 0 +#line 2 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +#include +#if defined(_WIN32) + #include +#endif + #define ID_WORD 1000 + #define ID_EOL 1001 + #define ID_CHAR 1002 +#line 375 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + } +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 11 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" + +#line 539 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 6 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 12 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_WORD; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 13 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_EOL; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 14 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_CHAR; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 15 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +ECHO; + YY_BREAK +#line 642 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 8); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef _WIN32 +#include +#else +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 15 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" + + +bool count(int tok, int* c, int* w, int* l) +{ + switch (tok) { + case ID_WORD: ++*w; *c += yyleng; break; + case ID_EOL: ++*l; ++*c; break; + case ID_CHAR: ++*c; break; + default: + return false; + } + return true; +} + +void main(int argc, char* argv[]) +{ + int tok = EOF; + int c = 0, w = 0, l = 0; + yyin = fopen(1 == argc ? "word_count.input" : argv[1], "r"); + if (NULL == yyin) { + fprintf(stderr, "Couldn't open input file!\n"); + exit(-1); + } + + boost::timer tim; + do { + tok = yylex(); + if (!count(tok, &c, &w, &l)) + break; + } while (EOF != tok); + printf("lines: %d, words: %d, characters: %d\n", l, w, c); + printf("Time elapsed: %f", tim.elapsed()); + fclose(yyin); +} + +extern "C" int yywrap() +{ + return 1; +} + diff --git a/example/lex/word_count_lexer.cpp b/example/lex/word_count_lexer.cpp new file mode 100644 index 000000000..101b9261e --- /dev/null +++ b/example/lex/word_count_lexer.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +/* +//[wcl_flex_version + %{ + int c = 0, w = 0, l = 0; + %} + %% + [^ \t\n]+ { ++w; c += yyleng; } + \n { ++c; ++l; } + . { ++c; } + %% + main() + { + yylex(); + printf("%d %d %d\n", l, w, c); + } +//] +*/ +// Its purpose is to do the word count function of the wc command in UNIX. It +// prints the number of lines, words and characters in a file. +// +// This examples shows how to use semantic actions associated with token +// definitions to directly attach actions to tokens. These get executed +// whenever the corresponding token got matched in the input sequence. Note, +// how this example implements all functionality directly in the lexer +// definition without any need for a parser. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +//[wcl_includes +#include +#include +#include +#include +#include +//] + +#include +#include + +#include "example.hpp" + +//[wcl_namespaces +using namespace boost::spirit; +using namespace boost::spirit::lex; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +// +// Note, the token definition type is derived from the 'lexertl_actor_lexer' +// template, which is a necessary to being able to use lexer semantic actions. +/////////////////////////////////////////////////////////////////////////////// +//[wcl_token_definition +template +struct word_count_tokens : lexer_def +{ + word_count_tokens() + : c(0), w(0), l(0), + word("[^ \t\n]+"), eol("\n"), any(".") // define tokens + {} + + template + void def (Self& self) + { + using boost::phoenix::ref; + using boost::phoenix::distance; + + // Note that all placeholders used in lexer semantic actions in + // conjunction with functors created based on Phoenix2 need to be from + // the namespace boost::phoenix::arg_names (not spirit::arg_names). + // Using the wrong placeholders leads to subtle compilation errors + // which are difficult to backtrack to their cause. + using boost::phoenix::arg_names::_1; + + // associate tokens with the lexer + self = word [++ref(w), ref(c) += distance(_1)] + | eol [++ref(c), ++ref(l)] + | any [++ref(c)] + ; + } + + std::size_t c, w, l; + token_def<> word, eol, any; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wcl_main +int main(int argc, char* argv[]) +{ + // read input from the given file + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + + // Specifying 'omitted' as the token value type generates a token class not + // holding any token value at all (not even the iterator_range of the + // matched input sequence), therefor optimizing the token, the lexer, and + // possibly the parser implementation as much as possible. + // + // Specifying mpl::false_ as the 3rd template parameter generates a token + // type and an iterator, both holding no lexer state, allowing for even more + // aggressive optimizations. + // + // As a result the token instances contain the token ids as the only data + // member. + typedef lexertl_token token_type; + + // lexer type + typedef lexertl_actor_lexer lexer_type; + + // create the lexer object instance needed to invoke the lexical analysis + word_count_tokens word_count_lexer; + + // tokenize the given string, all generated tokens are discarded + char const* first = str.c_str(); + char const* last = &first[str.size()]; + bool r = tokenize(first, last, make_lexer(word_count_lexer)); + + if (r) { + std::cout << "lines: " << word_count_lexer.l + << ", words: " << word_count_lexer.w + << ", characters: " << word_count_lexer.c + << "\n"; + } + else { + std::string rest(first, last); + std::cout << "Lexical analysis failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] diff --git a/example/qi/Jamfile b/example/qi/Jamfile new file mode 100644 index 000000000..9c8cc35b2 --- /dev/null +++ b/example/qi/Jamfile @@ -0,0 +1,46 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== +project spirit-qi-example ; + +exe sum : sum.cpp ; +exe complex_number : complex_number.cpp ; +exe employee : employee.cpp ; +exe roman : roman.cpp ; +exe mini_xml1 : mini_xml1.cpp ; +exe mini_xml2 : mini_xml2.cpp ; +exe num_list : num_list.cpp ; +exe num_list2 : num_list2.cpp ; +exe num_list3 : num_list3.cpp ; + +exe calc1 : calc1.cpp ; +exe calc2 : calc2.cpp ; +exe calc3 : calc3.cpp ; +exe calc4 : calc4.cpp ; +exe calc5 : calc5.cpp ; + +exe calc6 : + calc6/calc6.cpp + calc6/calc6a.cpp + calc6/calc6b.cpp + calc6/calc6c.cpp + ; + +exe calc7 : + calc7/calc7.cpp + calc7/calc7a.cpp + calc7/calc7b.cpp + calc7/calc7c.cpp + ; + +exe mini_c : + mini_c/mini_c.cpp + mini_c/mini_ca.cpp + mini_c/mini_cb.cpp + mini_c/mini_cc.cpp + mini_c/mini_cd.cpp + ; + diff --git a/example/qi/calc1.cpp b/example/qi/calc1.cpp new file mode 100644 index 000000000..f1c7913ea --- /dev/null +++ b/example/qi/calc1.cpp @@ -0,0 +1,104 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Plain calculator example demonstrating the grammar. The parser is a +// syntax checker only and does not do any semantic evaluation. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 4, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term + >> *( ('+' >> term) + | ('-' >> term) + ) + ; + + term = + factor + >> *( ('*' >> factor) + | ('/' >> factor) + ) + ; + + factor = + uint_ + | '(' >> expression >> ')' + | ('-' >> factor) + | ('+' >> factor) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc2.cpp b/example/qi/calc2.cpp new file mode 100644 index 000000000..c2110b41d --- /dev/null +++ b/example/qi/calc2.cpp @@ -0,0 +1,123 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A Calculator example demonstrating the grammar and semantic actions +// using phoenix to "bind" plain functions. The parser prints code suitable +// for a stack based virtual machine. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 4, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::bind; + +/////////////////////////////////////////////////////////////////////////////// +// Semantic actions +/////////////////////////////////////////////////////////////////////////////// +namespace +{ + void do_int(int n) { std::cout << "push " << n << std::endl; } + void do_add() { std::cout << "add\n"; } + void do_subt() { std::cout << "subtract\n"; } + void do_mult() { std::cout << "mult\n"; } + void do_div() { std::cout << "divide\n"; } + void do_neg() { std::cout << "negate\n"; } +} + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term + >> *( ('+' >> term [bind(&do_add)]) + | ('-' >> term [bind(&do_subt)]) + ) + ; + + term = + factor + >> *( ('*' >> factor [bind(&do_mult)]) + | ('/' >> factor [bind(&do_div)]) + ) + ; + + factor = + uint_ [bind(&do_int, _1)] + | '(' >> expression >> ')' + | ('-' >> factor [bind(&do_neg)]) + | ('+' >> factor) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc3.cpp b/example/qi/calc3.cpp new file mode 100644 index 000000000..5fe6c8deb --- /dev/null +++ b/example/qi/calc3.cpp @@ -0,0 +1,110 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A calculator example demonstrating the grammar and semantic actions +// using phoenix to do the actual expression evaluation. The parser is +// essentially an "interpreter" that evaluates expressions on the fly. +// +// [ JDG June 29, 2002 ] spirit1 +// [ JDG March 5, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term [_val = _1] + >> *( ('+' >> term [_val += _1]) + | ('-' >> term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) + ) + ; + + factor = + uint_ [_val = _1] + | '(' >> expression [_val = _1] >> ')' + | ('-' >> factor [_val = -_1]) + | ('+' >> factor [_val = _1]) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + int result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, result, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc3_lexer.cpp b/example/qi/calc3_lexer.cpp new file mode 100644 index 000000000..c8c68f63f --- /dev/null +++ b/example/qi/calc3_lexer.cpp @@ -0,0 +1,201 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A calculator example demonstrating the grammar and semantic actions +// using phoenix to do the actual expression evaluation. The parser is +// essentially an "interpreter" that evaluates expressions on the fly. +// +// Additionally this examples shows how to build and use a lexer based on +// Ben Hansons Lexertl (http://www.benhanson.net/lexertl.html). This way the +// parser matches the grammar against the tokens generated by the lexer +// component and not against the input character stream. +// +// Even if the benefits of using a lexer for this small calculator grammar may +// not outweight the corresponding overhead, we provide this example because +// it allows to concentrate on the essentials without having to understand +// the semantics first. +// +// [ JDG June 29, 2002 ] spirit1 +// [ JDG March 5, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our token definition +// This class is used to define all the tokens to be recognized by the lexer. +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator_tokens : lexer_def +{ + template + void def (Self& self) + { + // unsigned integer token definition + ui = "[1-9][0-9]*"; + + // whitespace token definitions + ws = "[ \\t\\f\\v]+"; + c_comment = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"; + + // build token set + skipper = ws | c_comment; // += is allowed as well + + // associate the tokens and the token set with the lexer + // default lexer state + self = token_def<>('+') | '-' | '*' | '/' | '(' | ')'; + self += ui; // still default state + + // The token_set 'skipper' get's assigned to a separate lexer state + // which allows to use it separately from the main tokenization + // (it is used as the skipper parser below) + self("SKIPPER") = skipper; // lexer state "SKIPPER" + } + + // This are the tokens to be recognized by the lexer. + token_def ui; // matched tokens will have a unsigned int + token_def<> ws, c_comment; // attribute will not be used + + // This is the only token set explicitly defined by this lexer because it + // needs to be accessible from the outside (used as skip parser below). + typename Lexer::token_set skipper; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +// +// The difference to the original example (calc3.cpp) is that we are +// specifying a second template parameter referring to the lexer. Further, we +// use a defined tokenset from above as the skip parser. +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + template + calculator(TokenDef const& tok) + { + // grammar + expression = + term [_val = _1] + >> *( ('+' >> term [_val += _1]) + | ('-' >> term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) + ) + ; + + factor = + tok.ui [_val = _1] + | '(' >> expression [_val = _1] >> ')' + | ('-' >> factor [_val = -_1]) + | ('+' >> factor [_val = _1]) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + // iterator type used to expose the underlying input stream + typedef std::string::const_iterator base_iterator_type; + + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; + + // This is the lexer type to use to tokenize the input. + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef calculator_tokens calculator_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef calculator calculator; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + calculator_tokens tokens; // Our token definition + calculator def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.expression); // Our grammar + + // get input line by line and feed the parser to evaluate the expressions + // read in from the input + std::string str; + int result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + iterator_type iter = lex.begin(str.begin(), str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. + bool r = phrase_parse(iter, end, calc, result, tokens.skipper); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc4.cpp b/example/qi/calc4.cpp new file mode 100644 index 000000000..5daa6d93c --- /dev/null +++ b/example/qi/calc4.cpp @@ -0,0 +1,126 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This time, we'll incorporate error handling and reporting. +// +// [ JDG June 29, 2002 ] spirit1 +// [ JDG March 5, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; +using boost::phoenix::construct; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term [_val = _1] + >> *( ('+' > term [_val += _1]) + | ('-' > term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' > factor [_val *= _1]) + | ('/' > factor [_val /= _1]) + ) + ; + + factor = + uint_ [_val = _1] + | '(' > expression [_val = _1] > ')' + | ('-' > factor [_val = -_1]) + | ('+' > factor [_val = _1]) + ; + + expression.name("expression"); + term.name("term"); + factor.name("factor"); + + on_error + ( + expression + , std::cout + << val("Error! Expecting ") + << _4 // what failed? + << val(" here: \"") + << construct(_3, _2) // iterators to error-pos, end + << val("\"") + << std::endl + ); + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + int result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, result, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc5.cpp b/example/qi/calc5.cpp new file mode 100644 index 000000000..074753362 --- /dev/null +++ b/example/qi/calc5.cpp @@ -0,0 +1,227 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Yet another calculator example! This time, we will compile to a simple +// virtual machine. This is actually one of the very first Spirit example +// circa 2000. Now, it's ported to Spirit2. +// +// [ JDG Sometime 2000 ] pre-boost +// [ JDG September 18, 2002 ] spirit1 +// [ JDG April 8, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::ref; +using boost::phoenix::push_back; +using boost::phoenix::val; +using boost::phoenix::construct; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + op_int, // push constant integer into the stack +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + , stack_ptr(stack.begin()) + { + } + + int top() const { return stack_ptr[-1]; }; + void execute(std::vector& code); + +private: + + std::vector stack; + std::vector::iterator stack_ptr; +}; + +void vmachine::execute(std::vector& code) +{ + std::vector::iterator pc = code.begin(); + stack_ptr = stack.begin(); + + while (pc != code.end()) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator(std::vector& code) + : code(code) + { + expression = + term + >> *( ('+' > term [push_back(ref(code), op_add)]) + | ('-' > term [push_back(ref(code), op_sub)]) + ) + ; + + term = + factor + >> *( ('*' > factor [push_back(ref(code), op_mul)]) + | ('/' > factor [push_back(ref(code), op_div)]) + ) + ; + + factor = + uint_ [ + push_back(ref(code), op_int), + push_back(ref(code), _1) + ] + | '(' > expression > ')' + | ('-' > factor [push_back(ref(code), op_neg)]) + | ('+' > factor) + ; + + expression.name("expression"); + term.name("term"); + factor.name("factor"); + + on_error + ( + expression + , std::cout + << val("Error! Expecting ") + << _4 // what failed? + << val(" here: \"") + << construct(_3, _2) // iterators to error-pos, end + << val("\"") + << std::endl + ); + } + + rule expression, term, factor; + std::vector& code; +}; + +template +bool compile(Grammar const& calc, std::string const& expr) +{ + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + calculator def(code); // Our grammar definition + grammar + calc(def, def.expression); // Our grammar + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + code.clear(); + if (::compile(calc, str)) + { + mach.execute(code); + std::cout << "\n\nresult = " << mach.top() << std::endl; + std::cout << "-------------------------\n\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc6/calc6.cpp b/example/qi/calc6/calc6.cpp new file mode 100644 index 000000000..2c8062818 --- /dev/null +++ b/example/qi/calc6/calc6.cpp @@ -0,0 +1,102 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Now we'll introduce variables and assignment. This time, we'll also +// be renaming some of the rules -- a strategy for a grander scheme +// to come ;-) +// +// This version also shows off grammar modularization. Here you will +// see how expressions and statements are built as modular grammars. +// +// [ JDG April 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include "calc6.hpp" + +template +bool compile(Grammar const& calc, std::string const& expr) +{ + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +struct var_printer +{ + var_printer(std::vector const& stack) + : stack(stack) + { + } + + template + void operator()(String const& s, Data const& data) + { + std::cout << " " << s << ": " << stack[data] << std::endl; + } + + std::vector const& stack; +}; + +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type some statements... "; + std::cout << "Then type period ('.') to compile, run and print results\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef statement statement; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + statement def(code); // Our grammar definition + grammar calc(def); // Our grammar + + std::string str; + std::string program; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == '.') + break; + program += str; + } + + if (::compile(calc, program)) + { + mach.execute(code, def.nvars); + + std::cout << "Results------------------\n\n"; + def.vars.for_each(var_printer(mach.get_stack())); + std::cout << "-------------------------\n\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc6/calc6.hpp b/example/qi/calc6/calc6.hpp new file mode 100644 index 000000000..ead83c285 --- /dev/null +++ b/example/qi/calc6/calc6.hpp @@ -0,0 +1,183 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC6) +#define BOOST_SPIRIT_CALC6 + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::function; +using boost::phoenix::ref; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + + op_load, // load a variable + op_store, // store a variable + op_int, // push constant integer into the stack +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + , stack_ptr(stack.begin()) + { + } + + std::vector const& get_stack() const { return stack; }; + void execute(std::vector& code, int nvars); + +private: + + std::vector stack; + std::vector::iterator stack_ptr; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A generic compiler that compiles 1 to 3 codes +/////////////////////////////////////////////////////////////////////////////// +struct compile_op +{ + template + struct result { typedef void type; }; + + compile_op(std::vector& code) + : code(code) + { + } + + void operator()(int a) const + { + code.push_back(a); + } + + void operator()(int a, int b) const + { + code.push_back(a); + code.push_back(b); + } + + void operator()(int a, int b, int c) const + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + } + + std::vector& code; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our error handler +/////////////////////////////////////////////////////////////////////////////// +struct error_handler_ +{ + template + struct result { typedef void type; }; + + template + void operator()( + std::string const& what + , Iterator err_pos, Iterator last) const + { + std::cout + << "Error! Expecting " + << what // what failed? + << " here: \"" + << std::string(err_pos, last) // iterators to error-pos, end + << "\"" + << std::endl + ; + } +}; + +function const error_handler = error_handler_(); + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct expression : grammar_def +{ + expression(std::vector& code, symbols& vars); + + rule + expr, additive_expr, multiplicative_expr + , unary_expr, primary_expr, variable; + + std::vector& code; + symbols& vars; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +struct var_adder +{ + template + struct result { typedef void type; }; + + var_adder(symbols& vars) + : vars(vars) + { + } + + void operator()(std::string const& var, int& nvars) const + { + vars.add(var.begin(), var.end(), nvars++); + }; + + symbols& vars; +}; + +template +struct statement : grammar_def +{ + statement(std::vector& code); + + std::vector& code; + symbols vars; + int nvars; + + expression expr_def; + grammar > expr; + rule start, var_decl; + rule identifier; + rule var_ref; + rule, space_type> assignment; + rule assignment_rhs; + + function add_var; + function op; +}; + +#endif + diff --git a/example/qi/calc6/calc6a.cpp b/example/qi/calc6/calc6a.cpp new file mode 100644 index 000000000..1f4be6ce0 --- /dev/null +++ b/example/qi/calc6/calc6a.cpp @@ -0,0 +1,58 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc6.hpp" + +void vmachine::execute(std::vector& code, int nvars) +{ + std::vector::iterator pc = code.begin(); + std::vector::iterator locals = stack.begin(); + stack_ptr = stack.begin() + nvars; + + while (pc != code.end()) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_load: + *stack_ptr++ = locals[*pc++]; + break; + + case op_store: + --stack_ptr; + locals[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + } + } +} + diff --git a/example/qi/calc6/calc6b.cpp b/example/qi/calc6/calc6b.cpp new file mode 100644 index 000000000..44041886e --- /dev/null +++ b/example/qi/calc6/calc6b.cpp @@ -0,0 +1,18 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc6b.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_expression() +{ + typedef std::string::const_iterator iterator_type; + symbols vars; + std::vector code; + expression g(code, vars); +} + diff --git a/example/qi/calc6/calc6b.hpp b/example/qi/calc6/calc6b.hpp new file mode 100644 index 000000000..ed03253ac --- /dev/null +++ b/example/qi/calc6/calc6b.hpp @@ -0,0 +1,70 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC6B) +#define BOOST_SPIRIT_CALC6B + +#include "calc6.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +expression::expression(std::vector& code, symbols& vars) + : code(code) + , vars(vars) + , op(code) +{ + expr = + additive_expr.alias() + ; + + additive_expr = + multiplicative_expr + >> *( ('+' > multiplicative_expr [op(op_add)]) + | ('-' > multiplicative_expr [op(op_sub)]) + ) + ; + + multiplicative_expr = + unary_expr + >> *( ('*' > unary_expr [op(op_mul)]) + | ('/' > unary_expr [op(op_div)]) + ) + ; + + unary_expr = + primary_expr + | ('-' > primary_expr [op(op_neg)]) + | ('+' > primary_expr) + ; + + primary_expr = + uint_ [op(op_int, _1)] + | variable + | '(' > expr > ')' + ; + + variable = + ( + lexeme[ + vars + >> !(alnum | '_') // make sure we have whole words + ] + ) [op(op_load, _1)] + ; + + expr.name("expression"); + additive_expr.name("additive-expression"); + multiplicative_expr.name("multiplicative-expression"); + unary_expr.name("unary-expression"); + primary_expr.name("primary-expression"); + variable.name("variable"); + + on_error(expr, error_handler(_4, _3, _2)); +} + +#endif diff --git a/example/qi/calc6/calc6c.cpp b/example/qi/calc6/calc6c.cpp new file mode 100644 index 000000000..e6b83eb9f --- /dev/null +++ b/example/qi/calc6/calc6c.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc6c.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_statement() +{ + typedef std::string::const_iterator iterator_type; + std::vector code; + statement g(code); +} diff --git a/example/qi/calc6/calc6c.hpp b/example/qi/calc6/calc6c.hpp new file mode 100644 index 000000000..4687e32aa --- /dev/null +++ b/example/qi/calc6/calc6c.hpp @@ -0,0 +1,65 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC6C) +#define BOOST_SPIRIT_CALC6C + +#include "calc6.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +statement::statement(std::vector& code) + : code(code) + , nvars(0) + , expr_def(code, vars) + , expr(expr_def, expr_def.expr) + , add_var(vars) + , op(code) +{ + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + var_ref = + lexeme + [ + vars [_val = _1] + >> !(alnum | '_') // make sure we have whole words + ] + ; + + var_decl = + "var" + > !var_ref // make sure the variable isn't redeclared + > identifier [add_var(_1, ref(nvars))] + > (';' | '=' > assignment_rhs(ref(nvars)-1)) + ; + + assignment = + var_ref [_a = _1] + >> '=' + > assignment_rhs(_a) + ; + + assignment_rhs = + expr + > char_(';') [op(op_store, _r1)] + ; + + start = +(var_decl | assignment); + + identifier.name("identifier"); + var_ref.name("variable-reference"); + var_decl.name("variable-declaration"); + assignment.name("assignment"); + assignment_rhs.name("assignment-rhs"); + + on_error(start, error_handler(_4, _3, _2)); +} + +#endif \ No newline at end of file diff --git a/example/qi/calc7/calc7.cpp b/example/qi/calc7/calc7.cpp new file mode 100644 index 000000000..e34437f1a --- /dev/null +++ b/example/qi/calc7/calc7.cpp @@ -0,0 +1,99 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Now we'll introduce boolean expressions and control structures. +// Is it obvious now what we are up to? ;-) +// +// [ JDG April 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include "calc7.hpp" + +template +bool compile(Grammar const& calc, std::string const& expr) +{ + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +struct var_printer +{ + var_printer(std::vector const& stack) + : stack(stack) + { + } + + template + void operator()(String const& s, Data const& data) + { + std::cout << " " << s << ": " << stack[data] << std::endl; + } + + std::vector const& stack; +}; + +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type some statements... "; + std::cout << "Then type period ('.') to compile, run and print results\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef statement statement; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + statement def(code); // Our grammar definition + grammar + calc(def, def.statement_list); // Our grammar + + std::string str; + std::string program; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == '.') + break; + program += str; + } + + if (::compile(calc, program)) + { + mach.execute(code, def.nvars); + + std::cout << "Results------------------\n\n"; + def.vars.for_each(var_printer(mach.get_stack())); + std::cout << "-------------------------\n\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc7/calc7.hpp b/example/qi/calc7/calc7.hpp new file mode 100644 index 000000000..b6088d277 --- /dev/null +++ b/example/qi/calc7/calc7.hpp @@ -0,0 +1,208 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7) +#define BOOST_SPIRIT_CALC7 + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::function; +using boost::phoenix::ref; +using boost::phoenix::size; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + + op_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to an absolute position in the code if top stack + // evaluates to false + op_jump // jump to an absolute position in the code +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + , stack_ptr(stack.begin()) + { + } + + std::vector const& get_stack() const { return stack; }; + void execute(std::vector& code, int nvars); + +private: + + std::vector stack; + std::vector::iterator stack_ptr; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A generic compiler that compiles 1 to 3 codes +/////////////////////////////////////////////////////////////////////////////// +struct compile_op +{ + template + struct result { typedef void type; }; + + compile_op(std::vector& code) + : code(code) + { + } + + void operator()(int a) const + { + code.push_back(a); + } + + void operator()(int a, int b) const + { + code.push_back(a); + code.push_back(b); + } + + void operator()(int a, int b, int c) const + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + } + + std::vector& code; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our error handler +/////////////////////////////////////////////////////////////////////////////// +struct error_handler_ +{ + template + struct result { typedef void type; }; + + template + void operator()( + std::string const& what + , Iterator err_pos, Iterator last) const + { + std::cout + << "Error! Expecting " + << what // what failed? + << " here: \"" + << std::string(err_pos, last) // iterators to error-pos, end + << "\"" + << std::endl + ; + } +}; + +function const error_handler = error_handler_(); + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct expression : grammar_def +{ + expression(std::vector& code, symbols& vars); + + rule + expr, equality_expr, relational_expr + , logical_expr, additive_expr, multiplicative_expr + , unary_expr, primary_expr, variable + ; + + std::vector& code; + symbols& vars; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +struct var_adder +{ + template + struct result { typedef void type; }; + + var_adder(symbols& vars) + : vars(vars) + { + } + + void operator()(std::string const& var, int& nvars) const + { + vars.add(var.begin(), var.end(), nvars++); + }; + + symbols& vars; +}; + +template +struct statement : grammar_def +{ + statement(std::vector& code); + + std::vector& code; + symbols vars; + int nvars; + + expression expr_def; + grammar > expr; + rule + statement_, statement_list, var_decl, compound_statement + ; + + rule, space_type> if_statement; + rule, space_type> while_statement; + rule identifier; + rule var_ref; + rule, space_type> assignment; + rule assignment_rhs; + + function add_var; + function op; +}; + +#endif diff --git a/example/qi/calc7/calc7a.cpp b/example/qi/calc7/calc7a.cpp new file mode 100644 index 000000000..1be7d2851 --- /dev/null +++ b/example/qi/calc7/calc7a.cpp @@ -0,0 +1,125 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc7.hpp" + +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) + +void vmachine::execute(std::vector& code, int nvars) +{ + std::vector::iterator pc = code.begin(); + std::vector::iterator locals = stack.begin(); + stack_ptr = stack.begin() + nvars; + + while (pc != code.end()) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(stack_ptr[-1]); + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = locals[*pc++]; + break; + + case op_store: + --stack_ptr; + locals[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc = code.begin() + *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc = code.begin() + *pc; + else + ++pc; + --stack_ptr; + break; + } + } +} + diff --git a/example/qi/calc7/calc7b.cpp b/example/qi/calc7/calc7b.cpp new file mode 100644 index 000000000..c0f2c6de4 --- /dev/null +++ b/example/qi/calc7/calc7b.cpp @@ -0,0 +1,17 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc7b.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_expression() +{ + typedef std::string::const_iterator iterator_type; + symbols vars; + std::vector code; + expression g(code, vars); +} diff --git a/example/qi/calc7/calc7b.hpp b/example/qi/calc7/calc7b.hpp new file mode 100644 index 000000000..98c1930df --- /dev/null +++ b/example/qi/calc7/calc7b.hpp @@ -0,0 +1,101 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7B) +#define BOOST_SPIRIT_CALC7B + +#include "calc7.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +expression::expression(std::vector& code, symbols& vars) + : code(code) + , vars(vars) + , op(code) +{ + expr = + equality_expr.alias() + ; + + equality_expr = + relational_expr + >> *( ("==" > relational_expr [op(op_eq)]) + | ("!=" > relational_expr [op(op_neq)]) + ) + ; + + relational_expr = + logical_expr + >> *( ("<=" > logical_expr [op(op_lte)]) + | ('<' > logical_expr [op(op_lt)]) + | (">=" > logical_expr [op(op_gte)]) + | ('>' > logical_expr [op(op_gt)]) + ) + ; + + logical_expr = + additive_expr + >> *( ("&&" > additive_expr [op(op_and)]) + | ("||" > additive_expr [op(op_or)]) + ) + ; + + additive_expr = + multiplicative_expr + >> *( ('+' > multiplicative_expr [op(op_add)]) + | ('-' > multiplicative_expr [op(op_sub)]) + ) + ; + + multiplicative_expr = + unary_expr + >> *( ('*' > unary_expr [op(op_mul)]) + | ('/' > unary_expr [op(op_div)]) + ) + ; + + unary_expr = + primary_expr + | ('!' > primary_expr [op(op_not)]) + | ('-' > primary_expr [op(op_neg)]) + | ('+' > primary_expr) + ; + + primary_expr = + uint_ [op(op_int, _1)] + | variable + | lit("true") [op(op_true)] + | lit("false") [op(op_false)] + | '(' > expr > ')' + ; + + variable = + ( + lexeme[ + vars + >> !(alnum | '_') // make sure we have whole words + ] + ) [op(op_load, _1)] + ; + + expr.name("expression"); + equality_expr.name("equality-expression"); + relational_expr.name("relational-expression"); + logical_expr.name("logical-expression"); + additive_expr.name("additive-expression"); + multiplicative_expr.name("multiplicative-expression"); + unary_expr.name("unary-expression"); + primary_expr.name("primary-expression"); + variable.name("variable"); + + on_error(expr, error_handler(_4, _3, _2)); +} + +#endif + + diff --git a/example/qi/calc7/calc7c.cpp b/example/qi/calc7/calc7c.cpp new file mode 100644 index 000000000..a29595f5a --- /dev/null +++ b/example/qi/calc7/calc7c.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc7c.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_statement() +{ + typedef std::string::const_iterator iterator_type; + std::vector code; + statement g(code); +} diff --git a/example/qi/calc7/calc7c.hpp b/example/qi/calc7/calc7c.hpp new file mode 100644 index 000000000..44d7c2290 --- /dev/null +++ b/example/qi/calc7/calc7c.hpp @@ -0,0 +1,131 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7C) +#define BOOST_SPIRIT_CALC7C + +#include "calc7.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +statement::statement(std::vector& code) + : code(code) + , nvars(0) + , expr_def(code, vars) + , expr(expr_def, expr_def.expr) + , add_var(vars) + , op(code) +{ + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + var_ref = + lexeme + [ + vars [_val = _1] + >> !(alnum | '_') // make sure we have whole words + ] + ; + + var_decl = + "var" + > !var_ref // make sure the variable isn't redeclared + > identifier [add_var(_1, ref(nvars))] + > (';' | '=' > assignment_rhs(ref(nvars)-1)) + ; + + assignment = + var_ref [_a = _1] + >> '=' + > assignment_rhs(_a) + ; + + assignment_rhs = + expr + > char_(';') [op(op_store, _r1)] + ; + + if_statement = + lit("if") + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + // now we know where to jump to (after the if branch) + ref(code)[_a] = size(ref(code)) + ] + >> + -( + lexeme[ + "else" + >> !(alnum | '_') // make sure we have whole words + ] + [ + ref(code)[_a] += 2, // adjust for the "else" jump + op(op_jump, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > statement_ [ + // now we know where to jump to (after the else branch) + ref(code)[_a] = size(ref(code)) + ] + ) + ; + + while_statement = + lit("while") [ + _a = size(ref(code)) // mark our position + ] + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _b = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + op(op_jump, _a), // loop back + // now we know where to jump to (to exit the loop) + ref(code)[_b] = size(ref(code)) + ] + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + statement_ = + var_decl + | assignment + | compound_statement + | if_statement + | while_statement + ; + + statement_list = +statement_; + + identifier.name("identifier"); + var_ref.name("variable-reference"); + var_decl.name("variable-declaration"); + assignment.name("assignment"); + assignment_rhs.name("assignment-rhs"); + if_statement.name("if-statement"); + while_statement.name("while-statement"); + compound_statement.name("compound-statement"); + statement_.name("statement"); + statement_list.name("statement-list"); + + on_error(statement_list, error_handler(_4, _3, _2)); +} + +#endif + + diff --git a/example/qi/complex_number.cpp b/example/qi/complex_number.cpp new file mode 100644 index 000000000..84c971a67 --- /dev/null +++ b/example/qi/complex_number.cpp @@ -0,0 +1,94 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A complex number micro parser. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG May 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our complex number parser/compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_complex(Iterator first, Iterator last, std::complex& c) +{ + double rN = 0.0; + double iN = 0.0; + bool r = phrase_parse(first, last, + + // Begin grammar + ( + '(' >> double_[ref(rN) = _1] + >> -(',' >> double_[ref(iN) = _1]) >> ')' + | double_[ref(rN) = _1] + ), + // End grammar + + space); + + if (!r || first != last) // fail if we did not get a full match + return false; + c = std::complex(rN, iN); + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA complex number micro parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a complex number of the form r or (r) or (r,i) \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::complex c; + if (parse_complex(str.begin(), str.end(), c)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << c << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/employee.cpp b/example/qi/employee.cpp new file mode 100644 index 000000000..e5c8e3bc0 --- /dev/null +++ b/example/qi/employee.cpp @@ -0,0 +1,132 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A parser for arbitrary tuples. This example presents a parser +// for an employee structure. +// +// [ JDG May 9, 2007 ] +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our employee struct +/////////////////////////////////////////////////////////////////////////////// +struct employee +{ + int age; + std::string surname; + std::string forename; + double salary; +}; + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + employee, + (int, age) + (std::string, surname) + (std::string, forename) + (double, salary) +) + +/////////////////////////////////////////////////////////////////////////////// +// Our employee parser +/////////////////////////////////////////////////////////////////////////////// +template +struct employee_parser : grammar_def +{ + employee_parser() + { + quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; + + start %= + lit("employee") + >> '{' + >> int_ >> ',' + >> quoted_string >> ',' + >> quoted_string >> ',' + >> double_ + >> '}' + ; + } + + rule quoted_string; + rule start; +}; + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tAn employee parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout + << "Give me an employee of the form :" + << "employee{age, \"surname\", \"forename\", salary } \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef employee_parser employee_parser; + + employee_parser def; // Our grammar definition + grammar g(def); // Our grammar + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + employee emp; + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, g, emp, space); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << boost::fusion::as_vector(emp) << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/mini_c/mini_c.cpp b/example/qi/mini_c/mini_c.cpp new file mode 100644 index 000000000..af8329754 --- /dev/null +++ b/example/qi/mini_c/mini_c.cpp @@ -0,0 +1,123 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Not a calculator anymore, right? :-) +// +// [ JDG April 10, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our main compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool compile(Grammar const& prog, std::string const& expr) +{ + typedef white_space_def white_space_def; + white_space_def white_; // Our skipper definition + grammar white_space(white_); // Our skipper + + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, prog, white_space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string source_code; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(source_code)); + + typedef std::string::const_iterator iterator_type; + typedef program program; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + program def(code); // Our grammar definition + grammar prog(def); // Our grammar + + if (::compile(prog, source_code)) + { + std::string fmain("main"); + std::string::iterator fbegin = fmain.begin(); + function_info* f = def.functions.lookup()->find(fbegin, fmain.end()); + if (f == 0) + { + std::cerr << "Error: main function not defined" << std::endl; + return 1; + } + + int nargs = argc-2; + if (f->arity != nargs) + { + std::cerr << "Error: main function requires " << f->arity << " arguments." << std::endl; + std::cerr << nargs << "supplied." << std::endl; + return 1; + } + + for (int i = 0; i < nargs; ++i) + mach.stack[i] = boost::lexical_cast(argv[i+2]); + + int r = mach.execute( + code // code + , code.begin() + f->address // pc + , mach.stack.begin() // frame_ptr + ); + + std::cout << "-------------------------\n"; + std::cout << "Result: " << r << std::endl; + std::cout << "-------------------------\n\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/mini_c/mini_c.hpp b/example/qi/mini_c/mini_c.hpp new file mode 100644 index 000000000..070e92f8c --- /dev/null +++ b/example/qi/mini_c/mini_c.hpp @@ -0,0 +1,354 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_C) +#define BOOST_SPIRIT_MINI_C + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::function; +using boost::phoenix::ref; +using boost::phoenix::size; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + + op_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to an absolute position in the code if top stack + // evaluates to false + op_jump, // jump to an absolute position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute( + std::vector const& code // the program code + , std::vector::const_iterator pc // program counter + , std::vector::iterator frame_ptr // start of arguments and locals + ); + + std::vector stack; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A generic compiler that compiles 1 to 3 codes +/////////////////////////////////////////////////////////////////////////////// +struct function_info +{ + int arity; + int address; +}; + +struct compile_op +{ + template + struct result { typedef void type; }; + + compile_op(std::vector& code) + : code(code) + { + } + + void operator()(int a) const + { + code.push_back(a); + } + + void operator()(int a, int b) const + { + code.push_back(a); + code.push_back(b); + } + + void operator()(int a, int b, int c) const + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + } + + // special overload for function calls + void operator()(function_info const& info, int got_nargs, bool& parse_result) const + { + if (got_nargs == info.arity) + { + code.push_back(op_call); + code.push_back(info.arity); + code.push_back(info.address); + } + else + { + parse_result = false; // fail the parse + std::cerr << "wrong number of args" << std::endl; + } + } + + std::vector& code; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Our error handler +/////////////////////////////////////////////////////////////////////////////// +struct error_handler_ +{ + template + struct result { typedef void type; }; + + template + void operator()( + std::string const& what + , Iterator err_pos, Iterator last) const + { + std::cout + << "Error! Expecting " + << what // what failed? + << " here: \"" + << std::string(err_pos, last) // iterators to error-pos, end + << "\"" + << std::endl + ; + } +}; + +function const error_handler = error_handler_(); + +/////////////////////////////////////////////////////////////////////////////// +// A functor that adds variables to our (variables) symbol-table +/////////////////////////////////////////////////////////////////////////////// +struct var_adder +{ + template + struct result { typedef void type; }; + + var_adder(symbols& vars, int& nvars) + : vars(vars), nvars(nvars) + { + } + + void operator()(std::string const& var) const + { + vars.add(var.begin(), var.end(), nvars++); + }; + + symbols& vars; + int& nvars; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A functor that adds functions to our (function) symbol-table +/////////////////////////////////////////////////////////////////////////////// +struct function_adder +{ + template + struct result { typedef void type; }; + + function_adder(symbols& functions) + : functions(functions) + { + } + + void operator()(std::string const& function_id, int arity, int address) const + { + function_info info = {arity, address}; + functions.add(function_id.begin(), function_id.end(), info); + }; + + symbols& functions; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A functor that resets the function-related state variables +/////////////////////////////////////////////////////////////////////////////// +struct function_state_reset +{ + template + struct result { typedef void type; }; + + function_state_reset( + std::vector& code + , symbols& vars + , int& nvars) + : code(code) + , vars(vars) + , nvars(nvars) + { + } + + void operator()(int address) const + { + code[address+1] = nvars; + nvars = 0; // reset + vars.clear(); // reset + }; + + std::vector& code; + symbols& vars; + int& nvars; +}; + +/////////////////////////////////////////////////////////////////////////////// +// White-space and comments grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct white_space_def : grammar_def +{ + white_space_def() + { + start = + space // tab/space/cr/lf + | "/*" >> *(char_ - "*/") >> "*/" // C-style comments + ; + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct expression : grammar_def > > +{ + expression( + std::vector& code + , symbols& vars + , symbols& functions); + + typedef grammar > white_space; + + rule + expr, equality_expr, relational_expr + , logical_expr, additive_expr, multiplicative_expr + , unary_expr, primary_expr, variable + ; + + rule, white_space> function_call; + + std::vector& code; + symbols& vars; + symbols& functions; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct statement : grammar_def > > +{ + statement(std::vector& code, symbols& functions); + + typedef grammar > white_space; + + std::vector& code; + symbols vars; + symbols& functions; + int nvars; + bool has_return; + + expression expr_def; + grammar > expr; + rule + statement_, statement_list, var_decl, compound_statement + , return_statement; + + rule, white_space> if_statement; + rule, white_space> while_statement; + rule identifier; + rule var_ref; + rule, white_space> assignment; + rule assignment_rhs; + + function add_var; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our program grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct program : grammar_def > > +{ + program(std::vector& code); + + typedef grammar > white_space; + + std::vector& code; + rule identifier; + rule start; + + typedef + locals< + std::string // function name + , int // address + > + function_locals; + + symbols functions; + statement statement_def; + grammar > statement; + + rule function; + boost::phoenix::function add_function; + boost::phoenix::function state_reset; + boost::phoenix::function op; +}; + +#endif + + diff --git a/example/qi/mini_c/mini_ca.cpp b/example/qi/mini_c/mini_ca.cpp new file mode 100644 index 000000000..cc9329ec8 --- /dev/null +++ b/example/qi/mini_c/mini_ca.cpp @@ -0,0 +1,155 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_c.hpp" + +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) + +int vmachine::execute( + std::vector const& code + , std::vector::const_iterator pc + , std::vector::iterator frame_ptr +) +{ + std::vector::iterator stack_ptr = frame_ptr; + + while (true) + { + BOOST_ASSERT(pc != code.end()); + + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(stack_ptr[-1]); + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc = code.begin() + *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc = code.begin() + *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr += *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } +} + diff --git a/example/qi/mini_c/mini_cb.cpp b/example/qi/mini_c/mini_cb.cpp new file mode 100644 index 000000000..64f507144 --- /dev/null +++ b/example/qi/mini_c/mini_cb.cpp @@ -0,0 +1,19 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_cb.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_expression() +{ + typedef std::string::const_iterator iterator_type; + symbols functions; + symbols vars; + std::vector code; + expression g(code, vars, functions); +} + diff --git a/example/qi/mini_c/mini_cb.hpp b/example/qi/mini_c/mini_cb.hpp new file mode 100644 index 000000000..5eab8a769 --- /dev/null +++ b/example/qi/mini_c/mini_cb.hpp @@ -0,0 +1,115 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_CB) +#define BOOST_SPIRIT_MINI_CB + +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +expression::expression( + std::vector& code + , symbols& vars + , symbols& functions) + : code(code) + , vars(vars) + , functions(functions) + , op(code) +{ + expr = + equality_expr.alias() + ; + + equality_expr = + relational_expr + >> *( ("==" > relational_expr [op(op_eq)]) + | ("!=" > relational_expr [op(op_neq)]) + ) + ; + + relational_expr = + logical_expr + >> *( ("<=" > logical_expr [op(op_lte)]) + | ('<' > logical_expr [op(op_lt)]) + | (">=" > logical_expr [op(op_gte)]) + | ('>' > logical_expr [op(op_gt)]) + ) + ; + + logical_expr = + additive_expr + >> *( ("&&" > additive_expr [op(op_and)]) + | ("||" > additive_expr [op(op_or)]) + ) + ; + + additive_expr = + multiplicative_expr + >> *( ('+' > multiplicative_expr [op(op_add)]) + | ('-' > multiplicative_expr [op(op_sub)]) + ) + ; + + multiplicative_expr = + unary_expr + >> *( ('*' > unary_expr [op(op_mul)]) + | ('/' > unary_expr [op(op_div)]) + ) + ; + + unary_expr = + primary_expr + | ('!' > primary_expr [op(op_not)]) + | ('-' > primary_expr [op(op_neg)]) + | ('+' > primary_expr) + ; + + primary_expr = + uint_ [op(op_int, _1)] + | variable + | function_call + | lit("true") [op(op_true)] + | lit("false") [op(op_false)] + | '(' > expr > ')' + ; + + variable = + ( + lexeme[ + vars + >> !(alnum | '_') // make sure we have whole words + ] + ) [op(op_load, _1)] + ; + + function_call = + functions [_a = _1] + >> '(' + >> -( + expr [++_b] + >> *(',' > expr [++_b]) + ) + > char_(')') [op(_a, _b, pass)] + ; + + expr.name("expression"); + equality_expr.name("equality-expression"); + relational_expr.name("relational-expression"); + logical_expr.name("logical-expression"); + additive_expr.name("additive-expression"); + multiplicative_expr.name("multiplicative-expression"); + unary_expr.name("unary-expression"); + primary_expr.name("primary-expression"); + variable.name("variable"); + function_call.name("function-call"); + + on_error(expr, error_handler(_4, _3, _2)); +} + +#endif diff --git a/example/qi/mini_c/mini_cc.cpp b/example/qi/mini_c/mini_cc.cpp new file mode 100644 index 000000000..db9b509f2 --- /dev/null +++ b/example/qi/mini_c/mini_cc.cpp @@ -0,0 +1,17 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_cc.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_statement() +{ + typedef std::string::const_iterator iterator_type; + symbols functions; + std::vector code; + statement g(code, functions); +} diff --git a/example/qi/mini_c/mini_cc.hpp b/example/qi/mini_c/mini_cc.hpp new file mode 100644 index 000000000..dc866f2f8 --- /dev/null +++ b/example/qi/mini_c/mini_cc.hpp @@ -0,0 +1,144 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_CC) +#define BOOST_SPIRIT_MINI_CC + +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +statement::statement( + std::vector& code, symbols& functions) + : code(code) + , functions(functions) + , nvars(0) + , expr_def(code, vars, functions) + , expr(expr_def, expr_def.expr) + , add_var(var_adder(vars, nvars)) + , op(code) +{ + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + var_ref = + lexeme + [ + vars [_val = _1] + >> !(alnum | '_') // make sure we have whole words + ] + ; + + var_decl = + "int" + > !var_ref // make sure the variable isn't redeclared + > identifier [add_var(_1)] + > (';' | '=' > assignment_rhs(ref(nvars)-1)) + ; + + assignment = + var_ref [_a = _1] + >> '=' + > assignment_rhs(_a) + ; + + assignment_rhs = + expr + > char_(';') [op(op_store, _r1)] + ; + + if_statement = + lit("if") + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + // now we know where to jump to (after the if branch) + ref(code)[_a] = size(ref(code)) + ] + >> + -( + lexeme[ + "else" + >> !(alnum | '_') // make sure we have whole words + ] [ + ref(code)[_a] += 2, // adjust for the "else" jump + op(op_jump, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > statement_ [ + // now we know where to jump to (after the else branch) + ref(code)[_a] = size(ref(code)) + ] + ) + ; + + while_statement = + lit("while") [ + _a = size(ref(code)) // mark our position + ] + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _b = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + op(op_jump, _a), // loop back + // now we know where to jump to (to exit the loop) + ref(code)[_b] = size(ref(code)) + ] + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + return_statement = + lexeme[ + "return" + >> !(alnum | '_') // make sure we have whole words + ] + >> -( + eps(ref(has_return)) > expr [op(op_return)] + ) + > ';' + ; + + statement_ = + var_decl + | assignment + | compound_statement + | if_statement + | while_statement + | return_statement + ; + + statement_list = +statement_; + + identifier.name("identifier"); + var_ref.name("variable-reference"); + var_decl.name("variable-declaration"); + assignment.name("assignment"); + assignment_rhs.name("assignment-rhs"); + if_statement.name("if-statement"); + while_statement.name("while-statement"); + compound_statement.name("compound-statement"); + return_statement.name("return-statement"); + statement_.name("statement"); + statement_list.name("statement-list"); + + on_error(statement_list, error_handler(_4, _3, _2)); +} + +#endif + diff --git a/example/qi/mini_c/mini_cd.cpp b/example/qi/mini_c/mini_cd.cpp new file mode 100644 index 000000000..cc1ae1431 --- /dev/null +++ b/example/qi/mini_c/mini_cd.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_cd.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_program() +{ + typedef std::string::const_iterator iterator_type; + std::vector code; + program g(code); +} diff --git a/example/qi/mini_c/mini_cd.hpp b/example/qi/mini_c/mini_cd.hpp new file mode 100644 index 000000000..2d0cee886 --- /dev/null +++ b/example/qi/mini_c/mini_cd.hpp @@ -0,0 +1,71 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_CD) +#define BOOST_SPIRIT_MINI_CD + +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our program grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +program::program(std::vector& code) + : code(code) + , statement_def(code, functions) + , statement(statement_def, statement_def.statement_list) + , add_function(function_adder(functions)) + , state_reset(function_state_reset(code, statement_def.vars, statement_def.nvars)) + , op(code) +{ + bool& has_return = statement_def.has_return; + int& nvars = statement_def.nvars; + boost::phoenix::function& add_var = statement_def.add_var; + + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + function = + ( + lit("void") [ref(has_return) = false] + | lit("int") [ref(has_return) = true] + ) + >> !functions // no duplicate functions! + >> identifier [_a = _1] + >> '(' + > -( + identifier [add_var(_1)] + >> *(',' > identifier [add_var(_1)]) + ) + > ')' + > char_('{') [ + _b = size(ref(code)), + add_function( + _a // function name + , ref(nvars) // arity + , size(ref(code)) // address + ), + op(op_stk_adj, 0) // adjust this later + ] + > statement + > char_('}') [state_reset(_b)] + ; + + start = + +function + ; + + identifier.name("identifier"); + function.name("function"); + start.name("program"); + + on_error(start, error_handler(_4, _3, _2)); +} + +#endif + + diff --git a/example/qi/mini_xml1.cpp b/example/qi/mini_xml1.cpp new file mode 100644 index 000000000..fd4b1e8a6 --- /dev/null +++ b/example/qi/mini_xml1.cpp @@ -0,0 +1,224 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +namespace fusion = boost::fusion; +namespace phoenix = boost::phoenix; + +using phoenix::at_c; +using phoenix::push_back; + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML tree representation +/////////////////////////////////////////////////////////////////////////////// +struct mini_xml; + +typedef + boost::variant< + boost::recursive_wrapper + , std::string + > +mini_xml_node; + +struct mini_xml +{ + std::string name; // tag name + std::vector children; // children +}; + +// We need to tell fusion about our mini_xml struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + mini_xml, + (std::string, name) + (std::vector, children) +) + +/////////////////////////////////////////////////////////////////////////////// +// Print out the mini xml tree +/////////////////////////////////////////////////////////////////////////////// +int const tabsize = 4; + +void tab(int indent) +{ + for (int i = 0; i < indent; ++i) + std::cout << ' '; +} + +struct mini_xml_printer +{ + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; +}; + +struct mini_xml_node_printer : boost::static_visitor<> +{ + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; +}; + +void mini_xml_printer::operator()(mini_xml const& xml) const +{ + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; +} + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_def : grammar_def +{ + mini_xml_def() + { + text = lexeme[+(char_ - '<') [_val += _1]]; + node = (xml | text) [_val = _1]; + + start_tag = + '<' + >> lexeme[+(char_ - '>') [_val += _1]] + >> '>' + ; + + end_tag = + "> lit(_r1) + >> '>' + ; + + xml = + start_tag [at_c<0>(_val) = _1] + >> *node [push_back(at_c<1>(_val), _1)] + >> end_tag(at_c<0>(_val)) + ; + } + + rule xml; + rule node; + rule text; + rule start_tag; + rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string storage; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(storage)); + + typedef mini_xml_def mini_xml_def; + mini_xml_def def; // Our grammar definition + grammar xml(def, def.xml); // Our grammar + mini_xml ast; // our tree + + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, ast, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/example/qi/mini_xml2.cpp b/example/qi/mini_xml2.cpp new file mode 100644 index 000000000..e306a2a94 --- /dev/null +++ b/example/qi/mini_xml2.cpp @@ -0,0 +1,225 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +namespace fusion = boost::fusion; +namespace phoenix = boost::phoenix; + +using phoenix::at_c; +using phoenix::push_back; + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML tree representation +/////////////////////////////////////////////////////////////////////////////// +struct mini_xml; + +typedef + boost::variant< + boost::recursive_wrapper + , std::string + > +mini_xml_node; + +struct mini_xml +{ + std::string name; // tag name + std::vector children; // children +}; + +// We need to tell fusion about our mini_xml struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + mini_xml, + (std::string, name) + (std::vector, children) +) + +/////////////////////////////////////////////////////////////////////////////// +// Print out the mini xml tree +/////////////////////////////////////////////////////////////////////////////// +int const tabsize = 4; + +void tab(int indent) +{ + for (int i = 0; i < indent; ++i) + std::cout << ' '; +} + +struct mini_xml_printer +{ + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; +}; + +struct mini_xml_node_printer : boost::static_visitor<> +{ + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; +}; + +void mini_xml_printer::operator()(mini_xml const& xml) const +{ + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; +} + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_def + : grammar_def, space_type> +{ + mini_xml_def() + { + text %= lexeme[+(char_ - '<')]; + node %= xml | text; + + start_tag %= + '<' + >> lexeme[+(char_ - '>')] + >> '>' + ; + + end_tag = + "> lit(_r1) + >> '>' + ; + + xml %= + start_tag[_a = _1] + >> *node + >> end_tag(_a) + ; + } + + rule, space_type> xml; + rule node; + rule text; + rule start_tag; + rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string storage; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(storage)); + + typedef mini_xml_def mini_xml_def; + mini_xml_def def; // Our grammar definition + grammar xml(def, def.xml); // Our grammar + mini_xml ast; // our tree + + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, ast, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/example/qi/mini_xml_karma.cpp b/example/qi/mini_xml_karma.cpp new file mode 100644 index 000000000..e2bea4693 --- /dev/null +++ b/example/qi/mini_xml_karma.cpp @@ -0,0 +1,262 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A mini XML-like parser, Karma is used to print out the generated AST +// +// [ JDG March 25, 2007 ] spirit2 +// [ HK April 02, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +namespace fusion = boost::fusion; +namespace phoenix = boost::phoenix; + +using phoenix::at_c; +using phoenix::push_back; + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML tree representation +/////////////////////////////////////////////////////////////////////////////// +struct mini_xml; + +typedef + boost::variant< + boost::recursive_wrapper + , std::string + > +mini_xml_node; + +struct mini_xml +{ + std::string name; // tag name + std::vector children; // children +}; + +// We need to tell fusion about our mini_xml struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + mini_xml, + (std::string, name) + (std::vector, children) +) + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_parser : + qi::grammar_def +{ + mini_xml_parser() + { + text = lexeme[+(char_ - '<') [text.val += _1]]; + node = (xml | text) [node.val = _1]; + + start_tag = + '<' + >> lexeme[+(char_ - '>') [start_tag.val += _1]] + >> '>' + ; + + end_tag = + "> lit(end_tag._1) + >> '>' + ; + + xml = + start_tag [at_c<0>(xml.val) = _1] + >> *node [push_back(at_c<1>(xml.val), _1)] + >> end_tag(at_c<0>(xml.val)) + ; + } + + qi::rule xml; + qi::rule node; + qi::rule text; + qi::rule start_tag; + qi::rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////////// +template +struct string_appender +{ + string_appender(String& s) + : str(s) + {} + + template + void operator()(T const &x) const + { + str += x; + } + + String& str; +}; + +template +inline string_appender +make_string_appender(String& str) +{ + return string_appender(str); +} + +template +struct output_iterator +{ + typedef std::basic_string string_type; + typedef string_appender appender_type; + typedef boost::function_output_iterator type; + + static type + call(std::basic_string& str) + { + return boost::make_function_output_iterator( + make_string_appender(str)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_generator + : boost::spirit::karma::grammar_def +{ +// typedef karma::grammar_def base_type; +// boost::mpl::print x; + + mini_xml_generator() + { +// text = verbatim[lit(text._1)]; +// node = (xml | text) [_1 = node._1]; +// +// start_tag = +// '<' +// << verbatim[lit(start_tag._1)] +// << '>' +// ; +// +// end_tag = +// "' +// ; +// +// xml = +// start_tag(at_c<0>(xml._1)) +// << (*node) [ref(at_c<1>(xml._1))] +// << end_tag(at_c<0>(xml._1)) + ; + } + + karma::rule xml; +// karma::rule node; +// karma::rule text; +// karma::rule start_tag; +// karma::rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string storage; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(storage)); + + typedef mini_xml_parser mini_xml_parser; + mini_xml_parser def; // Our grammar definition + qi::grammar xmlin(def, def.xml); // Our grammar + mini_xml ast; // our tree + + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xmlin, ast, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + + typedef output_iterator::type outiter_type; + typedef mini_xml_generator mini_xml_generator; + + mini_xml_generator gen; // Our grammar definition + karma::grammar xmlout(gen, gen.xml); // Our grammar + + std::string generated; + bool r = generate_delimited(output_iterator::call(generated), + xmlout(ast), space); + + if (r) + std::cout << generated << std::endl; + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/example/qi/mini_xml_samples/1.xml b/example/qi/mini_xml_samples/1.xml new file mode 100644 index 000000000..54181ed43 --- /dev/null +++ b/example/qi/mini_xml_samples/1.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/example/qi/mini_xml_samples/2.xml b/example/qi/mini_xml_samples/2.xml new file mode 100644 index 000000000..266e4b1ff --- /dev/null +++ b/example/qi/mini_xml_samples/2.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/example/qi/mini_xml_samples/3.xml b/example/qi/mini_xml_samples/3.xml new file mode 100644 index 000000000..9d89cf7b6 --- /dev/null +++ b/example/qi/mini_xml_samples/3.xml @@ -0,0 +1,5 @@ + + bar 1 + bar 2 + bar 3 + \ No newline at end of file diff --git a/example/qi/num_list.cpp b/example/qi/num_list.cpp new file mode 100644 index 000000000..156d1cefd --- /dev/null +++ b/example/qi/num_list.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This sample demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our number list compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_numbers(Iterator first, Iterator last, std::vector& v) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back(ref(v), _1)] + >> *(',' >> double_[push_back(ref(v), _1)]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector of numbers\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::vector v; + if (parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/num_list2.cpp b/example/qi/num_list2.cpp new file mode 100644 index 000000000..95ee677a2 --- /dev/null +++ b/example/qi/num_list2.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This sample demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our number list compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_numbers(Iterator first, Iterator last, std::vector& v) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back(ref(v), _1)] % ',' + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector of numbers\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::vector v; + if (parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/num_list3.cpp b/example/qi/num_list3.cpp new file mode 100644 index 000000000..fbcd293f6 --- /dev/null +++ b/example/qi/num_list3.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This sample demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our number list compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_numbers(Iterator first, Iterator last, std::vector& v) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_ % ',' + ) + , + // End grammar + + v, space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector of numbers\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::vector v; + if (parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/roman.cpp b/example/qi/roman.cpp new file mode 100644 index 000000000..a993558d3 --- /dev/null +++ b/example/qi/roman.cpp @@ -0,0 +1,168 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A Roman Numerals Parser (demonstrating the symbol table). This is +// discussed in the "Symbols" chapter in the Spirit User's Guide. +// +// [ JDG August 22, 2002 ] spirit1 +// [ JDG March 13, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; +using boost::phoenix::ref; + +/////////////////////////////////////////////////////////////////////////////// +// Parse roman hundreds (100..900) numerals using the symbol table. +// Notice that the data associated with each slot is passed +// to attached semantic actions. +/////////////////////////////////////////////////////////////////////////////// +struct hundreds_ : symbols +{ + hundreds_() + { + add + ("C" , 100) + ("CC" , 200) + ("CCC" , 300) + ("CD" , 400) + ("D" , 500) + ("DC" , 600) + ("DCC" , 700) + ("DCCC" , 800) + ("CM" , 900) + ; + } + +} hundreds; + +/////////////////////////////////////////////////////////////////////////////// +// Parse roman tens (10..90) numerals using the symbol table. +/////////////////////////////////////////////////////////////////////////////// +struct tens_ : symbols +{ + tens_() + { + add + ("X" , 10) + ("XX" , 20) + ("XXX" , 30) + ("XL" , 40) + ("L" , 50) + ("LX" , 60) + ("LXX" , 70) + ("LXXX" , 80) + ("XC" , 90) + ; + } + +} tens; + +/////////////////////////////////////////////////////////////////////////////// +// Parse roman ones (1..9) numerals using the symbol table. +/////////////////////////////////////////////////////////////////////////////// +struct ones_ : symbols +{ + ones_() + { + add + ("I" , 1) + ("II" , 2) + ("III" , 3) + ("IV" , 4) + ("V" , 5) + ("VI" , 6) + ("VII" , 7) + ("VIII" , 8) + ("IX" , 9) + ; + } + +} ones; + +/////////////////////////////////////////////////////////////////////////////// +// roman (numerals) grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct roman : grammar_def +{ + roman() + { + start + = +char_('M') [_val += 1000] + || hundreds [_val += _1] + || tens [_val += _1] + || ones [_val += _1]; + + // Note the use of the || operator. The expression + // a || b reads match a or b and in sequence. Try + // defining the roman numerals grammar in YACC or + // PCCTS. Spirit rules! :-) + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tRoman Numerals Parser\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type a Roman Numeral ...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef roman roman; + + roman def; // Our grammar definition + grammar roman_parser(def); // Our grammar + + std::string str; + unsigned result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = parse(iter, end, roman_parser[ref(result) = _1]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/sum.cpp b/example/qi/sum.cpp new file mode 100644 index 000000000..c6a66d4f9 --- /dev/null +++ b/example/qi/sum.cpp @@ -0,0 +1,92 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A parser for summing a comma-separated list of numbers using phoenix. +// +// [ JDG June 28, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our adder +/////////////////////////////////////////////////////////////////////////////// +template +bool adder(Iterator first, Iterator last, double& n) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[ref(n) = _1] >> *(',' >> double_[ref(n) += _1]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA parser for summing a list of numbers...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers are added using Phoenix.\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + double n; + if (adder(str.begin(), str.end(), n)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + std::cout << "sum = " << n; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/index.html b/index.html new file mode 100644 index 000000000..1eb81150a --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Automatic redirection failed, click this + link  
+

© Copyright Beman Dawes, 2001

+

Distributed under the Boost Software License, Version 1.0. (See + accompanying file + LICENSE_1_0.txt or copy at + www.boost.org/LICENSE_1_0.txt)

+ + \ No newline at end of file diff --git a/phoenix/doc/Jamfile.v2 b/phoenix/doc/Jamfile.v2 new file mode 100644 index 000000000..1e569300e --- /dev/null +++ b/phoenix/doc/Jamfile.v2 @@ -0,0 +1,12 @@ +project boost/libs/spirit/doc/phoenix/doc ; +import boostbook : boostbook ; +using quickbook ; + +boostbook quickbook + : + users_manual.qbk + : + boost.root=../../../../.. + boost.libraries=../../../../libraries.htm + ; + diff --git a/phoenix/doc/html/boostbook.css b/phoenix/doc/html/boostbook.css new file mode 100644 index 000000000..d84d53843 --- /dev/null +++ b/phoenix/doc/html/boostbook.css @@ -0,0 +1,511 @@ +/*============================================================================= + Copyright (c) 2004 Joel de Guzman + http://spirit.sourceforge.net/ + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +/*============================================================================= + Body defaults +=============================================================================*/ + + body + { + margin: 1em; + font-family: sans-serif; + } + +/*============================================================================= + Paragraphs +=============================================================================*/ + + p + { + text-align: left; + font-size: 10pt; + line-height: 1.15; + } + +/*============================================================================= + Program listings +=============================================================================*/ + + /* Code on paragraphs */ + p tt.computeroutput + { + font-size: 9pt; + } + + pre.synopsis + { + font-size: 90%; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + .programlisting, + .screen + { + font-size: 9pt; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + /* Program listings in tables don't get borders */ + td .programlisting, + td .screen + { + margin: 0pc 0pc 0pc 0pc; + padding: 0pc 0pc 0pc 0pc; + } + +/*============================================================================= + Headings +=============================================================================*/ + + h1, h2, h3, h4, h5, h6 + { + text-align: left; + margin: 1em 0em 0.5em 0em; + font-weight: bold; + } + + h1 { font: 140% } + h2 { font: bold 140% } + h3 { font: bold 130% } + h4 { font: bold 120% } + h5 { font: italic 110% } + h6 { font: italic 100% } + + /* Top page titles */ + title, + h1.title, + h2.title + h3.title, + h4.title, + h5.title, + h6.title, + .refentrytitle + { + font-weight: bold; + margin-bottom: 1pc; + } + + h1.title { font-size: 140% } + h2.title { font-size: 140% } + h3.title { font-size: 130% } + h4.title { font-size: 120% } + h5.title { font-size: 110% } + h6.title { font-size: 100% } + + .section h1 + { + margin: 0em 0em 0.5em 0em; + font-size: 140%; + } + + .section h2 { font-size: 140% } + .section h3 { font-size: 130% } + .section h4 { font-size: 120% } + .section h5 { font-size: 110% } + .section h6 { font-size: 100% } + + /* Code on titles */ + h1 tt.computeroutput { font-size: 140% } + h2 tt.computeroutput { font-size: 140% } + h3 tt.computeroutput { font-size: 130% } + h4 tt.computeroutput { font-size: 120% } + h5 tt.computeroutput { font-size: 110% } + h6 tt.computeroutput { font-size: 100% } + +/*============================================================================= + Author +=============================================================================*/ + + h3.author + { + font-size: 100% + } + +/*============================================================================= + Lists +=============================================================================*/ + + li + { + font-size: 10pt; + line-height: 1.3; + } + + /* Unordered lists */ + ul + { + text-align: left; + } + + /* Ordered lists */ + ol + { + text-align: left; + } + +/*============================================================================= + Links +=============================================================================*/ + + a + { + text-decoration: none; /* no underline */ + } + + a:hover + { + text-decoration: underline; + } + +/*============================================================================= + Spirit style navigation +=============================================================================*/ + + .spirit-nav + { + text-align: right; + } + + .spirit-nav a + { + color: white; + padding-left: 0.5em; + } + + .spirit-nav img + { + border-width: 0px; + } + +/*============================================================================= + Table of contents +=============================================================================*/ + + .toc + { + margin: 1pc 4% 0pc 4%; + padding: 0.1pc 1pc 0.1pc 1pc; + font-size: 80%; + line-height: 1.15; + } + + .boost-toc + { + float: right; + padding: 0.5pc; + } + +/*============================================================================= + Tables +=============================================================================*/ + + .table-title, + div.table p.title + { + margin-left: 4%; + padding-right: 0.5em; + padding-left: 0.5em; + } + + .informaltable table, + .table table + { + width: 92%; + margin-left: 4%; + margin-right: 4%; + } + + div.informaltable table, + div.table table + { + padding: 4px; + } + + /* Table Cells */ + div.informaltable table tr td, + div.table table tr td + { + padding: 0.5em; + text-align: left; + font-size: 9pt; + } + + div.informaltable table tr th, + div.table table tr th + { + padding: 0.5em 0.5em 0.5em 0.5em; + border: 1pt solid white; + font-size: 80%; + } + +/*============================================================================= + Blurbs +=============================================================================*/ + + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + font-size: 9pt; /* A little bit smaller than the main text */ + line-height: 1.2; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + p.blurb img + { + padding: 1pt; + } + +/*============================================================================= + Variable Lists +=============================================================================*/ + + /* Make the terms in definition lists bold */ + div.variablelist dl dt, + span.term + { + font-weight: bold; + font-size: 10pt; + } + + div.variablelist table tbody tr td + { + text-align: left; + vertical-align: top; + padding: 0em 2em 0em 0em; + font-size: 10pt; + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + + div.variablelist dl dt + { + margin-bottom: 0.2em; + } + + div.variablelist dl dd + { + margin: 0em 0em 0.5em 2em; + font-size: 10pt; + } + + div.variablelist table tbody tr td p, + div.variablelist dl dd p + { + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + +/*============================================================================= + Misc +=============================================================================*/ + + /* Title of books and articles in bibliographies */ + span.title + { + font-style: italic; + } + + span.underline + { + text-decoration: underline; + } + + span.strikethrough + { + text-decoration: line-through; + } + + /* Copyright, Legal Notice */ + div div.legalnotice p + { + text-align: left + } + +/*============================================================================= + Colors +=============================================================================*/ + + @media screen + { + /* Links */ + a + { + color: #005a9c; + } + + a:visited + { + color: #9c5a9c; + } + + h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, + h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, + h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited + { + text-decoration: none; /* no underline */ + color: #000000; + } + + /* Syntax Highlighting */ + .keyword { color: #0000AA; } + .identifier { color: #000000; } + .special { color: #707070; } + .preprocessor { color: #402080; } + .char { color: teal; } + .comment { color: #800000; } + .string { color: teal; } + .number { color: teal; } + .white_bkd { background-color: #FFFFFF; } + .dk_grey_bkd { background-color: #999999; } + + /* Copyright, Legal Notice */ + .copyright + { + color: #666666; + font-size: small; + } + + div div.legalnotice p + { + color: #666666; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid #DCDCDC; + } + + .programlisting, + .screen + { + border: 1px solid #DCDCDC; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Blurbs */ + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + border: 1px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid #DCDCDC; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid #DCDCDC; + } + + div.informaltable table tr th, + div.table table tr th + { + background-color: #F0F0F0; + border: 1px solid #DCDCDC; + } + + /* Misc */ + span.highlight + { + color: #00A000; + } + } + + @media print + { + /* Links */ + a + { + color: black; + } + + a:visited + { + color: black; + } + + .spirit-nav + { + display: none; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid gray; + } + + .programlisting, + .screen + { + border: 1px solid gray; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid gray; + } + + .informaltable table, + .table table + { + border: 1px solid gray; + border-collapse: collapse; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid gray; + } + + div.informaltable table tr th, + div.table table tr th + { + border: 1px solid gray; + } + + /* Misc */ + span.highlight + { + font-weight: bold; + } + } diff --git a/phoenix/doc/html/images/add2.png b/phoenix/doc/html/images/add2.png new file mode 100644 index 000000000..11b850a00 Binary files /dev/null and b/phoenix/doc/html/images/add2.png differ diff --git a/phoenix/doc/html/images/add2_call.png b/phoenix/doc/html/images/add2_call.png new file mode 100644 index 000000000..782edf876 Binary files /dev/null and b/phoenix/doc/html/images/add2_call.png differ diff --git a/phoenix/doc/html/images/adder.png b/phoenix/doc/html/images/adder.png new file mode 100644 index 000000000..1578757b2 Binary files /dev/null and b/phoenix/doc/html/images/adder.png differ diff --git a/phoenix/doc/html/images/alert.png b/phoenix/doc/html/images/alert.png new file mode 100644 index 000000000..b4645bc7e Binary files /dev/null and b/phoenix/doc/html/images/alert.png differ diff --git a/phoenix/doc/html/images/banner.png b/phoenix/doc/html/images/banner.png new file mode 100644 index 000000000..e663460be Binary files /dev/null and b/phoenix/doc/html/images/banner.png differ diff --git a/phoenix/doc/html/images/fbox.png b/phoenix/doc/html/images/fbox.png new file mode 100644 index 000000000..0ef9d0776 Binary files /dev/null and b/phoenix/doc/html/images/fbox.png differ diff --git a/phoenix/doc/html/images/funnel_in.png b/phoenix/doc/html/images/funnel_in.png new file mode 100644 index 000000000..13ae55547 Binary files /dev/null and b/phoenix/doc/html/images/funnel_in.png differ diff --git a/phoenix/doc/html/images/funnel_out.png b/phoenix/doc/html/images/funnel_out.png new file mode 100644 index 000000000..20c6f8aa6 Binary files /dev/null and b/phoenix/doc/html/images/funnel_out.png differ diff --git a/phoenix/doc/html/images/home.png b/phoenix/doc/html/images/home.png new file mode 100644 index 000000000..5584aacb0 Binary files /dev/null and b/phoenix/doc/html/images/home.png differ diff --git a/phoenix/doc/html/images/lambda_cpp.png b/phoenix/doc/html/images/lambda_cpp.png new file mode 100644 index 000000000..74aa93d06 Binary files /dev/null and b/phoenix/doc/html/images/lambda_cpp.png differ diff --git a/phoenix/doc/html/images/next.png b/phoenix/doc/html/images/next.png new file mode 100644 index 000000000..59800b4e8 Binary files /dev/null and b/phoenix/doc/html/images/next.png differ diff --git a/phoenix/doc/html/images/note.png b/phoenix/doc/html/images/note.png new file mode 100644 index 000000000..3ed047cac Binary files /dev/null and b/phoenix/doc/html/images/note.png differ diff --git a/phoenix/doc/html/images/organization.png b/phoenix/doc/html/images/organization.png new file mode 100644 index 000000000..8559621ee Binary files /dev/null and b/phoenix/doc/html/images/organization.png differ diff --git a/phoenix/doc/html/images/prev.png b/phoenix/doc/html/images/prev.png new file mode 100644 index 000000000..d88a40f92 Binary files /dev/null and b/phoenix/doc/html/images/prev.png differ diff --git a/phoenix/doc/html/images/smiley.png b/phoenix/doc/html/images/smiley.png new file mode 100644 index 000000000..30a77f71c Binary files /dev/null and b/phoenix/doc/html/images/smiley.png differ diff --git a/phoenix/doc/html/images/tip.png b/phoenix/doc/html/images/tip.png new file mode 100644 index 000000000..9f596b0b8 Binary files /dev/null and b/phoenix/doc/html/images/tip.png differ diff --git a/phoenix/doc/html/images/up.png b/phoenix/doc/html/images/up.png new file mode 100644 index 000000000..17d9c3ec4 Binary files /dev/null and b/phoenix/doc/html/images/up.png differ diff --git a/phoenix/doc/html/index.html b/phoenix/doc/html/index.html new file mode 100644 index 000000000..eee3a8289 --- /dev/null +++ b/phoenix/doc/html/index.html @@ -0,0 +1,224 @@ + + + +Chapter 1. Phoenix 2.0 + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
Next
+
+
+

+Chapter 1. Phoenix 2.0

+

+Joel de Guzman +

+

+Dan Marsden +

+
+
+

+ Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +

+
+
+ +
+

+Preface

+

+ Functional programming is so called because a program consists + entirely of functions. The main program itself is written as a function which + receives the program's input as its argument and delivers the program's output + as its result. Typically the main function is defined in terms of other functions, + which in turn are defined in terms of still more functions until at the bottom + level the functions are language primitives. +

+

+ John Hughes-- Why Functional Programming + Matters +

+

+ lambda_cpp +

+

+ + Description +

+

+ Phoenix enables Functional Programming (FP) in C++. The design and implementation + of Phoenix is highly influenced by FC++ + by Yannis Smaragdakis and Brian McNamara and the BLL + (Boost Lambda Library) by Jaakko Jaarvi and Gary Powell. Phoenix is a blend + of FC++ and BLL using the implementation techniques used in the Spirit + inline parser. Phoenix version 2, this version, will probably be the last release + of the library. Phoenix v2 will be the basis of the Phoenix and BLL + merger. +

+

+ Phoenix is a header only library. It is extremely modular by design. One can + extract and use only a small subset of the full library, literally tearing + the library into small pieces, without fear that the pieces won't work anymore. + The library is organized in highly independent modules and layers. +

+

+ + How to use this manual +

+

+ The Phoenix library is organized in logical modules. This documentation provides + a user's guide and reference for each module in the library. A simple and clear + code example is worth a hundred lines of documentation; therefore, the user's + guide is presented with abundant examples annotated and explained in step-wise + manner. The user's guide is based on examples: lots of them. +

+

+ As much as possible, forward information (i.e. citing a specific piece of information + that has not yet been discussed) is avoided in the user's manual portion of + each module. In many cases, though, it is unavoidable that advanced but related + topics not be interspersed with the normal flow of discussion. To alleviate + this problem, topics categorized as "advanced" may be skipped at + first reading. +

+

+ Some icons are used to mark certain topics indicative of their relevance. These + icons precede some text to indicate: +

+
+

+ + Icons +

+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
IconNameMeaning
noteNoteInformation provided + is auxiliary but will give the reader a deeper insight into a specific + topic. May be skipped.
alertAlertInformation provided + is of utmost importance.
tipTipA potentially useful + and helpful piece of information.
+
+

+ This documentation is automatically generated by Spirit QuickBook documentation + tool. QuickBook can be found in the Spirit + Repository. +

+

+ + Support +

+

+ Please direct all questions to Spirit's mailing list. You can subscribe to + the Spirit + Mailing List. The mailing list has a searchable archive. A search link + to this archive is provided in Spirit's + home page. You may also read and post messages to the mailing list through + Spirit General + NNTP news portal (thanks to Gmane). + The news group mirrors the mailing list. Here is a link to the archives: http://news.gmane.org/gmane.comp.parsers.spirit.general. +

+

+ + ...To my dear daughter, Phoenix +

+
+
+ + + +

Last revised: June 09, 2006 at 13:58:55 GMT

+
+
Next
+ + diff --git a/phoenix/doc/html/phoenix/acknowledgement.html b/phoenix/doc/html/phoenix/acknowledgement.html new file mode 100644 index 000000000..6786bba26 --- /dev/null +++ b/phoenix/doc/html/phoenix/acknowledgement.html @@ -0,0 +1,87 @@ + + + +Acknowledgement + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Acknowledgement

+
    +
  1. + Hartmut Kaiser implemented the original lazy casts and constructors based + on his original work on Spirit SE "semantic expressions" (the precursor + to Phoenix). +
  2. +
  3. + Angus Leeming implemented the container functions on Phoenix-1 which I then + ported to Phoenix-2. +
  4. +
  5. + Daniel Wallin helped with the scope module, local variables, let and lambda + and the algorithms. I frequently discuss design issues with Daniel on Yahoo + Messenger. +
  6. +
  7. + Jaakko Jarvi. DA Lambda MAN! +
  8. +
  9. + Dave Abrahams, for his constant presence, wherever, whenever. +
  10. +
  11. + Aleksey Gurtovoy, DA MPL MAN! +
  12. +
  13. + Doug Gregor, always a source of inpiration. +
  14. +
  15. + Dan Marsden, did almost all the work in bringing Phoenix-2 out the door. +
  16. +
  17. + Eric Niebler did a 2.0 pre-release review and wrote some range related code + that Phoenix stole and used in the algorithms. +
  18. +
  19. + Thorsten Ottosen; Eric's range_ex code began life as "container_algo" + in the old boost sandbox, by Thorsten in 2002-2003. +
  20. +
  21. + Jeremy Siek, even prior to Thorsten, in 2001, started the "container_algo". +
  22. +
  23. + Vladimir Prus wrote the mutating algorithms code from the Boost Wiki. +
  24. +
  25. + Daryle Walker did a 2.0 pre-release review. +
  26. +
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/actors.html b/phoenix/doc/html/phoenix/actors.html new file mode 100644 index 000000000..158be9cf3 --- /dev/null +++ b/phoenix/doc/html/phoenix/actors.html @@ -0,0 +1,82 @@ + + + +Actors + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Actors

+

+ The Actor is the main concept + behind the library. Actors are function objects. An actor can accept 0 to + PHOENIX_LIMIT arguments. +

+
+ + +
note You can set PHOENIX_LIMIT, + the predefined maximum arity an actor can take. By default, PHOENIX_LIMIT is set to 10.
+

+ Phoenix supplies an actor class + template whose specializations model the Actor + concept. actor has one template + parameter, Eval, that supplies + the smarts to evaluate the resulting function. +

+
+template <typename Eval>
+struct actor : Eval
+{
+    return_type
+    operator()() const;
+
+    template <typename T0>
+    return_type
+    operator()(T0& _0) const;
+
+    template <typename T0, typename T1>
+    return_type
+    operator()(T0& _0, T1& _1) const;
+
+    //...
+};
+
+

+ The actor class accepts the arguments through a set of function call operators + for 0 to PHOENIX_LIMIT arities + (Don't worry about the details, for now. Note, for example, that we skimp over + the details regarding return_type). + The arguments are then forwarded to the actor's Eval + for evaluation. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/algorithm.html b/phoenix/doc/html/phoenix/algorithm.html new file mode 100644 index 000000000..3bd04d99c --- /dev/null +++ b/phoenix/doc/html/phoenix/algorithm.html @@ -0,0 +1,408 @@ + + + +Algorithm + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Algorithm

+
+#include <boost/spirit/phoenix/algorithm.hpp>
+
+

+ The algorithm module provides wrappers for the standard algorithms in the + <algorithm> and <numeric> + headers. +

+

+ The algorithms are divided into the categories iteration, transformation and + querying, modelling the Boost.MPL + library. The different algorithm classes can be included using the headers: +

+
+#include <boost/spirit/phoenix/stl/algorithm/iteration.hpp>
+#include <boost/spirit/phoenix/stl/algorithm/transformation.hpp>
+#include <boost/spirit/phoenix/stl/algorithm/querying.hpp>
+
+

+ The functions of the algorithm module take ranges as arguments where appropriate. + This is different to the standard library, but easy enough to pick up. Ranges + are described in detail in the Boost.Range + library. +

+

+ For example, using the standard copy algorithm to copy between 2 arrays: +

+
+int array[] = {1, 2, 3};
+int output[3];
+std::copy(array, array + 3, output); // We have to provide iterators 
+                                     // to both the start and end of array
+
+

+ The analogous code using the phoenix algorithm module is: +

+
+int array[] = {1, 2, 3};
+int output[3];
+copy(arg1, arg2)(array, output); // Notice only 2 arguments, the end of 
+                                 // array is established automatically
+
+

+ The Boost.Range + library provides support for standard containers, strings and arrays, and can + be extended to support additional types. +

+

+ The following tables describe the different categories of algorithms, and their + semantics. +

+
+ + +
tip Arguments in brackets denote optional parameters.
+
+

+ + Iteration Algorithms +

+ ++++ + + + + + + + + + + + + + + +
Functionstl Semantics
for_each(r, c)for_each(begin(r), end(r), f)
accumulate(r, o[, f])accumulate(begin(r), end(r), o[, f])
+
+
+

+ + Querying Algorithms +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Functionstl Semantics
find(r, a)find(begin(r), end(r), a)
find_if(r, f)find_if(begin(r), end(r), f)
find_end(r1, r2[, f])find_end(begin(r1), end(r1), begin(r2), end(r2)[, f])
find_first_of(r1, r2[, f])find_first_of(begin(r1), end(r1), begin(r2), end(r2)[, f])
adjacent_find(r[, f])adjacent_find(begin(r), end(r)[, f])
count(r, a)count(begin(r), end(r), a)
count_if(r, f)count_if(begin(r), end(r), f)
mismatch(r, i[, f])mismatch(begin(r), end(r), i[, f])
equal(r, i[, f])equal(begin(r), end(r), i[, f])
search(r1, r2[, f])search(begin(r1), end(r1), begin(r2), end(r2)[, f])
lower_bound(r, a[, f])lower_bound(begin(r), end(r), a[, f])
upper_bound(r, a[, f])upper_bound(begin(r), end(r), a[, f])
equal_range(r, a[, f])equal_range(begin(r), end(r), a[, f])
binary_search(r, a[, f])binary_search(begin(r), end(r), a[, f])
includes(r1, r2[, f])includes(begin(r1), end(r1), begin(r2), end(r2)[, f])
min_element(r[, f])min_element(begin(r), end(r)[, f])
max_element(r[, f])max_element(begin(r), end(r)[, f])
lexicographical_compare(r1, r2[, f])lexicographical_compare(begin(r1), end(r1), begin(r2), end(r2)[, f])
+
+
+

+ + Transformation Algorithms +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Functionstl Semantics
copy(r, o)copy(begin(r), end(r), o)
copy_backward(r, o)copy_backward(begin(r), end(r), o)
transform(r, o, f)transform(begin(r), end(r), o, f)
transform(r, i, o, f)transform(begin(r), end(r), i, o, f)
replace(r, a, b)replace(begin(r), end(r), a, b)
replace_if(r, f, a)replace(begin(r), end(r), f, a)
replace_copy(r, o, a, b)replace_copy(begin(r), end(r), o, a, b)
replace_copy_if(r, o, f, a)replace_copy_if(begin(r), end(r), o, f, a)
fill(r, a)fill(begin(r), end(r), a)
fill_n(r, n, a)fill_n(begin(r), n, a)
generate(r, f)generate(begin(r), end(r), f)
generate_n(r, n, f)generate_n(begin(r), n, f)
remove(r, a)remove(begin(r), end(r), a)
remove_if(r, f)remove_if(begin(r), end(r), f)
remove_copy(r, o, a)remove_copy(begin(r), end(r), o, a)
remove_copy_if(r, o, f)remove_copy_if(begin(r), end(r), o, f)
unique(r[, f])unique(begin(r), end(r)[, f])
unique_copy(r, o[, f])unique_copy(begin(r), end(r), o[, f])
reverse(r)reverse(begin(r), end(r))
reverse_copy(r, o)reverse_copy(begin(r), end(r), o)
rotate(r, m)rotate(begin(r), m, end(r))
rotate_copy(r, m, o)rotate_copy(begin(r), m, end(r), o)
random_shuffle(r[, f])random_shuffle(begin(r), end(r), f)
partition(r, f)partition(begin(r), end(r), f)
stable_partition(r, f)stable_partition(begin(r), end(r), f)
sort(r[, f])sort(begin(r), end(r)[, f])
stable_sort(r[, f])stable_sort(begin(r), end(r)[, f])
partial_sort(r, m[, f])partial_sort(begin(r), m, end(r)[, f])
partial_sort_copy(r1, r2[, f])partial_sort_copy(begin(r1), end(r1), begin(r2), end(r2)[, f])
nth_element(r, n[, f])nth_element(begin(r), n, end(r)[, f])
merge(r1, r2, o[, f])merge(begin(r1), end(r1), begin(r2), end(r2), o[, f])
inplace_merge(r, m[, f])inplace_merge(begin(r), m, end(r)[, f])
set_union(r1, r2, o[, f])set_union(begin(r1), end(r1), begin(r2), end(r2)[, f])
set_intersection(r1, r2, o[, f])set_intersection(begin(r1), end(r1), begin(r2), end(r2)[, f])
set_difference(r1, r2, o[, f])set_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])
set_symmetric_difference(r1, r2, o[, f])set_symmetric_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])
push_heap(r[, f])push_heap(begin(r), end(r)[, f])
pop_heap(r[, f])pop_heap(begin(r), end(r)[, f])
make_heap(r[, f])make_heap(begin(r), end(r)[, f])
sort_heap(r[, f])sort_heap(begin(r), end(r)[, f])
next_permutation(r[, f])next_permutation(begin(r), end(r)[, f])
prev_permutation(r[, f])prev_permutation(begin(r), end(r)[, f])
inner_product(r, o, a[, f1, f2])inner_product(begin(r), end(r), o[, f1, f2])
partial_sum(r, o[, f])partial_sum(begin(r), end(r), o[, f])
adjacent_difference(r, o[, f])adjacent_difference(begin(r), end(r), o[, f])
+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/basics.html b/phoenix/doc/html/phoenix/basics.html new file mode 100644 index 000000000..0191e12f0 --- /dev/null +++ b/phoenix/doc/html/phoenix/basics.html @@ -0,0 +1,243 @@ + + + +Basics + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Basics

+

+ Almost everything is a function in the Phoenix library that can be evaluated + as f(a1, a2, ..., an), + where n is the function's arity, or number of arguments + that the function expects. Operators are also functions. For example, a + b is just a function with arity == 2 (or + binary). a + b is the same as add(a, b), a + b + c is the same as add(add(a, b), c). +

+
+ + +
note Amusingly, functions may even return functions. + We shall see what this means in a short while.
+

+ + Partial Function Application +

+

+ Think of a function as a black box. You pass arguments and it returns something + back. The figure below depicts the typical scenario. +

+

+ fbox +

+

+ A fully evaluated function is one in which all the arguments are given. All + functions in plain C++ are fully evaluated. When you call the sin(x) function, you have to pass a number x. The + function will return a result in return: the sin of x. When you call the add(x, y) function, you have to pass two numbers x and + y. The function will return the sum of the two numbers. The figure below is + a fully evaluated add function. +

+

+ adder +

+

+ A partially applied function, on the other hand, is one in which not all the + arguments are supplied. If we are able to partially apply the function add above, we may pass only the first argument. + In doing so, the function does not have all the required information it needs + to perform its task to compute and return a result. What it returns instead + is another function, a lambda function --another black box. Unlike the original + add function which has an arity + of 2, the resulting lambda function has an arity of 1. Why? because we already + supplied part of the input: 2 +

+

+ add2 +

+

+ Now, when we shove in a number into our lambda function, it will return 2 plus + whatever we pass in. The lambda function essentially remembers 1) the original + function, add, and 2) the partial + input, 2. The figure below illustrates a case where we pass 3 to our lambda + function, which then returns 5: +

+

+ add2_call +

+

+ Obviously, partially applying the add + function, as we see above, cannot be done directly in C++ where we are expected + to supply all the arguments that a function expects. That's where the Phoenix + library comes in. The library provides the facilities to do partial function + application. +

+

+ + STL and higher order functions +

+

+ So, what's all the fuss? What makes partial function application so useful? + Recall our original example in the previous + section: +

+
+find_if(c.begin(), c.end(), arg1 % 2 == 1)
+
+

+ The expression arg1 % 2 == 1 evaluates to a lambda function. arg1 is a placeholder for an argument to + be supplied later. Hence, since there's only one unsupplied argument, the lambda + function has an arity 1. It just so happens that find_if + supplies the unsupplied argument as it loops from c.begin() + to c.end(). +

+
+ + +
note Higher order functions are functions which can take + other functions as arguments, and may also return functions as results. + Higher order functions are functions that are treated like any other + objects and can be used as arguments and return values from functions.
+

+ + Lazy Evaluation +

+

+ In Phoenix, to put it more accurately, function evaluation has two stages: +

+
    +
  1. + Partial application +
  2. +
  3. + Final evaluation +
  4. +
+

+ The first stage is handled by a set of generator functions. These are your + front ends (in the client's perspective). These generators create (through + partial function application), higher order functions that can be passed on + just like any other function pointer or function object. The second stage, + the actual function call, can be invoked or executed anytime in the future, + or not at all; hence "lazy". +

+

+ If we look more closely, the first step involves partial function application: +

+
+arg1 % 2 == 1
+
+

+ The second step is the actual function invocation (done inside the find_if function. These are the back-ends + (often, the final invocation is never actually seen by the client). In our + example, the find_if, if we + take a look inside, we'll see something like: +

+
+template <class InputIterator, class Predicate>
+InputIterator
+find_if(InputIterator first, InputIterator last, Predicate pred)
+{
+    while (first != last && !pred(*first))  // <--- The lambda function is called here
+        ++first;                            //      passing in *first
+    return first;
+}
+
+

+ Again, typically, we, as clients, see only the first step. However, in this + document and in the examples and tests provided, don't be surprised to see + the first and second steps juxtaposed in order to illustrate the complete semantics + of Phoenix expressions. Examples: +

+
+int x = 1;
+int y = 2;
+
+cout << (arg1 % 2 == 1)(x) << endl; // prints 1 or true
+cout << (arg1 % 2 == 1)(y) << endl; // prints 0 or false
+
+

+ + Forwarding Function Problem +

+

+ Usually, we, as clients, write the call-back functions while libraries (such + as STL) provide the callee (e.g. find_if). + In case the role is reversed, e.g. if you have to write an STL algorithm that + takes in a predicate, or develop a GUI library that accepts event handlers, + you have to be aware of a little known problem in C++ called the "Forwarding + Function Problem". +

+

+ Look again at the code above: +

+
+(arg1 % 2 == 1)(x)
+
+

+ Notice that, in the second-stage (the final evaluation), we used a variable + x. Be aware that the second + stage cannot accept non-const temporaries and literal constants. Hence, this + will fail: +

+
+(arg1 % 2 == 1)(123) // Error!
+
+

+ Disallowing non-const rvalues partially solves the "Forwarding + Function Problem" but prohibits code like above. +

+

+ + Polymorphic Functions +

+

+ Unless otherwise noted, Phoenix generated functions are fully polymorphic. + For instance, the add example + above can apply to integers, floating points, user defined complex numbers + or even strings. Example: +

+
+std::string h("Hello");
+char const* w = " World";
+std::string r = add(arg1, arg2)(h, w);
+
+

+ evaluates to std::string("Hello + World"). The observant + reader might notice that this function call in fact takes in heterogeneous + arguments where arg1 is of + type std::string and arg2 + is of type char const*. add + still works because the C++ standard library allows the expression a + b where a + is a std::string and b + is a char const*. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/composite.html b/phoenix/doc/html/phoenix/composite.html new file mode 100644 index 000000000..5312b9052 --- /dev/null +++ b/phoenix/doc/html/phoenix/composite.html @@ -0,0 +1,1534 @@ + + + +Composite + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Composite

+ +

+ Actors may be combined in a multitude of ways to form composites. Composites + are actors that are composed of zero or more actors. Composition is hierarchical. + An element of the composite can be a primitive or again another composite. + The flexibility to arbitrarily compose hierarchical structures allows us to + form intricate constructions that model complex functions, statements and expressions. +

+

+ A composite is-a tuple of 0..N actors. N is the predefined maximum actors a + composite can take. +

+
+ + +
note You can set PHOENIX_COMPOSITE_LIMIT, + the predefined maximum actors a composite can take. By default, PHOENIX_COMPOSITE_LIMIT is set to + PHOENIX_LIMIT (See Actors).
+

+ As mentioned, each of the actors A0..AN can, in turn, be another composite, + since a composite is itself an actor. This makes the composite a recursive + structure. The actual evaluation is handled by a composite specific eval policy. +

+
+

+Function

+
+#include <boost/spirit/phoenix/function/function.hpp>
+
+

+ The function class template + provides a mechanism for implementing lazily evaluated functions. Syntactically, + a lazy function looks like an ordinary C/C++ function. The function call + looks familiar and feels the same as ordinary C++ functions. However, unlike + ordinary functions, the actual function execution is deferred. +

+

+ Unlike ordinary function pointers or functor objects that need to be explicitly + bound through the bind function (see Bind), + the argument types of these functions are automatically lazily bound. +

+

+ In order to create a lazy function, we need to implement a model of the FunctionEval + concept. For a function that takes N + arguments, a model of FunctionEval must provide: +

+
    +
  • + An operator() + that implements that takes N + arguments, and implements the function logic. +
  • +
  • + A nested metafunction result<A1, ... AN> + that takes the types of the N + arguments to the function and returns the result type of the function. + (There is a special case for function objects that accept no arguments. + Such nullary functors are only required to define a typedef result_type that reflects the return + type of its operator()). +
  • +
+

+ For example, the following type implements the FunctionEval concept, in order + to provide a lazy factorial function: +

+
+struct factorial_impl
+{
+    template <typename Arg>
+    struct result
+    {
+        typedef Arg type;
+    };
+
+    template <typename Arg>
+    Arg operator()(Arg n) const
+    {
+        return (n <= 0) ? 1 : n * this->operator()(n-1);
+    }
+};
+
+

+ (See factorial.cpp) +

+

+ Having implemented the factorial_impl + type, we can declare and instantiate a lazy factorial + function this way: +

+
+function<factorial_impl> factorial;
+
+

+ Invoking a lazy function such as factorial + does not immediately execute the function object factorial_impl. + Instead, an actor object is created + and returned to the caller. Example: +

+
+factorial(arg1)
+
+

+ does nothing more than return an actor. A second function call will invoke + the actual factorial function. Example: +

+
+int i = 4;
+cout << factorial(arg1)(i);
+
+

+ will print out "24". +

+

+ Take note that in certain cases (e.g. for function objects with state), an + instance of the model of FunctionEval may be passed on to the constructor. + Example: +

+
+function<factorial_impl> factorial(ftor);
+
+

+ where ftor is an instance of factorial_impl (this is not necessary in this + case as factorial_impl does + not require any state). +

+
+ + +
alert Take care though when using function objects with + state because they are often copied repeatedly, and state may change + in one of the copies, rather than the original.
+
+
+

+Operator

+

+ This facility provides a mechanism for lazily evaluating operators. Syntactically, + a lazy operator looks and feels like an ordinary C/C++ infix, prefix or postfix + operator. The operator application looks the same. However, unlike ordinary + operators, the actual operator execution is deferred. Samples: +

+
+arg1 + arg2
+1 + arg1 * arg2
+1 / -arg1
+arg1 < 150
+
+

+ We have seen the lazy operators in action (see Quick + Start). Let's go back and examine them a little bit further: +

+
+find_if(c.begin(), c.end(), arg1 % 2 == 1)
+
+

+ Through operator overloading, the expression arg1 % 2 == 1 actually + generates an actor. This actor object is passed on to STL's find_if function. From the viewpoint of + STL, the composite is simply a function object expecting a single argument + of the containers value_type. For each element in c, + the element is passed on as an argument arg1 + to the actor (function object). The actor checks if this is an odd value + based on the expression arg1 % 2 == 1 where + arg1 is replaced by the container's element. +

+

+ Like lazy functions (see function), + lazy operators are not immediately executed when invoked. Instead, an actor + (see actors) object is created and + returned to the caller. Example: +

+
+(arg1 + arg2) * arg3
+
+

+ does nothing more than return an actor. A second function call will evaluate + the actual operators. Example: +

+
+int i = 4, j = 5, k = 6;
+cout << ((arg1 + arg2) * arg3)(i, j, k);
+
+

+ will print out "54". +

+

+ Operator expressions are lazily evaluated following four simple rules: +

+
    +
  1. + A binary operator, except ->* + will be lazily evaluated when at least one of its + operands is an actor object (see actors). +
  2. +
  3. + Unary operators are lazily evaluted if their argument is an actor object. +
  4. +
  5. + Operator ->* is lazily + evaluted if the left hand argument is an actor object. +
  6. +
  7. + The result of a lazy operator is an actor object that can in turn allow + the applications of rules 1 and 2. +
  8. +
+

+ For example, to check the following expression is lazily evaluated: +

+
+-(arg1 + 3 + 6)
+
+
    +
  1. + Following rule 1, arg1 + 3 + is lazily evaluated since arg1 + is an actor (see primitives). +
  2. +
  3. + The result of this arg1 + 3 + expression is an actor object, following rule 4. +
  4. +
  5. + Continuing, arg1 + 3 + 6 + is again lazily evaluated. Rule 2. +
  6. +
  7. + By rule 4 again, the result of arg1 + 3 + 6 + is an actor object. +
  8. +
  9. + As arg1 + 3 + 6 is an actor, -(arg1 + 3 + 6) is lazily + evaluated. Rule 2. +
  10. +
+

+ Lazy-operator application is highly contagious. In most cases, a single + argN actor infects all its + immediate neighbors within a group (first level or parenthesized expression). +

+

+ Note that at least one operand of any operator must be a valid actor for + lazy evaluation to take effect. To force lazy evaluation of an ordinary expression, + we can use ref(x), val(x) or cref(x) + to transform an operand into a valid actor object (see primitives. + For example: +

+
+1 << 3;      // Immediately evaluated
+val(1) << 3; // Lazily evaluated
+
+

+ + Supported operators +

+

+ + Unary operators +

+
+prefix:   ~, !, -, +, ++, --, & (reference), * (dereference)
+postfix:  ++, --
+
+

+ + Binary operators +

+
+=, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
++, -, *, /, %, &, |, ^, <<, >>
+==, !=, <, >, <=, >=
+&&, ||, ->*
+
+

+ + Ternary operator +

+
+if_else(c, a, b)
+
+

+ The ternary operator deserves special mention. Since C++ does not allow us + to overload the conditional expression: c ? a : b, + the if_else pseudo function is provided for this purpose. The behavior is + identical, albeit in a lazy manner. +

+

+ + Member pointer operator +

+
+a->*member_object_pointer
+a->*member_function_pointer
+
+

+ The left hand side of the member pointer operator must be an actor returning + a pointer type. The right hand side of the member pointer operator may be + either a pointer to member object or pointer to member function. +

+

+ If the right hand side is a member object pointer, the result is an actor + which, when evaluated, returns a reference to that member. For example: +

+
+struct A
+{
+    int member;
+};
+
+A* a = new A;
+...
+
+(arg1->*&A::member)(a); // returns member a->member
+
+

+ If the right hand side is a member function pointer, the result is an actor + which, when invoked, calls the specified member function. For example: +

+
+struct A
+{
+    int func(int);
+};
+
+A* a = new A;
+int i = 0;
+
+(arg1->*&A::func)(arg2)(a, i); // returns a->func(i)
+
+
+

+ + Include Files +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorsFile
+-, +, ++, + --, +=, + -=, *=, + /=, %=, + *, /, + % +#include <boost/spirit/phoenix/operator/arithmetic.hpp>
+&=, |=, ^=, + <<=, >>=, &, + |, ^, + <<, >> +#include <boost/spirit/phoenix/operator/bitwise.hpp>
+==, !=, <, + <=, >, + >= +#include <boost/spirit/phoenix/operator/comparison.hpp>
+<<, >> +#include <boost/spirit/phoenix/operator/io.hpp>
+!, &&, + || +#include <boost/spirit/phoenix/operator/logical.hpp>
+&x, + *p, + =, [] +#include <boost/spirit/phoenix/operator/self.hpp>
if_else(c, a, b)#include <boost/spirit/phoenix/operator/if_else.hpp>
->*#include <boost/spirit/phoenix/operator/member.hpp>
+
+
+
+

+Statement

+ +

+ Lazy statements... +

+

+ The primitives and composite building blocks presented so far are sufficiently + powerful to construct quite elaborate structures. We have presented lazy- + functions and lazy-operators. How about lazy-statements? First, an appetizer: +

+

+ Print all odd-numbered contents of an STL container using std::for_each + (all_odds.cpp): +

+
+for_each(c.begin(), c.end(),
+    if_(arg1 % 2 == 1)
+    [
+        cout << arg1 << ' '
+    ]
+);
+
+

+ Huh? Is that valid C++? Read on... +

+

+ Yes, it is valid C++. The sample code above is as close as you can get to + the syntax of C++. This stylized C++ syntax differs from actual C++ code. + First, the if has a trailing + underscore. Second, the block uses square brackets instead of the familiar + curly braces {}. +

+
+ + +
note C++ in C++?
+
In as much as Spirit + attempts to mimic EBNF in C++, Phoenix attempts to mimic C++ in C++!!! +
+

+ Here are more examples with annotations. The code almost speaks for itself. +

+
+

+Block Statement

+
+#include <boost/spirit/phoenix/statement/sequence.hpp>
+
+

+ Syntax: +

+
+statement,
+statement,
+....
+statement
+
+

+ Basically, these are comma separated statements. Take note that unlike + the C/C++ semicolon, the comma is a separator put in-between + statements. This is like Pascal's semicolon separator, rather than C/C++'s + semicolon terminator. For example: +

+
+statement,
+statement,
+statement, // ERROR!
+
+

+ Is an error. The last statement should not have a comma. Block statements + can be grouped using the parentheses. Again, the last statement in a group + should not have a trailing comma. +

+
+statement,
+statement,
+(
+    statement,
+    statement
+),
+statement
+
+

+ Outside the square brackets, block statements should be grouped. For example: +

+
+for_each(c.begin(), c.end(),
+    (
+        do_this(arg1),
+        do_that(arg1)
+    )
+);
+
+

+ Wrapping a comma operator chain around a parentheses pair blocks the interpretation + as an argument separator. The reason for the exception for the square bracket + operator is that the operator always takes exactly one argument, so it + "transforms" any attempt at multiple arguments with a comma operator + chain (and spits out an error for zero arguments). +

+
+
+

+if_ Statement

+
+#include <boost/spirit/phoenix/statement/if.hpp>
+
+

+ We have seen the if_ statement. + The syntax is: +

+
+if_(conditional_expression)
+[
+    sequenced_statements
+]
+
+
+
+

+ifelse statement

+
+#include <boost/spirit/phoenix/statement/if.hpp>
+
+

+ The syntax is +

+
+if_(conditional_expression)
+[
+    sequenced_statements
+]
+.else_
+[
+    sequenced_statements
+]
+
+

+ Take note that else has a + leading dot and a trailing underscore: .else_ +

+

+ Example: This code prints out all the elements and appends " > 5", " + == 5" or " < 5" + depending on the element's actual value: +

+
+for_each(c.begin(), c.end(),
+    if_(arg1 > 5)
+    [
+        cout << arg1 << " > 5\n"
+    ]
+    .else_
+    [
+        if_(arg1 == 5)
+        [
+            cout << arg1 << " == 5\n"
+        ]
+        .else_
+        [
+            cout << arg1 << " < 5\n"
+        ]
+    ]
+);
+
+

+ Notice how the if_else_ + statement is nested. +

+
+
+

+switch_ statement

+
+#include <boost/spirit/phoenix/statement/switch.hpp>
+
+

+ The syntax is: +

+
+switch_(integral_expression)
+[
+    case_<integral_value>(sequenced_statements),
+    ...
+    default_<integral_value>(sequenced_statements)
+]
+
+

+ A comma separated list of cases, and an optional default can be provided. + Note unlike a normal switch statement, cases do not fall through. +

+

+ Example: This code prints out "one", + "two" or "other value" depending on the + element's actual value: +

+
+for_each(c.begin(), c.end(),
+    switch_(arg1)
+    [
+        case_<1>(cout << val("one") << '\n'),
+        case_<2>(cout << val("two") << '\n'),
+        default_(cout << val("other value") << '\n')
+    ]
+);
+
+
+
+

+while_ Statement

+
+#include <boost/spirit/phoenix/statement/while.hpp>
+
+

+ The syntax is: +

+
+while_(conditional_expression)
+[
+    sequenced_statements
+]
+
+

+ Example: This code decrements each element until it reaches zero and prints + out the number at each step. A newline terminates the printout of each + value. +

+
+for_each(c.begin(), c.end(),
+    (
+        while_(arg1--)
+        [
+            cout << arg1 << ", "
+        ],
+        cout << val("\n")
+    )
+);
+
+
+
+

+dowhile Statement

+
+#include <boost/spirit/phoenix/statement/do_while.hpp>
+
+

+ The syntax is: +

+
+do_
+[
+    sequenced_statements
+]
+.while_(conditional_expression)
+
+

+ Again, take note that while + has a leading dot and a trailing underscore: .while_ +

+

+ Example: This code is almost the same as the previous example above with + a slight twist in logic. +

+
+for_each(c.begin(), c.end(),
+    (
+        do_
+        [
+            cout << arg1 << ", "
+        ]
+        .while_(arg1--),
+        cout << val("\n")
+    )
+);
+
+
+
+

+for_ Statement

+
+#include <boost/spirit/phoenix/statement/for.hpp>
+
+

+ The syntax is: +

+
+for_(init_statement, conditional_expression, step_statement)
+[
+    sequenced_statements
+]
+
+

+ It is again very similar to the C++ for statement. Take note that the init_statement, + conditional_expression and step_statement are separated by the comma instead + of the semi-colon and each must be present (i.e. for_(,,) is invalid). This is a case where the + nothing actor can be + useful. +

+

+ Example: This code prints each element N times where N is the element's + value. A newline terminates the printout of each value. +

+
+int iii;
+for_each(c.begin(), c.end(),
+    (
+        for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii))
+        [
+            cout << arg1 << ", "
+        ],
+        cout << val("\n")
+    )
+);
+
+

+ As before, all these are lazily evaluated. The result of such statements + are in fact composites that are passed on to STL's for_each function. In + the viewpoint of for_each, + what was passed is just a functor, no more, no less. +

+
+ + +
note Unlike lazy functions and lazy operators, lazy + statements always return void.
+
+
+

+try_ catch_ Statement

+
+#include <boost/spirit/phoenix/statement/try_catch.hpp>
+
+

+ The syntax is: +

+
+try_
+[
+    sequenced_statements
+]
+.catch_<exception_type>()
+[
+    sequenced_statements
+]
+...
+.catch_all
+[
+    sequenced_statement
+]
+
+

+ Note the usual underscore after try and catch, and the extra parentheses + required after the catch. +

+

+ Example: The following code calls the (lazy) function f + for each element, and prints messages about different exception types it + catches. +

+
+try_
+[
+    f(arg1)
+]
+.catch_<runtime_error>()
+[
+    cout << val("caught runtime error or derived\n")
+]
+.catch_<exception>()
+[
+    cout << val("caught exception or derived\n")
+]
+.catch_all
+[
+    cout << val("caught some other type of exception\n")
+]
+
+
+
+

+throw_

+
+#include <boost/spirit/phoenix/statement/throw.hpp>
+
+

+ As a natural companion to the try/catch support, the statement module provides + lazy throwing and rethrowing of exceptions. +

+

+ The syntax to throw an exception is: +

+
+throw_(exception_expression)
+
+

+ The syntax to rethrow an exception is: +

+
+throw_()
+
+

+ Example: This code extends the try/catch example, rethrowing exceptions + derived from runtime_error or exception, and translating other exception + types to runtime_errors. +

+
+try_
+[
+    f(arg1)
+]
+.catch_<runtime_error>()
+[
+    cout << val("caught runtime error or derived\n"),
+    throw_()
+]
+.catch_<exception>()
+[
+    cout << val("caught exception or derived\n"),
+    throw_()
+]
+.catch_all
+[
+    cout << val("caught some other type of exception\n"),
+    throw_(runtime_error("translated exception"))
+]
+
+
+
+
+

+Object

+

+ The Object module deals with object construction, destruction and conversion. + The module provides "lazy" versions of C++'s + object constructor, new, delete, static_cast, + dynamic_cast, const_cast and reinterpret_cast. +

+

+ + Construction +

+

+ Lazy constructors... +

+
+#include <boost/spirit/phoenix/object/construct.hpp>
+
+

+ Lazily construct an object from an arbitrary set of arguments: +

+
+construct<T>(ctor_arg1, ctor_arg2, ..., ctor_argN);
+
+

+ where the given parameters are the parameters to the contructor of the object + of type T (This implies, that type T is expected to have a constructor with + a corresponding set of parameter types.). +

+

+ Example: +

+
+construct<std::string>(arg1, arg2)
+
+

+ Constructs a std::string from arg1 + and arg2. +

+
+ + +
note The maximum number of actual parameters is limited + by the preprocessor constant PHOENIX_COMPOSITE_LIMIT. Note though, + that this limit should not be greater than PHOENIX_LIMIT. By default, + PHOENIX_COMPOSITE_LIMIT + is set to PHOENIX_LIMIT + (See Actors).
+

+ + New +

+

+ Lazy new... +

+
+#include <boost/spirit/phoenix/object/new.hpp>
+
+

+ Lazily construct an object, on the heap, from an arbitrary set of arguments: +

+
+new_<T>(ctor_arg1, ctor_arg2, ..., ctor_argN);
+
+

+ where the given parameters are the parameters to the contructor of the object + of type T (This implies, that type T is expected to have a constructor with + a corresponding set of parameter types.). +

+

+ Example: +

+
+new_<std::string>(arg1, arg2) // note the spelling of new_ (with trailing underscore)
+
+

+ Creates a std::string from arg1 + and arg2 on the heap. +

+
+ + +
note Again, the maximum number of actual parameters + is limited by the preprocessor constant PHOENIX_COMPOSITE_LIMIT. See + the note above.
+

+ + Delete +

+

+ Lazy delete... +

+
+#include <boost/spirit/phoenix/object/delete.hpp>
+
+

+ Lazily delete an object, from the heap: +

+
+delete_(arg);
+
+

+ where arg is assumed to be a pointer to an object. +

+

+ Example: +

+
+delete_<std::string>(arg1) // note the spelling of delete_ (with trailing underscore)
+
+

+ + Casts +

+

+ Lazy casts... +

+
+#include <boost/spirit/phoenix/object/static_cast.hpp>
+#include <boost/spirit/phoenix/object/dynamic_cast.hpp>
+#include <boost/spirit/phoenix/object/const_cast.hpp>
+#include <boost/spirit/phoenix/object/reinterpret_cast.hpp>
+
+

+ The set of lazy C++ cast template functions provide a way of lazily casting + an object of a certain type to another type. The syntax resembles the well + known C++ casts. Take note however that the lazy versions have a trailing + underscore. +

+
+static_cast_<T>(lambda_expression)
+dynamic_cast_<T>(lambda_expression)
+const_cast_<T>(lambda_expression)
+reinterpret_cast_<T>(lambda_expression)
+
+

+ Example: +

+
+static_cast_<Base*>(&arg1)
+
+

+ Static-casts the address of arg1 + to a Base*. +

+
+
+

+Scope

+

+ Up until now, the most basic ingredient is missing: creation of and access + to local variables in the stack. When recursion comes into play, you will + soon realize the need to have true local variables. It may seem that we do + not need this at all since an unnamed lambda function cannot call itself + anyway; at least not directly. With some sort of arrangement, situations + will arise where a lambda function becomes recursive. A typical situation + occurs when we store a lambda function in a Boost.Function, + essentially naming the unnamed lambda. +

+

+ There will also be situations where a lambda function gets passed as an argument + to another function. This is a more common situation. In this case, the lambda + function assumes a new scope; new arguments and possibly new local variables. +

+

+ This section deals with local variables and nested lambda scopes. +

+

+ + Local Variables +

+
+#include <boost/spirit/phoenix/scope/local_variable.hpp>
+
+

+ We use an instance of: +

+
+actor<local_variable<Key> >
+
+

+ to represent a local variable. The local variable acts as an imaginary data-bin + where a local, stack based data will be placed. Key + is an arbitrary type that is used to identify the local variable. Example: +

+
+struct size_key;
+actor<local_variable<size_key> > size;
+
+

+ + Predefined Local Variables +

+

+ There are a few predefined instances of actor<local_variable<Key> > + named _a.._z + that you can already use. To make use of them, simply use the namespace boost::phoenix::local_names: +

+
+using namespace boost::phoenix::local_names;
+
+

+ + let +

+
+#include <boost/spirit/phoenix/scope/let.hpp>
+
+

+ You declare local variables using the syntax: +

+
+let(local-declarations)
+[
+    let-body
+]
+
+

+ let allows 1..N local variable + declarations (where N == PHOENIX_LOCAL_LIMIT). + Each declaration follows the form: +

+
+local-id = lambda-expression
+
+
+ + +
note You can set PHOENIX_LOCAL_LIMIT, + the predefined maximum local variable declarations in a let expression. + By default, PHOENIX_LOCAL_LIMIT + is set to PHOENIX_LIMIT.
+

+ Example: +

+
+let(_a = 123, _b = 456)
+[
+    _a + _b
+]
+
+

+ + Reference Preservation +

+

+ The type of the local variable assumes the type of the lambda- expression. + Type deduction is reference preserving. For example: +

+
+let(_a = arg1, _b = 456)
+
+

+ _a assumes the type of arg1: a reference to an argument, while + _b has type int. +

+

+ Consider this: +

+
+int i = 1;
+
+let(_a = arg1)
+[
+    cout << --_a << ' '
+]
+(i);
+
+cout << i << endl;
+
+

+ the output of above is : 0 0 +

+

+ While with this: +

+
+int i = 1;
+
+let(_a = val(arg1))
+[
+    cout << --_a << ' '
+]
+(i);
+
+cout << i << endl;
+
+

+ the output is : 0 1 +

+

+ Reference preservation is necessary because we need to have L-value access + to outer lambda-scopes (especially the arguments). args + and refs are L-values. vals are R-values. +

+

+ + Visibility +

+

+ The scope and lifetimes of the local variables is limited within the let-body. + let blocks can be nested. + A local variable may hide an outer local variable. For example: +

+
+let(_x = 1, _y = ", World")
+[
+    // _x here is an int: 1
+
+    let(_x = "Hello") // hides the outer _x
+    [
+        cout << _x << _y // prints "Hello, World"
+    ]
+]
+
+

+ The RHS (right hand side lambda-expression) of each local-declaration cannot + refer to any LHS local-id. At this point, the local-ids are not in scope + yet; they will only be in scope in the let-body. The code below is in error: +

+
+let(
+    _a = 1
+  , _b = _a // Error: _a is not in scope yet
+)
+[
+    // _a and _b's scope starts here
+    /*. body .*/
+]
+
+

+ However, if an outer let scope is available, this will be searched. Since + the scope of the RHS of a local-declaration is the outer scope enclosing + the let, the RHS of a local-declaration can refer to a local variable of + an outer scope: +

+
+let(_a = 1)
+[
+    let(
+        _a = 1
+      , _b = _a // Ok. _a refers to the outer _a
+    )
+    [
+        /*. body .*/
+    ]
+]
+
+

+ + lambda +

+
+#include <boost/spirit/phoenix/scope/lambda.hpp>
+
+

+ A lot of times, you'd want to write a lazy function that accepts one or more + functions (higher order functions). STL algorithms come to mind, for example. + Consider a lazy version of stl::for_each: +

+
+struct for_each_impl
+{
+    template <typename C, typename F>
+    struct result
+    {
+        typedef void type;
+    };
+
+    template <typename C, typename F>
+    void operator()(C& c, F f) const
+    {
+        std::for_each(c.begin(), c.end(), f);
+    }
+};
+
+function<for_each_impl> const for_each = for_each_impl();
+
+

+ Notice that the function accepts another function, f + as an argument. The scope of this function, f, + is limited within the operator(). When f + is called inside std::for_each, it exists in a new scope, along + with new arguments and, possibly, local variables. This new scope is not + at all related to the outer scopes beyond the operator(). +

+

+ Simple syntax: +

+
+lambda
+[
+    lambda-body
+]
+
+

+ Like let, local variables + may be declared, allowing 1..N local variable declarations (where N == PHOENIX_LOCAL_LIMIT): +

+
+lambda(local-declarations)
+[
+    lambda-body
+]
+
+

+ The same restrictions apply with regard to scope and visibility. The RHS + (right hand side lambda-expression) of each local-declaration cannot refer + to any LHS local-id. The local-ids are not in scope yet; they will be in + scope only in the lambda-body: +

+
+lambda(
+    _a = 1
+  , _b = _a // Error: _a is not in scope yet
+)
+
+

+ See let + Visibility above for more information. +

+

+ Example: Using our lazy for_each + let's print all the elements in a container: +

+
+for_each(arg1, lambda[cout << arg1])
+
+

+ As far as the arguments are concerned (arg1..argN), the scope in which the + lambda-body exists is totally new. The left arg1 + refers to the argument passed to for_each + (a container). The right arg1 + refers to the argument passed by std::for_each + when we finally get to call operator() in our for_each_impl + above (a container element). +

+

+ Yet, we may wish to get information from outer scopes. While we do not have + access to arguments in outer scopes, what we still have is access to local + variables from outer scopes. We may only be able to pass argument related + information from outer lambda + scopes through the local variables. +

+
+ + +
note This is a crucial difference between let and lambda: + let does not introduce + new arguments; lambda + does.
+

+ Another example: Using our lazy for_each, + and a lazy push_back: +

+
+struct push_back_impl
+{
+    template <typename C, typename T>
+    struct result
+    {
+        typedef void type;
+    };
+
+    template <typename C, typename T>
+    void operator()(C& c, T& x) const
+    {
+        c.push_back(x);
+    }
+};
+
+function<push_back_impl> const push_back = push_back_impl();
+
+

+ write a lambda expression that accepts: +

+
    +
  1. + a 2-dimensional container (e.g. vector<vector<int> >) +
  2. +
  3. + a container element (e.g. int) +
  4. +
+

+ and pushes-back the element to each of the vector<int>. +

+

+ Solution: +

+
+for_each(arg1, 
+    lambda(_a = arg2)
+    [
+        push_back(arg1, _a)
+    ]
+)
+
+

+ Since we do not have access to the arguments of the outer scopes beyond the + lambda-body, we introduce a local variable _a + that captures the second outer argument: arg2. + Hence: _a = arg2. This local variable is visible inside the lambda scope. +

+

+ (See lambda.cpp) +

+
+
+

+Bind

+

+ Binding is the act of tying together a function to some + arguments for deferred (lazy) evaluation. Named Lazy + functions require a bit of typing. Unlike (unnamed) lambda expressions, + we need to write a functor somewhere offline, detached from the call site. + If you wish to transform a plain function, member function or member variable + to a lambda expression, bind + is your friend. +

+
+ + +
note Take note that binders are monomorphic. Rather + than binding functions, the preferred way is to write true generic + and polymorphic lazy-functions. + However, since most of the time we are dealing with adaptation of exisiting + code, binders get the job done faster.
+

+ There is a set of overloaded bind + template functions. Each bind(x) + function generates a suitable binder object, a composite. +

+

+ + Binding Functions +

+
+#include <boost/spirit/phoenix/bind/bind_function.hpp>
+
+

+ Example, given a function foo: +

+
+void foo(int n) 
+{ 
+    std::cout << n << std::endl; 
+}
+
+

+ Here's how the function foo + may be bound: +

+
+bind(&foo, arg1)
+
+

+ This is now a full-fledged composite + that can finally be evaluated by another function call invocation. A second + function call will invoke the actual foo + function. Example: +

+
+int i = 4;
+bind(&foo, arg1)(i);
+
+

+ will print out "4". +

+

+ + Binding Member Functions +

+
+#include <boost/spirit/phoenix/bind/bind_member_function.hpp>
+
+

+ Binding member functions can be done similarly. A bound member function takes + in a pointer or reference to an object as the first argument. For instance, + given: +

+
+struct xyz 
+{ 
+    void foo(int) const; 
+};
+
+

+ xyz's foo + member function can be bound as: +

+
+bind(&xyz::foo, obj, arg1) // obj is an xyz object
+
+

+ Take note that a lazy-member functions expects the first argument to be a + pointer or reference to an object. Both the object (reference or pointer) + and the arguments can be lazily bound. Examples: +

+
+xyz obj;
+bind(&xyz::foo, arg1, arg2)     // arg1.foo(arg2)
+bind(&xyz::foo, obj, arg1)      // obj.foo(arg1)
+bind(&xyz::foo, obj, 100)       // obj.foo(100)
+
+

+ + Binding Member Variables +

+
+#include <boost/spirit/phoenix/bind/bind_member_variable.hpp>
+
+

+ Member variables can also be bound much like member functions. Member variables + are not functions. Yet, like the ref(x) that acts like a nullary function + returning a reference to the data, member variables, when bound, act like + a unary function, taking in a pointer or reference to an object as its argument + and returning a reference to the bound member variable. For instance, given: +

+
+struct xyz 
+{ 
+    int v; 
+};
+
+

+ xyz::v can be bound as: +

+
+bind(&xyz::v, obj) // obj is an xyz object
+
+

+ As noted, just like the bound member function, a bound member variable also + expects the first (and only) argument to be a pointer or reference to an + object. The object (reference or pointer) can be lazily bound. Examples: +

+
+xyz obj;
+bind(&xyz::v, arg1)             // arg1.v 
+bind(&xyz::v, obj)              // obj.v  
+bind(&xyz::v, arg1)(obj) = 4    // obj.v = 4
+
+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/inside_phoenix.html b/phoenix/doc/html/phoenix/inside_phoenix.html new file mode 100644 index 000000000..7a66d7002 --- /dev/null +++ b/phoenix/doc/html/phoenix/inside_phoenix.html @@ -0,0 +1,750 @@ + + + +Inside Phoenix + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Inside Phoenix

+ +

+ This chapter explains in more detail how the library operates. The information + henceforth should not be necessary to those who are interested in just using + the library. However, a microscopic view might prove to be beneficial to moderate + to advanced programmers who wish to extend the library. +

+
+

+Actors In Detail

+

+ + Actor Concept +

+

+ The main concept is the Actor. + Actors are function objects (that can accept 0 to N arguments (where N is + a predefined maximum). +

+
+ + +
note You can set PHOENIX_LIMIT, + the predefined maximum arity an actor can take. By default, PHOENIX_LIMIT is set to 10.
+

+ + actor template class +

+

+ The actor template class + models the Actor concept: +

+
+template <typename Eval>
+struct actor : Eval
+{
+    typedef Eval eval_type;
+
+    actor();
+    actor(Eval const& base);
+
+    template <typename T0>
+    explicit actor(T0 const& _0);
+
+    template <typename T0, typename T1>
+    actor(T0 const& _0, T1 const& _1);
+    
+    // more constructors
+
+    typename apply_actor<eval_type, basic_environment<> >::type
+    operator()() const;
+
+    template <typename T0>
+    typename apply_actor<eval_type, basic_environment<T0> >::type
+    operator()(T0& _0) const;
+
+    template <typename T0, typename T1>
+    typename apply_actor<eval_type, basic_environment<T0, T1> >::type
+    operator()(T0& _0, T1& _1) const;
+
+    // function call operators
+};
+
+
+

+ + Actor Concept Requirements +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpressionResult/Semantics
T::eval_typeThe actor's + Eval type
T()Default + Constructor
T(base)Constructor + from Eval
T(arg0, arg1, ..., argN)Pass through constructors
x(arg0, arg1, ..., argN)Function call operators
+
+

+ + Eval Concept +

+

+ The actor template class + has a single template parameter, Eval, + from which it derives from. While the Actor + concept represents a function, the Eval + concept represents the function body. The requirements for Eval are intentionally kept simple, to + make it easy to write models of the concept. We shall see an example in the + next section. +

+
+

+ + Eval Concept Requirements +

+ ++++ + + + + + + + + + + + + + + +
ExpressionResult/Semantics
return x.eval(env)Evaluates the function + (see Environment below)
T::result<Env>::typeThe return type + of eval (see Environment below)
+
+

+ + Constructors +

+

+ In addition to a default constructor and an constructor from a Eval object, + there are templated (pass through) constructors for 1 to N arguments (N == + PHOENIX_LIMIT). These constructors + simply forward the arguments to the base. +

+
+ + +
note Parametric Base Class + Pattern

Notice that actor derives from its + template argument Eval. This is the inverse of the curiously recurring + template pattern (CRTP). With the CRTP, a class, T, has a Derived template + parameter that is assumed to be its subclass. The "parametric + base class pattern" (PBCP), on the other hand, inverses the inheritance + and makes a class, T, the derived class. Both CRTP and PBCP techniques + have its pros and cons, which is outside the scope of this document. + CRTP should really be renamed "parametric subclass pattern (PSCP), + but again, that's another story.
+

+ + Function Call Operators +

+

+ There are N function call operators for 0 to N arguments (N == PHOENIX_LIMIT). The actor class accepts + the arguments and forwards the arguments to the actor's base Eval for evaluation. +

+
+ + +
note Forwarding Function + Problem

The function call operators cannot + accept non-const temporaries and literal constants. There is a known + issue with current C++ called the "Forwarding + Function Problem". The problem is that given an arbitrary + function F, using current + C++ language rules, one cannot create a forwarding function FF that transparently assumes the + arguments of F. Disallowing + non-const rvalues arguments partially solves the problem but prohibits + code such as f(1, 2, 3);. +
+

+ + Environment +

+

+ On an actor function call, before calling the actor's Eval::eval + for evaluation, the actor creates an environment. + Basically, the environment packages the arguments in a tuple. The Environment is a concept, of which, the + basic_environment template + class is a model of. +

+
+

+ + Environment Concept Requirements +

+ ++++ + + + + + + + + + + + + + + + + + + +
ExpressionResult/Semantics
x.args()The + arguments in a tie (a tuple of references)
T::args_typeThe arguments' + types in an MPL sequence
T::tie_typeThe tie (tuple + of references) type
+
+

+ Schematically: +

+

+ funnel_in +

+

+ Other parts of the library (e.g. the scope module) extends the Environment concept to hold other information + such as local variables, etc. +

+

+ + apply_actor +

+

+ apply_actor is a standard + MPL style metafunction that simply calls the Action's result + nested metafunction: +

+
+template <typename Action, typename Env>
+struct apply_actor
+{
+    typedef typename Action::template result<Env>::type type;
+};
+
+

+ After evaluating the arguments and doing some computation, the eval member function returns something + back to the client. To do this, the forwarding function (the actor's operator()) + needs to know the return type of the eval member function that it is calling. + For this purpose, models of Eval + are required to provide a nested template class: +

+
+template <typename Env>
+struct result;
+
+

+ This nested class provides the result type information returned by the Eval's eval + member function. The nested template class result + should have a typedef type + that reflects the return type of its member function eval. +

+

+ For reference, here's a typical actor::operator() that accepts two arguments: +

+
+template <typename T0, typename T1>
+typename apply_actor<eval_type, basic_environment<T0, T1> >::type
+operator()(T0& _0, T1& _1) const
+{
+    return eval_type::eval(basic_environment<T0, T1>(_0, _1));
+}
+
+

+ + actor_result +

+

+ For reasons of symmetry to the family of actor::operator() there is a special metafunction usable + for actor result type calculation named actor_result. + This metafunction allows us to directly to specify the types of the parameters + to be passed to the actor::operator() function. Here's a typical actor_result that accepts two arguments: +

+
+template <typename Action, typename T0, typename T1>
+struct actor_result
+{
+    typedef basic_environment<T0, T1> env_type;
+    typedef typename Action::template result<env_type>::type type;
+};
+
+
+
+

+Actor Example

+

+ Let us see a very simple prototypical example of an actor. This is not a + toy example. This is actually part of the library. Remember the reference?. +

+

+ First, we have a model of the Eval + concept: the reference: +

+
+template <typename T>
+struct reference
+{
+    template <typename Env>
+    struct result
+    {
+        typedef T& type;
+    };
+
+    reference(T& arg)
+        : ref(arg) {}
+
+    template <typename Env>
+    T& eval(Env const&) const
+    {
+        return ref;
+    }
+
+    T& ref;
+};
+
+

+ Models of Eval are never + created directly and its instances never exist alone. We have to wrap it + inside the actor template + class to be useful. The ref + template function does this for us: +

+
+template <typename T>
+actor<reference<T> > const
+ref(T& v)
+{
+    return reference<T>(v);
+}
+
+

+ The reference template class + conforms to the Eval concept. It has a nested result metafunction that reflects the return + type of its eval member function, + which peforms the actual function. reference<T> + stores a reference to a T. + Its eval member function + simply returns the reference. It does not make use of the environment Env. +

+

+ Pretty simple... +

+
+
+

+Composites In Detail

+

+ We stated before that composites are actors that are composed of zero or + more actors (see Composite). This + is not quite accurate. The definition was sufficient at that point where + we opted to keep things simple and not bury the reader with details which + she might not need anyway. +

+

+ Actually, a composite is a model of the Eval concept (more on this later). + At the same time, it is also composed of 0..N (where N is a predefined maximum) + Eval instances and an eval policy. + The individual Eval instances are stored in a tuple. +

+
+ + +
note In a sense, the original definition of "composite", + more or less, will do just fine because Eval instances never exist + alone and are always wrapped in an actor + template class which inherits from it anyway. The resulting actor IS-AN + Eval.
+
+ + +
note You can set PHOENIX_COMPOSITE_LIMIT, + the predefined maximum Evals + (actors) a composite can take. By default, PHOENIX_COMPOSITE_LIMIT + is set to PHOENIX_LIMIT + (See Actors).
+

+ + composite template class +

+
+template <typename EvalPolicy, typename EvalTuple>
+struct composite : EvalTuple
+{
+    typedef EvalTuple base_type;
+    typedef EvalPolicy eval_policy_type;
+
+    template <typename Env>
+    struct result
+    {
+        typedef implementation-defined type;
+    };
+
+    composite();
+    composite(base_type const& actors);
+
+    template <typename U0>
+    composite(U0 const& _0);
+
+    template <typename U0, typename U1>
+    composite(U0 const& _0, U1 const& _1);
+    
+    // more constructors
+
+    template <typename Env>
+    typename result<Env>::type
+    eval(Env const& env) const;
+};
+
+

+ + EvalTuple +

+

+ EvalTuple, holds all the + Eval instances. The composite template class inherits from + it. In addition to a default constructor and a constructor from an EvalTuple object, there are templated (pass + through) constructors for 1 to N arguments (again, where N == PHOENIX_COMPOSITE_LIMIT). These constructors + simply forward the arguments to the EvalTuple + base class. +

+

+ + EvalPolicy +

+

+ The composite's eval member + function calls its EvalPolicy's + eval member function (a static + member function) passing in the environment + and each of its actors, in parallel. The following diagram illustrates what's + happening: +

+

+ funnel_out +

+
+

+ + EvalPolicy Requirements +

+ ++++ + + + + + + + + + + + + + + +
ExpressionResult/Semantics
x.eval<RT>(env, eval0, eval1, ..., evalN)Evaluate the composite
T::result<Env, Eval0, Eval1, Eval2, ..., EvalN>::typeThe + return type of eval
+
+

+ The EvalPolicy is expected + to have a nested template class result + which has a typedef type + that reflects the return type of its member function eval. + Here's a typical example of the composite's eval member function for a 2-actor + composite: +

+
+template <typename Env>
+typename result<Env>::type
+eval(Env const& env) const
+{
+    typedef typename result<Env>::type return_type;
+    return EvalPolicy::template 
+        eval<return_type>(
+            env
+          , get<0>(*this)   // gets the 0th element from EvalTuple
+          , get<1>(*this)); // gets the 1st element from EvalTuple    
+}
+
+
+
+

+Composing

+ +

+ Composites are never instantiated directly. Front end expression templates + are used to generate the composites. Using expression templates, we implement + a DSEL (Domain Specific Embedded Language) that mimicks native C++. You've + seen this DSEL in action in the preceding sections. It is most evident in + the Statement section. +

+

+ There are some facilities in the library to make composition of composites + easier. We have a set of overloaded compose + functions and an as_composite + metafunction. Together, these helpers make composing a breeze. We'll provide + an example + of a composite later to see why. +

+
+

+compose

+
+compose<EvalPolicy>(arg0, arg1, arg2, ..., argN);
+
+

+ Given an EvalPolicy and some arguments + arg0...argN, returns a + proper composite. The arguments + may or may not be phoenix actors (primitives of composites). If not, the + arguments are converted to actors appropriately. For example: +

+
+compose<X>(3)
+
+

+ converts the argument 3 to + an actor<value<int> >(3). +

+
+
+

+as_composite

+
+as_composite<EvalPolicy, Arg0, Arg1, Arg2, ..., ArgN>::type
+
+

+ This is the metafunction counterpart of compose. + Given an EvalPolicy and some argument types + Arg0...ArgN, returns a + proper composite type. + For example: +

+
+as_composite<X, int>::type
+
+

+ is the composite type of the compose<X>(3) + expression above. +

+
+
+

+Composite Example

+

+ Now, let's examine an example. Again, this is not a toy example. This is + actually part of the library. Remember the while_ lazy statement? Putting + together everything we've learned so far, we will present it here in its + entirety (verbatim): +

+
+struct while_eval
+{
+    template <typename Env, typename Cond, typename Do>
+    struct result
+    {
+        typedef void type;
+    };
+
+    template <typename RT, typename Env, typename Cond, typename Do>
+    static void
+    eval(Env const& env, Cond& cond, Do& do_)
+    {
+        while (cond.eval(env))
+            do_.eval(env);
+    }
+};
+
+template <typename Cond>
+struct while_gen
+{
+    while_gen(Cond const& cond)
+        : cond(cond) {}
+
+    template <typename Do>
+    actor<typename as_composite<while_eval, Cond, Do>::type>
+    operator[](Do const& do_) const
+    {
+        return compose<while_eval>(cond, do_);
+    }
+
+    Cond cond;
+};
+
+template <typename Cond>
+while_gen<Cond>
+while_(Cond const& cond)
+{
+    return while_gen<Cond>(cond);
+}
+
+

+ while_eval is an example + of an EvalPolicy. while_gen + and while_ are the expression + template front ends. Let's break this apart to understand what's happening. + Let's start at the bottom. It's easier that way. +

+

+ When you write: +

+
+while_(cond)
+
+

+ we generate an instance of while_gen<Cond>, where Cond + is the type of cond. cond can be an arbitrarily complex actor + expression. The while_gen + template class has an operator[] accepting another expression. If we write: +

+
+while_(cond)
+[
+    do_
+]
+
+

+ it will generate a proper composite with the type: +

+
+as_composite<while_eval, Cond, Do>::type
+
+

+ where Cond is the type + of cond and Do is the type of do_. + Notice how we are using phoenix's composition + (compose and as_composite) mechanisms here +

+
+template <typename Do>
+actor<typename as_composite<while_eval, Cond, Do>::type>
+operator[](Do const& do_) const
+{
+    return compose<while_eval>(cond, do_);
+}
+
+

+ Finally, the while_eval + does its thing: +

+
+while (cond.eval(env))
+    do_.eval(env);
+
+

+ cond and do_, at this point, are instances of + Eval. cond + and do_ are the Eval elements held by the composite's + EvalTuple. env + is the Environment. +

+
+
+
+

+Extending

+

+ We've shown how it is very easy to extend phoenix by writing new primitives + and composites. The modular design of Phoenix makes it extremely extensible. + We have seen that layer upon layer, the whole library is built on a solid + foundation. There are only a few simple well designed concepts that are laid + out like bricks. Overall, the library is designed to be extended. Everything + above the core layer can in fact be considered just as extensions to the + library. This modular design was inherited from the Spirit + inline parser library. +

+

+ Extension is non-intrusive. And, whenever a component or module is extended, + the new extension automatically becomes a first class citizen and is automatically + recognized by all modules and components in the library. +

+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/intrinsic.html b/phoenix/doc/html/phoenix/intrinsic.html new file mode 100644 index 000000000..3720bb39f --- /dev/null +++ b/phoenix/doc/html/phoenix/intrinsic.html @@ -0,0 +1,320 @@ + + + +Intrinsic + + + + + + + + + + + + + + + +
boost.png (6897 bytes)HomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Intrinsic

+
+#include <boost/spirit/phoenix/intrinsic.hpp>
+
+

+ The intrinsic module predefines a set of lazy functions that work on STL sequences. + These functions provide a mechanism for the lazy evaluation of the public member + functions of the STL containers. The lazy functions are thin wrappers that + simply forward to their respective counterparts in the STL library. +

+

+ Lazy functions are provided for all of the member functions of the following + containers: +

+
    +
  • + deque +
  • +
  • + list +
  • +
  • + map +
  • +
  • + multimap +
  • +
  • + vector +
  • +
+

+ Indeed, should your class have member functions with the same names and signatures + as those listed below, then it will automatically be supported. To summarize, + lazy functions are provided for member functions: +

+
    +
  • + assign +
  • +
  • + at +
  • +
  • + back +
  • +
  • + begin +
  • +
  • + capacity +
  • +
  • + clear +
  • +
  • + empty +
  • +
  • + end +
  • +
  • + erase +
  • +
  • + front +
  • +
  • + get_allocator +
  • +
  • + insert +
  • +
  • + key_comp +
  • +
  • + max_size +
  • +
  • + pop_back +
  • +
  • + pop_front +
  • +
  • + push_back +
  • +
  • + push_front +
  • +
  • + rbegin +
  • +
  • + rend +
  • +
  • + reserve +
  • +
  • + resize +
  • +
  • + size +
  • +
  • + splice +
  • +
  • + value_comp +
  • +
+

+ The lazy functions' names are the same as the corresponding member function. + The difference is that the lazy functions are free functions and therefore + does not use the member "dot" syntax. +

+
+

+Sample usage +

+ ++++ + + + + + + + + + + + + + + + + + + +
"Normal" version"Lazy" version
my_vector.at(5)at(arg1, 5)
my_list.size()size(arg1)
my_vector1.swap(my_vector2)swap(arg1, arg2)
+
+

+ Notice that member functions with names that clash with stl algorithms are + absent. This will be provided in Phoenix's algorithm module. +

+

+ No support is provided here for lazy versions of operator+=, operator[] etc. Such operators are not specific to STL + containers and lazy versions can therefore be found in operators. +

+
+

+Lazy STL Intrinsic Functions +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionSemantics
assign(c, a[, b, c])c.assign(a[, b, c])
at(c, i)c.at(i)
back(c)c.back()
begin(c)c.begin()
capacity(c)c.capacity()
clear(c)c.clear()
empty(c)c.empty()
end(c)c.end()
erase(c, a[, b])c.erase(a[, b])
front(c)c.front()
get_allocator(c)c.get_allocator()
insert(c, a[, b, c])c.insert(a[, b, c])
key_comp(c)c.key_comp()
max_size(c)c.max_size()
pop_back(c)c.pop_back()
pop_front(c)c.pop_front()
push_back(c, d)c.push_back(d)
push_front(c, d)c.push_front(d)
pop_front(c)c.pop_front()
rbegin(c)c.rbegin()
rend(c)c.rend()
reserve(c, n)c.reserve(n)
resize(c, a[, b])c.resize(a[, b])
size(c)c.size()
splice(c, a[, b, c, d])c.splice(a[, b, c, d])
value_comp(c)c.value_comp()
+
+
+ + +
+ + Arguments in brackets denote optional parameters.
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/introduction.html b/phoenix/doc/html/phoenix/introduction.html new file mode 100644 index 000000000..0d3e921cb --- /dev/null +++ b/phoenix/doc/html/phoenix/introduction.html @@ -0,0 +1,66 @@ + + + +Introduction + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Introduction

+

+ banner +

+

+ The Phoenix library enables FP techniques such as higher order functions, + lambda (unnamed functions), currying + (partial function application) and lazy evaluation in C++. The focus is more + on usefulness and practicality than purity, elegance and strict adherence to + FP principles. +

+

+ FP is a programming discipline that is not at all tied to a specific language. + FP as a programming discipline can, in fact, be applied to many programming + languages. In the realm of C++ for instance, we are seeing more FP techniques + being applied. C++ is sufficiently rich to support at least some of the most + important facets of FP. C++ is a multiparadigm programming language. It is + not only procedural. It is not only object oriented. Beneath the core of the + standard C++ library, a closer look into STL gives us a glimpse of FP already + in place. It is obvious that the authors of STL know and practice FP. In the + near future, we shall surely see more FP trickle down into the mainstream. +

+

+ The truth is, most of the FP techniques can coexist quite well with the standard + object oriented and imperative programming paradigms. When we are using STL + algorithms and functors (function objects) for example, we are already doing + FP. Phoenix is an evolutionary next step. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/organization.html b/phoenix/doc/html/phoenix/organization.html new file mode 100644 index 000000000..dd916780e --- /dev/null +++ b/phoenix/doc/html/phoenix/organization.html @@ -0,0 +1,208 @@ + + + +Organization + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Organization

+

+ Care and attention to detail was given, painstakingly, to the design and implementation + of Phoenix. +

+

+ The library is organized in four layers: +

+

+ organization +

+

+ The modules are orthogonal, with no cyclic dependencies. Lower layers do not + depend on higher layers. Modules in a layer do not depend on other modules + in the same layer. This means, for example, that Bind can be completely discarded + if it is not required; or one could perhaps take out Operator and Statement + and just use Function, which may be desireable in a pure FP application. +

+

+ The library has grown from the original Phoenix but still comprises only header + files. There are no object files to link against. +

+

+ + Core +

+

+ The lowest two layers comprise the core. +

+

+ The Actor is the main concept + behind the library. Lazy functions are abstracted as actors. There are only + 2 kinds of actors: +

+
    +
  1. + Primitives +
  2. +
  3. + Composites +
  4. +
+

+ Primitives provide the basic building blocks of functionality within Phoenix. + Composites are used to combine these primitives together to provide more powerful + functionality. +

+

+ Composites are composed of zero or more actors. Each actor in a composite can + again be another composite. +

+
+

+ + Modules +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ModuleDescription
FunctionLazy functions support (e.g. add)
OperatorLazy operators support (e.g. +)
StatementLazy statments (e.g. if_, + while_)
ObjectLazy casts (e.g. static_cast_), + object creation destruction (e.g. new_, + delete_)
ScopeSupport for scopes, local variables and lambda-lambda
BindLazy functions from free functions, member + functions or member variables.
ContainerSet of predefined "lazy" functions + that work on STL containers and sequences (e.g. push_back).
AlgorithmSet of predefined "lazy" versions + of the STL algorithms (e.g. find_if).
+
+

+ Each module is defined in a header file with the same name. For example, the + core module is defined in <boost/spirit/phoenix/core.hpp>. +

+
+

+ + Includes +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ModuleFile
Core#include <boost/spirit/phoenix/core.hpp>
Function#include <boost/spirit/phoenix/function.hpp>
Operator#include <boost/spirit/phoenix/operator.hpp>
Statement#include <boost/spirit/phoenix/statement.hpp>
Object#include <boost/spirit/phoenix/object.hpp>
Scope#include <boost/spirit/phoenix/scope.hpp>
Bind#include <boost/spirit/phoenix/bind.hpp>
Container#include <boost/spirit/phoenix/container.hpp>
Algorithm#include <boost/spirit/phoenix/algorithm.hpp>
+
+
+ + +
tip Finer grained include files are available per feature; + see the succeeding sections.
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/primitives.html b/phoenix/doc/html/phoenix/primitives.html new file mode 100644 index 000000000..aec57b353 --- /dev/null +++ b/phoenix/doc/html/phoenix/primitives.html @@ -0,0 +1,329 @@ + + + +Primitives + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Primitives

+ +

+ Actors are composed to create more complex actors in a tree-like hierarchy. + The primitives are atomic entities that are like the leaves in the tree. Phoenix + is extensible. New primitives can be added anytime. Right out of the box, there + are only a few primitives. This section shall deal with these preset primitives. +

+
+

+Arguments

+
+#include <boost/spirit/phoenix/core/argument.hpp>
+
+

+ We use an instance of: +

+
+actor<argument<N> >
+
+

+ to represent the Nth function argument. The argument placeholder acts as + an imaginary data-bin where a function argument will be placed. +

+

+ + Predefined Arguments +

+

+ There are a few predefined instances of actor<argument<N> > named arg1..argN, and its BLL + counterpart _1.._N. (where N is a predefined maximum). +

+

+ Here are some sample preset definitions of arg1..argN +

+
+actor<argument<0> > const arg1 = argument<0>();
+actor<argument<1> > const arg2 = argument<1>();
+actor<argument<2> > const arg3 = argument<2>();
+
+

+ and its BLL _1.._N + style counterparts: +

+
+actor<argument<0> > const _1 = argument<0>();
+actor<argument<1> > const _2 = argument<1>();
+actor<argument<2> > const _3 = argument<2>();
+
+
+ + +
note You can set PHOENIX_ARG_LIMIT, + the predefined maximum placeholder index. By default, PHOENIX_ARG_LIMIT is set to PHOENIX_LIMIT (See Actors).
+

+ + User Defined Arguments +

+

+ When appropriate, you can define your own argument<N> + names. For example: +

+
+actor<argument<0> > x; // note zero based index
+
+

+ x may now be used as a parameter + to a lazy function: +

+
+add(x, 6)
+
+

+ which is equivalent to: +

+
+add(arg1, 6)
+
+

+ + Evaluating an Argument +

+

+ An argument, when evaluated, selects the Nth argument from the those passed + in by the client. +

+

+ For example: +

+
+char        c = 'A';
+int         i = 123;
+const char* s = "Hello World";
+
+cout << arg1(c) << endl;        //  Get the 1st argument: c
+cout << arg1(i, s) << endl;     //  Get the 1st argument: i
+cout << arg2(i, s) << endl;     //  Get the 2nd argument: s
+
+

+ will print out: +

+
+A
+123
+Hello World
+
+

+ + Extra Arguments +

+

+ In C and C++, a function can have extra arguments that are not at all used + by the function body itself. These extra arguments are simply ignored. +

+

+ Phoenix also allows extra arguments to be passed. For example, recall our + original add function: +

+
+add(arg1, arg2)
+
+

+ We know now that partially applying this function results to a function that + expects 2 arguments. However, the library is a bit more lenient and allows + the caller to supply more arguments than is actually required. Thus, add actually allows 2 or more + arguments. For instance, with: +

+
+add(arg1, arg2)(x, y, z)
+
+

+ the third argument z is ignored. + Taking this further, in-between arguments are also ignored. Example: +

+
+add(arg1, arg5)(a, b, c, d, e)
+
+

+ Here, arguments b, c, and d are ignored. The function add + takes in the first argument (arg1) + and the fifth argument (arg5). +

+
+ + +
note There are a few reasons why enforcing strict arity + is not desireable. A case in point is the callback function. Typical + callback functions provide more information than is actually needed. + Lambda functions are often used as callbacks.
+
+
+

+Values

+
+#include <boost/spirit/phoenix/core/value.hpp>
+
+

+ Whenever we see a constant in a partially applied function, an +

+
+actor<value<T> > 
+
+

+ (where T is the type of the constant) is automatically created for us. For + instance: +

+
+add(arg1, 6)
+
+

+ Passing a second argument, 6, + an actor<value<int> > is implicitly created behind the scenes. + This is also equivalent to: +

+
+add(arg1, val(6))
+
+

+ val(x) generates + an actor<value<T> > where T + is the type of x. In most + cases, there's no need to explicitly use val, + but, as we'll see later on, there are situations where this is unavoidable. +

+

+ + Evaluating a Value +

+

+ Like arguments, values are also actors. As such, values can be evaluated. + Invoking a value gives the value's identity. Example: +

+
+cout << val(3)() << val("Hello World")();
+
+

+ prints out "3 Hello World". +

+
+
+

+References

+
+#include <boost/spirit/phoenix/core/reference.hpp>
+
+

+ Values are immutable constants. Attempting to modify a value will result + in a compile time error. When we want the function to modify the parameter, + we use a reference instead. For instance, imagine a lazy function add_assign: +

+
+void add_assign(T& x, T y) { x += y; } // pseudo code
+
+

+ Here, we want the first function argument, x, to be mutable. Obviously, we + cannot write: +

+
+add_assign(1, 2) // error first argument is immutable
+
+

+ In C++, we can pass in a reference to a variable as the first argument in + our example above. Yet, by default, the library forces arguments passed to + partially applied functions functions to be immutable values (see Values). + To achieve our intent, we use: +

+
+actor<reference<T> >
+
+

+ This is similar to actor<value<T> > above but instead holds a reference to + a variable. +

+

+ We normally don't instantiate actor<reference<T> > objects directly. Instead we use ref. For example (where i + is an int variable): +

+
+add_assign(ref(i), 2)
+
+

+ + Evaluating a Reference +

+

+ References are actors. Hence, references can be evaluated. Such invocation + gives the references's identity. Example: +

+
+int i = 3;
+char const* s = "Hello World";
+cout << ref(i)() << ref(s)();
+
+

+ prints out "3 Hello World" +

+
+
+

+Constant References

+
+#include <boost/spirit/phoenix/core/reference.hpp>
+
+

+ Another free function cref(cv) + may also be used. cref(cv) creates + an actor<reference<T const&> > + object. This is similar to actor<value<T> > but when the data to be passed as argument + to a function is heavy and expensive to copy by value, the cref(cv) offers a lighter alternative. +

+
+
+

+Nothing

+
+#include <boost/spirit/phoenix/core/nothing.hpp>
+
+

+ Finally, the actor<null_actor> + does nothing; (a "bum", if you will :-). There's a sole actor<null_actor> + instance named "nothing". This actor is actually useful in situations + where we don't want to do anything. (See for_ + Statement for example). +

+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/references.html b/phoenix/doc/html/phoenix/references.html new file mode 100644 index 000000000..0feb9eeaf --- /dev/null +++ b/phoenix/doc/html/phoenix/references.html @@ -0,0 +1,86 @@ + + + +References + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHome +
+
+

+References

+
    +
  1. + Why Functional Programming Matters, John Hughes, 1989. Available online at + http://www.math.chalmers.se/~rjmh/Papers/whyfp.html. +
  2. +
  3. + Boost.Lambda library, Jaakko Jarvi, 1999-2004 Jaakko Jarvi, Gary Powell. + Available online at http://www.boost.org/libs/lambda/. +
  4. +
  5. + Functional Programming in C++ using the FC++ Library: a short article introducing + FC++, Brian McNamara and Yannis Smaragdakis, August 2003. Available online + at http://www.cc.gatech.edu/~yannis/fc++/. +
  6. +
  7. + Side-effects and partial function application in C++, Jaakko Jarvi and Gary + Powell, 2001. Available online at http://osl.iu.edu/~jajarvi/publications/papers/mpool01.pdf. +
  8. +
  9. + Spirit Version 1.8.1, Joel de Guzman, Nov 2004. Available online at http://www.boost.org/libs/spirit/. +
  10. +
  11. + The Boost MPL Library, Aleksey Gurtovoy and David Abrahams, 2002-2004. Available + online at http://www.boost.org/libs/mpl/. +
  12. +
  13. + Generic Programming Redesign of Patterns, Proceedings of the 5th European + Conference on Pattern Languages of Programs, (EuroPLoP'2000) Irsee, Germany, + July 2000. Available online at http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip. +
  14. +
  15. + A Gentle Introduction to Haskell, Paul Hudak, John Peterson and Joseph Fasel, + 1999. Available online at http://www.haskell.org/tutorial/. +
  16. +
  17. + Large scale software design, John Lackos, ISBN 0201633620, Addison-Wesley, + July 1996. +
  18. +
  19. + Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma, + Richard Helm, Ralph Jhonson, and John Vlissides, Addison-Wesley, 1995. +
  20. +
  21. + The Forwarding Problem: Arguments Peter Dimov, Howard E. Hinnant, Dave Abrahams, + September 09, 2002. Available online: Forwarding + Function Problem. +
  22. +
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHome +
+ + diff --git a/phoenix/doc/html/phoenix/starter_kit.html b/phoenix/doc/html/phoenix/starter_kit.html new file mode 100644 index 000000000..0b3cb662f --- /dev/null +++ b/phoenix/doc/html/phoenix/starter_kit.html @@ -0,0 +1,503 @@ + + + +Starter Kit + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Starter Kit

+ +

+ Most "quick starts" only get you a few blocks from where you are. + From there, you are on your own. Yet, typically, you'd want to get to the next + city. This starter kit shall be as minimal as possible, yet packed as much + power as possible. +

+

+ So you are busy and always on the go. You do not wish to spend a lot of time + studying the library. You wish to be spared the details for later when you + need it. For now, all you need to do is to get up to speed as quickly as possible + and start using the library. If this is the case, this is the right place to + start. +

+

+ This chapter is by no means a thorough discourse of the library. For more information + on Phoenix, please take some time to read the rest of the User's Guide. Yet, + if you just want to use the library quickly, now, this chapter will probably + suffice. Rather than taking you to the details of the library, we shall try + to provide you with annotated exemplars instead. Hopefully, this will get you + into high gear quickly. +

+

+ + Functors everywhere +

+

+ Phoenix is built on function objects (functors). The functor is the main building + block. We compose functors to build more complex functors... to build more + complex functors... and so on. Almost everything is a functor. +

+
+ + +
note Functors are so ubiquitous in Phoenix that, in the + manual, the words "functor" and "function" + are used interchangeably.
+
+

+Values

+

+ Values are functions! Examples: +

+
+val(3)
+val("Hello, World")
+
+

+ The first evaluates to a nullary function (a function taking no arguments) + that returns an int, 3. The second evaluates to a nullary function + that returns a char const(&)[13], "Hello, + World". +

+

+ + Lazy Evaluation +

+

+ Confused? val(3) is a unary + function, you say? Yes it is. However, read carefully: "evaluates + to a nullary function". val(3) + evaluates to (returns) a nullary function. Aha! val(3) + returns a function! So, since val(3) + returns a function, you can invoke it. Example: +

+
+cout << val(3)() << endl;
+
+

+ (See values.cpp) +

+
+ + +
tip Learn more about values here. +
+

+ The second function call (the one with no arguments) calls the nullary function + which then returns 3. The need + for a second function call is the reason why the function is said to be + Lazily Evaluated. The + first call doesn't do anything. You need a second call to finally evaluate + the thing. The first call lazily evaluates the function; i.e. doesn't do + anything and defers the evaluation for later. +

+

+ + Callbacks +

+

+ It may not be immediately apparent how lazy evaluation can be useful by just + looking at the example above. Putting the first and second function call + in a single line is really not very useful. However, thinking of val(3) as a callback function (and in most cases + they are actually used that way), will make it clear. Example: +

+
+template <typename F>
+void print(F f)
+{
+    cout << f() << endl;
+}
+
+int
+main()
+{
+    print(val(3));
+    print(val("Hello World"));
+    return 0;
+}
+
+

+ (See callback.cpp) +

+
+
+

+References

+

+ References are functions. They hold a reference to a value stored somehere. + For example, given: +

+
+int i = 3;
+char const* s = "Hello World";
+
+

+ we create references to + i and s + this way: +

+
+ref(i)
+ref(s)
+
+

+ Like val, the expressions + above evaluates to a nullary function; the first one returning an int&, + and the second one returning a char const*&. +

+

+ (See references.cpp) +

+
+ + +
tip Learn more about references here. +
+
+
+

+Arguments

+

+ Arguments are also functions? You bet! +

+

+ Until now, we have been dealing with expressions returning a nullary function. + Arguments, on the other hand, evaluate to an N-ary function. An argument + represents the Nth argument. There are a few predefined arguments arg1, arg2, + arg3, arg4 and so on (and it's BLL + counterparts: _1, _2, _3, _4 and so on). Examples: +

+
+arg1 // one-or-more argument function that returns its first argument
+arg2 // two-or-more argument function that returns its second argument
+arg3 // three-or-more argument function that returns its third argument
+
+

+ argN returns the Nth argument. + Examples: +

+
+int i = 3;
+char const* s = "Hello World";
+cout << arg1(i) << endl;        // prints 3
+cout << arg2(i, s) << endl;     // prints "Hello World"
+
+

+ (See arguments.cpp) +

+
+ + +
tip Learn more about arguments here. +
+
+
+

+Composites

+

+ What we have seen so far, are what are called primitives. + You can think of primitives (such as values, references and arguments) as + atoms. +

+

+ Things start to get interesting when we start composing + primitives to form composites. The composites + can, in turn, be composed to form even more complex composites. +

+
+
+

+Lazy Operators

+

+ You can use the usual set of operators to form composites. Examples: +

+
+arg1 * arg1
+ref(x) = arg1 + ref(z)
+arg1 = arg2 + (3 * arg3)
+ref(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is a valid index
+
+

+ Note the expression: 3 * arg3. + This expression is actually a short-hand equivalent to: val(3) * arg3. + In most cases, like above, you can get away with it. But in some cases, you + will have to explicitly wrap your values in val. + Rules of thumb: +

+
    +
  • + In a binary expression (e.g. 3 * arg3), + at least one of the operands must be a phoenix primitive or composite. +
  • +
  • + In a unary expression (e.g. arg1++), the single operand must be a phoenix + primitive or composite. +
  • +
+

+ If these basic rules are not followed, the result is either in error, or + is immediately evaluated. Some examples: +

+
+ref(x) = 123    // lazy
+x = 123         // immediate
+
+ref(x)[0]       // lazy
+x[0]            // immediate
+
+ref(x)[ref(i)]  // lazy
+ref(x)[i]       // lazy (equivalent to ref(x)[val(i)])
+x[ref(i)]       // illegal (x is not a phoenix primitive or composite)
+ref(x[ref(i)])  // illegal (x is not a phoenix primitive or composite)
+
+
+ + +
tip Learn more about operators here. +
+

+ + First Practical Example +

+

+ We've covered enough ground to present a real world example. We want to find + the first odd number in an STL container. Normally we use a functor (function + object) or a function pointer and pass that in to STL's find_if + generic function: +

+

+ Write a function: +

+
+bool
+is_odd(int arg1)
+{
+    return arg1 % 2 == 1;
+}
+
+

+ Pass a pointer to the function to STL's find_if + algorithm: +

+
+find_if(c.begin(), c.end(), &is_odd)
+
+

+ Using Phoenix, the same can be achieved directly with a one-liner: +

+
+find_if(c.begin(), c.end(), arg1 % 2 == 1)
+
+

+ The expression arg1 % 2 == 1 automagically + creates a functor with the expected behavior. In FP, this unnamed function + is called a lambda function. Unlike the function pointer version, which is + monomorphic (expects and works only with a fixed type int argument), the + Phoenix version is fully polymorphic and works with any container (of ints, + of longs, of bignum, etc.) as long as its elements can handle the arg1 % 2 == 1 expression. +

+

+ (See find_if.cpp) +

+
+ + +
tip ...That's it, we're done. + Well if you wish to know a little bit more, read on...
+
+
+

+Lazy Statements

+

+ Lazy statements? Sure. There are lazy versions of the C++ statements we all + know and love. For example: +

+
+if_(arg1 > 5)
+    cout << arg1
+
+

+ Say, for example, we wish to print all the elements that are greater than + 5 (separated by a comma) in a vector. Here's how we write it: +

+
+for_each(v.begin(), v.end(),
+    if_(arg1 > 5)
+    [
+        cout << arg1 << ", "
+    ]
+);
+
+

+ (See if.cpp) +

+
+ + +
tip Learn more about statements here. +
+
+
+

+Construct, New, Delete, Casts

+

+ You'll probably want to work with objects. There are lazy versions of constructor + calls, new, delete + and the suite of C++ casts. Examples: +

+
+construct<std::string>(arg1, arg2)  // constructs a std::string from arg1, arg2
+new_<std::string>(arg1, arg2)       // makes a new std::string from arg1, arg2
+delete_(arg1)                       // deletes arg1 (assumed to be a pointer)
+static_cast_<int*>(arg1)            // static_cast's arg1 to an int*
+
+
+ + +
note Take note that, by convention, names that conflict + with C++ reserved words are appended with a single trailing underscore + '_' +
+
+ + +
tip Learn more about this here. +
+
+
+

+Lazy Functions

+

+ As you write more lambda functions, you'll notice certain patterns that you + wish to refactor as reusable functions. When you reach that point, you'll + wish that ordinary functions can co-exist with phoenix functions. Unfortunately, + the immediate nature of plain C++ functions make them + incompatible. +

+

+ Lazy functions are your friends. The library provides a facility to make + lazy functions. The code below is a rewrite of the is_odd + function using the facility: +

+
+struct is_odd_impl
+{
+    template <typename Arg>
+    struct result 
+    { 
+        typedef bool type; 
+    };
+
+    template <typename Arg>
+    bool operator()(Arg arg1) const
+    { 
+        return arg1 % 2 == 1; 
+    }
+};
+
+function<is_odd_impl> is_odd;
+
+

+ + Things to note: +

+
    +
  • +result is a nested metafunction + that reflects the return type of the function (in this case, bool). This + makes the function fully polymorphic: It can work with arbitrary Arg types. +
  • +
  • + There are as many Args in the result + metafunction as in the actual operator(). +
  • +
  • +is_odd_impl implements + the function. +
  • +
  • +is_odd, an instance of + function<is_odd_impl>, + is the lazy function. +
  • +
+

+ Now, is_odd is a truly lazy + function that we can use in conjunction with the rest of phoenix. Example: +

+
+find_if(c.begin(), c.end(), is_odd(arg1));
+
+

+ (See function.cpp) +

+

+ + Predefined Lazy Functions +

+

+ The library is chock full of STL savvy, predefined lazy functions covering + the whole of the STL containers, iterators and algorithms. For example, there + are lazy versions of container related operations such as assign, at, back, + begin, pop_back, pop_front, push_back, push_front, etc. (See Container). +

+
+
+

+More

+

+ As mentioned earlier, this chapter is not a thorough discourse of the library. + It is meant only to cover enough ground to get you into high gear as quickly + as possible. Some advanced stuff is not discussed here (e.g. Scopes); + nor are features that provide alternative (short-hand) ways to do the same + things (e.g. Bind vs. Lazy + Functions). +

+
+ + +
tip ...If you still wish to + learn more, the read on... +
+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/wrap_up.html b/phoenix/doc/html/phoenix/wrap_up.html new file mode 100644 index 000000000..43050225d --- /dev/null +++ b/phoenix/doc/html/phoenix/wrap_up.html @@ -0,0 +1,72 @@ + + + +Wrap Up + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Wrap Up

+

+ Sooner or later more FP techniques become standard practice as people find + the true value of this programming discipline outside the academe and into + the mainstream. In as much as structured programming of the 70s and object + oriented programming in the 80s and generic programming in the 90s shaped our + thoughts towards a more robust sense of software engineering, FP will certainly + be a paradigm that will catapult us towards more powerful software design and + engineering onward into the new millenium. +

+

+ Let me quote Doug Gregor of Boost.org. About functional style programming libraries: +

+

+ They're gaining acceptance, but are somewhat stunted by the ubiquitousness + of broken compilers. The C++ community is moving deeper into the so-called + "STL- style" programming paradigm, which brings many aspects of + functional programming into the fold. Look at, for instance, the Spirit parser + to see how such function objects can be used to build Yacc-like grammars + with semantic actions that can build abstract syntax trees on the fly. This + type of functional composition is gaining momentum. +

+

+ Indeed. Phoenix is another attempt to introduce more FP techniques into the + mainstream. Not only is it a tool that will make life easier for the programmer. + In its own right, the actual design of the library itself is a model of true + C++ FP in action. The library is designed and structured in a strict but clear + and well mannered FP sense. By all means, use the library as a tool. But for + those who want to learn more about FP in C++, don't stop there, I invite you + to take a closer look at the design of the library itself. +

+

+ So there you have it. Have fun! See you in the FP world. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/users_manual.qbk b/phoenix/doc/users_manual.qbk new file mode 100644 index 000000000..64e861635 --- /dev/null +++ b/phoenix/doc/users_manual.qbk @@ -0,0 +1,2769 @@ +[library Phoenix + [quickbook 1.3] + [version 2.0] + [authors [de Guzman, Joel], [Marsden, Dan]] + [copyright 2002 2003 2004 2005 Joel de Guzman, Dan Marsden] + [category string-text] + [purpose Lambda Expressions in C++] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + +[/ September 2002] +[/ September 2004] +[/ September 2005] + +[/ Some links] + +[def __note__ [$images/note.png]] +[def __alert__ [$images/alert.png]] +[def __tip__ [$images/tip.png]] + +[def __spirit__ [@http://spirit.sourceforge.net Spirit]] +[def __haskell__ [@http://www.haskell.org Haskell]] +[def __mpl__ [@http://www.boost.org/libs/mpl/index.html MPL]] +[def __bll__ [@http://www.boost.org/libs/lambda/doc/index.html BLL]] +[def __fcpp__ [@http://www.cc.gatech.edu/~yannis/fc++/ FC++]] +[def __spirit_repo__ [@http://spirit.sourceforge.net/repository/applications/show_contents.php Spirit Repository]] +[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]] +[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]] +[def __gmane__ [@http://www.gmane.org Gmane]] +[def __mlist_archive__ [@http://news.gmane.org/gmane.comp.parsers.spirit.general]] +[def __forwarding__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm Forwarding Function Problem]] +[def __boost_mpl__ [@http://boost.org/libs/mpl/doc/index.html Boost.MPL]] +[def __boost_range__ [@http://boost.org/libs/range/index.html Boost.Range]] + +[section Preface] + +[:['Functional programming is so called because a program consists entirely of +functions. The main program itself is written as a function which receives the +program's input as its argument and delivers the program's output as its result. +Typically the main function is defined in terms of other functions, which in +turn are defined in terms of still more functions until at the bottom level the +functions are language primitives.]] + +[:*John Hughes*-- /Why Functional Programming Matters/] + +[$images/lambda_cpp.png] + +[h2 Description] + +Phoenix enables Functional Programming (FP) in C++. The design and +implementation of Phoenix is highly influenced by __fcpp__ by Yannis Smaragdakis +and Brian McNamara and the __bll__ (Boost Lambda Library) by Jaakko Jaarvi and +Gary Powell. Phoenix is a blend of FC++ and BLL using the implementation +techniques used in the __spirit__ inline parser. Phoenix version 2, this +version, will probably be the last release of the library. Phoenix v2 will be +the basis of the Phoenix and __bll__ merger. + +Phoenix is a header only library. It is extremely modular by design. One can +extract and use only a small subset of the full library, literally tearing the +library into small pieces, without fear that the pieces won't work anymore. The +library is organized in highly independent modules and layers. + +[h2 How to use this manual] + +The Phoenix library is organized in logical modules. This documentation +provides a user's guide and reference for each module in the library. A simple +and clear code example is worth a hundred lines of documentation; therefore, the +user's guide is presented with abundant examples annotated and explained in +step-wise manner. The user's guide is based on examples: lots of them. + +As much as possible, forward information (i.e. citing a specific piece of +information that has not yet been discussed) is avoided in the user's manual +portion of each module. In many cases, though, it is unavoidable that advanced +but related topics not be interspersed with the normal flow of discussion. To +alleviate this problem, topics categorized as "advanced" may be skipped at first +reading. + +Some icons are used to mark certain topics indicative of their relevance. These +icons precede some text to indicate: + +[table Icons + [[Icon] [Name] [Meaning]] + [[__note__] [Note] [Information provided is auxiliary but will + give the reader a deeper insight into a specific + topic. May be skipped.]] + [[__alert__] [Alert] [Information provided is of utmost importance.]] + [[__tip__] [Tip] [A potentially useful and helpful piece of + information.]] +] + +This documentation is automatically generated by Spirit QuickBook documentation +tool. QuickBook can be found in the __spirit_repo__. + +[h2 Support] + +Please direct all questions to Spirit's mailing list. You can subscribe to the +__spirit_list__. The mailing list has a searchable archive. A search link to +this archive is provided in __spirit__'s home page. You may also read and post +messages to the mailing list through __spirit_general__ (thanks to __gmane__). +The news group mirrors the mailing list. Here is a link to the archives: +__mlist_archive__. + +[h2 [*/...To my dear daughter, Phoenix/]] + +[endsect] + +[section Introduction] + +[$images/banner.png] + +The Phoenix library enables FP techniques such as higher order functions, +/lambda/ (unnamed functions), /currying/ (partial function application) and lazy +evaluation in C++. The focus is more on usefulness and practicality than purity, +elegance and strict adherence to FP principles. + +FP is a programming discipline that is not at all tied to a specific language. +FP as a programming discipline can, in fact, be applied to many programming +languages. In the realm of C++ for instance, we are seeing more FP techniques +being applied. C++ is sufficiently rich to support at least some of the most +important facets of FP. C++ is a multiparadigm programming language. It is not +only procedural. It is not only object oriented. Beneath the core of the +standard C++ library, a closer look into STL gives us a glimpse of FP already in +place. It is obvious that the authors of STL know and practice FP. In the near +future, we shall surely see more FP trickle down into the mainstream. + +The truth is, most of the FP techniques can coexist quite well with the standard +object oriented and imperative programming paradigms. When we are using STL +algorithms and functors (function objects) for example, we are already doing FP. +Phoenix is an evolutionary next step. + +[endsect] + +[section Starter Kit] + +Most "quick starts" only get you a few blocks from where you are. From there, +you are on your own. Yet, typically, you'd want to get to the next city. This +starter kit shall be as minimal as possible, yet packed as much power as +possible. + +So you are busy and always on the go. You do not wish to spend a lot of time +studying the library. You wish to be spared the details for later when you need +it. For now, all you need to do is to get up to speed as quickly as possible and +start using the library. If this is the case, this is the right place to start. + +This chapter is by no means a thorough discourse of the library. For more +information on Phoenix, please take some time to read the rest of the User's +Guide. Yet, if you just want to use the library quickly, now, this chapter will +probably suffice. Rather than taking you to the details of the library, we shall +try to provide you with annotated exemplars instead. Hopefully, this will get +you into high gear quickly. + +[h2 Functors everywhere] + +Phoenix is built on function objects (functors). The functor is the main +building block. We compose functors to build more complex functors... to build +more complex functors... and so on. Almost everything is a functor. + +[blurb __note__ Functors are so ubiquitous in Phoenix that, in the manual, the +words /"functor"/ and /"function"/ are used interchangeably.] + +[section Values] + +Values are functions! Examples: + + val(3) + val("Hello, World") + +The first evaluates to a nullary function (a function taking no arguments) that +returns an `int`, `3`. The second evaluates to a nullary function that returns +a `char const(&)[13]`, `"Hello, World"`. + +[h2 Lazy Evaluation] + +Confused? `val(3)` is a unary function, you say? Yes it is. However, read +carefully: /"evaluates to a nullary function"/. `val(3)` evaluates to (returns) a +nullary function. Aha! `val(3)` returns a function! So, since `val(3)` returns a +function, you can invoke it. Example: + + cout << val(3)() << endl; + +(See [@../../example/users_manual/values.cpp values.cpp]) + +[blurb __tip__ Learn more about values [link phoenix.primitives.values here.]] + +The second function call (the one with no arguments) calls the nullary function +which then returns `3`. The need for a second function call is the reason why +the function is said to be [*/Lazily Evaluated/]. The first call doesn't do +anything. You need a second call to finally evaluate the thing. The first call +lazily evaluates the function; i.e. doesn't do anything and defers the evaluation +for later. + +[h2 Callbacks] + +It may not be immediately apparent how lazy evaluation can be useful by just +looking at the example above. Putting the first and second function call in a +single line is really not very useful. However, thinking of `val(3)` as a +callback function (and in most cases they are actually used that way), will make +it clear. Example: + + template + void print(F f) + { + cout << f() << endl; + } + + int + main() + { + print(val(3)); + print(val("Hello World")); + return 0; + } + +(See [@../../example/users_manual/callback.cpp callback.cpp]) + +[endsect] +[section References] + +References are functions. They hold a reference to a value stored somehere. +For example, given: + + int i = 3; + char const* s = "Hello World"; + +we create `references` to `i` and `s` this way: + + ref(i) + ref(s) + +Like `val`, the expressions above evaluates to a nullary function; the first one +returning an `int&`, and the second one returning a `char const*&`. + +(See [@../../example/users_manual/references.cpp references.cpp]) + +[blurb __tip__ Learn more about references [link phoenix.primitives.references here.]] + +[endsect] +[section Arguments] + +Arguments are also functions? You bet! + +Until now, we have been dealing with expressions returning a nullary function. +Arguments, on the other hand, evaluate to an N-ary function. An argument +represents the Nth argument. There are a few predefined arguments arg1, +arg2, arg3, arg4 and so on (and it's __bll__ counterparts: _1, _2, _3, _4 and so +on). Examples: + + arg1 // one-or-more argument function that returns its first argument + arg2 // two-or-more argument function that returns its second argument + arg3 // three-or-more argument function that returns its third argument + +`argN` returns the Nth argument. Examples: + + int i = 3; + char const* s = "Hello World"; + cout << arg1(i) << endl; // prints 3 + cout << arg2(i, s) << endl; // prints "Hello World" + +(See [@../../example/users_manual/arguments.cpp arguments.cpp]) + +[blurb __tip__ Learn more about arguments [link phoenix.primitives.arguments here.]] + +[endsect] +[section Composites] + +What we have seen so far, are what are called *primitives*. You can think of +primitives (such as values, references and arguments) as atoms. + +Things start to get interesting when we start /composing/ primitives to form +*composites*. The composites can, in turn, be composed to form even more complex +composites. + +[endsect] +[section Lazy Operators] + +You can use the usual set of operators to form composites. Examples: + + arg1 * arg1 + ref(x) = arg1 + ref(z) + arg1 = arg2 + (3 * arg3) + ref(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is a valid index + +Note the expression: `3 * arg3`. This expression is actually a short-hand +equivalent to: `val(3) * arg3`. In most cases, like above, you can get away with +it. But in some cases, you will have to explicitly wrap your values in `val`. +Rules of thumb: + +* In a binary expression (e.g. `3 * arg3`), at least one of the operands must be + a phoenix primitive or composite. +* In a unary expression (e.g. `arg1++`), the single operand must be a phoenix + primitive or composite. + +If these basic rules are not followed, the result is either in error, or is +immediately evaluated. Some examples: + + ref(x) = 123 // lazy + x = 123 // immediate + + ref(x)[0] // lazy + x[0] // immediate + + ref(x)[ref(i)] // lazy + ref(x)[i] // lazy (equivalent to ref(x)[val(i)]) + x[ref(i)] // illegal (x is not a phoenix primitive or composite) + ref(x[ref(i)]) // illegal (x is not a phoenix primitive or composite) + +[blurb __tip__ Learn more about operators [link phoenix.composite.operator here.]] + +[h2 First Practical Example] + +We've covered enough ground to present a real world example. We want to find the +first odd number in an STL container. Normally we use a functor (function +object) or a function pointer and pass that in to STL's `find_if` generic +function: + +Write a function: + + bool + is_odd(int arg1) + { + return arg1 % 2 == 1; + } + +Pass a pointer to the function to STL's `find_if` algorithm: + + find_if(c.begin(), c.end(), &is_odd) + +Using Phoenix, the same can be achieved directly with a one-liner: + + find_if(c.begin(), c.end(), arg1 % 2 == 1) + +The expression `arg1 % 2 == 1` automagically creates a functor with the expected +behavior. In FP, this unnamed function is called a lambda function. Unlike the +function pointer version, which is monomorphic (expects and works only with a +fixed type int argument), the Phoenix version is fully polymorphic and works +with any container (of ints, of longs, of bignum, etc.) as long as its elements +can handle the `arg1 % 2 == 1` expression. + +(See [@../../example/users_manual/find_if.cpp find_if.cpp]) + +[blurb __tip__ ...[*That's it, we're done]. Well if you wish to know a little bit +more, read on...] + +[endsect] +[section Lazy Statements] + +Lazy statements? Sure. There are lazy versions of the C++ statements we all know +and love. For example: + + if_(arg1 > 5) + cout << arg1 + +Say, for example, we wish to print all the elements that are greater than 5 +(separated by a comma) in a vector. Here's how we write it: + + for_each(v.begin(), v.end(), + if_(arg1 > 5) + [ + cout << arg1 << ", " + ] + ); + +(See [@../../example/users_manual/if.cpp if.cpp]) + +[blurb __tip__ Learn more about statements [link phoenix.composite.statement here.]] + +[endsect] +[section Construct, New, Delete, Casts] + +You'll probably want to work with objects. There are lazy versions of +constructor calls, `new`, `delete` and the suite of C++ casts. Examples: + + construct(arg1, arg2) // constructs a std::string from arg1, arg2 + new_(arg1, arg2) // makes a new std::string from arg1, arg2 + delete_(arg1) // deletes arg1 (assumed to be a pointer) + static_cast_(arg1) // static_cast's arg1 to an int* + +[blurb __note__ Take note that, by convention, names that conflict with C++ +reserved words are appended with a single trailing underscore `'_'`] + +[blurb __tip__ Learn more about this [link phoenix.composite.object here.]] + +[endsect] +[section Lazy Functions] + +As you write more lambda functions, you'll notice certain patterns that you wish +to refactor as reusable functions. When you reach that point, you'll wish that +ordinary functions can co-exist with phoenix functions. Unfortunately, the +/immediate/ nature of plain C++ functions make them incompatible. + +Lazy functions are your friends. The library provides a facility to make lazy +functions. The code below is a rewrite of the `is_odd` function using the +facility: + + struct is_odd_impl + { + template + struct result + { + typedef bool type; + }; + + template + bool operator()(Arg arg1) const + { + return arg1 % 2 == 1; + } + }; + + function is_odd; + +[h2 Things to note:] + +* `result` is a nested metafunction that reflects the return type of the + function (in this case, bool). This makes the function fully polymorphic: + It can work with arbitrary `Arg` types. +* There are as many Args in the `result` metafunction as in the actual + `operator()`. +* `is_odd_impl` implements the function. +* `is_odd`, an instance of `function`, is the lazy function. + +Now, `is_odd` is a truly lazy function that we can use in conjunction with the +rest of phoenix. Example: + + find_if(c.begin(), c.end(), is_odd(arg1)); + +(See [@../../example/users_manual/function.cpp function.cpp]) + +[h2 Predefined Lazy Functions] + +The library is chock full of STL savvy, predefined lazy functions covering the +whole of the STL containers, iterators and algorithms. For example, there are lazy +versions of container related operations such as assign, at, back, begin, +pop_back, pop_front, push_back, push_front, etc. (See [link phoenix.container +Container]). + +[endsect] +[section More] + +As mentioned earlier, this chapter is not a thorough discourse of the library. +It is meant only to cover enough ground to get you into high gear as quickly as +possible. Some advanced stuff is not discussed here (e.g. [link phoenix.composite.scope +Scopes]); nor are features that provide alternative (short-hand) ways to do the +same things (e.g. [link phoenix.composite.bind Bind] vs. Lazy Functions). + +[blurb __tip__ ...*If you still wish to learn more, the read on...*] + +[endsect] +[endsect] + +[section Basics] + +[def __constant_n__ /n/] +[def __argument_n__ a/n/] + +Almost everything is a function in the Phoenix library that can be evaluated as +`f(a1, a2, ..., __argument_n__)`, where __constant_n__ is the function's arity, or number of arguments that the +function expects. Operators are also functions. For example, `a + b` is just a +function with arity == 2 (or binary). `a + b` is the same as `add(a, b)`, `a + b ++ c` is the same as `add(add(a, b), c)`. + +[blurb __note__ Amusingly, functions may even return functions. We shall see +what this means in a short while.] + +[h2 Partial Function Application] + +Think of a function as a black box. You pass arguments and it returns something +back. The figure below depicts the typical scenario. + +[$images/fbox.png] + +A fully evaluated function is one in which all the arguments are given. All +functions in plain C++ are fully evaluated. When you call the `sin(x)` function, +you have to pass a number x. The function will return a result in return: the +sin of x. When you call the `add(x, y)` function, you have to pass two numbers x +and y. The function will return the sum of the two numbers. The figure below is +a fully evaluated `add` function. + +[$images/adder.png] + +A partially applied function, on the other hand, is one in which not all the +arguments are supplied. If we are able to partially apply the function `add` +above, we may pass only the first argument. In doing so, the function does not +have all the required information it needs to perform its task to compute and +return a result. What it returns instead is another function, a lambda function +--another black box. Unlike the original `add` function which has an arity of 2, +the resulting lambda function has an arity of 1. Why? because we already +supplied part of the input: `2` + +[$images/add2.png] + +Now, when we shove in a number into our lambda function, it will return 2 plus +whatever we pass in. The lambda function essentially remembers 1) the original +function, `add`, and 2) the partial input, 2. The figure below illustrates a +case where we pass 3 to our lambda function, which then returns 5: + +[$images/add2_call.png] + +Obviously, partially applying the `add` function, as we see above, cannot be +done directly in C++ where we are expected to supply all the arguments that a +function expects. That's where the Phoenix library comes in. The library +provides the facilities to do partial function application. + +[h2 STL and higher order functions] + +So, what's all the fuss? What makes partial function application so useful? +Recall our original example in the [link phoenix.starter_kit previous section]: + + find_if(c.begin(), c.end(), arg1 % 2 == 1) + +The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a placeholder +for an argument to be supplied later. Hence, since there's only one unsupplied argument, the +lambda function has an arity 1. It just so happens that `find_if` supplies the +unsupplied argument as it loops from `c.begin()` to `c.end()`. + +[blurb __note__ Higher order functions are functions which can take other +functions as arguments, and may also return functions as results. Higher order +functions are functions that are treated like any other objects and can be used as +arguments and return values from functions.] + +[h2 Lazy Evaluation] + +In Phoenix, to put it more accurately, function evaluation has two stages: + +# Partial application +# Final evaluation + +The first stage is handled by a set of generator functions. These are your front +ends (in the client's perspective). These generators create (through partial +function application), higher order functions that can be passed on just like +any other function pointer or function object. The second stage, the actual +function call, can be invoked or executed anytime in the future, or not at all; +hence /"lazy"/. + +If we look more closely, the first step involves partial function application: + + arg1 % 2 == 1 + +The second step is the actual function invocation (done inside the `find_if` +function. These are the back-ends (often, the final invocation is never actually +seen by the client). In our example, the `find_if`, if we take a look inside, +we'll see something like: + + template + InputIterator + find_if(InputIterator first, InputIterator last, Predicate pred) + { + while (first != last && !pred(*first)) // <--- The lambda function is called here + ++first; // passing in *first + return first; + } + +Again, typically, we, as clients, see only the first step. However, in this +document and in the examples and tests provided, don't be surprised to see the +first and second steps juxtaposed in order to illustrate the complete semantics +of Phoenix expressions. Examples: + + int x = 1; + int y = 2; + + cout << (arg1 % 2 == 1)(x) << endl; // prints 1 or true + cout << (arg1 % 2 == 1)(y) << endl; // prints 0 or false + + +[h2 Forwarding Function Problem] + +Usually, we, as clients, write the call-back functions while libraries (such as +STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g. +if you have to write an STL algorithm that takes in a predicate, or develop a +GUI library that accepts event handlers, you have to be aware of a little known +problem in C++ called the "__forwarding__". + +Look again at the code above: + + (arg1 % 2 == 1)(x) + +Notice that, in the second-stage (the final evaluation), we used a variable `x`. +Be aware that the second stage cannot accept non-const temporaries and literal +constants. Hence, this will fail: + + (arg1 % 2 == 1)(123) // Error! + +Disallowing non-const rvalues partially solves the "__forwarding__" but +prohibits code like above. + +[h2 Polymorphic Functions] + +Unless otherwise noted, Phoenix generated functions are fully polymorphic. For +instance, the `add` example above can apply to integers, floating points, user +defined complex numbers or even strings. Example: + + std::string h("Hello"); + char const* w = " World"; + std::string r = add(arg1, arg2)(h, w); + +evaluates to `std::string("Hello World")`. The observant reader might notice +that this function call in fact takes in heterogeneous arguments where `arg1` is +of type `std::string` and `arg2` is of type `char const*`. `add` still works +because the C++ standard library allows the expression `a + b` where `a` is a +`std::string` and `b` is a `char const*`. + +[endsect] + +[section Organization] + +Care and attention to detail was given, painstakingly, to the design and +implementation of Phoenix. + +The library is organized in four layers: + +[$images/organization.png] + +The modules are orthogonal, with no cyclic dependencies. +Lower layers do not depend on higher layers. Modules in a layer do not depend on other modules in the same layer. +This means, for example, that Bind can be completely discarded if it is +not required; or one could perhaps take out Operator and Statement and just use Function, +which may be desireable in a pure FP application. + +The library has grown from the original Phoenix but still comprises only +header files. There are no object files to link against. + +[h2 Core] + +The lowest two layers comprise the core. + +The `Actor` is the main concept behind the library. Lazy functions are +abstracted as actors. There are only 2 +kinds of actors: + +# Primitives +# Composites + +Primitives provide the basic building blocks of functionality within Phoenix. +Composites are used to combine these primitives together to provide more +powerful functionality. + +Composites are composed of zero or more actors. Each actor in a composite can +again be another composite. + +[table Modules + [[Module] [Description]] + [[Function] [Lazy functions support (e.g. `add`)]] + [[Operator] [Lazy operators support (e.g. `+`)]] + [[Statement] [Lazy statments (e.g. `if_`, `while_`)]] + [[Object] [Lazy casts (e.g. `static_cast_`), + object creation destruction (e.g. + `new_`, `delete_`)]] + [[Scope] [Support for scopes, local variables and lambda-lambda]] + [[Bind] [Lazy functions from free functions, member functions or member variables.]] + [[Container] [Set of predefined "lazy" functions that work on STL + containers and sequences (e.g. `push_back`).]] + [[Algorithm] [Set of predefined "lazy" versions of the STL algorithms + (e.g. `find_if`).]] +] + +Each module is defined in a header file with the same name. For example, +the core module is defined in ``. + +[table Includes + [[Module] [File]] + [[Core] [`#include `]] + [[Function] [`#include `]] + [[Operator] [`#include `]] + [[Statement] [`#include `]] + [[Object] [`#include `]] + [[Scope] [`#include `]] + [[Bind] [`#include `]] + [[Container] [`#include `]] + [[Algorithm] [`#include `]] +] + +[blurb __tip__ Finer grained include files are available per feature; see the +succeeding sections.] + +[endsect] + +[section Actors] + +The `Actor` is the main concept behind the library. Actors are function objects. +An actor can accept 0 to `PHOENIX_LIMIT` arguments. + +[blurb __note__ You can set `PHOENIX_LIMIT`, the predefined maximum arity an +actor can take. By default, `PHOENIX_LIMIT` is set to 10.] + +Phoenix supplies an `actor` class template whose specializations +model the `Actor` concept. `actor` has one template parameter, `Eval`, +that supplies the smarts to evaluate the resulting function. + + template + struct actor : Eval + { + return_type + operator()() const; + + template + return_type + operator()(T0& _0) const; + + template + return_type + operator()(T0& _0, T1& _1) const; + + //... + }; + +The actor class accepts the arguments through a set of function call operators +for 0 to `PHOENIX_LIMIT` arities (Don't worry about the details, for now. Note, for example, +that we skimp over the details regarding `return_type`). The arguments +are then forwarded to the actor's `Eval` for evaluation. + +[endsect] + +[section Primitives] + +Actors are composed to create more complex actors in a tree-like hierarchy. The +primitives are atomic entities that are like the leaves in the tree. Phoenix is +extensible. New primitives can be added anytime. Right out of the box, there are +only a few primitives. This section shall deal with these preset primitives. + +[section Arguments] + + #include + +We use an instance of: + + actor > + +to represent the Nth function argument. The argument placeholder acts as an +imaginary data-bin where a function argument will be placed. + +[h2 Predefined Arguments] + +There are a few predefined instances of `actor >` named +`arg1`..`argN`, and its __bll__ counterpart `_1`..`_N`. (where N is a predefined +maximum). + +Here are some sample preset definitions of `arg1`..`argN` + + actor > const arg1 = argument<0>(); + actor > const arg2 = argument<1>(); + actor > const arg3 = argument<2>(); + +and its __bll__ `_1`..`_N` style counterparts: + + actor > const _1 = argument<0>(); + actor > const _2 = argument<1>(); + actor > const _3 = argument<2>(); + +[blurb __note__ You can set `PHOENIX_ARG_LIMIT`, the predefined maximum +placeholder index. By default, `PHOENIX_ARG_LIMIT` is set to `PHOENIX_LIMIT` +(See [link phoenix.actors Actors]).] + +[h2 User Defined Arguments] + +When appropriate, you can define your own `argument` names. For example: + + actor > x; // note zero based index + +`x` may now be used as a parameter to a lazy function: + + add(x, 6) + +which is equivalent to: + + add(arg1, 6) + +[h2 Evaluating an Argument] + +An argument, when evaluated, selects the Nth argument from the those passed +in by the client. + +For example: + + char c = 'A'; + int i = 123; + const char* s = "Hello World"; + + cout << arg1(c) << endl; // Get the 1st argument: c + cout << arg1(i, s) << endl; // Get the 1st argument: i + cout << arg2(i, s) << endl; // Get the 2nd argument: s + +will print out: + + A + 123 + Hello World + +[h2 Extra Arguments] + +In C and C++, a function can have extra arguments that are not at all used by +the function body itself. These extra arguments are simply ignored. + +Phoenix also allows extra arguments to be passed. For example, recall our +original `add` function: + + add(arg1, arg2) + +We know now that partially applying this function results to a function that +expects 2 arguments. However, the library is a bit more lenient and allows the +caller to supply more arguments than is actually required. Thus, `add` actually +allows 2 /or more/ arguments. For instance, with: + + add(arg1, arg2)(x, y, z) + +the third argument `z` is ignored. Taking this further, in-between arguments are +also ignored. Example: + + add(arg1, arg5)(a, b, c, d, e) + +Here, arguments b, c, and d are ignored. The function `add` takes in the first +argument (`arg1`) and the fifth argument (`arg5`). + +[blurb __note__ There are a few reasons why enforcing strict arity is not +desireable. A case in point is the callback function. Typical callback functions +provide more information than is actually needed. Lambda functions are often +used as callbacks.] + +[endsect] + +[section Values] + + #include + +Whenever we see a constant in a partially applied function, an + + actor > + +(where T is the type of the constant) is automatically created for +us. For instance: + + add(arg1, 6) + +Passing a second argument, `6`, an `actor >` is implicitly created +behind the scenes. This is also equivalent to: + + add(arg1, val(6)) + +`val(x)` generates an `actor >` where `T` is the type of `x`. In most +cases, there's no need to explicitly use `val`, but, as we'll see later on, +there are situations where this is unavoidable. + +[h2 Evaluating a Value] + +Like arguments, values are also actors. As such, values can be evaluated. +Invoking a value gives the value's identity. Example: + + cout << val(3)() << val("Hello World")(); + +prints out "3 Hello World". + +[endsect] + +[section References] + + #include + +Values are immutable constants. Attempting to modify a value will result in a +compile time error. When we want the function to modify the parameter, we use a +reference instead. For instance, imagine a lazy function `add_assign`: + + void add_assign(T& x, T y) { x += y; } // pseudo code + +Here, we want the first function argument, x, to be mutable. Obviously, we +cannot write: + + add_assign(1, 2) // error first argument is immutable + +In C++, we can pass in a reference to a variable as the first argument in our +example above. Yet, by default, the library forces arguments passed to partially +applied functions functions to be immutable values (see [link phoenix.primitives.values +Values]). To achieve our intent, we use: + + actor > + +This is similar to `actor >` above but instead holds a reference to a +variable. + +We normally don't instantiate `actor >` objects directly. Instead we +use `ref`. For example (where `i` is an `int` variable): + + add_assign(ref(i), 2) + +[h2 Evaluating a Reference] + +References are actors. Hence, references can be evaluated. Such invocation gives +the references's identity. Example: + + int i = 3; + char const* s = "Hello World"; + cout << ref(i)() << ref(s)(); + +prints out "3 Hello World" + +[endsect] +[section Constant References] + + #include + +Another free function `cref(cv)` may also be used. `cref(cv)` creates an +`actor >` object. This is similar to `actor >` but +when the data to be passed as argument to a function is heavy and expensive to +copy by value, the `cref(cv)` offers a lighter alternative. + +[endsect] +[section Nothing] + + #include + +Finally, the `actor` does nothing; (a "bum", if you will :-). +There's a sole `actor` instance named "nothing". This actor is +actually useful in situations where we don't want to do anything. (See +[link phoenix.composite.statement.for__statement for_ Statement] for example). + +[endsect] + +[endsect] + +[section Composite] + +Actors may be combined in a multitude of ways to form composites. Composites are +actors that are composed of zero or more actors. Composition is hierarchical. An +element of the composite can be a primitive or again another composite. The +flexibility to arbitrarily compose hierarchical structures allows us to form +intricate constructions that model complex functions, statements and +expressions. + +A composite is-a tuple of 0..N actors. N is the predefined maximum actors a +composite can take. + +[blurb __note__ You can set `PHOENIX_COMPOSITE_LIMIT`, the predefined maximum +actors a composite can take. By default, `PHOENIX_COMPOSITE_LIMIT` is set to +`PHOENIX_LIMIT` (See [link phoenix.actors Actors]).] + +As mentioned, each of the actors A0..AN can, in turn, be another composite, +since a composite is itself an actor. This makes the composite a recursive +structure. The actual evaluation is handled by a composite specific eval policy. + +[section Function] + + #include + +The `function` class template provides a mechanism for implementing lazily +evaluated functions. Syntactically, a lazy function looks like an ordinary C/C++ function. +The function call looks familiar and feels the same as ordinary C++ functions. +However, unlike ordinary functions, the actual function execution is deferred. + +Unlike ordinary function pointers or functor objects that need to be explicitly bound through the bind function (see [link phoenix.composite.bind Bind]), +the argument types of these functions are automatically lazily bound. + +In order to create a lazy function, we need to implement a model of the +FunctionEval concept. For a function that takes `N` arguments, a model of FunctionEval must +provide: + +* An `operator()` that implements that takes `N` arguments, and implements +the function logic. +* A nested metafunction `result` that takes the types of the `N` arguments to +the function and returns the result type of the function. (There is a special case for function +objects that accept no arguments. Such nullary functors are only required to define a typedef +`result_type` that reflects the return type of its `operator()`). + +For example, the following type implements the FunctionEval concept, in order to provide a +lazy factorial function: + + struct factorial_impl + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * this->operator()(n-1); + } + }; + +(See [@../../example/users_manual/factorial.cpp factorial.cpp]) + +Having implemented the `factorial_impl` type, we can declare and instantiate a lazy +`factorial` function this way: + + function factorial; + +Invoking a lazy function such as `factorial` does not immediately execute the function +object `factorial_impl`. Instead, an [link phoenix.actors actor] object is +created and returned to the caller. Example: + + factorial(arg1) + +does nothing more than return an actor. A second function call will invoke +the actual factorial function. Example: + + int i = 4; + cout << factorial(arg1)(i); + +will print out "24". + +Take note that in certain cases (e.g. for function objects with state), an +instance of the model of FunctionEval may be passed on to the constructor. Example: + + function factorial(ftor); + +where ftor is an instance of factorial_impl (this is not necessary in this case +as `factorial_impl` does not require any state). + +[blurb __alert__ Take care though when using function objects with state because they are +often copied repeatedly, and state may change in one of the copies, rather than the +original.] + +[endsect] + +[section Operator] + +This facility provides a mechanism for lazily evaluating operators. +Syntactically, a lazy operator looks and feels like an ordinary C/C++ infix, +prefix or postfix operator. The operator application looks the same. However, +unlike ordinary operators, the actual operator execution is deferred. Samples: + + arg1 + arg2 + 1 + arg1 * arg2 + 1 / -arg1 + arg1 < 150 + +We have seen the lazy operators in action (see [link phoenix.starter_kit +Quick Start]). Let's go back and examine them a little bit further: + + find_if(c.begin(), c.end(), arg1 % 2 == 1) + +Through operator overloading, the expression `arg1 % 2 == 1` actually generates +an actor. This actor object is passed on to STL's `find_if` function. From +the viewpoint of STL, the composite is simply a function object expecting a +single argument of the containers value_type. For each element in `c`, +the element is passed on as an argument `arg1` to the actor (function +object). The actor checks if this is an odd value based on the expression +`arg1 % 2 == 1` where arg1 is replaced by the container's element. + +Like lazy functions (see +[link phoenix.composite.function function]), lazy operators are not immediately executed +when invoked. Instead, an actor (see [link phoenix.actors actors]) +object is created and returned to the caller. Example: + + (arg1 + arg2) * arg3 + +does nothing more than return an actor. A second function call will evaluate +the actual operators. Example: + + int i = 4, j = 5, k = 6; + cout << ((arg1 + arg2) * arg3)(i, j, k); + +will print out "54". + +Operator expressions are lazily evaluated following four simple rules: + +# A binary operator, except `->*` will be lazily evaluated when + /at least/ one of its operands is an actor object + (see [link phoenix.actors actors]). +# Unary operators are lazily evaluted if their argument is an actor object. +# Operator `->*` is lazily evaluted if the left hand argument is an actor object. +# The result of a lazy operator is an actor object that can in turn allow the + applications of rules 1 and 2. + +For example, to check the following expression is lazily evaluated: + + -(arg1 + 3 + 6) + +# Following rule 1, `arg1 + 3` is lazily evaluated since `arg1` is an actor + (see [link phoenix.primitives primitives]). +# The result of this `arg1 + 3` expression is an actor object, following rule 4. +# Continuing, `arg1 + 3 + 6` is again lazily evaluated. + Rule 2. +# By rule 4 again, the result of `arg1 + 3 + 6` is an actor object. +# As `arg1 + 3 + 6` is an actor, `-(arg1 + 3 + 6)` is lazily evaluated. Rule 2. + +Lazy-operator application is highly contagious. In most cases, a single `argN` +actor infects all its immediate neighbors within a group (first level or +parenthesized expression). + +Note that at least one operand of any operator must be a valid actor +for lazy evaluation to take effect. To force lazy evaluation of an +ordinary expression, we can use `ref(x)`, `val(x)` or `cref(x)` to +transform an operand into a valid actor object (see [link phoenix.primitives primitives]. +For example: + + 1 << 3; // Immediately evaluated + val(1) << 3; // Lazily evaluated + +[h2 Supported operators] + +[h3 Unary operators] + + prefix: ~, !, -, +, ++, --, & (reference), * (dereference) + postfix: ++, -- + +[h3 Binary operators] + + =, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= + +, -, *, /, %, &, |, ^, <<, >> + ==, !=, <, >, <=, >= + &&, ||, ->* + +[h3 Ternary operator] + + if_else(c, a, b) + +The ternary operator deserves special mention. Since C++ does not allow us to +overload the conditional expression: `c ? a : b`, the if_else pseudo function is +provided for this purpose. The behavior is identical, albeit in a lazy manner. + +[h3 Member pointer operator] + + a->*member_object_pointer + a->*member_function_pointer + +The left hand side of the member pointer operator must be an actor returning a pointer +type. The right hand side of the member pointer operator may be either a pointer to member +object or pointer to member function. + +If the right hand side is a member object pointer, the result is an actor which, when evaluated, +returns a reference to that member. For example: + + struct A + { + int member; + }; + + A* a = new A; + ... + + (arg1->*&A::member)(a); // returns member a->member + +If the right hand side is a member function pointer, the result is an actor which, when invoked, calls the specified member function. For example: + + struct A + { + int func(int); + }; + + A* a = new A; + int i = 0; + + (arg1->*&A::func)(arg2)(a, i); // returns a->func(i) + +[table Include Files + [[Operators] [File]] + [[`-`, `+`, `++`, `--`, `+=`, + `-=`, `*=`, `/=`, `%=`, + `*`, `/`, `%`] [`#include `]] + [[`&=`, `|=`, `^=`, `<<=`, + `>>=`, `&`, `|`, `^`, `<<`, + `>>`] [`#include `]] + [[`==`, `!=`, `<`, + `<=`, `>`, `>=`] [`#include `]] + [[`<<`, `>>`] [`#include `]] + [[`!`, &&, `||`] [`#include `]] + [[`&x`, `*p`, `=`, `[]`] [`#include `]] + [[`if_else(c, a, b)`] [`#include `]] + [[`->*`] [`#include `]] +] + +[endsect] + +[section Statement] + +[*/Lazy statements.../] + +The primitives and composite building blocks presented so far are sufficiently +powerful to construct quite elaborate structures. We have presented lazy- +functions and lazy-operators. How about lazy-statements? First, an appetizer: + +Print all odd-numbered contents of an STL container using `std::for_each` +([@../../example/users_manual/all_odds.cpp all_odds.cpp]): + + for_each(c.begin(), c.end(), + if_(arg1 % 2 == 1) + [ + cout << arg1 << ' ' + ] + ); + +Huh? Is that valid C++? Read on... + +Yes, it is valid C++. The sample code above is as close as you can get to the +syntax of C++. This stylized C++ syntax differs from actual C++ code. First, the +`if` has a trailing underscore. Second, the block uses square brackets instead +of the familiar curly braces {}. + +[blurb __note__ *C++ in C++?*\n\n + In as much as __spirit__ attempts to mimic EBNF in C++, + Phoenix attempts to mimic C++ in C++!!! +] + +Here are more examples with annotations. The code almost speaks for itself. + +[section Block Statement] + + #include + +Syntax: + + statement, + statement, + .... + statement + +Basically, these are comma separated statements. Take note that unlike the C/C++ +semicolon, the comma is a separator put *in-between* statements. This is like +Pascal's semicolon separator, rather than C/C++'s semicolon terminator. For +example: + + statement, + statement, + statement, // ERROR! + +Is an error. The last statement should not have a comma. Block statements can be +grouped using the parentheses. Again, the last statement in a group should not +have a trailing comma. + + statement, + statement, + ( + statement, + statement + ), + statement + +Outside the square brackets, block statements should be grouped. For example: + + for_each(c.begin(), c.end(), + ( + do_this(arg1), + do_that(arg1) + ) + ); + +Wrapping a comma operator chain around a parentheses pair blocks the +interpretation as an argument separator. The reason for the exception for +the square bracket operator is that the operator always takes exactly one +argument, so it "transforms" any attempt at multiple arguments with a comma +operator chain (and spits out an error for zero arguments). + +[endsect] +[section if_ Statement] + + #include + +We have seen the `if_` statement. The syntax is: + + if_(conditional_expression) + [ + sequenced_statements + ] + +[endsect] +[section if_else_ statement] + + #include + +The syntax is + + if_(conditional_expression) + [ + sequenced_statements + ] + .else_ + [ + sequenced_statements + ] + +Take note that `else` has a leading dot and a trailing underscore: `.else_` + +Example: This code prints out all the elements and appends `" > 5"`, `" == 5"` +or `" < 5"` depending on the element's actual value: + + for_each(c.begin(), c.end(), + if_(arg1 > 5) + [ + cout << arg1 << " > 5\n" + ] + .else_ + [ + if_(arg1 == 5) + [ + cout << arg1 << " == 5\n" + ] + .else_ + [ + cout << arg1 << " < 5\n" + ] + ] + ); + +Notice how the `if_else_` statement is nested. + +[endsect] +[section switch_ statement] + + #include + +The syntax is: + + switch_(integral_expression) + [ + case_(sequenced_statements), + ... + default_(sequenced_statements) + ] + +A comma separated list of cases, and an optional default can be provided. Note unlike +a normal switch statement, cases do not fall through. + +Example: This code prints out `"one"`, `"two"` or `"other value"` depending on the +element's actual value: + + for_each(c.begin(), c.end(), + switch_(arg1) + [ + case_<1>(cout << val("one") << '\n'), + case_<2>(cout << val("two") << '\n'), + default_(cout << val("other value") << '\n') + ] + ); + +[endsect] +[section while_ Statement] + + #include + +The syntax is: + + while_(conditional_expression) + [ + sequenced_statements + ] + +Example: This code decrements each element until it reaches zero and prints out +the number at each step. A newline terminates the printout of each value. + + for_each(c.begin(), c.end(), + ( + while_(arg1--) + [ + cout << arg1 << ", " + ], + cout << val("\n") + ) + ); + +[endsect] +[section do_while_ Statement] + + #include + +The syntax is: + + do_ + [ + sequenced_statements + ] + .while_(conditional_expression) + +Again, take note that `while` has a leading dot and a trailing underscore: +`.while_` + +Example: This code is almost the same as the previous example above with a +slight twist in logic. + + for_each(c.begin(), c.end(), + ( + do_ + [ + cout << arg1 << ", " + ] + .while_(arg1--), + cout << val("\n") + ) + ); + +[endsect] +[section for_ Statement] + + #include + +The syntax is: + + for_(init_statement, conditional_expression, step_statement) + [ + sequenced_statements + ] + +It is again very similar to the C++ for statement. Take note that the +init_statement, conditional_expression and step_statement are separated by the +comma instead of the semi-colon and each must be present (i.e. `for_(,,)` is +invalid). This is a case where the [link phoenix.primitives.nothing nothing] +actor can be useful. + +Example: This code prints each element N times where N is the element's value. A +newline terminates the printout of each value. + + int iii; + for_each(c.begin(), c.end(), + ( + for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii)) + [ + cout << arg1 << ", " + ], + cout << val("\n") + ) + ); + +As before, all these are lazily evaluated. The result of such statements are in +fact composites that are passed on to STL's for_each function. In the viewpoint +of `for_each`, what was passed is just a functor, no more, no less. + +[blurb __note__ Unlike lazy functions and lazy operators, lazy statements always +return void.] + +[endsect] +[section try_ catch_ Statement] + + #include + +The syntax is: + + try_ + [ + sequenced_statements + ] + .catch_() + [ + sequenced_statements + ] + ... + .catch_all + [ + sequenced_statement + ] + +Note the usual underscore after try and catch, and the extra parentheses required +after the catch. + +Example: The following code calls the (lazy) function `f` for each element, and +prints messages about different exception types it catches. + + try_ + [ + f(arg1) + ] + .catch_() + [ + cout << val("caught runtime error or derived\n") + ] + .catch_() + [ + cout << val("caught exception or derived\n") + ] + .catch_all + [ + cout << val("caught some other type of exception\n") + ] + +[endsect] +[section throw_] + + #include + +As a natural companion to the try/catch support, the statement module provides +lazy throwing and rethrowing of exceptions. + +The syntax to throw an exception is: + + throw_(exception_expression) + +The syntax to rethrow an exception is: + + throw_() + +Example: This code extends the try/catch example, rethrowing exceptions derived from +runtime_error or exception, and translating other exception types to runtime_errors. + + try_ + [ + f(arg1) + ] + .catch_() + [ + cout << val("caught runtime error or derived\n"), + throw_() + ] + .catch_() + [ + cout << val("caught exception or derived\n"), + throw_() + ] + .catch_all + [ + cout << val("caught some other type of exception\n"), + throw_(runtime_error("translated exception")) + ] + +[endsect] +[endsect] + +[section Object] + +The Object module deals with object construction, destruction and conversion. +The module provides /"lazy"/ versions of C++'s object constructor, `new`, +`delete`, `static_cast`, `dynamic_cast`, `const_cast` and `reinterpret_cast`. + +[h2 Construction] + +[*/Lazy constructors.../] + + #include + +Lazily construct an object from an arbitrary set of arguments: + + construct(ctor_arg1, ctor_arg2, ..., ctor_argN); + +where the given parameters are the parameters to the contructor of the object of +type T (This implies, that type T is expected to have a constructor with a +corresponding set of parameter types.). + +Example: + + construct(arg1, arg2) + +Constructs a `std::string` from `arg1` and `arg2`. + +[blurb __note__ The maximum number of actual parameters is limited by the +preprocessor constant PHOENIX_COMPOSITE_LIMIT. Note though, that this limit +should not be greater than PHOENIX_LIMIT. By default, `PHOENIX_COMPOSITE_LIMIT` +is set to `PHOENIX_LIMIT` (See [link phoenix.actors Actors]).] + +[h2 New] + +[*/Lazy new.../] + + #include + +Lazily construct an object, on the heap, from an arbitrary set of arguments: + + new_(ctor_arg1, ctor_arg2, ..., ctor_argN); + +where the given parameters are the parameters to the contructor of the object of +type T (This implies, that type T is expected to have a constructor with a +corresponding set of parameter types.). + +Example: + + new_(arg1, arg2) // note the spelling of new_ (with trailing underscore) + +Creates a `std::string` from `arg1` and `arg2` on the heap. + +[blurb __note__ Again, the maximum number of actual parameters is limited by the +preprocessor constant PHOENIX_COMPOSITE_LIMIT. See the note above.] + +[h2 Delete] + +[*/Lazy delete.../] + + #include + +Lazily delete an object, from the heap: + + delete_(arg); + +where arg is assumed to be a pointer to an object. + +Example: + + delete_(arg1) // note the spelling of delete_ (with trailing underscore) + +[h2 Casts] + +[*/Lazy casts.../] + + #include + #include + #include + #include + +The set of lazy C++ cast template functions provide a way of lazily casting an +object of a certain type to another type. The syntax resembles the well known +C++ casts. Take note however that the lazy versions have a trailing underscore. + + static_cast_(lambda_expression) + dynamic_cast_(lambda_expression) + const_cast_(lambda_expression) + reinterpret_cast_(lambda_expression) + +Example: + + static_cast_(&arg1) + +Static-casts the address of `arg1` to a `Base*`. + +[endsect] + +[section Scope] + +Up until now, the most basic ingredient is missing: creation of and access to +local variables in the stack. When recursion comes into play, you will soon +realize the need to have true local variables. It may seem that we do not need +this at all since an unnamed lambda function cannot call itself anyway; at least +not directly. With some sort of arrangement, situations will arise where a +lambda function becomes recursive. A typical situation occurs when we store a +lambda function in a [@http://www.boost.org/libs/function Boost.Function], +essentially naming the unnamed lambda. + +There will also be situations where a lambda function gets passed as an argument +to another function. This is a more common situation. In this case, the lambda +function assumes a new scope; new arguments and possibly new local variables. + +This section deals with local variables and nested lambda scopes. + +[h2 Local Variables] + + #include + +We use an instance of: + + actor > + +to represent a local variable. The local variable acts as an imaginary data-bin +where a local, stack based data will be placed. `Key` is an arbitrary type that +is used to identify the local variable. Example: + + struct size_key; + actor > size; + +[h2 Predefined Local Variables] + +There are a few predefined instances of `actor >` +named `_a`..`_z` that you can already use. To make use of them, simply use the +`namespace boost::phoenix::local_names`: + + using namespace boost::phoenix::local_names; + +[h2 let] + + #include + +You declare local variables using the syntax: + + let(local-declarations) + [ + let-body + ] + +`let` allows 1..N local variable declarations (where N == +`PHOENIX_LOCAL_LIMIT`). Each declaration follows the form: + + local-id = lambda-expression + +[blurb __note__ You can set `PHOENIX_LOCAL_LIMIT`, the predefined maximum local +variable declarations in a let expression. By default, `PHOENIX_LOCAL_LIMIT` is +set to `PHOENIX_LIMIT`.] + +Example: + + let(_a = 123, _b = 456) + [ + _a + _b + ] + +[h2 Reference Preservation] + +The type of the local variable assumes the type of the lambda- expression. Type +deduction is reference preserving. For example: + + let(_a = arg1, _b = 456) + +`_a` assumes the type of `arg1`: a reference to an argument, while `_b` has type +`int`. + +Consider this: + + int i = 1; + + let(_a = arg1) + [ + cout << --_a << ' ' + ] + (i); + + cout << i << endl; + +the output of above is : 0 0 + +While with this: + + int i = 1; + + let(_a = val(arg1)) + [ + cout << --_a << ' ' + ] + (i); + + cout << i << endl; + +the output is : 0 1 + +Reference preservation is necessary because we need to have L-value access to +outer lambda-scopes (especially the arguments). `arg`s and `ref`s are L-values. +`val`s are R-values. + +[h2 Visibility] + +The scope and lifetimes of the local variables is limited within the let-body. +`let` blocks can be nested. A local variable may hide an outer local variable. +For example: + + let(_x = 1, _y = ", World") + [ + // _x here is an int: 1 + + let(_x = "Hello") // hides the outer _x + [ + cout << _x << _y // prints "Hello, World" + ] + ] + +The RHS (right hand side lambda-expression) of each local-declaration cannot +refer to any LHS local-id. At this point, the local-ids are not in scope yet; +they will only be in scope in the let-body. The code below is in error: + + let( + _a = 1 + , _b = _a // Error: _a is not in scope yet + ) + [ + // _a and _b's scope starts here + /*. body .*/ + ] + +However, if an outer let scope is available, this will be searched. Since +the scope of the RHS of a local-declaration is the outer scope enclosing +the let, the RHS of a local-declaration can refer to a local variable of +an outer scope: + + let(_a = 1) + [ + let( + _a = 1 + , _b = _a // Ok. _a refers to the outer _a + ) + [ + /*. body .*/ + ] + ] + +[h2 lambda] + + #include + +A lot of times, you'd want to write a lazy function that accepts one or more +functions (higher order functions). STL algorithms come to mind, for example. +Consider a lazy version of `stl::for_each`: + + struct for_each_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, F f) const + { + std::for_each(c.begin(), c.end(), f); + } + }; + + function const for_each = for_each_impl(); + +Notice that the function accepts another function, `f` as an argument. The scope +of this function, `f`, is limited within the `operator()`. When `f` is called +inside `std::for_each`, it exists in a new scope, along with new arguments and, +possibly, local variables. This new scope is not at all related to the outer +scopes beyond the `operator()`. + +Simple syntax: + + lambda + [ + lambda-body + ] + +Like `let`, local variables may be declared, allowing 1..N local variable +declarations (where N == `PHOENIX_LOCAL_LIMIT`): + + lambda(local-declarations) + [ + lambda-body + ] + +The same restrictions apply with regard to scope and visibility. The RHS +(right hand side lambda-expression) of each local-declaration cannot refer +to any LHS local-id. The local-ids are not in scope yet; they will be in +scope only in the lambda-body: + + lambda( + _a = 1 + , _b = _a // Error: _a is not in scope yet + ) + +See [link phoenix.composite.scope.visibility `let` Visibility] above for more information. + +Example: Using our lazy `for_each` let's print all the elements in a container: + + for_each(arg1, lambda[cout << arg1]) + +As far as the arguments are concerned (arg1..argN), the scope in which the +lambda-body exists is totally new. The left `arg1` refers to the argument passed +to `for_each` (a container). The right `arg1` refers to the argument passed by +`std::for_each` when we finally get to call `operator()` in our `for_each_impl` +above (a container element). + +Yet, we may wish to get information from outer scopes. While we do not have +access to arguments in outer scopes, what we still have is access to local +variables from outer scopes. We may only be able to pass argument related +information from outer `lambda` scopes through the local variables. + +[blurb __note__ This is a crucial difference between `let` and `lambda`: `let` +does not introduce new arguments; `lambda` does.] + +Another example: Using our lazy `for_each`, and a lazy `push_back`: + + struct push_back_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, T& x) const + { + c.push_back(x); + } + }; + + function const push_back = push_back_impl(); + +write a lambda expression that accepts: + +# a 2-dimensional container (e.g. `vector >`) +# a container element (e.g. `int`) + +and pushes-back the element to each of the `vector`. + +Solution: + + for_each(arg1, + lambda(_a = arg2) + [ + push_back(arg1, _a) + ] + ) + +Since we do not have access to the arguments of the outer scopes beyond the +lambda-body, we introduce a local variable `_a` that captures the second outer +argument: `arg2`. Hence: _a = arg2. This local variable is visible inside the +lambda scope. + +(See [@../../example/users_manual/lambda.cpp lambda.cpp]) + +[endsect] + +[section Bind] + +['Binding] is the act of tying together a function to some arguments for +deferred (lazy) evaluation. Named [link phoenix.composite.function Lazy functions] +require a bit of typing. Unlike (unnamed) lambda expressions, we need to write a +functor somewhere offline, detached from the call site. If you wish to transform a +plain function, member function or member variable to a lambda expression, `bind` +is your friend. + +[blurb __note__ Take note that binders are monomorphic. Rather than binding +functions, the preferred way is to write true generic and polymorphic [link +phoenix.composite.function lazy-functions]. However, since most of the time we +are dealing with adaptation of exisiting code, binders get the job done faster.] + +There is a set of overloaded `bind` template functions. Each `bind(x)` +function generates a suitable binder object, a [link phoenix.composite +composite]. + +[h2 Binding Functions] + + #include + +Example, given a function `foo`: + + void foo(int n) + { + std::cout << n << std::endl; + } + +Here's how the function `foo` may be bound: + + bind(&foo, arg1) + +This is now a full-fledged [link phoenix.composite composite] that can finally +be evaluated by another function call invocation. A second function call will +invoke the actual `foo` function. Example: + + int i = 4; + bind(&foo, arg1)(i); + +will print out "4". + +[h2 Binding Member Functions] + + #include + +Binding member functions can be done similarly. A bound member function takes in +a pointer or reference to an object as the first argument. For instance, given: + + struct xyz + { + void foo(int) const; + }; + +`xyz`'s `foo` member function can be bound as: + + bind(&xyz::foo, obj, arg1) // obj is an xyz object + +Take note that a lazy-member functions expects the first argument to be a +pointer or reference to an object. Both the object (reference or pointer) and +the arguments can be lazily bound. Examples: + + xyz obj; + bind(&xyz::foo, arg1, arg2) // arg1.foo(arg2) + bind(&xyz::foo, obj, arg1) // obj.foo(arg1) + bind(&xyz::foo, obj, 100) // obj.foo(100) + +[h2 Binding Member Variables] + + #include + +Member variables can also be bound much like member functions. Member variables +are not functions. Yet, like the [link phoenix.primitives.references `ref(x)`] that +acts like a nullary function returning a reference to the data, member variables, +when bound, act like a unary function, taking in a pointer or reference to an +object as its argument and returning a reference to the bound member variable. +For instance, given: + + struct xyz + { + int v; + }; + +`xyz::v` can be bound as: + + bind(&xyz::v, obj) // obj is an xyz object + +As noted, just like the bound member function, a bound member variable also +expects the first (and only) argument to be a pointer or reference to an object. +The object (reference or pointer) can be lazily bound. Examples: + + xyz obj; + bind(&xyz::v, arg1) // arg1.v + bind(&xyz::v, obj) // obj.v + bind(&xyz::v, arg1)(obj) = 4 // obj.v = 4 + +[endsect] + +[endsect] +[section Container] + + #include + +The container module predefines a set of lazy functions that work on STL +containers. These functions provide a mechanism for the lazy evaluation of the +public member functions of the STL containers. The lazy functions are thin +wrappers that simply forward to their respective counterparts in the STL +library. + +Lazy functions are provided for all of the member functions of the following +containers: + +* deque +* list +* map +* multimap +* vector + +Indeed, should your class have member functions with the same names and +signatures as those listed below, then it will automatically be supported. To +summarize, lazy functions are provided for member functions: + +* assign +* at +* back +* begin +* capacity +* clear +* empty +* end +* erase +* front +* get_allocator +* insert +* key_comp +* max_size +* pop_back +* pop_front +* push_back +* push_front +* rbegin +* rend +* reserve +* resize +* size +* splice +* value_comp + +The lazy functions' names are the same as the corresponding member function. The +difference is that the lazy functions are free functions and therefore does not +use the member "dot" syntax. + +[table Sample usage + [["Normal" version] ["Lazy" version]] + [[`my_vector.at(5)`] [`at(arg1, 5)`]] + [[`my_list.size()`] [`size(arg1)`]] + [[`my_vector1.swap(my_vector2)`] [`swap(arg1, arg2)`]] +] + +Notice that member functions with names that clash with stl algorithms are +absent. This will be provided in Phoenix's algorithm module. + +No support is provided here for lazy versions of `operator+=`, `operator[]` etc. +Such operators are not specific to STL containers and lazy versions can +therefore be found in [link phoenix.composite.operator operators]. + +The following table describes the container functions and their semantics. + +[blurb __tip__ Arguments in brackets denote optional parameters.] + +[table Lazy STL Container Functions + [[Function] [Semantics]] + [[`assign(c, a[, b, c])`] [`c.assign(a[, b, c])`]] + [[`at(c, i)`] [`c.at(i)`]] + [[`back(c)`] [`c.back()`]] + [[`begin(c)`] [`c.begin()`]] + [[`capacity(c)`] [`c.capacity()`]] + [[`clear(c)`] [`c.clear()`]] + [[`empty(c)`] [`c.empty()`]] + [[`end(c)`] [`c.end()`]] + [[`erase(c, a[, b])`] [`c.erase(a[, b])`]] + [[`front(c)`] [`c.front()`]] + [[`get_allocator(c)`] [`c.get_allocator()`]] + [[`insert(c, a[, b, c])`] [`c.insert(a[, b, c])`]] + [[`key_comp(c)`] [`c.key_comp()`]] + [[`max_size(c)`] [`c.max_size()`]] + [[`pop_back(c)`] [`c.pop_back()`]] + [[`pop_front(c)`] [`c.pop_front()`]] + [[`push_back(c, d)`] [`c.push_back(d)`]] + [[`push_front(c, d)`] [`c.push_front(d)`]] + [[`pop_front(c)`] [`c.pop_front()`]] + [[`rbegin(c)`] [`c.rbegin()`]] + [[`rend(c)`] [`c.rend()`]] + [[`reserve(c, n)`] [`c.reserve(n)`]] + [[`resize(c, a[, b])`] [`c.resize(a[, b])`]] + [[`size(c)`] [`c.size()`]] + [[`splice(c, a[, b, c, d])`] [`c.splice(a[, b, c, d])`]] + [[`value_comp(c)`] [`c.value_comp()`]] +] + +[endsect] + +[section Algorithm] + + #include + +The algorithm module provides wrappers for the standard algorithms in the +`` and `` headers. + +The algorithms are divided into the categories iteration, transformation and querying, +modelling the __boost_mpl__ library. The different algorithm classes can be +included using the headers: + + #include + #include + #include + +The functions of the algorithm module take ranges as arguments where +appropriate. This is different to the standard +library, but easy enough to pick up. Ranges are described in detail in the +__boost_range__ library. + +For example, using the standard copy algorithm to copy between 2 arrays: + + int array[] = {1, 2, 3}; + int output[3]; + std::copy(array, array + 3, output); // We have to provide iterators + // to both the start and end of array + +The analogous code using the phoenix algorithm module is: + + int array[] = {1, 2, 3}; + int output[3]; + copy(arg1, arg2)(array, output); // Notice only 2 arguments, the end of + // array is established automatically + +The __boost_range__ library provides support for standard containers, strings and +arrays, and can be extended to support additional types. + +The following tables describe the different categories of algorithms, and their +semantics. + +[blurb __tip__ Arguments in brackets denote optional parameters.] + +[table Iteration Algorithms + [[Function] [stl Semantics]] + [[`for_each(r, c)`] [`for_each(begin(r), end(r), f)`]] + [[`accumulate(r, o[, f])`] [`accumulate(begin(r), end(r), o[, f])`]] +] + +[table Querying Algorithms + [[Function] [stl Semantics]] + [[`find(r, a)`] [`find(begin(r), end(r), a)`]] + [[`find_if(r, f)`] [`find_if(begin(r), end(r), f)`]] + [[`find_end(r1, r2[, f])`] [`find_end(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`find_first_of(r1, r2[, f])`] [`find_first_of(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`adjacent_find(r[, f])`] [`adjacent_find(begin(r), end(r)[, f])`]] + [[`count(r, a)`] [`count(begin(r), end(r), a)`]] + [[`count_if(r, f)`] [`count_if(begin(r), end(r), f)`]] + [[`distance(r)`] [`distance(begin(r), end(r))`]] + [[`mismatch(r, i[, f])`] [`mismatch(begin(r), end(r), i[, f])`]] + [[`equal(r, i[, f])`] [`equal(begin(r), end(r), i[, f])`]] + [[`search(r1, r2[, f])`] [`search(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`lower_bound(r, a[, f])`] [`lower_bound(begin(r), end(r), a[, f])`]] + [[`upper_bound(r, a[, f])`] [`upper_bound(begin(r), end(r), a[, f])`]] + [[`equal_range(r, a[, f])`] [`equal_range(begin(r), end(r), a[, f])`]] + [[`binary_search(r, a[, f])`] [`binary_search(begin(r), end(r), a[, f])`]] + [[`includes(r1, r2[, f])`] [`includes(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`min_element(r[, f])`] [`min_element(begin(r), end(r)[, f])`]] + [[`max_element(r[, f])`] [`max_element(begin(r), end(r)[, f])`]] + [[`lexicographical_compare(r1, r2[, f])`] [`lexicographical_compare(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] +] + +[table Transformation Algorithms + [[Function] [stl Semantics]] + [[`copy(r, o)`] [`copy(begin(r), end(r), o)`]] + [[`copy_backward(r, o)`] [`copy_backward(begin(r), end(r), o)`]] + [[`transform(r, o, f)`] [`transform(begin(r), end(r), o, f)`]] + [[`transform(r, i, o, f)`] [`transform(begin(r), end(r), i, o, f)`]] + [[`replace(r, a, b)`] [`replace(begin(r), end(r), a, b)`]] + [[`replace_if(r, f, a)`] [`replace(begin(r), end(r), f, a)`]] + [[`replace_copy(r, o, a, b)`] [`replace_copy(begin(r), end(r), o, a, b)`]] + [[`replace_copy_if(r, o, f, a)`] [`replace_copy_if(begin(r), end(r), o, f, a)`]] + [[`fill(r, a)`] [`fill(begin(r), end(r), a)`]] + [[`fill_n(r, n, a)`] [`fill_n(begin(r), n, a)`]] + [[`generate(r, f)`] [`generate(begin(r), end(r), f)`]] + [[`generate_n(r, n, f)`] [`generate_n(begin(r), n, f)`]] + [[`remove(r, a)`] [`remove(begin(r), end(r), a)`]] + [[`remove_if(r, f)`] [`remove_if(begin(r), end(r), f)`]] + [[`remove_copy(r, o, a)`] [`remove_copy(begin(r), end(r), o, a)`]] + [[`remove_copy_if(r, o, f)`] [`remove_copy_if(begin(r), end(r), o, f)`]] + [[`unique(r[, f])`] [`unique(begin(r), end(r)[, f])`]] + [[`unique_copy(r, o[, f])`] [`unique_copy(begin(r), end(r), o[, f])`]] + [[`reverse(r)`] [`reverse(begin(r), end(r))`]] + [[`reverse_copy(r, o)`] [`reverse_copy(begin(r), end(r), o)`]] + [[`rotate(r, m)`] [`rotate(begin(r), m, end(r))`]] + [[`rotate_copy(r, m, o)`] [`rotate_copy(begin(r), m, end(r), o)`]] + [[`random_shuffle(r[, f])`] [`random_shuffle(begin(r), end(r), f)`]] + [[`partition(r, f)`] [`partition(begin(r), end(r), f)`]] + [[`stable_partition(r, f)`] [`stable_partition(begin(r), end(r), f)`]] + [[`sort(r[, f])`] [`sort(begin(r), end(r)[, f])`]] + [[`stable_sort(r[, f])`] [`stable_sort(begin(r), end(r)[, f])`]] + [[`partial_sort(r, m[, f])`] [`partial_sort(begin(r), m, end(r)[, f])`]] + [[`partial_sort_copy(r1, r2[, f])`] [`partial_sort_copy(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`nth_element(r, n[, f])`] [`nth_element(begin(r), n, end(r)[, f])`]] + [[`merge(r1, r2, o[, f])`] [`merge(begin(r1), end(r1), begin(r2), end(r2), o[, f])`]] + [[`inplace_merge(r, m[, f])`] [`inplace_merge(begin(r), m, end(r)[, f])`]] + [[`set_union(r1, r2, o[, f])`] [`set_union(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`set_intersection(r1, r2, o[, f])`] [`set_intersection(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`set_difference(r1, r2, o[, f])`] [`set_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`set_symmetric_difference(r1, r2, o[, f])`] [`set_symmetric_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`push_heap(r[, f])`] [`push_heap(begin(r), end(r)[, f])`]] + [[`pop_heap(r[, f])`] [`pop_heap(begin(r), end(r)[, f])`]] + [[`make_heap(r[, f])`] [`make_heap(begin(r), end(r)[, f])`]] + [[`sort_heap(r[, f])`] [`sort_heap(begin(r), end(r)[, f])`]] + [[`next_permutation(r[, f])`] [`next_permutation(begin(r), end(r)[, f])`]] + [[`prev_permutation(r[, f])`] [`prev_permutation(begin(r), end(r)[, f])`]] + + [[`inner_product(r, o, a[, f1, f2])`] [`inner_product(begin(r), end(r), o[, f1, f2])`]] + [[`partial_sum(r, o[, f])`] [`partial_sum(begin(r), end(r), o[, f])`]] + [[`adjacent_difference(r, o[, f])`] [`adjacent_difference(begin(r), end(r), o[, f])`]] +] + +[endsect] + +[section Inside Phoenix] + +[def __eval_policy__ [link phoenix.inside_phoenix.composites_in_detail.evalpolicy `EvalPolicy`]] +[def __eval__ [link phoenix.inside_phoenix.actors_in_detail.eval_concept `Eval`]] +[def __eval_tuple__ [link phoenix.inside_phoenix.composites_in_detail.evaltuple `EvalTuple`]] +[def __environment__ [link phoenix.inside_phoenix.actors_in_detail.environment `Environment`]] + +This chapter explains in more detail how the library operates. The information +henceforth should not be necessary to those who are interested in just using the +library. However, a microscopic view might prove to be beneficial to moderate +to advanced programmers who wish to extend the library. + +[section Actors In Detail] + +[h3 Actor Concept] + +The main concept is the `Actor`. Actors are function objects (that can accept 0 +to N arguments (where N is a predefined maximum). + +[blurb __note__ You can set `PHOENIX_LIMIT`, the predefined maximum arity an +actor can take. By default, `PHOENIX_LIMIT` is set to 10.] + +[h3 actor template class] + +The `actor` template class models the `Actor` concept: + + template + struct actor : Eval + { + typedef Eval eval_type; + + actor(); + actor(Eval const& base); + + template + explicit actor(T0 const& _0); + + template + actor(T0 const& _0, T1 const& _1); + + // more constructors + + typename apply_actor >::type + operator()() const; + + template + typename apply_actor >::type + operator()(T0& _0) const; + + template + typename apply_actor >::type + operator()(T0& _0, T1& _1) const; + + // function call operators + }; + +[table Actor Concept Requirements + [ [Expression] [Result/Semantics] ] + [ [`T::eval_type`] [The actor's Eval type] ] + [ [`T()`] [Default Constructor] ] + [ [`T(base)`] [Constructor from Eval] ] + [ [`T(arg0, arg1, ..., argN)`] [Pass through constructors] ] + [ [`x(arg0, arg1, ..., argN)`] [Function call operators] ] +] + +[h3 Eval Concept] + +The `actor` template class has a single template parameter, `Eval`, from which +it derives from. While the `Actor` concept represents a function, the `Eval` +concept represents the function body. The requirements for `Eval` are +intentionally kept simple, to make it easy to write models of the concept. We +shall see an example in the [link phoenix.inside_phoenix.actor_example next section]. + +[table Eval Concept Requirements + [ [Expression] [Result/Semantics] ] + [ [`return x.eval(env)`] [Evaluates the function (see Environment below)] ] + [ [`T::result::type`] [The return type of eval (see Environment below)] ] +] + +[h3 Constructors] + +In addition to a default constructor and an constructor from a Eval object, +there are templated (pass through) constructors for 1 to N arguments (N == +`PHOENIX_LIMIT`). These constructors simply forward the arguments to the `base`. + +[blurb __note__ *Parametric Base Class Pattern*\n\n + Notice that actor derives from its template argument Eval. This is the + inverse of the curiously recurring template pattern (CRTP). With the CRTP, a + class, T, has a Derived template parameter that is assumed to be its + subclass. The "parametric base class pattern" (PBCP), on the other hand, + inverses the inheritance and makes a class, T, the derived class. Both CRTP + and PBCP techniques have its pros and cons, which is outside the scope of + this document. CRTP should really be renamed "parametric subclass pattern + (PSCP), but again, that's another story. +] + +[h3 Function Call Operators] + +There are N function call operators for 0 to N arguments (N == `PHOENIX_LIMIT`). +The actor class accepts the arguments and forwards the arguments to the actor's +base `Eval` for evaluation. + +[def [$http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm] + +[blurb __note__ *Forwarding Function Problem*\n\n + The function call operators cannot accept non-const temporaries and literal + constants. There is a known issue with current C++ called the + "__forwarding__". The problem is that given an arbitrary function `F`, using + current C++ language rules, one cannot create a forwarding function `FF` + that transparently assumes the arguments of `F`. Disallowing non-const + rvalues arguments partially solves the problem but prohibits code such as + `f(1, 2, 3);`. +] + +[h3 Environment] + +On an actor function call, before calling the actor's `Eval::eval` for +evaluation, the actor creates an ['*environment*]. Basically, the environment +packages the arguments in a tuple. The `Environment` is a concept, of which, the +`basic_environment` template class is a model of. + +[table Environment Concept Requirements + [ [Expression] [Result/Semantics] ] + [ [`x.args()`] [The arguments in a tie (a tuple of references)] ] + [ [`T::args_type`] [The arguments' types in an MPL sequence] ] + [ [`T::tie_type`] [The tie (tuple of references) type] ] +] + +Schematically: + +[$images/funnel_in.png] + +Other parts of the library (e.g. the scope module) extends the `Environment` +concept to hold other information such as local variables, etc. + +[h3 apply_actor] + +`apply_actor` is a standard MPL style metafunction that simply calls the +Action's `result` nested metafunction: + + template + struct apply_actor + { + typedef typename Action::template result::type type; + }; + +After evaluating the arguments and doing some computation, the `eval` member +function returns something back to the client. To do this, the forwarding +function (the actor's `operator()`) needs to know the return type of the eval +member function that it is calling. For this purpose, models of `Eval` are +required to provide a nested template class: + + template + struct result; + +This nested class provides the result type information returned by the `Eval`'s +`eval` member function. The nested template class `result` should have a typedef +`type` that reflects the return type of its member function `eval`. + +For reference, here's a typical `actor::operator()` that accepts two arguments: + + template + typename apply_actor >::type + operator()(T0& _0, T1& _1) const + { + return eval_type::eval(basic_environment(_0, _1)); + } + +[h3 actor_result] + +For reasons of symmetry to the family of `actor::operator()` there is a special +metafunction usable for actor result type calculation named `actor_result`. This +metafunction allows us to directly to specify the types of the parameters to be +passed to the `actor::operator()` function. Here's a typical `actor_result` that +accepts two arguments: + + template + struct actor_result + { + typedef basic_environment env_type; + typedef typename Action::template result::type type; + }; + +[endsect] +[section Actor Example] + +Let us see a very simple prototypical example of an actor. This is not a toy +example. This is actually part of the library. Remember the [link +phoenix.primitives.references `reference`]?. + +First, we have a model of the `Eval` concept: the `reference`: + + template + struct reference + { + template + struct result + { + typedef T& type; + }; + + reference(T& arg) + : ref(arg) {} + + template + T& eval(Env const&) const + { + return ref; + } + + T& ref; + }; + +Models of `Eval` are never created directly and its instances never exist alone. +We have to wrap it inside the `actor` template class to be useful. The `ref` +template function does this for us: + + template + actor > const + ref(T& v) + { + return reference(v); + } + +The `reference` template class conforms to the __eval__ concept. It has a nested +`result` metafunction that reflects the return type of its `eval` member +function, which peforms the actual function. `reference` stores a reference to +a `T`. Its `eval` member function simply returns the reference. It does not make +use of the environment `Env`. + +/Pretty simple.../ + +[endsect] +[section Composites In Detail] + +We stated before that composites are actors that are composed of zero or more +actors (see [link phoenix.composite Composite]). This is not quite accurate. The +definition was sufficient at that point where we opted to keep things simple and +not bury the reader with details which she might not need anyway. + +Actually, a composite is a model of the __eval__ concept (more on this later). +At the same time, it is also composed of 0..N (where N is a predefined maximum) +__eval__ instances and an eval policy. The individual __eval__ instances are +stored in a tuple. + +[blurb __note__ In a sense, the original definition of "composite", more or +less, will do just fine because __eval__ instances never exist alone and are +always wrapped in an `actor` template class which inherits from it anyway. The +resulting actor IS-AN __eval__.] + +[blurb __note__ You can set `PHOENIX_COMPOSITE_LIMIT`, the predefined maximum +`Eval`s (actors) a composite can take. By default, `PHOENIX_COMPOSITE_LIMIT` is +set to `PHOENIX_LIMIT` (See [link phoenix.actors Actors]).] + +[h2 composite template class] + + template + struct composite : EvalTuple + { + typedef EvalTuple base_type; + typedef EvalPolicy eval_policy_type; + + template + struct result + { + typedef implementation-defined type; + }; + + composite(); + composite(base_type const& actors); + + template + composite(U0 const& _0); + + template + composite(U0 const& _0, U1 const& _1); + + // more constructors + + template + typename result::type + eval(Env const& env) const; + }; + +[h2 EvalTuple] + +`EvalTuple`, holds all the __eval__ instances. The `composite` template class +inherits from it. In addition to a default constructor and a constructor from an +`EvalTuple` object, there are templated (pass through) constructors for 1 to N +arguments (again, where N == `PHOENIX_COMPOSITE_LIMIT`). These constructors +simply forward the arguments to the `EvalTuple` base class. + +[h2 EvalPolicy] + +The composite's `eval` member function calls its `EvalPolicy`'s `eval` member +function (a static member function) passing in the [link +phoenix.inside_phoenix.actors_in_detail.environment environment] and each of +its actors, in parallel. The following diagram illustrates what's happening: + +[$images/funnel_out.png] + +[table EvalPolicy Requirements + [ [Expression] [Result/Semantics] ] + [ [`x.eval(env, eval0, eval1, ..., evalN)`] [Evaluate the composite] ] + [ [`T::result::type`] [The return type of eval] ] +] + +The `EvalPolicy` is expected to have a nested template class `result` which has a +typedef `type` that reflects the return type of its member function `eval`. +Here's a typical example of the composite's eval member function for a 2-actor +composite: + + template + typename result::type + eval(Env const& env) const + { + typedef typename result::type return_type; + return EvalPolicy::template + eval( + env + , get<0>(*this) // gets the 0th element from EvalTuple + , get<1>(*this)); // gets the 1st element from EvalTuple + } + +[endsect] +[section Composing] + +Composites are never instantiated directly. Front end expression templates are +used to generate the composites. Using expression templates, we implement a DSEL +(Domain Specific Embedded Language) that mimicks native C++. You've seen this +DSEL in action in the preceding sections. It is most evident in the +[link phoenix.composite.statement Statement] section. + +There are some facilities in the library to make composition of composites +easier. We have a set of overloaded `compose` functions and an `as_composite` +metafunction. Together, these helpers make composing a breeze. We'll provide an +[link phoenix.inside_phoenix.composing.composite_example example of a +composite] later to see why. + +[section compose] + + compose(arg0, arg1, arg2, ..., argN); + +Given an __eval_policy__ and some arguments `arg0`...argN, returns a proper +`composite`. The arguments may or may not be phoenix actors (primitives of +composites). If not, the arguments are converted to actors appropriately. For +example: + + compose(3) + +converts the argument `3` to an `actor >(3)`. + +[endsect] + +[section as_composite] + + as_composite::type + +This is the metafunction counterpart of `compose`. Given an __eval_policy__ and +some argument types `Arg0`...ArgN, returns a proper `composite` type. For +example: + + as_composite::type + +is the composite type of the `compose(3)` expression above. + +[endsect] + +[section Composite Example] + +Now, let's examine an example. Again, this is not a toy example. This is actually +part of the library. Remember the [link phoenix.composite.statement.while__statement +`while_`] lazy statement? Putting together everything we've learned so far, we +will present it here in its entirety (verbatim): + + struct while_eval + { + template + struct result + { + typedef void type; + }; + + template + static void + eval(Env const& env, Cond& cond, Do& do_) + { + while (cond.eval(env)) + do_.eval(env); + } + }; + + template + struct while_gen + { + while_gen(Cond const& cond) + : cond(cond) {} + + template + actor::type> + operator[](Do const& do_) const + { + return compose(cond, do_); + } + + Cond cond; + }; + + template + while_gen + while_(Cond const& cond) + { + return while_gen(cond); + } + +`while_eval` is an example of an __eval_policy__. `while_gen` and `while_` are +the expression template front ends. Let's break this apart to understand what's +happening. Let's start at the bottom. It's easier that way. + +When you write: + + while_(cond) + +we generate an instance of `while_gen`, where `Cond` is the type of +`cond`. `cond` can be an arbitrarily complex actor expression. The `while_gen` +template class has an `operator[]` accepting another expression. If we write: + + while_(cond) + [ + do_ + ] + +it will generate a proper composite with the type: + + as_composite::type + +where `Cond` is the type of `cond` and `Do` is the type of `do_`. Notice how we +are using phoenix's [link phoenix.inside_phoenix.composing composition] (`compose` +and `as_composite`) mechanisms here + + template + actor::type> + operator[](Do const& do_) const + { + return compose(cond, do_); + } + +Finally, the `while_eval` does its thing: + + while (cond.eval(env)) + do_.eval(env); + +`cond` and `do_`, at this point, are instances of __eval__. `cond` and `do_` are +the __eval__ elements held by the composite's __eval_tuple__. `env` is the +__environment__. + +[endsect] + +[endsect] + +[section Extending] + +We've shown how it is very easy to extend phoenix by writing new primitives and +composites. The modular design of Phoenix makes it extremely extensible. We have +seen that layer upon layer, the whole library is built on a solid foundation. +There are only a few simple well designed concepts that are laid out like +bricks. Overall, the library is designed to be extended. Everything above the +core layer can in fact be considered just as extensions to the library. This +modular design was inherited from the __spirit__ inline parser library. + +Extension is non-intrusive. And, whenever a component or module is extended, the +new extension automatically becomes a first class citizen and is automatically +recognized by all modules and components in the library. + +[endsect] + +[endsect] + +[section Wrap Up] + +Sooner or later more FP techniques become standard practice as people find the +true value of this programming discipline outside the academe and into the +mainstream. In as much as structured programming of the 70s and object oriented +programming in the 80s and generic programming in the 90s shaped our thoughts +towards a more robust sense of software engineering, FP will certainly be a +paradigm that will catapult us towards more powerful software design and +engineering onward into the new millenium. + +Let me quote Doug Gregor of Boost.org. About functional style programming +libraries: + +[:['They're gaining acceptance, but are somewhat stunted by the ubiquitousness +of broken compilers. The C++ community is moving deeper into the so-called "STL- +style" programming paradigm, which brings many aspects of functional programming +into the fold. Look at, for instance, the Spirit parser to see how such function +objects can be used to build Yacc-like grammars with semantic actions that can +build abstract syntax trees on the fly. This type of functional composition is +gaining momentum.]] + +Indeed. Phoenix is another attempt to introduce more FP techniques into the +mainstream. Not only is it a tool that will make life easier for the programmer. +In its own right, the actual design of the library itself is a model of true C++ +FP in action. The library is designed and structured in a strict but clear and +well mannered FP sense. By all means, use the library as a tool. But for those +who want to learn more about FP in C++, don't stop there, I invite you to take a +closer look at the design of the library itself. + +So there you have it. Have fun! See you in the FP world. + +[endsect] + +[section Acknowledgement] + +# Hartmut Kaiser implemented the original lazy casts and constructors based on + his original work on Spirit SE "semantic expressions" (the precursor to + Phoenix). +# Angus Leeming implemented the container functions on Phoenix-1 which I then + ported to Phoenix-2. +# Daniel Wallin helped with the scope module, local variables, let and lambda + and the algorithms. I frequently discuss design issues with Daniel on Yahoo Messenger. +# Jaakko Jarvi. DA Lambda MAN! +# Dave Abrahams, for his constant presence, wherever, whenever. +# Aleksey Gurtovoy, DA MPL MAN! +# Doug Gregor, always a source of inpiration. +# Dan Marsden, did almost all the work in bringing Phoenix-2 out the door. +# Eric Niebler did a 2.0 pre-release review and wrote some range related code + that Phoenix stole and used in the algorithms. +# Thorsten Ottosen; Eric's range_ex code began life as "container_algo" in the + old boost sandbox, by Thorsten in 2002-2003. +# Jeremy Siek, even prior to Thorsten, in 2001, started the "container_algo". +# Vladimir Prus wrote the mutating algorithms code from the Boost Wiki. +# Daryle Walker did a 2.0 pre-release review. + +[endsect] + +[section References] + +# Why Functional Programming Matters, John Hughes, 1989. + Available online at [@http://www.math.chalmers.se/~rjmh/Papers/whyfp.html]. +# Boost.Lambda library, Jaakko Jarvi, 1999-2004 Jaakko Jarvi, Gary Powell. + Available online at [@http://www.boost.org/libs/lambda/]. +# Functional Programming in C++ using the FC++ Library: a short article + introducing FC++, Brian McNamara and Yannis Smaragdakis, August 2003. Available + online at [@http://www.cc.gatech.edu/~yannis/fc++/]. +# Side-effects and partial function application in C++, Jaakko Jarvi and Gary + Powell, 2001. Available online at + [@http://osl.iu.edu/~jajarvi/publications/papers/mpool01.pdf]. +# Spirit Version 1.8.1, Joel de Guzman, Nov 2004. Available online at + [@http://www.boost.org/libs/spirit/]. +# The Boost MPL Library, Aleksey Gurtovoy and David Abrahams, 2002-2004. + Available online at [@http://www.boost.org/libs/mpl/]. +# Generic Programming Redesign of Patterns, Proceedings of the 5th European + Conference on Pattern Languages of Programs, (EuroPLoP'2000) Irsee, Germany, + July 2000. Available online at + [@http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip]. +# A Gentle Introduction to Haskell, Paul Hudak, John Peterson and Joseph Fasel, + 1999. Available online at [@http://www.haskell.org/tutorial/]. +# Large scale software design, John Lackos, ISBN 0201633620, Addison-Wesley, July + 1996. +# Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma, + Richard Helm, Ralph Jhonson, and John Vlissides, Addison-Wesley, 1995. +# The Forwarding Problem: Arguments Peter Dimov, Howard E. Hinnant, Dave + Abrahams, September 09, 2002. Available online: __forwarding__. + +[endsect] + diff --git a/phoenix/example/Jamfile.v2 b/phoenix/example/Jamfile.v2 new file mode 100644 index 000000000..c72426ade --- /dev/null +++ b/phoenix/example/Jamfile.v2 @@ -0,0 +1,24 @@ +#============================================================================== +# Copyright (c) 2003, 2005 Joel de Guzman +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +# bring in rules for testing +import testing ; + +test-suite users_manual : + [ run users_manual/all_odds.cpp ] + [ run users_manual/arguments.cpp ] + [ run users_manual/callback.cpp ] + [ run users_manual/factorial.cpp ] + [ run users_manual/find_if.cpp ] + [ run users_manual/function.cpp ] + [ run users_manual/if.cpp ] + [ run users_manual/lambda.cpp ] + [ run users_manual/values.cpp ] + [ run users_manual/references.cpp ] + ; + diff --git a/phoenix/example/users_manual/algorithm.cpp b/phoenix/example/users_manual/algorithm.cpp new file mode 100644 index 000000000..6eaddeea1 --- /dev/null +++ b/phoenix/example/users_manual/algorithm.cpp @@ -0,0 +1,21 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; + +int +main() +{ + int array[] = {1, 2, 3}; + int output[3]; + copy(arg1, arg2)(array, output); + return 0; +} diff --git a/phoenix/example/users_manual/all_odds.cpp b/phoenix/example/users_manual/all_odds.cpp new file mode 100644 index 000000000..b8c932bd1 --- /dev/null +++ b/phoenix/example/users_manual/all_odds.cpp @@ -0,0 +1,35 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 2, 10, 4, 5, 1, 6, 8, 3, 9, 7 }; + vector c(init, init + 10); + typedef vector::iterator iterator; + + // Print all odd contents of an stl container c + for_each(c.begin(), c.end(), + if_(arg1 % 2 == 1) + [ + cout << arg1 << ' ' + ] + ); + + return 0; +} diff --git a/phoenix/example/users_manual/arguments.cpp b/phoenix/example/users_manual/arguments.cpp new file mode 100644 index 000000000..0997a8f1e --- /dev/null +++ b/phoenix/example/users_manual/arguments.cpp @@ -0,0 +1,22 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int i = 3; + char const* s = "Hello World"; + cout << arg1(i) << endl; // prints 3 + cout << arg2(i, s) << endl; // prints "Hello World" + return 0; +} diff --git a/phoenix/example/users_manual/callback.cpp b/phoenix/example/users_manual/callback.cpp new file mode 100644 index 000000000..375117545 --- /dev/null +++ b/phoenix/example/users_manual/callback.cpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +template +void print(F f) +{ + cout << f() << endl; +} + +int +main() +{ + print(val(3)); + print(val("Hello World")); + return 0; +} diff --git a/phoenix/example/users_manual/factorial.cpp b/phoenix/example/users_manual/factorial.cpp new file mode 100644 index 000000000..a093edc29 --- /dev/null +++ b/phoenix/example/users_manual/factorial.cpp @@ -0,0 +1,40 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct factorial_impl +{ + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * this->operator()(n-1); + } +}; + +function factorial; + +int +main() +{ + int i = 4; + cout << factorial(arg1)(i) << endl; + return 0; +} diff --git a/phoenix/example/users_manual/find_if.cpp b/phoenix/example/users_manual/find_if.cpp new file mode 100644 index 000000000..d008b3977 --- /dev/null +++ b/phoenix/example/users_manual/find_if.cpp @@ -0,0 +1,30 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 2, 10, 4, 5, 1, 6, 8, 3, 9, 7 }; + vector c(init, init + 10); + typedef vector::iterator iterator; + + // Find the first odd number in container c + iterator it = find_if(c.begin(), c.end(), arg1 % 2 == 1); + + if (it != c.end()) + cout << *it; // if found, print the result + return 0; +} diff --git a/phoenix/example/users_manual/function.cpp b/phoenix/example/users_manual/function.cpp new file mode 100644 index 000000000..9701703a3 --- /dev/null +++ b/phoenix/example/users_manual/function.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct is_odd_ +{ + template + struct result + { + typedef bool type; + }; + + template + bool operator()(Arg arg1) const + { + return arg1 % 2 == 1; + } +}; + +function is_odd; + +int +main() +{ + int init[] = { 2, 10, 4, 5, 1, 6, 8, 3, 9, 7 }; + vector c(init, init + 10); + typedef vector::iterator iterator; + + // Find the first odd number in container c + iterator it = find_if(c.begin(), c.end(), is_odd(arg1)); + + if (it != c.end()) + cout << *it; // if found, print the result + return 0; +} diff --git a/phoenix/example/users_manual/if.cpp b/phoenix/example/users_manual/if.cpp new file mode 100644 index 000000000..967d6535e --- /dev/null +++ b/phoenix/example/users_manual/if.cpp @@ -0,0 +1,35 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + cout << dec; + int x = 0; + + for_each(v.begin(), v.end(), + if_(arg1 > 5) + [ + cout << arg1 << ", " + ] + ); + + return 0; +} diff --git a/phoenix/example/users_manual/lambda.cpp b/phoenix/example/users_manual/lambda.cpp new file mode 100644 index 000000000..52c6d4813 --- /dev/null +++ b/phoenix/example/users_manual/lambda.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::local_names; +using namespace std; + +namespace lazy_stuff +{ + struct for_each_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, F f) const + { + std::for_each(c.begin(), c.end(), f); + } + }; + + function const for_each = for_each_impl(); + + struct push_back_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, T& x) const + { + c.push_back(x); + } + }; + + function const push_back = push_back_impl(); +} + +int +main() +{ + { + using lazy_stuff::for_each; + using lazy_stuff::push_back; + + int x = 10; + std::vector > v(10); + + for_each(arg1, + lambda(_a = arg2) + [ + push_back(arg1, _a) + ] + ) + (v, x); + } + + return 0; +} + diff --git a/phoenix/example/users_manual/references.cpp b/phoenix/example/users_manual/references.cpp new file mode 100644 index 000000000..28f3cf1ed --- /dev/null +++ b/phoenix/example/users_manual/references.cpp @@ -0,0 +1,22 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int i = 3; + char const* s = "Hello World"; + cout << ref(i)() << endl; + cout << ref(s)() << endl; + return 0; +} diff --git a/phoenix/example/users_manual/values.cpp b/phoenix/example/users_manual/values.cpp new file mode 100644 index 000000000..bb196d7ed --- /dev/null +++ b/phoenix/example/users_manual/values.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + cout << val(3)() << endl; + cout << val("Hello World")() << endl; + return 0; +} diff --git a/phoenix/index.html b/phoenix/index.html new file mode 100644 index 000000000..b6ce29ba0 --- /dev/null +++ b/phoenix/index.html @@ -0,0 +1,10 @@ + + + + + + + Automatic redirection failed, click this + link + + diff --git a/phoenix/test/Jamfile.v2 b/phoenix/test/Jamfile.v2 new file mode 100644 index 000000000..62d5182bf --- /dev/null +++ b/phoenix/test/Jamfile.v2 @@ -0,0 +1,90 @@ +#============================================================================== +# Copyright (c) 2003-2006 Joel de Guzman +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +# bring in rules for testing +import testing ; + +local multi-threading = /boost/thread//boost_thread + multi BOOST_ALL_NO_LIB=1 ; + +test-suite phoenix_detail : + [ run detail/type_deduction_tests.cpp ] + ; + +test-suite phoenix_core : + [ run core/primitives_tests.cpp ] + [ run core/compose_tests.cpp ] + ; + +test-suite phoenix_operator : + [ run operator/arithmetic_tests.cpp ] + [ run operator/bitwise_tests.cpp ] + [ run operator/comparison_tests.cpp ] + [ run operator/if_else_tests.cpp ] + [ run operator/io_tests.cpp ] + [ run operator/logical_tests.cpp ] + [ run operator/misc_binary_tests.cpp ] + [ run operator/self_tests.cpp ] + [ run operator/unary_tests.cpp ] + [ run operator/member.cpp ] + ; + +test-suite phoenix_object : + [ run object/cast_tests.cpp ] + [ run object/new_delete_tests.cpp ] + ; + +test-suite phoenix_function : + [ run function/function_tests.cpp ] + ; + +test-suite phoenix_bind : + [ run bind/bind_function_tests.cpp ] + [ run bind/bind_function_object_tests.cpp ] + [ run bind/bind_member_function_tests.cpp ] + [ run bind/bind_member_variable_tests.cpp ] + ; + +test-suite phoenix_statement : + [ run statement/if_tests.cpp ] + [ run statement/loops_tests.cpp ] + [ run statement/switch_tests.cpp ] + [ run statement/exceptions.cpp ] + ; + +test-suite phoenix_container : + [ run container/container_tests1a.cpp ] + [ run container/container_tests1b.cpp ] + [ run container/container_tests2a.cpp ] + [ run container/container_tests2b.cpp ] + [ run container/container_tests3a.cpp ] + [ run container/container_tests3b.cpp ] + [ run container/container_tests4a.cpp ] + [ run container/container_tests4b.cpp ] + [ run container/container_tests5a.cpp ] + [ run container/container_tests5b.cpp ] + [ run container/container_tests6a.cpp ] + [ run container/container_tests6b.cpp ] + ; + +test-suite phoenix_scope : + [ run scope/lambda_tests.cpp ] + [ run scope/let_tests.cpp ] + [ run scope/dynamic_tests.cpp ] + [ run scope/bug_000008.cpp : : : $(multi-threading) ] + ; + +test-suite phoenix_algorithm : + [ run algorithm/iteration.cpp ] + [ run algorithm/transformation1.cpp ] + [ run algorithm/transformation2.cpp ] + [ run algorithm/transformation3.cpp ] + [ run algorithm/transformation4.cpp ] + [ run algorithm/querying.cpp ] + [ run algorithm/querying2.cpp ] + ; diff --git a/phoenix/test/algorithm/iteration.cpp b/phoenix/test/algorithm/iteration.cpp new file mode 100644 index 000000000..e1b12f795 --- /dev/null +++ b/phoenix/test/algorithm/iteration.cpp @@ -0,0 +1,57 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +namespace +{ + struct for_each_tester + { + int value_; + for_each_tester() : value_(0) { } + void operator()( + int& i) + { + value_ += i++; + return; + } + }; + + void for_each_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(for_each(arg1, for_each_tester())(array).value_ == 6); + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 3); + BOOST_TEST(array[2] == 4); + return; + } + + void accumulate_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(accumulate(arg1, 0)(array) == 6); + BOOST_TEST(boost::phoenix::accumulate(arg1, 0, std::minus())(array) == -6); + return; + } +} + +int main() +{ + for_each_test(); + accumulate_test(); + boost::report_errors(); +} diff --git a/phoenix/test/algorithm/querying.cpp b/phoenix/test/algorithm/querying.cpp new file mode 100644 index 000000000..04bb4f678 --- /dev/null +++ b/phoenix/test/algorithm/querying.cpp @@ -0,0 +1,270 @@ +/*============================================================================= + Copyright (c) 2005 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + Copyright (c) 2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include +#include + +namespace +{ + struct even + { + bool operator()(const int i) const + { + return i % 2 == 0; + } + }; + + struct mod_2_comparison + { + bool operator()( + const int lhs, + const int rhs) + { + return lhs % 2 == rhs % 2; + }; + }; + + void find_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(find(arg1,2)(array) == array + 1); + + std::set s(array, array + 3); + BOOST_TEST(find(arg1, 2)(s) == s.find(2)); + + return; + } + + + void find_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(find_if(arg1, even())(array) == array + 1); + return; + } + + void find_end_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,1,2,3,1}; + int pattern[] = {1,2,3}; + BOOST_TEST(find_end(arg1, arg2)(array, pattern) == array + 3); + int pattern2[] = {5,6,5}; + BOOST_TEST(find_end(arg1, arg2, mod_2_comparison())(array, pattern2) == array + 3); + return; + } + + void find_first_of_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int search_for[] = {2,3,4}; + BOOST_TEST(find_first_of(arg1, arg2)(array, search_for) == array + 1); + + int search_for2[] = {0}; + BOOST_TEST(find_first_of(arg1, arg2, mod_2_comparison())(array, search_for2) == array + 1); + return; + } + + void adjacent_find_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,1,3,4,4}; + BOOST_TEST(adjacent_find(arg1)(array) == array + 3); + BOOST_TEST(adjacent_find(arg1, mod_2_comparison())(array) == array + 1); + return; + } + + void count_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,1,0,1,1}; + BOOST_TEST(count(arg1, 1)(array) == 4); + return; + } + + void count_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,4,5}; + BOOST_TEST(count_if(arg1, even())(array) == 2); + return; + } + + void distance_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,1,0,1,1}; + BOOST_TEST(distance(arg1)(array) == 5); + return; + } + + void mismatch_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,4,5}; + int search[] = {1,2,4}; + + BOOST_TEST( + mismatch(arg1, arg2)(array, search) == + std::make_pair(array + 2, search + 2)); + int search2[] = {1,2,1,1}; + BOOST_TEST( + mismatch(arg1, arg2, mod_2_comparison())(array, search2) + == std::make_pair(array + 3, search2 + 3)); + + return; + } + + void equal_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {1,2,3}; + int array3[] = {1,2,4}; + BOOST_TEST( + equal(arg1, arg2)(array, array2)); + BOOST_TEST( + !equal(arg1, arg2)(array, array3)); + + BOOST_TEST( + equal(arg1, arg2, mod_2_comparison())(array, array2)); + BOOST_TEST( + !equal(arg1, arg2, mod_2_comparison())(array, array3)); + return; + } + + void search_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,1,2,3}; + int pattern[] = {2,3}; + BOOST_TEST( + search(arg1, arg2)(array, pattern) == array + 1); + int pattern2[] = {1,1}; + BOOST_TEST( + search(arg1, arg2, mod_2_comparison())(array, pattern2) == array + 2); + return; + } + + void lower_bound_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + const std::set test_set(array, array + 3); + BOOST_TEST(lower_bound(arg1, 2)(array) == array + 1); + BOOST_TEST(lower_bound(arg1, 2)(test_set) == test_set.lower_bound(2)); + + int array2[] = {3,2,1}; + const std::set > test_set2(array2, array2 + 3); + BOOST_TEST(boost::phoenix::lower_bound(arg1, 2, std::greater())(array2) == + array2 + 1); + BOOST_TEST(boost::phoenix::lower_bound(arg1, 2, std::greater())(test_set2) == + test_set2.lower_bound(2)); + return; + } + + void upper_bound_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + const std::set test_set(array, array + 3); + BOOST_TEST(upper_bound(arg1, 2)(array) == array + 2); + BOOST_TEST(upper_bound(arg1, 2)(test_set) == test_set.upper_bound(2)); + + int array2[] = {3,2,1}; + const std::set > test_set2(array2, array2 + 3); + BOOST_TEST(boost::phoenix::upper_bound(arg1, 2, std::greater())(array2) == + array2 + 2); + BOOST_TEST(boost::phoenix::upper_bound(arg1, 2, std::greater())(test_set2) == + test_set2.upper_bound(2)); + return; + } + + void equal_range_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,2,3}; + const std::set test_set(array, array + 4); + BOOST_TEST(equal_range(arg1, 2)(array).first == + array + 1); + BOOST_TEST(equal_range(arg1, 2)(array).second == + array + 3); + + BOOST_TEST(equal_range(arg1, 2)(test_set).first == + test_set.equal_range(2).first); + BOOST_TEST(equal_range(arg1, 2)(test_set).second == + test_set.equal_range(2).second); + + int array2[] = {3,2,2,1}; + const std::set > test_set2(array2, array2 + 4); + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(array2).first == + array2 + 1); + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(array2).second == + array2 + 3); + + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(test_set2).first == + test_set2.equal_range(2).first); + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(test_set2).second == + test_set2.equal_range(2).second); + + return; + } + + void binary_search_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(binary_search(arg1, 2)(array)); + BOOST_TEST(!binary_search(arg1, 4)(array)); + return; + } + +} + +int main() +{ + find_test(); + find_if_test(); + find_end_test(); + find_first_of_test(); + adjacent_find_test(); + count_test(); + count_if_test(); + distance_test(); + mismatch_test(); + equal_test(); + search_test(); + lower_bound_test(); + upper_bound_test(); + equal_range_test(); + binary_search_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/querying2.cpp b/phoenix/test/algorithm/querying2.cpp new file mode 100644 index 000000000..a11bca955 --- /dev/null +++ b/phoenix/test/algorithm/querying2.cpp @@ -0,0 +1,83 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +#include + +namespace +{ + void includes_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {1,2}; + BOOST_TEST(includes(arg1, arg2)(array, array2)); + boost::iterator_range rng(array + 1, array + 3); + BOOST_TEST(!includes(arg1, arg2)(rng, array2)); + + int array3[] = {3,2,1}; + int array4[] = {2,1}; + BOOST_TEST(boost::phoenix::includes(arg1, arg2, std::greater())(array3, array4)); + boost::iterator_range rng2(array3, array3 + 2); + BOOST_TEST(!boost::phoenix::includes(arg1, arg2, std::greater())(rng2, array4)); + return; + } + + void min_element_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,3,2}; + BOOST_TEST(min_element(arg1)(array) == array); + BOOST_TEST(boost::phoenix::min_element(arg1, std::greater())(array) == array + 1); + return; + } + + void max_element_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,3,2}; + BOOST_TEST(max_element(arg1)(array) == array + 1); + BOOST_TEST(boost::phoenix::max_element(arg1, std::greater())(array) == array); + return; + } + + void lexicographical_compare_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {1,2,4}; + + BOOST_TEST(lexicographical_compare(arg1, arg2)(array, array2)); + BOOST_TEST(!lexicographical_compare(arg1, arg2)(array2, array)); + BOOST_TEST(!lexicographical_compare(arg1, arg2)(array, array)); + + BOOST_TEST(!boost::phoenix::lexicographical_compare(arg1, arg2, std::greater())(array, array2)); + BOOST_TEST(boost::phoenix::lexicographical_compare(arg1, arg2, std::greater())(array2, array)); + BOOST_TEST(!boost::phoenix::lexicographical_compare(arg1, arg2, std::greater())(array, array)); + + return; + } +} + +int main() +{ + includes_test(); + min_element_test(); + max_element_test(); + lexicographical_compare_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation1.cpp b/phoenix/test/algorithm/transformation1.cpp new file mode 100644 index 000000000..cca0858c2 --- /dev/null +++ b/phoenix/test/algorithm/transformation1.cpp @@ -0,0 +1,390 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include +#include + +namespace +{ + struct even + { + bool operator()(const int i) const + { + return i % 2 == 0; + } + }; + + struct mod_2_comparison + { + bool operator()( + const int lhs, + const int rhs) + { + return lhs % 2 == rhs % 2; + }; + }; + + void swap_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int a = 123; + int b = 456; + swap(ref(a), ref(b))(); + BOOST_TEST(a == 456 && b == 123); + swap(ref(a), _1)(b); + BOOST_TEST(a == 123 && b == 456); + swap(_1, _2)(a, b); + BOOST_TEST(a == 456 && b == 123); + return; + } + + void copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[4]; + BOOST_TEST( + copy(arg1, arg2)(array, output) == output + 3); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 2); + BOOST_TEST(output[2] == 3); + return; + } + + void copy_backward_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[4]; + int* output_end = output + 3; + BOOST_TEST( + copy_backward(arg1, arg2)(array, output_end) == output); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 2); + BOOST_TEST(output[2] == 3); + return; + } + + struct increment + { + int operator()( + int i) const + { + return i+1; + } + }; + + void transform_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST( + transform(arg1, arg2, increment())(array, array) == + array + 3); + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 3); + BOOST_TEST(array[2] == 4); + + int array2[] = {1,2,3}; + BOOST_TEST( + boost::phoenix::transform(arg1, arg2, arg3, std::plus())(array, array2, array) == + array +3); + BOOST_TEST(array[0] == 2 + 1); + BOOST_TEST(array[1] == 3 + 2); + BOOST_TEST(array[2] == 4 + 3); + return; + } + + void replace_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + replace(arg1,2,4)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 4); + BOOST_TEST(array[2] == 3); + return; + } + + void replace_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + replace_if(arg1, even(), 4)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 4); + BOOST_TEST(array[2] == 3); + return; + } + + void replace_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int input[] = {1,2,3}; + int output[3]; + replace_copy(arg1, arg2, 2, 4)(input, output); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 4); + BOOST_TEST(output[2] == 3); + return; + } + + void replace_copy_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int input[] = {1,2,3}; + int output[3]; + replace_copy_if(arg1, arg2, even(), 4)(input, output); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 4); + BOOST_TEST(output[2] == 3); + return; + } + + void fill_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,0,0}; + fill(arg1, 1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 1); + return; + } + + void fill_n_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,0,0}; + fill_n(arg1, 2, 1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 0); + return; + } + + class int_seq + { + public: + int_seq() : val_(0) { } + + int operator()() + { + return val_++; + } + private: + int val_; + }; + + void generate_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[3]; + generate(arg1, int_seq())(array); + BOOST_TEST(array[0] == 0); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 2); + return; + } + + void generate_n_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,0,1}; + generate_n(arg1, 2, int_seq())(array); + BOOST_TEST(array[0] == 0); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 1); + return; + } + + + void remove_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::list test_list(array, array + 3); + BOOST_TEST(boost::phoenix::remove(arg1, 2)(array) == array + 2); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 3); + BOOST_TEST(boost::phoenix::remove(arg1, 2)(test_list) == test_list.end()); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 3); + return; + } + + void remove_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::list test_list(array, array + 3); + BOOST_TEST(boost::phoenix::remove_if(arg1, even())(array) == array + 2); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 3); + BOOST_TEST(boost::phoenix::remove_if(arg1, even())(test_list) == test_list.end()); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 3); + return; + } + + void remove_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[2]; + BOOST_TEST(boost::phoenix::remove_copy(arg1, arg2, 2)(array, array2) == array2 + 2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 3); + return; + } + + void remove_copy_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[2]; + BOOST_TEST(boost::phoenix::remove_copy_if(arg1, arg2, even())(array, array2) == array2 + 2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 3); + return; + } + + void unique_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,2,3}; + std::list test_list(array, array + 4); + BOOST_TEST(unique(arg1)(array) == array + 3); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + BOOST_TEST(unique(arg1)(test_list) == test_list.end()); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 2); + BOOST_TEST(*it++ == 3); + + int array2[] = {1,3,2}; + std::list test_list2(array2, array2 + 3); + BOOST_TEST(unique(arg1, mod_2_comparison())(array2) == array2 + 2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 2); + + BOOST_TEST(unique(arg1, mod_2_comparison())(test_list2) == test_list2.end()); + std::list::const_iterator jt(test_list2.begin()); + BOOST_TEST(*jt++ == 1); + BOOST_TEST(*jt++ == 2); + + return; + } + + void unique_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,2,3}; + int out[3]; + BOOST_TEST(unique_copy(arg1, arg2)(array, out) == out + 3); + BOOST_TEST(out[0] == 1); + BOOST_TEST(out[1] == 2); + BOOST_TEST(out[2] == 3); + + int array2[] = {1,3,2}; + int out2[2]; + BOOST_TEST(unique_copy(arg1, arg2, mod_2_comparison())(array2, out2) == out2 + 2); + BOOST_TEST(out2[0] == 1); + BOOST_TEST(out2[1] == 2); + + return; + } + + void reverse_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::list test_list(array, array + 3); + reverse(arg1)(array); + BOOST_TEST(array[0] == 3); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 1); + + reverse(arg1)(test_list); + std::list::iterator it(test_list.begin()); + BOOST_TEST(*it++ == 3); + BOOST_TEST(*it++ == 2); + BOOST_TEST(*it++ == 1); + return; + } + + void reverse_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[3]; + reverse_copy(arg1, arg2)(array, array2); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + BOOST_TEST(array2[0] == 3); + BOOST_TEST(array2[1] == 2); + BOOST_TEST(array2[2] == 1); + + return; + } +} + +int main() +{ + copy_test(); + copy_backward_test(); + transform_test(); + replace_test(); + replace_if_test(); + replace_copy_test(); + replace_copy_if_test(); + fill_test(); + fill_n_test(); + generate_test(); + generate_n_test(); + remove_test(); + remove_if_test(); + remove_copy_test(); + remove_copy_if_test(); + unique_test(); + unique_copy_test(); + reverse_test(); + reverse_copy_test(); + boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation2.cpp b/phoenix/test/algorithm/transformation2.cpp new file mode 100644 index 000000000..a1a27c0aa --- /dev/null +++ b/phoenix/test/algorithm/transformation2.cpp @@ -0,0 +1,188 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +namespace +{ + struct even + { + bool operator()(const int i) const + { + return i % 2 == 0; + } + }; + + void rotate_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + rotate(arg1, array + 1)(array); + std::cout << array[0] << array[1] << array[2] << std::endl; + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 3); + BOOST_TEST(array[2] == 1); + + return; + } + + void rotate_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[3]; + rotate_copy(arg1, array + 1, arg2)(array, array2); + BOOST_TEST(array2[0] == 2); + BOOST_TEST(array2[1] == 3); + BOOST_TEST(array2[2] == 1); + + return; + } + + void random_shuffle_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + random_shuffle(arg1)(array); + const int first = array[0]; + BOOST_TEST(first == 1 || first == 2 || first == 3); + const int second = array[1]; + BOOST_TEST(second == 1 || second == 2 || second == 3); + BOOST_TEST(first != second); + const int third = array[2]; + BOOST_TEST(third == 1 || third == 2 || third == 3); + BOOST_TEST(first != third && second != third); + return; + } + + void partition_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int* const end = partition(arg1, even())(array); + BOOST_TEST(end == array + 1); + BOOST_TEST(array[0] % 2 == 0); + BOOST_TEST(array[1] % 2 != 0); + BOOST_TEST(array[2] % 2 != 0); + return; + } + + void stable_partition_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int* const end = stable_partition(arg1, even())(array); + BOOST_TEST(end == array + 1); + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 3); + return; + } + + void sort_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {3,1,2}; + std::list test_list(array, array + 3); + sort(arg1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + sort(arg1)(test_list); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 2); + BOOST_TEST(*it++ == 3); + + boost::phoenix::sort(arg1, std::greater())(array); + BOOST_TEST(array[0] == 3); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 1); + + boost::phoenix::sort(arg1, std::greater())(test_list); + std::list::const_iterator jt(test_list.begin()); + BOOST_TEST(*jt++ == 3); + BOOST_TEST(*jt++ == 2); + BOOST_TEST(*jt++ == 1); + + return; + } + + void stable_sort_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {3,1,2}; + stable_sort(arg1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + boost::phoenix::stable_sort(arg1, std::greater())(array); + BOOST_TEST(array[0] == 3); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 1); + + return; + } + + void partial_sort_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {2,4,1,3}; + partial_sort(arg1, array + 2)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + + boost::phoenix::partial_sort(arg1, array + 2, std::greater())(array); + BOOST_TEST(array[0] == 4); + BOOST_TEST(array[1] == 3); + return; + } + + void partial_sort_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {2,4,1,3}; + int array2[2]; + partial_sort_copy(arg1, arg2)(array, array2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 2); + + boost::phoenix::partial_sort(arg1, arg2, std::greater())(array, array2); + BOOST_TEST(array2[0] == 4); + BOOST_TEST(array2[1] == 3); + return; + } +} + +int main() +{ + rotate_test(); + rotate_copy_test(); + random_shuffle_test(); + partition_test(); + stable_partition_test(); + sort_test(); + stable_sort_test(); + partial_sort_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation3.cpp b/phoenix/test/algorithm/transformation3.cpp new file mode 100644 index 000000000..9a0e22fb9 --- /dev/null +++ b/phoenix/test/algorithm/transformation3.cpp @@ -0,0 +1,176 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +namespace +{ + void nth_element_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {5,1,4,3,2}; + nth_element(arg1, array + 2)(array); + BOOST_TEST(array[0] < 3); + BOOST_TEST(array[1] < 3); + BOOST_TEST(array[2] == 3); + BOOST_TEST(array[3] > 3); + BOOST_TEST(array[4] > 3); + + boost::phoenix::nth_element(arg1, array + 2, std::greater())(array); + BOOST_TEST(array[0] > 3); + BOOST_TEST(array[1] > 3); + BOOST_TEST(array[2] == 3); + BOOST_TEST(array[3] < 3); + BOOST_TEST(array[4] < 3); + + return; + } + + void merge_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[6]; + + BOOST_TEST(merge(arg1, arg2, arg3)(array, array2, output) == output + 6); + int expected_result[] = {1,2,2,3,3,4}; + BOOST_TEST(std::equal(output, output + 6, expected_result)); + + int array3[] = {5,4,3}; + int array4[] = {3,2,1}; + int output2[6]; + BOOST_TEST(boost::phoenix::merge(arg1, arg2, arg3, std::greater())(array3, array4, output2) == + output2 + 6); + int expected_result2[] = {5,4,3,3,2,1}; + BOOST_TEST(std::equal(output2, output2 + 6, expected_result2)); + return; + } + + void inplace_merge_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,2,3,4}; + inplace_merge(arg1, array + 3)(array); + int expected_result[] = {1,2,2,3,3,4}; + BOOST_TEST(std::equal(array, array + 6, expected_result)); + + int array2[] = {5,4,3,4,3,2}; + boost::phoenix::inplace_merge(arg1, array2 + 3, std::greater())(array2); + int expected_result2[] = {5,4,4,3,3,2}; + BOOST_TEST(std::equal(array2, array2 + 6, expected_result2)); + return; + } + + void set_union_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[4]; + BOOST_TEST(set_union(arg1, arg2, arg3)(array, array2, output) == output + 4); + int expected_result[] = {1,2,3,4}; + BOOST_TEST(std::equal(output, output + 4, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[4]; + BOOST_TEST(boost::phoenix::set_union(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 4); + int expected_result2[] = {4,3,2,1}; + BOOST_TEST(std::equal(output2, output2 + 4, expected_result2)); + return; + } + + void set_intersection_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[2]; + BOOST_TEST(set_intersection(arg1, arg2, arg3)(array, array2, output) == output + 2); + int expected_result[] = {2,3}; + BOOST_TEST(std::equal(output, output + 2, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[2]; + BOOST_TEST(boost::phoenix::set_intersection(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 2); + int expected_result2[] = {3,2}; + BOOST_TEST(std::equal(output2, output2 + 2, expected_result2)); + return; + } + + void set_difference_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[1]; + BOOST_TEST(set_difference(arg1, arg2, arg3)(array, array2, output) == output + 1); + int expected_result[] = {1}; + BOOST_TEST(std::equal(output, output + 1, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[1]; + BOOST_TEST(boost::phoenix::set_difference(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 1); + int expected_result2[] = {1}; + BOOST_TEST(std::equal(output2, output2 + 1, expected_result2)); + return; + } + + void set_symmetric_difference_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[2]; + BOOST_TEST(set_symmetric_difference(arg1, arg2, arg3)(array, array2, output) == output + 2); + int expected_result[] = {1,4}; + BOOST_TEST(std::equal(output, output + 2, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[2]; + BOOST_TEST(boost::phoenix::set_symmetric_difference(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 2); + int expected_result2[] = {4,1}; + BOOST_TEST(std::equal(output2, output2 + 2, expected_result2)); + return; + } +} + +int main() +{ + nth_element_test(); + merge_test(); + inplace_merge_test(); + set_union_test(); + set_intersection_test(); + set_difference_test(); + set_symmetric_difference_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation4.cpp b/phoenix/test/algorithm/transformation4.cpp new file mode 100644 index 000000000..4dd157d9e --- /dev/null +++ b/phoenix/test/algorithm/transformation4.cpp @@ -0,0 +1,153 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include +#include +#include + +namespace +{ + void heap_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::vector vec(array, array + 3); + boost::phoenix::make_heap(arg1)(vec); + vec.push_back(5); + boost::phoenix::push_heap(arg1)(vec); + vec.push_back(4); + boost::phoenix::push_heap(arg1)(vec); + boost::phoenix::pop_heap(arg1)(vec); + BOOST_TEST(vec.back() == 5); + vec.pop_back(); + boost::phoenix::sort_heap(arg1)(vec); + int expected_result[] = {1,2,3,4}; + BOOST_TEST(std::equal(vec.begin(), vec.end(), expected_result)); + + int array2[] = {3,2,1}; + std::vector vec2(array2, array2 + 3); + boost::phoenix::make_heap(arg1, std::greater())(vec2); + vec2.push_back(5); + boost::phoenix::push_heap(arg1, std::greater())(vec2); + vec2.push_back(4); + boost::phoenix::push_heap(arg1, std::greater())(vec2); + boost::phoenix::pop_heap(arg1, std::greater())(vec2); + BOOST_TEST(vec2.back() == 1); + vec2.pop_back(); + boost::phoenix::sort_heap(arg1, std::greater())(vec2); + int expected_result2[] = {5,4,3,2}; + BOOST_TEST(std::equal(vec2.begin(), vec2.end(), expected_result2)); + + return; + } + + void next_permutation_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2}; + int expected_result[] = {2,1}; + int expected_result2[] = {1,2}; + + BOOST_TEST(next_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + BOOST_TEST(!next_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + + std::reverse(array, array + 2); + BOOST_TEST(boost::phoenix::next_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + BOOST_TEST(!boost::phoenix::next_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + return; + } + + void prev_permutation_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {2,1}; + int expected_result[] = {1,2}; + int expected_result2[] = {2,1}; + + BOOST_TEST(prev_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + BOOST_TEST(!prev_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + + std::reverse(array, array + 2); + BOOST_TEST(boost::phoenix::prev_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + BOOST_TEST(!boost::phoenix::prev_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + return; + } + + void inner_product_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int lhs[] = {1,2,3}; + int rhs[] = {4,5,6}; + BOOST_TEST(inner_product(arg1, arg2, 0) + (lhs, rhs) == 1*4 + 2*5 + 3*6); + BOOST_TEST(boost::phoenix::inner_product(arg1, arg2, 1, std::multiplies(), std::minus()) + (lhs, rhs) == (1 - 4) * (2 - 5) * (3 - 6)); + return; + } + + void partial_sum_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[3]; + BOOST_TEST(partial_sum(arg1, arg2)(array, output) == output + 3); + int expected_result[] = {1, 3, 6}; + BOOST_TEST(std::equal(output, output + 3, expected_result)); + + BOOST_TEST(boost::phoenix::partial_sum(arg1, arg2, std::multiplies()) + (array, output) == output + 3); + int expected_result2[] = {1, 2, 6}; + BOOST_TEST(std::equal(output, output + 3, expected_result2)); + return; + } + + void adjacent_difference_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[3]; + BOOST_TEST(adjacent_difference(arg1, arg2)(array, output) == output + 3); + int expected_result[] = {1, 1, 1}; + BOOST_TEST(std::equal(output, output + 3, expected_result)); + BOOST_TEST(boost::phoenix::adjacent_difference(arg1, arg2, std::plus()) + (array, output) == output + 3); + int expected_result2[] = {1, 3, 5}; + BOOST_TEST(std::equal(output, output + 3, expected_result2)); + return; + } + +} + +int main() +{ + heap_test(); + next_permutation_test(); + prev_permutation_test(); + inner_product_test(); + partial_sum_test(); + adjacent_difference_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_function_object_tests.cpp b/phoenix/test/bind/bind_function_object_tests.cpp new file mode 100644 index 000000000..6ae8a801f --- /dev/null +++ b/phoenix/test/bind/bind_function_object_tests.cpp @@ -0,0 +1,106 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + + struct test + { + typedef void result_type; + void operator()() const + { + cout << "Test lazy functions...\n"; + } + }; + + struct sqr + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return n * n; + } + }; + + struct fact + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * (*this)(n-1); + } + }; + + struct power + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b) const + { + return pow(a, b); + } + }; + + struct add + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b, Arg3 c, Arg4 d) const + { + return a + b + c + d; + } + }; + +int +main() +{ + int i5 = 5; + double d5 = 5, d3 = 3; + + test()(); + BOOST_TEST(bind(sqr(), arg1)(i5) == (i5*i5)); + BOOST_TEST(bind(fact(), 4)() == 24); + BOOST_TEST(bind(fact(), arg1)(i5) == 120); + BOOST_TEST((int)bind(power(), arg1, arg2)(d5, d3) == (int)pow(d5, d3)); + BOOST_TEST((bind(sqr(), arg1) + 5)(i5) == ((i5*i5)+5)); + BOOST_TEST(bind(add(), arg1, arg1, arg1, arg1)(i5) == (5+5+5+5)); + + int const ic5 = 5; + // testing consts + BOOST_TEST(bind(sqr(), arg1)(ic5) == (ic5*ic5)); + + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_function_tests.cpp b/phoenix/test/bind/bind_function_tests.cpp new file mode 100644 index 000000000..c55b51818 --- /dev/null +++ b/phoenix/test/bind/bind_function_tests.cpp @@ -0,0 +1,57 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +namespace test +{ + void + test() + { + cout << "Test binding functions...\n"; + } + + int + negate(int n) + { + return -n; + } + + int + plus(int a, int b) + { + return a + b; + } + + int + plus4(int a, int b, int c, int d) + { + return a + b + c + d; + } +} + +int +main() +{ + int a = 123; + int b = 256; + + bind(test::test)(); + BOOST_TEST(bind(test::negate, arg1)(a) == -a); + BOOST_TEST(bind(test::plus, arg1, arg2)(a, b) == a+b); + BOOST_TEST(bind(test::plus4, arg1, arg2, 3, 4)(a, b) == a+b+3+4); + + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_member_function_tests.cpp b/phoenix/test/bind/bind_member_function_tests.cpp new file mode 100644 index 000000000..552a8305e --- /dev/null +++ b/phoenix/test/bind/bind_member_function_tests.cpp @@ -0,0 +1,76 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; +namespace phx = boost::phoenix; + +namespace test +{ + struct x : boost::noncopyable // test non-copyable (hold this by reference) + { + void + test() const + { + cout << "Test binding member functions...\n"; + } + }; + + struct y : boost::noncopyable // test non-copyable (hold this by reference) + { + int + negate(int n) + { + return -n; + } + }; + + struct z : boost::noncopyable // test non-copyable (hold this by reference) + { + int + plus(int a, int b) + { + return a + b; + } + }; + + struct zz : boost::noncopyable // test non-copyable (hold this by reference) + { + int + plus3(int a, int b, int c) + { + return a + b + c; + } + }; +} + +int +main() +{ + int a = 123; + int b = 256; + test::x x_; + test::y y_; + test::z z_; + test::zz zz_; + + bind(&test::x::test, x_)(); + BOOST_TEST(bind(&test::y::negate, y_, arg1)(a) == -a); + BOOST_TEST(bind(&test::z::plus, arg1, arg2, arg3)(z_, a, b) == a+b); + BOOST_TEST(bind(&test::zz::plus3, zz_, arg1, arg2, arg3)(a, b, a) == a+b+a); + BOOST_TEST(bind(&test::y::negate, &y_, 777)(a) == -777); + + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_member_variable_tests.cpp b/phoenix/test/bind/bind_member_variable_tests.cpp new file mode 100644 index 000000000..29b11652b --- /dev/null +++ b/phoenix/test/bind/bind_member_variable_tests.cpp @@ -0,0 +1,37 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; +namespace phx = boost::phoenix; + +namespace test +{ + struct x : boost::noncopyable // test non-copyable (hold this by reference) + { + int m; + }; +} + +int +main() +{ + test::x x_; + bind(&test::x::m, x_)() = 123; + bind(&test::x::m, arg1)(x_) = 123; + BOOST_TEST(x_.m == 123); + + return boost::report_errors(); +} diff --git a/phoenix/test/container/container_tests.hpp b/phoenix/test/container/container_tests.hpp new file mode 100644 index 000000000..cf37f83ba --- /dev/null +++ b/phoenix/test/container/container_tests.hpp @@ -0,0 +1,814 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#if !defined(CONTAINER_TESTS_HPP) +#define CONTAINER_TESTS_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_MSVC +#pragma warning(disable : 4800) +#endif + +using std::cerr; +namespace phx = boost::phoenix; + +std::deque const build_deque(); +std::list const build_list(); +std::map const build_map(); +std::multimap const build_multimap(); +std::vector const build_vector(); + +inline bool +test(bool fail) +{ + BOOST_TEST(!fail); + return fail; +} + +template +void test_assign(Container c) +{ + using phx::arg_names::arg1; + using phx::assign; + + typename Container::size_type count = 2; + typename Container::const_iterator first = c.begin(); + typename Container::const_iterator second = first; + typename Container::value_type value = *first; + + assign(arg1, count, value)(c); + + // iterators may be invalidated! + first = c.begin(); + second = first; + + std::advance(second, 1); + if (test(*first != *second)) { + cerr << "Failed " << typeid(Container).name() << " test_assign 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + Container const const_c = c; + assign(const_c, count, value); +#endif +} + +template +void test_assign2(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + using phx::assign; + + Container c2 = c; + typename Container::const_iterator first = c2.begin(); + typename Container::const_iterator last = c2.end(); + typename Container::size_type size = c2.size(); + + c.clear(); + assign(arg1, arg2, arg3)(c, first, last); + if (test(c.size() != size)) { + cerr << "Failed " << typeid(Container).name() + << " test_assign2 1\n" + << "size == " << c.size() << '\n'; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + Container const const_c = c; + assign(const_c, first, second); +#endif +} + +template +void test_at(Container c) +{ + using phx::arg_names::arg1; + using phx::at; + + typename Container::reference r1 = at(arg1, 0)(c); + if (test(r1 != c.at(0))) { + cerr << "Failed " << typeid(Container).name() << " test_at 1\n"; + return; + } + + typename Container::const_reference r2 = at(arg1, 0)(c); + if (test(r2 != c.at(0))) { + cerr << "Failed " << typeid(Container).name() << " test_at 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reference r3 = at(arg1, 0)(const_c); +#endif + + typename Container::const_reference r4 = at(arg1, 0)(const_c); + if (test(r4 != c.at(0))) { + cerr << "Failed " << typeid(Container).name() << " test_at 4\n"; + return; + } +} + +template +void test_back(Container c) +{ + using phx::arg_names::arg1; + using phx::back; + + typename Container::reference r1 = back(arg1)(c); + if (test(r1 != c.back())) { + cerr << "Failed " << typeid(Container).name() << " test_back 1\n"; + return; + } + typename Container::const_reference r2 = back(arg1)(c); + if (test(r2 != c.back())) { + cerr << "Failed " << typeid(Container).name() << " test_back 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reference r3 = back(arg1)(const_c); +#endif + + typename Container::const_reference r4 = back(arg1)(const_c); + if (test(r4 != c.back())) { + cerr << "Failed " << typeid(Container).name() << " test_back 4\n"; + return; + } +} + +template +void test_begin(Container c) +{ + using phx::arg_names::arg1; + using phx::begin; + + typename Container::iterator it1 = begin(arg1)(c); + if (test(it1 != c.begin())) { + cerr << "Failed " << typeid(Container).name() << " test_begin 1\n"; + return; + } + typename Container::const_iterator it2 = begin(arg1)(c); + if (test(it2 != c.begin())) { + cerr << "Failed " << typeid(Container).name() << " test_begin 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::iterator it3 = begin(arg1)(const_c); +#endif + + typename Container::const_iterator it4 = begin(arg1)(const_c); + if (test(it4 != const_c.begin())) { + cerr << "Failed " << typeid(Container).name() << " test_begin 4\n"; + return; + } +} + +template +void test_capacity(Container c) +{ + using phx::arg_names::arg1; + using phx::capacity; + + typename Container::size_type s1 = capacity(arg1)(c); + if (test(s1 != c.capacity())) { + cerr << "Failed " << typeid(Container).name() << " test_capacity 1\n"; + return; + } + + Container const const_c = c; + typename Container::size_type s2 = capacity(arg1)(const_c); + if (test(s2 != const_c.capacity())) { + cerr << "Failed " << typeid(Container).name() << " test_capacity 2\n"; + return; + } +} + +template +void test_clear(Container c) +{ + using phx::arg_names::arg1; + using phx::clear; + + clear(arg1)(c); + if (test(!c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_clear 1\n"; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + Container const const_c = c; + clear(arg1)(const_c); +#endif +} + +template +void test_empty(Container c) +{ + using phx::arg_names::arg1; + using phx::empty; + + typename Container::size_type s1 = empty(arg1)(c); + if (test(bool(s1) != c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_empty 1\n"; + return; + } + + Container const const_c = c; + typename Container::size_type s2 = empty(arg1)(const_c); + if (test(bool(s2) != const_c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_empty 2\n"; + return; + } +} + +template +void test_end(Container c) +{ + using phx::arg_names::arg1; + using phx::end; + + typename Container::iterator it1 = end(arg1)(c); + if (test(it1 != c.end())) { + cerr << "Failed " << typeid(Container).name() << " test_end 1\n"; + return; + } + typename Container::const_iterator it2 = end(arg1)(c); + if (test(it2 != c.end())) { + cerr << "Failed " << typeid(Container).name() << " test_end 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::iterator it3 = end(arg1)(const_c); +#endif + + typename Container::const_iterator it4 = end(arg1)(const_c); + if (test(it4 != const_c.end())) { + cerr << "Failed " << typeid(Container).name() << " test_end 4\n"; + return; + } +} + +template +void test_erase(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + using phx::erase; + + Container const const_c = c; + + typename Container::size_type size = c.size(); + typename Container::iterator c_begin = c.begin(); + erase(arg1, arg2)(c, c_begin); + if (test(c.size() + 1 != size)) { + cerr << "Failed " << typeid(Container).name() << " test_erase 1\n"; + return; + } + + c_begin = c.begin(); + typename Container::iterator c_end = c.end(); + erase(arg1, arg2, arg3)(c, c_begin, c_end); + if (test(!c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_erase 2\n"; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + erase(arg1, const_c.begin())(const_c); + erase(arg1, const_c.begin(), const_c.end())(const_c); +#endif +} + +template +void test_map_erase(Container c) +{ + test_erase(c); + if (boost::report_errors() != 0) + return; + + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::erase; + + typename Container::value_type const value = *c.begin(); + typename Container::key_type const key = value.first; + typename Container::size_type const removed = + erase(arg1, arg2)(c, key); + if (test(removed != 1)) { + cerr << "Failed " << typeid(Container).name() << " test_map_erase 1\n"; + return; + } +} + +template +void test_front(Container c) +{ + using phx::arg_names::arg1; + using phx::front; + + typename Container::reference r1 = front(arg1)(c); + if (test(r1 != c.front())) { + cerr << "Failed " << typeid(Container).name() << " test_front 1\n"; + return; + } + typename Container::const_reference r2 = front(arg1)(c); + if (test(r2 != c.front())) { + cerr << "Failed " << typeid(Container).name() << " test_front 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reference r3 = front(arg1)(const_c); +#endif + + typename Container::const_reference r4 = front(arg1)(const_c); + if (test(r4 != c.front())) { + cerr << "Failed " << typeid(Container).name() << " test_front 4\n"; + return; + } +} + +template +void test_get_allocator(Container c) +{ + using phx::arg_names::arg1; + using phx::get_allocator; + + Container const const_c = c; + + typename Container::allocator_type a1 = get_allocator(arg1)(c); + if (test(a1 != c.get_allocator())) { + cerr << "Failed " << typeid(Container).name() << " test_get_allocator 1\n"; + return; + } + + typename Container::allocator_type a2 = get_allocator(arg1)(const_c); + if (test(a2 != const_c.get_allocator())) { + cerr << "Failed " << typeid(Container).name() << " test_get_allocator 2\n"; + return; + } +} + +template +void test_insert(Container c) +{ + using phx::arg_names::arg1; + using phx::insert; + + typename Container::value_type const value = *c.begin(); + typename Container::iterator it = insert(arg1, c.begin(), value)(c); + if (test(it != c.begin() || *it != *(++it))) { + cerr << "Failed " << typeid(Container).name() << " test_insert 1\n"; + return; + } + + typename Container::size_type size = c.size(); + insert(arg1, c.begin(), 3, value)(c); + if (test(c.size() != size + 3)) { + cerr << "Failed " << typeid(Container).name() << " test_insert 2\n"; + return; + } + + Container const const_c = c; + size = c.size(); + insert(arg1, c.begin(), const_c.begin(), const_c.end())(c); + if (test(c.size() != 2 * size)) { + cerr << "Failed " << typeid(Container).name() << " test_insert 3\n"; + return; + } +} + +inline void test_map_insert(std::map c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + + typedef std::map Map; + + Map::value_type const value = *c.begin(); + Map::iterator c_begin = c.begin(); + // wrapper for + // iterator insert(iterator where, const value_type& val); + Map::iterator it = + phx::insert(arg1, arg2, arg3)(c, c_begin, value); + + if (test(it != c.begin() /*|| *it != *(++it)*/)) { + cerr << "Failed " << typeid(Map).name() << " test_map_insert 1\n"; + return; + } + + // wrapper for + // pair insert(const value_type& val); + Map::value_type const value2(1400, 2200); + std::pair result = + phx::insert(arg1, arg2)(c, value2); + if (test(!result.second)) { + cerr << "Failed " << typeid(Map).name() << " test_map_insert 2\n"; + return; + } + + // wrapper for + // template + // void insert(InIt first, InIt last); + Map const const_c = build_map(); + Map::size_type size = c.size(); + phx::insert(arg1, const_c.begin(), const_c.end())(c); + if (test(c.size() != size + const_c.size())) { + cerr << "Failed " << typeid(Map).name() << " test_map_insert 3\n"; + return; + } +} + +inline void test_multimap_insert(std::multimap c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + + typedef std::multimap Multimap; + + Multimap::value_type const value = *c.begin(); + Multimap::iterator c_begin = c.begin(); + // wrapper for + // iterator insert(iterator where, const value_type& val); + Multimap::iterator it = + phx::insert(arg1, arg2, arg3)(c, c_begin, value); + + if (test(it != c.begin() || *it != *(++it))) { + cerr << "Failed " << typeid(Multimap).name() + << " test_multimap_insert 1\n"; + return; + } + + // wrapper for + // iterator insert(const value_type& val); + Multimap::value_type const value2(1400, 2200); + it = phx::insert(arg1, arg2)(c, value2); + if (test(it == c.end())) { + cerr << "Failed " << typeid(Multimap).name() + << " test_multimap_insert 2\n"; + return; + } + + // wrapper for + // template + // void insert(InIt first, InIt last); + Multimap const const_c = build_multimap(); + Multimap::size_type size = c.size(); + phx::insert(arg1, const_c.begin(), const_c.end())(c); + if (test(c.size() != size + const_c.size())) { + cerr << "Failed " << typeid(Multimap).name() + << " test_multimap_insert 3\n"; + return; + } +} + +template +void test_key_comp(Container c) +{ + using phx::arg_names::arg1; + using phx::key_comp; + + typename Container::key_compare comp = key_comp(arg1)(c); + + Container const const_c = c; + comp = key_comp(arg1)(const_c); +} + +template +void test_max_size(Container c) +{ + using phx::arg_names::arg1; + using phx::max_size; + + Container const const_c = c; + + typename Container::size_type s1 = max_size(arg1)(c); + if (test(s1 != c.max_size())) { + cerr << "Failed " << typeid(Container).name() << " test_max_size 1\n"; + return; + } + + typename Container::size_type s2 = max_size(arg1)(const_c); + if (test(s2 != const_c.max_size())) { + cerr << "Failed " << typeid(Container).name() << " test_max_size 2\n"; + return; + } +} + +template +void test_pop_back(Container c) +{ + using phx::arg_names::arg1; + using phx::pop_back; + + Container const const_c = c; + + typename Container::size_type size = c.size(); + + pop_back(arg1)(c); + if (test(c.size() + 1 != size)) { + cerr << "Failed " << typeid(Container).name() << " test_pop_back 1\n"; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + pop_back(arg1)(const_c); +#endif +} + +template +void test_pop_front(Container c) +{ + using phx::arg_names::arg1; + using phx::pop_front; + + Container const const_c = c; + + typename Container::size_type size = c.size(); + + pop_front(arg1)(c); + if (test(c.size() + 1 != size)) { + cerr << "Failed " << typeid(Container).name() << " test_pop_front 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + pop_front(arg1)(const_c); +#endif +} + +template +void test_push_back(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::push_back; + + Container const const_c = c; + + typename Container::value_type data = *c.begin(); + typename Container::size_type size = c.size(); + push_back(arg1, arg2)(c, data); + if (test(c.size() != size + 1)) { + cerr << "Failed " << typeid(Container).name() << " test_push_back 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + push_back(arg1, arg2)(const_c, data); +#endif +} + +template +void test_push_front(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::push_front; + + Container const const_c = c; + + typename Container::value_type data = *c.begin(); + typename Container::size_type size = c.size(); + push_front(arg1, arg2)(c, data); + if (test(c.size() != size + 1)) { + cerr << "Failed " << typeid(Container).name() << " test_push_front 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + push_front(arg1, arg2)(const_c, data); +#endif +} + +template +void test_rbegin(Container c) +{ + using phx::arg_names::arg1; + using phx::rbegin; + + typename Container::reverse_iterator it1 = rbegin(arg1)(c); + typename Container::reverse_iterator it1_test = c.rbegin(); + if (test(it1 != it1_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rbegin 1\n"; + return; + } + typename Container::const_reverse_iterator it2 = rbegin(arg1)(c); + typename Container::const_reverse_iterator it2_test = c.rbegin(); + if (test(it2 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rbegin 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reverse_iterator it3 = rbegin(arg1)(const_c); +#endif + + typename Container::const_reverse_iterator it4 = rbegin(arg1)(const_c); + it2_test = const_c.rbegin(); + if (test(it4 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rbegin 4\n"; + return; + } +} + +template +void test_rend(Container c) +{ + using phx::arg_names::arg1; + using phx::rend; + + typename Container::reverse_iterator it1 = rend(arg1)(c); + typename Container::reverse_iterator it1_test = c.rend(); + if (test(it1 != it1_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rend 1\n"; + return; + } + typename Container::const_reverse_iterator it2 = rend(arg1)(c); + typename Container::const_reverse_iterator it2_test = c.rend(); + if (test(it2 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rend 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reverse_iterator it3 = rend(arg1)(const_c); +#endif + + typename Container::const_reverse_iterator it4 = rend(arg1)(const_c); + it2_test = const_c.rend(); + if (test(it4 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rend 4\n"; + return; + } +} + +template +void test_reserve(Container c) +{ + using phx::arg_names::arg1; + using phx::reserve; + + Container const const_c = c; + + typename Container::size_type count = 2 * c.size(); + reserve(arg1, count)(c); + if (test(c.capacity() < count)) { + cerr << "Failed " << typeid(Container).name() << " test_reserve 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + reserve(arg1, count)(const_c)(const_c); +#endif +} + +template +void test_resize(Container c) +{ + using phx::arg_names::arg1; + using phx::resize; + + Container const const_c = c; + + typename Container::size_type new_size = 2 * c.size(); + resize(arg1, new_size)(c); + if (test(c.size() != new_size)) { + cerr << "Failed " << typeid(Container).name() << " test_resize 1\n"; + return; + } + + new_size = 2 * c.size(); + typename Container::value_type value = *c.begin(); + resize(arg1, new_size, value)(c); + if (test(c.size() != new_size)) { + cerr << "Failed " << typeid(Container).name() << " test_resize 2\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + new_size = 2 * const_c.size(); + resize(arg1, new_size)(const_c); + + new_size = 2 * const_c.size(); + resize(arg1, new_size, value)(const_c); +#endif +} + +template +void test_size(Container c) +{ + using phx::arg_names::arg1; + using phx::size; + + Container const const_c = c; + + typename Container::size_type s1 = size(arg1)(c); + if (test(s1 != c.size())) { + cerr << "Failed " << typeid(Container).name() << " test_size 1\n"; + return; + } + + typename Container::size_type s2 = size(arg1)(const_c); + if (test(s2 != const_c.size())) { + cerr << "Failed " << typeid(Container).name() << " test_size 2\n"; + return; + } +} + +template +void test_splice(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + using phx::arg_names::arg4; + using phx::arg_names::arg5; + using phx::splice; + + typename Container::iterator c_end; + typename Container::iterator c2_begin; + typename Container::iterator c2_end; + typename Container::size_type size = c.size(); + + Container const copy = c; + Container const copy2 = build_list(); + Container c2 = copy2; + + size = c.size(); + c_end = c.end(); + splice(arg1, arg2, arg3)(c, c_end, c2); + if (test(c.size() != 2 * size)) { + cerr << "Failed " << typeid(Container).name() << " test_splice 1\n"; + return; + } + + c = copy; + c_end = c.end(); + c2 = copy2; + c2_begin = c2.begin(); + size = c.size() + 1; + splice(arg1, arg2, arg3, arg4)(c, c_end, c2, c2_begin); + if (test(c.size() != size)) { + cerr << "Failed " << typeid(Container).name() << " test_splice 2\n"; + return; + } + + c = copy; + c_end = c.end(); + c2 = copy2; + c2_begin = c2.begin(); + c2_end = c2.end(); + size = c.size() + c2.size(); + splice(arg1, arg2, arg3, arg4, arg5)(c, c_end, c2, c2_begin, c2_end); + if (test(c.size() != size)) { + cerr << "Failed " << typeid(Container).name() << " test_splice 3\n"; + return; + } +} + +template +void test_value_comp(Container c) +{ + using phx::arg_names::arg1; + using phx::value_comp; + + typename Container::value_compare comp = value_comp(arg1)(c); + + Container const const_c = c; + comp = value_comp(arg1)(const_c); +} + +#endif diff --git a/phoenix/test/container/container_tests1a.cpp b/phoenix/test/container/container_tests1a.cpp new file mode 100644 index 000000000..9d6d3eb51 --- /dev/null +++ b/phoenix/test/container/container_tests1a.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_assign(data); + test_assign2(data); + test_back(data); + test_begin(data); + test_clear(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests1b.cpp b/phoenix/test/container/container_tests1b.cpp new file mode 100644 index 000000000..ed54395fa --- /dev/null +++ b/phoenix/test/container/container_tests1b.cpp @@ -0,0 +1,48 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_empty(data); + test_end(data); + test_erase(data); + test_front(data); + test_get_allocator(data); + test_insert(data); + test_max_size(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests2a.cpp b/phoenix/test/container/container_tests2a.cpp new file mode 100644 index 000000000..e8fc8a3e3 --- /dev/null +++ b/phoenix/test/container/container_tests2a.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_pop_back(data); + test_pop_front(data); + test_push_back(data); + test_push_front(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests2b.cpp b/phoenix/test/container/container_tests2b.cpp new file mode 100644 index 000000000..c666dd07b --- /dev/null +++ b/phoenix/test/container/container_tests2b.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_rbegin(data); + test_rend(data); + test_resize(data); + test_size(data); + test_splice(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests3a.cpp b/phoenix/test/container/container_tests3a.cpp new file mode 100644 index 000000000..aad9ad8bb --- /dev/null +++ b/phoenix/test/container/container_tests3a.cpp @@ -0,0 +1,60 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + BOOST_STATIC_ASSERT((phx::stl::has_mapped_type >::value)); + + std::map const data = build_map(); + test_begin(data); + test_clear(data); + test_empty(data); + test_end(data); + test_map_erase(data); + test_get_allocator(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests3b.cpp b/phoenix/test/container/container_tests3b.cpp new file mode 100644 index 000000000..4add4541a --- /dev/null +++ b/phoenix/test/container/container_tests3b.cpp @@ -0,0 +1,61 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + BOOST_STATIC_ASSERT((phx::stl::has_mapped_type >::value)); + + std::map const data = build_map(); + test_map_insert(data); + test_key_comp(data); + test_max_size(data); + test_rbegin(data); + test_rend(data); + test_size(data); + test_value_comp(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests4a.cpp b/phoenix/test/container/container_tests4a.cpp new file mode 100644 index 000000000..cd42ae8b7 --- /dev/null +++ b/phoenix/test/container/container_tests4a.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::vector const data = build_vector(); + test_assign(data); + test_assign2(data); + test_at(data); + test_back(data); + test_begin(data); + test_capacity(data); + test_clear(data); + test_end(data); + test_empty(data); + test_erase(data); + test_front(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests4b.cpp b/phoenix/test/container/container_tests4b.cpp new file mode 100644 index 000000000..2967d4ba1 --- /dev/null +++ b/phoenix/test/container/container_tests4b.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::vector const data = build_vector(); + test_get_allocator(data); + test_insert(data); + test_max_size(data); + test_pop_back(data); + test_push_back(data); + test_rbegin(data); + test_rend(data); + test_reserve(data); + test_resize(data); + test_size(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests5a.cpp b/phoenix/test/container/container_tests5a.cpp new file mode 100644 index 000000000..6c6f1d83c --- /dev/null +++ b/phoenix/test/container/container_tests5a.cpp @@ -0,0 +1,53 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::deque const build_deque() +{ + std::vector const data = build_vector(); + return std::deque(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::deque const data = build_deque(); + test_assign(data); + test_assign2(data); + test_at(data); + test_back(data); + test_begin(data); + test_clear(data); + test_front(data); + test_empty(data); + test_end(data); + test_erase(data); + test_get_allocator(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests5b.cpp b/phoenix/test/container/container_tests5b.cpp new file mode 100644 index 000000000..26f5a04ca --- /dev/null +++ b/phoenix/test/container/container_tests5b.cpp @@ -0,0 +1,52 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::deque const build_deque() +{ + std::vector const data = build_vector(); + return std::deque(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::deque const data = build_deque(); + test_insert(data); + test_max_size(data); + test_pop_back(data); + test_pop_front(data); + test_push_back(data); + test_push_front(data); + test_rbegin(data); + test_rend(data); + test_resize(data); + test_size(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests6a.cpp b/phoenix/test/container/container_tests6a.cpp new file mode 100644 index 000000000..14392769c --- /dev/null +++ b/phoenix/test/container/container_tests6a.cpp @@ -0,0 +1,69 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::multimap const build_multimap() +{ + typedef std::map int_map; + typedef std::multimap int_multimap; + int_map const data = build_map(); + return int_multimap(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::multimap const data = build_multimap(); + test_begin(data); + test_clear(data); + test_empty(data); + test_end(data); + test_map_erase(data); + test_get_allocator(data); + return boost::report_errors(); +} + + + + diff --git a/phoenix/test/container/container_tests6b.cpp b/phoenix/test/container/container_tests6b.cpp new file mode 100644 index 000000000..4c0b2127b --- /dev/null +++ b/phoenix/test/container/container_tests6b.cpp @@ -0,0 +1,70 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::multimap const build_multimap() +{ + typedef std::map int_map; + typedef std::multimap int_multimap; + int_map const data = build_map(); + return int_multimap(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::multimap const data = build_multimap(); + test_multimap_insert(data); + test_key_comp(data); + test_max_size(data); + test_rbegin(data); + test_rend(data); + test_size(data); + test_value_comp(data); + return boost::report_errors(); +} + + + + diff --git a/phoenix/test/core/compose_tests.cpp b/phoenix/test/core/compose_tests.cpp new file mode 100644 index 000000000..60968a4ae --- /dev/null +++ b/phoenix/test/core/compose_tests.cpp @@ -0,0 +1,84 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct X +{ + template < + typename Env + , typename A0 = void_ + , typename A1 = void_ + , typename A2 = void_ + > + struct result + { + typedef int type; + }; + + template + static RT + eval(Env const& env, A0& a0, A1& a1, A2& a2) + { + return a0.eval(env) + a1.eval(env) + a2.eval(env); + } +}; + +int +main() +{ + using boost::fusion::at_c; + { + // testing as_actor + BOOST_STATIC_ASSERT((boost::is_same< + as_actor > >::type, actor > >::value)); + BOOST_STATIC_ASSERT((boost::is_same< + as_actor::type, actor > >::value)); + } + + { + // testing compose + char const* s = "Hi"; + int x = 123; + + BOOST_TEST(at_c<0>(compose(1, arg1, val(1))) + .eval(basic_environment<>()) == 1); + BOOST_TEST(at_c<1>(compose(1, arg1, val(456))) + .eval(basic_environment(s)) == s); + BOOST_TEST(at_c<2>(compose(1, arg1, val(456))) + .eval(basic_environment<>()) == 456); + BOOST_TEST(compose(9876, arg1, val(456)) + .eval(basic_environment(x)) == 10455); + + // testing composite sizes + cout << "sizeof(arg1) is: " + << sizeof(arg1) << endl; + cout << "sizeof(compose(arg1)) is: " + << sizeof(compose(arg1)) << endl; + cout << "sizeof(compose(1, arg1, val(456))) is: " + << sizeof(compose(1, arg1, val(456))) << endl; + cout << "sizeof(compose()) is: " + << sizeof(compose()) << endl; + cout << "sizeof(compose('x')) is: " + << sizeof(compose('x')) << endl; + cout << "sizeof(compose('x', 3)) is: " + << sizeof(compose('x', 3)) << endl; + cout << "sizeof(compose('x', 'y', 3)) is: " + << sizeof(compose('x', 'y', 3)) << endl; + } + + return boost::report_errors(); +} diff --git a/phoenix/test/core/primitives_tests.cpp b/phoenix/test/core/primitives_tests.cpp new file mode 100644 index 000000000..5224edd6b --- /dev/null +++ b/phoenix/test/core/primitives_tests.cpp @@ -0,0 +1,69 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + char c1 = '1'; + int i1 = 1, i2 = 2, i = 4; + const char* s2 = "2"; + + /////////////////////////////////////////////////////////////////////////// + // + // Values, references and arguments + // + /////////////////////////////////////////////////////////////////////////// + + // argument + BOOST_TEST(arg1(c1) == c1); + BOOST_TEST(arg1(i1, i2) == i1); + BOOST_TEST(arg2(i1, s2) == s2); + BOOST_TEST(&(arg1(c1)) == &c1); // must be an lvalue + + // value + cout << val("Hello,")() << val(' ')() << val("World")() << endl; + BOOST_TEST(val(3)() == 3); + BOOST_TEST(val("Hello, world")() == std::string("Hello, world")); + BOOST_TEST(val(_1)(i1) == i1); + + // should not compile: +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + &val(_1)(i1); // should return an rvalue +#endif + + // reference + BOOST_TEST(cref(i)() == ref(i)()); + BOOST_TEST(cref(i)() == 4); + BOOST_TEST(i == 4); + BOOST_TEST(ref(++i)() == 5); + BOOST_TEST(i == 5); + + // should not compile: +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + ref(arg1); +#endif + + // testing consts + int const ic = 123; + BOOST_TEST(arg1(ic) == 123); + + // should not compile: +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + arg1(); +#endif + + return boost::report_errors(); +} diff --git a/phoenix/test/detail/type_deduction_tests.cpp b/phoenix/test/detail/type_deduction_tests.cpp new file mode 100644 index 000000000..9d4dcb678 --- /dev/null +++ b/phoenix/test/detail/type_deduction_tests.cpp @@ -0,0 +1,374 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + BOOST_UNARY_RESULT_OF(-x, result_of_negate); + BOOST_UNARY_RESULT_OF(+x, result_of_posit); + BOOST_UNARY_RESULT_OF(!x, result_of_logical_not); + BOOST_UNARY_RESULT_OF(~x, result_of_invert); + BOOST_UNARY_RESULT_OF(&x, result_of_reference); + BOOST_UNARY_RESULT_OF(*x, result_of_dereference); + + BOOST_UNARY_RESULT_OF(++x, result_of_pre_increment); + BOOST_UNARY_RESULT_OF(--x, result_of_pre_decrement); + BOOST_UNARY_RESULT_OF(x++, result_of_post_increment); + BOOST_UNARY_RESULT_OF(x--, result_of_post_decrement); + + BOOST_BINARY_RESULT_OF(x = y, result_of_assign); + BOOST_ASYMMETRIC_BINARY_RESULT_OF(x[y], result_of_index); + + BOOST_BINARY_RESULT_OF(x += y, result_of_plus_assign); + BOOST_BINARY_RESULT_OF(x -= y, result_of_minus_assign); + BOOST_BINARY_RESULT_OF(x *= y, result_of_multiplies_assign); + BOOST_BINARY_RESULT_OF(x /= y, result_of_divides_assign); + BOOST_BINARY_RESULT_OF(x %= y, result_of_modulus_assign); + + BOOST_BINARY_RESULT_OF(x &= y, result_of_and_assign); + BOOST_BINARY_RESULT_OF(x |= y, result_of_or_assign); + BOOST_BINARY_RESULT_OF(x ^= y, result_of_xor_assign); + BOOST_BINARY_RESULT_OF(x <<= y, result_of_shift_left_assign); + BOOST_BINARY_RESULT_OF(x >>= y, result_of_shift_right_assign); + + BOOST_BINARY_RESULT_OF(x + y, result_of_plus); + BOOST_BINARY_RESULT_OF(x - y, result_of_minus); + BOOST_BINARY_RESULT_OF(x * y, result_of_multiplies); + BOOST_BINARY_RESULT_OF(x / y, result_of_divides); + BOOST_BINARY_RESULT_OF(x % y, result_of_modulus); + + BOOST_BINARY_RESULT_OF(x & y, result_of_and); + BOOST_BINARY_RESULT_OF(x | y, result_of_or); + BOOST_BINARY_RESULT_OF(x ^ y, result_of_xor); + BOOST_BINARY_RESULT_OF(x << y, result_of_shift_left); + BOOST_BINARY_RESULT_OF(x >> y, result_of_shift_right); + + BOOST_BINARY_RESULT_OF(x == y, result_of_equal_to); + BOOST_BINARY_RESULT_OF(x != y, result_of_not_equal_to); + BOOST_BINARY_RESULT_OF(x < y, result_of_less); + BOOST_BINARY_RESULT_OF(x <= y, result_of_less_equal); + BOOST_BINARY_RESULT_OF(x > y, result_of_greater); + BOOST_BINARY_RESULT_OF(x >= y, result_of_greater_equal); + + BOOST_BINARY_RESULT_OF(x && y, result_of_logical_and); + BOOST_BINARY_RESULT_OF(x || y, result_of_logical_or); + BOOST_BINARY_RESULT_OF(true ? x : y, result_of_if_else); +} + +using namespace boost; +using namespace std; + +struct X {}; +X operator+(X, int); + +struct Y {}; +Y* operator+(Y, int); + +struct Z {}; +Z const* operator+(Z const&, int); +Z& operator+(Z&, int); +bool operator==(Z, Z); +bool operator==(Z, int); + +struct W {}; +Z operator+(W, int); +bool operator==(W, Z); + +int +main() +{ + // ASSIGN + { + typedef result_of_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + { + typedef result_of_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // PLUS + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus, double>::type result; + BOOST_STATIC_ASSERT((is_same >::value)); + } + { + typedef result_of_plus >::type result; + BOOST_STATIC_ASSERT((is_same >::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // INDEX + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index, int>::type result; + BOOST_STATIC_ASSERT((is_same::reference>::value)); + } + { + typedef result_of_index const, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index const, int>::type result; + BOOST_STATIC_ASSERT((is_same::const_reference>::value)); + } + { + typedef result_of_index, int>::type result; + BOOST_STATIC_ASSERT((is_same::reference>::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::iterator::reference>::value)); + } + { + typedef result_of_index::const_iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::const_iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::const_iterator::reference>::value)); + } + { + typedef result_of_index, char>::type result; + BOOST_STATIC_ASSERT((is_same::mapped_type>::value)); + } + + // PLUS ASSIGN + { + typedef result_of_plus_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus_assign, double>::type result; + BOOST_STATIC_ASSERT((is_same&>::value)); + } + + // SHIFT LEFT + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // EQUAL + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // MINUS (pointers) + { + typedef result_of_minus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // DEREFERENCE + { + typedef result_of_dereference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_dereference::iterator>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_dereference >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // ADDRESS OF + { + typedef result_of_reference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_reference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // PRE INCREMENT + { + typedef result_of_pre_increment::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // POST INCREMENT + { + typedef result_of_post_increment::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // IF-ELSE-EXPRESSION ( c ? a : b ) + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // DEDUCTION FAILURE + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // local_reference + { + using phoenix::detail::local_reference; + typedef result_of_assign, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // local_reference + { + using phoenix::detail::local_reference; + typedef result_of_pre_increment >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // local_reference + { + using phoenix::detail::local_reference; + typedef result_of_if_else, local_reference >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/function/function_tests.cpp b/phoenix/test/function/function_tests.cpp new file mode 100644 index 000000000..b567b7aba --- /dev/null +++ b/phoenix/test/function/function_tests.cpp @@ -0,0 +1,116 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + + struct test_impl + { + typedef void result_type; + void operator()() const + { + cout << "Test lazy functions...\n"; + } + }; + + function test; + + struct sqr_impl + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return n * n; + } + }; + + function sqr; + + struct fact_impl + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * (*this)(n-1); + } + }; + + function fact; + + struct pow_impl + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b) const + { + return pow(a, b); + } + }; + + function power; + + struct add_impl + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b, Arg3 c, Arg4 d) const + { + return a + b + c + d; + } + }; + + function add; + +int +main() +{ + int i5 = 5; + double d5 = 5, d3 = 3; + + test()(); + BOOST_TEST(sqr(arg1)(i5) == (i5*i5)); + BOOST_TEST(fact(4)() == 24); + BOOST_TEST(fact(arg1)(i5) == 120); + BOOST_TEST((int)power(arg1, arg2)(d5, d3) == (int)pow(d5, d3)); + BOOST_TEST((sqr(arg1) + 5)(i5) == ((i5*i5)+5)); + BOOST_TEST(add(arg1, arg1, arg1, arg1)(i5) == (5+5+5+5)); + + int const ic5 = 5; + // testing consts + BOOST_TEST(sqr(arg1)(ic5) == (ic5*ic5)); + + return boost::report_errors(); +} diff --git a/phoenix/test/object/cast_tests.cpp b/phoenix/test/object/cast_tests.cpp new file mode 100644 index 000000000..338ed7e48 --- /dev/null +++ b/phoenix/test/object/cast_tests.cpp @@ -0,0 +1,63 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct T +{ + string foo() { return "T"; } +}; + +struct U : T +{ + string foo() { return "U"; } +}; + +struct VT +{ + virtual string foo() { return "T"; } +}; + +struct VU : VT +{ + virtual string foo() { return "U"; } +}; + +int +main() +{ + { + U u; + BOOST_TEST(arg1(u).foo() == "U"); + BOOST_TEST(static_cast_(arg1)(u).foo() == "T"); + } + + { + U const u = U(); + BOOST_TEST(const_cast_(arg1)(u).foo() == "U"); + } + + { + VU u; + VT* tp = &u; + BOOST_TEST(arg1(tp)->foo() == "U"); + BOOST_TEST(dynamic_cast_(arg1)(tp) != 0); + } + + { + void* p = 0; + reinterpret_cast_(arg1)(p); // compile test only + } + + return boost::report_errors(); +} diff --git a/phoenix/test/object/new_delete_tests.cpp b/phoenix/test/object/new_delete_tests.cpp new file mode 100644 index 000000000..d9018c7a9 --- /dev/null +++ b/phoenix/test/object/new_delete_tests.cpp @@ -0,0 +1,52 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int n = 0; + +struct X +{ + X(int, int, int) { cout << "new X(int, int, int)" << endl; ++n; } + X() { cout << "new X" << endl; ++n; } + ~X() { cout << "delete X" << endl; --n; } +}; + +int +main() +{ + { + vector v(10); + + for_each(v.begin(), v.end(), arg1 = new_()); + for_each(v.begin(), v.end(), delete_(arg1)); + + for_each(v.begin(), v.end(), arg1 = new_(1, 2, 3)); + for_each(v.begin(), v.end(), delete_(arg1)); + } + + { + using boost::shared_ptr; + vector > v(10); + for_each(v.begin(), v.end(), + arg1 = construct >(new_()) + ); + } + + BOOST_TEST(n == 0); + return boost::report_errors(); +} diff --git a/phoenix/test/operator/arithmetic_tests.cpp b/phoenix/test/operator/arithmetic_tests.cpp new file mode 100644 index 000000000..33f77d58c --- /dev/null +++ b/phoenix/test/operator/arithmetic_tests.cpp @@ -0,0 +1,54 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace std; + +int +main() +{ + { + int x = 123; + + BOOST_TEST((ref(x) += 456)() == 123 + 456); + BOOST_TEST(x == 123 + 456); + BOOST_TEST((ref(x) -= 456)() == 123); + BOOST_TEST(x == 123); + BOOST_TEST((ref(x) *= 456)() == 123 * 456); + BOOST_TEST(x == 123 * 456); + BOOST_TEST((ref(x) /= 456)() == 123); + BOOST_TEST(x == 123); + + int& r1 = (ref(x) += 456)(); // should be an lvalue + int& r2 = (ref(x) -= 456)(); // should be an lvalue + int& r3 = (ref(x) *= 456)(); // should be an lvalue + int& r4 = (ref(x) /= 456)(); // should be an lvalue + BOOST_TEST(r1 == 123 && r2 == 123 && r3 == 123 && r4 == 123); + + BOOST_TEST((ref(x) %= 456)() == 123 % 456); + BOOST_TEST(x == 123 % 456); + } + + { + BOOST_TEST((val(123) + 456)() == 123 + 456); + BOOST_TEST((val(123) - 456)() == 123 - 456); + BOOST_TEST((val(123) * 456)() == 123 * 456); + BOOST_TEST((val(123) / 456)() == 123 / 456); + BOOST_TEST((val(123) % 456)() == 123 % 456); + + BOOST_TEST((123 + val(456))() == 123 + 456); + BOOST_TEST((123 - val(456))() == 123 - 456); + BOOST_TEST((123 * val(456))() == 123 * 456); + BOOST_TEST((123 / val(456))() == 123 / 456); + BOOST_TEST((123 % val(456))() == 123 % 456); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/bitwise_tests.cpp b/phoenix/test/operator/bitwise_tests.cpp new file mode 100644 index 000000000..f21b94a76 --- /dev/null +++ b/phoenix/test/operator/bitwise_tests.cpp @@ -0,0 +1,73 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + int x; + int y; + + x = 123; + y = 123; + (ref(x) &= 456)(); + y &= 456; + BOOST_TEST(x == y); + + x = 123; + y = 123; + (ref(x) |= 456)(); + y |= 456; + BOOST_TEST(x == y); + + x = 123; + y = 123; + (ref(x) ^= 456)(); + y ^= 456; + BOOST_TEST(x == y); + + x = 123; + y = 123; + (ref(x) <<= 4)(); + y <<= 4; + BOOST_TEST(x == y); + + x = 1230000; + y = 1230000; + (ref(x) >>= 4)(); + y >>= 4; + BOOST_TEST(x == y); + + int& r1 = (ref(x) &= 456)(); // should be an lvalue + int& r2 = (ref(x) |= 456)(); // should be an lvalue + int& r3 = (ref(x) ^= 456)(); // should be an lvalue + int& r4 = (ref(x) <<= 4)(); // should be an lvalue + int& r5 = (ref(x) >>= 4)(); // should be an lvalue + BOOST_TEST(&r1 == &r2 && &r2 == &r3 && &r3 == &r4 && &r4 == &r5); + } + + { + BOOST_TEST((val(123) & 456)() == (123 & 456)); + BOOST_TEST((val(123) | 456)() == (123 | 456)); + BOOST_TEST((val(123) ^ 456)() == (123 ^ 456)); + BOOST_TEST((val(123) << 4)() == (123 << 4)); + BOOST_TEST((val(1230000) >> 4)() == (1230000 >> 4)); + + char const* s = "Yabadabadoo!!!\n"; + (cout << arg1)(s); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/comparison_tests.cpp b/phoenix/test/operator/comparison_tests.cpp new file mode 100644 index 000000000..c1be505d1 --- /dev/null +++ b/phoenix/test/operator/comparison_tests.cpp @@ -0,0 +1,28 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace std; + +int +main() +{ + { + BOOST_TEST(!(val(123) == 456)()); + BOOST_TEST((val(123) != 456)()); + BOOST_TEST(!(val(123) > 456)()); + BOOST_TEST((val(123) < 456)()); + BOOST_TEST(!(val(123) > 456)()); + BOOST_TEST((val(123) <= 123)()); + BOOST_TEST((val(123) >= 123)()); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/if_else_tests.cpp b/phoenix/test/operator/if_else_tests.cpp new file mode 100644 index 000000000..6ae4c150d --- /dev/null +++ b/phoenix/test/operator/if_else_tests.cpp @@ -0,0 +1,34 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + int x = 0; + int y = 0; + bool c = false; + + BOOST_TEST(if_else(arg1, 1234, 5678)(c) == 5678); + BOOST_TEST(if_else(arg1, 1234, 'x')(c) == 'x'); + + int& r = if_else(arg1, ref(x), ref(y))(c); // should be an lvalue + BOOST_TEST(&y == &r); + + (if_else(arg1, ref(x), ref(y)) = 986754321)(c); + BOOST_TEST(y == 986754321); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/io_tests.cpp b/phoenix/test/operator/io_tests.cpp new file mode 100644 index 000000000..e7125b031 --- /dev/null +++ b/phoenix/test/operator/io_tests.cpp @@ -0,0 +1,51 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int i100 = 100; + string hello = "hello"; + const char* world = " world"; + + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + char const* msg = "cout assert\n"; + (cout << arg1)(msg); + (cout << arg1 << endl)(hello); + (arg1 << hex)(cout); + (cout << val(hello))(); + + (cout << val(hello) << world << ", you da man!\n")(); + for_each(v.begin(), v.end(), cout << arg1 << ','); + + (cout << arg1 << "this is it, shukz:" << hex << arg2 << endl << endl)(msg, i100); + + int in; + int out = 12345; + stringstream sstr; + (sstr << arg1)(out); + (sstr >> arg1)(in); + BOOST_TEST(in == out); + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/logical_tests.cpp b/phoenix/test/operator/logical_tests.cpp new file mode 100644 index 000000000..6a49e5887 --- /dev/null +++ b/phoenix/test/operator/logical_tests.cpp @@ -0,0 +1,36 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + bool x = false; + bool y = true; + + BOOST_TEST((!arg1)(x) == true); + BOOST_TEST((!arg1)(y) == false); + BOOST_TEST((arg1 || arg2)(x, y) == true); + BOOST_TEST((arg1 && arg2)(x, y) == false); + + // short circuiting: + int i = 1234; + (arg1 || (arg2 = 4567))(y, i); + BOOST_TEST(i == 1234); + (arg1 && (arg2 = 4567))(y, i); + BOOST_TEST(i == 4567); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/member.cpp b/phoenix/test/operator/member.cpp new file mode 100644 index 000000000..b13204459 --- /dev/null +++ b/phoenix/test/operator/member.cpp @@ -0,0 +1,83 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +#include +#include + +namespace +{ + struct Test + { + int value; + + int func(int n) const { return n; } + int dunc() { return 10; } + }; +} + +int main() +{ + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + using namespace boost; + + Test test = {1}; + const Test* cptr = &test; + Test* ptr = &test; + + BOOST_TEST((val(ptr)->*&Test::value)() == 1); + BOOST_TEST((val(cptr)->*&Test::value)() == 1); + BOOST_TEST((arg1->*&Test::value)(cptr) == 1); + + ((val(ptr)->*&Test::value) = 2)(); + BOOST_TEST(test.value == 2); + + BOOST_TEST((val(ptr)->*&Test::func)(3)() == 3); + BOOST_TEST((val(cptr)->*&Test::func)(4)() == 4); + BOOST_TEST((val(ptr)->*&Test::dunc)()() == 10); + + BOOST_TEST((arg1->*&Test::func)(5)(ptr) == 5); + + shared_ptr sptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(sptr) == 2); + BOOST_TEST((arg1->*&Test::func)(6)(sptr) == 6); + + scoped_ptr scptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(scptr) == 2); + BOOST_TEST((arg1->*&Test::func)(7)(scptr) == 7); + + shared_ptr csptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(csptr) == 2); + BOOST_TEST((arg1->*&Test::func)(8)(csptr) == 8); + + scoped_ptr cscptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(cscptr) == 2); + BOOST_TEST((arg1->*&Test::func)(9)(cscptr) == 9); + + std::auto_ptr aptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(aptr) == 2); + BOOST_TEST((arg1->*&Test::func)(10)(aptr) == 10); + + std::auto_ptr captr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(captr) == 2); + BOOST_TEST((arg1->*&Test::func)(11)(captr) == 11); + + return 0; +} diff --git a/phoenix/test/operator/misc_binary_tests.cpp b/phoenix/test/operator/misc_binary_tests.cpp new file mode 100644 index 000000000..b538225db --- /dev/null +++ b/phoenix/test/operator/misc_binary_tests.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { // From Phoenix 1.1 binary tests + + int i2 = 2, i3 = 3, i = 5; + const char* world = " world"; + + BOOST_TEST((ref(i) = ref(i))() == 5); + BOOST_TEST((ref(i) = 3)() == 3); + BOOST_TEST(i == 3); + i = 5; + int x, y, z; + (ref(x) = ref(y) = ref(z) = 10)(); + BOOST_TEST(x == 10 && y == 10 && z == 10); + BOOST_TEST((val(world)[3])() == world[3]); + + BOOST_TEST((ref(i) += 5)() == 10); + BOOST_TEST((ref(i) -= 5)() == 5); + BOOST_TEST((ref(i) *= 5)() == 25); + BOOST_TEST((ref(i) /= 5)() == 5); + BOOST_TEST((ref(i) %= 2)() == 1); + + BOOST_TEST((ref(i) <<= 3)() == 8); + BOOST_TEST((ref(i) >>= 1)() == 4); + BOOST_TEST((ref(i) |= 0xFF)() == 0xFF); + BOOST_TEST((ref(i) &= 0xF0)() == 0xF0); + BOOST_TEST((ref(i) ^= 0xFFFFFFFF)() == int(0xFFFFFF0F)); + + BOOST_TEST((val(5) == val(5))()); + BOOST_TEST((val(5) == 5)()); + + BOOST_TEST((arg1 + arg2)(i2, i3) == i2 + i3); + BOOST_TEST((arg1 - arg2)(i2, i3) == i2 - i3); + BOOST_TEST((arg1 * arg2)(i2, i3) == i2 * i3); + BOOST_TEST((arg1 / arg2)(i2, i3) == i2 / i3); + BOOST_TEST((arg1 % arg2)(i2, i3) == i2 % i3); + BOOST_TEST((arg1 & arg2)(i2, i3) == (i2 & i3)); + BOOST_TEST((arg1 | arg2)(i2, i3) == (i2 | i3)); + BOOST_TEST((arg1 ^ arg2)(i2, i3) == (i2 ^ i3)); + BOOST_TEST((arg1 << arg2)(i2, i3) == i2 << i3); + BOOST_TEST((arg1 >> arg2)(i2, i3) == i2 >> i3); + + BOOST_TEST((val(5) != val(6))()); + BOOST_TEST((val(5) < val(6))()); + BOOST_TEST(!(val(5) > val(6))()); + BOOST_TEST((val(5) < val(6))()); + BOOST_TEST((val(5) <= val(6))()); + BOOST_TEST((val(5) <= val(5))()); + BOOST_TEST((val(7) >= val(6))()); + BOOST_TEST((val(7) >= val(7))()); + + BOOST_TEST((val(false) && val(false))() == false); + BOOST_TEST((val(true) && val(false))() == false); + BOOST_TEST((val(false) && val(true))() == false); + BOOST_TEST((val(true) && val(true))() == true); + + BOOST_TEST((val(false) || val(false))() == false); + BOOST_TEST((val(true) || val(false))() == true); + BOOST_TEST((val(false) || val(true))() == true); + BOOST_TEST((val(true) || val(true))() == true); + } + + { // From Phoenix 1.1 mixed_binary tests + + int i1 = 1, i2 = 2, i50 = 50, i100 = 100; + double d2_5 = 2.5; + string hello = "hello"; + const char* world = " world"; + + BOOST_TEST((arg1 + arg2)(i100, i50) == (i100 + i50)); + BOOST_TEST((arg1 + 3)(i100) == (3 + i100)); + BOOST_TEST((arg1 + arg2)(hello, world) == "hello world"); + BOOST_TEST((arg1 + arg2)(i1, d2_5) == (i1 + d2_5)); + + BOOST_TEST((*(arg1 + arg2))(world, i2) == *(world + i2)); + BOOST_TEST((*(arg1 + arg2))(i2, world) == *(i2 + world)); + BOOST_TEST((*(val(world+i2) - arg1))(i2) == *world); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/self_tests.cpp b/phoenix/test/operator/self_tests.cpp new file mode 100644 index 000000000..b723e0f71 --- /dev/null +++ b/phoenix/test/operator/self_tests.cpp @@ -0,0 +1,55 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + int x = 123; + BOOST_TEST((&arg1)(x) == &x); + BOOST_TEST((*&arg1)(x) == 123); + + int y = 968; + (ref(x) = arg1)(y); + BOOST_TEST(x == y); + + (arg1 = 456)(x); + BOOST_TEST(x == 456); + int& r = (arg1 = 456)(x); // must be an lvalue + BOOST_TEST(&r == &x); + + int c[] = { 1, 2, 3, 4, 5 }; + BOOST_TEST((arg1[3])(c) == 4); + + int& r2 = (arg1[3])(c); // must be an lvalue + BOOST_TEST(&r2 == &c[3]); + + vector v; + v.push_back("a"); + v.push_back("b"); + v.push_back("c"); + v.push_back("d"); + + BOOST_TEST((arg1[3])(v) == "d"); + + map m; + (arg1["Kimpo"] = arg2)(m, x); + BOOST_TEST(m["Kimpo"] == x); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/unary_tests.cpp b/phoenix/test/operator/unary_tests.cpp new file mode 100644 index 000000000..2a546ad5a --- /dev/null +++ b/phoenix/test/operator/unary_tests.cpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + BOOST_TEST((-val(123))() == -123); + BOOST_TEST((- -val(123))() == 123); + BOOST_TEST((+val(123))() == 123); + } + + { + int x = 123; + + BOOST_TEST((++ref(x))() == 124); + BOOST_TEST(x == 124); + BOOST_TEST((--ref(x))() == 123); + BOOST_TEST(x == 123); + + BOOST_TEST((ref(x)++)() == 123); + BOOST_TEST(x == 124); + BOOST_TEST((ref(x)--)() == 124); + BOOST_TEST(x == 123); + + int& r1 = (++ref(x))(); // should be an lvalue + int& r2 = (--ref(x))(); // should be an lvalue + BOOST_TEST(r1 == 123 && r2 == 123); + } + + { // From Phoenix 1.1 unary tests + + int i1 = 1, i = 5; + + BOOST_TEST((!val(true))() == false); + BOOST_TEST((-val(1))() == -1); + BOOST_TEST((+val(1))() == +1); + BOOST_TEST((~val(1))() == ~1); + BOOST_TEST(*(&arg1)(i1) == *(&i1)); + BOOST_TEST((&arg1)(i1) == &i1); + + BOOST_TEST((*val(&i1))() == *(&i1)); + BOOST_TEST((*&arg1)(i1) == *(&i1)); + BOOST_TEST((++ref(i))() == 6); + BOOST_TEST((--ref(i))() == 5); + BOOST_TEST((ref(i)++)() == 5); + BOOST_TEST(i == 6); + BOOST_TEST((ref(i)--)() == 6); + BOOST_TEST(i == 5); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/scope/bug_000008.cpp b/phoenix/test/scope/bug_000008.cpp new file mode 100644 index 000000000..32334ce59 --- /dev/null +++ b/phoenix/test/scope/bug_000008.cpp @@ -0,0 +1,96 @@ +/*============================================================================= + Copyright (c) 2003 Martin Wille + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + + // see http://article.gmane.org/gmane.comp.parsers.spirit.general/4575 + // or https://sf.net/mailarchive/forum.php?thread_id=2692308&forum_id=1595 + // for a description of the bug being tested for by this program + // + // This code is borrowed from Spirit's bug_000008.cpp test for multithreads. +#include +#include +#include +#include + +#if defined(DONT_HAVE_BOOST) \ + || !defined(BOOST_HAS_THREADS) \ + || defined(BOOST_DISABLE_THREADS) \ + || (defined(__GNUC__) && defined(__WIN32__)) // MinGW +#define SKIP_TEST +#endif + + +#if defined(SKIP_TEST) +// we end here if we can't do multithreading +static void skipped() +{ + std::cout << "skipped\n"; +} + +int +main() +{ + skipped(); + return boost::report_errors(); +} + +#else +// the real MT stuff + +#include +#include +#include + +static const int number_of_calls_per_thread=20000; + +struct test_dynamic : boost::phoenix::dynamic +{ + test_dynamic() : b(*this) {} + member1 b; +}; + +void +in_thread(void) +{ + test_dynamic s; // should now be a local + + for (int i = 0; i < number_of_calls_per_thread; ++i) + { + boost::phoenix::dynamic_frame frame(s); + (s.b = 123)(); + { + boost::phoenix::dynamic_frame frame(s); + (s.b = 456)(); + BOOST_ASSERT((s.b == 456)()); + } + BOOST_ASSERT((s.b == 123)()); + } +} + +void +bug_000008() +{ + boost::thread t1(in_thread); + boost::thread t2(in_thread); + boost::thread t3(in_thread); + boost::thread t4(in_thread); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); +} + +int +main() +{ + bug_000008(); + return boost::report_errors(); +} + +#endif + diff --git a/phoenix/test/scope/dynamic_tests.cpp b/phoenix/test/scope/dynamic_tests.cpp new file mode 100644 index 000000000..4dd9b9766 --- /dev/null +++ b/phoenix/test/scope/dynamic_tests.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +#define PHOENIX_LIMIT 6 + +#include +#include +#include +#include + +struct my_dynamic : ::boost::phoenix::dynamic +{ + my_dynamic() : num(*this), message(*this), real(*this) {} + + member1 num; + member2 message; + member3 real; +}; + +// You may also use the macro below to achieve the same as above: +// +// PHOENIX_DYNAMIC( +// my_dynamic, +// (int, num) +// (std::string, message) +// (double, real) +// ); + +int +main() +{ + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + + my_dynamic clos; + + { // First stack frame + dynamic_frame frame(clos); + (clos.num = 123)(); + (clos.num += 456)(); + (clos.real = clos.num / 56.5)(); + (clos.message = "Hello " + std::string("World "))(); + + { // Second stack frame + dynamic_frame frame(clos); + (clos.num = 987)(); + (clos.message = "Abracadabra ")(); + (clos.real = clos.num * 1e30)(); + + { // Third stack frame + boost::fusion::vector init(-1, "Direct Init ", 3.14); + dynamic_frame frame(clos, init); + + (std::cout << clos.message << clos.num << ", " << clos.real << '\n')(); + BOOST_TEST(clos.num() == -1); + BOOST_TEST(clos.real() == 3.14); + BOOST_TEST(clos.message() == "Direct Init "); + } + + (std::cout << clos.message << clos.num << ", " << clos.real << '\n')(); + BOOST_TEST(clos.num() == 987); + BOOST_TEST(clos.real() == clos.num() * 1e30); + BOOST_TEST(clos.message() == "Abracadabra "); + } + + (std::cout << clos.message << clos.num << ", " << clos.real << '\n')(); + BOOST_TEST(clos.num() == 123+456); + BOOST_TEST(clos.real() == clos.num() / 56.5); + BOOST_TEST(clos.message() == "Hello " + std::string("World ")); + } + + return 0; +} diff --git a/phoenix/test/scope/lambda_tests.cpp b/phoenix/test/scope/lambda_tests.cpp new file mode 100644 index 000000000..570cc7cd3 --- /dev/null +++ b/phoenix/test/scope/lambda_tests.cpp @@ -0,0 +1,179 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +#define PHOENIX_LIMIT 5 + +#include +#include +#include +#include +#include + +namespace boost { namespace phoenix +{ + struct for_each_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, F f) const + { + std::for_each(c.begin(), c.end(), f); + } + }; + + function const for_each = for_each_impl(); + + struct push_back_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, T& x) const + { + c.push_back(x); + } + }; + + function const push_back = push_back_impl(); +}} + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::local_names; +using namespace std; + +struct zzz {}; + +int +main() +{ + { + int x = 1; + int y = lambda[_1]()(x); + BOOST_TEST(x == y); + } + + { + int x = 1, y = 10; + BOOST_TEST( + (_1 + lambda[_1 + 2])(x)(y) == 1+10+2 + ); + BOOST_TEST( + (_1 + lambda[-_1])(x)(y) == 1+-10 + ); + } + + { + int x = 1, y = 10, z = 13; + BOOST_TEST( + lambda(_a = _1, _b = _2) + [ + _1 + _a + _b + ] + (x, z)(y) == x + y + z + ); + } + + { + int x = 4; + int y = 5; + lambda(_a = _1)[_a = 555](x)(); + BOOST_TEST(x == 555); + (void)y; + } + + { + int x = 1; + long x2 = 2; + short x3 = 3; + char const* y = "hello"; + zzz z; + + BOOST_TEST(lambda[_1](x)(y) == y); + BOOST_TEST(lambda(_a = _1)[_a](x)(y) == x); + BOOST_TEST(lambda(_a = _1)[lambda[_a]](x)(y)(z) == x); + BOOST_TEST(lambda(_a = _1)[lambda[_a + _1]](x)(y)(x) == 2); + BOOST_TEST(lambda(_a = _1)[lambda(_b = _1)[_a + _b + _1]](x)(x2)(x3) == 6); + } + + { + int x = 1, y = 10; + BOOST_TEST( + (_1 + lambda(_a = _1)[_a + _1 + 2])(x)(y) == 1+1+10+2 + ); + } + + { + int x = 1, y = 10; + BOOST_TEST( + (_1 + + lambda(_a = _1) + [ + _a + lambda[_a + 2] + ] + ) + (x)(y)(y) == 1+1+1+2 + ); + } + + { + using boost::phoenix::for_each; + + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::vector v(init, init+10); + + int x = 0; + for_each(_1, lambda(_a = _2)[_a += _1])(v, x); + BOOST_TEST(x == 55); + } + + { + using boost::phoenix::for_each; + using boost::phoenix::push_back; + + int x = 10; + std::vector > v(10); + + for_each(_1, lambda(_a = _2)[push_back(_1, _a)])(v, x); + + int y = 0; + for_each(arg1, lambda[ref(y) += _1[0]])(v); + BOOST_TEST(y == 100); + } + + { + int x = 1, y = 10, z = 13; + BOOST_TEST( + lambda(_a = _1, _b = _2) + [ + _1 + _a + _b + ] + (x, z)(y) == x + y + z + ); + } + + { + int x = (let(_a = lambda[val(1)])[_a])()(); + BOOST_TEST(x == 1); + } + + return boost::report_errors(); +} + diff --git a/phoenix/test/scope/let_tests.cpp b/phoenix/test/scope/let_tests.cpp new file mode 100644 index 000000000..a4d3804e6 --- /dev/null +++ b/phoenix/test/scope/let_tests.cpp @@ -0,0 +1,146 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +#define PHOENIX_LIMIT 6 + +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::local_names; +using namespace std; + +int +main() +{ + { + int x = 1; + BOOST_TEST( + let(_a = _1) + [ + _a + ] + (x) == x + ); + } + + { + int x = 1, y = 10; + BOOST_TEST( + let(_a = _1, _b = _2) + [ + _a + _b + ] + (x, y) == x + y + ); + } + + { + int x = 1, y = 10, z = 13; + BOOST_TEST( + let(_x = _1, _y = _2) + [ + let(_z = _3) + [ + _x + _y + _z + ] + ] + (x, y, z) == x + y + z + ); + } + + { + int x = 1, y = 10; + BOOST_TEST( + let(_x = _1) + [ + _x + + let(_x = _2) + [ + -_x + ] + ] + (x, y) == x + -y + ); + } + + { + int x = 999; + BOOST_TEST( + let(_x = _1) // _x is a reference to _x + [ + _x += 888 + ] + (x) == 999 + 888 + ); + + BOOST_TEST(x == 888 + 999); + } + + { + int x = 999; + BOOST_TEST( + let(_x = val(_1)) // _x holds x by value + [ + val(_x += 888) + ] + (x) == x + 888 + ); + + BOOST_TEST(x == 999); + } + + { + BOOST_TEST( + let(_a = 1, _b = 2, _c = 3, _d = 4, _e = 5) + [ + _a + _b + _c + _d + _e + ] + () == 1 + 2 + 3 + 4 + 5 + ); + } + +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + { + // disallow this: + int i; + (_a + _b)(i); + } +#endif + + { + // show that we can return a local from an outer scope + int y = 0; + int x = (let(_a = 1)[let(_b = _1)[ _a ]])(y); + BOOST_TEST(x == 1); + } + + { + // show that this code returns an lvalue + int i = 1; + let(_a = arg1)[ _a ](i)++; + BOOST_TEST(i == 2); + } + + { + // show that what you put in is what you get out + int i = 1; + int& j = let(_a = arg1)[ _a ](i); + BOOST_TEST(&i == &j); + } + + return boost::report_errors(); +} + diff --git a/phoenix/test/statement/exceptions.cpp b/phoenix/test/statement/exceptions.cpp new file mode 100644 index 000000000..2c58c3bb0 --- /dev/null +++ b/phoenix/test/statement/exceptions.cpp @@ -0,0 +1,100 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include + +#include +#include +#include + +#include + +int main() +{ + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + using namespace std; + + { + try + { + throw_(runtime_error("error"))(); + BOOST_ERROR("exception should have been thrown"); + } + catch(runtime_error& err) + { + BOOST_TEST(err.what() == string("error")); + } + } + + { + try + { + try + { + throw runtime_error("error"); + } + catch(exception&) + { + throw_()(); + BOOST_ERROR("exception should have been rethrown"); + } + } + catch(exception& err) + { + BOOST_TEST(err.what() == string("error")); + } + } + + { + bool caught_exception = false; + + try_ + [ throw_(runtime_error("error")) ] + .catch_() + [ ref(caught_exception) = true ](); + + BOOST_TEST(caught_exception); + } + + { + bool caught_exception = false; + try_ + [ throw_(runtime_error("error")) ] + .catch_all + [ ref(caught_exception) = true ](); + BOOST_TEST(caught_exception); + } + + { + bool caught_correct_exception = false; + try_ + [ throw_(runtime_error("error")) ] + .catch_() + [ ref(caught_correct_exception) = false ] + .catch_() + [ ref(caught_correct_exception) = true](); + + BOOST_TEST(caught_correct_exception); + } + + { + bool caught_correct_exception = false; + try_ + [ throw_(runtime_error("error")) ] + .catch_() + [ ref(caught_correct_exception) = false ] + .catch_all + [ ref(caught_correct_exception) = true](); + + BOOST_TEST(caught_correct_exception); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/statement/if_tests.cpp b/phoenix/test/statement/if_tests.cpp new file mode 100644 index 000000000..7e986bc74 --- /dev/null +++ b/phoenix/test/statement/if_tests.cpp @@ -0,0 +1,70 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + cout << dec; + int x = 0; + + for_each(v.begin(), v.end(), + if_(arg1 > 3 && arg1 <= 8) + [ + cout << arg1 << ", ", + ref(x) += arg1 + ] + ); + + cout << endl; + BOOST_TEST(x == 4+5+6+7+8); + + x = 0; + int y = 0; + int z = 0; + + for_each(v.begin(), v.end(), + if_(arg1 > 5) + [ + cout << arg1 << " > 5\n", + ref(x) += arg1 + ] + .else_ + [ + if_(arg1 == 5) + [ + cout << arg1 << " == 5\n", + ref(z) += arg1 + ] + .else_ + [ + cout << arg1 << " < 5\n", + ref(y) += arg1 + ] + ] + ); + + cout << endl; + BOOST_TEST(x == 6+7+8+9+10); + BOOST_TEST(y == 1+2+3+4); + BOOST_TEST(z == 5); + + return boost::report_errors(); +} diff --git a/phoenix/test/statement/loops_tests.cpp b/phoenix/test/statement/loops_tests.cpp new file mode 100644 index 000000000..f3ad4b2e8 --- /dev/null +++ b/phoenix/test/statement/loops_tests.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + vector t = v; + cout << endl; + int x = 0; + + for_each(v.begin(), v.end(), + ( + while_(arg1--) + [ + cout << arg1 << ", ", + ++ref(x) + ], + cout << val("\n") + ) + ); + + BOOST_TEST(x == 1+2+3+4+5+6+7+8+9+10); + cout << endl; + v = t; + x = 0; + + for_each(v.begin(), v.end(), + ( + do_ + [ + cout << arg1 << ", ", + ++ref(x) + ] + .while_(arg1--), + cout << val("\n") + ) + ); + + BOOST_TEST(x == 2+3+4+5+6+7+8+9+10+11); + cout << endl; + v = t; + x = 0; + + int iii; + for_each(v.begin(), v.end(), + ( + for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii)) + [ + cout << arg1 << ", ", + ++ref(x) + ], + cout << val("\n") + ) + ); + + BOOST_TEST(x == 1+2+3+4+5+6+7+8+9+10); + cout << endl; + v = t; + x = 0; + + return boost::report_errors(); +} diff --git a/phoenix/test/statement/switch_tests.cpp b/phoenix/test/statement/switch_tests.cpp new file mode 100644 index 000000000..5116639bb --- /dev/null +++ b/phoenix/test/statement/switch_tests.cpp @@ -0,0 +1,69 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + for_each(v.begin(), v.end(), + switch_(_1) + [ + // wierd case, why not just use if(...), but valid, nonetheless + case_<4>(cout << val("<4>") << endl) + ] + ); + + cout << endl; + + for_each(v.begin(), v.end(), + switch_(_1) + [ + // wierd case, but valid, nonetheless + default_(cout << val("") << endl) + ] + ); + + cout << endl; + + for_each(v.begin(), v.end(), + switch_(_1) + [ + case_<1>(cout << val("<1>") << endl), + case_<2>(cout << val("<2>") << endl), + case_<3>(cout << val("<3>") << endl), + case_<4>(cout << val("<4>") << endl) + ] + ); + + cout << endl; + + for_each(v.begin(), v.end(), + switch_(_1) + [ + case_<1>(cout << val("<1>") << endl), + case_<2>(cout << val("<2>") << endl), + case_<3>(cout << val("<3>") << endl), + case_<4>(cout << val("<4>") << endl), + default_(cout << val("") << endl) + ] + ); + + return boost::report_errors(); +} diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 000000000..5caa9cbbc --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,98 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2008 Hartmut Kaiser +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +# bring in rules for testing +import testing ; + +{ + test-suite spirit_v2 : + + # run Qi tests + [ run qi/char.cpp : : : : ] + [ run qi/char_class.cpp : : : : ] + [ run qi/lit.cpp : : : : ] + [ run qi/int.cpp : : : : ] + [ run qi/uint.cpp : : : : ] + [ run qi/real.cpp : : : : ] + [ run qi/eps.cpp : : : : ] + [ run qi/none.cpp : : : : ] + [ run qi/lazy.cpp : : : : ] + [ run qi/tst.cpp : : : : ] + [ run qi/symbols.cpp : : : : ] + [ run qi/range_run.cpp : : : : ] + [ run qi/no_case.cpp : : : : ] + [ run qi/lexeme.cpp : : : : ] + [ run qi/raw.cpp : : : : ] + [ run qi/sequence.cpp : : : : ] + [ run qi/alternative.cpp : : : : ] + [ run qi/sequential_or.cpp : : : : ] + [ run qi/permutation.cpp : : : : ] + [ run qi/difference.cpp : : : : ] + [ run qi/list.cpp : : : : ] + [ run qi/optional.cpp : : : : ] + [ run qi/kleene.cpp : : : : ] + [ run qi/plus.cpp : : : : ] + [ run qi/and_predicate.cpp : : : : ] + [ run qi/not_predicate.cpp : : : : ] + [ run qi/expect.cpp : : : : ] + [ run qi/rule.cpp : : : : ] + [ run qi/grammar.cpp : : : : ] + [ run qi/functor.cpp : : : : ] + [ run qi/match_manip.cpp : : : : ] + [ run qi/binary.cpp : : : : ] + [ run qi/debug.cpp : : : : ] + + [ compile-fail qi/grammar_fail.cpp : : ] + [ compile-fail qi/rule_fail.cpp : : ] + + # run Karma tests + [ run karma/alternative.cpp : : : : karma_alternative ] + [ run karma/binary.cpp : : : : karma_binary ] + [ run karma/case_handling.cpp : : : : ] + [ run karma/center_alignment.cpp : : : : ] + [ run karma/char.cpp : : : : karma_char ] + [ run karma/delimiter.cpp : : : : ] + [ run karma/eps.cpp : : : : karma_eps ] + [ run karma/format_manip.cpp : : : : ] + [ run karma/functor.cpp : : : : karma_functor ] + [ run karma/grammar.cpp : : : : karma_grammar ] + [ run karma/int_numerics.cpp : : : : ] + [ run karma/kleene.cpp : : : : karma_kleene ] + [ run karma/lazy.cpp : : : : karma_lazy ] + [ run karma/left_alignment.cpp : : : : ] + [ run karma/list.cpp : : : : karma_list ] + [ run karma/lit.cpp : : : : karma_lit ] + [ run karma/none.cpp : : : : karma_none ] + [ run karma/optional.cpp : : : : karma_optional ] + [ run karma/pattern.cpp : : : : ] + [ run karma/real_numerics.cpp : : : : ] + [ run karma/right_alignment.cpp : : : : ] + [ run karma/sequence.cpp : : : : karma_sequence ] + + [ compile-fail karma/grammar_fail.cpp : : karma_grammar_fail ] + [ compile-fail karma/rule_fail.cpp : : karma_rule_fail ] + + # run support tests + [ run support/hold_any.cpp : : : : ] + [ run support/multi_pass_compile.cpp : : : : ] + [ run support/multi_pass.cpp : : : : ] + + # run lexer tests + [ run lex/lexertl1.cpp : : : : ] + [ run lex/lexertl2.cpp : : : : ] + [ run lex/lexertl3.cpp : : : : ] + [ run lex/lexertl4.cpp : : : : ] + [ run lex/lexertl5.cpp : : : : ] + [ run lex/state_switcher_test.cpp : : : : ] + + ; + +} + + diff --git a/test/karma/alternative.cpp b/test/karma/alternative.cpp new file mode 100644 index 000000000..39e179336 --- /dev/null +++ b/test/karma/alternative.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// #define KARMA_TEST_COMPILE_FAIL + +#include + +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost; + using namespace boost::spirit; + + { + { + BOOST_TEST(test("x", char_('x') | char_('i'))); + BOOST_TEST(test("xi", char_('x') << char_('i') | char_('i'))); + + variant v (10); + BOOST_TEST(test("10", char_ | int_, v)); + BOOST_TEST(test("a", char_('a') | char_ | int_, v)); + + v = 'c'; + BOOST_TEST(test("c", char_ | int_, v)); + BOOST_TEST(test("a", char_('a') | char_ | int_, v)); + } + + { + // test if alternatives with all components having unused + // parameter return attributes them self + fusion::vector v('a', 'b'); + BOOST_TEST(test("axb", char_ << (char_('x') | char_('i')) << char_, v)); + BOOST_TEST(test("axib", + char_ << (char_('x') << char_('i') | char_('i')) << char_, v)); + } + + { + BOOST_TEST(test_delimited("x ", char_('x') | char_('i'), char_(' '))); + BOOST_TEST(test_delimited("x i ", + char_('x') << char_('i') | char_('i'), char_(' '))); + + variant v (10); + BOOST_TEST(test_delimited("10 ", char_ | int_, v, char_(' '))); + + v = 'c'; + BOOST_TEST(test_delimited("c ", char_ | int_, v, char_(' '))); + } + + { + // if nothing matches, the first explicit alternative will be chosen + variant v (10.0); + BOOST_TEST(test("11", char_ | int_(11), v)); + BOOST_TEST(test("10.0", double_ | int_(11), v)); + BOOST_TEST(!test("", char_ | int_, v)); + + v = "c"; + BOOST_TEST(test("11", char_ | int_(11), v)); + BOOST_TEST(test("11", double_ | int_(11), v)); + BOOST_TEST(!test("", char_ | int_, v)); + } + + { + // if nothing matches, the first explicit alternative will be chosen + variant v (10.0); + BOOST_TEST(test_delimited("11 ", char_ | int_(11), v, char_(' '))); + + v = "c"; + BOOST_TEST(test_delimited("11 ", char_ | int_(11), v, char_(' '))); + } + } + + return boost::report_errors(); +} + diff --git a/test/karma/binary.cpp b/test/karma/binary.cpp new file mode 100644 index 000000000..2c650711e --- /dev/null +++ b/test/karma/binary.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(binary_test("\x01", 1, byte, 0x01)); + BOOST_TEST(binary_test("\x01\x02", 2, word, 0x0201)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword, 0x04030201)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword, + 0x0807060504030201LL)); +#endif +#else + BOOST_TEST(binary_test("\x01", 1, byte, 0x01)); + BOOST_TEST(binary_test("\x01\x02", 2, word, 0x0102)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword, 0x01020304)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword, + 0x0102030405060708LL)); +#endif +#endif + } + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(binary_test("\x01", 1, byte(0x01))); + BOOST_TEST(binary_test("\x01\x02", 2, word(0x0201))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + qword(0x0807060504030201LL))); +#endif +#else + BOOST_TEST(binary_test("\x01", 1, byte(0x01))); + BOOST_TEST(binary_test("\x01\x02", 2, word(0x0102))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + qword(0x0102030405060708LL))); +#endif +#endif + } + + { // test big endian binaries + BOOST_TEST(binary_test("\x01\x02", 2, big_word, 0x0102)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, big_dword, 0x01020304)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, big_qword, + 0x0102030405060708LL)); +#endif + } + + { + BOOST_TEST(binary_test("\x01\x02", 2, big_word(0x0102))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, big_dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + big_qword(0x0102030405060708LL))); +#endif + } + + { // test little endian binaries + BOOST_TEST(binary_test("\x01\x02", 2, little_word, 0x0201)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, little_dword, 0x04030201)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, little_qword, + 0x0807060504030201LL)); +#endif + } + + { + BOOST_TEST(binary_test("\x01\x02", 2, little_word(0x0201))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, little_dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + little_qword(0x0807060504030201LL))); +#endif + } + + return boost::report_errors(); +} diff --git a/test/karma/case_handling.cpp b/test/karma/case_handling.cpp new file mode 100644 index 000000000..6d7c53c21 --- /dev/null +++ b/test/karma/case_handling.cpp @@ -0,0 +1,176 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test("x", lower['X'])); + BOOST_TEST(test("x", lower['x'])); + BOOST_TEST(test(L"x", lower[L'X'])); + BOOST_TEST(test(L"x", lower[L'x'])); + + BOOST_TEST(test("x", lower[char_], 'X')); + BOOST_TEST(test("x", lower[char_], 'x')); + BOOST_TEST(test("x", lower[char_('X')])); + BOOST_TEST(test("x", lower[char_('x')])); + + BOOST_TEST(test(" ", lower[space])); + BOOST_TEST(test("\t", lower[space], '\t')); + BOOST_TEST(test("\t", lower[space('\t')])); + + BOOST_TEST(test("x", lower[lower['X']])); + BOOST_TEST(test("x", lower[lower['x']])); + BOOST_TEST(test(L"x", lower[lower[L'X']])); + BOOST_TEST(test(L"x", lower[lower[L'x']])); + + BOOST_TEST(test("x", lower[lower[char_]], 'X')); + BOOST_TEST(test("x", lower[lower[char_]], 'x')); + BOOST_TEST(test("x", lower[lower[char_('X')]])); + BOOST_TEST(test("x", lower[lower[char_('x')]])); + + BOOST_TEST(test(" ", lower[lower[space]])); + BOOST_TEST(test("\t", lower[lower[space]], '\t')); + BOOST_TEST(test("\t", lower[lower[space('\t')]])); + + BOOST_TEST(test("X", upper[lower['X']])); + BOOST_TEST(test("X", upper[lower['x']])); + BOOST_TEST(test(L"X", upper[lower[L'X']])); + BOOST_TEST(test(L"X", upper[lower[L'x']])); + + BOOST_TEST(test("X", upper[lower[char_]], 'X')); + BOOST_TEST(test("X", upper[lower[char_]], 'x')); + BOOST_TEST(test("X", upper[lower[char_('X')]])); + BOOST_TEST(test("X", upper[lower[char_('x')]])); + + BOOST_TEST(test(" ", upper[lower[space]])); + BOOST_TEST(test("\t", upper[lower[space]], '\t')); + BOOST_TEST(test("\t", upper[lower[space('\t')]])); + + BOOST_TEST(test("X", upper['X'])); + BOOST_TEST(test("X", upper['x'])); + BOOST_TEST(test(L"X", upper[L'X'])); + BOOST_TEST(test(L"X", upper[L'x'])); + + BOOST_TEST(test("X", upper[char_], 'X')); + BOOST_TEST(test("X", upper[char_], 'x')); + BOOST_TEST(test("X", upper[char_('X')])); + BOOST_TEST(test("X", upper[char_('x')])); + + BOOST_TEST(test(" ", upper[space])); + BOOST_TEST(test("\t", upper[space], '\t')); + BOOST_TEST(test("\t", upper[space('\t')])); + + BOOST_TEST(test("x", lower[upper['X']])); + BOOST_TEST(test("x", lower[upper['x']])); + BOOST_TEST(test(L"x", lower[upper[L'X']])); + BOOST_TEST(test(L"x", lower[upper[L'x']])); + + BOOST_TEST(test("x", lower[upper[char_]], 'X')); + BOOST_TEST(test("x", lower[upper[char_]], 'x')); + BOOST_TEST(test("x", lower[upper[char_('X')]])); + BOOST_TEST(test("x", lower[upper[char_('x')]])); + + BOOST_TEST(test(" ", lower[upper[space]])); + BOOST_TEST(test("\t", lower[upper[space]], '\t')); + BOOST_TEST(test("\t", lower[upper[space('\t')]])); + + BOOST_TEST(test("X", upper[upper['X']])); + BOOST_TEST(test("X", upper[upper['x']])); + BOOST_TEST(test(L"X", upper[upper[L'X']])); + BOOST_TEST(test(L"X", upper[upper[L'x']])); + + BOOST_TEST(test("X", upper[upper[char_]], 'X')); + BOOST_TEST(test("X", upper[upper[char_]], 'x')); + BOOST_TEST(test("X", upper[upper[char_('X')]])); + BOOST_TEST(test("X", upper[upper[char_('x')]])); + + BOOST_TEST(test(" ", upper[upper[space]])); + BOOST_TEST(test("\t", upper[upper[space]], '\t')); + BOOST_TEST(test("\t", upper[upper[space('\t')]])); + } + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test("a1- ", lower["a1- "])); + BOOST_TEST(test("a1- ", lower["a1- "])); + BOOST_TEST(test("a1- ", lower["a1- "])); + BOOST_TEST(test("a1- ", lower["A1- "])); + + BOOST_TEST(test("a1- ", lower[lit], "a1- ")); + BOOST_TEST(test("a1- ", lower[lit], "A1- ")); + BOOST_TEST(test("a1- ", lower[lit("a1- ")])); + BOOST_TEST(test("a1- ", lower[lit("A1- ")])); + + BOOST_TEST(test("a1- ", lower[lower["a1- "]])); + BOOST_TEST(test("a1- ", lower[lower["a1- "]])); + BOOST_TEST(test("a1- ", lower[lower["a1- "]])); + BOOST_TEST(test("a1- ", lower[lower["A1- "]])); + + BOOST_TEST(test("a1- ", lower[lower[lit]], "a1- ")); + BOOST_TEST(test("a1- ", lower[lower[lit]], "A1- ")); + BOOST_TEST(test("a1- ", lower[lower[lit("a1- ")]])); + BOOST_TEST(test("a1- ", lower[lower[lit("A1- ")]])); + + BOOST_TEST(test("A1- ", upper[lower["a1- "]])); + BOOST_TEST(test("A1- ", upper[lower["a1- "]])); + BOOST_TEST(test("A1- ", upper[lower["a1- "]])); + BOOST_TEST(test("A1- ", upper[lower["A1- "]])); + + BOOST_TEST(test("A1- ", upper[lower[lit]], "a1- ")); + BOOST_TEST(test("A1- ", upper[lower[lit]], "A1- ")); + BOOST_TEST(test("A1- ", upper[lower[lit("a1- ")]])); + BOOST_TEST(test("A1- ", upper[lower[lit("A1- ")]])); + + BOOST_TEST(test("A1- ", upper["a1- "])); + BOOST_TEST(test("A1- ", upper["a1- "])); + BOOST_TEST(test("A1- ", upper["a1- "])); + BOOST_TEST(test("A1- ", upper["A1- "])); + + BOOST_TEST(test("A1- ", upper[lit], "a1- ")); + BOOST_TEST(test("A1- ", upper[lit], "A1- ")); + BOOST_TEST(test("A1- ", upper[lit("a1- ")])); + BOOST_TEST(test("A1- ", upper[lit("A1- ")])); + + BOOST_TEST(test("a1- ", lower[upper["a1- "]])); + BOOST_TEST(test("a1- ", lower[upper["a1- "]])); + BOOST_TEST(test("a1- ", lower[upper["a1- "]])); + BOOST_TEST(test("a1- ", lower[upper["A1- "]])); + + BOOST_TEST(test("a1- ", lower[upper[lit]], "a1- ")); + BOOST_TEST(test("a1- ", lower[upper[lit]], "A1- ")); + BOOST_TEST(test("a1- ", lower[upper[lit("a1- ")]])); + BOOST_TEST(test("a1- ", lower[upper[lit("A1- ")]])); + + BOOST_TEST(test("A1- ", upper[upper["a1- "]])); + BOOST_TEST(test("A1- ", upper[upper["a1- "]])); + BOOST_TEST(test("A1- ", upper[upper["a1- "]])); + BOOST_TEST(test("A1- ", upper[upper["A1- "]])); + + BOOST_TEST(test("A1- ", upper[upper[lit]], "a1- ")); + BOOST_TEST(test("A1- ", upper[upper[lit]], "A1- ")); + BOOST_TEST(test("A1- ", upper[upper[lit("a1- ")]])); + BOOST_TEST(test("A1- ", upper[upper[lit("A1- ")]])); + } + + return boost::report_errors(); +} diff --git a/test/karma/center_alignment.cpp b/test/karma/center_alignment.cpp new file mode 100644 index 000000000..2cdf5bd47 --- /dev/null +++ b/test/karma/center_alignment.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test(" x ", center[char_('x')])); + BOOST_TEST(test(" x ", center[char_], 'x')); + BOOST_TEST(test(" x ", center['x'])); + + BOOST_TEST(test(" x ", center(10)[char_('x')])); + BOOST_TEST(test(" x ", center(10)[char_], 'x')); + BOOST_TEST(test(" x ", center(10)['x'])); + + BOOST_TEST(test("*****x****", center(10, char_('*'))[char_('x')])); + BOOST_TEST(test("*****x****", center(10, '*')[char_], 'x')); + BOOST_TEST(test("*****x****", center(10, '*')['x'])); + + BOOST_TEST(test("*****x****", center(char_('*'))[char_('x')])); + BOOST_TEST(test("*****x****", center(char_('*'))[char_], 'x')); + BOOST_TEST(test("*****x****", center(char_('*'))['x'])); + + BOOST_TEST(test(" x ", center(11)[char_('x')])); + + BOOST_TEST(test(" abc ", center[lit("abc")])); + BOOST_TEST(test(" abc ", center[lit], "abc")); + + BOOST_TEST(test(" abc ", center(10)[lit("abc")])); + BOOST_TEST(test(" abc ", center(10)[lit], "abc")); + BOOST_TEST(test(" abc ", center(10)["abc"])); + + BOOST_TEST(test("****abc***", center(10, char_('*'))[lit("abc")])); + BOOST_TEST(test("****abc***", center(10, '*')[lit], "abc")); + BOOST_TEST(test("****abc***", center(10, '*')["abc"])); + + BOOST_TEST(test("****abc***", center(char_('*'))[lit("abc")])); + BOOST_TEST(test("****abc***", center(char_('*'))[lit], "abc")); + BOOST_TEST(test("****abc***", center(char_('*'))["abc"])); + + BOOST_TEST(test(" abc ", center(11)[lit("abc")])); + + BOOST_TEST(test(" 100 ", center[int_(100)])); + BOOST_TEST(test(" 100 ", center[int_], 100)); + + BOOST_TEST(test(" 100 ", center(10)[int_(100)])); + BOOST_TEST(test(" 100 ", center(10)[int_], 100)); + + BOOST_TEST(test("****100***", center(10, char_('*'))[int_(100)])); + BOOST_TEST(test("****100***", center(10, '*')[int_], 100)); + + BOOST_TEST(test(" 100 ", center(11)[int_(100)])); + + BOOST_TEST(test("****100***", center(char_('*'))[int_(100)])); + BOOST_TEST(test("****100***", center(char_('*'))[int_], 100)); + } + + return boost::report_errors(); +} diff --git a/test/karma/char.cpp b/test/karma/char.cpp new file mode 100644 index 000000000..372fc32b5 --- /dev/null +++ b/test/karma/char.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test(" ", space)); + BOOST_TEST(test(L" ", space)); + BOOST_TEST(!test("\t", space)); + BOOST_TEST(!test(L"\t", space)); + + BOOST_TEST(test(" ", space(' '))); + BOOST_TEST(test(L" ", space(L' '))); + BOOST_TEST(test("\t", space('\t'))); + BOOST_TEST(test(L"\t", space(L'\t'))); + + BOOST_TEST(test(" ", space(' '), '\t')); + BOOST_TEST(test(L" ", space(' '), L'\t')); + BOOST_TEST(test("\t", space('\t'), ' ')); + BOOST_TEST(test(L"\t", space('\t'), L' ')); + + BOOST_TEST(test(" ", space, ' ')); + BOOST_TEST(test(L" ", space, L' ')); + BOOST_TEST(test("\t", space, '\t')); + BOOST_TEST(test(L"\t", space, L'\t')); + } + + { + BOOST_TEST(test("x", 'x')); + BOOST_TEST(test(L"x", L'x')); + BOOST_TEST(!test("x", 'y')); + BOOST_TEST(!test(L"x", L'y')); + + BOOST_TEST(test("x", char_, 'x')); + BOOST_TEST(test(L"x", char_, L'x')); + BOOST_TEST(!test("x", char_, 'y')); + BOOST_TEST(!test(L"x", char_, L'y')); + + BOOST_TEST(test("x", char_('x'))); + BOOST_TEST(!test("x", char_('y'))); + BOOST_TEST(test(L"x", char_(L'x'))); + BOOST_TEST(!test(L"x", char_(L'y'))); + +// BOOST_TEST(test("x", char_("x"))); +// BOOST_TEST(test(L"x", char_(L"x"))); + +#if defined(KARMA_FAIL_COMPILATION) + BOOST_TEST(test("x", char_)); // anychar without a parameter doesn't make any sense +#endif + } + + { + BOOST_TEST(test(L"x", L'x')); + BOOST_TEST(test(L"x", 'x')); + + BOOST_TEST(test(L"x", wchar, L'x')); + BOOST_TEST(test(L"x", wchar, 'x')); + + BOOST_TEST(test(L"x", wchar(L'x'))); + BOOST_TEST(test(L"x", wchar('x'))); + } + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test_delimited("x ", 'x', ' ')); + BOOST_TEST(test_delimited(L"x ", L'x', L' ')); + BOOST_TEST(!test_delimited("x ", 'y', ' ')); + BOOST_TEST(!test_delimited(L"x ", L'y', L' ')); + + BOOST_TEST(test_delimited("x ", 'x', space)); + BOOST_TEST(test_delimited(L"x ", L'x', space(L' '))); + BOOST_TEST(!test_delimited("x ", 'y', space)); + BOOST_TEST(!test_delimited(L"x ", L'y', space(L' '))); + + BOOST_TEST(test_delimited("x ", char_, 'x', space)); + BOOST_TEST(test_delimited(L"x ", char_, L'x', space(L' '))); + BOOST_TEST(!test_delimited("x ", char_, 'y', space)); + BOOST_TEST(!test_delimited(L"x ", char_, L'y', space(L' '))); + + BOOST_TEST(test_delimited("x ", char_('x'), space)); + BOOST_TEST(!test_delimited("x ", char_('y'), space(L' '))); + BOOST_TEST(test_delimited(L"x ", char_(L'x'), space)); + BOOST_TEST(!test_delimited(L"x ", char_(L'y'), space(L' '))); + +// BOOST_TEST(test_delimited("x ", char_("x"), space)); + +#if defined(KARMA_FAIL_COMPILATION) + BOOST_TEST(test_delimited("x ", char_, space)); // anychar without a parameter doesn't make any sense +#endif + } + + { + BOOST_TEST(test_delimited(L"x ", L'x', wchar(' '))); + BOOST_TEST(test_delimited(L"x ", 'x', wchar(' '))); + + BOOST_TEST(test_delimited(L"x ", wchar, L'x', wchar(' '))); + BOOST_TEST(test_delimited(L"x ", wchar, 'x', wchar(' '))); + + BOOST_TEST(test_delimited(L"x ", wchar(L'x'), wchar(' '))); + BOOST_TEST(test_delimited(L"x ", wchar('x'), wchar(' '))); + +#if defined(KARMA_FAIL_COMPILATION) + BOOST_TEST(test_delimited("x ", char_, space)); // anychar without a parameter doesn't make any sense +#endif + } + + // action tests + { + BOOST_TEST(test("x", char_[_1 = val('x')])); + BOOST_TEST(test(L"x", char_[_1 = val(L'x')])); + BOOST_TEST(!test("x", char_[_1 = val('y')])); + BOOST_TEST(!test(L"x", char_[_1 = val(L'y')])); + } + + { // lazy chars + + using namespace boost::phoenix; + BOOST_TEST((test("x", char_(val('x'))))); + BOOST_TEST((test(L"x", char_(val(L'x'))))); + } + + return boost::report_errors(); +} diff --git a/test/karma/delimiter.cpp b/test/karma/delimiter.cpp new file mode 100644 index 000000000..e1a60707a --- /dev/null +++ b/test/karma/delimiter.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include + +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test("a b ", delimit[char_('a') << 'b'])); + BOOST_TEST(test("a*b*", delimit('*')[char_('a') << 'b'])); + + BOOST_TEST(test("ab c d", + char_('a') << delimit[char_('b') << 'c'] << 'd')); + BOOST_TEST(test("ab*c*d", + char_('a') << delimit('*')[char_('b') << 'c'] << 'd')); + + BOOST_TEST(test_delimited("a b ", delimit[char_('a') << 'b'], char_(' '))); + BOOST_TEST(test_delimited("a*b*", delimit('*')[char_('a') << 'b'], char_(' '))); + + BOOST_TEST(test_delimited("a b c d ", + char_('a') << delimit[char_('b') << 'c'] << 'd', char_(' '))); + BOOST_TEST(test_delimited("a b*c*d ", + char_('a') << delimit('*')[char_('b') << 'c'] << 'd', char_(' '))); + } + + { + BOOST_TEST(test("ab", verbatim[char_('a') << 'b'])); + BOOST_TEST(test("abcd", + char_('a') << verbatim[char_('b') << 'c'] << 'd')); + + BOOST_TEST(test_delimited("ab ", + verbatim[char_('a') << 'b'], char_(' '))); + BOOST_TEST(test_delimited("a bc d ", + char_('a') << verbatim[char_('b') << 'c'] << 'd', char_(' '))); + } + + return boost::report_errors(); +} diff --git a/test/karma/eps.cpp b/test/karma/eps.cpp new file mode 100644 index 000000000..a2ee9150c --- /dev/null +++ b/test/karma/eps.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2001-2007 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST(test("", eps)); + } + + { // test action + + using namespace boost::phoenix; + BOOST_TEST(test("", eps(val(true)))); + BOOST_TEST(!test("", eps(val(false)))); + } + + return boost::report_errors(); +} diff --git a/test/karma/format_manip.cpp b/test/karma/format_manip.cpp new file mode 100644 index 000000000..0f291753e --- /dev/null +++ b/test/karma/format_manip.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +template +bool test(Char const *expected, Expr const& xpr) +{ + namespace spirit = boost::spirit; + typedef + spirit::traits::is_component + is_component; + + // report invalid expression error as early as possible + BOOST_MPL_ASSERT_MSG(is_component::value, + xpr_is_not_convertible_to_a_generator, ()); + + typedef + typename spirit::result_of::as_component::type + component; + typedef typename component::director director; + + component c = spirit::as_component(spirit::karma::domain(), xpr); + + std::ostringstream ostrm; + ostrm << c; + return ostrm.good() && ostrm.str() == expected; +} + +template +bool test(Char const *expected, + boost::spirit::karma::detail::format_manip const& fm) +{ + std::ostringstream ostrm; + ostrm << fm; + return ostrm.good() && ostrm.str() == expected; +} + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::karma; + + namespace fusion = boost::fusion; + using namespace boost::phoenix; + + { + BOOST_TEST(test( "a", + char_[_1 = val('a')] + )); + BOOST_TEST(test( "a", + format(char_[_1 = val('a')]) + )); + BOOST_TEST(test( "a ", + format_delimited(char_[_1 = val('a')], space) + )); + BOOST_TEST(test( "a", + format(char_, 'a') + )); + BOOST_TEST(test( "a ", + format_delimited(char_, 'a', space) + )); + } + + { + BOOST_TEST(test( "ab", + char_[_1 = val('a')] << char_[_1 = val('b')] + )); + BOOST_TEST(test( "ab", + format(char_[_1 = val('a')] << char_[_1 = val('b')]) + )); + BOOST_TEST(test( "a b ", + format_delimited(char_[_1 = val('a')] << char_[_1 = val('b')], space) + )); + + fusion::vector t('a', 'b'); + + BOOST_TEST(test( "ab", + format(char_ << char_, t) + )); + BOOST_TEST(test( "a b ", + format_delimited(char_ << char_, t, space) + )); + } + + { + BOOST_TEST(test( "abc", + char_[_1 = 'a'] << char_[_1 = 'b'] << char_[_1 = 'c'] + )); + BOOST_TEST(test( "abc", + format(char_('a') << char_('b') << char_('c')) + )); + BOOST_TEST(test( "a b c ", + format_delimited(char_('a') << char_('b') << char_('c'), space) + )); + + fusion::vector t('a', 'b', 'c'); + + BOOST_TEST(test( "abc", + format(char_ << char_ << char_, t) + )); + BOOST_TEST(test( "a b c ", + format_delimited(char_ << char_ << char_, t, space) + )); + } + + { + BOOST_TEST(test( "a2", + (char_ << int_)[_1 = 'a', _2 = 2] + )); + + fusion::vector t('a', 2); + + BOOST_TEST(test( "a2", + format(char_ << int_, t) + )); + BOOST_TEST(test( "a 2 ", + format_delimited(char_ << int_, t, space) + )); + } + + using namespace boost::assign; + + { + // output all elements of a vector + std::vector v; + v += 'a', 'b', 'c'; + + BOOST_TEST(test( "abc", + (*char_)[_1 = v] + )); + BOOST_TEST(test( "abc", + format(*char_, v) + )); + BOOST_TEST(test( "a b c ", + format_delimited(*char_, v, space) + )); + + // output a comma separated list of vector elements + BOOST_TEST(test( "a, b, c", + (char_ % lit(", "))[_0 = fusion::make_single_view(v)] + )); + BOOST_TEST(test( "a, b, c", + format((char_ % lit(", "))[_0 = fusion::make_single_view(v)]) + )); + BOOST_TEST(test( "a , b , c ", + format_delimited((char_ % ',')[_0 = fusion::make_single_view(v)], space) + )); + BOOST_TEST(test( "a,b,c", + format(char_ % ',', v) + )); + BOOST_TEST(test( "a , b , c ", + format_delimited(char_ % ',', v, space) + )); + + // output all elements of a list + std::list l; + l += 'a', 'b', 'c'; + +// BOOST_TEST(test( "abc", +// (*char_)[_1 = l] +// )); +// BOOST_TEST(test( "abc", +// format((*char_)[_1 = l]) +// )); +// BOOST_TEST(test( "a b c ", +// format_delimited((*char_)[_1 = l], space) +// )); + BOOST_TEST(test( "abc", + format(*char_, l) + )); + BOOST_TEST(test( "a b c ", + format_delimited(*char_, l, space) + )); + } + + return boost::report_errors(); +} + diff --git a/test/karma/functor.cpp b/test/karma/functor.cpp new file mode 100644 index 000000000..c4e83eb4e --- /dev/null +++ b/test/karma/functor.cpp @@ -0,0 +1,58 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +struct number_generator : public boost::spirit::karma::functor_base +{ + template + struct apply + { + typedef int type; + }; + + template + bool operator()(Parameter v, Context& ctx, OutputIterator& sink) const + { + char ch = v % 10 + '0'; + v /= 10; + + if (0 != v) + (*this)(v, ctx, sink); + + *sink = ch; + ++sink; + return true; + } +}; + +boost::spirit::karma::functor_generator number; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::karma; + + { + BOOST_TEST(test("0", number)); + BOOST_TEST(test("1234", number, 1234)); + } + + return boost::report_errors(); +} diff --git a/test/karma/grammar.cpp b/test/karma/grammar.cpp new file mode 100644 index 000000000..59be46e0f --- /dev/null +++ b/test/karma/grammar.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +// include this first to make rules have placeholders (e.g. r._1) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test.hpp" + +using namespace spirit_test; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; + +typedef spirit_test::output_iterator::type outiter_type; + +struct num_list : grammar_def +{ + num_list() + { + using boost::spirit::int_; + num1 = int_(123); + num2 = int_(456); + num3 = int_(789); + start = num1 << ',' << num2 << ',' << num3; + } + + rule start, num1, num2, num3; +}; + +int +main() +{ + { // simple grammar test + num_list def; + grammar nlist(def); + BOOST_TEST(test_delimited("123 , 456 , 789 ", nlist, space)); + } + + { // direct access to the rules + + num_list def; + BOOST_TEST(test_delimited("123 ", def.num1, space)); + BOOST_TEST(test_delimited("123 , 456 , 789 ", def.start, space)); + } + + return boost::report_errors(); +} + diff --git a/test/karma/grammar_fail.cpp b/test/karma/grammar_fail.cpp new file mode 100644 index 000000000..22f837083 --- /dev/null +++ b/test/karma/grammar_fail.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; + +struct num_list : grammar_def > +{ + num_list() + { + using boost::spirit::int_; + start = int_(1) << ',' << int_(0); + } + + rule > start; +}; + +// this test must fail compiling +int main() +{ + using boost::make_function_output_iterator; + using spirit_test::make_string_appender; + + std::string generated; + + num_list def; + bool r = generate_delimited( + make_function_output_iterator(make_string_appender(generated)), + make_generator(def), char_('%') << '\n'); + + return 0; +} diff --git a/test/karma/int_numerics.cpp b/test/karma/int_numerics.cpp new file mode 100644 index 000000000..0ac68e175 --- /dev/null +++ b/test/karma/int_numerics.cpp @@ -0,0 +1,306 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +struct test_minmax +{ + template + void operator()(T) const + { + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + T minval = (std::numeric_limits::min)(); + T maxval = (std::numeric_limits::max)(); + + std::string expected_minval = boost::lexical_cast(minval); + std::string expected_maxval = boost::lexical_cast(maxval); + + // create a correct generator type from the given integer type + typedef typename + boost::mpl::if_< + boost::mpl::bool_::is_signed>, + karma::int_spec, + karma::uint_spec + >::type + int_spec_type; + + int_spec_type const gen = int_spec_type(); + + BOOST_TEST(test(expected_maxval, gen, maxval)); + BOOST_TEST(test(expected_minval, gen, minval)); + BOOST_TEST(test(expected_maxval, gen(maxval))); + BOOST_TEST(test(expected_minval, gen(minval))); + + BOOST_TEST(test_delimited(expected_maxval + " ", gen, maxval, char(' '))); + BOOST_TEST(test_delimited(expected_minval + " ", gen, minval, char(' '))); + BOOST_TEST(test_delimited(expected_maxval + " ", gen(maxval), char(' '))); + BOOST_TEST(test_delimited(expected_minval + " ", gen(minval), char(' '))); + + // action tests + BOOST_TEST(test(expected_maxval, gen[_1 = val(maxval)])); + BOOST_TEST(test(expected_minval, gen[_1 = val(minval)])); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + // this is currently ambiguous with character literals +// BOOST_TEST(test("0", 0)); +// BOOST_TEST(test("123", 123)); +// BOOST_TEST(test("-123", -123)); + + BOOST_TEST(test("0", int_, 0)); + BOOST_TEST(test("123", int_, 123)); + BOOST_TEST(test("-123", int_, -123)); + + BOOST_TEST(test_delimited("0 ", int_, 0, char_(' '))); + BOOST_TEST(test_delimited("123 ", int_, 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", int_, -123, char_(' '))); + + BOOST_TEST(test("0", lower[int_], 0)); + BOOST_TEST(test("123", lower[int_], 123)); + BOOST_TEST(test("-123", lower[int_], -123)); + + BOOST_TEST(test_delimited("0 ", lower[int_], 0, char_(' '))); + BOOST_TEST(test_delimited("123 ", lower[int_], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[int_], -123, char_(' '))); + + BOOST_TEST(test("0", upper[int_], 0)); + BOOST_TEST(test("123", upper[int_], 123)); + BOOST_TEST(test("-123", upper[int_], -123)); + + BOOST_TEST(test_delimited("0 ", upper[int_], 0, char_(' '))); + BOOST_TEST(test_delimited("123 ", upper[int_], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[int_], -123, char_(' '))); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test("0", int_(0))); + BOOST_TEST(test("123", int_(123))); + BOOST_TEST(test("-123", int_(-123))); + + BOOST_TEST(test_delimited("0 ", int_(0), char_(' '))); + BOOST_TEST(test_delimited("123 ", int_(123), char_(' '))); + BOOST_TEST(test_delimited("-123 ", int_(-123), char_(' '))); + + BOOST_TEST(test("0", lower[int_(0)])); + BOOST_TEST(test("123", lower[int_(123)])); + BOOST_TEST(test("-123", lower[int_(-123)])); + + BOOST_TEST(test_delimited("0 ", lower[int_(0)], char_(' '))); + BOOST_TEST(test_delimited("123 ", lower[int_(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[int_(-123)], char_(' '))); + + BOOST_TEST(test("0", upper[int_(0)])); + BOOST_TEST(test("123", upper[int_(123)])); + BOOST_TEST(test("-123", upper[int_(-123)])); + + BOOST_TEST(test_delimited("0 ", upper[int_(0)], char_(' '))); + BOOST_TEST(test_delimited("123 ", upper[int_(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[int_(-123)], char_(' '))); + } + + { + using namespace boost::spirit::ascii; + + karma::int_spec const signed_int = + karma::int_spec(); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test(" 0", signed_int, 0)); + BOOST_TEST(test("+123", signed_int, 123)); + BOOST_TEST(test("-123", signed_int, -123)); + + BOOST_TEST(test_delimited(" 0 ", signed_int, 0, char_(' '))); + BOOST_TEST(test_delimited("+123 ", signed_int, 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", signed_int, -123, char_(' '))); + + BOOST_TEST(test(" 0", lower[signed_int], 0)); + BOOST_TEST(test("+123", lower[signed_int], 123)); + BOOST_TEST(test("-123", lower[signed_int], -123)); + + BOOST_TEST(test_delimited(" 0 ", lower[signed_int], 0, char_(' '))); + BOOST_TEST(test_delimited("+123 ", lower[signed_int], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[signed_int], -123, char_(' '))); + + BOOST_TEST(test(" 0", upper[signed_int], 0)); + BOOST_TEST(test("+123", upper[signed_int], 123)); + BOOST_TEST(test("-123", upper[signed_int], -123)); + + BOOST_TEST(test_delimited(" 0 ", upper[signed_int], 0, char_(' '))); + BOOST_TEST(test_delimited("+123 ", upper[signed_int], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[signed_int], -123, char_(' '))); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test(" 0", signed_int(0))); + BOOST_TEST(test("+123", signed_int(123))); + BOOST_TEST(test("-123", signed_int(-123))); + + BOOST_TEST(test_delimited(" 0 ", signed_int(0), char_(' '))); + BOOST_TEST(test_delimited("+123 ", signed_int(123), char_(' '))); + BOOST_TEST(test_delimited("-123 ", signed_int(-123), char_(' '))); + + BOOST_TEST(test(" 0", lower[signed_int(0)])); + BOOST_TEST(test("+123", lower[signed_int(123)])); + BOOST_TEST(test("-123", lower[signed_int(-123)])); + + BOOST_TEST(test_delimited(" 0 ", lower[signed_int(0)], char_(' '))); + BOOST_TEST(test_delimited("+123 ", lower[signed_int(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[signed_int(-123)], char_(' '))); + + BOOST_TEST(test(" 0", upper[signed_int(0)])); + BOOST_TEST(test("+123", upper[signed_int(123)])); + BOOST_TEST(test("-123", upper[signed_int(-123)])); + + BOOST_TEST(test_delimited(" 0 ", upper[signed_int(0)], char_(' '))); + BOOST_TEST(test_delimited("+123 ", upper[signed_int(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[signed_int(-123)], char_(' '))); + } + + { + /////////////////////////////////////////////////////////////////////// + using boost::spirit::uint_; + using namespace boost::spirit::ascii; + + BOOST_TEST(test("1234", uint_, 1234)); + BOOST_TEST(test("ff", hex, 0xff)); + BOOST_TEST(test("1234", oct, 01234)); + BOOST_TEST(test("11110000", bin, 0xf0)); + + BOOST_TEST(test_delimited("1234 ", uint_, 1234, char_(' '))); + BOOST_TEST(test_delimited("ff ", hex, 0xff, char_(' '))); + BOOST_TEST(test_delimited("1234 ", oct, 01234, char_(' '))); + BOOST_TEST(test_delimited("11110000 ", bin, 0xf0, char_(' '))); + + BOOST_TEST(test("1234", lower[uint_], 1234)); + BOOST_TEST(test("ff", lower[hex], 0xff)); + BOOST_TEST(test("1234", lower[oct], 01234)); + BOOST_TEST(test("11110000", lower[bin], 0xf0)); + + BOOST_TEST(test_delimited("1234 ", lower[uint_], 1234, char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[hex], 0xff, char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[oct], 01234, char_(' '))); + BOOST_TEST(test_delimited("11110000 ", lower[bin], 0xf0, char_(' '))); + + BOOST_TEST(test("1234", upper[uint_], 1234)); + BOOST_TEST(test("FF", upper[hex], 0xff)); + BOOST_TEST(test("1234", upper[oct], 01234)); + BOOST_TEST(test("11110000", upper[bin], 0xf0)); + + BOOST_TEST(test_delimited("1234 ", upper[uint_], 1234, char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[hex], 0xff, char_(' '))); + BOOST_TEST(test_delimited("1234 ", upper[oct], 01234, char_(' '))); + BOOST_TEST(test_delimited("11110000 ", upper[bin], 0xf0, char_(' '))); + + // no generator transformation should occur for uint_'s + BOOST_TEST(test("1234", upper[upper[uint_]], 1234)); + BOOST_TEST(test("1234", upper[lower[uint_]], 1234)); + BOOST_TEST(test("1234", lower[upper[uint_]], 1234)); + BOOST_TEST(test("1234", lower[lower[uint_]], 1234)); + + BOOST_TEST(test_delimited("1234 ", upper[upper[uint_]], 1234, char_(' '))); + BOOST_TEST(test_delimited("1234 ", upper[lower[uint_]], 1234, char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[upper[uint_]], 1234, char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[lower[uint_]], 1234, char_(' '))); + + BOOST_TEST(test("FF", upper[upper[hex]], 0xff)); + BOOST_TEST(test("FF", upper[lower[hex]], 0xff)); + BOOST_TEST(test("ff", lower[upper[hex]], 0xff)); + BOOST_TEST(test("ff", lower[lower[hex]], 0xff)); + + BOOST_TEST(test_delimited("FF ", upper[upper[hex]], 0xff, char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[lower[hex]], 0xff, char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[upper[hex]], 0xff, char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[lower[hex]], 0xff, char_(' '))); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test("1234", uint_(1234))); + BOOST_TEST(test("ff", hex(0xff))); + BOOST_TEST(test("1234", oct(01234))); + BOOST_TEST(test("11110000", bin(0xf0))); + + BOOST_TEST(test_delimited("1234 ", uint_(1234), char_(' '))); + BOOST_TEST(test_delimited("ff ", hex(0xff), char_(' '))); + BOOST_TEST(test_delimited("1234 ", oct(01234), char_(' '))); + BOOST_TEST(test_delimited("11110000 ", bin(0xf0), char_(' '))); + + BOOST_TEST(test("1234", lower[uint_(1234)])); + BOOST_TEST(test("ff", lower[hex(0xff)])); + BOOST_TEST(test("1234", lower[oct(01234)])); + BOOST_TEST(test("11110000", lower[bin(0xf0)])); + + BOOST_TEST(test_delimited("1234 ", lower[uint_(1234)], char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[hex(0xff)], char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[oct(01234)], char_(' '))); + BOOST_TEST(test_delimited("11110000 ", lower[bin(0xf0)], char_(' '))); + + BOOST_TEST(test("1234", upper[uint_(1234)])); + BOOST_TEST(test("FF", upper[hex(0xff)])); + BOOST_TEST(test("1234", upper[oct(01234)])); + BOOST_TEST(test("11110000", upper[bin(0xf0)])); + + BOOST_TEST(test_delimited("1234 ", upper[uint_(1234)], char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[hex(0xff)], char_(' '))); + BOOST_TEST(test_delimited("1234 ", upper[oct(01234)], char_(' '))); + BOOST_TEST(test_delimited("11110000 ", upper[bin(0xf0)], char_(' '))); + + BOOST_TEST(test("FF", upper[upper[hex(0xff)]])); + BOOST_TEST(test("FF", upper[lower[hex(0xff)]])); + BOOST_TEST(test("ff", lower[upper[hex(0xff)]])); + BOOST_TEST(test("ff", lower[lower[hex(0xff)]])); + + BOOST_TEST(test_delimited("FF ", upper[upper[hex(0xff)]], char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[lower[hex(0xff)]], char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[upper[hex(0xff)]], char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[lower[hex(0xff)]], char_(' '))); + } + +// test boundary values + typedef boost::mpl::vector< +#ifdef BOOST_HAS_LONG_LONG + boost::long_long_type, boost::ulong_long_type, +#endif + short, unsigned short, + int, unsigned int, + long, unsigned long + > integer_types; + boost::mpl::for_each(test_minmax()); + + return boost::report_errors(); +} + diff --git a/test/karma/kleene.cpp b/test/karma/kleene.cpp new file mode 100644 index 000000000..7278c3f05 --- /dev/null +++ b/test/karma/kleene.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using namespace boost::spirit; + namespace fusion = boost::fusion; + + { + using namespace boost::assign; + + std::vector v; + v += 'a', 'b', 'c'; + + BOOST_TEST(test("abc", *char_, v)); + BOOST_TEST(test_delimited("a b c ", *char_, v, ' ')); + } + + { + using namespace boost::assign; + + std::vector v; + + // these need to fail, because the return value should be false + BOOST_TEST(!test("", +char_, v)); + BOOST_TEST(!test_delimited("", +char_, v, ' ')); + + v += 'a', 'b', 'c'; + + BOOST_TEST(test("abc", +char_, v)); + BOOST_TEST(test_delimited("a b c ", +char_, v, ' ')); + } + + { + using namespace boost::assign; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + std::vector v; + v += 10, 20, 30; + + BOOST_TEST(test("102030", *int_, v)); + BOOST_TEST(test_delimited("10, 20, 30, ", *int_, v, lit(", "))); + + typedef fusion::vector fvec; + std::vector sv; + sv += fvec(10), fvec(20), fvec(30); + + BOOST_TEST(test("10,20,30,", *(int_ << ','), sv)); + BOOST_TEST(test_delimited("10 , 20 , 30 , ", *(int_ << ','), sv, lit(" "))); + + fusion::vector cc ('a', 'c'); + BOOST_TEST(test("ac", char_ << *(char_(' ') << ',') << char_, cc)); + BOOST_TEST(test_delimited("a c ", + char_ << *(char_(' ') << ',') << char_, cc, " ")); + } + + { + using namespace boost::assign; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + std::vector v; + + BOOST_TEST(!test("", +int_, v)); + BOOST_TEST(!test_delimited("", +int_, v, lit(", "))); + + v += 10, 20, 30; + + BOOST_TEST(test("102030", +int_, v)); + BOOST_TEST(test_delimited("10, 20, 30, ", +int_, v, lit(", "))); + + typedef fusion::vector fvec; + std::vector sv; + sv += fvec(10), fvec(20), fvec(30); + + BOOST_TEST(test("10,20,30,", +(int_ << ','), sv)); + BOOST_TEST(test_delimited("10 , 20 , 30 , ", +(int_ << ','), sv, lit(" "))); + + fusion::vector cc ('a', 'c'); + BOOST_TEST(!test("", char_ << +(char_(' ') << ',') << char_, cc)); + BOOST_TEST(!test_delimited("", + char_ << +(char_(' ') << ',') << char_, cc, " ")); + } + + return boost::report_errors(); +} + diff --git a/test/karma/lazy.cpp b/test/karma/lazy.cpp new file mode 100644 index 000000000..16a5f29c1 --- /dev/null +++ b/test/karma/lazy.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::karma; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { + BOOST_TEST(test("123", lazy(val(int_)), 123)); + } + + { + int result = 123; + BOOST_TEST(test("123", lazy(val(int_))[_1 = ref(result)])); + } + + { + typedef spirit_test::output_iterator::type outiter_type; + rule r; + + r = char_('<') << lazy(_r1) << '>' << "'; + + std::string tag("tag"), foo("foo"); + BOOST_TEST(test("", r (ref(tag)))); + BOOST_TEST(!test("", r (ref(foo)))); + } + + return boost::report_errors(); +} diff --git a/test/karma/left_alignment.cpp b/test/karma/left_alignment.cpp new file mode 100644 index 000000000..b6be0a0d9 --- /dev/null +++ b/test/karma/left_alignment.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test("x ", left_align[char_('x')])); + BOOST_TEST(test("x ", left_align[char_], 'x')); + BOOST_TEST(test("x ", left_align['x'])); + + BOOST_TEST(test("x ", left_align(10)[char_('x')])); + BOOST_TEST(test("x ", left_align(10)[char_], 'x')); + BOOST_TEST(test("x ", left_align(10)['x'])); + + BOOST_TEST(test("x*********", left_align(10, char_('*'))[char_('x')])); + BOOST_TEST(test("x*********", left_align(10, '*')[char_], 'x')); + BOOST_TEST(test("x*********", left_align(10, '*')['x'])); + + BOOST_TEST(test("x*********", left_align(char_('*'))[char_('x')])); + BOOST_TEST(test("x*********", left_align(char_('*'))[char_], 'x')); + BOOST_TEST(test("x*********", left_align(char_('*'))['x'])); + + BOOST_TEST(test("abc ", left_align[lit("abc")])); + BOOST_TEST(test("abc ", left_align[lit], "abc")); + + BOOST_TEST(test("abc ", left_align(10)[lit("abc")])); + BOOST_TEST(test("abc ", left_align(10)[lit], "abc")); + BOOST_TEST(test("abc ", left_align(10)["abc"])); + + BOOST_TEST(test("abc*******", left_align(10, char_('*'))[lit("abc")])); + BOOST_TEST(test("abc*******", left_align(10, '*')[lit], "abc")); + BOOST_TEST(test("abc*******", left_align(10, '*')["abc"])); + + BOOST_TEST(test("abc*******", left_align(char_('*'))[lit("abc")])); + BOOST_TEST(test("abc*******", left_align(char_('*'))[lit], "abc")); + BOOST_TEST(test("abc*******", left_align(char_('*'))["abc"])); + + BOOST_TEST(test("100 ", left_align[int_(100)])); + BOOST_TEST(test("100 ", left_align[int_], 100)); + + BOOST_TEST(test("100 ", left_align(10)[int_(100)])); + BOOST_TEST(test("100 ", left_align(10)[int_], 100)); + + BOOST_TEST(test("100*******", left_align(10, char_('*'))[int_(100)])); + BOOST_TEST(test("100*******", left_align(10, '*')[int_], 100)); + + BOOST_TEST(test("100*******", left_align(char_('*'))[int_(100)])); + BOOST_TEST(test("100*******", left_align(char_('*'))[int_], 100)); + } + + return boost::report_errors(); +} diff --git a/test/karma/list.cpp b/test/karma/list.cpp new file mode 100644 index 000000000..c9f1d2023 --- /dev/null +++ b/test/karma/list.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + using namespace boost::assign; + + std::vector v; + v += 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'; + + { + BOOST_TEST(test("a,b,c,d,e,f,g,h", char_ % ',', v)); + BOOST_TEST(test_delimited("a , b , c , d , e , f , g , h ", + char_ % ',', v, space)); + } + + { + std::string s ("abcdefgh"); + BOOST_TEST(test("a,b,c,d,e,f,g,h", char_ % ',', s)); + BOOST_TEST(test_delimited("a , b , c , d , e , f , g , h ", + char_ % ',', s, space)); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + BOOST_TEST(test("a,b,c,d,e,f,g,h", (char_ % ',')[_1 = ref(v)])); + BOOST_TEST(test_delimited("a , b , c , d , e , f , g , h ", + (char_ % ',')[_1 = ref(v)], space)); + } + + return boost::report_errors(); +} + diff --git a/test/karma/lit.cpp b/test/karma/lit.cpp new file mode 100644 index 000000000..7b04cd5ed --- /dev/null +++ b/test/karma/lit.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test("a", lit('a'))); + BOOST_TEST(!test("a", lit('b'))); + + BOOST_TEST(test("abc", "abc")); + BOOST_TEST(!test("abcd", "abc")); + + BOOST_TEST(test("abc", lit("abc"))); + BOOST_TEST(!test("abcd", lit("abc"))); + + BOOST_TEST(test("abc", lit, "abc")); + BOOST_TEST(!test("abcd", lit, "abc")); + } + + { + std::string str("abc"); + BOOST_TEST(test("abc", lit(str))); + BOOST_TEST(!test("abcd", lit(str))); + + BOOST_TEST(test("abc", lit, str)); + BOOST_TEST(!test("abcd", lit, str)); + + BOOST_TEST(test("abc", str)); + BOOST_TEST(!test("abcd", str)); + + std::basic_string wstr(L"abc"); + BOOST_TEST(test(L"abc", lit(wstr))); + BOOST_TEST(!test(L"abcd", lit(wstr))); + + BOOST_TEST(test(L"abc", lit, wstr)); + BOOST_TEST(!test(L"abcd", lit, wstr)); + + BOOST_TEST(test(L"abc", wstr)); + BOOST_TEST(!test(L"abcd", wstr)); + } + + { + BOOST_TEST(test(L"a", lit(L'a'))); + BOOST_TEST(!test(L"a", lit(L'b'))); + + BOOST_TEST(test(L"abc", L"abc")); + BOOST_TEST(test(L"abc", "abc")); + BOOST_TEST(!test(L"abcd", L"abc")); + + BOOST_TEST(test(L"abc", lit(L"abc"))); + BOOST_TEST(test(L"abc", wlit(L"abc"))); + BOOST_TEST(!test(L"abcd", lit(L"abc"))); + + BOOST_TEST(test(L"abc", lit, L"abc")); + BOOST_TEST(test(L"abc", wlit, L"abc")); + BOOST_TEST(!test(L"abcd", lit, L"abc")); + + BOOST_TEST(test(L"abc", lit, "abc")); + BOOST_TEST(test(L"abc", wlit, "abc")); + BOOST_TEST(!test(L"abcd", lit, "abc")); + } + + { + BOOST_TEST(test_delimited("a ", lit('a'), ' ')); + BOOST_TEST(!test_delimited("a ", lit('b'), ' ')); + + BOOST_TEST(test_delimited("abc ", "abc", ' ')); + BOOST_TEST(!test_delimited("abcd ", "abc", ' ')); + + BOOST_TEST(test_delimited("abc ", lit("abc"), ' ')); + BOOST_TEST(!test_delimited("abcd ", lit("abc"), ' ')); + + BOOST_TEST(test_delimited("abc ", lit, "abc", ' ')); + BOOST_TEST(!test_delimited("abcd ", lit, "abc", ' ')); + } + + { + BOOST_TEST(test_delimited(L"a ", lit(L'a'), ' ')); + BOOST_TEST(!test_delimited(L"a ", lit(L'b'), ' ')); + + BOOST_TEST(test_delimited(L"abc ", L"abc", ' ')); + BOOST_TEST(!test_delimited(L"abcd ", L"abc", ' ')); + + BOOST_TEST(test_delimited(L"abc ", lit(L"abc"), ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit(L"abc"), ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit("abc"), ' ')); + BOOST_TEST(!test_delimited(L"abcd ", lit(L"abc"), ' ')); + + BOOST_TEST(test_delimited(L"abc ", lit, L"abc", ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit, L"abc", ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit, "abc", ' ')); + BOOST_TEST(!test_delimited(L"abcd ", lit, L"abc", ' ')); + } + + { // test action + + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + using namespace boost::spirit::ascii; + + std::string str("abc"); + BOOST_TEST(test("abc", lit[_1 = ref(str)])); + BOOST_TEST(test_delimited("abc ", lit[_1 = ref(str)], space)); + } + + { // lazy strings + + using namespace boost::phoenix; + std::basic_string s("abc"); + BOOST_TEST((test("abc", lit(val(s))))); + + std::basic_string ws(L"abc"); + BOOST_TEST((test(L"abc", lit(ref(ws))))); + } + + return boost::report_errors(); +} diff --git a/test/karma/none.cpp b/test/karma/none.cpp new file mode 100644 index 000000000..8fde9474c --- /dev/null +++ b/test/karma/none.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using namespace spirit_test; + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST((!test("", none, 1))); + BOOST_TEST((!test("", none, "test"))); + } + + { + BOOST_TEST((!test_delimited(" ", none, 1, space))); + BOOST_TEST((!test_delimited(" ", none, "test", space))); + } + + return boost::report_errors(); +} diff --git a/test/karma/optional.cpp b/test/karma/optional.cpp new file mode 100644 index 000000000..94dbceaae --- /dev/null +++ b/test/karma/optional.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int main() +{ + using namespace spirit_test; + using namespace boost::spirit; + + { + boost::optional opt; + BOOST_TEST(test("", -int_, opt)); + + opt = 10; + BOOST_TEST(test("10", -int_, opt)); + } + + { + using namespace boost::spirit::ascii; + + boost::optional opt; + BOOST_TEST(test_delimited("", -int_, opt, space)); + + opt = 10; + BOOST_TEST(test_delimited("10 ", -int_, opt, space)); + } + + { // test action + using namespace boost::phoenix; + namespace phoenix = boost::phoenix; + using namespace boost::spirit::arg_names; + + boost::optional n ; + BOOST_TEST(test("", (-int_)[_1 = phoenix::ref(n)])); + + n = 1234; + BOOST_TEST(test("1234", (-int_)[_1 = phoenix::ref(n)])); + } + + { // test action + using namespace boost::phoenix; + namespace phoenix = boost::phoenix; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::ascii; + + boost::optional n; + BOOST_TEST(test_delimited("", (-int_)[_1 = phoenix::ref(n)], space)); + + n = 1234; + BOOST_TEST(test_delimited("1234 ", (-int_)[_1 = phoenix::ref(n)], space)); + } + + return boost::report_errors(); +} diff --git a/test/karma/pattern.cpp b/test/karma/pattern.cpp new file mode 100644 index 000000000..92b851b6d --- /dev/null +++ b/test/karma/pattern.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +// include this first to make rules have placeholders (e.g. _r1) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using namespace boost; + using namespace boost::spirit; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::karma; + using namespace boost::spirit::ascii; + + typedef spirit_test::output_iterator::type outiter_type; + + // basic tests + { + rule start; + + start = char_[_1 = 'a'] << int_[_1 = 10] << double_[_1 = 12.4]; + BOOST_TEST(test("a1012.4", start)); + + start = (char_ << int_ << double_)[_1 = 'a', _2 = 10, _3 = 12.4]; + BOOST_TEST(test("a1012.4", start)); + + rule a, b, c; + a = char_[_1 = 'a']; + b = int_[_1 = 10]; + c = double_[_1 = 12.4]; + + start = a << b << c; + BOOST_TEST(test("a1012.4", start)); + } + + // basic tests involving a direct parameter + { + typedef variant var_type; + fusion::vector v (unused, 'a'); + + rule start; + + start = (char_ | int_ | double_)[_1 = _r1]; + BOOST_TEST(test("a", start, v)); + + v = fusion::vector(unused, 10); + BOOST_TEST(test("10", start, v)); + v = fusion::vector(unused, 12.4); + BOOST_TEST(test("12.4", start, v)); + } + + { + rule start; + fusion::vector vec(unused, 'a', 10, 12.4); + + start = char_[_1 = _r1] << int_[_1 = _r2] << double_[_1 = _r3]; + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + + start = (char_ << int_ << double_)[_1 = _r1, _2 = _r2, _3 = _r3]; + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + + rule a; + rule b; + rule c; + + a = char_[_1 = _r1]; + b = int_[_1 = _r1]; + c = double_[_1 = _r1]; + start = a(_r1) << b(_r2) << c(_r3); + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + } + + // test rule parameter propagation + { + rule start; + fusion::vector vec(unused, 'a', 10, 12.4); + + start %= char_ << int_ << double_; + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + + rule a; + rule b; + rule c; + + a %= char_ << eps; + b %= int_; + c %= double_; + start = a(_r1) << b(_r2) << c(_r3); + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + } + + // basic tests with delimiter + { + rule start; + + start = char_[_1 = 'a'] << int_[_1 = 10] << double_[_1 = 12.4]; + BOOST_TEST(test_delimited("a 10 12.4 ", start, space)); + + start = (char_ << int_ << double_)[_1 = 'a', _2 = 10, _3 = 12.4]; + BOOST_TEST(test_delimited("a 10 12.4 ", start, space)); + + rule a, b, c; + a = char_[_1 = 'a']; + b = int_[_1 = 10]; + c = double_[_1 = 12.4]; + + start = a << b << c; + BOOST_TEST(test_delimited("a 10 12.4 ", start, space)); + } + + // locals test + { + rule > start; + + start = lit[_1 = "abc", _a = _1] << int_[_1 = 10] << lit[_1 = _a]; + BOOST_TEST(test("abc10abc", start)); + } + + //~ { // alias tests + //~ typedef variant var_type; + //~ fusion::vector v (unused, 'a'); + + //~ rule d, start; + + //~ d = start.alias(); // d will always track start + + //~ start = (char_ | int_ | double_)[_1 = _r1]; + //~ BOOST_TEST(test("a", d, v)); + + //~ v = fusion::vector(unused, 10); + //~ BOOST_TEST(test("10", d, v)); + //~ v = fusion::vector(unused, 12.4); + //~ BOOST_TEST(test("12.4", d, v)); + //~ } + + //~ { // copy tests + + //~ rule a, b, c, start; + + //~ a = 'a'; + //~ b = 'b'; + //~ c = 'c'; + + //~ // The FF is the dynamic equivalent of start = *(a | b | c); + //~ start = a; + //~ start = start.copy() | b; + //~ start = start.copy() | c; + //~ start = *(start.copy()); + + //~ BOOST_TEST(test("abcabcacb", start)); + + //~ // The FF is the dynamic equivalent of start = (a | b) >> (start | b); + //~ start = b; + //~ start = a | copy(start); + //~ start = start.copy() >> (start | b); + + //~ BOOST_TEST(test("aaaabababaaabbb", start)); + //~ BOOST_TEST(test("aaaabababaaabba", start, false)); + //~ } + + return boost::report_errors(); +} + diff --git a/test/karma/real_numerics.cpp b/test/karma/real_numerics.cpp new file mode 100644 index 000000000..a8a9dc378 --- /dev/null +++ b/test/karma/real_numerics.cpp @@ -0,0 +1,448 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include +#include +#include + +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces the scientific notation +template +struct scientific_policy : boost::spirit::karma::real_generator_policies +{ + // we want the numbers always to be in scientific format + typedef boost::spirit::karma::real_generator_policies base_type; + static int floatfield(T n) { return base_type::scientific; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces the fixed notation +template +struct fixed_policy : boost::spirit::karma::real_generator_policies +{ + typedef boost::spirit::karma::real_generator_policies base_type; + + // we want the numbers always to be in scientific format + static int floatfield(T n) { return base_type::fixed; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces to output trailing zeros in the +// fractional part +template +struct trailing_zeros_policy + : boost::spirit::karma::real_generator_policies // 4 digits +{ + // we want the numbers always to contain trailing zeros up to 4 digits in + // the fractional part + static bool const trailing_zeros = true; + + // we want to generate up to 4 fractional digits + static unsigned int precision(T n) { return 4; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces the sign to be generated +template +struct signed_policy + : boost::spirit::karma::real_generator_policies +{ + // we want to always have a sign generated + static bool const force_sign = true; +}; + +/////////////////////////////////////////////////////////////////////////////// +// this is a workaround for the missing lround for the real_concept type +long lround(boost::math::concepts::real_concept n) +{ + if (n >= 0) + return boost::math::tools::real_cast(n + 0.5); + return boost::math::tools::real_cast(n - 0.5); +} + +/////////////////////////////////////////////////////////////////////////////// +// We need to specialize is_real_lit_tag to allow to use a real_concept as a +// literal below +namespace boost { namespace spirit +{ + template + struct is_real_lit_tag + : boost::mpl::true_ {}; +}} + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + /////////////////////////////////////////////////////////////////////// + // use the default real_policies + BOOST_TEST(test("0.0", double_, 0.0)); + BOOST_TEST(test("1.0", double_, 1.0)); + BOOST_TEST(test("1.0", double_, 1.0001)); + BOOST_TEST(test("1.001", double_, 1.001)); + BOOST_TEST(test("1.01", double_, 1.010)); + BOOST_TEST(test("1.1", double_, 1.100)); + + BOOST_TEST(test("1.234e-04", double_, 0.00012345)); + BOOST_TEST(test("0.001", double_, 0.0012345)); + BOOST_TEST(test("0.012", double_, 0.012345)); + BOOST_TEST(test("0.123", double_, 0.12345)); + BOOST_TEST(test("1.234", double_, 1.2345)); + BOOST_TEST(test("12.346", double_, 12.346)); + BOOST_TEST(test("123.46", double_, 123.46)); + BOOST_TEST(test("1234.5", double_, 1234.5)); + BOOST_TEST(test("12342.0", double_, 12342.)); + BOOST_TEST(test("1.234e05", double_, 123420.)); + + BOOST_TEST(test("-1.0", double_, -1.0)); + BOOST_TEST(test("-1.234", double_, -1.2345)); + BOOST_TEST(test("-1.235", double_, -1.2346)); + BOOST_TEST(test("-1234.2", double_, -1234.2)); + + BOOST_TEST(test("1.0", double_(1.0))); + BOOST_TEST(test("1.0", double_(1.0001))); + BOOST_TEST(test("1.001", double_(1.001))); + BOOST_TEST(test("1.01", double_(1.010))); + BOOST_TEST(test("1.1", double_(1.100))); + + BOOST_TEST(test("1.234e-04", double_(0.00012345))); + BOOST_TEST(test("0.001", double_(0.0012345))); + BOOST_TEST(test("0.012", double_(0.012345))); + BOOST_TEST(test("0.123", double_(0.12345))); + BOOST_TEST(test("1.234", double_(1.2345))); + BOOST_TEST(test("12.346", double_(12.346))); + BOOST_TEST(test("123.46", double_(123.46))); + BOOST_TEST(test("1234.5", double_(1234.5))); + BOOST_TEST(test("12342.0", double_(12342.))); + BOOST_TEST(test("1.234e05", double_(123420.))); + } + + { + /////////////////////////////////////////////////////////////////////// + // test NaN and Inf + BOOST_TEST(test("nan", double_, std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-nan", double_, -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("inf", double_, std::numeric_limits::infinity())); + BOOST_TEST(test("-inf", double_, -std::numeric_limits::infinity())); + + typedef karma::real_spec > signed_type; + signed_type const signed_ = signed_type(); + + BOOST_TEST(test("+nan", signed_, std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-nan", signed_, -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("+inf", signed_, std::numeric_limits::infinity())); + BOOST_TEST(test("-inf", signed_, -std::numeric_limits::infinity())); + BOOST_TEST(test(" 0.0", signed_, 0.0)); + + BOOST_TEST(test("+nan", signed_(std::numeric_limits::quiet_NaN()))); + BOOST_TEST(test("-nan", signed_(-std::numeric_limits::quiet_NaN()))); + BOOST_TEST(test("+inf", signed_(std::numeric_limits::infinity()))); + BOOST_TEST(test("-inf", signed_(-std::numeric_limits::infinity()))); + BOOST_TEST(test(" 0.0", signed_(0.0))); + } + + { + /////////////////////////////////////////////////////////////////////// + typedef karma::real_spec > + trailing_zeros_type; + trailing_zeros_type const trail_zeros = trailing_zeros_type(); + + BOOST_TEST(test("0.0000", trail_zeros, 0.0)); + BOOST_TEST(test("1.0000", trail_zeros, 1.0)); + BOOST_TEST(test("1.0001", trail_zeros, 1.0001)); + BOOST_TEST(test("1.0010", trail_zeros, 1.001)); + BOOST_TEST(test("1.0100", trail_zeros, 1.010)); + BOOST_TEST(test("1.1000", trail_zeros, 1.100)); + + BOOST_TEST(test("1.2345e-04", trail_zeros, 0.00012345)); + BOOST_TEST(test("0.0012", trail_zeros, 0.0012345)); + BOOST_TEST(test("0.0123", trail_zeros, 0.012345)); + BOOST_TEST(test("0.1235", trail_zeros, 0.12345)); + BOOST_TEST(test("1.2345", trail_zeros, 1.2345)); + BOOST_TEST(test("12.3460", trail_zeros, 12.346)); + BOOST_TEST(test("123.4600", trail_zeros, 123.46)); + BOOST_TEST(test("1234.5000", trail_zeros, 1234.5)); + BOOST_TEST(test("12342.0000", trail_zeros, 12342.)); + BOOST_TEST(test("1.2342e05", trail_zeros, 123420.)); + + BOOST_TEST(test("-1.0000", trail_zeros, -1.0)); + BOOST_TEST(test("-1.2345", trail_zeros, -1.2345)); + BOOST_TEST(test("-1.2346", trail_zeros, -1.2346)); + BOOST_TEST(test("-1234.2000", trail_zeros, -1234.2)); + + BOOST_TEST(test("1.0000", trail_zeros(1.0))); + BOOST_TEST(test("1.0001", trail_zeros(1.0001))); + BOOST_TEST(test("1.0010", trail_zeros(1.001))); + BOOST_TEST(test("1.0100", trail_zeros(1.010))); + BOOST_TEST(test("1.1000", trail_zeros(1.100))); + + BOOST_TEST(test("1.2345e-04", trail_zeros(0.00012345))); + BOOST_TEST(test("0.0012", trail_zeros(0.0012345))); + BOOST_TEST(test("0.0123", trail_zeros(0.012345))); + BOOST_TEST(test("0.1235", trail_zeros(0.12345))); + BOOST_TEST(test("1.2345", trail_zeros(1.2345))); + BOOST_TEST(test("12.3460", trail_zeros(12.346))); + BOOST_TEST(test("123.4600", trail_zeros(123.46))); + BOOST_TEST(test("1234.5000", trail_zeros(1234.5))); + BOOST_TEST(test("12342.0000", trail_zeros(12342.))); + BOOST_TEST(test("1.2342e05", trail_zeros(123420.))); + } + + { + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test_delimited("0.0 ", double_, 0.0, char_(' '))); + BOOST_TEST(test_delimited("1.0 ", double_, 1.0, char_(' '))); + BOOST_TEST(test_delimited("1.0 ", double_, 1.0001, char_(' '))); + BOOST_TEST(test_delimited("1.001 ", double_, 1.001, char_(' '))); + BOOST_TEST(test_delimited("1.01 ", double_, 1.010, char_(' '))); + BOOST_TEST(test_delimited("1.1 ", double_, 1.100, char_(' '))); + + BOOST_TEST(test_delimited("1.234e-04 ", double_, 0.00012345, char_(' '))); + BOOST_TEST(test_delimited("0.001 ", double_, 0.0012345, char_(' '))); + BOOST_TEST(test_delimited("0.012 ", double_, 0.012345, char_(' '))); + BOOST_TEST(test_delimited("0.123 ", double_, 0.12345, char_(' '))); + BOOST_TEST(test_delimited("1.234 ", double_, 1.2345, char_(' '))); + BOOST_TEST(test_delimited("12.346 ", double_, 12.346, char_(' '))); + BOOST_TEST(test_delimited("123.46 ", double_, 123.46, char_(' '))); + BOOST_TEST(test_delimited("1234.5 ", double_, 1234.5, char_(' '))); + BOOST_TEST(test_delimited("12342.0 ", double_, 12342., char_(' '))); + BOOST_TEST(test_delimited("1.234e05 ", double_, 123420., char_(' '))); + + BOOST_TEST(test_delimited("-1.0 ", double_, -1.0, char_(' '))); + BOOST_TEST(test_delimited("-1.234 ", double_, -1.2345, char_(' '))); + BOOST_TEST(test_delimited("-1.235 ", double_, -1.2346, char_(' '))); + BOOST_TEST(test_delimited("-1234.2 ", double_, -1234.2, char_(' '))); + + BOOST_TEST(test_delimited("1.0 ", double_(1.0), char_(' '))); + BOOST_TEST(test_delimited("1.0 ", double_(1.0001), char_(' '))); + BOOST_TEST(test_delimited("1.001 ", double_(1.001), char_(' '))); + BOOST_TEST(test_delimited("1.01 ", double_(1.010), char_(' '))); + BOOST_TEST(test_delimited("1.1 ", double_(1.100), char_(' '))); + + BOOST_TEST(test_delimited("1.234e-04 ", double_(0.00012345), char_(' '))); + BOOST_TEST(test_delimited("0.001 ", double_(0.0012345), char_(' '))); + BOOST_TEST(test_delimited("0.012 ", double_(0.012345), char_(' '))); + BOOST_TEST(test_delimited("0.123 ", double_(0.12345), char_(' '))); + BOOST_TEST(test_delimited("1.234 ", double_(1.2345), char_(' '))); + BOOST_TEST(test_delimited("12.346 ", double_(12.346), char_(' '))); + BOOST_TEST(test_delimited("123.46 ", double_(123.46), char_(' '))); + BOOST_TEST(test_delimited("1234.5 ", double_(1234.5), char_(' '))); + BOOST_TEST(test_delimited("12342.0 ", double_(12342.), char_(' '))); + BOOST_TEST(test_delimited("1.234e05 ", double_(123420.), char_(' '))); + } + + { + /////////////////////////////////////////////////////////////////////// + // test NaN and Inf + BOOST_TEST(test_delimited("nan ", double_, + std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("-nan ", double_, + -std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("inf ", double_, + std::numeric_limits::infinity(), char_(' '))); + BOOST_TEST(test_delimited("-inf ", double_, + -std::numeric_limits::infinity(), char_(' '))); + + typedef karma::real_spec > signed_type; + signed_type const signed_ = signed_type(); + + BOOST_TEST(test_delimited("+nan ", signed_, + std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("-nan ", signed_, + -std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("+inf ", signed_, + std::numeric_limits::infinity(), char_(' '))); + BOOST_TEST(test_delimited("-inf ", signed_, + -std::numeric_limits::infinity(), char_(' '))); + BOOST_TEST(test_delimited(" 0.0 ", signed_, 0.0, char_(' '))); + } + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + typedef karma::real_spec > + science_type; + science_type const science = science_type(); + + BOOST_TEST(test("0.0e00", science, 0.0)); + BOOST_TEST(test("1.0e00", science, 1.0)); + + BOOST_TEST(test("1.234e-05", science, 0.000012345)); + BOOST_TEST(test("1.234e-04", science, 0.00012345)); + BOOST_TEST(test("1.234e-03", science, 0.0012345)); + BOOST_TEST(test("1.234e-02", science, 0.012345)); + BOOST_TEST(test("1.235e-01", science, 0.12345)); // note the rounding error! + BOOST_TEST(test("1.234e00", science, 1.2345)); + BOOST_TEST(test("1.235e01", science, 12.346)); + BOOST_TEST(test("1.235e02", science, 123.46)); + BOOST_TEST(test("1.234e03", science, 1234.5)); + BOOST_TEST(test("1.234e04", science, 12342.)); + BOOST_TEST(test("1.234e05", science, 123420.)); + + BOOST_TEST(test("-1.234e-05", science, -0.000012345)); + BOOST_TEST(test("-1.234e-04", science, -0.00012345)); + BOOST_TEST(test("-1.234e-03", science, -0.0012345)); + BOOST_TEST(test("-1.234e-02", science, -0.012345)); + BOOST_TEST(test("-1.235e-01", science, -0.12345)); // note the rounding error! + BOOST_TEST(test("-1.234e00", science, -1.2345)); + BOOST_TEST(test("-1.235e01", science, -12.346)); + BOOST_TEST(test("-1.235e02", science, -123.46)); + BOOST_TEST(test("-1.234e03", science, -1234.5)); + BOOST_TEST(test("-1.234e04", science, -12342.)); + BOOST_TEST(test("-1.234e05", science, -123420.)); + + BOOST_TEST(test("1.234E-05", upper[science], 0.000012345)); + BOOST_TEST(test("1.234E-04", upper[science], 0.00012345)); + BOOST_TEST(test("1.234E-03", upper[science], 0.0012345)); + BOOST_TEST(test("1.234E-02", upper[science], 0.012345)); + BOOST_TEST(test("1.235E-01", upper[science], 0.12345)); // note the rounding error! + BOOST_TEST(test("1.234E00", upper[science], 1.2345)); + BOOST_TEST(test("1.235E01", upper[science], 12.346)); + BOOST_TEST(test("1.235E02", upper[science], 123.46)); + BOOST_TEST(test("1.234E03", upper[science], 1234.5)); + BOOST_TEST(test("1.234E04", upper[science], 12342.)); + BOOST_TEST(test("1.234E05", upper[science], 123420.)); + + BOOST_TEST(test("-1.234E-05", upper[science], -0.000012345)); + BOOST_TEST(test("-1.234E-04", upper[science], -0.00012345)); + BOOST_TEST(test("-1.234E-03", upper[science], -0.0012345)); + BOOST_TEST(test("-1.234E-02", upper[science], -0.012345)); + BOOST_TEST(test("-1.235E-01", upper[science], -0.12345)); // note the rounding error! + BOOST_TEST(test("-1.234E00", upper[science], -1.2345)); + BOOST_TEST(test("-1.235E01", upper[science], -12.346)); + BOOST_TEST(test("-1.235E02", upper[science], -123.46)); + BOOST_TEST(test("-1.234E03", upper[science], -1234.5)); + BOOST_TEST(test("-1.234E04", upper[science], -12342.)); + BOOST_TEST(test("-1.234E05", upper[science], -123420.)); + } + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + typedef karma::real_spec > fixed_type; + fixed_type const fixed = fixed_type(); + + BOOST_TEST(test("0.0", fixed, 0.0)); + BOOST_TEST(test("1.0", fixed, 1.0)); + + BOOST_TEST(test("0.0", fixed, 0.000012345)); + BOOST_TEST(test("0.0", fixed, 0.00012345)); + BOOST_TEST(test("0.001", fixed, 0.0012345)); + BOOST_TEST(test("0.012", fixed, 0.012345)); + BOOST_TEST(test("0.123", fixed, 0.12345)); + BOOST_TEST(test("1.234", fixed, 1.2345)); + BOOST_TEST(test("12.345", fixed, 12.345)); + BOOST_TEST(test("123.45", fixed, 123.45)); + BOOST_TEST(test("1234.5", fixed, 1234.5)); + BOOST_TEST(test("12342.0", fixed, 12342.)); + BOOST_TEST(test("123420.0", fixed, 123420.)); + BOOST_TEST(test("123420000000000000000.0", fixed, 1.23420e20)); + + BOOST_TEST(test("0.0", fixed, -0.000012345)); + BOOST_TEST(test("0.0", fixed, -0.00012345)); + BOOST_TEST(test("-0.001", fixed, -0.0012345)); + BOOST_TEST(test("-0.012", fixed, -0.012345)); + BOOST_TEST(test("-0.123", fixed, -0.12345)); + BOOST_TEST(test("-1.234", fixed, -1.2345)); + BOOST_TEST(test("-12.346", fixed, -12.346)); + BOOST_TEST(test("-123.46", fixed, -123.46)); + BOOST_TEST(test("-1234.5", fixed, -1234.5)); + BOOST_TEST(test("-12342.0", fixed, -12342.)); + BOOST_TEST(test("-123420.0", fixed, -123420.)); + BOOST_TEST(test("-123420000000000000000.0", fixed, -1.23420e20)); + } + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + // test NaN and Inf + BOOST_TEST(test("NAN", upper[double_], + std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-NAN", upper[double_], + -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("INF", upper[double_], + std::numeric_limits::infinity())); + BOOST_TEST(test("-INF", upper[double_], + -std::numeric_limits::infinity())); + + typedef karma::real_spec > signed_type; + signed_type const signed_ = signed_type(); + + BOOST_TEST(test("+NAN", upper[signed_], + std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-NAN", upper[signed_], + -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("+INF", upper[signed_], + std::numeric_limits::infinity())); + BOOST_TEST(test("-INF", upper[signed_], + -std::numeric_limits::infinity())); + BOOST_TEST(test(" 0.0", upper[signed_], 0.0)); + } + + { + using boost::math::concepts::real_concept; + typedef karma::real_spec custom_type; + custom_type const custom = custom_type(); + + BOOST_TEST(test("0.0", custom, real_concept(0.0))); + BOOST_TEST(test("1.0", custom, real_concept(1.0))); + BOOST_TEST(test("1.0", custom, real_concept(1.0001))); + BOOST_TEST(test("1.001", custom, real_concept(1.001))); + BOOST_TEST(test("1.01", custom, real_concept(1.010))); + BOOST_TEST(test("1.1", custom, real_concept(1.100))); + + BOOST_TEST(test("1.234e-04", custom, real_concept(0.00012345))); + BOOST_TEST(test("0.001", custom, real_concept(0.0012345))); + BOOST_TEST(test("0.012", custom, real_concept(0.012345))); + BOOST_TEST(test("0.123", custom, real_concept(0.12345))); + BOOST_TEST(test("1.234", custom, real_concept(1.2345))); + BOOST_TEST(test("12.346", custom, real_concept(12.346))); + BOOST_TEST(test("123.46", custom, real_concept(123.46))); + BOOST_TEST(test("1234.5", custom, real_concept(1234.5))); + BOOST_TEST(test("12342.0", custom, real_concept(12342.))); + BOOST_TEST(test("1.234e05", custom, real_concept(123420.))); + + BOOST_TEST(test("-1.0", custom, real_concept(-1.0))); + BOOST_TEST(test("-1.234", custom, real_concept(-1.2345))); + BOOST_TEST(test("-1.235", custom, real_concept(-1.2346))); + BOOST_TEST(test("-1234.2", custom, real_concept(-1234.2))); + + BOOST_TEST(test("1.0", custom(real_concept(1.0)))); + BOOST_TEST(test("1.0", custom(real_concept(1.0001)))); + BOOST_TEST(test("1.001", custom(real_concept(1.001)))); + BOOST_TEST(test("1.01", custom(real_concept(1.010)))); + BOOST_TEST(test("1.1", custom(real_concept(1.100)))); + + BOOST_TEST(test("1.234e-04", custom(real_concept(0.00012345)))); + BOOST_TEST(test("0.001", custom(real_concept(0.0012345)))); + BOOST_TEST(test("0.012", custom(real_concept(0.012345)))); + BOOST_TEST(test("0.123", custom(real_concept(0.12345)))); + BOOST_TEST(test("1.234", custom(real_concept(1.2345)))); + BOOST_TEST(test("12.346", custom(real_concept(12.346)))); + BOOST_TEST(test("123.46", custom(real_concept(123.46)))); + BOOST_TEST(test("1234.5", custom(real_concept(1234.5)))); + BOOST_TEST(test("12342.0", custom(real_concept(12342.)))); + BOOST_TEST(test("1.234e05", custom(real_concept(123420.)))); + } + + return boost::report_errors(); +} diff --git a/test/karma/right_alignment.cpp b/test/karma/right_alignment.cpp new file mode 100644 index 000000000..3a1343bdf --- /dev/null +++ b/test/karma/right_alignment.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test(" x", right_align[char_('x')])); + BOOST_TEST(test(" x", right_align[char_], 'x')); + BOOST_TEST(test(" x", right_align['x'])); + + BOOST_TEST(test(" x", right_align(10)[char_('x')])); + BOOST_TEST(test(" x", right_align(10)[char_], 'x')); + BOOST_TEST(test(" x", right_align(10)['x'])); + + BOOST_TEST(test("*********x", right_align(10, char_('*'))[char_('x')])); + BOOST_TEST(test("*********x", right_align(10, '*')[char_], 'x')); + BOOST_TEST(test("*********x", right_align(10, '*')['x'])); + + BOOST_TEST(test("*********x", right_align(char_('*'))[char_('x')])); + BOOST_TEST(test("*********x", right_align(char_('*'))[char_], 'x')); + BOOST_TEST(test("*********x", right_align(char_('*'))['x'])); + + BOOST_TEST(test(" abc", right_align[lit("abc")])); + BOOST_TEST(test(" abc", right_align[lit], "abc")); + + BOOST_TEST(test(" abc", right_align(10)[lit("abc")])); + BOOST_TEST(test(" abc", right_align(10)[lit], "abc")); + BOOST_TEST(test(" abc", right_align(10)["abc"])); + + BOOST_TEST(test("*******abc", right_align(10, char_('*'))[lit("abc")])); + BOOST_TEST(test("*******abc", right_align(10, '*')[lit], "abc")); + BOOST_TEST(test("*******abc", right_align(10, '*')["abc"])); + + BOOST_TEST(test("*******abc", right_align(char_('*'))[lit("abc")])); + BOOST_TEST(test("*******abc", right_align(char_('*'))[lit], "abc")); + BOOST_TEST(test("*******abc", right_align(char_('*'))["abc"])); + + BOOST_TEST(test(" 100", right_align[int_(100)])); + BOOST_TEST(test(" 100", right_align[int_], 100)); + + BOOST_TEST(test(" 100", right_align(10)[int_(100)])); + BOOST_TEST(test(" 100", right_align(10)[int_], 100)); + + BOOST_TEST(test("*******100", right_align(10, char_('*'))[int_(100)])); + BOOST_TEST(test("*******100", right_align(10, '*')[int_], 100)); + + BOOST_TEST(test("*******100", right_align(char_('*'))[int_(100)])); + BOOST_TEST(test("*******100", right_align(char_('*'))[int_], 100)); + } + + return boost::report_errors(); +} diff --git a/test/karma/rule_fail.cpp b/test/karma/rule_fail.cpp new file mode 100644 index 000000000..c22a1c5d7 --- /dev/null +++ b/test/karma/rule_fail.cpp @@ -0,0 +1,36 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; + +// this test must fail compiling +int main() +{ + using boost::make_function_output_iterator; + using spirit_test::make_string_appender; + + std::string generated; + + rule > def; + def = int_(1) << ',' << int_(0); + + bool r = generate_delimited( + make_function_output_iterator(make_string_appender(generated)), + def, char_('%') << '\n'); + + return 0; +} diff --git a/test/karma/sequence.cpp b/test/karma/sequence.cpp new file mode 100644 index 000000000..94461db27 --- /dev/null +++ b/test/karma/sequence.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// #define KARMA_TEST_COMPILE_FAIL + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + namespace fusion = boost::fusion; + + { + { + BOOST_TEST(test("xi", char_('x') << char_('i'))); + BOOST_TEST(!test("xi", char_('x') << char_('o'))); + } + + { + BOOST_TEST(test_delimited("x i ", char_('x') << 'i', char(' '))); + BOOST_TEST(!test_delimited("x i ", + char_('x') << char_('o'), char(' '))); + } + + { + BOOST_TEST(test_delimited("Hello , World ", + lit("Hello") << ',' << "World", char(' '))); + } + + { + fusion::vector p ('a', 'b', "cdefg"); + BOOST_TEST(test("abcdefg", char_ << char_ << lit, p)); + BOOST_TEST(test_delimited("a b cdefg ", + char_ << char_ << lit, p, char(' '))); + } + + { + fusion::vector p ('a', 12, 'c'); + BOOST_TEST(test("a12c", char_ << int_ << char_, p)); + BOOST_TEST(test_delimited("a 12 c ", + char_ << int_ << char_, p, char(' '))); + } + + { + // if all elements of a sequence have unused parameters, the whole + // sequence has an unused parameter as well + fusion::vector p ('a', 'e'); + BOOST_TEST(test("abcde", + char_ << (char_('b') << 'c' << 'd') << char_, p)); + BOOST_TEST(test_delimited("a b c d e ", + char_ << (char_('b') << 'c' << 'd') << char_, p, char(' '))); + } + + { + // literal generators do not need a parameter + fusion::vector p('a', 'c'); + BOOST_TEST(test("abc", char_ << 'b' << char_, p)); + BOOST_TEST(test_delimited("a b c ", + char_ << 'b' << char_, p, char(' '))); + } + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test("aa", lower[char_('A') << 'a'])); + BOOST_TEST(test_delimited("BEGIN END ", + upper[lit("begin") << "end"], char(' '))); + BOOST_TEST(!test_delimited("BEGIN END ", + upper[lit("begin") << "nend"], char(' '))); + + BOOST_TEST(test("Aa ", left_align[char_('A') << 'a'])); + BOOST_TEST(test(" Aa ", center[char_('A') << 'a'])); + BOOST_TEST(test(" Aa", right_align[char_('A') << 'a'])); + } + + // action tests + { + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::ascii; + + BOOST_TEST(test("abcdefg", + (char_ << char_ << lit)[_1 = 'a', _2 = 'b', _3 = "cdefg"])); + BOOST_TEST(test_delimited("a b cdefg ", + (char_ << char_ << lit)[_1 = 'a', _2 = 'b', _3 = "cdefg"], + char(' '))); + + BOOST_TEST(test_delimited("a 12 c ", + (char_ << int_(12) << char_)[_1 = 'a', _2 = 'c'], char(' '))); + + char c = 'c'; + BOOST_TEST(test("abc", + (char_[_1 = 'a'] << 'b' << char_)[_1 = 'x', _2 = ref(c)])); + BOOST_TEST(test_delimited("a b c ", + (char_[_1 = 'a'] << 'b' << char_)[_2 = ref(c)], char(' '))); + + BOOST_TEST(test("aa", lower[char_ << 'A'][_1 = 'A'])); + BOOST_TEST(test("AA", upper[char_ << 'a'][_1 = 'a'])); + + BOOST_TEST(test("Aa ", left_align[char_ << 'a'][_1 = 'A'])); + BOOST_TEST(test(" Aa ", center[char_ << 'a'][_1 = 'A'])); + BOOST_TEST(test(" Aa", right_align[char_ << 'a'][_1 = 'A'])); + } + } + + return boost::report_errors(); +} + diff --git a/test/karma/test.hpp b/test/karma/test.hpp new file mode 100644 index 000000000..740e5d3f3 --- /dev/null +++ b/test/karma/test.hpp @@ -0,0 +1,290 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(BOOST_SPIRIT_KARMA_TEST_FEB_23_2007_1221PM) +#define BOOST_SPIRIT_KARMA_TEST_FEB_23_2007_1221PM + +#include +#include +#include + +#include +#include +#include + +namespace spirit_test +{ + + /////////////////////////////////////////////////////////////////////////// + struct display_type + { + template + void operator()(T const &) const + { + std::cout << typeid(T).name() << std::endl; + } + + template + static void print() + { + std::cout << typeid(T).name() << std::endl; + } + }; + + display_type const display = {}; + + /////////////////////////////////////////////////////////////////////////// + template + struct string_appender + { + string_appender(String& s) + : str(s) + {} + + template + void operator()(T const &x) const + { + str += x; + } + + String& str; + }; + + template + inline string_appender + make_string_appender(String& str) + { + return string_appender(str); + } + + template + struct output_iterator + { + typedef std::basic_string string_type; + typedef string_appender appender_type; + typedef boost::function_output_iterator type; + }; + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test(Char const *expected, Generator const& g) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g); + + return result && generated == expected; + } + + template + inline bool test(std::basic_string const& expected, Generator const& g) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test(Char const *expected, Generator const& g, + Parameter const ¶meter) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g, parameter); + + return result && generated == expected; + } + + template + inline bool test(std::basic_string const& expected, Generator const& g, + Parameter const ¶meter) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g, parameter); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test_delimited(Char const *expected, Generator const& g, + Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, d); + + return result && generated == expected; + } + + template + inline bool test_delimited(std::basic_string const& expected, + Generator const& g, Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, d); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test_delimited(Char const *expected, Generator const& g, + Parameter const ¶meter, Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, parameter, d); + + return result && generated == expected; + } + + template + inline bool test_delimited(std::basic_string const& expected, + Generator const& g, Parameter const ¶meter, Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, parameter, d); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool + binary_test(char const *expected, std::size_t size, + Generator const& g) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g); + + return result && !std::memcmp(generated.c_str(), expected, size); + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool + binary_test(char const *expected, std::size_t size, + Generator const& g, Parameter const ¶meter) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g, parameter); + + return result && !std::memcmp(generated.c_str(), expected, size); + } + +} // namespace spirit_test + +#endif // !BOOST_SPIRIT_KARMA_TEST_FEB_23_2007_1221PM diff --git a/test/lex/lexertl1.cpp b/test/lex/lexertl1.cpp new file mode 100644 index 000000000..feb8d16e2 --- /dev/null +++ b/test/lex/lexertl1.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace spirit_test; + + // the following test aims at the low level lexer and token_def objects, + // normally not visible to/directly used by the user + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + token_def c_comment ("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + token_def cpp_comment ("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + typedef lex::lexer_def lexer_def; + + { + // initialize lexer + lexer_def def; + def.self = c_comment; + def.self += cpp_comment; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + } + + { + // initialize lexer + lexer_def def; + def.self = c_comment | cpp_comment; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + } + + { + // initialize lexer + lexer_def def; + def.self = token_def('+') | '-' | c_comment; + def.self += '*' | cpp_comment; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "+", '+')); + BOOST_TEST(test (lex, "-", '-')); + BOOST_TEST(test (lex, "*", '*')); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl2.cpp b/test/lex/lexertl2.cpp new file mode 100644 index 000000000..c304f4db6 --- /dev/null +++ b/test/lex/lexertl2.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// a simple lexer class +template +struct lexertl_test : boost::spirit::lex::lexer_def +{ + typedef boost::spirit::lex::token_def token_def; + + static std::size_t const CCOMMENT = 1; + static std::size_t const CPPCOMMENT = 2; + + template + void def(Self& self) + { + c_comment = token_def("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + cpp_comment = token_def("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + + self = c_comment; + self += cpp_comment; + } + + token_def c_comment, cpp_comment; +}; + +template +struct wlexertl_test : boost::spirit::lex::lexer_def +{ + typedef + boost::spirit::lex::token_def, wchar_t> + token_def; + + static std::size_t const CCOMMENT = 1; + static std::size_t const CPPCOMMENT = 2; + + template + void def(Self& self) + { + c_comment = token_def(L"\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + cpp_comment = token_def(L"\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + + self = c_comment; + self += cpp_comment; + } + + token_def c_comment, cpp_comment; +}; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace spirit_test; + + // the following test aims at the low level lexer_ and token_ objects, + // normally not visible/used by the user + { + // initialize lexer + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + typedef lexertl_test lexer_def; + + // test lexer for two different input strings + lexer_def def; + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", lexer_def::CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", lexer_def::CPPCOMMENT)); + } + + { + // initialize lexer + typedef std::basic_string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + typedef wlexertl_test lexer_def; + + // test lexer for two different input strings + lexer_def def; + lex::lexer lex(def); + BOOST_TEST(test (lex, L"/* this is a comment */", lexer_def::CCOMMENT)); + BOOST_TEST(test (lex, L"// this is a comment as well\n", lexer_def::CPPCOMMENT)); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl3.cpp b/test/lex/lexertl3.cpp new file mode 100644 index 000000000..616637781 --- /dev/null +++ b/test/lex/lexertl3.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::lex; + using namespace spirit_test; + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + token_def c_comment ("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + token_def cpp_comment ("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + token_def ws_tok ("[\\v\\f\\n\\r]*"); + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + + typedef lexer_type::token_set token_set; + typedef lex::lexer_def lexer_def; + + token_set s; + lexer_def def; + + { + // initialize lexer + std::string str("def"); + lexer_def def; + def.self = c_comment; + def.self += cpp_comment | '1' | '2' | '3' | "abc" | str; + def.self += token_def(' ') | '\t' | ws_tok; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ')); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc")); + BOOST_TEST(test (lex, "def")); + } + + { + // init a token set + token_set ws; + ws = token_def(' ') | '\t' | ws_tok; + + // initialize lexer + lexer_def def; + def.self = c_comment; + def.self += cpp_comment | '1' | '2' | '3'; + def.self("WHITESPACE") = ws; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(!test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ', "WHITESPACE")); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id(), "WHITESPACE")); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl4.cpp b/test/lex/lexertl4.cpp new file mode 100644 index 000000000..0ab9674e9 --- /dev/null +++ b/test/lex/lexertl4.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::lex; + using namespace spirit_test; + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + std::size_t const TOKEN_ID_ABC = 1000; + std::size_t const TOKEN_ID_STR = 1001; + std::size_t const TOKEN_ID_WS = 1002; + + token_def c_comment ("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + token_def cpp_comment ("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + token_def ws_tok ("[\\v\\f\\n\\r]*", TOKEN_ID_WS); + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + + typedef lexer_type::token_set token_set; + typedef lex::lexer_def lexer_def; + + token_set s; + std::string str("def"); + + { + // initialize lexer + lexer_def def; + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + def.self += token_def(' ') | '\t' | ws_tok; + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ')); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + } + + { + // init a token set + token_set ws; + ws.add + (' ')('\t') + (ws_tok) + ; + + // initialize lexer + lexer_def def; + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + + def.self("WHITESPACE").add(ws); + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + + BOOST_TEST(!test (lex, "\n\n\v\f\r", TOKEN_ID_WS)); + BOOST_TEST(test (lex, " ", ' ', "WHITESPACE")); + BOOST_TEST(test (lex, "\n\n\v\f\r", TOKEN_ID_WS, "WHITESPACE")); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl5.cpp b/test/lex/lexertl5.cpp new file mode 100644 index 000000000..5ac26487d --- /dev/null +++ b/test/lex/lexertl5.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// test pattern definition capabilities +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::lex; + using namespace spirit_test; + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + std::size_t const TOKEN_ID_ABC = 1000; + std::size_t const TOKEN_ID_STR = 1001; + std::size_t const TOKEN_ID_WS = 1002; + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + + typedef lexer_type::token_set token_set; + typedef lex::lexer_def lexer_def; + + token_set s; + std::string str("def"); + + { + // initialize lexer + lexer_def def; + + def.self.add_pattern + ("CCOMMENT", "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/") + ("CPPCOMMENT", "\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)") + ("WS", "[\\v\\f\\n\\r]*") + ; + + token_def c_comment ("{CCOMMENT}", CCOMMENT); + token_def cpp_comment ("{CPPCOMMENT}", CPPCOMMENT); + token_def ws_tok ("{WS}"); + + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + def.self += token_def(' ') | '\t' | ws_tok; + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ')); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + } + + { + // initialize lexer + lexer_def def; + + def.self.add_pattern + ("CCOMMENT", "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/") + ("CPPCOMMENT", "\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)") + ("WS", "[\\v\\f\\n\\r]*") + ; + + token_def c_comment ("{CCOMMENT}", CCOMMENT); + token_def cpp_comment ("{CPPCOMMENT}", CPPCOMMENT); + token_def ws_tok ("{WS}"); + + // init a token set + token_set ws; + ws.add + (' ')('\t') + (ws_tok, TOKEN_ID_WS) + ; + + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + + def.self("WHITESPACE").add(ws); + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + + BOOST_TEST(!test (lex, "\n\n\v\f\r", TOKEN_ID_WS)); + BOOST_TEST(test (lex, " ", ' ', "WHITESPACE")); + BOOST_TEST(test (lex, "\n\n\v\f\r", TOKEN_ID_WS, "WHITESPACE")); + } + + return boost::report_errors(); +} diff --git a/test/lex/state_switcher_test.cpp b/test/lex/state_switcher_test.cpp new file mode 100644 index 000000000..72bd9c181 --- /dev/null +++ b/test/lex/state_switcher_test.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "test_parser.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct switch_state_tokens : boost::spirit::lex::lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + self = identifier; + + // any token definition to be used as the skip parser during parsing + // has to be associated with a separate lexer state (here 'WS') + white_space = "[ \\t\\n]+"; + self("WS") = white_space; + + separators = "[,;]"; + self("SEP") = separators; + } + + boost::spirit::lex::token_def<> identifier, white_space, separators; +}; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::qi; + using namespace boost::spirit::lex; + using namespace spirit_test; + + typedef std::string::iterator base_iterator_type; + typedef boost::spirit::lex::lexertl_token token_type; + typedef boost::spirit::lex::lexertl_lexer lexer_type; + + { + // the tokens class will be initialized inside the test_parser function + switch_state_tokens tokens; + + BOOST_TEST(test_parser("ident", tokens.identifier, tokens)); + BOOST_TEST(!test_parser("ident", set_state("WS") >> tokens.identifier, tokens)); + BOOST_TEST(!test_parser("ident", in_state("WS")[tokens.identifier], tokens)); + + BOOST_TEST(test_parser("\t \n", set_state("WS") >> tokens.white_space, tokens)); + BOOST_TEST(test_parser("\t \n", in_state("WS")[tokens.white_space], tokens)); + BOOST_TEST(!test_parser("\t \n", tokens.white_space, tokens)); + } + + { + // the tokens class will be initialized inside the test_parser function + switch_state_tokens tokens; + + BOOST_TEST(test_parser(",ident", tokens.identifier, tokens, + in_state("SEP")[tokens.separators])); + BOOST_TEST(!test_parser(";ident", set_state("WS") >> tokens.identifier, + tokens, in_state("SEP")[tokens.separators])); + BOOST_TEST(!test_parser(",ident", in_state("WS")[tokens.identifier], + tokens, in_state("SEP")[tokens.separators])); + + BOOST_TEST(test_parser(",\t \n", set_state("WS") >> tokens.white_space, + tokens, in_state("SEP")[tokens.separators])); + BOOST_TEST(test_parser(";\t \n", in_state("WS")[tokens.white_space], + tokens, in_state("SEP")[tokens.separators])); + BOOST_TEST(!test_parser(",\t \n", tokens.white_space, tokens, + in_state("SEP")[tokens.separators])); + } + + { + // the tokens class will be initialized inside the test_parser function + switch_state_tokens tokens; + + BOOST_TEST(test_parser("ident\t \n", + tokens.identifier >> set_state("WS") >> tokens.white_space, tokens)); + BOOST_TEST(test_parser("\t \nident", + in_state("WS")[tokens.white_space] >> tokens.identifier, tokens)); + } + + return boost::report_errors(); +} diff --git a/test/lex/test.hpp b/test/lex/test.hpp new file mode 100644 index 000000000..2a587e1b5 --- /dev/null +++ b/test/lex/test.hpp @@ -0,0 +1,89 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(BOOST_SPIRIT_LEX_TEST_MAR_23_2007_0721PM) +#define BOOST_SPIRIT_LEX_TEST_MAR_23_2007_0721PM + +namespace spirit_test +{ + /////////////////////////////////////////////////////////////////////////// + struct display_type + { + template + void operator()(T const &) const + { + std::cout << typeid(T).name() << std::endl; + } + + template + static void print() + { + std::cout << typeid(T).name() << std::endl; + } + }; + + /////////////////////////////////////////////////////////////////////////// + display_type const display = {}; + + /////////////////////////////////////////////////////////////////////////// + template + inline boost::iterator_range const& + get_iterpair(boost::iterator_range const& itp) + { + return itp; + } + + template + inline boost::iterator_range const& + get_iterpair(boost::variant const& v) + { + return get >(v); + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool + test(Lexer& lex, Char const* input, std::size_t token_id = 0, + Char const* state = NULL) + { + typedef typename Lexer::iterator_type iterator_type; + typedef std::basic_string string_type; + + string_type str(input); + typename string_type::iterator it = str.begin(); + + iterator_type first = lex.begin(it, str.end()); + iterator_type last = lex.end(); + + bool r = true; + + if (NULL != state) { + std::size_t stateid = lex.map_state(state); + r = r && (static_cast(~0) != stateid); + first.set_state(stateid); + } + + r = r && lex; + r = r && first != last; + + if (token_id != 0) + r = r && (*first).id() == token_id; + else + r = r && (*first).id() != 0; + + using namespace boost; + + typedef typename Lexer::iterator_type::base_iterator_type iterator; + typedef iterator_range iterpair_type; + iterpair_type const& ip = get_iterpair((*first).value()); + + r = r && string_type(ip.begin(), ip.end()) == str; + return r && first != last && ++first == last; + } +} + +#endif + + diff --git a/test/lex/test_parser.hpp b/test/lex/test_parser.hpp new file mode 100644 index 000000000..ef00e8bc2 --- /dev/null +++ b/test/lex/test_parser.hpp @@ -0,0 +1,67 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_TEST_PARSER_SEP_24_2007_0558PM) +#define BOOST_SPIRIT_TEST_PARSER_SEP_24_2007_0558PM + +#include +#include + +namespace spirit_test +{ + template + bool test_parser(Char const* in, Parser const& p, Tokens& tokens, + bool full_match = true) + { + boost::spirit::lex::lexer lex(tokens); + + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + std::string str (in); + std::string::iterator it_in = str.begin(); + std::string::iterator end_in = str.end(); + + typedef typename + boost::spirit::lex::lexer::iterator_type + iterator_type; + + iterator_type iter = lex.begin(it_in, end_in); + iterator_type end = lex.end(); + + return boost::spirit::qi::parse(iter, end, p) + && (!full_match || (iter == end)); + } + + template + bool test_parser(Char const* in, Parser const& p, Tokens& tokens, + Skipper const& s, bool full_match = true) + { + boost::spirit::lex::lexer lex(tokens); + + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + std::string str (in); + std::string::iterator it_in = str.begin(); + std::string::iterator end_in = str.end(); + + typedef typename + boost::spirit::lex::lexer::iterator_type + iterator_type; + + iterator_type iter = lex.begin(it_in, end_in); + iterator_type end = lex.end(); + + return boost::spirit::qi::phrase_parse(iter, end, p, s) + && (!full_match || (iter == end)); + } + +} + +#endif diff --git a/test/qi/alternative.cpp b/test/qi/alternative.cpp new file mode 100644 index 000000000..c67e9a8e8 --- /dev/null +++ b/test/qi/alternative.cpp @@ -0,0 +1,87 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST((test("a", char_ | char_))); + BOOST_TEST((test("x", char_('x') | char_('i')))); + BOOST_TEST((test("i", char_('x') | char_('i')))); + BOOST_TEST((!test("z", char_('x') | char_('o')))); + BOOST_TEST((test("rock", lit("rock") | lit("roll")))); + BOOST_TEST((test("roll", lit("rock") | lit("roll")))); + BOOST_TEST((test("rock", lit("rock") | int_))); + BOOST_TEST((test("12345", lit("rock") | int_))); + } + + { + boost::variant v; + + BOOST_TEST((test_attr("12345", lit("rock") | int_ | char_, v))); + BOOST_TEST(boost::get(v) == 12345); + + BOOST_TEST((test_attr("rock", lit("rock") | int_ | char_, v))); +// BOOST_TEST(boost::get(&v) == 0); + BOOST_TEST(boost::get(&v) == 0); + BOOST_TEST(boost::get(&v) == 0); + + BOOST_TEST((test_attr("x", lit("rock") | int_ | char_, v))); + BOOST_TEST(boost::get(v) == 'x'); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + namespace phx = boost::phoenix; + + boost::variant v; + + BOOST_TEST((test("12345", (lit("rock") | int_ | char_)[phx::ref(v) = _1]))); + BOOST_TEST(boost::get(v) == 12345); + } + + { + boost::variant v; + BOOST_TEST((test("rock", lit("rock") | char_('x')))); + } + + { + // test if alternatives with all components having unused + // attributes return attributes them self + using namespace boost::fusion; + + vector v; + BOOST_TEST((test_attr("abc", + char_ >> (omit[char_] | omit[char_]) >> char_, v))); + BOOST_TEST((at_c<0>(v) == 'a')); + BOOST_TEST((at_c<1>(v) == 'c')); + } + + return boost::report_errors(); +} + diff --git a/test/qi/and_predicate.cpp b/test/qi/and_predicate.cpp new file mode 100644 index 000000000..027bfc8b2 --- /dev/null +++ b/test/qi/and_predicate.cpp @@ -0,0 +1,27 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((test("1234", &int_, false))); + BOOST_TEST((!test("abcd", &int_))); + } + + return boost::report_errors(); +} diff --git a/test/qi/binary.cpp b/test/qi/binary.cpp new file mode 100644 index 000000000..f871c3e31 --- /dev/null +++ b/test/qi/binary.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + boost::uint8_t uc; + boost::uint16_t us; + boost::uint32_t ui; +#ifdef BOOST_HAS_LONG_LONG + boost::uint64_t ul; +#endif + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(test_attr("\x01", byte, uc) && uc == 0x01); + BOOST_TEST(test_attr("\x01\x02", word, us) && us == 0x0201); + BOOST_TEST(test_attr("\x01\x02\x03\x04", dword, ui) && ui == 0x04030201); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", qword, ul) && + ul == 0x0807060504030201LL); +#endif +#else + BOOST_TEST(test_attr("\x01", byte, uc) && uc == 0x01); + BOOST_TEST(test_attr("\x01\x02", word, us) && us == 0x0102); + BOOST_TEST(test_attr("\x01\x02\x03\x04", dword, ui) && ui == 0x01020304); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", qword, ul) && + ul == 0x0102030405060708LL); +#endif +#endif + } + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(test("\x01", byte(0x01))); + BOOST_TEST(test("\x01\x02", word(0x0201))); + BOOST_TEST(test("\x01\x02\x03\x04", dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", + qword(0x0807060504030201LL))); +#endif +#else + BOOST_TEST(test("\x01", 1, byte(0x01))); + BOOST_TEST(test("\x01\x02", 2, word(0x0102))); + BOOST_TEST(test("\x01\x02\x03\x04", 4, dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + qword(0x0102030405060708LL))); +#endif +#endif + } + + { // test big endian binaries + BOOST_TEST(test_attr("\x01\x02", big_word, us) && us == 0x0102); + BOOST_TEST(test_attr("\x01\x02\x03\x04", big_dword, ui) && ui == 0x01020304); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", big_qword, ul) + && ul == 0x0102030405060708LL); +#endif + } + + { + BOOST_TEST(test("\x01\x02", big_word(0x0102))); + BOOST_TEST(test("\x01\x02\x03\x04", big_dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", + big_qword(0x0102030405060708LL))); +#endif + } + + { // test little endian binaries + BOOST_TEST(test_attr("\x01\x02", little_word, us) && us == 0x0201); + BOOST_TEST(test_attr("\x01\x02\x03\x04", little_dword, ui) && ui == 0x04030201); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", little_qword, ul) + && ul == 0x0807060504030201LL); +#endif + } + + { + BOOST_TEST(test("\x01\x02", little_word(0x0201))); + BOOST_TEST(test("\x01\x02\x03\x04", little_dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", + little_qword(0x0807060504030201LL))); +#endif + } + + return boost::report_errors(); +} diff --git a/test/qi/char.cpp b/test/qi/char.cpp new file mode 100644 index 000000000..b2c52fa4d --- /dev/null +++ b/test/qi/char.cpp @@ -0,0 +1,102 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit::qi; + using namespace boost::spirit::ascii; + using boost::spirit::char_; + using boost::spirit::wchar; + + { + BOOST_TEST(test("x", 'x')); + BOOST_TEST(test(L"x", L'x')); + + BOOST_TEST(test("x", char_)); + BOOST_TEST(test("x", char_('x'))); + BOOST_TEST(!test("x", char_('y'))); + BOOST_TEST(test(L"x", char_(L'x'))); + BOOST_TEST(!test(L"x", char_(L'y'))); + BOOST_TEST(test("x", char_('a', 'z'))); + BOOST_TEST(!test("x", char_('0', '9'))); + + BOOST_TEST(!test("x", ~char_)); + BOOST_TEST(!test("x", ~char_('x'))); + BOOST_TEST(test(" ", ~char_('x'))); + BOOST_TEST(test("X", ~char_('x'))); + BOOST_TEST(!test("x", ~char_('b', 'y'))); + BOOST_TEST(test("a", ~char_('b', 'y'))); + BOOST_TEST(test("z", ~char_('b', 'y'))); + + BOOST_TEST(test("x", ~~char_)); + BOOST_TEST(test("x", ~~char_('x'))); + BOOST_TEST(!test(" ", ~~char_('x'))); + BOOST_TEST(!test("X", ~~char_('x'))); + BOOST_TEST(test("x", ~~char_('b', 'y'))); + BOOST_TEST(!test("a", ~~char_('b', 'y'))); + BOOST_TEST(!test("z", ~~char_('b', 'y'))); + } + + { + BOOST_TEST(test(" x", 'x', space)); + BOOST_TEST(test(L" x", L'x', space)); + + BOOST_TEST(test(" x", char_, space)); + BOOST_TEST(test(" x", char_('x'), space)); + BOOST_TEST(!test(" x", char_('y'), space)); + BOOST_TEST(test(L" x", char_(L'x'), space)); + BOOST_TEST(!test(L" x", char_(L'y'), space)); + BOOST_TEST(test(" x", char_('a', 'z'), space)); + BOOST_TEST(!test(" x", char_('0', '9'), space)); + + } + + { + BOOST_TEST(test(L"x", wchar)); + BOOST_TEST(test(L"x", wchar(L'x'))); + BOOST_TEST(!test(L"x", wchar(L'y'))); + BOOST_TEST(test(L"x", wchar('a', 'z'))); + BOOST_TEST(!test(L"x", wchar('0', '9'))); + + BOOST_TEST(!test(L"x", ~wchar)); + BOOST_TEST(!test(L"x", ~wchar('x'))); + BOOST_TEST(test(L" ", ~wchar('x'))); + BOOST_TEST(test(L"X", ~wchar('x'))); + BOOST_TEST(!test(L"x", ~wchar('b', 'y'))); + BOOST_TEST(test(L"a", ~wchar('b', 'y'))); + BOOST_TEST(test(L"z", ~wchar('b', 'y'))); + + BOOST_TEST(test(L"x", ~~wchar)); + BOOST_TEST(test(L"x", ~~wchar('x'))); + BOOST_TEST(!test(L" ", ~~wchar('x'))); + BOOST_TEST(!test(L"X", ~~wchar('x'))); + BOOST_TEST(test(L"x", ~~wchar('b', 'y'))); + BOOST_TEST(!test(L"a", ~~wchar('b', 'y'))); + BOOST_TEST(!test(L"z", ~~wchar('b', 'y'))); + } + + { // lazy chars + + using namespace boost::phoenix; + BOOST_TEST((test("x", char_(val('x'))))); + BOOST_TEST((test("h", char_(val('a'), val('n'))))); + } + + return boost::report_errors(); +} diff --git a/test/qi/char_class.cpp b/test/qi/char_class.cpp new file mode 100644 index 000000000..32dc38fb6 --- /dev/null +++ b/test/qi/char_class.cpp @@ -0,0 +1,170 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("1", alnum)); + BOOST_TEST(!test(" ", alnum)); + BOOST_TEST(!test("1", alpha)); + BOOST_TEST(test("x", alpha)); + BOOST_TEST(test(" ", blank)); + BOOST_TEST(!test("x", blank)); + BOOST_TEST(test("1", digit)); + BOOST_TEST(!test("x", digit)); + BOOST_TEST(test("a", lower)); + BOOST_TEST(!test("A", lower)); + BOOST_TEST(test("!", punct)); + BOOST_TEST(!test("x", punct)); + BOOST_TEST(test(" ", space)); + BOOST_TEST(test("\n", space)); + BOOST_TEST(test("\r", space)); + BOOST_TEST(test("\t", space)); + BOOST_TEST(test("A", upper)); + BOOST_TEST(!test("a", upper)); + BOOST_TEST(test("A", xdigit)); + BOOST_TEST(test("0", xdigit)); + BOOST_TEST(test("f", xdigit)); + BOOST_TEST(!test("g", xdigit)); + } + + { + using namespace boost::spirit::iso8859_1; + BOOST_TEST(test("1", alnum)); + BOOST_TEST(!test(" ", alnum)); + BOOST_TEST(!test("1", alpha)); + BOOST_TEST(test("x", alpha)); + BOOST_TEST(test(" ", blank)); + BOOST_TEST(!test("x", blank)); + BOOST_TEST(test("1", digit)); + BOOST_TEST(!test("x", digit)); + BOOST_TEST(test("a", lower)); + BOOST_TEST(!test("A", lower)); + BOOST_TEST(test("!", punct)); + BOOST_TEST(!test("x", punct)); + BOOST_TEST(test(" ", space)); + BOOST_TEST(test("\n", space)); + BOOST_TEST(test("\r", space)); + BOOST_TEST(test("\t", space)); + BOOST_TEST(test("A", upper)); + BOOST_TEST(!test("a", upper)); + BOOST_TEST(test("A", xdigit)); + BOOST_TEST(test("0", xdigit)); + BOOST_TEST(test("f", xdigit)); + BOOST_TEST(!test("g", xdigit)); + } + + { + using namespace boost::spirit::standard; + BOOST_TEST(test("1", alnum)); + BOOST_TEST(!test(" ", alnum)); + BOOST_TEST(!test("1", alpha)); + BOOST_TEST(test("x", alpha)); + BOOST_TEST(test(" ", blank)); + BOOST_TEST(!test("x", blank)); + BOOST_TEST(test("1", digit)); + BOOST_TEST(!test("x", digit)); + BOOST_TEST(test("a", lower)); + BOOST_TEST(!test("A", lower)); + BOOST_TEST(test("!", punct)); + BOOST_TEST(!test("x", punct)); + BOOST_TEST(test(" ", space)); + BOOST_TEST(test("\n", space)); + BOOST_TEST(test("\r", space)); + BOOST_TEST(test("\t", space)); + BOOST_TEST(test("A", upper)); + BOOST_TEST(!test("a", upper)); + BOOST_TEST(test("A", xdigit)); + BOOST_TEST(test("0", xdigit)); + BOOST_TEST(test("f", xdigit)); + BOOST_TEST(!test("g", xdigit)); + } + + { + using namespace boost::spirit::standard_wide; + BOOST_TEST(test(L"1", alnum)); + BOOST_TEST(!test(L" ", alnum)); + BOOST_TEST(!test(L"1", alpha)); + BOOST_TEST(test(L"x", alpha)); + BOOST_TEST(test(L" ", blank)); + BOOST_TEST(!test(L"x", blank)); + BOOST_TEST(test(L"1", digit)); + BOOST_TEST(!test(L"x", digit)); + BOOST_TEST(test(L"a", lower)); + BOOST_TEST(!test(L"A", lower)); + BOOST_TEST(test(L"!", punct)); + BOOST_TEST(!test(L"x", punct)); + BOOST_TEST(test(L" ", space)); + BOOST_TEST(test(L"\n", space)); + BOOST_TEST(test(L"\r", space)); + BOOST_TEST(test(L"\t", space)); + BOOST_TEST(test(L"A", upper)); + BOOST_TEST(!test(L"a", upper)); + BOOST_TEST(test(L"A", xdigit)); + BOOST_TEST(test(L"0", xdigit)); + BOOST_TEST(test(L"f", xdigit)); + BOOST_TEST(!test(L"g", xdigit)); + } + + { // test attribute extraction + using boost::spirit::qi::domain; + using boost::spirit::traits::attribute_of; + using boost::spirit::iso8859_1::alpha; + using boost::spirit::iso8859_1::alpha_type; + + BOOST_STATIC_ASSERT(( + boost::is_same< + attribute_of::type + , char>::value)); + + int attr = 0; + BOOST_TEST(test_attr("a", alpha, attr)); + BOOST_TEST(attr == 'a'); + } + + { // test attribute extraction + using boost::spirit::iso8859_1::alpha; + using boost::spirit::iso8859_1::space; + char attr = 0; + BOOST_TEST(test_attr(" a", alpha, attr, space)); + BOOST_TEST(attr == 'a'); + } + + { // test action + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + char ch; + + BOOST_TEST(test("x", alnum[ref(ch) = _1])); + BOOST_TEST(ch == 'x'); + BOOST_TEST(test(" A", alnum[ref(ch) = _1], space)); + BOOST_TEST(ch == 'A'); + } + + return boost::report_errors(); +} diff --git a/test/qi/debug.cpp b/test/qi/debug.cpp new file mode 100644 index 000000000..b12747ecd --- /dev/null +++ b/test/qi/debug.cpp @@ -0,0 +1,241 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#define BOOST_SPIRIT_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit::qi; + using namespace boost::spirit::ascii; + + { // basic tests + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + BOOST_SPIRIT_DEBUG_NODE(a); + BOOST_SPIRIT_DEBUG_NODE(b); + BOOST_SPIRIT_DEBUG_NODE(c); + + start = *(a | b | c); + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test("aaaabababaaabbb", start)); + BOOST_TEST(test("aaaabababaaabba", start, false)); + } + + { // basic tests w/ skipper + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + BOOST_SPIRIT_DEBUG_NODE(a); + BOOST_SPIRIT_DEBUG_NODE(b); + BOOST_SPIRIT_DEBUG_NODE(c); + + start = *(a | b | c); + BOOST_TEST(test(" a b c a b c a c b", start, space)); + + // allow no skipping too: + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test(" a a a a b a b a b a a a b b b ", start, space)); + BOOST_TEST(test(" a a a a b a b a b a a a b b a ", start, space, false)); + } + +// { // alias tests +// +// rule a, b, c, d, start; +// +// a = 'a'; +// b = 'b'; +// c = 'c'; +// d = start.alias(); // d will always track start +// +// start = *(a | b | c); +// BOOST_TEST(test("abcabcacb", d)); +// +// start = (a | b) >> (start | b); +// BOOST_TEST(test("aaaabababaaabbb", d)); +// } +// +// { // copy tests +// +// rule a, b, c, start; +// +// a = 'a'; +// b = 'b'; +// c = 'c'; +// +// // The FF is the dynamic equivalent of start = *(a | b | c); +// start = a; +// start = start.copy() | b; +// start = start.copy() | c; +// start = *(start.copy()); +// +// BOOST_TEST(test("abcabcacb", start)); +// +// // The FF is the dynamic equivalent of start = (a | b) >> (start | b); +// start = b; +// start = a | start.copy(); +// start = start.copy() >> (start | b); +// +// BOOST_TEST(test("aaaabababaaabbb", start)); +// BOOST_TEST(test("aaaabababaaabba", start, false)); +// } +// +// { // context tests +// +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_val; +// +// char ch; +// rule a; +// a = alpha[_val = _1]; +// +// BOOST_TEST(test("x", a[ref(ch) = _1])); +// BOOST_TEST(ch == 'x'); +// +// BOOST_TEST(test_attr("z", a, ch)); // attribute is given. +// BOOST_TEST(ch == 'z'); +// } +// +// { // context (w/arg) tests +// +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_r1; +// using boost::spirit::arg_names::_r2; +// using boost::spirit::arg_names::_val; +// +// char ch; +// rule a; // 1 arg +// a = alpha[_val = _1 + _r1]; +// +// BOOST_TEST(test("x", a(val(1))[ref(ch) = _1])); +// BOOST_TEST(ch == 'x' + 1); +// +// BOOST_TEST(test_attr("a", a(1), ch)); // allow scalars as rule args too. +// BOOST_TEST(ch == 'a' + 1); +// +// rule b; // 2 args +// b = alpha[_val = _1 + _r1 + _r2]; +// BOOST_TEST(test_attr("a", b(1, 2), ch)); +// BOOST_TEST(ch == 'a' + 1 + 2); +// } +// +// { // context (w/locals) tests +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_a; +// using boost::spirit::char_; +// using boost::spirit::locals; +// +// rule > a; // 1 local +// a = alpha[_a = _1] >> char_(_a); +// BOOST_TEST(test("aa", a)); +// BOOST_TEST(!test("ax", a)); +// } +// +// { // context (w/args and locals) tests +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_r1; +// using boost::spirit::arg_names::_a; +// using boost::spirit::char_; +// using boost::spirit::locals; +// +// rule > a; // 1 arg + 1 local +// a = alpha[_a = _1 + _r1] >> char_(_a); +// BOOST_TEST(test("ab", a(val(1)))); +// BOOST_TEST(test("xy", a(val(1)))); +// BOOST_TEST(!test("ax", a(val(1)))); +// } +// +// { // bug: test that injected attributes are ok +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_r1; +// using boost::spirit::arg_names::_val; +// using boost::spirit::char_; +// +// rule r; +// +// // problem code: +// r = char_(_r1)[_val = _1]; +// } + + { // error handling + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::phoenix::val; + using boost::spirit::int_; + using boost::spirit::arg_names::_4; // what + using boost::spirit::arg_names::_3; // error pos + using boost::spirit::arg_names::_2; // end + using boost::spirit::qi::fail; + + rule r; + r = '(' > int_ > ',' > int_ > ')'; + + on_error + ( + r, std::cout + << val("Error! Expecting: ") + << _4 + << val(" Here: \"") + << construct(_3, _2) + << val("\"") + << std::endl + ); + + BOOST_SPIRIT_DEBUG_NODE(r); + BOOST_TEST(test("(123,456)", r)); + BOOST_TEST(!test("(abc,def)", r)); + BOOST_TEST(!test("(123,456]", r)); + BOOST_TEST(!test("(123;456)", r)); + BOOST_TEST(!test("[123,456]", r)); + } + + return boost::report_errors(); +} + diff --git a/test/qi/difference.cpp b/test/qi/difference.cpp new file mode 100644 index 000000000..f02b79b86 --- /dev/null +++ b/test/qi/difference.cpp @@ -0,0 +1,78 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("b", char_ - 'a')); + BOOST_TEST(!test("a", char_ - 'a')); + BOOST_TEST(test("/* abcdefghijk */", "/*" >> *(char_ - "*/") >> "*/")); + } + + { + BOOST_TEST(test("b", char_ - no_case['a'])); + BOOST_TEST(!test("a", char_ - no_case['a'])); + BOOST_TEST(!test("A", char_ - no_case['a'])); + + BOOST_TEST(test("b", no_case[lower - 'a'])); + BOOST_TEST(test("B", no_case[lower - 'a'])); + BOOST_TEST(!test("a", no_case[lower - 'a'])); + BOOST_TEST(!test("A", no_case[lower - 'a'])); + } + + { + // $$$ See difference.hpp why these tests are not done anymore. $$$ + + //~ BOOST_TEST(test("switcher", lit("switcher") - "switch")); + //~ BOOST_TEST(test(" switcher ", lit("switcher") - "switch", space)); + + BOOST_TEST(!test("switch", lit("switch") - "switch")); + } + + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + std::string s; + + BOOST_TEST(test( + "/*abcdefghijk*/" + , "/*" >> *(char_ - "*/")[ref(s) += _1] >> "*/" + )); + BOOST_TEST(s == "abcdefghijk"); + s.clear(); + + BOOST_TEST(test( + " /*abcdefghijk*/ " + , "/*" >> *(char_ - "*/")[ref(s) += _1] >> "*/" + , space + )); + BOOST_TEST(s == "abcdefghijk"); + } + + return boost::report_errors(); +} + diff --git a/test/qi/eps.cpp b/test/qi/eps.cpp new file mode 100644 index 000000000..f65477253 --- /dev/null +++ b/test/qi/eps.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((test("", eps))); + BOOST_TEST((test("xxx", eps, false))); + } + + { // test action + + using namespace boost::phoenix; + BOOST_TEST((test("", eps(val(true))))); + BOOST_TEST((!test("", eps(val(false))))); + } + + return boost::report_errors(); +} diff --git a/test/qi/expect.cpp b/test/qi/expect.cpp new file mode 100644 index 000000000..1b9da41c8 --- /dev/null +++ b/test/qi/expect.cpp @@ -0,0 +1,89 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using spirit_test::test; + using boost::spirit::qi::expectation_failure; + + { + try + { + BOOST_TEST((test("aa", char_ > char_))); + BOOST_TEST((test("aaa", char_ > char_ > char_('a')))); + BOOST_TEST((test("xi", char_('x') > char_('i')))); + BOOST_TEST((!test("xi", char_('y') > char_('o')))); // should not throw! + BOOST_TEST((test("xin", char_('x') > char_('i') > char_('n')))); + BOOST_TEST((!test("xi", char_('x') > char_('o')))); + } + catch (expectation_failure const& x) + { + std::cout << "expected: " << x.what << std::endl; + std::cout << "got: \"" << x.first << '"' << std::endl; + + BOOST_TEST(x.what == "'o'"); + BOOST_TEST(std::string(x.first, x.last) == "i"); + } + } + + { + try + { + BOOST_TEST((test(" a a ", char_ > char_, space))); + BOOST_TEST((test(" x i ", char_('x') > char_('i'), space))); + BOOST_TEST((!test(" x i ", char_('x') > char_('o'), space))); + } + catch (expectation_failure const& x) + { + std::cout << "expected: " << x.what << std::endl; + std::cout << "got: \"" << x.first << '"' << std::endl; + + BOOST_TEST(x.what == "'o'"); + BOOST_TEST(std::string(x.first, x.last) == "i "); + } + } + + { + try + { + BOOST_TEST((test("aA", no_case[char_('a') > 'a']))); + BOOST_TEST((test("BEGIN END", no_case[lit("begin") > "end"], space))); + BOOST_TEST((!test("BEGIN END", no_case[lit("begin") > "nend"], space))); + } + catch (expectation_failure const& x) + { + std::cout << "expected: " << x.what << std::endl; + std::cout << "got: \"" << x.first << '"' << std::endl; + + BOOST_TEST(x.what == "case-insensitive \"nend\""); + BOOST_TEST(std::string(x.first, x.last) == "END"); + } + } + + return boost::report_errors(); +} + diff --git a/test/qi/functor.cpp b/test/qi/functor.cpp new file mode 100644 index 000000000..ec7ee7a2e --- /dev/null +++ b/test/qi/functor.cpp @@ -0,0 +1,68 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +struct number_parser : public boost::spirit::qi::functor_base +{ + template + struct apply + { + typedef int type; + }; + + template + bool operator()(Attribute& attr, Context& ctx, + Iterator& first, Iterator const& last) const + { + if (first == last) + return false; + + char ch = *first; + if (ch < '0' || ch > '9') + return false; + + attr = 0; + do { + attr = attr * 10 + int(ch - '0'); + ++first; + } while (first != last && (ch = *first, ch >= '0' && ch <= '9')); + return true; + } +}; + +boost::spirit::qi::functor_parser number; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::qi; + + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + int n = 0; + BOOST_TEST(test("1234", number)); + BOOST_TEST(test("1234", number[ref(n) = _1])); + BOOST_TEST(n == 1234); + } + + return boost::report_errors(); +} diff --git a/test/qi/grammar.cpp b/test/qi/grammar.cpp new file mode 100644 index 000000000..01841c5fc --- /dev/null +++ b/test/qi/grammar.cpp @@ -0,0 +1,109 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +struct num_list : grammar_def +{ + num_list() + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule start, num; +}; + +struct inh_g : grammar_def +{ + inh_g() + { + start = lit("inherited")[_val = _r1]; + } + + rule start, num; +}; + +struct my_skipper : grammar_def +{ + my_skipper() + { + start = space; + } + + rule start, num; +}; + +struct num_list2 : grammar_def > +{ + num_list2() + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule > start, num; +}; + +int +main() +{ + { // simple grammar test + + num_list def; + grammar nlist(def); + BOOST_TEST(test("123, 456, 789", nlist, space)); + } + + { // simple grammar test with user-skipper + + num_list2 def; + grammar nlist(def); + my_skipper skipdef; + grammar skip(skipdef); + BOOST_TEST(test("123, 456, 789", nlist, skip)); + } + + { // direct access to the rules + + num_list def; + BOOST_TEST(test("123", def.num)); + BOOST_TEST(test("123, 456, 789", def.start, space)); + } + + { // grammar with inherited attributes + + inh_g def; + grammar g(def); + int n = -1; + BOOST_TEST(test_attr("inherited", def.start(123), n, space)); // direct to the rule + BOOST_TEST(n == 123); + BOOST_TEST(test_attr("inherited", g(123), n, space)); // using the grammar + BOOST_TEST(n == 123); + } + return boost::report_errors(); +} + diff --git a/test/qi/grammar_fail.cpp b/test/qi/grammar_fail.cpp new file mode 100644 index 000000000..7696c66dd --- /dev/null +++ b/test/qi/grammar_fail.cpp @@ -0,0 +1,42 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; + +struct num_list : grammar_def > +{ + num_list() + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule > start, num; +}; + +// this test must fail compiling +int main() +{ + char const* input = "some input, it doesn't matter"; + char const* end = &input[strlen(input)+1]; + + num_list def; + bool r = phrase_parse(input, end, make_parser(def), + space | ('%' >> *~char_('\n') >> '\n')); + + return 0; +} diff --git a/test/qi/int.cpp b/test/qi/int.cpp new file mode 100644 index 000000000..ad2705d2a --- /dev/null +++ b/test/qi/int.cpp @@ -0,0 +1,164 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// +// *** BEWARE PLATFORM DEPENDENT!!! *** +// *** The following assumes 32 bit integers and 64 bit long longs. +// *** Modify these constant strings when appropriate. +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef BOOST_HAS_LONG_LONG +// Some compilers have long long, but don't define the +// LONG_LONG_MIN and LONG_LONG_MAX macros in limits.h. This +// assumes that long long is 64 bits. +#if !defined(LONG_LONG_MIN) && !defined(LONG_LONG_MAX) +# define LONG_LONG_MAX 0x7fffffffffffffffLL +# define LONG_LONG_MIN (-LONG_LONG_MAX - 1) +#endif +#endif // BOOST_HAS_LONG_LONG + + char const* max_int = "2147483647"; + char const* int_overflow = "2147483648"; + char const* min_int = "-2147483648"; + char const* int_underflow = "-2147483649"; + +#ifdef BOOST_HAS_LONG_LONG + char const* max_long_long = "9223372036854775807"; + char const* long_long_overflow = "9223372036854775808"; + char const* min_long_long = "-9223372036854775808"; + char const* long_long_underflow = "-9223372036854775809"; +#endif + +int +main() +{ + using namespace spirit_test; + + /////////////////////////////////////////////////////////////////////////// + // signed integer tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::int_; + int i; + + BOOST_TEST(test("123456", int_)); + BOOST_TEST(test_attr("123456", int_, i)); + BOOST_TEST(i == 123456); + + BOOST_TEST(test("+123456", int_)); + BOOST_TEST(test_attr("+123456", int_, i)); + BOOST_TEST(i == 123456); + + BOOST_TEST(test("-123456", int_)); + BOOST_TEST(test_attr("-123456", int_, i)); + BOOST_TEST(i == -123456); + + BOOST_TEST(test(max_int, int_)); + BOOST_TEST(test_attr(max_int, int_, i)); + BOOST_TEST(i == INT_MAX); + + BOOST_TEST(test(min_int, int_)); + BOOST_TEST(test_attr(min_int, int_, i)); + BOOST_TEST(i == INT_MIN); + + BOOST_TEST(!test(int_overflow, int_)); + BOOST_TEST(!test_attr(int_overflow, int_, i)); + BOOST_TEST(!test(int_underflow, int_)); + BOOST_TEST(!test_attr(int_underflow, int_, i)); + + BOOST_TEST(!test("-", int_)); + BOOST_TEST(!test_attr("-", int_, i)); + + BOOST_TEST(!test("+", int_)); + BOOST_TEST(!test_attr("+", int_, i)); + + // Bug report from Steve Nutt + BOOST_TEST(test_attr("5368709120", int_, i, false)); + BOOST_TEST(i == 536870912); + + // with leading zeros + BOOST_TEST(test("0000000000123456", int_)); + BOOST_TEST(test_attr("0000000000123456", int_, i)); + BOOST_TEST(i == 123456); + } + + /////////////////////////////////////////////////////////////////////////// + // long long tests + /////////////////////////////////////////////////////////////////////////// +#ifdef BOOST_HAS_LONG_LONG + { + using boost::spirit::long_long; + boost::long_long_type ll; + + BOOST_TEST(test("1234567890123456789", long_long)); + BOOST_TEST(test_attr("1234567890123456789", long_long, ll)); + BOOST_TEST(ll == 1234567890123456789LL); + + BOOST_TEST(test("-1234567890123456789", long_long)); + BOOST_TEST(test_attr("-1234567890123456789", long_long, ll)); + BOOST_TEST(ll == -1234567890123456789LL); + + BOOST_TEST(test(max_long_long, long_long)); + BOOST_TEST(test_attr(max_long_long, long_long, ll)); + BOOST_TEST(ll == LONG_LONG_MAX); + + BOOST_TEST(test(min_long_long, long_long)); + BOOST_TEST(test_attr(min_long_long, long_long, ll)); + BOOST_TEST(ll == LONG_LONG_MIN); + + BOOST_TEST(!test(long_long_overflow, long_long)); + BOOST_TEST(!test_attr(long_long_overflow, long_long, ll)); + BOOST_TEST(!test(long_long_underflow, long_long)); + BOOST_TEST(!test_attr(long_long_underflow, long_long, ll)); + } +#endif + + /////////////////////////////////////////////////////////////////////////// + // int_spec tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::int_spec; + using boost::spirit::unused_type; + int_spec any_int; + + BOOST_TEST(test("123456", any_int)); + BOOST_TEST(test("-123456", any_int)); + BOOST_TEST(test("-1234567890123456789", any_int)); + } + + /////////////////////////////////////////////////////////////////////////// + // action tests + /////////////////////////////////////////////////////////////////////////// + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + using boost::spirit::ascii::space; + using boost::spirit::int_; + int n, m; + + BOOST_TEST(test("123", int_[ref(n) = _1])); + BOOST_TEST(n == 123); + BOOST_TEST(test_attr("789", int_[ref(n) = _1], m)); + BOOST_TEST(n == 789 && m == 789); + BOOST_TEST(test(" 456", int_[ref(n) = _1], space)); + BOOST_TEST(n == 456); + } + + return boost::report_errors(); +} diff --git a/test/qi/kleene.cpp b/test/qi/kleene.cpp new file mode 100644 index 000000000..9f74b97e2 --- /dev/null +++ b/test/qi/kleene.cpp @@ -0,0 +1,113 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("aaaaaaaa", *char_)); + BOOST_TEST(test("a", *char_)); + BOOST_TEST(test("", *char_)); + BOOST_TEST(test("aaaaaaaa", *alpha)); + BOOST_TEST(!test("aaaaaaaa", *upper)); + } + + { + BOOST_TEST(test(" a a aaa aa", *char_, space)); + BOOST_TEST(test("12345 678 9 ", *digit, space)); + } + + { + BOOST_TEST(test("aBcdeFGH", no_case[*char_])); + BOOST_TEST(test("a B cde FGH ", no_case[*char_], space)); + } + + { + using boost::spirit::uint; + BOOST_TEST(test("12345 678 955 987", *uint, space)); + BOOST_TEST(test("12345, 678, 955, 987", uint >> *(',' >> uint), space)); + } + + { + std::vector v; + BOOST_TEST(test_attr("bbbb", *char_, v) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", *char_, v, space) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + + v.clear(); + BOOST_TEST(test_attr("bbbb", *omit[char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("bbbb", omit[*char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", *omit[char_('b')], v, space) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", omit[*char_('b')], v, space) && 0 == v.size()); + } + + { + std::vector v; + BOOST_TEST(test_attr("123 456 789 10", *int_, v, space) && 4 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789 && v[3] == 10); + } + + { + std::vector v; + BOOST_TEST(test_attr("123 456 789", *int_, v, space) && 3 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("bbbb", (*char_)[ref(v) = _1]) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + } + + { // more actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("123 456 789", (*int_)[ref(v) = _1], space) && 3 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789); + } + + return boost::report_errors(); +} + diff --git a/test/qi/lazy.cpp b/test/qi/lazy.cpp new file mode 100644 index 000000000..281b74736 --- /dev/null +++ b/test/qi/lazy.cpp @@ -0,0 +1,54 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::qi; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { + BOOST_TEST(test("123", lazy(val(int_)))); + } + + { + int result; + BOOST_TEST(test("123", lazy(val(int_))[ref(result) = _1])); + BOOST_TEST((result == 123)); + } + + { + rule r; + + r = + '<' >> *(char_ - '>')[_val += _1] >> '>' + >> "> lazy(_val) >> '>' + ; + + BOOST_TEST(test("", r)); + BOOST_TEST(!test("", r)); + } + + return boost::report_errors(); +} diff --git a/test/qi/lexeme.cpp b/test/qi/lexeme.cpp new file mode 100644 index 000000000..0b1884232 --- /dev/null +++ b/test/qi/lexeme.cpp @@ -0,0 +1,38 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using boost::spirit::qi::rule; + + { + BOOST_TEST((test(" 1 2 3 4 5", +digit, space))); + BOOST_TEST((!test(" 1 2 3 4 5", lexeme[+digit], space))); + BOOST_TEST((test(" 12345", lexeme[+digit], space))); + BOOST_TEST((test(" 12345 ", lexeme[+digit], space, false))); + + rule r, rr; + r = +digit; + rr = lexeme[r]; + BOOST_TEST((!test(" 1 2 3 4 5", rr, space))); + BOOST_TEST((test(" 12345", rr, space))); + } + + return boost::report_errors(); +} diff --git a/test/qi/list.cpp b/test/qi/list.cpp new file mode 100644 index 000000000..0524d1e2c --- /dev/null +++ b/test/qi/list.cpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("a,b,c,d,e,f,g,h", char_ % ',')); + BOOST_TEST(test("a,b,c,d,e,f,g,h,", char_ % ',', false)); + } + + { + BOOST_TEST(test("a, b, c, d, e, f, g, h", char_ % ',', space)); + BOOST_TEST(test("a, b, c, d, e, f, g, h,", char_ % ',', space, false)); + } + + { + std::string s; + BOOST_TEST(test_attr("a,b,c,d,e,f,g,h", char_ % ',', s)); + BOOST_TEST(s == "abcdefgh"); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::string s; + BOOST_TEST(test("a,b,c,d,e,f,g,h", (char_ % ',') + [ref(s) = construct(begin(_1), end(_1))])); + } + + return boost::report_errors(); +} + diff --git a/test/qi/lit.cpp b/test/qi/lit.cpp new file mode 100644 index 000000000..5d9a65087 --- /dev/null +++ b/test/qi/lit.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + http://spirit.sourceforge.net/ + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + using namespace boost::spirit; + + { + BOOST_TEST((test("kimpo", "kimpo"))); + BOOST_TEST((test("kimpo", lit("kimpo")))); + BOOST_TEST((test("x", lit("x")))); + BOOST_TEST((test("x", lit('x')))); + BOOST_TEST((test(L"x", lit(L'x')))); + } + + { + BOOST_TEST((test(L"kimpo", L"kimpo"))); + BOOST_TEST((test(L"kimpo", wlit(L"kimpo")))); + BOOST_TEST((test(L"x", wlit(L"x")))); + BOOST_TEST((test(L"x", wlit(L'x')))); + BOOST_TEST((test(L"x", wlit(L'x')))); + } + + { + std::basic_string s("kimpo"); + BOOST_TEST((test("kimpo", lit(s)))); + + std::basic_string ws(L"kimpo"); + BOOST_TEST((test(L"kimpo", lit(ws)))); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST((test(" kimpo", lit("kimpo"), space))); + BOOST_TEST((test(L" kimpo", lit(L"kimpo"), space))); + BOOST_TEST((test(" x", lit("x"), space))); + BOOST_TEST((test(" x", lit('x'), space))); + BOOST_TEST((test(L" x", lit(L'x'), space))); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST((test(" kimpo", wlit("kimpo"), space))); + BOOST_TEST((test(L" kimpo", wlit(L"kimpo"), space))); + BOOST_TEST((test(" x", wlit("x"), space))); + BOOST_TEST((test(" x", wlit('x'), space))); + BOOST_TEST((test(L" x", wlit(L'x'), space))); + } + + { // lazy strings + + using namespace boost::phoenix; + std::basic_string s("kimpo"); + BOOST_TEST((test("kimpo", lit(val(s))))); + + std::basic_string ws(L"kimpo"); + BOOST_TEST((test(L"kimpo", lit(ref(ws))))); + } + + return boost::report_errors(); +} diff --git a/test/qi/match_manip.cpp b/test/qi/match_manip.cpp new file mode 100644 index 000000000..8b64f85b1 --- /dev/null +++ b/test/qi/match_manip.cpp @@ -0,0 +1,293 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +template +bool test(Char const *toparse, Expr const& xpr) +{ + namespace spirit = boost::spirit; + typedef + spirit::traits::is_component + is_component; + + // report invalid expression error as early as possible + BOOST_MPL_ASSERT_MSG(is_component::value, + xpr_is_not_convertible_to_a_parser, ()); + + typedef + typename spirit::result_of::as_component::type + component; + typedef typename component::director director; + + component c = spirit::as_component(spirit::qi::domain(), xpr); + + std::istringstream istrm(toparse); + istrm >> c; + return istrm.good() || istrm.eof(); +} + +template +bool test(Char const *toparse, + boost::spirit::qi::detail::match_manip const& mm) +{ + std::istringstream istrm(toparse); + istrm >> mm; + return istrm.good() || istrm.eof(); +} + +/////////////////////////////////////////////////////////////////////////////// +bool is_list_ok(std::list const& l) +{ + std::list::const_iterator cit = l.begin(); + if (cit == l.end() || *cit != 'a') + return false; + if (++cit == l.end() || *cit != 'b') + return false; + + return ++cit != l.end() && *cit == 'c'; +} + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::qi; + + namespace fusion = boost::fusion; + using namespace boost::phoenix; + + { + char c = '\0'; + BOOST_TEST(test( "a", + char_[ref(c) = _1] + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( "a", + match(char_[ref(c) = _1]) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(char_[ref(c) = _1], space) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( "a", + match(char_, c) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(char_, c, space) + ) && c == 'a'); + } + + { + /////////////////////////////////////////////////////////////////////// + typedef typed_stream char_stream_type; + char_stream_type const char_stream = char_stream_type(); + + typedef typed_stream int_stream_type; + int_stream_type const int_stream = int_stream_type(); + + /////////////////////////////////////////////////////////////////////// + char c = '\0'; + BOOST_TEST(test( "a", + char_stream[ref(c) = _1] + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( "a", + match(char_stream[ref(c) = _1]) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(char_stream[ref(c) = _1], space) + ) && c == 'a'); + + int i = 0; + BOOST_TEST(test( "42", + int_stream[ref(i) = _1] + ) && i == 42); + + i = 0; + BOOST_TEST(test( "42", + match(int_stream[ref(i) = _1]) + ) && i == 42); + + i = 0; + BOOST_TEST(test( " 42", + phrase_match(int_stream[ref(i) = _1], space) + ) && i == 42); + + /////////////////////////////////////////////////////////////////////// + c = '\0'; + BOOST_TEST(test( "a", + match(stream, c) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(stream, c, space) + ) && c == 'a'); + + i = 0; + BOOST_TEST(test( "42", + match(stream, i) + ) && i == 42); + + i = 0; + BOOST_TEST(test( " 42", + phrase_match(stream, i, space) + ) && i == 42); + } + + { + char a = '\0', b = '\0'; + BOOST_TEST(test( "ab", + char_[ref(a) = _1] >> char_[ref(b) = _1] + ) && a == 'a' && b == 'b'); + + a = '\0', b = '\0'; + BOOST_TEST(test( "ab", + match(char_[ref(a) = _1] >> char_[ref(b) = _1]) + ) && a == 'a' && b == 'b'); + + a = '\0', b = '\0'; + BOOST_TEST(test( " a b", + phrase_match(char_[ref(a) = _1] >> char_[ref(b) = _1], space) + ) && a == 'a' && b == 'b'); + + fusion::vector t; + BOOST_TEST(test( "ab", + match(char_ >> char_, t) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b'); + + t = fusion::vector(); + BOOST_TEST(test( " a b", + phrase_match(char_ >> char_, t, space) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b'); + } + + { + char a = '\0', b = '\0', c = '\0'; + BOOST_TEST(test( "abc", + char_[ref(a) = _1] >> char_[ref(b) = _1] >> char_[ref(c) = _1] + ) && a == 'a' && b == 'b' && c == 'c'); + + BOOST_TEST(test( "abc", + match(char_('a') >> char_('b') >> char_('c')) + )); + + BOOST_TEST(test( " a b c", + phrase_match(char_('a') >> char_('b') >> char_('c'), space) + )); + + BOOST_TEST(!test( "abc", + match(char_('a') >> char_('b') >> char_('d')) + )); + + BOOST_TEST(!test( " a b c", + phrase_match(char_('a') >> char_('b') >> char_('d'), space) + )); + + fusion::vector t; + BOOST_TEST(test( "abc", + match(char_ >> char_ >> char_, t) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b' && fusion::at_c<2>(t) == 'c'); + + t = fusion::vector(); + BOOST_TEST(test( " a b c", + phrase_match(char_ >> char_ >> char_, t, space) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b' && fusion::at_c<2>(t) == 'c'); + } + + { + char a = '\0'; + int i = 0; + BOOST_TEST(test( "a2", + (char_ >> int_)[ref(a) = _1, ref(i) = _2] + ) && a == 'a' && i == 2); + + fusion::vector t; + BOOST_TEST(test( "a2", + match(char_ >> int_, t) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 2); + + t = fusion::vector(); + BOOST_TEST(test( " a 2", + phrase_match(char_ >> int_, t, space) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 2); + + BOOST_TEST(!test( "a2", + match(char_ >> alpha, t) + )); + BOOST_TEST(!test( " a 2", + phrase_match(char_ >> alpha, t, space) + )); + } + + { + // output all elements of a vector + std::vector v; + BOOST_TEST(test( "abc", + (*char_)[ref(v) = _1] + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + v.clear(); + BOOST_TEST(test( "abc", + match(*char_, v) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + v.clear(); + BOOST_TEST(test( " a b c", + phrase_match(*char_, v, space) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + // parse a comma separated list of vector elements + v.clear(); + BOOST_TEST(test( "a,b,c", + match(char_ % ',', v) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + v.clear(); + BOOST_TEST(test( " a , b , c", + phrase_match(char_ % ',', v, space) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + // output all elements of a list + std::list l; + BOOST_TEST(test( "abc", + match(*char_, l) + ) && 3 == l.size() && is_list_ok(l)); + + l.clear(); + BOOST_TEST(test( " a b c", + phrase_match(*char_, l, space) + ) && 3 == l.size() && is_list_ok(l)); + } + + return boost::report_errors(); +} + diff --git a/test/qi/no_case.cpp b/test/qi/no_case.cpp new file mode 100644 index 000000000..afd171e5a --- /dev/null +++ b/test/qi/no_case.cpp @@ -0,0 +1,95 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + http://spirit.sourceforge.net/ + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("x", no_case[char_])); + BOOST_TEST(test("X", no_case[char_('x')])); + BOOST_TEST(test("X", no_case[char_('X')])); + BOOST_TEST(test("x", no_case[char_('X')])); + BOOST_TEST(test("x", no_case[char_('x')])); + BOOST_TEST(!test("z", no_case[char_('X')])); + BOOST_TEST(!test("z", no_case[char_('x')])); + BOOST_TEST(test("x", no_case[char_('a', 'z')])); + BOOST_TEST(test("X", no_case[char_('a', 'z')])); + BOOST_TEST(!test("a", no_case[char_('b', 'z')])); + BOOST_TEST(!test("z", no_case[char_('a', 'y')])); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("Bochi Bochi", no_case[lit("bochi bochi")])); + BOOST_TEST(test("BOCHI BOCHI", no_case[lit("bochi bochi")])); + BOOST_TEST(!test("Vavoo", no_case[lit("bochi bochi")])); + } + + { + // should work! + using namespace boost::spirit::ascii; + BOOST_TEST(test("x", no_case[no_case[char_]])); + BOOST_TEST(test("x", no_case[no_case[char_('x')]])); + BOOST_TEST(test("yabadabadoo", no_case[no_case[lit("Yabadabadoo")]])); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("X", no_case[alnum])); + BOOST_TEST(test("6", no_case[alnum])); + BOOST_TEST(!test(":", no_case[alnum])); + + BOOST_TEST(test("X", no_case[lower])); + BOOST_TEST(test("x", no_case[lower])); + BOOST_TEST(test("X", no_case[upper])); + BOOST_TEST(test("x", no_case[upper])); + BOOST_TEST(!test(":", no_case[lower])); + BOOST_TEST(!test(":", no_case[upper])); + } + + { + using namespace boost::spirit::iso8859_1; + BOOST_TEST(test("X", no_case[alnum])); + BOOST_TEST(test("6", no_case[alnum])); + BOOST_TEST(!test(":", no_case[alnum])); + + BOOST_TEST(test("X", no_case[lower])); + BOOST_TEST(test("x", no_case[lower])); + BOOST_TEST(test("X", no_case[upper])); + BOOST_TEST(test("x", no_case[upper])); + BOOST_TEST(!test(":", no_case[lower])); + BOOST_TEST(!test(":", no_case[upper])); + } + + { + using namespace boost::spirit::standard; + BOOST_TEST(test("X", no_case[alnum])); + BOOST_TEST(test("6", no_case[alnum])); + BOOST_TEST(!test(":", no_case[alnum])); + + BOOST_TEST(test("X", no_case[lower])); + BOOST_TEST(test("x", no_case[lower])); + BOOST_TEST(test("X", no_case[upper])); + BOOST_TEST(test("x", no_case[upper])); + BOOST_TEST(!test(":", no_case[lower])); + BOOST_TEST(!test(":", no_case[upper])); + } + + return boost::report_errors(); +} diff --git a/test/qi/none.cpp b/test/qi/none.cpp new file mode 100644 index 000000000..d083fbe4f --- /dev/null +++ b/test/qi/none.cpp @@ -0,0 +1,25 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((!test("", none))); + BOOST_TEST((!test("xxx", none))); + } + + return boost::report_errors(); +} diff --git a/test/qi/not_predicate.cpp b/test/qi/not_predicate.cpp new file mode 100644 index 000000000..72a2288e8 --- /dev/null +++ b/test/qi/not_predicate.cpp @@ -0,0 +1,27 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((!test("1234", !int_))); + BOOST_TEST((test("abcd", !int_, false))); + } + + return boost::report_errors(); +} diff --git a/test/qi/optional.cpp b/test/qi/optional.cpp new file mode 100644 index 000000000..a60ad3abb --- /dev/null +++ b/test/qi/optional.cpp @@ -0,0 +1,66 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + using namespace boost::spirit; + + { + BOOST_TEST((test("1234", -int_))); + BOOST_TEST((test("abcd", -int_, false))); + } + + { // test propagation of unused + using namespace boost::fusion; + + vector v; + BOOST_TEST((test_attr("a1234c", char_ >> -omit[int_] >> char_, v))); + BOOST_TEST((at_c<0>(v) == 'a')); + BOOST_TEST((at_c<1>(v) == 'c')); + + v = boost::fusion::vector(); + BOOST_TEST((test_attr("a1234c", char_ >> omit[-int_] >> char_, v))); + BOOST_TEST((at_c<0>(v) == 'a')); + BOOST_TEST((at_c<1>(v) == 'c')); + + //~ char ch; + //~ BOOST_TEST((test_attr(",c", -(',' >> char_), ch))); + //~ BOOST_TEST((ch == 'c')); + } + + { // test action + + using namespace boost::phoenix; + namespace phx = boost::phoenix; + using namespace boost::spirit::arg_names; + + boost::optional n = 0; + BOOST_TEST((test("1234", (-int_)[phx::ref(n) = _1]))); + BOOST_TEST(n.get() == 1234); + + n = boost::optional(); + BOOST_TEST((test("abcd", (-int_)[phx::ref(n) = _1], false))); + BOOST_TEST(!n); + } + + return boost::report_errors(); +} diff --git a/test/qi/permutation.cpp b/test/qi/permutation.cpp new file mode 100644 index 000000000..c38d4bea7 --- /dev/null +++ b/test/qi/permutation.cpp @@ -0,0 +1,121 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using boost::spirit::ascii::alpha; + using boost::spirit::qi::rule; + using boost::fusion::vector; + using boost::fusion::at_c; + using boost::optional; + + { + BOOST_TEST((test("a", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("b", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("ab", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("ba", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("abc", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("acb", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("bca", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("bac", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("cab", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("cba", char_('a') ^ char_('b') ^ char_('c')))); + + BOOST_TEST((!test("cca", char_('a') ^ char_('b') ^ char_('c')))); + } + + { + vector, optional > attr; + + BOOST_TEST((test_attr("a", int_ ^ alpha, attr))); + BOOST_TEST((!at_c<0>(attr))); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123", int_ ^ alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((!at_c<1>(attr))); + + at_c<0>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123a", int_ ^ alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<0>(attr) = optional(); // clear the optional + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("a123", int_ ^ alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + namespace phx = boost::phoenix; + + optional i; + optional c; + + BOOST_TEST((test("123a", (int_ ^ alpha)[phx::ref(i) = _1, phx::ref(c) = _2]))); + BOOST_TEST((i.get() == 123)); + BOOST_TEST((c.get() == 'a')); + } + + { // test rule %= + + typedef vector, optional > attr_type; + attr_type attr; + + rule r; + r %= int_ ^ alpha; + + BOOST_TEST((test_attr("a", r, attr))); + BOOST_TEST((!at_c<0>(attr))); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123", r, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((!at_c<1>(attr))); + + at_c<0>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123a", r, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<0>(attr) = optional(); // clear the optional + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("a123", r, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + } + + return boost::report_errors(); +} + diff --git a/test/qi/plus.cpp b/test/qi/plus.cpp new file mode 100644 index 000000000..72d425f8b --- /dev/null +++ b/test/qi/plus.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("aaaaaaaa", +char_)); + BOOST_TEST(test("a", +char_)); + BOOST_TEST(!test("", +char_)); + BOOST_TEST(test("aaaaaaaa", +alpha)); + BOOST_TEST(!test("aaaaaaaa", +upper)); + } + + { + BOOST_TEST(test(" a a aaa aa", +char_, space)); + BOOST_TEST(test("12345 678 9 ", +digit, space)); + } + + { + BOOST_TEST(test("aBcdeFGH", no_case[+char_])); + BOOST_TEST(test("a B cde FGH ", no_case[+char_], space)); + } + + { + std::vector v; + BOOST_TEST(test_attr("123 456 789 10", +int_, v, space) && 4 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789 && v[3] == 10); + } + + { + BOOST_TEST(test("Kim Kim Kim", +lit("Kim"), space)); + } + + { + std::vector v; + v.clear(); + BOOST_TEST(test_attr("bbbb", +omit[char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("bbbb", omit[+char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", +omit[char_('b')], v, space) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", omit[+char_('b')], v, space) && 0 == v.size()); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("bbbb", (+char_)[ref(v) = _1]) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + } + + { // more actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("1 2 3", (+int_)[ref(v) = _1], space) && 3 == v.size() && + v[0] == 1 && v[1] == 2 && v[2] == 3); + } + + return boost::report_errors(); +} + diff --git a/test/qi/range_run.cpp b/test/qi/range_run.cpp new file mode 100644 index 000000000..d9d2d4169 --- /dev/null +++ b/test/qi/range_run.cpp @@ -0,0 +1,167 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_MSVC) +# pragma warning(disable: 4800) // 'int' : forcing value to bool 'true' or 'false' warning +#endif + +template +void acid_test() +{ + using boost::spirit::qi::detail::range_run; + using boost::spirit::qi::detail::range; + + typedef boost::integer_traits integer_traits; + Char const const_min = integer_traits::const_min; + Char const const_max = integer_traits::const_max; + int const test_size = 1000; + + boost::mt19937 rng; + boost::uniform_int<> char_(const_min, const_max); + boost::variate_generator > + gen(rng, char_); + boost::uniform_int<> _1of10(1, 10); + boost::variate_generator > + on_or_off(rng, _1of10); + + range_run rr; + boost::dynamic_bitset<> bset(const_max-const_min+1); + + for (int i = 0; i < test_size; ++i) + { + range r = range(gen(), gen()); + if (r.first > r.last) + std::swap(r.first, r.last); + + bool set = on_or_off() != 1; + if (set) + rr.set(r); + else + rr.clear(r); + for (int j = r.first; j <= int(r.last); ++j) + bset[j-const_min] = set; + } + + for (int i = const_min; i <= int(const_max); ++i) + { + BOOST_TEST(rr.test(i) == bset[i-const_min]); + } +} + +int +main() +{ + using boost::spirit::qi::detail::range_run; + using boost::spirit::qi::detail::range; + + { + range_run rr; + rr.set(range('a', 'a')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(c == 'a' == rr.test(c)); + } + } + { + range_run rr; + rr.set(range('a', 'z')); + rr.set(range('A', 'Z')); + rr.clear(range('A', 'Z')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(bool(std::islower(c)) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range(0, 0)); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(c == 0 == rr.test(c)); + } + rr.set(range(0, 50)); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(((c >= 0) && (c <= 50)) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range(255, 255)); + for (unsigned char c = 0; c < 255; ++c) + { + BOOST_TEST(c == 255 == rr.test(c)); + } + rr.set(range(250, 255)); + for (unsigned char c = 0; c < 255; ++c) + { + BOOST_TEST((c >= 250) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range('a', 'z')); + rr.set(range('A', 'Z')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(bool(std::isalpha(c)) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range('a', 'z')); + rr.set(range('A', 'Z')); + rr.clear(range('J', 'j')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST((bool(std::isalpha(c)) && (c < 'J' || c > 'j')) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range(3, 3)); + rr.set(range(1, 5)); + BOOST_TEST(rr.test(5)); + } + { + range_run rr; + for (char c = 0; c < 127; ++c) + { + if (c & 1) + { + rr.set(range(c, c)); + } + } + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(bool((c & 1)) == rr.test(c)); + } + rr.clear(range(90, 105)); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST((bool((c & 1)) && (c < 90 || c > 105)) == rr.test(c)); + } + } + { + acid_test(); + acid_test(); + acid_test(); + acid_test(); + acid_test(); + acid_test(); + acid_test(); + } + + return boost::report_errors(); +} diff --git a/test/qi/raw.cpp b/test/qi/raw.cpp new file mode 100644 index 000000000..98c907b44 --- /dev/null +++ b/test/qi/raw.cpp @@ -0,0 +1,39 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test_attr; + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using boost::spirit::qi::rule; + + { + boost::iterator_range range; + std::string str; + BOOST_TEST((test_attr("spirit_test_123", raw[alpha >> *(alnum | '_')], range))); + BOOST_TEST((std::string(range.begin(), range.end()) == "spirit_test_123")); + } + + { + std::string str; + BOOST_TEST((test_attr("spirit_test_123", raw[alpha >> *(alnum | '_')], str))); + BOOST_TEST((str == "spirit_test_123")); + } + + return boost::report_errors(); +} diff --git a/test/qi/real.cpp b/test/qi/real.cpp new file mode 100644 index 000000000..5ec2c7c7f --- /dev/null +++ b/test/qi/real.cpp @@ -0,0 +1,374 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +// These policies can be used to parse thousand separated +// numbers with at most 2 decimal digits after the decimal +// point. e.g. 123,456,789.01 +/////////////////////////////////////////////////////////////////////////////// +template +struct ts_real_policies : boost::spirit::qi::ureal_policies +{ + // 2 decimal places Max + template + static bool + parse_frac_n(Iterator& first, Iterator const& last, Attribute& attr) + { + return boost::spirit::qi:: + extract_uint::call(first, last, attr); + } + + // No exponent + template + static bool + parse_exp(Iterator& first, Iterator const& last) + { + return false; + } + + // No exponent + template + static bool + parse_exp_n(Iterator& first, Iterator const& last, Attribute& attr) + { + return false; + } + + // Thousands separated numbers + template + static bool + parse_n(Iterator& first, Iterator const& last, Attribute& attr) + { + using namespace boost::spirit::qi; + using namespace boost::spirit; + + uint_spec uint3; + uint_spec uint3_3; + + T result = 0; + if (parse(first, last, uint3, result)) + { + bool hit = false; + T n; + Iterator save = first; + + while (parse(first, last, ',') && parse(first, last, uint3_3, n)) + { + result = result * 1000 + n; + save = first; + hit = true; + } + + first = save; + if (hit) + attr = result; + return hit; + } + return false; + } +}; + +template +struct no_trailing_dot_policy : boost::spirit::qi::real_policies +{ + static bool const allow_trailing_dot = false; +}; + +template +struct no_leading_dot_policy : boost::spirit::qi::real_policies +{ + static bool const allow_leading_dot = false; +}; + +template +bool +compare(T n, double expected) +{ + double const eps = 0.00001; + T delta = n - expected; + return (delta >= -eps) && (delta <= eps); +} + +int +main() +{ + /////////////////////////////////////////////////////////////////////////////// + // thousand separated numbers + /////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::uint_spec; + using boost::spirit::qi::parse; + + uint_spec uint3; + uint_spec uint3_3; + + #define r (uint3 >> *(',' >> uint3_3)) + + BOOST_TEST(test("1,234,567,890", r)); + BOOST_TEST(test("12,345,678,900", r)); + BOOST_TEST(test("123,456,789,000", r)); + BOOST_TEST(!test("1000,234,567,890", r)); + BOOST_TEST(!test("1,234,56,890", r)); + BOOST_TEST(!test("1,66", r)); + } + + /////////////////////////////////////////////////////////////////////////////// + // unsigned real number tests + /////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::real_spec; + using boost::spirit::qi::parse; + using boost::spirit::qi::ureal_policies; + + real_spec > udouble; + double d; + + BOOST_TEST(test("1234", udouble)); + BOOST_TEST(test_attr("1234", udouble, d) && compare(d, 1234)); + + BOOST_TEST(test("1.2e3", udouble)); + BOOST_TEST(test_attr("1.2e3", udouble, d) && compare(d, 1.2e3)); + + BOOST_TEST(test("1.2e-3", udouble)); + BOOST_TEST(test_attr("1.2e-3", udouble, d) && compare(d, 1.2e-3)); + + BOOST_TEST(test("1.e2", udouble)); + BOOST_TEST(test_attr("1.e2", udouble, d) && compare(d, 1.e2)); + + BOOST_TEST(test("1.", udouble)); + BOOST_TEST(test_attr("1.", udouble, d) && compare(d, 1.)); + + BOOST_TEST(test(".2e3", udouble)); + BOOST_TEST(test_attr(".2e3", udouble, d) && compare(d, .2e3)); + + BOOST_TEST(test("2e3", udouble)); + BOOST_TEST(test_attr("2e3", udouble, d) && compare(d, 2e3)); + + BOOST_TEST(test("2", udouble)); + BOOST_TEST(test_attr("2", udouble, d) && compare(d, 2)); + + using boost::math::fpclassify; + BOOST_TEST(test("inf", udouble)); + BOOST_TEST(test("infinity", udouble)); + BOOST_TEST(test("INF", udouble)); + BOOST_TEST(test("INFINITY", udouble)); + BOOST_TEST(test_attr("inf", udouble, d) && FP_INFINITE == fpclassify(d)); + BOOST_TEST(test_attr("INF", udouble, d) && FP_INFINITE == fpclassify(d)); + BOOST_TEST(test_attr("infinity", udouble, d) && FP_INFINITE == fpclassify(d)); + BOOST_TEST(test_attr("INFINITY", udouble, d) && FP_INFINITE == fpclassify(d)); + + BOOST_TEST(test("nan", udouble)); + BOOST_TEST(test_attr("nan", udouble, d) && FP_NAN == fpclassify(d)); + BOOST_TEST(test("NAN", udouble)); + BOOST_TEST(test_attr("NAN", udouble, d) && FP_NAN == fpclassify(d)); + + BOOST_TEST(test("nan(...)", udouble)); + BOOST_TEST(test_attr("nan(...)", udouble, d) && FP_NAN == fpclassify(d)); + BOOST_TEST(test("NAN(...)", udouble)); + BOOST_TEST(test_attr("NAN(...)", udouble, d) && FP_NAN == fpclassify(d)); + + BOOST_TEST(!test("e3", udouble)); + BOOST_TEST(!test_attr("e3", udouble, d)); + + BOOST_TEST(!test("-1.2e3", udouble)); + BOOST_TEST(!test_attr("-1.2e3", udouble, d)); + + BOOST_TEST(!test("+1.2e3", udouble)); + BOOST_TEST(!test_attr("+1.2e3", udouble, d)); + + BOOST_TEST(!test("1.2e", udouble)); + BOOST_TEST(!test_attr("1.2e", udouble, d)); + + BOOST_TEST(!test("-.3", udouble)); + BOOST_TEST(!test_attr("-.3", udouble, d)); + } + +/////////////////////////////////////////////////////////////////////////////// +// signed real number tests +/////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::double_; + using boost::spirit::qi::parse; + double d; + + BOOST_TEST(test("-1234", double_)); + BOOST_TEST(test_attr("-1234", double_, d) && compare(d, -1234)); + + BOOST_TEST(test("-1.2e3", double_)); + BOOST_TEST(test_attr("-1.2e3", double_, d) && compare(d, -1.2e3)); + + BOOST_TEST(test("+1.2e3", double_)); + BOOST_TEST(test_attr("+1.2e3", double_, d) && compare(d, 1.2e3)); + + BOOST_TEST(test("-0.1", double_)); + BOOST_TEST(test_attr("-0.1", double_, d) && compare(d, -0.1)); + + BOOST_TEST(test("-1.2e-3", double_)); + BOOST_TEST(test_attr("-1.2e-3", double_, d) && compare(d, -1.2e-3)); + + BOOST_TEST(test("-1.e2", double_)); + BOOST_TEST(test_attr("-1.e2", double_, d) && compare(d, -1.e2)); + + BOOST_TEST(test("-.2e3", double_)); + BOOST_TEST(test_attr("-.2e3", double_, d) && compare(d, -.2e3)); + + BOOST_TEST(test("-2e3", double_)); + BOOST_TEST(test_attr("-2e3", double_, d) && compare(d, -2e3)); + + BOOST_TEST(!test("-e3", double_)); + BOOST_TEST(!test_attr("-e3", double_, d)); + + BOOST_TEST(!test("-1.2e", double_)); + BOOST_TEST(!test_attr("-1.2e", double_, d)); + + using boost::math::fpclassify; + using boost::math::signbit; + BOOST_TEST(test("-inf", double_)); + BOOST_TEST(test("-infinity", double_)); + BOOST_TEST(test_attr("-inf", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + BOOST_TEST(test_attr("-infinity", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + BOOST_TEST(test("-INF", double_)); + BOOST_TEST(test("-INFINITY", double_)); + BOOST_TEST(test_attr("-INF", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + BOOST_TEST(test_attr("-INFINITY", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + + BOOST_TEST(test("-nan", double_)); + BOOST_TEST(test_attr("-nan", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + BOOST_TEST(test("-NAN", double_)); + BOOST_TEST(test_attr("-NAN", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + + BOOST_TEST(test("-nan(...)", double_)); + BOOST_TEST(test_attr("-nan(...)", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + BOOST_TEST(test("-NAN(...)", double_)); + BOOST_TEST(test_attr("-NAN(...)", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + } + + /////////////////////////////////////////////////////////////////////////////// + // strict real number tests + /////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::real_spec; + using boost::spirit::qi::parse; + using boost::spirit::qi::strict_ureal_policies; + using boost::spirit::qi::strict_real_policies; + + real_spec > strict_udouble; + real_spec > strict_double; + double d; + + BOOST_TEST(!test("1234", strict_udouble)); + BOOST_TEST(!test_attr("1234", strict_udouble, d)); + + BOOST_TEST(test("1.2", strict_udouble)); + BOOST_TEST(test_attr("1.2", strict_udouble, d) && compare(d, 1.2)); + + BOOST_TEST(!test("-1234", strict_double)); + BOOST_TEST(!test_attr("-1234", strict_double, d)); + + BOOST_TEST(test("123.", strict_double)); + BOOST_TEST(test_attr("123.", strict_double, d) && compare(d, 123)); + + BOOST_TEST(test("3.E6", strict_double)); + BOOST_TEST(test_attr("3.E6", strict_double, d) && compare(d, 3e6)); + + real_spec > notrdot_real; + real_spec > nolddot_real; + + BOOST_TEST(!test("1234.", notrdot_real)); // Bad trailing dot + BOOST_TEST(!test(".1234", nolddot_real)); // Bad leading dot + } + + /////////////////////////////////////////////////////////////////////////// + // Special thousands separated numbers + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::real_spec; + using boost::spirit::qi::parse; + real_spec > ts_real; + double d; + + BOOST_TEST(test("123,456,789.01", ts_real)); + BOOST_TEST(test_attr("123,456,789.01", ts_real, d) && compare(d, 123456789.01)); + + BOOST_TEST(test("12,345,678.90", ts_real)); + BOOST_TEST(test_attr("12,345,678.90", ts_real, d) && compare(d, 12345678.90)); + + BOOST_TEST(test("1,234,567.89", ts_real)); + BOOST_TEST(test_attr("1,234,567.89", ts_real, d) && compare(d, 1234567.89)); + + BOOST_TEST(!test("1234,567,890", ts_real)); + BOOST_TEST(!test("1,234,5678,9", ts_real)); + BOOST_TEST(!test("1,234,567.89e6", ts_real)); + BOOST_TEST(!test("1,66", ts_real)); + } + + /////////////////////////////////////////////////////////////////////////// + // Custom data type + /////////////////////////////////////////////////////////////////////////// + { + using boost::math::concepts::real_concept; + using boost::spirit::qi::real_spec; + using boost::spirit::qi::real_policies; + using boost::spirit::qi::parse; + + real_spec > custom_real; + real_concept d; + + BOOST_TEST(test("-1234", custom_real)); + BOOST_TEST(test_attr("-1234", custom_real, d) && compare(d, -1234)); + + BOOST_TEST(test("-1.2e3", custom_real)); + BOOST_TEST(test_attr("-1.2e3", custom_real, d) && compare(d, -1.2e3)); + + BOOST_TEST(test("+1.2e3", custom_real)); + BOOST_TEST(test_attr("+1.2e3", custom_real, d) && compare(d, 1.2e3)); + + BOOST_TEST(test("-0.1", custom_real)); + BOOST_TEST(test_attr("-0.1", custom_real, d) && compare(d, -0.1)); + + BOOST_TEST(test("-1.2e-3", custom_real)); + BOOST_TEST(test_attr("-1.2e-3", custom_real, d) && compare(d, -1.2e-3)); + + BOOST_TEST(test("-1.e2", custom_real)); + BOOST_TEST(test_attr("-1.e2", custom_real, d) && compare(d, -1.e2)); + + BOOST_TEST(test("-.2e3", custom_real)); + BOOST_TEST(test_attr("-.2e3", custom_real, d) && compare(d, -.2e3)); + + BOOST_TEST(test("-2e3", custom_real)); + BOOST_TEST(test_attr("-2e3", custom_real, d) && compare(d, -2e3)); + + BOOST_TEST(!test("-e3", custom_real)); + BOOST_TEST(!test_attr("-e3", custom_real, d)); + + BOOST_TEST(!test("-1.2e", custom_real)); + BOOST_TEST(!test_attr("-1.2e", custom_real, d)); + } + + return boost::report_errors(); +} diff --git a/test/qi/rule.cpp b/test/qi/rule.cpp new file mode 100644 index 000000000..0eea493c5 --- /dev/null +++ b/test/qi/rule.cpp @@ -0,0 +1,227 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit::qi; + using namespace boost::spirit::ascii; + + { // basic tests + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + + start = *(a | b | c); + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_TEST(test("aaaabababaaabbb", start)); + BOOST_TEST(test("aaaabababaaabba", start, false)); + } + + { // basic tests w/ skipper + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + + start = *(a | b | c); + BOOST_TEST(test(" a b c a b c a c b", start, space)); + + // allow no skipping too: + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_TEST(test(" a a a a b a b a b a a a b b b ", start, space)); + BOOST_TEST(test(" a a a a b a b a b a a a b b a ", start, space, false)); + } + + { // alias tests + + rule a, b, c, d, start; + + a = 'a'; + b = 'b'; + c = 'c'; + d = start.alias(); // d will always track start + + start = *(a | b | c); + BOOST_TEST(test("abcabcacb", d)); + + start = (a | b) >> (start | b); + BOOST_TEST(test("aaaabababaaabbb", d)); + } + + { // copy tests + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + + // The FF is the dynamic equivalent of start = *(a | b | c); + start = a; + start = start.copy() | b; + start = start.copy() | c; + start = *(start.copy()); + + BOOST_TEST(test("abcabcacb", start)); + + // The FF is the dynamic equivalent of start = (a | b) >> (start | b); + start = b; + start = a | start.copy(); + start = start.copy() >> (start | b); + + BOOST_TEST(test("aaaabababaaabbb", start)); + BOOST_TEST(test("aaaabababaaabba", start, false)); + } + + { // context tests + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_val; + + char ch; + rule a; + a = alpha[_val = _1]; + + BOOST_TEST(test("x", a[ref(ch) = _1])); + BOOST_TEST(ch == 'x'); + + BOOST_TEST(test_attr("z", a, ch)); // attribute is given. + BOOST_TEST(ch == 'z'); + } + + { // context (w/arg) tests + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_r1; + using boost::spirit::arg_names::_r2; + using boost::spirit::arg_names::_val; + + char ch; + rule a; // 1 arg + a = alpha[_val = _1 + _r1]; + + BOOST_TEST(test("x", a(val(1))[ref(ch) = _1])); + BOOST_TEST(ch == 'x' + 1); + + BOOST_TEST(test_attr("a", a(1), ch)); // allow scalars as rule args too. + BOOST_TEST(ch == 'a' + 1); + + rule b; // 2 args + b = alpha[_val = _1 + _r1 + _r2]; + BOOST_TEST(test_attr("a", b(1, 2), ch)); + BOOST_TEST(ch == 'a' + 1 + 2); + } + + { // context (w/locals) tests + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_a; + using boost::spirit::char_; + using boost::spirit::locals; + + rule > a; // 1 local + a = alpha[_a = _1] >> char_(_a); + BOOST_TEST(test("aa", a)); + BOOST_TEST(!test("ax", a)); + } + + { // context (w/args and locals) tests + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_r1; + using boost::spirit::arg_names::_a; + using boost::spirit::char_; + using boost::spirit::locals; + + rule > a; // 1 arg + 1 local + a = alpha[_a = _1 + _r1] >> char_(_a); + BOOST_TEST(test("ab", a(val(1)))); + BOOST_TEST(test("xy", a(val(1)))); + BOOST_TEST(!test("ax", a(val(1)))); + } + + { // bug: test that injected attributes are ok + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_r1; + using boost::spirit::arg_names::_val; + using boost::spirit::char_; + + rule r; + + // problem code: + r = char_(_r1)[_val = _1]; + } + + { // error handling + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::phoenix::val; + using boost::spirit::int_; + using boost::spirit::arg_names::_4; // what + using boost::spirit::arg_names::_3; // error pos + using boost::spirit::arg_names::_2; // end + using boost::spirit::qi::fail; + + rule r; + r = '(' > int_ > ',' > int_ > ')'; + + on_error + ( + r, std::cout + << val("Error! Expecting: ") + << _4 + << val(" Here: \"") + << construct(_3, _2) + << val("\"") + << std::endl + ); + + BOOST_TEST(test("(123,456)", r)); + BOOST_TEST(!test("(abc,def)", r)); + BOOST_TEST(!test("(123,456]", r)); + BOOST_TEST(!test("(123;456)", r)); + BOOST_TEST(!test("[123,456]", r)); + } + + return boost::report_errors(); +} + diff --git a/test/qi/rule_fail.cpp b/test/qi/rule_fail.cpp new file mode 100644 index 000000000..7ac5c4a0d --- /dev/null +++ b/test/qi/rule_fail.cpp @@ -0,0 +1,31 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; + +// this test must fail compiling +int main() +{ + char const* input = "some input, it doesn't matter"; + char const* end = &input[strlen(input)+1]; + + rule > def; + def = int_ >> *(',' >> int_); + + bool r = phrase_parse(input, end, def, + space | ('%' >> *~char_('\n') >> '\n')); + + return 0; +} diff --git a/test/qi/sequence.cpp b/test/qi/sequence.cpp new file mode 100644 index 000000000..001f7f29b --- /dev/null +++ b/test/qi/sequence.cpp @@ -0,0 +1,204 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using boost::fusion::vector; + using boost::fusion::at_c; + using spirit_test::test; + using spirit_test::test_attr; + + { + BOOST_TEST((test("aa", char_ >> char_))); + BOOST_TEST((test("aaa", char_ >> char_ >> char_('a')))); + BOOST_TEST((test("xi", char_('x') >> char_('i')))); + BOOST_TEST((!test("xi", char_('x') >> char_('o')))); + BOOST_TEST((test("xin", char_('x') >> char_('i') >> char_('n')))); + + } + + { + BOOST_TEST((test(" a a ", char_ >> char_, space))); + BOOST_TEST((test(" x i ", char_('x') >> char_('i'), space))); + BOOST_TEST((!test(" x i ", char_('x') >> char_('o'), space))); + } + + { + BOOST_TEST((test(" Hello, World", lit("Hello") >> ',' >> "World", space))); + } + + { + vector attr; + BOOST_TEST((test_attr("abcdefg", char_ >> char_ >> "cdefg", attr))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'b')); + } + + { + vector attr; + BOOST_TEST((test_attr(" a\n b\n c\n ", char_ >> char_ >> char_, attr, space))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'b')); + BOOST_TEST((at_c<2>(attr) == 'c')); + } + + { + // unused_type means we don't care about the attribute + vector attr; + BOOST_TEST((test_attr("abc", char_ >> 'b' >> char_, attr))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'c')); + } + + { + // omit[] means we don't receive the attribute + vector attr; + BOOST_TEST((test_attr("abc", omit[char_] >> omit['b'] >> char_, attr))); + BOOST_TEST((at_c<0>(attr) == 'c')); + } + + { + // If all elements except 1 is omitted, the attribute is + // a single-element sequence. For this case alone, we allow + // naked attributes (unwrapped in a fusion sequence). + char attr; + BOOST_TEST((test_attr("abc", omit[char_] >> 'b' >> char_, attr))); + BOOST_TEST((attr == 'c')); + } + + { + // omit[] means we don't receive the attribute + vector<> attr; + BOOST_TEST((test_attr("abc", omit[char_] >> omit['b'] >> omit[char_], attr))); + } + + { + // omit[] means we don't receive the attribute + // this test is merely a compile test, because using a unused as the + // explicit attribute doesn't make any sense + unused_type attr; + BOOST_TEST((test_attr("abc", omit[char_ >> 'b' >> char_], attr))); + } + + { + // omit[] means we don't receive the attribute, if all elements of a + // sequence have unused attributes, the whole sequence has an unused + // attribute as well + vector attr; + BOOST_TEST((test_attr("abcde", + char_ >> (omit[char_] >> omit['c'] >> omit[char_]) >> char_, attr))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'e')); + } + + { + // "hello" has an unused_type. unused attrubutes are not part of the sequence + vector attr; + BOOST_TEST((test_attr("a hello c", char_ >> "hello" >> char_, attr, space))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'c')); + } + + { + // omit[] means we don't receive the attribute + vector attr; + BOOST_TEST((test_attr("a hello c", char_ >> "hello" >> omit[char_], attr, space))); + BOOST_TEST((at_c<0>(attr) == 'a')); + } + + { + // if only one node in a sequence is left (all the others are omitted), + // then we should also allow "naked" attributes (unwraped in a tuple) + int attr; + BOOST_TEST((test_attr("a 123 c", omit['a'] >> int_ >> omit['c'], attr, space))); + BOOST_TEST((attr == 123)); + } + + { + // unused means we don't care about the attribute + BOOST_TEST((test_attr("abc", char_ >> 'b' >> char_, unused))); + } + + { + BOOST_TEST((test("aA", no_case[char_('a') >> 'a']))); + BOOST_TEST((test("BEGIN END", no_case[lit("begin") >> "end"], space))); + BOOST_TEST((!test("BEGIN END", no_case[lit("begin") >> "nend"], space))); + } + + { +#ifdef SPIRIT_TEST_COMPILE_FAIL // $$$ + char_ >> char_ = char_ >> char_; // disallow this! +#endif + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + char c = 0; + int n = 0; + + BOOST_TEST(test("x123\"a string\"", (char_ >> int_ >> "\"a string\"") + [ref(c) = _1, ref(n) = _2])); + BOOST_TEST(c == 'x'); + BOOST_TEST(n == 123); + } + + { // test action with omitted attribute + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + char c = 0; + + BOOST_TEST(test("x123\"a string\"", (char_ >> omit[int_] >> "\"a string\"") + [ref(c) = _1])); + BOOST_TEST(c == 'x'); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + char c = 0; + int n = 0; + + BOOST_TEST(test("x 123 \"a string\"", (char_ >> int_ >> "\"a string\"") + [ref(c) = _1, ref(n) = _2], space)); + BOOST_TEST(c == 'x'); + BOOST_TEST(n == 123); + } + + { // test action with omitted attribute + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + int n = 0; + + BOOST_TEST(test("x 123 \"a string\"", + (omit[char_] >> int_ >> "\"a string\"")[ref(n) = _1], space)); + BOOST_TEST(n == 123); + } + + return boost::report_errors(); +} + diff --git a/test/qi/sequential_or.cpp b/test/qi/sequential_or.cpp new file mode 100644 index 000000000..b0418c44a --- /dev/null +++ b/test/qi/sequential_or.cpp @@ -0,0 +1,77 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using boost::spirit::ascii::alpha; + using boost::fusion::vector; + using boost::fusion::at_c; + using boost::optional; + + { + BOOST_TEST((test("a", char_('a') || char_('b')))); + BOOST_TEST((test("b", char_('a') || char_('b')))); + BOOST_TEST((test("ab", char_('a') || char_('b')))); + } + + { + vector, optional > attr; + + BOOST_TEST((test_attr("a", int_ || alpha, attr))); + BOOST_TEST((!at_c<0>(attr))); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123", int_ || alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((!at_c<1>(attr))); + + at_c<0>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123a", int_ || alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + BOOST_TEST((!test("a123", int_ || alpha))); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + namespace phx = boost::phoenix; + + optional i; + optional c; + + BOOST_TEST((test("123a", (int_ || alpha)[phx::ref(i) = _1, phx::ref(c) = _2]))); + BOOST_TEST((i.get() == 123)); + BOOST_TEST((c.get() == 'a')); + } + + return boost::report_errors(); +} + diff --git a/test/qi/symbols.cpp b/test/qi/symbols.cpp new file mode 100644 index 000000000..499e95131 --- /dev/null +++ b/test/qi/symbols.cpp @@ -0,0 +1,189 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + using namespace boost::spirit::qi; + + { // basics + symbols sym; + + sym.add + ("Joel") + ("Ruby") + ("Tenji") + ("Tutit") + ("Kim") + ("Joey") + ; + + BOOST_TEST((test("Joel", sym))); + BOOST_TEST((test("Ruby", sym))); + BOOST_TEST((test("Tenji", sym))); + BOOST_TEST((test("Tutit", sym))); + BOOST_TEST((test("Kim", sym))); + BOOST_TEST((test("Joey", sym))); + BOOST_TEST((!test("XXX", sym))); + + sym.remove + ("Joel") + ("Ruby") + ; + + BOOST_TEST((!test("Joel", sym))); + BOOST_TEST((!test("Ruby", sym))); + } + + { // comma syntax + symbols sym; + sym += "Joel", "Ruby", "Tenji", "Tutit", "Kim", "Joey"; + + BOOST_TEST((test("Joel", sym))); + BOOST_TEST((test("Ruby", sym))); + BOOST_TEST((test("Tenji", sym))); + BOOST_TEST((test("Tutit", sym))); + BOOST_TEST((test("Kim", sym))); + BOOST_TEST((test("Joey", sym))); + BOOST_TEST((!test("XXX", sym))); + + sym -= "Joel", "Ruby"; + + BOOST_TEST((!test("Joel", sym))); + BOOST_TEST((!test("Ruby", sym))); + } + + { // no-case handling + using namespace boost::spirit::ascii; + + symbols sym; + // NOTE: make sure all entries are in lower-case!!! + sym = "joel", "ruby", "tenji", "tutit", "kim", "joey"; + + BOOST_TEST((test("joel", no_case[sym]))); + BOOST_TEST((test("ruby", no_case[sym]))); + BOOST_TEST((test("tenji", no_case[sym]))); + BOOST_TEST((test("tutit", no_case[sym]))); + BOOST_TEST((test("kim", no_case[sym]))); + BOOST_TEST((test("joey", no_case[sym]))); + + BOOST_TEST((test("JOEL", no_case[sym]))); + BOOST_TEST((test("RUBY", no_case[sym]))); + BOOST_TEST((test("TENJI", no_case[sym]))); + BOOST_TEST((test("TUTIT", no_case[sym]))); + BOOST_TEST((test("KIM", no_case[sym]))); + BOOST_TEST((test("JOEY", no_case[sym]))); + } + + { // attributes + symbols sym; + + sym.add + ("Joel", 1) + ("Ruby", 2) + ("Tenji", 3) + ("Tutit", 4) + ("Kim", 5) + ("Joey", 6) + ; + + int i; + BOOST_TEST((test_attr("Joel", sym, i))); + BOOST_TEST(i == 1); + BOOST_TEST((test_attr("Ruby", sym, i))); + BOOST_TEST(i == 2); + BOOST_TEST((test_attr("Tenji", sym, i))); + BOOST_TEST(i == 3); + BOOST_TEST((test_attr("Tutit", sym, i))); + BOOST_TEST(i == 4); + BOOST_TEST((test_attr("Kim", sym, i))); + BOOST_TEST(i == 5); + BOOST_TEST((test_attr("Joey", sym, i))); + BOOST_TEST(i == 6); + BOOST_TEST((!test_attr("XXX", sym, i))); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + symbols sym; + sym.add + ("Joel", 1) + ("Ruby", 2) + ("Tenji", 3) + ("Tutit", 4) + ("Kim", 5) + ("Joey", 6) + ; + + int i; + BOOST_TEST((test("Joel", sym[ref(i) = _1]))); + BOOST_TEST(i == 1); + BOOST_TEST((test("Ruby", sym[ref(i) = _1]))); + BOOST_TEST(i == 2); + BOOST_TEST((test("Tenji", sym[ref(i) = _1]))); + BOOST_TEST(i == 3); + BOOST_TEST((test("Tutit", sym[ref(i) = _1]))); + BOOST_TEST(i == 4); + BOOST_TEST((test("Kim", sym[ref(i) = _1]))); + BOOST_TEST(i == 5); + BOOST_TEST((test("Joey", sym[ref(i) = _1]))); + BOOST_TEST(i == 6); + BOOST_TEST((!test("XXX", sym[ref(i) = _1]))); + } + + { // construction from symbol array + char const* syms[] = {"Joel","Ruby","Tenji","Tutit","Kim","Joey"}; + symbols sym(syms); + + BOOST_TEST((test("Joel", sym))); + BOOST_TEST((test("Ruby", sym))); + BOOST_TEST((test("Tenji", sym))); + BOOST_TEST((test("Tutit", sym))); + BOOST_TEST((test("Kim", sym))); + BOOST_TEST((test("Joey", sym))); + BOOST_TEST((!test("XXX", sym))); + } + + { // construction from 2 arrays + + char const* syms[] = {"Joel","Ruby","Tenji","Tutit","Kim","Joey"}; + int data[] = {1,2,3,4,5,6}; + symbols sym(syms, data); + + int i; + BOOST_TEST((test_attr("Joel", sym, i))); + BOOST_TEST(i == 1); + BOOST_TEST((test_attr("Ruby", sym, i))); + BOOST_TEST(i == 2); + BOOST_TEST((test_attr("Tenji", sym, i))); + BOOST_TEST(i == 3); + BOOST_TEST((test_attr("Tutit", sym, i))); + BOOST_TEST(i == 4); + BOOST_TEST((test_attr("Kim", sym, i))); + BOOST_TEST(i == 5); + BOOST_TEST((test_attr("Joey", sym, i))); + BOOST_TEST(i == 6); + BOOST_TEST((!test_attr("XXX", sym, i))); + } + + return boost::report_errors(); +} diff --git a/test/qi/test.hpp b/test/qi/test.hpp new file mode 100644 index 000000000..4f78eaaef --- /dev/null +++ b/test/qi/test.hpp @@ -0,0 +1,75 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_TEST_FEB_01_2007_0605PM) +#define BOOST_SPIRIT_TEST_FEB_01_2007_0605PM + +#include +#include + +namespace spirit_test +{ + template + bool test(Char const* in, Parser const& p, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::parse(in, last, p) + && (!full_match || (in == last)); + } + + template + bool test(Char const* in, Parser const& p + , Skipper const& s, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::phrase_parse(in, last, p, s) + && (!full_match || (in == last)); + } + + template + bool test_attr(Char const* in, Parser const& p + , Attr& attr, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::parse(in, last, p, attr) + && (!full_match || (in == last)); + } + + template + bool test_attr(Char const* in, Parser const& p + , Attr& attr, Skipper const& s, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::phrase_parse(in, last, p, attr, s) + && (!full_match || (in == last)); + } +} + +#endif diff --git a/test/qi/tst.cpp b/test/qi/tst.cpp new file mode 100644 index 000000000..0387de263 --- /dev/null +++ b/test/qi/tst.cpp @@ -0,0 +1,354 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include + +#include +#include +#include + +namespace +{ + template + void add(TST& tst, Char const* s, int data) + { + Char const* last = s; + while (*last) + last++; + tst.add(s, last, data); + } + + template + void remove(TST& tst, Char const* s) + { + Char const* last = s; + while (*last) + last++; + tst.remove(s, last); + } + + template + void check(TST const& tst, Char const* s, bool expected, int N = 0, int val = -1) + { + Char const* first = s; + Char const* last = s; + while (*last) + last++; + int* r = tst.find(s, last); + BOOST_TEST((r != 0) == expected); + if (r != 0) + BOOST_TEST((s-first) == N); + if (r) + BOOST_TEST(*r == val); + } + + struct printer + { + template + void operator()(String const& s, Data const& data) + { + std::cout << " " << s << ": " << data << std::endl; + } + }; + + template + void print(TST const& tst) + { + std::cout << '[' << std::endl; + tst.for_each(printer()); + std::cout << ']' << std::endl; + } + + struct no_case_filter + { + template + Char operator()(Char ch) const + { + return std::tolower(ch); + } + }; + + template + void nc_check(TST const& tst, Char const* s, bool expected, int N = 0, int val = -1) + { + Char const* first = s; + Char const* last = s; + while (*last) + last++; + int* r = tst.find(s, last, no_case_filter()); + BOOST_TEST((r != 0) == expected); + if (r != 0) + BOOST_TEST((s-first) == N); + if (r) + BOOST_TEST(*r == val); + } +} + +template +void tests() +{ + { // basic tests + Lookup lookup; + + check(lookup, "not-yet-there", false); + check(lookup, "", false); + + add(lookup, "apple", 123); + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applexxx", true, 5, 123); // partial match + + add(lookup, "applepie", 456); + check(lookup, "applepie", true, 8, 456); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applepiexxx", true, 8, 456); // partial match + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "applexxx", true, 5, 123); // partial match + } + + { // variation of above + Lookup lookup; + + add(lookup, "applepie", 456); + add(lookup, "apple", 123); + + check(lookup, "applepie", true, 8, 456); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applepiexxx", true, 8, 456); // partial match + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "applexxx", true, 5, 123); // partial match + } + { // variation of above + Lookup lookup; + + add(lookup, "applepie", 456); + add(lookup, "apple", 123); + + check(lookup, "applepie", true, 8, 456); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applepiexxx", true, 8, 456); // partial match + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "applexxx", true, 5, 123); // partial match + } + + { // narrow char tests + Lookup lookup; + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "banana", true, 6, 3); + check(lookup, "apple", true, 5, 5); + check(lookup, "pizza", false); + check(lookup, "steak", false); + check(lookup, "applepie", true, 8, 4); + check(lookup, "bananarama", true, 6, 3); + check(lookup, "applet", true, 5, 5); + check(lookup, "applepi", true, 5, 5); + check(lookup, "appl", false); + + check(lookup, "pineapplez", true, 9, 1); + check(lookup, "orangez", true, 6, 2); + check(lookup, "bananaz", true, 6, 3); + check(lookup, "applez", true, 5, 5); + check(lookup, "pizzaz", false); + check(lookup, "steakz", false); + check(lookup, "applepiez", true, 8, 4); + check(lookup, "bananaramaz", true, 6, 3); + check(lookup, "appletz", true, 5, 5); + check(lookup, "applepix", true, 5, 5); + } + + { // wide char tests + WideLookup lookup; + add(lookup, L"pineapple", 1); + add(lookup, L"orange", 2); + add(lookup, L"banana", 3); + add(lookup, L"applepie", 4); + add(lookup, L"apple", 5); + + check(lookup, L"pineapple", true, 9, 1); + check(lookup, L"orange", true, 6, 2); + check(lookup, L"banana", true, 6, 3); + check(lookup, L"apple", true, 5, 5); + check(lookup, L"pizza", false); + check(lookup, L"steak", false); + check(lookup, L"applepie", true, 8, 4); + check(lookup, L"bananarama", true, 6, 3); + check(lookup, L"applet", true, 5, 5); + check(lookup, L"applepi", true, 5, 5); + check(lookup, L"appl", false); + + check(lookup, L"pineapplez", true, 9, 1); + check(lookup, L"orangez", true, 6, 2); + check(lookup, L"bananaz", true, 6, 3); + check(lookup, L"applez", true, 5, 5); + check(lookup, L"pizzaz", false); + check(lookup, L"steakz", false); + check(lookup, L"applepiez", true, 8, 4); + check(lookup, L"bananaramaz", true, 6, 3); + check(lookup, L"appletz", true, 5, 5); + check(lookup, L"applepix", true, 5, 5); + } + + { // test remove + Lookup lookup; + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "banana", true, 6, 3); + check(lookup, "apple", true, 5, 5); + check(lookup, "applepie", true, 8, 4); + check(lookup, "bananarama", true, 6, 3); + check(lookup, "applet", true, 5, 5); + check(lookup, "applepi", true, 5, 5); + check(lookup, "appl", false); + + remove(lookup, "banana"); + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "banana", false); + check(lookup, "apple", true, 5, 5); + check(lookup, "applepie", true, 8, 4); + check(lookup, "bananarama", false); + check(lookup, "applet", true, 5, 5); + check(lookup, "applepi", true, 5, 5); + check(lookup, "appl", false); + + remove(lookup, "apple"); + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "apple", false); + check(lookup, "applepie", true, 8, 4); + check(lookup, "applet", false); + check(lookup, "applepi", false); + check(lookup, "appl", false); + + remove(lookup, "orange"); + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", false); + check(lookup, "applepie", true, 8, 4); + + remove(lookup, "pineapple"); + check(lookup, "pineapple", false); + check(lookup, "orange", false); + check(lookup, "applepie", true, 8, 4); + + remove(lookup, "applepie"); + check(lookup, "applepie", false); + } + + { // copy/assign/clear test + Lookup lookupa; + add(lookupa, "pineapple", 1); + add(lookupa, "orange", 2); + add(lookupa, "banana", 3); + add(lookupa, "applepie", 4); + add(lookupa, "apple", 5); + + Lookup lookupb(lookupa); // copy ctor + check(lookupb, "pineapple", true, 9, 1); + check(lookupb, "orange", true, 6, 2); + check(lookupb, "banana", true, 6, 3); + check(lookupb, "apple", true, 5, 5); + check(lookupb, "pizza", false); + check(lookupb, "steak", false); + check(lookupb, "applepie", true, 8, 4); + check(lookupb, "bananarama", true, 6, 3); + check(lookupb, "applet", true, 5, 5); + check(lookupb, "applepi", true, 5, 5); + check(lookupb, "appl", false); + + lookupb.clear(); // clear + check(lookupb, "pineapple", false); + check(lookupb, "orange", false); + check(lookupb, "banana", false); + check(lookupb, "apple", false); + check(lookupb, "applepie", false); + check(lookupb, "bananarama", false); + check(lookupb, "applet", false); + check(lookupb, "applepi", false); + check(lookupb, "appl", false); + + lookupb = lookupa; // assign + check(lookupb, "pineapple", true, 9, 1); + check(lookupb, "orange", true, 6, 2); + check(lookupb, "banana", true, 6, 3); + check(lookupb, "apple", true, 5, 5); + check(lookupb, "pizza", false); + check(lookupb, "steak", false); + check(lookupb, "applepie", true, 8, 4); + check(lookupb, "bananarama", true, 6, 3); + check(lookupb, "applet", true, 5, 5); + check(lookupb, "applepi", true, 5, 5); + check(lookupb, "appl", false); + } + + { // test for_each + Lookup lookup; + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + print(lookup); + } + + { // case insensitive tests + Lookup lookup; + + // NOTE: make sure all entries are in lower-case!!! + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + nc_check(lookup, "pineapple", true, 9, 1); + nc_check(lookup, "orange", true, 6, 2); + nc_check(lookup, "banana", true, 6, 3); + nc_check(lookup, "apple", true, 5, 5); + nc_check(lookup, "applepie", true, 8, 4); + + nc_check(lookup, "PINEAPPLE", true, 9, 1); + nc_check(lookup, "ORANGE", true, 6, 2); + nc_check(lookup, "BANANA", true, 6, 3); + nc_check(lookup, "APPLE", true, 5, 5); + nc_check(lookup, "APPLEPIE", true, 8, 4); + + nc_check(lookup, "pineApple", true, 9, 1); + nc_check(lookup, "orangE", true, 6, 2); + nc_check(lookup, "Banana", true, 6, 3); + nc_check(lookup, "aPPLe", true, 5, 5); + nc_check(lookup, "ApplePie", true, 8, 4); + + print(lookup); + } +} + +int +main() +{ + using namespace boost::spirit::qi; + + tests, tst >(); + tests, tst_map >(); + + return boost::report_errors(); +} + diff --git a/test/qi/uint.cpp b/test/qi/uint.cpp new file mode 100644 index 000000000..7645e66ac --- /dev/null +++ b/test/qi/uint.cpp @@ -0,0 +1,175 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// +// *** BEWARE PLATFORM DEPENDENT!!! *** +// *** The following assumes 32 bit integers and 64 bit long longs. +// *** Modify these constant strings when appropriate. +// +/////////////////////////////////////////////////////////////////////////////// + + char const* max_unsigned = "4294967295"; + char const* unsigned_overflow = "4294967296"; + char const* max_int = "2147483647"; + char const* int_overflow = "2147483648"; + char const* min_int = "-2147483648"; + char const* int_underflow = "-2147483649"; + char const* max_binary = "11111111111111111111111111111111"; + char const* binary_overflow = "100000000000000000000000000000000"; + char const* max_octal = "37777777777"; + char const* octal_overflow = "100000000000"; + char const* max_hex = "FFFFFFFF"; + char const* hex_overflow = "100000000"; + +int +main() +{ + using namespace spirit_test; + + /////////////////////////////////////////////////////////////////////////// + // unsigned tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::uint; + unsigned u; + + BOOST_TEST(test("123456", uint)); + BOOST_TEST(test_attr("123456", uint, u)); + BOOST_TEST(u == 123456); + + BOOST_TEST(test(max_unsigned, uint)); + BOOST_TEST(test_attr(max_unsigned, uint, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(unsigned_overflow, uint)); + BOOST_TEST(!test_attr(unsigned_overflow, uint, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // binary tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::bin; + unsigned u; + + BOOST_TEST(test("11111110", bin)); + BOOST_TEST(test_attr("11111110", bin, u)); + BOOST_TEST(u == 0xFE); + + BOOST_TEST(test(max_binary, bin)); + BOOST_TEST(test_attr(max_binary, bin, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(binary_overflow, bin)); + BOOST_TEST(!test_attr(binary_overflow, bin, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // octal tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::oct; + unsigned u; + + BOOST_TEST(test("12545674515", oct)); + BOOST_TEST(test_attr("12545674515", oct, u)); + BOOST_TEST(u == 012545674515); + + BOOST_TEST(test(max_octal, oct)); + BOOST_TEST(test_attr(max_octal, oct, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(octal_overflow, oct)); + BOOST_TEST(!test_attr(octal_overflow, oct, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // hex tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::hex; + unsigned u; + + BOOST_TEST(test("95BC8DF", hex)); + BOOST_TEST(test_attr("95BC8DF", hex, u)); + BOOST_TEST(u == 0x95BC8DF); + + BOOST_TEST(test("abcdef12", hex)); + BOOST_TEST(test_attr("abcdef12", hex, u)); + BOOST_TEST(u == 0xabcdef12); + + BOOST_TEST(test(max_hex, hex)); + BOOST_TEST(test_attr(max_hex, hex, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(hex_overflow, hex)); + BOOST_TEST(!test_attr(hex_overflow, hex, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // limited fieldwidth + /////////////////////////////////////////////////////////////////////////// + { + unsigned u; + using boost::spirit::qi::uint_spec; + + uint_spec uint3; + BOOST_TEST(test("123456", uint3, false)); + BOOST_TEST(test_attr("123456", uint3, u, false)); + BOOST_TEST(u == 123); + + uint_spec uint4; + BOOST_TEST(test("123456", uint4, false)); + BOOST_TEST(test_attr("123456", uint4, u, false)); + BOOST_TEST(u == 1234); + + BOOST_TEST(!test("1", uint4)); + BOOST_TEST(!test_attr("1", uint4, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // uint_spec tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::uint_spec; + using boost::spirit::unused_type; + uint_spec any_int; + + BOOST_TEST(test("123456", any_int)); + BOOST_TEST(test("1234567890123456789", any_int)); + } + + /////////////////////////////////////////////////////////////////////////// + // action tests + /////////////////////////////////////////////////////////////////////////// + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + using boost::spirit::ascii::space; + using boost::spirit::uint; + int n; + + BOOST_TEST(test("123", uint[ref(n) = _1])); + BOOST_TEST(n == 123); + BOOST_TEST(test(" 456", uint[ref(n) = _1], space)); + BOOST_TEST(n == 456); + } + + return boost::report_errors(); +} diff --git a/test/support/detail/sstream.hpp b/test/support/detail/sstream.hpp new file mode 100644 index 000000000..bb94cb9c9 --- /dev/null +++ b/test/support/detail/sstream.hpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2003 Martin Wille + http://spirit.sourceforge.net/ + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include + +/////////////////////////////////////////////////////////////////////////// +// workaround for prestandard support of stringstreams +// +// * defines sstream_t for the string stream type +// * defines std::string getstring(sstream_t &); +// + +#ifdef BOOST_NO_STRINGSTREAM +# include + typedef strstream sstream_t; + std::string + getstring(std::strstream& ss) + { + ss << ends; + std::string rval = ss.str(); + ss.freeze(false); + return rval; + } +#else +# include + typedef std::stringstream sstream_t; + std::string + getstring(std::stringstream &ss) + { + return ss.str(); + } +#endif + +void use_getstring_to_avoid_compiler_warnings_about_unused_functions() +{ + sstream_t ss; + getstring(ss); + if(!ss) { // to be not recursive on all control paths + use_getstring_to_avoid_compiler_warnings_about_unused_functions(); + } +} diff --git a/test/support/hold_any.cpp b/test/support/hold_any.cpp new file mode 100644 index 000000000..988ef321c --- /dev/null +++ b/test/support/hold_any.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2007-2008 Hartmut Kaiser +// Copyright (c) Christopher Diggins 2005 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// #define BOOST_SPIRIT_ANY_IMPLICIT_CASTING + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace boost::spirit; + +/////////////////////////////////////////////////////////////////////////////// +bool output_any (hold_any const& a, std::string expected) +{ + std::ostringstream o; + if (a.type() == typeid(int)) + { + o << any_cast(a); + } + else if (a.type() == typeid(char)) + { + o << any_cast(a); + } + else if (a.type() == typeid(double)) + { + o << any_cast(a); + } + else if (a.type() == typeid(complex)) + { + o << any_cast >(a); + } + else + { + o << "unexpected type: " << a.type().name(); + return false; + } + return o.str() == expected; +} + +template +bool output_any_direct (T const& v, std::string expected) +{ + std::ostringstream o; + o << v; + return o.str() == expected; +} + +void simple_any_test() +{ + BOOST_TEST(output_any(42, "42")); + BOOST_TEST(output_any('q', "q")); + BOOST_TEST(output_any(3.14, "3.14")); + BOOST_TEST(output_any(complex(1, 2), "(1,2)")); + + int n = 42; BOOST_TEST(output_any(n, "42")); + char c = 'q'; BOOST_TEST(output_any(c, "q")); + double d = 3.14; BOOST_TEST(output_any(d, "3.14")); + complex x(1, 2); BOOST_TEST(output_any(x, "(1,2)")); + + hold_any a; + BOOST_TEST(output_any(a = n, "42")); + BOOST_TEST(output_any(a = c, "q")); + BOOST_TEST(output_any(a = d, "3.14")); + BOOST_TEST(output_any(a = x, "(1,2)")); + BOOST_TEST(output_any(a = 13, "13")); + +#ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING + BOOST_TEST(output_any_direct(n = hold_any(42), "42")); + BOOST_TEST(output_any_direct(c = hold_any('q'), "q")); + BOOST_TEST(output_any_direct(d = hold_any(3.14), "3.14")); + BOOST_TEST(output_any_direct(x = complex(hold_any(complex(1, 2))), "(1,2)")); +#endif + + BOOST_TEST(output_any_direct(hold_any(42), "42")); + BOOST_TEST(output_any_direct(hold_any('q'), "q")); + BOOST_TEST(output_any_direct(hold_any(3.14), "3.14")); + BOOST_TEST(output_any_direct(hold_any(complex(1, 2)), "(1,2)")); + + BOOST_TEST(!a.empty()); + a = 0; + BOOST_TEST(!a.empty()); + a.reset(); + BOOST_TEST(a.empty()); + + try { + any_cast(a); + BOOST_TEST(false); + } + catch (boost::spirit::bad_any_cast const&) { + BOOST_TEST(true); + } + catch (...) { + BOOST_TEST(false); + } +} + +void test2(hold_any const& x, hold_any const& y) +{ + BOOST_TEST(x.type() != y.type()); + BOOST_TEST(x.type().name() != y.type().name()); +} + +/////////////////////////////////////////////////////////////////////////////// +int state; + +/////////////////////////////////////////////////////////////////////////////// +struct small_object +{ + small_object() {} + small_object(small_object const& fb) { state = 1; } + ~small_object() { state = 2; } +}; + +inline std::istream& +operator>> (std::istream& i, small_object&) +{ + return i; +} + +inline std::ostream& +operator<< (std::ostream& o, small_object const&) +{ + return o; +} + +/////////////////////////////////////////////////////////////////////////////// +struct large_object +{ + large_object() {} + large_object(large_object const& fb) { state = 3; } + ~large_object() { state = 4; } + + int data0; + int data1; + int data2; + int data3; +}; + +inline std::istream& +operator>> (std::istream& i, large_object&) +{ + return i; +} + +inline std::ostream& +operator<< (std::ostream& o, large_object const&) +{ + return o; +} + +void constructor_test() +{ + small_object lfb; + large_object bfb; + hold_any a; + state = 0; + + a = lfb; BOOST_TEST(1 == state); state = 0; + a = 42; BOOST_TEST(2 == state); state = 0; + a = bfb; BOOST_TEST(3 == state); state = 0; + a = 42; BOOST_TEST(4 == state); state = 0; + + // test assignment of large objects + a = bfb; + a = bfb; +} + +int main() +{ + test2(std::string("10"), std::complex(20)); + + constructor_test(); + simple_any_test(); + + hold_any non_const(10); + BOOST_TEST(any_cast(non_const) == 10); + *any_cast(&non_const) = 15; + BOOST_TEST(any_cast(non_const) == 15); + + hold_any const const_(10); + BOOST_TEST(any_cast(const_) == *any_cast(&const_)); + + hold_any a = 42, b = 'q'; + swap(a, b); + BOOST_TEST(any_cast(b) == 42); + BOOST_TEST(any_cast(a) == 'q'); + + return boost::report_errors(); +} + diff --git a/test/support/multi_pass.cpp b/test/support/multi_pass.cpp new file mode 100644 index 000000000..e3df135c0 --- /dev/null +++ b/test/support/multi_pass.cpp @@ -0,0 +1,770 @@ +/*============================================================================= + Copyright (c) 2001-2003 Daniel Nuffer + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "detail/sstream.hpp" + +using namespace std; +using namespace boost::spirit; + +sstream_t res; + +typedef boost::spirit::multi_pass > default_multi_pass_t; + +typedef look_ahead, 6> fixed_multi_pass_t; + +typedef multi_pass< + istream_iterator, + multi_pass_policies::input_iterator, + multi_pass_policies::first_owner, + multi_pass_policies::buf_id_check, + multi_pass_policies::std_deque +> first_owner_multi_pass_t; + + +// a functor to test out the functor_multi_pass +class my_functor +{ + public: + typedef char result_type; + my_functor() + : c('A') + {} + + char operator()() + { + if (c == 'M') + return eof; + else + return c++; + } + + static result_type eof; + private: + char c; +}; + +my_functor::result_type my_functor::eof = '\0'; + +typedef multi_pass< + my_functor, + multi_pass_policies::functor_input, + multi_pass_policies::first_owner, + multi_pass_policies::no_check, + multi_pass_policies::std_deque +> functor_multi_pass_t; + +void test_default_multi_pass() +{ + res << "-*= test_default_multi_pass =*-\n"; + istream_iterator end; + boost::scoped_ptr mpend(new default_multi_pass_t(end)); + + { + sstream_t ss; + ss << "test string"; + + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + + while (*mp1 != *mpend) + { + res << *((*mp1)++); + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new default_multi_pass_t(b)); + boost::scoped_ptr mp3(new default_multi_pass_t(b)); + *mp3 = *mp2; + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + boost::scoped_ptr mp2(new default_multi_pass_t(*mp1)); + + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new default_multi_pass_t(b)); + boost::scoped_ptr mp3(new default_multi_pass_t(b)); + *mp3 = *mp2; + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + ++*mp2; + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + boost::scoped_ptr mp2(new default_multi_pass_t(*mp1)); + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mp1) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + boost::scoped_ptr mp2(new default_multi_pass_t(a)); + BOOST_TEST(*mp1 != *mp2); + ++*mp1; + BOOST_TEST(*mp1 != *mp2); + + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new default_multi_pass_t(b)); + boost::scoped_ptr mp3(new default_multi_pass_t(b)); + *mp3 = *mp2; + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp2->clear_queue(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + try + { + res << **mp3; // this should throw illegal_backtracking + BOOST_TEST(0); + } + catch (const boost::spirit::multi_pass_policies::illegal_backtracking& /*e*/) + { + } + res << endl; + } + + +} + +void test_fixed_multi_pass() +{ + res << "-*= test_fixed_multi_pass =*-\n"; + istream_iterator end; + boost::scoped_ptr mpend(new fixed_multi_pass_t(end)); + + { + sstream_t ss; + ss << "test string"; + + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + + while (*mp1 != *mpend) + { + res << *((*mp1)++); + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new fixed_multi_pass_t(b)); + boost::scoped_ptr mp3(new fixed_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + boost::scoped_ptr mp2(new fixed_multi_pass_t(*mp1)); + + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new fixed_multi_pass_t(b)); + boost::scoped_ptr mp3(new fixed_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + ++*mp2; + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + boost::scoped_ptr mp2(new fixed_multi_pass_t(*mp1)); + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mp1) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + boost::scoped_ptr mp2(new fixed_multi_pass_t(a)); + BOOST_TEST(*mp1 != *mp2); + ++*mp1; + BOOST_TEST(*mp1 != *mp2); + + } + +} + +void test_first_owner_multi_pass() +{ + res << "-*= test_first_owner_multi_pass =*-\n"; + istream_iterator end; + boost::scoped_ptr mpend(new first_owner_multi_pass_t(end)); + + { + sstream_t ss; + ss << "test string"; + + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + + while (*mp1 != *mpend) + { + res << *((*mp1)++); + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(b)); + boost::scoped_ptr mp3(new first_owner_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(*mp1)); + + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(b)); + boost::scoped_ptr mp3(new first_owner_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + ++*mp2; + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(*mp1)); + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mp1) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(a)); + BOOST_TEST(*mp1 != *mp2); + ++*mp1; + BOOST_TEST(*mp1 != *mp2); + + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(b)); + boost::scoped_ptr mp3(new first_owner_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp2->clear_queue(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + try + { + res << **mp3; // this should throw illegal_backtracking + BOOST_TEST(0); + } + catch (const boost::spirit::multi_pass_policies::illegal_backtracking& /*e*/) + { + } + res << endl; + } + +} + + +void test_functor_multi_pass() +{ + res << "-*= test_functor_multi_pass =*-\n"; + functor_multi_pass_t mpend; + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + + while (mp1 != mpend) + { + res << *(mp1++); + } + + res << endl; + } + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + functor_multi_pass_t mp2 = functor_multi_pass_t(mp1); + + for (int i = 0; i < 4; ++i) + { + res << *mp1; + ++mp1; + } + + while (mp2 != mpend) + { + res << *mp2; + ++mp2; + } + + while (mp1 != mpend) + { + res << *mp1; + ++mp1; + } + + res << endl; + } + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + functor_multi_pass_t mp2 = functor_multi_pass_t(mp1); + + BOOST_TEST(mp1 == mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp1 <= mp2); + for (int i = 0; i < 4; ++i) + { + res << *mp1; + ++mp1; + } + + BOOST_TEST(mp1 != mp2); + BOOST_TEST(mp1 > mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp2 < mp1); + BOOST_TEST(mp2 <= mp1); + while (mp2 != mp1) + { + res << *mp2; + ++mp2; + } + + BOOST_TEST(mp1 == mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp1 <= mp2); + while (mp1 != mpend) + { + res << *mp1; + ++mp1; + } + + BOOST_TEST(mp1 != mp2); + BOOST_TEST(mp1 > mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp2 < mp1); + BOOST_TEST(mp2 <= mp1); + while (mp2 != mpend) + { + res << *mp2; + ++mp2; + } + + BOOST_TEST(mp1 == mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp1 <= mp2); + res << endl; + } + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + functor_multi_pass_t mp2 = functor_multi_pass_t(my_functor()); + BOOST_TEST(mp1 != mp2); + ++mp1; + BOOST_TEST(mp1 != mp2); + + } +} + +int main(int, char**) +{ + + test_default_multi_pass(); + test_fixed_multi_pass(); + test_first_owner_multi_pass(); + test_functor_multi_pass(); + + BOOST_TEST(getstring(res) == "-*= test_default_multi_pass =*-\n" + "teststring\n" + "teststring\n" + "testteststringstring\n" + "testtring\n" + "testteststringstring\n" + "teststring\n" + "-*= test_fixed_multi_pass =*-\n" + "teststring\n" + "teststring\n" + "testteststringstring\n" + "testtring\n" + "testteststringstring\n" + "-*= test_first_owner_multi_pass =*-\n" + "teststring\n" + "teststring\n" + "testteststringstring\n" + "testtring\n" + "testteststringstring\n" + "teststring\n" + "-*= test_functor_multi_pass =*-\n" + "ABCDEFGHIJKL\n" + "ABCDABCDEFGHIJKLEFGHIJKL\n" + "ABCDABCDEFGHIJKLEFGHIJKL\n"); + + return boost::report_errors(); +} diff --git a/test/support/multi_pass_compile.cpp b/test/support/multi_pass_compile.cpp new file mode 100644 index 000000000..860053577 --- /dev/null +++ b/test/support/multi_pass_compile.cpp @@ -0,0 +1,63 @@ +/*============================================================================= + Copyright (c) 2004-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +// This is a compile only test for verifying, whether the multi_pass<> +// iterator works ok with an input iterator, which returns a value_type and not +// a reference from its dereferencing operator. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_HAS_UNISTD_H) +#include // unlink() +#endif + +#if defined(__MINGW32__) +#include // unlink() +#endif + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace std; + +int main () +{ + // create a sample file + { + ofstream out("./input_file.txt"); + out << 1.0 << "," << 2.0; + } + + // read in the values from the sample file + { + ifstream in("./input_file.txt"); // we get our input from this file + + typedef multi_pass > iterator_type; + + iterator_type first(make_multi_pass(istreambuf_iterator(in))); + iterator_type last(make_multi_pass(istreambuf_iterator())); + + rule n_list; + n_list = double_ >> *(char_(',') >> double_); + BOOST_TEST(parse(first, last, n_list)); + } + +#if !defined(__COMO_VERSION__) + unlink("./input_file.txt"); +#endif + + return boost::report_errors(); +}