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