source: NonGTP/Boost/boost/wave/util/cpp_iterator.hpp @ 857

Revision 857, 65.6 KB checked in by igarcia, 18 years ago (diff)
Line 
1/*=============================================================================
2    Boost.Wave: A Standard compliant C++ preprocessor library
3
4    Definition of the preprocessor iterator
5   
6    http://www.boost.org/
7
8    Copyright (c) 2001-2005 Hartmut Kaiser. Distributed under the Boost
9    Software License, Version 1.0. (See accompanying file
10    LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11=============================================================================*/
12
13#if !defined(CPP_ITERATOR_HPP_175CA88F_7273_43FA_9039_BCF7459E1F29_INCLUDED)
14#define CPP_ITERATOR_HPP_175CA88F_7273_43FA_9039_BCF7459E1F29_INCLUDED
15
16#include <string>
17#include <vector>
18#include <list>
19#include <cstdlib>
20#include <cctype>
21
22#include <boost/assert.hpp>
23#include <boost/shared_ptr.hpp>
24#include <boost/filesystem/path.hpp>
25#include <boost/filesystem/operations.hpp>
26#include <boost/spirit/iterator/multi_pass.hpp>
27#include <boost/spirit/tree/parse_tree_utils.hpp>
28#include <boost/pool/pool_alloc.hpp>
29
30#include <boost/wave/wave_config.hpp>
31
32#include <boost/wave/util/insert_whitespace_detection.hpp>
33#include <boost/wave/util/eat_whitespace.hpp>
34#include <boost/wave/util/macro_helpers.hpp>
35#include <boost/wave/util/cpp_macromap_utils.hpp>
36#include <boost/wave/util/interpret_pragma.hpp>
37#include <boost/wave/util/transform_iterator.hpp>
38#include <boost/wave/util/functor_input.hpp>
39
40#include <boost/wave/grammars/cpp_grammar_gen.hpp>
41#include <boost/wave/grammars/cpp_expression_grammar_gen.hpp>
42#if BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0
43#include <boost/wave/grammars/cpp_predef_macros_gen.hpp>
44#endif
45
46#include <boost/wave/cpp_iteration_context.hpp>
47#include <boost/wave/cpp_exceptions.hpp>
48#include <boost/wave/language_support.hpp>
49
50///////////////////////////////////////////////////////////////////////////////
51namespace boost {
52namespace wave {
53namespace util {
54
55///////////////////////////////////////////////////////////////////////////////
56// retrieve the macro name from the parse tree
57template <typename ParseNodeT, typename TokenT, typename PositionT>
58inline void 
59retrieve_macroname(ParseNodeT const &node, boost::spirit::parser_id id,
60    TokenT &macroname, PositionT const &act_pos)
61{
62ParseNodeT const *name_node = 0;
63
64    using boost::spirit::find_node;
65    if (!find_node(node, id, &name_node))
66    {
67        // ill formed define statement (unexpected, should not happen)
68        BOOST_WAVE_THROW(preprocess_exception, bad_define_statement,
69            "bad parse tree (unexpected)", act_pos);
70    }
71   
72typename ParseNodeT::children_t const &children = name_node->children;
73
74    if (0 == children.size() ||
75        children[0].value.begin() == children[0].value.end())
76    {
77        // ill formed define statement (unexpected, should not happen)
78        BOOST_WAVE_THROW(preprocess_exception, bad_define_statement,
79            "bad parse tree (unexpected)", act_pos);
80    }
81
82// retrieve the macro name
83    macroname = *children[0].value.begin();
84}
85
86///////////////////////////////////////////////////////////////////////////////
87// retrieve the macro parameters or the macro definition from the parse tree
88template <typename ParseNodeT, typename TokenT, typename ContainerT>
89inline bool 
90retrieve_macrodefinition(
91    ParseNodeT const &node, boost::spirit::parser_id id,
92    ContainerT &macrodefinition, TokenT const &/*t*/)
93{
94    using namespace boost::wave;
95    typedef typename ParseNodeT::const_tree_iterator const_tree_iterator;
96
97// find macro parameters/macro definition inside the parse tree
98std::pair<const_tree_iterator, const_tree_iterator> nodes;
99
100    using boost::spirit::get_node_range;
101    if (get_node_range(node, id, nodes)) {
102    // copy all parameters to the supplied container
103        typename ContainerT::iterator last_nonwhite = macrodefinition.end();
104        const_tree_iterator end = nodes.second;
105       
106        for (const_tree_iterator cit = nodes.first; cit != end; ++cit) {
107            if ((*cit).value.begin() != (*cit).value.end()) {
108            typename ContainerT::iterator inserted = macrodefinition.insert(
109                macrodefinition.end(), *(*cit).value.begin());
110               
111                if (!IS_CATEGORY(macrodefinition.back(), WhiteSpaceTokenType) &&
112                    T_NEWLINE != token_id(macrodefinition.back()) &&
113                    T_EOF != token_id(macrodefinition.back()))
114                {
115                    last_nonwhite = inserted;
116                }
117            }
118        }
119       
120    // trim trailing whitespace (leading whitespace is trimmed by the grammar)
121        if (last_nonwhite != macrodefinition.end()) {
122            macrodefinition.erase(++last_nonwhite, macrodefinition.end());
123        }
124        return true;
125    }
126    return false;
127}
128
129#if BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0
130///////////////////////////////////////////////////////////////////////////////
131//  add an additional predefined macro given by a string (MACRO(x)=definition)
132template <typename ContextT>
133bool add_macro_definition(ContextT &ctx, std::string macrostring,
134    bool is_predefined, boost::wave::language_support language)
135{
136    typedef typename ContextT::token_type token_type;
137    typedef typename ContextT::lexer_type lexer_type;
138    typedef typename token_type::position_type position_type;
139    typedef boost::wave::grammars::predefined_macros_grammar_gen<lexer_type>
140        predef_macros_type;
141
142    using namespace boost::wave;
143    using namespace std;    // isspace is in std namespace for some systems
144   
145// skip leading whitespace
146std::string::iterator begin = macrostring.begin();
147std::string::iterator end = macrostring.end();
148
149    while(begin != end && isspace(*begin))
150        ++begin;
151       
152// parse the macro definition
153position_type act_pos("command line", 0);
154boost::spirit::tree_parse_info<lexer_type> hit =
155    predef_macros_type::parse_predefined_macro(
156        lexer_type(begin, end, position_type(), language), lexer_type());
157
158    if (!hit.match || (!hit.full && T_EOF != token_id(*hit.stop))) {
159        BOOST_WAVE_THROW(preprocess_exception, bad_macro_definition,
160            macrostring.c_str(), act_pos);
161    }
162   
163// retrieve the macro definition from the parse tree
164token_type macroname;
165std::vector<token_type> macroparameters;
166typename ContextT::token_sequence_type macrodefinition;
167bool has_parameters = false;
168
169    boost::wave::util::retrieve_macroname(*hit.trees.begin(),
170        predef_macros_type::rule_ids.plain_define_id, macroname, act_pos);
171    has_parameters = boost::wave::util::retrieve_macrodefinition(*hit.trees.begin(),
172        predef_macros_type::rule_ids.macro_parameters_id, macroparameters,
173        token_type());
174    boost::wave::util::retrieve_macrodefinition(*hit.trees.begin(),
175        predef_macros_type::rule_ids.macro_definition_id, macrodefinition,
176        token_type());
177
178//  If no macrodefinition is given, and the macro string does not end with a
179//  '=', then the macro should be defined with the value '1'
180    if (0 == macrodefinition.size() &&
181        '=' != macrostring[macrostring.size()-1])
182    {
183        macrodefinition.push_back(token_type(T_INTLIT, "1", act_pos));
184    }
185   
186// add the new macro to the macromap
187    return ctx.add_macro_definition(macroname, has_parameters, macroparameters,
188        macrodefinition, is_predefined);
189}
190#endif // BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0
191
192///////////////////////////////////////////////////////////////////////////////
193}   // namespace util
194
195///////////////////////////////////////////////////////////////////////////////
196//  forward declaration
197template <typename ContextT> class pp_iterator;
198
199namespace impl {
200
201///////////////////////////////////////////////////////////////////////////////
202// 
203//  pp_iterator_functor
204//
205///////////////////////////////////////////////////////////////////////////////
206template <typename ContextT>
207class pp_iterator_functor {
208
209public:
210// interface to the boost::spirit::multi_pass_policies::functor_input policy
211    typedef typename ContextT::token_type               result_type;
212
213//  eof token
214    static result_type const eof;
215
216private:
217    typedef typename ContextT::lexer_type               lexer_type;
218    typedef typename result_type::string_type           string_type;
219    typedef boost::wave::grammars::cpp_grammar_gen<lexer_type>
220        cpp_grammar_type;
221
222//  iteration context related types (an iteration context represents a current
223//  position in an included file)
224    typedef base_iteration_context<lexer_type>    base_iteration_context_type;
225    typedef
226        iteration_context<lexer_type, typename ContextT::input_policy_type>
227        iteration_context_type;
228
229// parse tree related types
230    typedef
231        boost::spirit::node_val_data_factory<boost::spirit::nil_t>
232        node_factory_t;
233    typedef
234        boost::spirit::tree_match<lexer_type, node_factory_t>
235        parse_tree_match_t;
236    typedef typename parse_tree_match_t::node_t         parse_node_type;       // tree_node<node_val_data<> >
237    typedef typename parse_tree_match_t::parse_node_t   parse_node_value_type; // node_val_data<>
238    typedef typename parse_tree_match_t::container_t    parse_tree_type;       // parse_node_type::children_t
239
240// type of a token sequence
241    typedef typename ContextT::token_sequence_type      token_sequence_type;
242   
243public:
244    template <typename IteratorT>
245    pp_iterator_functor(ContextT &ctx_, IteratorT const &first_,
246            IteratorT const &last_, typename ContextT::position_type const &pos_,
247            boost::wave::language_support language)
248    :   ctx(ctx_),
249        iter_ctx(new base_iteration_context_type(
250                lexer_type(first_, last_, pos_, language), lexer_type(),
251                pos_.get_file().c_str()
252            )),
253        seen_newline(true), must_emit_line_directive(false),
254        act_pos(ctx_.get_main_pos()), //last_line(0),
255        eater(need_preserve_comments(ctx_.get_language()))
256    {
257        act_pos.set_file(pos_.get_file());
258#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
259        ctx_.set_current_filename(pos_.get_file().c_str());
260#endif
261    }
262   
263// get the next preprocessed token
264    result_type const &operator()();
265
266// get the last recognized token (for error processing etc.)
267    result_type const &current_token() const { return act_token; }
268
269protected:
270    friend class pp_iterator<ContextT>;
271    void on_include_helper(char const *t, char const *s, bool is_system,
272        bool include_next);
273   
274protected:
275    result_type const &get_next_token();
276    result_type const &pp_token(bool consider_emitting_line_directive = false);
277
278    bool pp_directive();
279    bool dispatch_directive(boost::spirit::tree_parse_info<lexer_type> const &hit);
280
281    void on_include(string_type const &s, bool is_system, bool include_next);
282    void on_include(typename parse_tree_type::const_iterator const &begin,
283        typename parse_tree_type::const_iterator const &end, bool include_next);
284
285    void on_define(parse_node_type const &node);
286    void on_undefine(result_type const &t);
287   
288    void on_ifdef(typename parse_tree_type::const_iterator const &begin,
289        typename parse_tree_type::const_iterator const &end);
290    void on_ifndef(typename parse_tree_type::const_iterator const &begin,
291        typename parse_tree_type::const_iterator const &end);
292    void on_else();
293    void on_endif();
294    void on_illformed(typename result_type::string_type const &s);
295       
296    void on_line(typename parse_tree_type::const_iterator const &begin,
297        typename parse_tree_type::const_iterator const &end);
298    void on_if(typename parse_tree_type::const_iterator const &begin,
299        typename parse_tree_type::const_iterator const &end);
300    void on_elif(typename parse_tree_type::const_iterator const &begin,
301        typename parse_tree_type::const_iterator const &end);
302    void on_error(typename parse_tree_type::const_iterator const &begin,
303        typename parse_tree_type::const_iterator const &end);
304#if BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0
305    void on_warning(typename parse_tree_type::const_iterator const &begin,
306        typename parse_tree_type::const_iterator const &end);
307#endif
308    bool on_pragma(typename parse_tree_type::const_iterator const &begin,
309        typename parse_tree_type::const_iterator const &end);
310
311    result_type const &emit_line_directive();
312    bool returned_from_include();
313
314    bool interpret_pragma(token_sequence_type const &pragma_body,
315        token_sequence_type &result);
316
317private:
318    ContextT &ctx;              // context, this iterator is associated with
319    boost::shared_ptr<base_iteration_context_type> iter_ctx;
320   
321    bool seen_newline;              // needed for recognizing begin of line
322    bool must_emit_line_directive;  // must emit a line directive
323    result_type act_token;          // current token
324    typename result_type::position_type &act_pos;   // current fileposition (references the macromap)
325//    unsigned int last_line;         // line number of the previous token
326       
327    token_sequence_type unput_queue;     // tokens to be preprocessed again
328    token_sequence_type pending_queue;   // tokens already preprocessed
329   
330    // detect whether to insert additional whitespace in between two adjacent
331    // tokens, which otherwise would form a different token type, if
332    // retokenized
333    boost::wave::util::insert_whitespace_detection whitespace;
334   
335    // remove not needed whitespace from the output stream
336    boost::wave::util::eat_whitespace<result_type> eater;
337};
338
339///////////////////////////////////////////////////////////////////////////////
340//  eof token
341template <typename ContextT>
342typename pp_iterator_functor<ContextT>::result_type const
343    pp_iterator_functor<ContextT>::eof;
344
345///////////////////////////////////////////////////////////////////////////////
346//
347//  returned_from_include()
348//
349//      Tests if it is necessary to pop the include file context (eof inside
350//      a file was reached). If yes, it pops this context. Preprocessing will
351//      continue with the next outer file scope.
352//
353///////////////////////////////////////////////////////////////////////////////
354template <typename ContextT>
355inline bool
356pp_iterator_functor<ContextT>::returned_from_include()
357{
358    if (iter_ctx->first == iter_ctx->last && ctx.get_iteration_depth() > 0) {
359    // call the include policy trace function
360        ctx.get_trace_policy().returning_from_include_file();
361       
362    // restore the previous iteration context after finishing the preprocessing
363    // of the included file
364        BOOST_WAVE_STRINGTYPE oldfile = iter_ctx->real_filename;
365       
366        iter_ctx = ctx.pop_iteration_context();
367
368    // ensure the integrity of the #if/#endif stack
369        if (iter_ctx->if_block_depth != ctx.get_if_block_depth()) {
370            using boost::wave::util::impl::escape_lit;
371            BOOST_WAVE_THROW(preprocess_exception, unbalanced_if_endif,
372                escape_lit(oldfile).c_str(), act_pos);
373        }
374       
375        must_emit_line_directive = true;
376        seen_newline = true;
377
378    // restore current file position
379        act_pos.set_file(iter_ctx->filename);
380#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
381        ctx.set_current_filename(iter_ctx->real_filename.c_str());
382#endif
383
384//        last_line = iter_ctx->line;
385        act_pos.set_line(iter_ctx->line);
386        act_pos.set_column(0);
387       
388    // restore the actual current directory
389        ctx.set_current_directory(iter_ctx->real_filename.c_str());
390        return true;
391    }
392    return false;
393}
394
395///////////////////////////////////////////////////////////////////////////////
396//
397//  operator()(): get the next preprocessed token
398//
399//      throws a preprocess_exception, if appropriate
400//
401///////////////////////////////////////////////////////////////////////////////
402template <typename ContextT>
403inline typename pp_iterator_functor<ContextT>::result_type const &
404pp_iterator_functor<ContextT>::operator()()
405{
406    using namespace boost::wave;
407
408// loop over skipable whitespace until something significant is found
409bool skipped_newline = false;
410bool was_seen_newline = seen_newline;
411token_id id = T_ANY;
412   
413    do {
414    // get_next_token assigns result to act_token member
415        if (!seen_newline && skipped_newline)
416            seen_newline = true;
417        get_next_token();
418
419    // if comments shouldn't be preserved replace them with newlines
420        id = token_id(act_token);
421        if (!need_preserve_comments(ctx.get_language()) &&
422            (T_CPPCOMMENT == id || util::ccomment_has_newline(act_token)))
423        {
424            act_token.set_token_id(id = T_NEWLINE);
425            act_token.set_value("\n");
426        }
427       
428    } while (eater.may_skip(act_token, skipped_newline));
429   
430// if there were skipped any newlines, we must emit a #line directive
431    if ((must_emit_line_directive || (was_seen_newline && skipped_newline)) &&
432        !IS_CATEGORY(id, WhiteSpaceTokenType) &&
433        !IS_CATEGORY(id, EOLTokenType) && !IS_CATEGORY(id, EOFTokenType))
434    {
435    // must emit a #line directive
436        emit_line_directive();
437        eater.may_skip(act_token, skipped_newline);     // feed ws eater FSM
438        id = token_id(act_token);
439    }
440   
441// cleanup of certain tokens required
442    seen_newline = skipped_newline;
443    switch (static_cast<unsigned int>(id)) {
444    case T_NONREPLACABLE_IDENTIFIER:
445        act_token.set_token_id(T_IDENTIFIER);
446        break;
447       
448    case T_NEWLINE:
449    case T_CPPCOMMENT:
450        seen_newline = true;
451        ++iter_ctx->emitted_lines;
452        break;
453
454    case T_EOF:
455        seen_newline = true;
456        break;
457
458    default:
459        break;
460    }
461
462    if (whitespace.must_insert(id, act_token.get_value())) {
463    // must insert some whitespace into the output stream to avoid adjacent
464    // tokens, which would form different (and wrong) tokens
465        whitespace.shift_tokens(T_SPACE);
466        pending_queue.push_front(act_token);        // push this token back
467        return act_token = result_type(T_SPACE,
468            typename result_type::string_type(" "),
469            act_token.get_position());
470    }
471    whitespace.shift_tokens(id);
472    return act_token;
473}
474
475
476template <typename ContextT>
477inline typename pp_iterator_functor<ContextT>::result_type const &
478pp_iterator_functor<ContextT>::get_next_token()
479{
480    using namespace boost::wave;
481   
482// if there is something in the unput_queue, then return the next token from
483// there (all tokens in the queue are preprocessed already)
484    if (pending_queue.size() > 0 || unput_queue.size() > 0)
485        return pp_token();      // return next token
486   
487// test for EOF, if there is a pending input context, pop it back and continue
488// parsing with it
489bool returned_from_include_file = returned_from_include();
490   
491// try to generate the next token
492    if (iter_ctx->first != iter_ctx->last) {
493        do {
494        // If there are pending tokens in the queue, we'll have to return
495        // these. This may happen from a #pragma directive, which got replaced
496        // by some token sequence.
497            if (!pending_queue.empty()) {
498            util::on_exit::pop_front<token_sequence_type>
499                pop_front_token(pending_queue);
500
501                whitespace.shift_tokens(act_token = pending_queue.front());
502                return act_token;
503            }
504           
505        // fetch the current token       
506            act_token = *iter_ctx->first;
507
508        // adjust the current position (line and column)
509        bool was_seen_newline = seen_newline || returned_from_include_file;
510//        int current_line = act_token.get_position().get_line();
511//       
512//            act_pos.set_line(act_pos.get_line() + current_line - last_line);
513//            act_pos.set_column(act_token.get_position().get_column());
514//            last_line = current_line;
515
516            act_pos = act_token.get_position();
517//            last_line = act_pos.get_line();
518           
519        // act accordingly on the current token
520        token_id id = token_id(act_token);
521       
522            if (T_EOF == id) {
523                if (!seen_newline &&
524                    !(support_option_single_line & get_support_options(ctx.get_language())))
525                {
526                // warn, if this file does not end with a newline
527                    BOOST_WAVE_THROW(preprocess_exception,
528                        last_line_not_terminated, "", act_pos);
529                }
530               
531            // returned from an include file, continue with the next token
532                whitespace.shift_tokens(T_EOF);
533                ++iter_ctx->first;
534                continue;   // if this is the main file, the while loop breaks
535            }
536            else if (T_NEWLINE == id || T_CPPCOMMENT == id) {   
537            // a newline is to be returned ASAP, a C++ comment too
538            // (the C++ comment token includes the trailing newline)
539                seen_newline = true;
540                ++iter_ctx->first;
541                whitespace.shift_tokens(id);  // whitespace controller
542               
543                if (!ctx.get_if_block_status()) {
544                // skip this token because of the disabled #if block
545                    continue;
546                }
547                return act_token;
548            }
549            seen_newline = false;
550
551            if (was_seen_newline && pp_directive()) {
552            // a pp directive was found
553                seen_newline = true;
554                must_emit_line_directive = true;
555
556            // loop to the next token to analyze
557            // simply fall through, since the iterator was already adjusted
558            // correctly
559            }
560            else if (ctx.get_if_block_status()) {
561            // preprocess this token, eat up more, if appropriate, return
562            // the next preprocessed token
563                return pp_token(was_seen_newline);
564            }
565            else {
566            // compilation condition is false: if the current token is a
567            // newline, account for it, otherwise discard the actual token and
568            // try the next one
569                if (T_NEWLINE == act_token) {
570                    seen_newline = true;
571                    must_emit_line_directive = true;
572                }
573
574            // next token
575                ++iter_ctx->first;
576            }
577           
578        } while (iter_ctx->first != iter_ctx->last || returned_from_include());
579    }
580   
581    if (returned_from_include_file) {
582    // if there was an '#include' statement on the last line of the main file
583    // we have to return an additional newline token
584        seen_newline = true;
585       
586        whitespace.shift_tokens(T_NEWLINE);  // whitespace controller
587        return act_token = result_type(T_NEWLINE,
588            typename result_type::string_type("\n"),
589            cpp_grammar_type::pos_of_newline);
590    }
591   
592// overall eof reached
593    if (ctx.get_if_block_depth() > 0) {
594    // missing endif directive(s)
595        BOOST_WAVE_THROW(preprocess_exception, missing_matching_endif, "",
596            act_pos);
597    }
598
599    whitespace.shift_tokens(T_EOF);     // whitespace controller
600    return act_token = eof;             // return eof token
601}
602
603///////////////////////////////////////////////////////////////////////////////
604//
605//  emit_line_directive(): emits a line directive from the act_token data
606//
607///////////////////////////////////////////////////////////////////////////////
608template <typename ContextT>
609inline typename pp_iterator_functor<ContextT>::result_type const &
610pp_iterator_functor<ContextT>::emit_line_directive()
611{
612    using namespace boost::wave;
613   
614typename ContextT::position_type pos = act_token.get_position();
615
616    if (must_emit_line_directive ||
617        iter_ctx->emitted_lines != act_pos.get_line())
618    {
619    // unput the current token
620        pending_queue.push_front(act_token);
621        pos.set_line(act_pos.get_line());
622
623        if (!must_emit_line_directive &&
624            iter_ctx->emitted_lines+1 == act_pos.get_line())
625        {
626        // prefer to output a single newline instead of the #line directive
627            whitespace.shift_tokens(T_NEWLINE);
628            act_token = result_type(T_NEWLINE, "\n", pos);
629        }
630        else {
631        // account for the newline emitted here
632            act_pos.set_line(act_pos.get_line()-1);
633            iter_ctx->emitted_lines = act_pos.get_line();
634//            --last_line;
635       
636        // the #line directive has to be pushed back into the pending queue in
637        // reverse order
638
639        // unput the complete #line directive in reverse order
640        std::string file("\"");
641        boost::filesystem::path filename(act_pos.get_file().c_str(),
642            boost::filesystem::native);
643       
644            using boost::wave::util::impl::escape_lit;
645            file += escape_lit(filename.native_file_string()) + "\"";
646
647        // 21 is the max required size for a 64 bit integer represented as a
648        // string
649        char buffer[22];
650
651            using namespace std;    // for some systems sprintf is in namespace std
652            sprintf (buffer, "%d", pos.get_line());
653
654        // adjust the generated column numbers accordingly
655        // #line<space>number<space>filename<newline>
656        unsigned int filenamelen = (unsigned int)file.size();
657        unsigned int column = 7 + (unsigned int)strlen(buffer) + filenamelen;
658
659            pos.set_line(pos.get_line() - 1);         // adjust line number
660           
661            pos.set_column(column);
662            pending_queue.push_front(result_type(T_NEWLINE, "\n", pos));
663            pos.set_column(column -= filenamelen);    // account for filename
664            pending_queue.push_front(result_type(T_STRINGLIT, file.c_str(), pos));
665            pos.set_column(--column);                 // account for ' '
666            pending_queue.push_front(result_type(T_SPACE, " ", pos));
667            pos.set_column(column -= (unsigned int)strlen(buffer)); // account for <number>
668            pending_queue.push_front(result_type(T_INTLIT, buffer, pos));
669            pos.set_column(--column);                 // account for ' '
670            pending_queue.push_front(result_type(T_SPACE, " ", pos));
671           
672        // return the #line token itself
673            whitespace.shift_tokens(T_PP_LINE);
674            pos.set_column(1);
675            act_token = result_type(T_PP_LINE, "#line", pos);
676        }
677    }
678
679// we are now in sync
680    must_emit_line_directive = false;
681    return act_token;
682}
683
684///////////////////////////////////////////////////////////////////////////////
685//
686//  pptoken(): return the next preprocessed token
687//
688///////////////////////////////////////////////////////////////////////////////
689template <typename ContextT>
690inline typename pp_iterator_functor<ContextT>::result_type const &
691pp_iterator_functor<ContextT>::pp_token(bool consider_emitting_line_directive)
692{
693    using namespace boost::wave;
694
695token_id id = token_id(*iter_ctx->first);
696
697    // eat all T_PLACEHOLDER tokens, eventually slipped through out of the
698    // macro engine
699    do {
700        if (!pending_queue.empty()) {
701        // if there are pending tokens in the queue, return the first one
702            act_token = pending_queue.front();
703            pending_queue.pop_front();
704        }
705        else if (!unput_queue.empty()
706            || T_IDENTIFIER == id
707            || IS_CATEGORY(id, KeywordTokenType)
708            || IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType))
709        {
710        //  call the lexer, preprocess the required number of tokens, put them
711        //  into the unput queue
712            act_token = ctx.expand_tokensequence(iter_ctx->first,
713                iter_ctx->last, pending_queue, unput_queue);
714        }
715        else {
716        // simply return the next token
717            act_token = *iter_ctx->first;
718            ++iter_ctx->first;
719        }
720        id = token_id(act_token);
721       
722    } while (T_PLACEHOLDER == id);
723
724    return act_token;
725}
726
727///////////////////////////////////////////////////////////////////////////////
728//
729//  pp_directive(): recognize a preprocessor directive
730//
731///////////////////////////////////////////////////////////////////////////////
732namespace {
733
734    template <typename IteratorT>
735    bool next_token_is_pp_directive(IteratorT &it, IteratorT const &end)
736    {
737        using namespace boost::wave;
738       
739        token_id id = T_ANY;
740        for (/**/; it != end; ++it) {
741            id = token_id(*it);
742            if (!IS_CATEGORY(id, WhiteSpaceTokenType))
743                break;          // skip leading whitespace
744            if (IS_CATEGORY(id, EOLTokenType))
745                break;          // do not enter a new line
746        }
747        BOOST_ASSERT(it == end || id != T_ANY);
748        return it != end && IS_CATEGORY(id, PPTokenType);
749    }
750   
751    template <typename IteratorT>
752    bool is_pp_null(IteratorT &it, IteratorT const &end)
753    {
754        using namespace boost::wave;
755       
756        BOOST_ASSERT(T_POUND == BASE_TOKEN(token_id(*it)));
757        for (++it; it != end; ++it) {
758        token_id id = token_id(*it);
759       
760            if (T_CPPCOMMENT == id || T_NEWLINE == id) {
761                ++it;           // skip eol/C++ comment
762                return true;    // found pp_null
763            }
764
765            if (!IS_CATEGORY(id, WhiteSpaceTokenType))
766                break;
767        }
768        return false;
769    }
770
771    template <typename IteratorT>
772    bool skip_to_eol(IteratorT &it, IteratorT const &end)
773    {
774        using namespace boost::wave;
775       
776        for (/**/; it != end; ++it) {
777        token_id id = token_id(*it);
778       
779            if (T_CPPCOMMENT == id || T_NEWLINE == id) {
780                ++it;           // skip eol/C++ comment
781                return true;    // found pp_null
782            }
783        }
784        return false;
785    }
786}
787
788template <typename ContextT>
789inline bool
790pp_iterator_functor<ContextT>::pp_directive()
791{
792    using namespace cpplexer;
793   
794// test, if the next non-whitespace token is a pp directive
795lexer_type it = iter_ctx->first;
796
797    if (!next_token_is_pp_directive(it, iter_ctx->last)) {
798    // eventually skip null pp directive (no need to do it via the parser)
799        if (it != iter_ctx->last && T_POUND == BASE_TOKEN(token_id(*it))) {
800            if (is_pp_null(it, iter_ctx->last)) {
801                seen_newline = true;
802                iter_ctx->first = it;   // start over with the next line
803                return true;
804            }
805            else {
806                on_illformed((*it).get_value());
807            }
808        }
809       
810    // this line does not contain a pp directive, so simply return
811        return false;
812    }
813   
814    if (it == iter_ctx->last)
815        return false;
816
817// ignore all pp directives not related to conditional compilation while
818// if block status is false
819    if (!ctx.get_if_block_status() &&
820        !IS_EXTCATEGORY(*it, PPConditionalTokenType))
821    {
822        seen_newline = true;
823        skip_to_eol(it, iter_ctx->last);
824        iter_ctx->first = it;       // start over with the next line
825        return true;
826    }
827
828// found a pp directive, so try to identify it, start with the pp_token
829bool found_eof = false;
830boost::spirit::tree_parse_info<lexer_type> hit =
831    cpp_grammar_type::parse_cpp_grammar(it, iter_ctx->last, found_eof, act_pos);
832
833    if (hit.match) {
834    // position the iterator past the matched sequence to allow
835    // resynchronisation, if an error occurs
836        iter_ctx->first = hit.stop;
837       
838    // found a valid pp directive, dispatch to the correct function to handle
839    // the found pp directive
840    bool result = dispatch_directive (hit);
841   
842        if (found_eof) {
843        // The line was terminated with an end of file token.
844        // So trigger a warning, that the last line was not terminated with a
845        // newline.
846            BOOST_WAVE_THROW(preprocess_exception, last_line_not_terminated, "",
847                act_pos);
848        }
849        return result;
850    }
851    return false;
852}
853
854///////////////////////////////////////////////////////////////////////////////
855//
856//  dispatch_directive(): dispatch a recognized preprocessor directive
857//
858///////////////////////////////////////////////////////////////////////////////
859template <typename ContextT>
860inline bool
861pp_iterator_functor<ContextT>::dispatch_directive(
862    boost::spirit::tree_parse_info<lexer_type> const &hit)
863{
864    using namespace cpplexer;
865    using namespace boost::spirit;
866   
867    typedef typename parse_tree_type::const_iterator const_child_iterator_t;
868   
869// this iterator points to the root node of the parse tree
870const_child_iterator_t begin = hit.trees.begin();
871
872// decide, which preprocessor directive was found
873parse_tree_type const &root = (*begin).children;
874parse_node_value_type const &nodeval = get_first_leaf(*root.begin()).value;
875//long node_id = nodeval.id().to_long();
876
877const_child_iterator_t begin_child_it = (*root.begin()).children.begin();
878const_child_iterator_t end_child_it = (*root.begin()).children.end();
879
880token_id id = cpp_grammar_type::found_directive;
881
882    switch (static_cast<unsigned int>(id)) {
883    case T_PP_QHEADER:      // #include "..."
884#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
885    case T_PP_QHEADER_NEXT: // #include_next "..."
886#endif
887        on_include ((*nodeval.begin()).get_value(), false,
888            T_PP_QHEADER_NEXT == id);
889        break;
890
891    case T_PP_HHEADER:      // #include <...>
892#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
893    case T_PP_HHEADER_NEXT: // #include_next <...>
894#endif
895        on_include ((*nodeval.begin()).get_value(), true,
896            T_PP_HHEADER_NEXT == id);
897        break;
898   
899    case T_PP_INCLUDE:      // #include ...
900#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
901    case T_PP_INCLUDE_NEXT: // #include_next ...
902#endif
903        on_include (begin_child_it, end_child_it, T_PP_INCLUDE_NEXT == id);
904        break;
905
906    case T_PP_DEFINE:       // #define
907        on_define (*begin);
908        break;
909
910    case T_PP_UNDEF:        // #undef
911        on_undefine(*nodeval.begin());
912        break;
913
914    case T_PP_IFDEF:        // #ifdef
915        on_ifdef(begin_child_it, end_child_it);
916        break;
917
918    case T_PP_IFNDEF:       // #ifndef
919        on_ifndef(begin_child_it, end_child_it);
920        break;
921
922    case T_PP_IF:           // #if
923        on_if(begin_child_it, end_child_it);
924        break;
925
926    case T_PP_ELIF:         // #elif
927        on_elif(begin_child_it, end_child_it);
928        break;
929
930    case T_PP_ELSE:         // #else
931        on_else();
932        break;
933
934    case T_PP_ENDIF:        // #endif
935        on_endif();
936        break;
937
938    case T_PP_LINE:         // #line
939        on_line(begin_child_it, end_child_it);
940        break;
941       
942    case T_PP_ERROR:        // #error
943        on_error(begin_child_it, end_child_it);
944        break;
945
946#if BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0
947    case T_PP_WARNING:      // #warning
948        on_warning(begin_child_it, end_child_it);
949        break;
950#endif
951
952    case T_PP_PRAGMA:       // #pragma
953        return on_pragma(begin_child_it, end_child_it);
954
955#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
956    case T_MSEXT_PP_REGION:
957    case T_MSEXT_PP_ENDREGION:
958        break;              // ignore these
959#endif
960
961    default:                // #something else
962        on_illformed((*nodeval.begin()).get_value());
963        break;
964    }
965    return true;    // return newline only
966}
967
968///////////////////////////////////////////////////////////////////////////////
969//
970//  on_include: handle #include <...> or #include "..." directives
971//
972///////////////////////////////////////////////////////////////////////////////
973template <typename ContextT>
974inline void 
975pp_iterator_functor<ContextT>::on_include (string_type const &s,
976    bool is_system, bool include_next)
977{
978    BOOST_ASSERT(ctx.get_if_block_status());
979
980// strip quotes first, extract filename
981typename string_type::size_type pos_end = s.find_last_of(is_system ? '>' : '\"');
982
983    if (string_type::npos == pos_end) {
984        BOOST_WAVE_THROW(preprocess_exception, bad_include_statement,
985            s.c_str(), act_pos);
986    }
987
988typename string_type::size_type pos_begin =
989    s.find_last_of(is_system ? '<' : '\"', pos_end-1);
990
991    if (string_type::npos == pos_begin) {
992        BOOST_WAVE_THROW(preprocess_exception, bad_include_statement,
993            s.c_str(), act_pos);
994    }
995
996std::string file_token(s.substr(pos_begin, pos_end-pos_begin+1).c_str());
997std::string file_path(s.substr(pos_begin+1, pos_end-pos_begin-1).c_str());
998
999// finally include the file
1000    on_include_helper(file_token.c_str(), file_path.c_str(), is_system,
1001        include_next);
1002}
1003       
1004template <typename ContextT>
1005inline void 
1006pp_iterator_functor<ContextT>::on_include_helper (char const *f, char const *s,
1007    bool is_system, bool include_next)
1008{
1009    namespace fs = boost::filesystem;
1010
1011// try to locate the given file, searching through the include path lists
1012std::string file_path(s);
1013std::string dir_path;
1014#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
1015char const *current_name = include_next ? iter_ctx->real_filename.c_str() : 0;
1016#else
1017char const *current_name = 0;   // never try to match current file name
1018#endif
1019
1020// call the include policy trace function
1021    ctx.get_trace_policy().found_include_directive(f, include_next);
1022
1023    if (!ctx.find_include_file (file_path, dir_path, is_system, current_name)) {
1024        BOOST_WAVE_THROW(preprocess_exception, bad_include_file,
1025            file_path.c_str(), act_pos);
1026    }
1027
1028fs::path native_path(file_path, fs::native);
1029
1030    if (!fs::exists(native_path)) {
1031        BOOST_WAVE_THROW(preprocess_exception, bad_include_file,
1032            file_path.c_str(), act_pos);
1033    }
1034
1035// test, if this file is known through a #pragma once directive
1036#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1037    if (!ctx.has_pragma_once(native_path.native_file_string()))
1038#endif
1039    {
1040    // the new include file determines the actual current directory
1041        ctx.set_current_directory(native_path.native_file_string().c_str());
1042       
1043    // preprocess the opened file
1044    boost::shared_ptr<base_iteration_context_type> new_iter_ctx (
1045        new iteration_context_type(native_path.native_file_string().c_str(),
1046            act_pos, ctx.get_language()));
1047
1048    // call the include policy trace function
1049        ctx.get_trace_policy().opened_include_file(dir_path, file_path,
1050            ctx.get_iteration_depth(), is_system);
1051
1052    // store current file position
1053        iter_ctx->filename = act_pos.get_file();
1054        iter_ctx->line = act_pos.get_line();
1055        iter_ctx->if_block_depth = ctx.get_if_block_depth();
1056       
1057    // push the old iteration context onto the stack and continue with the new
1058        ctx.push_iteration_context(act_pos, iter_ctx);
1059        iter_ctx = new_iter_ctx;
1060        seen_newline = true;        // fake a newline to trigger pp_directive
1061        must_emit_line_directive = true;
1062       
1063        act_pos.set_file(iter_ctx->filename);  // initialize file position
1064#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
1065        ctx.set_current_filename(iter_ctx->real_filename.c_str());
1066#endif
1067
1068//        last_line = iter_ctx->line;
1069        act_pos.set_line(iter_ctx->line);
1070        act_pos.set_column(0);
1071    }
1072}
1073
1074///////////////////////////////////////////////////////////////////////////////
1075// 
1076//  on_include(): handle #include ... directives
1077//
1078///////////////////////////////////////////////////////////////////////////////
1079
1080namespace {
1081
1082    // trim all whitespace from the beginning and the end of the given string
1083    template <typename StringT>
1084    inline StringT
1085    trim_whitespace(StringT const &s)
1086    {
1087        typedef typename StringT::size_type size_type;
1088       
1089        size_type first = s.find_first_not_of(" \t\v\f");
1090        if (StringT::npos == first)
1091            return StringT();
1092        size_type last = s.find_last_not_of(" \t\v\f");
1093        return s.substr(first, last-first+1);
1094    }
1095}
1096
1097template <typename ContextT>
1098inline void 
1099pp_iterator_functor<ContextT>::on_include(
1100    typename parse_tree_type::const_iterator const &begin,
1101    typename parse_tree_type::const_iterator const &end, bool include_next)
1102{
1103    BOOST_ASSERT(ctx.get_if_block_status());
1104
1105// preprocess the given token sequence (the body of the #include directive)
1106get_token_value<result_type, parse_node_type> get_value;
1107token_sequence_type expanded;
1108token_sequence_type toexpand;
1109
1110    std::copy(make_ref_transform_iterator(begin, get_value),
1111        make_ref_transform_iterator(end, get_value),
1112        std::inserter(toexpand, toexpand.end()));
1113
1114    typename token_sequence_type::iterator begin2 = toexpand.begin();
1115    ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded,
1116        false);
1117
1118// now, include the file
1119string_type s (trim_whitespace(boost::wave::util::impl::as_string(expanded)));
1120bool is_system = '<' == s[0] && '>' == s[s.size()-1];
1121
1122    if (!is_system && !('\"' == s[0] && '\"' == s[s.size()-1])) {
1123    // should resolve into something like <...> or "..."
1124        BOOST_WAVE_THROW(preprocess_exception, bad_include_statement,
1125            s.c_str(), act_pos);
1126    }
1127    on_include(s, is_system, include_next);
1128}
1129
1130///////////////////////////////////////////////////////////////////////////////
1131// 
1132//  on_define(): handle #define directives
1133//
1134///////////////////////////////////////////////////////////////////////////////
1135
1136template <typename ContextT>
1137inline void 
1138pp_iterator_functor<ContextT>::on_define (parse_node_type const &node)
1139{
1140    BOOST_ASSERT(ctx.get_if_block_status());
1141
1142// retrieve the macro definition from the parse tree
1143result_type macroname;
1144std::vector<result_type> macroparameters;
1145token_sequence_type macrodefinition;
1146bool has_parameters = false;
1147
1148    boost::wave::util::retrieve_macroname(node,
1149        cpp_grammar_type::rule_ids.plain_define_id, macroname,
1150        act_token.get_position());
1151    has_parameters = boost::wave::util::retrieve_macrodefinition(node,
1152        cpp_grammar_type::rule_ids.macro_parameters_id, macroparameters, act_token);
1153    boost::wave::util::retrieve_macrodefinition(node,
1154        cpp_grammar_type::rule_ids.macro_definition_id, macrodefinition, act_token);
1155
1156    if (has_parameters) {
1157#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
1158        if (boost::wave::need_variadics(ctx.get_language())) {
1159        // test whether ellipsis are given, and if yes, if these are placed as the
1160        // last argument
1161            using namespace cpplexer;
1162            typedef typename std::vector<result_type>::iterator
1163                parameter_iterator_t;
1164           
1165            bool seen_ellipses = false;
1166            parameter_iterator_t end = macroparameters.end();
1167            for (parameter_iterator_t pit = macroparameters.begin();
1168                pit != end; ++pit)
1169            {
1170                if (seen_ellipses) {
1171                // ellipses are not the last given formal argument
1172                    BOOST_WAVE_THROW(preprocess_exception, bad_define_statement,
1173                        macroname.get_value().c_str(), (*pit).get_position());
1174                }
1175                if (T_ELLIPSIS == token_id(*pit))
1176                    seen_ellipses = true;
1177            }
1178           
1179        // if there wasn't an ellipsis, then there shouldn't be a __VA_ARGS__
1180        // placeholder in the definition too [C99 Standard 6.10.3.5]
1181            if (!seen_ellipses) {
1182                typedef typename token_sequence_type::iterator definition_iterator_t;
1183
1184                bool seen_va_args = false;
1185                definition_iterator_t pend = macrodefinition.end();
1186                for (definition_iterator_t dit = macrodefinition.begin();
1187                     dit != pend; ++dit)
1188                {
1189                    if (T_IDENTIFIER == token_id(*dit) &&
1190                        "__VA_ARGS__" == (*dit).get_value())
1191                    {
1192                        seen_va_args = true;
1193                    }
1194                }
1195                if (seen_va_args) {
1196                // must not have seen __VA_ARGS__ placeholder
1197                    BOOST_WAVE_THROW(preprocess_exception, bad_define_statement,
1198                        macroname.get_value().c_str(), act_token.get_position());
1199                }
1200            }
1201        }
1202        else
1203#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
1204        {
1205        // test, that there is no T_ELLIPSES given
1206            using namespace cpplexer;
1207            typedef typename std::vector<result_type>::iterator
1208                parameter_iterator_t;
1209           
1210            parameter_iterator_t end = macroparameters.end();
1211            for (parameter_iterator_t pit = macroparameters.begin();
1212                pit != end; ++pit)
1213            {
1214                if (T_ELLIPSIS == token_id(*pit)) {
1215                // if variadics are disabled, no ellipses should be given
1216                    BOOST_WAVE_THROW(preprocess_exception, bad_define_statement,
1217                        macroname.get_value().c_str(), (*pit).get_position());
1218                }
1219            }
1220        }
1221    }
1222   
1223// add the new macro to the macromap
1224    ctx.add_macro_definition(macroname, has_parameters, macroparameters,
1225        macrodefinition);
1226}
1227
1228///////////////////////////////////////////////////////////////////////////////
1229// 
1230//  on_undefine(): handle #undef directives
1231//
1232///////////////////////////////////////////////////////////////////////////////
1233template <typename ContextT>
1234inline void 
1235pp_iterator_functor<ContextT>::on_undefine (result_type const &token)
1236{
1237    BOOST_ASSERT(ctx.get_if_block_status());
1238
1239// retrieve the macro name to undefine from the parse tree
1240    ctx.remove_macro_definition(token.get_value()); // throws for predefined macros
1241}
1242
1243///////////////////////////////////////////////////////////////////////////////
1244// 
1245//  on_ifdef(): handle #ifdef directives
1246//
1247///////////////////////////////////////////////////////////////////////////////
1248template <typename ContextT>
1249inline void 
1250pp_iterator_functor<ContextT>::on_ifdef(
1251    typename parse_tree_type::const_iterator const &begin,
1252    typename parse_tree_type::const_iterator const &end)
1253{
1254get_token_value<result_type, parse_node_type> get_value;
1255bool is_defined = ctx.is_defined_macro(
1256        make_ref_transform_iterator((*begin).children.begin(), get_value),
1257        make_ref_transform_iterator((*begin).children.end(), get_value));
1258
1259    ctx.enter_if_block(is_defined);
1260}
1261
1262///////////////////////////////////////////////////////////////////////////////
1263// 
1264//  on_ifndef(): handle #ifndef directives
1265//
1266///////////////////////////////////////////////////////////////////////////////
1267template <typename ContextT>
1268inline void 
1269pp_iterator_functor<ContextT>::on_ifndef(
1270    typename parse_tree_type::const_iterator const &begin,
1271    typename parse_tree_type::const_iterator const &end)
1272{
1273get_token_value<result_type, parse_node_type> get_value;
1274bool is_defined = ctx.is_defined_macro(
1275        make_ref_transform_iterator((*begin).children.begin(), get_value),
1276        make_ref_transform_iterator((*begin).children.end(), get_value));
1277
1278    ctx.enter_if_block(!is_defined);
1279}
1280
1281///////////////////////////////////////////////////////////////////////////////
1282// 
1283//  on_else(): handle #else directives
1284//
1285///////////////////////////////////////////////////////////////////////////////
1286template <typename ContextT>
1287inline void 
1288pp_iterator_functor<ContextT>::on_else()
1289{
1290    if (!ctx.enter_else_block()) {
1291    // #else without matching #if
1292        BOOST_WAVE_THROW(preprocess_exception, missing_matching_if, "#else",
1293            act_pos);
1294    }
1295}
1296
1297///////////////////////////////////////////////////////////////////////////////
1298// 
1299//  on_endif(): handle #endif directives
1300//
1301///////////////////////////////////////////////////////////////////////////////
1302template <typename ContextT>
1303inline void 
1304pp_iterator_functor<ContextT>::on_endif()
1305{
1306    if (!ctx.exit_if_block()) {
1307    // #endif without matching #if
1308        BOOST_WAVE_THROW(preprocess_exception, missing_matching_if, "#endif",
1309            act_pos);
1310    }
1311}
1312
1313///////////////////////////////////////////////////////////////////////////////
1314// 
1315//  on_if(): handle #if directives
1316//
1317///////////////////////////////////////////////////////////////////////////////
1318template <typename ContextT>
1319inline void 
1320pp_iterator_functor<ContextT>::on_if(
1321    typename parse_tree_type::const_iterator const &begin,
1322    typename parse_tree_type::const_iterator const &end)
1323{
1324// preprocess the given sequence into the provided list
1325get_token_value<result_type, parse_node_type> get_value;
1326token_sequence_type expanded;
1327token_sequence_type toexpand;
1328
1329    std::copy(make_ref_transform_iterator(begin, get_value),
1330        make_ref_transform_iterator(end, get_value),
1331        std::inserter(toexpand, toexpand.end()));
1332
1333    typename token_sequence_type::iterator begin2 = toexpand.begin();
1334    ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded);
1335
1336// replace all remaining (== undefined) identifiers with an integer literal '0'
1337    typename token_sequence_type::iterator exp_end = expanded.end();
1338    for (typename token_sequence_type::iterator exp_it = expanded.begin();
1339         exp_it != exp_end; ++exp_it)
1340    {
1341        using namespace boost::wave;
1342       
1343        token_id id = token_id(*exp_it);
1344        if (IS_CATEGORY(id, IdentifierTokenType) ||
1345            IS_CATEGORY(id, KeywordTokenType))
1346        {
1347            (*exp_it).set_token_id(T_INTLIT);
1348            (*exp_it).set_value("0");
1349        }
1350    }
1351   
1352#if BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS != 0
1353    {
1354        string_type outstr(boost::wave::util::impl::as_string(toexpand));
1355        outstr += "(" + boost::wave::util::impl::as_string(expanded) + ")";
1356        BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS_OUT << "#if " << outstr
1357            << std::endl;
1358    }
1359#endif
1360
1361// parse the expression and enter the #if block
1362    ctx.enter_if_block(grammars::expression_grammar_gen<result_type>::
1363            evaluate(expanded.begin(), expanded.end(), act_pos,
1364                ctx.get_if_block_status()));
1365}
1366
1367///////////////////////////////////////////////////////////////////////////////
1368// 
1369//  on_elif(): handle #elif directives
1370//
1371///////////////////////////////////////////////////////////////////////////////
1372template <typename ContextT>
1373inline void 
1374pp_iterator_functor<ContextT>::on_elif(
1375    typename parse_tree_type::const_iterator const &begin,
1376    typename parse_tree_type::const_iterator const &end)
1377{
1378    if (ctx.get_if_block_status()) {
1379        if (!ctx.enter_elif_block(false)) {
1380        // #else without matching #if
1381            BOOST_WAVE_THROW(preprocess_exception, missing_matching_if, "#elif",
1382                act_pos);
1383        }
1384        return;     // #if/previous #elif was true, so don't enter this #elif
1385    }
1386           
1387// preprocess the given sequence into the provided list
1388get_token_value<result_type, parse_node_type> get_value;
1389token_sequence_type expanded;
1390token_sequence_type toexpand;
1391
1392    std::copy(make_ref_transform_iterator(begin, get_value),
1393        make_ref_transform_iterator(end, get_value),
1394        std::inserter(toexpand, toexpand.end()));
1395
1396    typename token_sequence_type::iterator begin2 = toexpand.begin();
1397    ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded);
1398   
1399// replace all remaining (== undefined) identifiers with an integer literal '0'
1400    typename token_sequence_type::iterator exp_end = expanded.end();
1401    for (typename token_sequence_type::iterator exp_it = expanded.begin();
1402         exp_it != exp_end; ++exp_it)
1403    {
1404        using namespace boost::wave;
1405       
1406        token_id id = token_id(*exp_it);
1407        if (IS_CATEGORY(id, IdentifierTokenType) ||
1408            IS_CATEGORY(id, KeywordTokenType))
1409        {
1410            (*exp_it).set_token_id(T_INTLIT);
1411            (*exp_it).set_value("0");
1412        }
1413    }
1414
1415#if BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS != 0
1416    {
1417        string_type outstr(boost::wave::util::impl::as_string(toexpand));
1418        outstr += "(" + boost::wave::util::impl::as_string(expanded) + ")";
1419        BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS_OUT << "#elif " << outstr << std::endl;
1420    }
1421#endif
1422
1423// parse the expression and enter the #elif block
1424    ctx.enter_elif_block(grammars::expression_grammar_gen<result_type>::
1425            evaluate(expanded.begin(), expanded.end(), act_pos,
1426                ctx.get_if_block_status()));
1427}
1428
1429///////////////////////////////////////////////////////////////////////////////
1430// 
1431//  on_illformed(): handles the illegal directive
1432//
1433///////////////////////////////////////////////////////////////////////////////
1434template <typename ContextT>
1435inline void 
1436pp_iterator_functor<ContextT>::on_illformed(
1437    typename result_type::string_type const &s)
1438{
1439    BOOST_ASSERT(ctx.get_if_block_status());
1440    BOOST_WAVE_THROW(preprocess_exception, ill_formed_directive, s.c_str(),
1441        act_pos);
1442}
1443
1444///////////////////////////////////////////////////////////////////////////////
1445// 
1446//  on_line(): handle #line directives
1447//
1448///////////////////////////////////////////////////////////////////////////////
1449
1450namespace {
1451
1452    template <typename IteratorT, typename StringT>
1453    bool retrieve_line_info (IteratorT first, IteratorT const &last,
1454        int &line, StringT &file)
1455    {
1456        using namespace boost::wave;
1457        if (T_INTLIT == token_id(*first)) {
1458        // extract line number
1459            using namespace std;    // some systems have atoi in namespace std
1460            line = atoi((*first).get_value().c_str());
1461           
1462        // extract file name (if it is given)
1463            while (++first != last && IS_CATEGORY(*first, WhiteSpaceTokenType))
1464                /**/;   // skip whitespace
1465               
1466            if (first != last) {
1467                if (T_STRINGLIT != token_id(*first))
1468                    return false;
1469
1470            StringT const &file_lit = (*first).get_value();
1471           
1472                if ('L' == file_lit[0])
1473                    return false;       // shouldn't be a wide character string
1474                   
1475                file = file_lit.substr(1, file_lit.size()-2);
1476            }
1477            return true;
1478        }
1479        return false;
1480    }
1481}
1482
1483template <typename ContextT>
1484inline void 
1485pp_iterator_functor<ContextT>::on_line(
1486    typename parse_tree_type::const_iterator const &begin,
1487    typename parse_tree_type::const_iterator const &end)
1488{
1489    BOOST_ASSERT(ctx.get_if_block_status());
1490
1491// Try to extract the line number and file name from the given token list
1492// directly. If that fails, preprocess the whole token sequence and try again
1493// to extract this information.
1494token_sequence_type expanded;
1495get_token_value<result_type, parse_node_type> get_value;
1496
1497    typedef typename ref_transform_iterator_generator<
1498            get_token_value<result_type, parse_node_type>,
1499            typename parse_tree_type::const_iterator
1500        >::type const_tree_iterator_t;
1501       
1502const_tree_iterator_t first = make_ref_transform_iterator(begin, get_value);
1503const_tree_iterator_t last = make_ref_transform_iterator(end, get_value);
1504   
1505// try to interpret the #line body as a number followed by an optional
1506// string literal
1507int line = 0;
1508string_type file_name;
1509
1510    if (!retrieve_line_info(first, last, line, file_name)) {
1511    // preprocess the body of this #line message
1512    token_sequence_type toexpand;
1513
1514        std::copy(first, make_ref_transform_iterator(end, get_value),
1515            std::inserter(toexpand, toexpand.end()));
1516
1517        typename token_sequence_type::iterator begin2 = toexpand.begin();
1518        ctx.expand_whole_tokensequence(begin2, toexpand.end(),
1519            expanded, false);
1520           
1521        if (!retrieve_line_info(expanded.begin(), expanded.end(), line,
1522            file_name))
1523        {
1524            BOOST_WAVE_THROW(preprocess_exception, bad_line_statement,
1525                boost::wave::util::impl::as_string(expanded).c_str(), act_pos)
1526        }
1527    }
1528   
1529// the queues should be empty at this point
1530    BOOST_ASSERT(unput_queue.empty());
1531    BOOST_ASSERT(pending_queue.empty());
1532
1533    if (!file_name.empty())     // reuse current file name
1534        act_pos.set_file(file_name.c_str());
1535    act_pos.set_line(line);
1536//    last_line = act_token.get_position().get_line();
1537   
1538//typename result_type::position_type nextline_pos = act_pos;
1539//   
1540//    nextline_pos.set_line(nextline_pos.get_line() + 1);
1541    iter_ctx->first.set_position(act_pos);
1542    must_emit_line_directive = true;
1543}
1544
1545///////////////////////////////////////////////////////////////////////////////
1546// 
1547//  on_error(): handle #error directives
1548//
1549///////////////////////////////////////////////////////////////////////////////
1550template <typename ContextT>
1551inline void
1552pp_iterator_functor<ContextT>::on_error(
1553    typename parse_tree_type::const_iterator const &begin,
1554    typename parse_tree_type::const_iterator const &end)
1555{
1556    BOOST_ASSERT(ctx.get_if_block_status());
1557
1558// preprocess the given sequence into the provided list
1559token_sequence_type expanded;
1560get_token_value<result_type, parse_node_type> get_value;
1561
1562typename ref_transform_iterator_generator<
1563        get_token_value<result_type, parse_node_type>,
1564        typename parse_tree_type::const_iterator
1565    >::type first = make_ref_transform_iterator(begin, get_value);
1566   
1567#if BOOST_WAVE_PREPROCESS_ERROR_MESSAGE_BODY != 0
1568// preprocess the body of this #error message
1569token_sequence_type toexpand;
1570
1571    std::copy(first, make_ref_transform_iterator(end, get_value),
1572        std::inserter(toexpand, toexpand.end()));
1573
1574    typename token_sequence_type::iterator begin2 = toexpand.begin();
1575    ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded,
1576        false);
1577#else
1578// simply copy the body of this #error message to the issued diagnostic
1579// message
1580    std::copy(first, make_ref_transform_iterator(end, get_value),
1581        std::inserter(expanded, expanded.end()));
1582#endif
1583
1584// report the corresponding error
1585    BOOST_WAVE_THROW(preprocess_exception, error_directive,
1586        boost::wave::util::impl::as_string(expanded).c_str(), act_pos);
1587}
1588
1589#if BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0
1590///////////////////////////////////////////////////////////////////////////////
1591// 
1592//  on_warning(): handle #warning directives
1593//
1594///////////////////////////////////////////////////////////////////////////////
1595template <typename ContextT>
1596inline void
1597pp_iterator_functor<ContextT>::on_warning(
1598    typename parse_tree_type::const_iterator const &begin,
1599    typename parse_tree_type::const_iterator const &end)
1600{
1601    BOOST_ASSERT(ctx.get_if_block_status());
1602
1603// preprocess the given sequence into the provided list
1604token_sequence_type expanded;
1605get_token_value<result_type, parse_node_type> get_value;
1606
1607typename ref_transform_iterator_generator<
1608        get_token_value<result_type, parse_node_type>,
1609        typename parse_tree_type::const_iterator
1610    >::type first = make_ref_transform_iterator(begin, get_value);
1611   
1612#if BOOST_WAVE_PREPROCESS_ERROR_MESSAGE_BODY != 0
1613// preprocess the body of this #warning message
1614token_sequence_type toexpand;
1615
1616    std::copy(first, make_ref_transform_iterator(end, get_value),
1617        std::inserter(toexpand, toexpand.end()));
1618
1619    typename token_sequence_type::iterator begin2 = toexpand.begin();
1620    ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded,
1621        false);
1622#else
1623// simply copy the body of this #warning message to the issued diagnostic
1624// message
1625    std::copy(first, make_ref_transform_iterator(end, get_value),
1626        std::inserter(expanded, expanded.end()));
1627#endif
1628
1629// report the corresponding error
1630    BOOST_WAVE_THROW(preprocess_exception, warning_directive,
1631        boost::wave::util::impl::as_string(expanded).c_str(), act_pos);
1632}
1633#endif // BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0
1634
1635///////////////////////////////////////////////////////////////////////////////
1636// 
1637//  on_pragma(): handle #pragma directives
1638//
1639///////////////////////////////////////////////////////////////////////////////
1640template <typename ContextT>
1641inline bool
1642pp_iterator_functor<ContextT>::on_pragma(
1643    typename parse_tree_type::const_iterator const &begin,
1644    typename parse_tree_type::const_iterator const &end)
1645{
1646    using namespace boost::wave;
1647   
1648    BOOST_ASSERT(ctx.get_if_block_status());
1649
1650// Look at the pragma token sequence and decide, if the first token is STDC
1651// (see C99 standard [6.10.6.2]), if it is, the sequence must _not_ be
1652// preprocessed.
1653token_sequence_type expanded;
1654get_token_value<result_type, parse_node_type> get_value;
1655
1656    typedef typename ref_transform_iterator_generator<
1657            get_token_value<result_type, parse_node_type>,
1658            typename parse_tree_type::const_iterator
1659        >::type const_tree_iterator_t;
1660       
1661const_tree_iterator_t first = make_ref_transform_iterator(begin, get_value);
1662const_tree_iterator_t last = make_ref_transform_iterator(end, get_value);
1663
1664    expanded.push_back(result_type(T_PP_PRAGMA, "#pragma", act_token.get_position()));
1665    expanded.push_back(result_type(T_SPACE, " ", act_token.get_position()));
1666       
1667    while (++first != last && IS_CATEGORY(*first, WhiteSpaceTokenType))
1668        expanded.push_back(*first);   // skip whitespace
1669
1670    if (first != last) {
1671        if (T_IDENTIFIER == token_id(*first) &&
1672            boost::wave::need_c99(ctx.get_language()) &&
1673            (*first).get_value() == "STDC")
1674        {
1675        // do _not_ preprocess the token sequence
1676            std::copy(first, last, std::inserter(expanded, expanded.end()));
1677        }
1678        else {
1679#if BOOST_WAVE_PREPROCESS_PRAGMA_BODY != 0
1680        // preprocess the given tokensequence
1681        token_sequence_type toexpand;
1682
1683            std::copy(first, last, std::inserter(toexpand, toexpand.end()));
1684
1685            typename token_sequence_type::iterator begin2 = toexpand.begin();
1686            ctx.expand_whole_tokensequence(begin2, toexpand.end(),
1687                expanded, false);
1688#else
1689        // do _not_ preprocess the token sequence
1690            std::copy(first, last, std::inserter(expanded, expanded.end()));
1691#endif
1692        }
1693    }
1694    expanded.push_back(result_type(T_NEWLINE, "\n", act_token.get_position()));
1695       
1696// the queues should be empty at this point
1697    BOOST_ASSERT(unput_queue.empty());
1698    BOOST_ASSERT(pending_queue.empty());
1699
1700// try to interpret the expanded #pragma body
1701    token_sequence_type pending;
1702    if (interpret_pragma(expanded, pending)) {
1703    // if there is some replacement text, insert it into the pending queue
1704        if (pending.size() > 0)
1705            pending_queue.splice(pending_queue.begin(), pending);
1706        return true;        // this #pragma was successfully recognized
1707    }
1708   
1709#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
1710// Move the resulting token sequence into the pending_queue, so it will be
1711// returned to the caller.
1712    pending_queue.splice(pending_queue.begin(), expanded);
1713    return false;           // return the whole #pragma directive
1714#else
1715    return true;            // skip the #pragma at all
1716#endif
1717}
1718
1719template <typename ContextT>
1720inline bool
1721pp_iterator_functor<ContextT>::interpret_pragma(
1722    token_sequence_type const &pragma_body, token_sequence_type &result)
1723{
1724    using namespace cpplexer;
1725   
1726    typename token_sequence_type::const_iterator end = pragma_body.end();
1727    typename token_sequence_type::const_iterator it = pragma_body.begin();
1728    for (++it; it != end && IS_CATEGORY(*it, WhiteSpaceTokenType); ++it)
1729        /**/;   // skip whitespace
1730   
1731    if (it == end)      // eof reached
1732        return false;
1733
1734    return boost::wave::util::interpret_pragma(ctx, act_token, it, end, result);
1735}
1736
1737///////////////////////////////////////////////////////////////////////////////
1738}   // namespace impl
1739
1740///////////////////////////////////////////////////////////////////////////////
1741// 
1742//  pp_iterator
1743//
1744//      The boost::wave::pp_iterator template is the iterator, through which
1745//      the resulting preprocessed input stream is accessible.
1746// 
1747///////////////////////////////////////////////////////////////////////////////
1748
1749template <typename ContextT>
1750class pp_iterator
1751:   public boost::spirit::multi_pass<
1752        boost::wave::impl::pp_iterator_functor<ContextT>,
1753        boost::wave::util::functor_input
1754    >
1755{
1756public:
1757    typedef boost::wave::impl::pp_iterator_functor<ContextT> input_policy_type;
1758
1759private:
1760    typedef
1761        boost::spirit::multi_pass<input_policy_type, boost::wave::util::functor_input>
1762        base_type;
1763    typedef pp_iterator<ContextT> self_type;
1764    typedef boost::wave::util::functor_input functor_input_type;
1765   
1766public:
1767    pp_iterator()
1768    {}
1769   
1770    template <typename IteratorT>
1771    pp_iterator(ContextT &ctx, IteratorT const &first, IteratorT const &last,
1772        typename ContextT::position_type const &pos,
1773        boost::wave::language_support language)
1774    :   base_type(input_policy_type(ctx, first, last, pos, language))
1775    {}
1776   
1777    void force_include(char const *path_, bool is_last)
1778    {
1779        this->get_functor().on_include_helper(path_, path_, false, false);
1780        if (is_last) {
1781            this->functor_input_type::
1782                template inner<input_policy_type>::advance_input();
1783        }
1784    }
1785};
1786
1787///////////////////////////////////////////////////////////////////////////////
1788}   // namespace wave
1789}   // namespace boost
1790
1791#endif // !defined(CPP_ITERATOR_HPP_175CA88F_7273_43FA_9039_BCF7459E1F29_INCLUDED)
Note: See TracBrowser for help on using the repository browser.