Home | History | Annotate | Line # | Download | only in text
      1  1.1  jmmv // Copyright 2012 Google Inc.
      2  1.1  jmmv // All rights reserved.
      3  1.1  jmmv //
      4  1.1  jmmv // Redistribution and use in source and binary forms, with or without
      5  1.1  jmmv // modification, are permitted provided that the following conditions are
      6  1.1  jmmv // met:
      7  1.1  jmmv //
      8  1.1  jmmv // * Redistributions of source code must retain the above copyright
      9  1.1  jmmv //   notice, this list of conditions and the following disclaimer.
     10  1.1  jmmv // * Redistributions in binary form must reproduce the above copyright
     11  1.1  jmmv //   notice, this list of conditions and the following disclaimer in the
     12  1.1  jmmv //   documentation and/or other materials provided with the distribution.
     13  1.1  jmmv // * Neither the name of Google Inc. nor the names of its contributors
     14  1.1  jmmv //   may be used to endorse or promote products derived from this software
     15  1.1  jmmv //   without specific prior written permission.
     16  1.1  jmmv //
     17  1.1  jmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18  1.1  jmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19  1.1  jmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20  1.1  jmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21  1.1  jmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22  1.1  jmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23  1.1  jmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  1.1  jmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  1.1  jmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  1.1  jmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27  1.1  jmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  1.1  jmmv 
     29  1.1  jmmv #include "utils/text/templates.hpp"
     30  1.1  jmmv 
     31  1.1  jmmv #include <algorithm>
     32  1.1  jmmv #include <fstream>
     33  1.1  jmmv #include <sstream>
     34  1.1  jmmv #include <stack>
     35  1.1  jmmv 
     36  1.1  jmmv #include "utils/format/macros.hpp"
     37  1.1  jmmv #include "utils/noncopyable.hpp"
     38  1.1  jmmv #include "utils/sanity.hpp"
     39  1.1  jmmv #include "utils/text/exceptions.hpp"
     40  1.1  jmmv #include "utils/text/operations.ipp"
     41  1.1  jmmv 
     42  1.1  jmmv namespace text = utils::text;
     43  1.1  jmmv 
     44  1.1  jmmv 
     45  1.1  jmmv namespace {
     46  1.1  jmmv 
     47  1.1  jmmv 
     48  1.1  jmmv /// Definition of a template statement.
     49  1.1  jmmv ///
     50  1.1  jmmv /// A template statement is a particular line in the input file that is
     51  1.1  jmmv /// preceeded by a template marker.  This class provides a high-level
     52  1.1  jmmv /// representation of the contents of such statement and a mechanism to parse
     53  1.1  jmmv /// the textual line into this high-level representation.
     54  1.1  jmmv class statement_def {
     55  1.1  jmmv public:
     56  1.1  jmmv     /// Types of the known statements.
     57  1.1  jmmv     enum statement_type {
     58  1.1  jmmv         /// Alternative clause of a conditional.
     59  1.1  jmmv         ///
     60  1.1  jmmv         /// Takes no arguments.
     61  1.1  jmmv         type_else,
     62  1.1  jmmv 
     63  1.1  jmmv         /// End of conditional marker.
     64  1.1  jmmv         ///
     65  1.1  jmmv         /// Takes no arguments.
     66  1.1  jmmv         type_endif,
     67  1.1  jmmv 
     68  1.1  jmmv         /// End of loop marker.
     69  1.1  jmmv         ///
     70  1.1  jmmv         /// Takes no arguments.
     71  1.1  jmmv         type_endloop,
     72  1.1  jmmv 
     73  1.1  jmmv         /// Beginning of a conditional.
     74  1.1  jmmv         ///
     75  1.1  jmmv         /// Takes a single argument, which denotes the name of the variable or
     76  1.1  jmmv         /// vector to check for existence.  This is the only expression
     77  1.1  jmmv         /// supported.
     78  1.1  jmmv         type_if,
     79  1.1  jmmv 
     80  1.1  jmmv         /// Beginning of a loop over all the elements of a vector.
     81  1.1  jmmv         ///
     82  1.1  jmmv         /// Takes two arguments: the name of the vector over which to iterate
     83  1.1  jmmv         /// and the name of the iterator to later index this vector.
     84  1.1  jmmv         type_loop,
     85  1.1  jmmv     };
     86  1.1  jmmv 
     87  1.1  jmmv private:
     88  1.1  jmmv     /// Internal data describing the structure of a particular statement type.
     89  1.1  jmmv     struct type_descriptor {
     90  1.1  jmmv         /// The native type of the statement.
     91  1.1  jmmv         statement_type type;
     92  1.1  jmmv 
     93  1.1  jmmv         /// The expected number of arguments.
     94  1.1  jmmv         unsigned int n_arguments;
     95  1.1  jmmv 
     96  1.1  jmmv         /// Constructs a new type descriptor.
     97  1.1  jmmv         ///
     98  1.1  jmmv         /// \param type_ The native type of the statement.
     99  1.1  jmmv         /// \param n_arguments_ The expected number of arguments.
    100  1.1  jmmv         type_descriptor(const statement_type type_,
    101  1.1  jmmv                         const unsigned int n_arguments_)
    102  1.1  jmmv             : type(type_), n_arguments(n_arguments_)
    103  1.1  jmmv         {
    104  1.1  jmmv         }
    105  1.1  jmmv     };
    106  1.1  jmmv 
    107  1.1  jmmv     /// Mapping of statement type names to their definitions.
    108  1.1  jmmv     typedef std::map< std::string, type_descriptor > types_map;
    109  1.1  jmmv 
    110  1.1  jmmv     /// Description of the different statement types.
    111  1.1  jmmv     ///
    112  1.1  jmmv     /// This static map is initialized once and reused later for any statement
    113  1.1  jmmv     /// lookup.  Unfortunately, we cannot perform this initialization in a
    114  1.1  jmmv     /// static manner without C++11.
    115  1.1  jmmv     static types_map _types;
    116  1.1  jmmv 
    117  1.1  jmmv     /// Generates a new types definition map.
    118  1.1  jmmv     ///
    119  1.1  jmmv     /// \return A new types definition map, to be assigned to _types.
    120  1.1  jmmv     static types_map
    121  1.1  jmmv     generate_types_map(void)
    122  1.1  jmmv     {
    123  1.1  jmmv         // If you change this, please edit the comments in the enum above.
    124  1.1  jmmv         types_map types;
    125  1.1  jmmv         types.insert(types_map::value_type(
    126  1.1  jmmv             "else", type_descriptor(type_else, 0)));
    127  1.1  jmmv         types.insert(types_map::value_type(
    128  1.1  jmmv             "endif", type_descriptor(type_endif, 0)));
    129  1.1  jmmv         types.insert(types_map::value_type(
    130  1.1  jmmv             "endloop", type_descriptor(type_endloop, 0)));
    131  1.1  jmmv         types.insert(types_map::value_type(
    132  1.1  jmmv             "if", type_descriptor(type_if, 1)));
    133  1.1  jmmv         types.insert(types_map::value_type(
    134  1.1  jmmv             "loop", type_descriptor(type_loop, 2)));
    135  1.1  jmmv         return types;
    136  1.1  jmmv     }
    137  1.1  jmmv 
    138  1.1  jmmv public:
    139  1.1  jmmv     /// The type of the statement.
    140  1.1  jmmv     statement_type type;
    141  1.1  jmmv 
    142  1.1  jmmv     /// The arguments to the statement, in textual form.
    143  1.1  jmmv     const std::vector< std::string > arguments;
    144  1.1  jmmv 
    145  1.1  jmmv     /// Creates a new statement.
    146  1.1  jmmv     ///
    147  1.1  jmmv     /// \param type_ The type of the statement.
    148  1.1  jmmv     /// \param arguments_ The arguments to the statement.
    149  1.1  jmmv     statement_def(const statement_type& type_,
    150  1.1  jmmv                   const std::vector< std::string >& arguments_) :
    151  1.1  jmmv         type(type_), arguments(arguments_)
    152  1.1  jmmv     {
    153  1.1  jmmv #if !defined(NDEBUG)
    154  1.1  jmmv         for (types_map::const_iterator iter = _types.begin();
    155  1.1  jmmv              iter != _types.end(); ++iter) {
    156  1.1  jmmv             const type_descriptor& descriptor = (*iter).second;
    157  1.1  jmmv             if (descriptor.type == type_) {
    158  1.1  jmmv                 PRE(descriptor.n_arguments == arguments_.size());
    159  1.1  jmmv                 return;
    160  1.1  jmmv             }
    161  1.1  jmmv         }
    162  1.1  jmmv         UNREACHABLE;
    163  1.1  jmmv #endif
    164  1.1  jmmv     }
    165  1.1  jmmv 
    166  1.1  jmmv     /// Parses a statement.
    167  1.1  jmmv     ///
    168  1.1  jmmv     /// \param line The textual representation of the statement without any
    169  1.1  jmmv     ///     prefix.
    170  1.1  jmmv     ///
    171  1.1  jmmv     /// \return The parsed statement.
    172  1.1  jmmv     ///
    173  1.1  jmmv     /// \throw text::syntax_error If the statement is not correctly defined.
    174  1.1  jmmv     static statement_def
    175  1.1  jmmv     parse(const std::string& line)
    176  1.1  jmmv     {
    177  1.1  jmmv         if (_types.empty())
    178  1.1  jmmv             _types = generate_types_map();
    179  1.1  jmmv 
    180  1.1  jmmv         const std::vector< std::string > words = text::split(line, ' ');
    181  1.1  jmmv         if (words.empty())
    182  1.1  jmmv             throw text::syntax_error("Empty statement");
    183  1.1  jmmv 
    184  1.1  jmmv         const types_map::const_iterator iter = _types.find(words[0]);
    185  1.1  jmmv         if (iter == _types.end())
    186  1.1  jmmv             throw text::syntax_error(F("Unknown statement '%s'") % words[0]);
    187  1.1  jmmv         const type_descriptor& descriptor = (*iter).second;
    188  1.1  jmmv 
    189  1.1  jmmv         if (words.size() - 1 != descriptor.n_arguments)
    190  1.1  jmmv             throw text::syntax_error(F("Invalid number of arguments for "
    191  1.1  jmmv                                        "statement '%s'") % words[0]);
    192  1.1  jmmv 
    193  1.1  jmmv         std::vector< std::string > new_arguments;
    194  1.1  jmmv         new_arguments.resize(words.size() - 1);
    195  1.1  jmmv         std::copy(words.begin() + 1, words.end(), new_arguments.begin());
    196  1.1  jmmv 
    197  1.1  jmmv         return statement_def(descriptor.type, new_arguments);
    198  1.1  jmmv     }
    199  1.1  jmmv };
    200  1.1  jmmv 
    201  1.1  jmmv 
    202  1.1  jmmv statement_def::types_map statement_def::_types;
    203  1.1  jmmv 
    204  1.1  jmmv 
    205  1.1  jmmv /// Definition of a loop.
    206  1.1  jmmv ///
    207  1.1  jmmv /// This simple structure is used to keep track of the parameters of a loop.
    208  1.1  jmmv struct loop_def {
    209  1.1  jmmv     /// The name of the vector over which this loop is iterating.
    210  1.1  jmmv     std::string vector;
    211  1.1  jmmv 
    212  1.1  jmmv     /// The name of the iterator defined by this loop.
    213  1.1  jmmv     std::string iterator;
    214  1.1  jmmv 
    215  1.1  jmmv     /// Position in the input to which to rewind to on looping.
    216  1.1  jmmv     ///
    217  1.1  jmmv     /// This position points to the line after the loop statement, not the loop
    218  1.1  jmmv     /// itself.  This is one of the reasons why we have this structure, so that
    219  1.1  jmmv     /// we can maintain the data about the loop without having to re-process it.
    220  1.1  jmmv     std::istream::pos_type position;
    221  1.1  jmmv 
    222  1.1  jmmv     /// Constructs a new loop definition.
    223  1.1  jmmv     ///
    224  1.1  jmmv     /// \param vector_ The name of the vector (first argument).
    225  1.1  jmmv     /// \param iterator_ The name of the iterator (second argumnet).
    226  1.1  jmmv     /// \param position_ Position of the next line after the loop statement.
    227  1.1  jmmv     loop_def(const std::string& vector_, const std::string& iterator_,
    228  1.1  jmmv              const std::istream::pos_type position_) :
    229  1.1  jmmv         vector(vector_), iterator(iterator_), position(position_)
    230  1.1  jmmv     {
    231  1.1  jmmv     }
    232  1.1  jmmv };
    233  1.1  jmmv 
    234  1.1  jmmv 
    235  1.1  jmmv /// Stateful class to instantiate the templates in an input stream.
    236  1.1  jmmv ///
    237  1.1  jmmv /// The goal of this parser is to scan the input once and not buffer anything in
    238  1.1  jmmv /// memory.  The only exception are loops: loops are reinterpreted on every
    239  1.1  jmmv /// iteration from the same input file by rewidining the stream to the
    240  1.1  jmmv /// appropriate position.
    241  1.1  jmmv class templates_parser : utils::noncopyable {
    242  1.1  jmmv     /// The templates to apply.
    243  1.1  jmmv     ///
    244  1.1  jmmv     /// Note that this is not const because the parser has to have write access
    245  1.1  jmmv     /// to the templates.  In particular, it needs to be able to define the
    246  1.1  jmmv     /// iterators as regular variables.
    247  1.1  jmmv     text::templates_def _templates;
    248  1.1  jmmv 
    249  1.1  jmmv     /// Prefix that marks a line as a statement.
    250  1.1  jmmv     const std::string _prefix;
    251  1.1  jmmv 
    252  1.1  jmmv     /// Delimiter to surround an expression instantiation.
    253  1.1  jmmv     const std::string _delimiter;
    254  1.1  jmmv 
    255  1.1  jmmv     /// Whether to skip incoming lines or not.
    256  1.1  jmmv     ///
    257  1.1  jmmv     /// The top of the stack is true whenever we encounter a conditional that
    258  1.1  jmmv     /// evaluates to false or a loop that does not have any iterations left.
    259  1.1  jmmv     /// Under these circumstances, we need to continue scanning the input stream
    260  1.1  jmmv     /// until we find the matching closing endif or endloop construct.
    261  1.1  jmmv     ///
    262  1.1  jmmv     /// This is a stack rather than a plain boolean to allow us deal with
    263  1.1  jmmv     /// if-else clauses.
    264  1.1  jmmv     std::stack< bool > _skip;
    265  1.1  jmmv 
    266  1.1  jmmv     /// Current count of nested conditionals.
    267  1.1  jmmv     unsigned int _if_level;
    268  1.1  jmmv 
    269  1.1  jmmv     /// Level of the top-most conditional that evaluated to false.
    270  1.1  jmmv     unsigned int _exit_if_level;
    271  1.1  jmmv 
    272  1.1  jmmv     /// Current count of nested loops.
    273  1.1  jmmv     unsigned int _loop_level;
    274  1.1  jmmv 
    275  1.1  jmmv     /// Level of the top-most loop that does not have any iterations left.
    276  1.1  jmmv     unsigned int _exit_loop_level;
    277  1.1  jmmv 
    278  1.1  jmmv     /// Information about all the nested loops up to the current point.
    279  1.1  jmmv     std::stack< loop_def > _loops;
    280  1.1  jmmv 
    281  1.1  jmmv     /// Checks if a line is a statement or not.
    282  1.1  jmmv     ///
    283  1.1  jmmv     /// \param line The line to validate.
    284  1.1  jmmv     ///
    285  1.1  jmmv     /// \return True if the line looks like a statement, which is determined by
    286  1.1  jmmv     /// checking if the line starts by the predefined prefix.
    287  1.1  jmmv     bool
    288  1.1  jmmv     is_statement(const std::string& line)
    289  1.1  jmmv     {
    290  1.1  jmmv         return ((line.length() >= _prefix.length() &&
    291  1.1  jmmv                  line.substr(0, _prefix.length()) == _prefix) &&
    292  1.1  jmmv                 (line.length() < _delimiter.length() ||
    293  1.1  jmmv                  line.substr(0, _delimiter.length()) != _delimiter));
    294  1.1  jmmv     }
    295  1.1  jmmv 
    296  1.1  jmmv     /// Parses a given statement line into a statement definition.
    297  1.1  jmmv     ///
    298  1.1  jmmv     /// \param line The line to validate; it must be a valid statement.
    299  1.1  jmmv     ///
    300  1.1  jmmv     /// \return The parsed statement.
    301  1.1  jmmv     ///
    302  1.1  jmmv     /// \throw text::syntax_error If the input is not a valid statement.
    303  1.1  jmmv     statement_def
    304  1.1  jmmv     parse_statement(const std::string& line)
    305  1.1  jmmv     {
    306  1.1  jmmv         PRE(is_statement(line));
    307  1.1  jmmv         return statement_def::parse(line.substr(_prefix.length()));
    308  1.1  jmmv     }
    309  1.1  jmmv 
    310  1.1  jmmv     /// Processes a line from the input when not in skip mode.
    311  1.1  jmmv     ///
    312  1.1  jmmv     /// \param line The line to be processed.
    313  1.1  jmmv     /// \param input The input stream from which the line was read.  The current
    314  1.1  jmmv     ///     position in the stream must be after the line being processed.
    315  1.1  jmmv     /// \param output The output stream into which to write the results.
    316  1.1  jmmv     ///
    317  1.1  jmmv     /// \throw text::syntax_error If the input is not valid.
    318  1.1  jmmv     void
    319  1.1  jmmv     handle_normal(const std::string& line, std::istream& input,
    320  1.1  jmmv                   std::ostream& output)
    321  1.1  jmmv     {
    322  1.1  jmmv         if (!is_statement(line)) {
    323  1.1  jmmv             // Fast path.  Mostly to avoid an indentation level for the big
    324  1.1  jmmv             // chunk of code below.
    325  1.1  jmmv             output << line << '\n';
    326  1.1  jmmv             return;
    327  1.1  jmmv         }
    328  1.1  jmmv 
    329  1.1  jmmv         const statement_def statement = parse_statement(line);
    330  1.1  jmmv 
    331  1.1  jmmv         switch (statement.type) {
    332  1.1  jmmv         case statement_def::type_else:
    333  1.1  jmmv             _skip.top() = !_skip.top();
    334  1.1  jmmv             break;
    335  1.1  jmmv 
    336  1.1  jmmv         case statement_def::type_endif:
    337  1.1  jmmv             _if_level--;
    338  1.1  jmmv             break;
    339  1.1  jmmv 
    340  1.1  jmmv         case statement_def::type_endloop: {
    341  1.1  jmmv             PRE(_loops.size() == _loop_level);
    342  1.1  jmmv             loop_def& loop = _loops.top();
    343  1.1  jmmv 
    344  1.1  jmmv             const std::size_t next_index = 1 + text::to_type< std::size_t >(
    345  1.1  jmmv                 _templates.get_variable(loop.iterator));
    346  1.1  jmmv 
    347  1.1  jmmv             if (next_index < _templates.get_vector(loop.vector).size()) {
    348  1.1  jmmv                 _templates.add_variable(loop.iterator, F("%s") % next_index);
    349  1.1  jmmv                 input.seekg(loop.position);
    350  1.1  jmmv             } else {
    351  1.1  jmmv                 _loop_level--;
    352  1.1  jmmv                 _loops.pop();
    353  1.1  jmmv                 _templates.remove_variable(loop.iterator);
    354  1.1  jmmv             }
    355  1.1  jmmv         } break;
    356  1.1  jmmv 
    357  1.1  jmmv         case statement_def::type_if: {
    358  1.1  jmmv             _if_level++;
    359  1.1  jmmv             const std::string value = _templates.evaluate(
    360  1.1  jmmv                 statement.arguments[0]);
    361  1.1  jmmv             if (value.empty() || value == "0" || value == "false") {
    362  1.1  jmmv                 _exit_if_level = _if_level;
    363  1.1  jmmv                 _skip.push(true);
    364  1.1  jmmv             } else {
    365  1.1  jmmv                 _skip.push(false);
    366  1.1  jmmv             }
    367  1.1  jmmv         } break;
    368  1.1  jmmv 
    369  1.1  jmmv         case statement_def::type_loop: {
    370  1.1  jmmv             _loop_level++;
    371  1.1  jmmv 
    372  1.1  jmmv             const loop_def loop(statement.arguments[0], statement.arguments[1],
    373  1.1  jmmv                                 input.tellg());
    374  1.1  jmmv             if (_templates.get_vector(loop.vector).empty()) {
    375  1.1  jmmv                 _exit_loop_level = _loop_level;
    376  1.1  jmmv                 _skip.push(true);
    377  1.1  jmmv             } else {
    378  1.1  jmmv                 _templates.add_variable(loop.iterator, "0");
    379  1.1  jmmv                 _loops.push(loop);
    380  1.1  jmmv                 _skip.push(false);
    381  1.1  jmmv             }
    382  1.1  jmmv         } break;
    383  1.1  jmmv         }
    384  1.1  jmmv     }
    385  1.1  jmmv 
    386  1.1  jmmv     /// Processes a line from the input when in skip mode.
    387  1.1  jmmv     ///
    388  1.1  jmmv     /// \param line The line to be processed.
    389  1.1  jmmv     ///
    390  1.1  jmmv     /// \throw text::syntax_error If the input is not valid.
    391  1.1  jmmv     void
    392  1.1  jmmv     handle_skip(const std::string& line)
    393  1.1  jmmv     {
    394  1.1  jmmv         PRE(_skip.top());
    395  1.1  jmmv 
    396  1.1  jmmv         if (!is_statement(line))
    397  1.1  jmmv             return;
    398  1.1  jmmv 
    399  1.1  jmmv         const statement_def statement = parse_statement(line);
    400  1.1  jmmv         switch (statement.type) {
    401  1.1  jmmv         case statement_def::type_else:
    402  1.1  jmmv             if (_exit_if_level == _if_level)
    403  1.1  jmmv                 _skip.top() = !_skip.top();
    404  1.1  jmmv             break;
    405  1.1  jmmv 
    406  1.1  jmmv         case statement_def::type_endif:
    407  1.1  jmmv             INV(_if_level >= _exit_if_level);
    408  1.1  jmmv             if (_if_level == _exit_if_level)
    409  1.1  jmmv                 _skip.top() = false;
    410  1.1  jmmv             _if_level--;
    411  1.1  jmmv             _skip.pop();
    412  1.1  jmmv             break;
    413  1.1  jmmv 
    414  1.1  jmmv         case statement_def::type_endloop:
    415  1.1  jmmv             INV(_loop_level >= _exit_loop_level);
    416  1.1  jmmv             if (_loop_level == _exit_loop_level)
    417  1.1  jmmv                 _skip.top() = false;
    418  1.1  jmmv             _loop_level--;
    419  1.1  jmmv             _skip.pop();
    420  1.1  jmmv             break;
    421  1.1  jmmv 
    422  1.1  jmmv         case statement_def::type_if:
    423  1.1  jmmv             _if_level++;
    424  1.1  jmmv             _skip.push(true);
    425  1.1  jmmv             break;
    426  1.1  jmmv 
    427  1.1  jmmv         case statement_def::type_loop:
    428  1.1  jmmv             _loop_level++;
    429  1.1  jmmv             _skip.push(true);
    430  1.1  jmmv             break;
    431  1.1  jmmv 
    432  1.1  jmmv         default:
    433  1.1  jmmv             break;
    434  1.1  jmmv         }
    435  1.1  jmmv     }
    436  1.1  jmmv 
    437  1.1  jmmv     /// Evaluates expressions on a given input line.
    438  1.1  jmmv     ///
    439  1.1  jmmv     /// An expression is surrounded by _delimiter on both sides.  We scan the
    440  1.1  jmmv     /// string from left to right finding any expressions that may appear, yank
    441  1.1  jmmv     /// them out and call templates_def::evaluate() to get their value.
    442  1.1  jmmv     ///
    443  1.1  jmmv     /// Lonely or unbalanced appearances of _delimiter on the input line are
    444  1.1  jmmv     /// not considered an error, given that the user may actually want to supply
    445  1.1  jmmv     /// that character sequence without being interpreted as a template.
    446  1.1  jmmv     ///
    447  1.1  jmmv     /// \param in_line The input line from which to evaluate expressions.
    448  1.1  jmmv     ///
    449  1.1  jmmv     /// \return The evaluated line.
    450  1.1  jmmv     ///
    451  1.1  jmmv     /// \throw text::syntax_error If the expressions in the line are malformed.
    452  1.1  jmmv     std::string
    453  1.1  jmmv     evaluate(const std::string& in_line)
    454  1.1  jmmv     {
    455  1.1  jmmv         std::string out_line;
    456  1.1  jmmv 
    457  1.1  jmmv         std::string::size_type last_pos = 0;
    458  1.1  jmmv         while (last_pos != std::string::npos) {
    459  1.1  jmmv             const std::string::size_type open_pos = in_line.find(
    460  1.1  jmmv                 _delimiter, last_pos);
    461  1.1  jmmv             if (open_pos == std::string::npos) {
    462  1.1  jmmv                 out_line += in_line.substr(last_pos);
    463  1.1  jmmv                 last_pos = std::string::npos;
    464  1.1  jmmv             } else {
    465  1.1  jmmv                 const std::string::size_type close_pos = in_line.find(
    466  1.1  jmmv                     _delimiter, open_pos + _delimiter.length());
    467  1.1  jmmv                 if (close_pos == std::string::npos) {
    468  1.1  jmmv                     out_line += in_line.substr(last_pos);
    469  1.1  jmmv                     last_pos = std::string::npos;
    470  1.1  jmmv                 } else {
    471  1.1  jmmv                     out_line += in_line.substr(last_pos, open_pos - last_pos);
    472  1.1  jmmv                     out_line += _templates.evaluate(in_line.substr(
    473  1.1  jmmv                         open_pos + _delimiter.length(),
    474  1.1  jmmv                         close_pos - open_pos - _delimiter.length()));
    475  1.1  jmmv                     last_pos = close_pos + _delimiter.length();
    476  1.1  jmmv                 }
    477  1.1  jmmv             }
    478  1.1  jmmv         }
    479  1.1  jmmv 
    480  1.1  jmmv         return out_line;
    481  1.1  jmmv     }
    482  1.1  jmmv 
    483  1.1  jmmv public:
    484  1.1  jmmv     /// Constructs a new template parser.
    485  1.1  jmmv     ///
    486  1.1  jmmv     /// \param templates_ The templates to apply to the processed file.
    487  1.1  jmmv     /// \param prefix_ The prefix that identifies lines as statements.
    488  1.1  jmmv     /// \param delimiter_ Delimiter to surround a variable instantiation.
    489  1.1  jmmv     templates_parser(const text::templates_def& templates_,
    490  1.1  jmmv                      const std::string& prefix_,
    491  1.1  jmmv                      const std::string& delimiter_) :
    492  1.1  jmmv         _templates(templates_),
    493  1.1  jmmv         _prefix(prefix_),
    494  1.1  jmmv         _delimiter(delimiter_),
    495  1.1  jmmv         _if_level(0),
    496  1.1  jmmv         _exit_if_level(0),
    497  1.1  jmmv         _loop_level(0),
    498  1.1  jmmv         _exit_loop_level(0)
    499  1.1  jmmv     {
    500  1.1  jmmv     }
    501  1.1  jmmv 
    502  1.1  jmmv     /// Applies the templates to a given input.
    503  1.1  jmmv     ///
    504  1.1  jmmv     /// \param input The stream to which to apply the templates.
    505  1.1  jmmv     /// \param output The stream into which to write the results.
    506  1.1  jmmv     ///
    507  1.1  jmmv     /// \throw text::syntax_error If the input is not valid.  Note that the
    508  1.1  jmmv     ///     is not guaranteed to be unmodified on exit if an error is
    509  1.1  jmmv     ///     encountered.
    510  1.1  jmmv     void
    511  1.1  jmmv     instantiate(std::istream& input, std::ostream& output)
    512  1.1  jmmv     {
    513  1.1  jmmv         std::string line;
    514  1.1  jmmv         while (std::getline(input, line).good()) {
    515  1.1  jmmv             if (!_skip.empty() && _skip.top())
    516  1.1  jmmv                 handle_skip(line);
    517  1.1  jmmv             else
    518  1.1  jmmv                 handle_normal(evaluate(line), input, output);
    519  1.1  jmmv         }
    520  1.1  jmmv     }
    521  1.1  jmmv };
    522  1.1  jmmv 
    523  1.1  jmmv 
    524  1.1  jmmv }  // anonymous namespace
    525  1.1  jmmv 
    526  1.1  jmmv 
    527  1.1  jmmv /// Constructs an empty templates definition.
    528  1.1  jmmv text::templates_def::templates_def(void)
    529  1.1  jmmv {
    530  1.1  jmmv }
    531  1.1  jmmv 
    532  1.1  jmmv 
    533  1.1  jmmv /// Sets a string variable in the templates.
    534  1.1  jmmv ///
    535  1.1  jmmv /// If the variable already exists, its value is replaced.  This behavior is
    536  1.1  jmmv /// required to implement iterators, but client code should really not be
    537  1.1  jmmv /// redefining variables.
    538  1.1  jmmv ///
    539  1.1  jmmv /// \pre The variable must not already exist as a vector.
    540  1.1  jmmv ///
    541  1.1  jmmv /// \param name The name of the variable to set.
    542  1.1  jmmv /// \param value The value to set the given variable to.
    543  1.1  jmmv void
    544  1.1  jmmv text::templates_def::add_variable(const std::string& name,
    545  1.1  jmmv                                   const std::string& value)
    546  1.1  jmmv {
    547  1.1  jmmv     PRE(_vectors.find(name) == _vectors.end());
    548  1.1  jmmv     _variables[name] = value;
    549  1.1  jmmv }
    550  1.1  jmmv 
    551  1.1  jmmv 
    552  1.1  jmmv /// Unsets a string variable from the templates.
    553  1.1  jmmv ///
    554  1.1  jmmv /// Client code has no reason to use this.  This is only required to implement
    555  1.1  jmmv /// proper scoping of loop iterators.
    556  1.1  jmmv ///
    557  1.1  jmmv /// \pre The variable must exist.
    558  1.1  jmmv ///
    559  1.1  jmmv /// \param name The name of the variable to remove from the templates.
    560  1.1  jmmv void
    561  1.1  jmmv text::templates_def::remove_variable(const std::string& name)
    562  1.1  jmmv {
    563  1.1  jmmv     PRE(_variables.find(name) != _variables.end());
    564  1.1  jmmv     _variables.erase(_variables.find(name));
    565  1.1  jmmv }
    566  1.1  jmmv 
    567  1.1  jmmv 
    568  1.1  jmmv /// Creates a new vector in the templates.
    569  1.1  jmmv ///
    570  1.1  jmmv /// If the vector already exists, it is cleared.  Client code should really not
    571  1.1  jmmv /// be redefining variables.
    572  1.1  jmmv ///
    573  1.1  jmmv /// \pre The vector must not already exist as a variable.
    574  1.1  jmmv ///
    575  1.1  jmmv /// \param name The name of the vector to set.
    576  1.1  jmmv void
    577  1.1  jmmv text::templates_def::add_vector(const std::string& name)
    578  1.1  jmmv {
    579  1.1  jmmv     PRE(_variables.find(name) == _variables.end());
    580  1.1  jmmv     _vectors[name] = strings_vector();
    581  1.1  jmmv }
    582  1.1  jmmv 
    583  1.1  jmmv 
    584  1.1  jmmv /// Adds a value to an existing vector in the templates.
    585  1.1  jmmv ///
    586  1.1  jmmv /// \pre name The vector must exist.
    587  1.1  jmmv ///
    588  1.1  jmmv /// \param name The name of the vector to append the value to.
    589  1.1  jmmv /// \param value The textual value to append to the vector.
    590  1.1  jmmv void
    591  1.1  jmmv text::templates_def::add_to_vector(const std::string& name,
    592  1.1  jmmv                                    const std::string& value)
    593  1.1  jmmv {
    594  1.1  jmmv     PRE(_variables.find(name) == _variables.end());
    595  1.1  jmmv     PRE(_vectors.find(name) != _vectors.end());
    596  1.1  jmmv     _vectors[name].push_back(value);
    597  1.1  jmmv }
    598  1.1  jmmv 
    599  1.1  jmmv 
    600  1.1  jmmv /// Checks whether a given identifier exists as a variable or a vector.
    601  1.1  jmmv ///
    602  1.1  jmmv /// This is used to implement the evaluation of conditions in if clauses.
    603  1.1  jmmv ///
    604  1.1  jmmv /// \param name The name of the variable or vector.
    605  1.1  jmmv ///
    606  1.1  jmmv /// \return True if the given name exists as a variable or a vector; false
    607  1.1  jmmv /// otherwise.
    608  1.1  jmmv bool
    609  1.1  jmmv text::templates_def::exists(const std::string& name) const
    610  1.1  jmmv {
    611  1.1  jmmv     return (_variables.find(name) != _variables.end() ||
    612  1.1  jmmv             _vectors.find(name) != _vectors.end());
    613  1.1  jmmv }
    614  1.1  jmmv 
    615  1.1  jmmv 
    616  1.1  jmmv /// Gets the value of a variable.
    617  1.1  jmmv ///
    618  1.1  jmmv /// \param name The name of the variable.
    619  1.1  jmmv ///
    620  1.1  jmmv /// \return The value of the requested variable.
    621  1.1  jmmv ///
    622  1.1  jmmv /// \throw text::syntax_error If the variable does not exist.
    623  1.1  jmmv const std::string&
    624  1.1  jmmv text::templates_def::get_variable(const std::string& name) const
    625  1.1  jmmv {
    626  1.1  jmmv     const variables_map::const_iterator iter = _variables.find(name);
    627  1.1  jmmv     if (iter == _variables.end())
    628  1.1  jmmv         throw text::syntax_error(F("Unknown variable '%s'") % name);
    629  1.1  jmmv     return (*iter).second;
    630  1.1  jmmv }
    631  1.1  jmmv 
    632  1.1  jmmv 
    633  1.1  jmmv /// Gets a vector.
    634  1.1  jmmv ///
    635  1.1  jmmv /// \param name The name of the vector.
    636  1.1  jmmv ///
    637  1.1  jmmv /// \return A reference to the requested vector.
    638  1.1  jmmv ///
    639  1.1  jmmv /// \throw text::syntax_error If the vector does not exist.
    640  1.1  jmmv const text::templates_def::strings_vector&
    641  1.1  jmmv text::templates_def::get_vector(const std::string& name) const
    642  1.1  jmmv {
    643  1.1  jmmv     const vectors_map::const_iterator iter = _vectors.find(name);
    644  1.1  jmmv     if (iter == _vectors.end())
    645  1.1  jmmv         throw text::syntax_error(F("Unknown vector '%s'") % name);
    646  1.1  jmmv     return (*iter).second;
    647  1.1  jmmv }
    648  1.1  jmmv 
    649  1.1  jmmv 
    650  1.1  jmmv /// Indexes a vector and gets the value.
    651  1.1  jmmv ///
    652  1.1  jmmv /// \param name The name of the vector to index.
    653  1.1  jmmv /// \param index_name The name of a variable representing the index to use.
    654  1.1  jmmv ///     This must be convertible to a natural.
    655  1.1  jmmv ///
    656  1.1  jmmv /// \return The value of the vector at the given index.
    657  1.1  jmmv ///
    658  1.1  jmmv /// \throw text::syntax_error If the vector does not existor if the index is out
    659  1.1  jmmv ///     of range.
    660  1.1  jmmv const std::string&
    661  1.1  jmmv text::templates_def::get_vector(const std::string& name,
    662  1.1  jmmv                                 const std::string& index_name) const
    663  1.1  jmmv {
    664  1.1  jmmv     const strings_vector& vector = get_vector(name);
    665  1.1  jmmv     const std::string& index_str = get_variable(index_name);
    666  1.1  jmmv 
    667  1.1  jmmv     std::size_t index;
    668  1.1  jmmv     try {
    669  1.1  jmmv         index = text::to_type< std::size_t >(index_str);
    670  1.1  jmmv     } catch (const text::syntax_error& e) {
    671  1.1  jmmv         throw text::syntax_error(F("Index '%s' not an integer, value '%s'") %
    672  1.1  jmmv                                  index_name % index_str);
    673  1.1  jmmv     }
    674  1.1  jmmv     if (index >= vector.size())
    675  1.1  jmmv         throw text::syntax_error(F("Index '%s' out of range at position '%s'") %
    676  1.1  jmmv                                  index_name % index);
    677  1.1  jmmv 
    678  1.1  jmmv     return vector[index];
    679  1.1  jmmv }
    680  1.1  jmmv 
    681  1.1  jmmv 
    682  1.1  jmmv /// Evaluates a expression using these templates.
    683  1.1  jmmv ///
    684  1.1  jmmv /// An expression is a query on the current templates to fetch a particular
    685  1.1  jmmv /// value.  The value is always returned as a string, as this is how templates
    686  1.1  jmmv /// are internally stored.
    687  1.1  jmmv ///
    688  1.1  jmmv /// \param expression The expression to evaluate.  This should not include any
    689  1.1  jmmv ///     of the delimiters used in the user input, as otherwise the expression
    690  1.1  jmmv ///     will not be evaluated properly.
    691  1.1  jmmv ///
    692  1.1  jmmv /// \return The result of the expression evaluation as a string.
    693  1.1  jmmv ///
    694  1.1  jmmv /// \throw text::syntax_error If there is any problem while evaluating the
    695  1.1  jmmv ///     expression.
    696  1.1  jmmv std::string
    697  1.1  jmmv text::templates_def::evaluate(const std::string& expression) const
    698  1.1  jmmv {
    699  1.1  jmmv     const std::string::size_type paren_open = expression.find('(');
    700  1.1  jmmv     if (paren_open == std::string::npos) {
    701  1.1  jmmv         return get_variable(expression);
    702  1.1  jmmv     } else {
    703  1.1  jmmv         const std::string::size_type paren_close = expression.find(
    704  1.1  jmmv             ')', paren_open);
    705  1.1  jmmv         if (paren_close == std::string::npos)
    706  1.1  jmmv             throw text::syntax_error(F("Expected ')' in expression '%s')") %
    707  1.1  jmmv                                      expression);
    708  1.1  jmmv         if (paren_close != expression.length() - 1)
    709  1.1  jmmv             throw text::syntax_error(F("Unexpected text found after ')' in "
    710  1.1  jmmv                                        "expression '%s'") % expression);
    711  1.1  jmmv 
    712  1.1  jmmv         const std::string arg0 = expression.substr(0, paren_open);
    713  1.1  jmmv         const std::string arg1 = expression.substr(
    714  1.1  jmmv             paren_open + 1, paren_close - paren_open - 1);
    715  1.1  jmmv         if (arg0 == "defined") {
    716  1.1  jmmv             return exists(arg1) ? "true" : "false";
    717  1.1  jmmv         } else if (arg0 == "length") {
    718  1.1  jmmv             return F("%s") % get_vector(arg1).size();
    719  1.1  jmmv         } else {
    720  1.1  jmmv             return get_vector(arg0, arg1);
    721  1.1  jmmv         }
    722  1.1  jmmv     }
    723  1.1  jmmv }
    724  1.1  jmmv 
    725  1.1  jmmv 
    726  1.1  jmmv /// Applies a set of templates to an input stream.
    727  1.1  jmmv ///
    728  1.1  jmmv /// \param templates The templates to use.
    729  1.1  jmmv /// \param input The input to process.
    730  1.1  jmmv /// \param output The stream to which to write the processed text.
    731  1.1  jmmv ///
    732  1.1  jmmv /// \throw text::syntax_error If there is any problem processing the input.
    733  1.1  jmmv void
    734  1.1  jmmv text::instantiate(const templates_def& templates,
    735  1.1  jmmv                   std::istream& input, std::ostream& output)
    736  1.1  jmmv {
    737  1.1  jmmv     templates_parser parser(templates, "%", "%%");
    738  1.1  jmmv     parser.instantiate(input, output);
    739  1.1  jmmv }
    740  1.1  jmmv 
    741  1.1  jmmv 
    742  1.1  jmmv /// Applies a set of templates to an input file and writes an output file.
    743  1.1  jmmv ///
    744  1.1  jmmv /// \param templates The templates to use.
    745  1.1  jmmv /// \param input_file The path to the input to process.
    746  1.1  jmmv /// \param output_file The path to the file into which to write the output.
    747  1.1  jmmv ///
    748  1.1  jmmv /// \throw text::error If the input or output files cannot be opened.
    749  1.1  jmmv /// \throw text::syntax_error If there is any problem processing the input.
    750  1.1  jmmv void
    751  1.1  jmmv text::instantiate(const templates_def& templates,
    752  1.1  jmmv                   const fs::path& input_file, const fs::path& output_file)
    753  1.1  jmmv {
    754  1.1  jmmv     std::ifstream input(input_file.c_str());
    755  1.1  jmmv     if (!input)
    756  1.1  jmmv         throw text::error(F("Failed to open %s for read") % input_file);
    757  1.1  jmmv 
    758  1.1  jmmv     std::ofstream output(output_file.c_str());
    759  1.1  jmmv     if (!output)
    760  1.1  jmmv         throw text::error(F("Failed to open %s for write") % output_file);
    761  1.1  jmmv 
    762  1.1  jmmv     instantiate(templates, input, output);
    763  1.1  jmmv }
    764