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