program_reader.hpp
Go to the documentation of this file.
1 #ifndef STAN_IO_PROGRAM_READER_HPP
2 #define STAN_IO_PROGRAM_READER_HPP
3 
4 #include <stan/io/read_line.hpp>
6 #include <cstdio>
7 #include <istream>
8 #include <iostream>
9 #include <fstream>
10 #include <set>
11 #include <sstream>
12 #include <stdexcept>
13 #include <string>
14 #include <utility>
15 #include <vector>
16 
17 namespace stan {
18  namespace io {
19 
20  /**
21  * Structure to hold preprocessing events, which consist of (a)
22  * line number in concatenated program after includes, (b) line
23  * number in the stream from which the text is read, (c) a
24  * string-based action, and (d) a path to the current file.
25  */
26  struct preproc_event {
28  int line_num_;
31 
32  preproc_event(int concat_line_num, int line_num,
33  const std::string& action, const std::string& path)
34  : concat_line_num_(concat_line_num), line_num_(line_num),
35  action_(action), path_(path) { }
36 
37  void print(std::ostream& out) {
38  out << "(" << concat_line_num_ << ", " << line_num_
39  << ", " << action_ << ", " << path_ << ")";
40  }
41  };
42 
43  /**
44  * A <code>program_reader</code> reads a Stan program and unpacks
45  * the include statements relative to a search path in such a way
46  * that error messages can reproduce the include path.
47  */
49  public:
50  /**
51  * A pair for holding a path and a line number.
52  */
53  typedef std::pair<std::string, int> path_line_t;
54 
55  /**
56  * Ordered sequence of path and line number pairs.
57  */
58  typedef std::vector<path_line_t> trace_t;
59 
60  /**
61  * Construct a program reader from the specified stream derived
62  * from the specified name or path, and a sequence of paths to
63  * search for include files. The paths should be directories.
64  *
65  * <p>Calling this method does not close the specified input stream.
66  *
67  * @param[in] in stream from which to start reading
68  * @param[in] name name path or name attached to stream
69  * @param[in] search_path ordered sequence of directory names to
70  * search for included files
71  */
72  program_reader(std::istream& in, const std::string& name,
73  const std::vector<std::string>& search_path) {
74  int concat_line_num = 0;
75  read(in, name, search_path, concat_line_num);
76  }
77 
78  /**
79  * Construct a copy of the specified reader. Both the
80  * underlying program string and history will be copied.
81  *
82  * @param r reader to copy
83  */
85  : program_(r.program_.str()), history_(r.history_) { }
86 
87  /**
88  * Construct a program reader with an empty program and
89  * history.
90  */
91  program_reader() : program_(""), history_() { }
92 
93  /**
94  * Return a string representing the concatenated program. This
95  * string may be wrapped in a <code>std::stringstream</code> for
96  * reading.
97  *
98  * @return stream for program
99  */
101  return program_.str();
102  }
103 
104  /**
105  * Return the include trace of the path and line numbers leading
106  * to the specified line of text in the concatenated program.
107  * The top of the stack is the most recently read path.
108  *
109  * @param[in] target line number in concatenated program file
110  * @return sequence of files and positions for includes
111  */
112  trace_t trace(int target) const {
113  if (target < 1)
114  throw std::runtime_error("trace() argument target must be"
115  " greater than 1");
116  trace_t result;
117  std::string file = "ERROR: UNINITIALIZED";
118  int file_start = -1;
119  int concat_start = -1;
120  for (size_t i = 0; i < history_.size(); ++i) {
121  if (target <= history_[i].concat_line_num_) {
122  int line = file_start + target - concat_start;
123  result.push_back(path_line_t(file, line));
124  return result;
125  } else if (history_[i].action_ == "start"
126  || history_[i].action_ == "restart" ) {
127  file = history_[i].path_;
128  file_start = history_[i].line_num_;
129  concat_start = history_[i].concat_line_num_;
130  } else if (history_[i].action_ == "end") {
131  if (result.size() == 0) break;
132  result.pop_back();
133  } else if (history_[i].action_ == "include") {
134  result.push_back(path_line_t(file, history_[i].line_num_ + 1));
135  }
136  }
137  throw std::runtime_error("ran beyond end of program in trace()");
138  }
139 
140  /**
141  * Return the record of the files and includes used to build up
142  * this program.
143  *
144  * @return I/O history of the program
145  */
146  const std::vector<preproc_event>& history() const {
147  return history_;
148  }
149 
150  /**
151  * Adds preprocessing event with specified components to the
152  * back of the history sequence.
153  *
154  * @param[in] concat_line_num position in concatenated program
155  * @param[in] line_num position in current file
156  * @param[in] action purpose of preprocessing event
157  * @param[in] path location of current file
158  */
159  void add_event(int concat_line_num, int line_num,
160  const std::string& action, const std::string& path) {
161  preproc_event e(concat_line_num, line_num, action, path);
162  history_.push_back(e);
163  }
164 
165  private:
166  std::stringstream program_;
167  std::vector<preproc_event> history_;
168 
169  /**
170  * Returns the characters following <code>#include</code> on
171  * the line, trimming whitespace characters. Assumes that
172  * <code>#include</code>" is line initial.
173  *
174  * @param line line of text beginning with <code>#include</code>
175  * @return text after <code>#include</code> with whitespace
176  * trimmed
177  */
179  int start = std::string("#include").size();
180  while (line[start] == ' ') ++start;
181  int end = line.size() - 1;
182  while (line[end] == ' ') --end;
183  return line.substr(start, end - start);
184  }
185 
186  void read(std::istream& in, const std::string& path,
187  const std::vector<std::string>& search_path,
188  int& concat_line_num, bool is_nested,
189  std::set<std::string>& visited_paths) {
190  if (visited_paths.find(path) != visited_paths.end())
191  return; // avoids recursive visitation
192  visited_paths.insert(path);
193  history_.push_back(preproc_event(concat_line_num, 0, "start", path));
194  for (int line_num = 1; ; ++line_num) {
195  std::string line = read_line(in);
196  if (line.empty()) {
197  // ends initial out of loop start event
198  if (!is_nested) {
199  // pad end concat_line_num of outermost file in order to properly
200  // report end-of-file parse error - else trace throws exception
201  history_.push_back(preproc_event(concat_line_num + 2,
202  line_num - 1, "end", path));
203  } else {
204  history_.push_back(preproc_event(concat_line_num,
205  line_num - 1, "end", path));
206  }
207  break;
208  } else if (starts_with("#include ", line)) {
209  std::string incl_path = include_path(line);
210  history_.push_back(preproc_event(concat_line_num, line_num - 1,
211  "include", incl_path));
212  bool found_path = false;
213  for (size_t i = 0; i < search_path.size(); ++i) {
214  std::string f = search_path[i] + incl_path;
215  std::ifstream include_in(f.c_str());
216  if (!include_in.good()) {
217  include_in.close();
218  continue;
219  }
220  try {
221  read(include_in, incl_path, search_path, concat_line_num, true,
222  visited_paths);
223  } catch (...) {
224  include_in.close();
225  throw;
226  }
227  include_in.close();
228  history_.push_back(preproc_event(concat_line_num, line_num,
229  "restart", path));
230  found_path = true;
231  break;
232  }
233  if (!found_path)
234  throw std::runtime_error("could not find include file: " + line);
235  } else {
236  ++concat_line_num;
237  program_ << line;
238  }
239  }
240  visited_paths.erase(path); // allow multiple, just not nested
241  }
242 
243 
244  /**
245  * Read the rest of a program from the specified input stream in
246  * the specified path, with the specified search path for
247  * include files, and incrementing the specified concatenated
248  * line number. This method is called recursively for included
249  * files. If a file is included recursively, the second include
250  * is ignored.
251  *
252  * @param[in] in stream from which to read
253  * @param[in] path name of stream
254  * @param[in] search_path sequence of path names to search for
255  * include files
256  * @param[in,out] concat_line_num position in concatenated file
257  * to be updated
258  * @throw std::runtime_error if an included file cannot be found
259  */
260  void read(std::istream& in, const std::string& path,
261  const std::vector<std::string>& search_path,
262  int& concat_line_num) {
263  std::set<std::string> visited_paths;
264  read(in, path, search_path, concat_line_num, false, visited_paths);
265  }
266  };
267 
268  }
269 }
270 #endif
271 
const XML_Char * name
Definition: expat.h:151
void read(std::istream &in, const std::string &path, const std::vector< std::string > &search_path, int &concat_line_num, bool is_nested, std::set< std::string > &visited_paths)
const XML_Char * target
Definition: expat.h:268
void add_event(int concat_line_num, int line_num, const std::string &action, const std::string &path)
std::string read_line(std::istream &in)
Definition: read_line.hpp:22
program_reader(std::istream &in, const std::string &name, const std::vector< std::string > &search_path)
std::stringstream program_
std::pair< std::string, int > path_line_t
bool starts_with(const std::string &p, const std::string &s)
Definition: starts_with.hpp:17
program_reader(const program_reader &r)
std::vector< path_line_t > trace_t
static std::string include_path(const std::string &line)
void print(std::ostream &out)
trace_t trace(int target) const
const std::vector< preproc_event > & history() const
std::string program() const
const std::string path
Definition: plot_BEN.C:43
::xsd::cxx::tree::string< char, simple_type > string
Definition: Database.h:154
ifstream in
Definition: comparison.C:7
void read(std::istream &in, const std::string &path, const std::vector< std::string > &search_path, int &concat_line_num)
std::vector< preproc_event > history_
TFile * file
Definition: cellShifts.C:17
TRandom3 r(0)
Float_t e
Definition: plot.C:35
preproc_event(int concat_line_num, int line_num, const std::string &action, const std::string &path)