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