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