stanc_helper.hpp
Go to the documentation of this file.
1 #ifndef STAN_COMMAND_STANC_HELPER_HPP
2 #define STAN_COMMAND_STANC_HELPER_HPP
3 
4 #include <stan/version.hpp>
5 #include <stan/lang/compiler.hpp>
7 #include <stan/io/cmd_line.hpp>
8 #include <exception>
9 #include <fstream>
10 #include <iostream>
11 #include <stdexcept>
12 #include <string>
13 #include <vector>
14 
15 
16 /**
17  * Print the version of stanc with major, minor and patch.
18  *
19  * @param[in,out] out_stream stream to which version is written.
20  */
21 inline void print_version(std::ostream* out_stream) {
22  if (!out_stream) return;
23  *out_stream << "stanc version "
25  << "."
27  << "."
29  << std::endl;
30 }
31 
32 /**
33  * Prints the Stan compiler (stanc) help.
34  *
35  * @param[in,out] out_stream stream to which help is written
36  */
37 inline void print_stanc_help(std::ostream* out_stream) {
39 
40  if (!out_stream) return;
41 
42  *out_stream << std::endl;
43  print_version(out_stream);
44  *out_stream << std::endl;
45 
46  *out_stream << "USAGE: " << "stanc [options] <model_file>" << std::endl;
47  *out_stream << std::endl;
48 
49  *out_stream << "OPTIONS:" << std::endl;
50  *out_stream << std::endl;
51 
52  print_help_option(out_stream, "help", "", "Display this information");
53 
54  print_help_option(out_stream, "version", "", "Display stanc version number");
55 
56  print_help_option(out_stream, "name", "string",
57  "Model name",
58  "default = \"$model_filename_model\"");
59 
60  print_help_option(out_stream, "o", "file",
61  "Output file for generated C++ code",
62  "default = \"$name.cpp\"");
63 
64  print_help_option(out_stream, "allow_undefined", "",
65  "Do not fail if a function is declared but not defined");
66  // TODO(martincerny) help for standalone function compilation
67 }
68 
69 /**
70  * Delte the file at the specified path, writing messages to error
71  * stream if not possible. Do nothing on zero size file name input.
72  * Only write to error stream if it is non-null.
73  *
74  * @param[in,out] err_stream stream to which error messages are
75  * written
76  * @param[in] file_name path of file
77  */
78 inline void delete_file(std::ostream* err_stream,
79  const std::string& file_name) {
80  if (file_name.size() == 0)
81  return;
82  int return_code = std::remove(file_name.c_str());
83  if (return_code != 0)
84  if (err_stream)
85  *err_stream << "Could not remove output file=" << file_name
86  << std::endl;
87 }
88 
89 /**
90  * Transform a provided input file name into a valid C++ identifier
91  * @param[in] in_file_name the name of the input file
92  * @return a valid C++ identifier based on the file name.
93  */
94 inline std::string identifier_from_file_name(const std::string& in_file_name) {
95  size_t slashInd = in_file_name.rfind('/');
96  size_t ptInd = in_file_name.rfind('.');
97  if (ptInd == std::string::npos)
98  ptInd = in_file_name.length();
99  if (slashInd == std::string::npos) {
100  slashInd = in_file_name.rfind('\\');
101  }
102  if (slashInd == std::string::npos) {
103  slashInd = 0;
104  } else {
105  slashInd++;
106  }
108  in_file_name.substr(slashInd, ptInd - slashInd);
109  for (std::string::iterator strIt = result.begin();
110  strIt != result.end(); strIt++) {
111  if (!isalnum(*strIt) && *strIt != '_') {
112  *strIt = '_';
113  }
114  }
115 
116  return result;
117 }
118 
119 
120 /**
121  * Check whether a given file has the specified extension.
122  *
123  * @param[in] file_name The name of the file
124  * @param[in] extension The extension (WITHOUT dot)- e.g. "stan".
125  * @return true if the file has the extension
126  */
128  const std::string& extension) {
129  if (file_name.length() >= extension.length() + 1) { // +1 for the dot
130  if (0 == file_name.compare (file_name.length() - extension.length(),
131  extension.length(), extension)
132  && file_name[file_name.length() - extension.length() - 1] == '.')
133  return true;
134  else
135  return false;
136  } else {
137  return false;
138  }
139 }
140 
141 /**
142  * Test whether a given string is a valid C++ identifier and throw
143  * an exception when it is not.
144  * @param[in] identifier the identifier to be checked
145  * @param[in] identifier_type the type of the identifier to be reported
146  * in error messages
147  */
148 inline void check_identifier(const std::string& identifier,
149  const std::string& identifier_type) {
150  if (!isalpha(identifier[0]) && identifier[0] != '_') {
151  std::string msg(identifier_type + " must not start with a "
152  "number or symbol other than _");
153  throw std::invalid_argument(msg);
154  }
155  for (std::string::const_iterator strIt = identifier.begin();
156  strIt != identifier.end(); strIt++) {
157  if (!isalnum(*strIt) && *strIt != '_') {
158  std::string msg(identifier_type
159  + " must contain only letters, numbers and _");
160  throw std::invalid_argument(msg);
161  }
162  }
163 }
164 
165 /**
166  * Invoke the stanc command on the specified argument list, writing
167  * output and error messages to the specified streams, return a return
168  * code.
169  *
170  * <p>The return codes are: 0 for success, -1 for an exception,
171  * -2 is parsing failed, and -3 if there are invalid arguments.
172  *
173  * @param[in] argc number of arguments
174  * @param[in] argv arguments
175  * @param[in,out] out_stream stream to which output is written
176  * @param[in,out] err_stream stream to which error messages are
177  * written
178  * @return return code
179  */
180 inline int stanc_helper(int argc, const char* argv[],
181  std::ostream* out_stream, std::ostream* err_stream) {
182  enum CompilationType {
183  kModel,
184  kStandaloneFunctions
185  };
186  static const int SUCCESS_RC = 0;
187  static const int EXCEPTION_RC = -1;
188  static const int PARSE_FAIL_RC = -2;
189  static const int INVALID_ARGUMENT_RC = -3;
190 
191  std::string out_file_name; // declare outside of try to delete in catch
192 
193  try {
194  stan::io::cmd_line cmd(argc, argv);
195 
196  if (cmd.has_flag("help")) {
197  print_stanc_help(out_stream);
198  return SUCCESS_RC;
199  }
200 
201  if (cmd.has_flag("version")) {
202  print_version(out_stream);
203  return SUCCESS_RC;
204  }
205 
206  if (cmd.bare_size() != 1) {
207  std::string msg("Require model file as argument. ");
208  throw std::invalid_argument(msg);
209  }
210  std::string in_file_name;
211  cmd.bare(0, in_file_name);
212 
213  CompilationType compilation_type;
214  if (has_extension(in_file_name, "stanfuncs")) {
215  compilation_type = kStandaloneFunctions;
216  } else {
217  compilation_type = kModel;
218  }
219 
220  std::ifstream in(in_file_name.c_str());
221  if (!in.is_open()) {
222  std::stringstream msg;
223  msg << "Failed to open model file "
224  << in_file_name.c_str();
225  throw std::invalid_argument(msg.str());
226  }
227 
228  std::vector<std::string> include_paths;
229  include_paths.push_back("");
230 
231  bool allow_undefined = cmd.has_flag("allow_undefined");
232 
233  bool valid_input = false;
234 
235  switch (compilation_type) {
236  case kModel: {
237  std::string model_name;
238  if (cmd.has_key("name")) {
239  cmd.val("name", model_name);
240  } else {
241  model_name = identifier_from_file_name(in_file_name) + "_model";
242  }
243 
244  // TODO(martincerny) Check that the -namespace flag is not set
245 
246  if (cmd.has_key("o")) {
247  cmd.val("o", out_file_name);
248  } else {
249  out_file_name = model_name;
250  // TODO(carpenter): shouldn't this be .hpp without a main()?
251  out_file_name += ".cpp";
252  }
253 
254  check_identifier(model_name, "model_name");
255 
256  std::fstream out(out_file_name.c_str(), std::fstream::out);
257  if (out_stream) {
258  *out_stream << "Model name=" << model_name << std::endl;
259  *out_stream << "Input file=" << in_file_name << std::endl;
260  *out_stream << "Output file=" << out_file_name << std::endl;
261  }
262  if (!out.is_open()) {
263  std::stringstream msg;
264  msg << "Failed to open output file "
265  << out_file_name.c_str();
266  throw std::invalid_argument(msg.str());
267  }
268 
269  valid_input = stan::lang::compile(err_stream, in, out, model_name,
270  allow_undefined, in_file_name,
271  include_paths);
272  out.close();
273  break;
274  }
275  case kStandaloneFunctions: {
276  if (cmd.has_key("o")) {
277  cmd.val("o", out_file_name);
278  } else {
279  out_file_name = identifier_from_file_name(in_file_name);
280  out_file_name += ".hpp";
281  }
282 
283  // TODO(martincerny) Allow multiple namespaces
284  // (split namespace argument by "::")
285  std::vector<std::string> namespaces;
286  if (cmd.has_key("namespace")) {
287  std::string ns;
288  cmd.val("namespace", ns);
289  namespaces.push_back(ns);
290  } else {
291  namespaces.push_back(identifier_from_file_name(in_file_name)
292  + "_functions");
293  }
294 
295  // TODO(martincerny) Check that the -name flag is not set
296 
297  for (size_t namespace_i = 0;
298  namespace_i < namespaces.size(); ++namespace_i) {
299  check_identifier(namespaces[namespace_i], "namespace");
300  }
301 
302  std::fstream out(out_file_name.c_str(), std::fstream::out);
303  if (out_stream) {
304  *out_stream << "Parsing a fuctions-only file" << std::endl;
305 
306  *out_stream << "Target namespace= ";
307  for (size_t namespace_i = 0;
308  namespace_i < namespaces.size(); ++namespace_i) {
309  *out_stream << "::" << namespaces[namespace_i];
310  }
311  *out_stream << std::endl;
312 
313  *out_stream << "Input file=" << in_file_name << std::endl;
314  *out_stream << "Output file=" << out_file_name << std::endl;
315  }
316 
317  valid_input = stan::lang::compile_functions(err_stream, in, out,
318  namespaces, allow_undefined);
319 
320  out.close();
321  break;
322  }
323  default: {
324  assert(false);
325  }
326  }
327 
328  if (!valid_input) {
329  if (err_stream)
330  *err_stream << "PARSING FAILED." << std::endl;
331  // FIXME: how to remove triple cut-and-paste?
332  delete_file(out_stream, out_file_name);
333  return PARSE_FAIL_RC;
334  }
335  } catch (const std::invalid_argument& e) {
336  if (err_stream) {
337  *err_stream << std::endl
338  << e.what()
339  << std::endl;
340  delete_file(out_stream, out_file_name);
341  }
342  return INVALID_ARGUMENT_RC;
343  } catch (const std::exception& e) {
344  if (err_stream) {
345  *err_stream << std::endl
346  << e.what()
347  << std::endl;
348  }
349  delete_file(out_stream, out_file_name);
350  return EXCEPTION_RC;
351  }
352  return SUCCESS_RC;
353 }
354 
355 #endif
bool compile(std::ostream *msgs, std::istream &in, std::ostream &out, const std::string &name, const bool allow_undefined=false, const std::string &filename="unknown file name", const std::vector< std::string > &include_paths=std::vector< std::string >())
Definition: compiler.hpp:35
std::string identifier_from_file_name(const std::string &in_file_name)
void check_identifier(const std::string &identifier, const std::string &identifier_type)
bool val(const std::string &key, T &x) const
Definition: cmd_line.hpp:190
::xsd::cxx::tree::exception< char > exception
Definition: Database.h:225
void delete_file(std::ostream *err_stream, const std::string &file_name)
bool has_key(const std::string &key) const
Definition: cmd_line.hpp:166
bool has_extension(const std::string &file_name, const std::string &extension)
void print_version(std::ostream *out_stream)
int stanc_helper(int argc, const char *argv[], std::ostream *out_stream, std::ostream *err_stream)
string cmd
Definition: run_hadd.py:52
bool has_flag(const std::string &flag) const
Definition: cmd_line.hpp:204
const std::string MINOR_VERSION
Definition: version.hpp:24
bool compile_functions(std::ostream *msgs, std::istream &stan_funcs_in, std::ostream &cpp_out, const std::vector< std::string > &namespaces, const bool allow_undefined=false, const std::string &filename="unknown file name", const std::vector< std::string > &include_paths=std::vector< std::string >())
void invalid_argument(const char *function, const char *name, const T &y, const char *msg1, const char *msg2)
static const double ns
Module that plots metrics from reconstructed cosmic ray data.
size_t bare_size() const
Definition: cmd_line.hpp:213
::xsd::cxx::tree::string< char, simple_type > string
Definition: Database.h:154
ifstream in
Definition: comparison.C:7
const std::string PATCH_VERSION
Definition: version.hpp:27
void print_stanc_help(std::ostream *out_stream)
assert(nhit_max >=nhit_nbins)
bool bare(size_t n, T &x) const
Definition: cmd_line.hpp:231
void print_help_option(std::ostream *o, const std::string &key, const std::string &value_type, const std::string &msg, const std::string &note="")
Definition: cmd_line.hpp:74
const std::string MAJOR_VERSION
Definition: version.hpp:21
Float_t e
Definition: plot.C:35