+++ /dev/null
-//---------------------------- expand_instantiations.cc -------------------
-// $Id$
-// Version: $Name$
-//
-// Copyright (C) 2007, 2008, 2009, 2010, 2011 by the deal.II authors
-//
-// This file is subject to QPL and may not be distributed
-// without copyright and license information. Please refer
-// to the file deal.II/doc/license.html for the text and
-// further information on this license.
-//
-//---------------------------- expand_instantiations.cc -------------------
-
-// This is the program that we use to generate explicit instantiations for a
-// variety of template arguments. It takes two kinds of input files. The first
-// is given as arguments on the command line and contains entries of the
-// following form:
-// --------------------
-// REAL_SCALARS := { double; float; long double }
-// COMPLEX_SCALARS := { std::complex<double>;
-// std::complex<float>;
-// std::complex<long double> }
-// VECTORS := { Vector<double>; Vector<float>; Vector<long double> }
-// --------------------
-//
-// The input file of this form is typically located in the common/ directory
-// and is generated by ./configure to contain the list of vectors etc that
-// make sense for the current configuration. For example, the list of VECTORS
-// is going to contain PETSc vectors if so configured.
-//
-// The second intput is read from the command line and consists of a sequence
-// of statements of the following form:
-// --------------------
-// for (u,v:VECTORS; z:SCALARS) { f(u, z, const v &); }
-// --------------------
-// Here, everything between {...} will be copied as many times as there are
-// combinations of arguments u,v in the list of substitutions given by
-// VECTORS. For each copy, the arguments u,v will be replaced by one of these
-// combinations.
-
-// Author: Wolfgang Bangerth, 2007
-
-
-#include <iostream>
-#include <fstream>
-#include <map>
-#include <list>
-#include <cstdlib>
-#include <string>
-
-// a map from the keys in the expansion lists to the list itself. For
-// instance, the example above will lead to the entry
-// expansion_lists[REAL_SCALARS] = (double, float, long double)
-// in this map, among others
-std::map<std::string, std::list<std::string> > expansion_lists;
-
-
-
-/* ======================== auxiliary functions ================= */
-
-
-// replace all occurrences of 'pattern' by 'substitute' in 'in', and
-// return the result
-std::string
-replace_all (const std::string &in,
- const std::string &pattern,
- const std::string &substitute)
-{
- std::string x = in;
- while (x.find(pattern) != std::string::npos)
- x.replace (x.find(pattern),
- pattern.size(),
- substitute);
- return x;
-}
-
-
-// extract from the start of #in the part of the string that ends with one of
-// the characters in #delim_list. The extracted part is deleted from #in
-// and returned. We skip characters in delim_list if they are preceded
-// by a backslash
-std::string
-get_substring_with_delim (std::string &in,
- const std::string &delim_list)
-{
- std::string x;
- while (in.size() != 0)
- {
- // stop copying to the result
- // if the current character is
- // a delimiter, but only if the
- // previous character was not a
- // backslash
- if ((delim_list.find (in[0]) != std::string::npos)
- &&
- !((x.size() > 0)
- &&
- (x[x.size()-1] == '\\')))
- break;
-
- x += in[0];
- in.erase (0, 1);
- }
-
- return x;
-}
-
-
-// delete all whitespace at the beginning of the given argument
-void
-skip_space (std::string &in)
-{
- while ((in.size() != 0)
- &&
- ((in[0] == ' ') || (in[0] == '\t') || (in[0] == '\n')))
- in.erase (0, 1);
-}
-
-
-std::string remove_comments (std::string line)
-{
- const std::string::size_type double_slash_comment = line.find ("//");
- if (double_slash_comment != std::string::npos)
- line.erase (double_slash_comment, std::string::npos);
-
- const std::string::size_type slash_star_comment_begin = line.find ("/*");
- if (slash_star_comment_begin != std::string::npos)
- {
- const std::string::size_type slash_star_comment_end = line.find ("*/");
- if (slash_star_comment_end == std::string::npos)
- {
- std::cerr << "The program can currently only handle /* block */"
- << "comments that start and end within the same line."
- << std::endl;
- std::exit (1);
- }
- line.erase (slash_star_comment_begin,
- slash_star_comment_end - slash_star_comment_begin + 2);
- }
-
- return line;
-}
-
-
-// read the whole file specified by the stream given as argument into a string
-// for simpler parsing, and return it
-std::string read_whole_file (std::istream &in)
-{
- std::string whole_file;
- while (in)
- {
- std::string line;
- getline (in, line);
-
- whole_file += remove_comments (line);
- whole_file += '\n';
- }
- // substitute tabs by spaces, multiple
- // spaces by single ones
- for (unsigned int i=0; i<whole_file.size(); ++i)
- if (whole_file[i] == '\t')
- whole_file[i] = ' ';
- while (whole_file.find(" ") != std::string::npos)
- whole_file.replace (whole_file.find(" "), 2, " ");
-
- return whole_file;
-}
-
-
-
-// split a given string assumed to consist of a list of substrings delimited
-// by a particular character into its components
-std::list<std::string>
-split_string_list (const std::string &s,
- const char delimiter)
-{
- std::string tmp = s;
- std::list<std::string> split_list;
-
- // split the input list
- while (tmp.length() != 0)
- {
- std::string name;
- name = tmp;
-
- if (name.find(delimiter) != std::string::npos)
- {
- name.erase (name.find(delimiter), std::string::npos);
- tmp.erase (0, tmp.find(delimiter)+1);
- }
- else
- tmp = "";
-
- skip_space (name);
-
- while ((name.size() != 0) && (name[name.length()-1] == ' '))
- name.erase (name.length()-1, 1);
-
- split_list.push_back (name);
- }
-
- return split_list;
-}
-
-
-
-// return the given list but without empty entries
-std::list<std::string>
-delete_empty_entries (const std::list<std::string> &list)
-{
- std::list<std::string> return_list;
- for (std::list<std::string>::const_iterator i = list.begin();
- i != list.end(); ++i)
- if (*i != "")
- return_list.push_back (*i);
-
- return return_list;
-}
-
-
-
-// determine whether a given substring at position #pos and length #length in
-// the string #text is a real token, i.e. not just part of another word
-bool is_real_token (const std::string &text,
- const std::string::size_type pos,
- const std::string::size_type length)
-{
- static const std::string token_chars ("abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "_");
- if ((pos != 0) && (token_chars.find (text[pos-1]) != std::string::npos))
- return false;
-
- if ((pos+length < text.size()) &&
- (token_chars.find (text[pos+length]) != std::string::npos))
- return false;
-
- return true;
-}
-
-
-// substitute all occurrences of #token in #text by #substitute. because a
-// replacement token could be a templated class like std::complex<double> and
-// because the token to the substituted may be a template argument itself, we
-// surround the substitution by a space which shouldn't matter in C++
-std::string substitute_tokens (const std::string &text,
- const std::string &token,
- const std::string &substitute)
-{
- std::string x_text = text;
- std::string::size_type pos = 0;
- while ((pos = x_text.find(token, pos)) != std::string::npos)
- {
- if (is_real_token (x_text, pos, token.size()))
- {
- x_text.replace (pos, token.size(),
- std::string(" ")+substitute+std::string(" "));
- pos += substitute.size()+2;
- }
- else
- ++pos;
- }
-
- return x_text;
-}
-
-
-
-/* ======================== the main functions ================= */
-
-
-// read and parse the expansion lists like
-// REAL_SCALARS := { double; float; long double }
-// as specified at the top of the file and store them in
-// the global expansion_lists variable
-void read_expansion_lists (const std::string &filename)
-{
- std::ifstream in (filename.c_str());
-
- if (! in)
- {
- std::cerr << "Instantiation list file can not be read!"
- << std::endl;
- std::exit (1);
- }
-
- // read the entire file into a string for
- // simpler processing. replace end-of-line
- // characters by spaces
- std::string whole_file = read_whole_file (in);
-
- // now process entries of the form
- // NAME := { class1; class2; ...}.
- while (whole_file.size() != 0)
- {
- const std::string
- name = get_substring_with_delim (whole_file, " :");
-
- skip_space (whole_file);
- if (whole_file.find (":=") != 0)
- {
- std::cerr << "Invalid entry <" << name << '>' << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 2);
- skip_space (whole_file);
- if (whole_file.find ("{") != 0)
- {
- std::cerr << "Invalid entry <" << name << '>' << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 1);
- skip_space (whole_file);
-
- std::string
- expansion = get_substring_with_delim (whole_file, "}");
-
- if (whole_file.find ("}") != 0)
- {
- std::cerr << "Invalid entry <" << name << '>' << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 1);
- skip_space (whole_file);
-
- // assign but remove empty entries;
- // this may happen if an expansion list
- // ends in a semicolon (then we get an
- // empty entry at the end), or if there
- // are multiple semicolons after each
- // other (this may happen if, for
- // example, we have "Vector<double>;
- // TRILINOS_VECTOR;" and if
- // TRILINOS_VECTOR is an empty
- // expansion after running ./configure)
- expansion_lists[name]
- = delete_empty_entries (split_string_list (expansion, ';'));
- }
-}
-
-
-
-// produce all combinations of substitutions of the tokens given in the
-// #substitutions list in #text and output it to std::cout
-void substitute (const std::string &text,
- const std::list<std::pair<std::string, std::string> > &substitutions)
-{
- // do things recursively: if the list of
- // substitutions has a single entry, then
- // process all of them. otherwise, process
- // the first in the list and call the
- // function recursively with the rest of
- // the substitutions
- if (substitutions.size() > 1)
- {
- // do the first substitution, then call
- // function recursively
- const std::string name = substitutions.front().first,
- pattern = substitutions.front().second;
-
- const std::list<std::pair<std::string, std::string> >
- rest_of_substitutions (++substitutions.begin(),
- substitutions.end());
-
- for (std::list<std::string>::const_iterator
- expansion = expansion_lists[pattern].begin();
- expansion != expansion_lists[pattern].end();
- ++expansion)
- {
- std::string new_text
- = substitute_tokens (text, name, *expansion);
-
- substitute (new_text, rest_of_substitutions);
- }
- }
- else if (substitutions.size() == 1)
- {
- // do the substitutions
- const std::string name = substitutions.front().first,
- pattern = substitutions.front().second;
-
- for (std::list<std::string>::const_iterator
- expansion = expansion_lists[pattern].begin();
- expansion != expansion_lists[pattern].end();
- ++expansion)
- std::cout << substitute_tokens (text, name, *expansion)
- << std::endl;
- }
- else
- {
- std::cout << text
- << std::endl;
- }
-}
-
-
-
-// process the list of instantiations given in the form
-// --------------------
-// for (u,v:VECTORS; z:SCALARS) { f(u, z, const v &); }
-// --------------------
-void process_instantiations ()
-{
- std::string whole_file = read_whole_file (std::cin);
-
- // process entries of the form
- // for (X:Y; A:B) { INST }
- while (whole_file.size() != 0)
- {
- skip_space (whole_file);
- if (whole_file.find ("for") != 0)
- {
- std::cerr << "Invalid instantiation list: missing 'for'" << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 3);
- skip_space (whole_file);
- if (whole_file.find ("(") != 0)
- {
- std::cerr << "Invalid instantiation list: missing '('" << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 1);
- skip_space (whole_file);
-
- const std::list<std::string>
- substitutions_list
- = split_string_list (get_substring_with_delim (whole_file,
- ")"),
- ';');
- if (whole_file.find (")") != 0)
- {
- std::cerr << "Invalid instantiation list: missing ')'" << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 1);
- skip_space (whole_file);
-
- // process the header
- std::list<std::pair<std::string, std::string> >
- substitutions;
-
- for (std::list<std::string>::const_iterator
- s = substitutions_list.begin();
- s != substitutions_list.end(); ++s)
- {
- const std::list<std::string>
- names_and_type = split_string_list (*s, ':');
- if (names_and_type.size() != 2)
- {
- std::cerr << "Invalid instantiation header" << std::endl;
- std::exit (1);
- }
-
- const std::list<std::string>
- names = split_string_list (names_and_type.front(), ',');
-
- for (std::list<std::string>::const_iterator
- x = names.begin(); x != names.end(); ++x)
- substitutions.push_back (std::make_pair (*x,
- names_and_type.back()));
- }
-
- // now read the part in {...}
- skip_space (whole_file);
- if (whole_file.find ("{") != 0)
- {
- std::cerr << "Invalid substitution text" << std::endl;
- std::exit (1);
- }
- whole_file.erase (0, 1);
- skip_space (whole_file);
- const std::string text_to_substitute
- = get_substring_with_delim (whole_file, "}");
- whole_file.erase (0,1);
- skip_space (whole_file);
-
- // now produce the
- // substitutions. first replace
- // all occurrences of "\{" by
- // "{"
- substitute (replace_all(replace_all(text_to_substitute, "\\{", "{"),
- "\\}", "}"),
- substitutions);
- }
-}
-
-
-
-int main (int argc, char **argv)
-{
- if (argc < 2)
- {
- std::cerr << "Usage: " << std::endl
- << " expand_instantiations class_list_files < in_file > out_file"
- << std::endl;
- std::exit (1);
- }
-
- for (int i=1; i<argc; ++i)
- read_expansion_lists (argv[i]);
-
- process_instantiations ();
-}