NEURON
modl.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 /*
4  * int main(int argc, char *argv[]) --- returns 0 if translation is
5  * successful. Diag will exit with 1 if error.
6  *
7  * ---The overall strategy of the translation consists of three phases.
8  *
9  * 1) read in the whole file as a sequence of tokens, parsing as we go. Most of
10  * the trivial C translation such as appending ';' to statements is performed
11  * in this phase as is the creation of the symbol table. Item lists maintain
12  * the proper token order. Ater a whole block is read in, nontrivial
13  * manipulation may be performed on the entire block.
14  *
15  * 2) Some blocks and statements can be manipulated only after the entire file
16  * has been read in. The solve statement is an example since it can be
17  * analysed only after we know what is the type of the associated block. The
18  * kinetic block is another example whose translation depends on the SOLVE
19  * method and so cannot be processed until the whole input file has been
20  * read.
21  *
22  * 3) Output the lists.
23  *
24  * void openfiles(int argc, char *argv[]) parse the argument list, and open
25  * files. Print usage message and exit if no argument
26  *
27  */
28 
29 /* the first arg may also be a file.mod (containing the .mod suffix)*/
30 
31 #include <CLI/CLI.hpp>
32 
33 #include <stdlib.h>
34 #include "modl.h"
35 
36 #include <cstring>
37 #include <string>
38 #include <filesystem>
39 
40 namespace fs = std::filesystem;
41 
42 FILE *fin, /* input file descriptor for filename.mod */
43  /* or file2 from the second argument */
44  *fparout, /* output file descriptor for filename.var */
45  *fcout; /* output file descriptor for filename.c */
46 
47 
48 char* modprefix;
49 
51 
52 #if LINT
53 char* clint;
54 int ilint;
55 Item* qlint;
56 #endif
57 
58 int nmodl_text = 1;
60 
61 extern int yyparse();
62 
63 extern int vectorize;
64 extern int numlist;
65 extern const char* nmodl_version_;
66 extern int usederivstatearray;
67 
68 static void openfiles(const char* given_filename, const char* output_dir);
69 
70 int main(int argc, char** argv) {
71  std::string output_dir{};
72  std::string inputfile{};
73 
74  CLI::App app{"Source to source compiler from NMODL to C++"};
75  app.add_option("-o,--outdir", output_dir, "directory where output files will be written");
76  app.set_version_flag("-v,--version", nmodl_version_, "print version number");
77  app.set_help_flag("-h,--help", "print this message");
78  app.add_option("Inputfile", inputfile)->required();
79  app.allow_extras();
80 
81  CLI11_PARSE(app, argc, argv);
82 
83  if (!app.remaining().empty()) {
84  fprintf(stderr,
85  "%s: Warning several input files specified on command line but only one will be "
86  "processed\n",
87  argv[0]);
88  }
89 
90  filetxtlist = newlist();
91 
92  init(); /* keywords into symbol table, initialize
93  * lists, etc. */
94 
95  std::strcpy(finname, inputfile.c_str());
96  openfiles(inputfile.c_str(),
97  output_dir.empty() ? nullptr : output_dir.c_str()); /* .mrg else .mod, .var, .c */
98  IGNORE(yyparse());
99  /*
100  * At this point all blocks are fully processed except the kinetic
101  * block and the solve statements. Even in these cases the
102  * processing doesn't involve syntax since the information is
103  * held in intermediate lists of specific structure.
104  *
105  */
106 
107  /*
108  * go through the list of solve statements and construct the model()
109  * code
110  */
111  solvhandler();
112  netrec_discon();
113  /*
114  * NAME's can be used in many cases before they were declared and
115  * no checking up to this point has been done to make sure that
116  * names have been used in only one way.
117  *
118  */
119  consistency();
120  chk_thread_safe();
123 
124  parout(); /* print .var file.
125  * Also #defines which used to be in defs.h
126  * are printed into .c file at beginning.
127  */
128  c_out(); /* print .c file */
129 
130 #if !defined NMODL_TEXT
131 #define NMODL_TEXT 1
132 #endif
133 #if NMODL_TEXT
134 #if 0
135 /* test: temp.txt should be identical to text of input file except for INCLUDE */
136 {
137  FILE* f = fopen("temp.txt", "w");
138  assert(f);
139  Item* q;
140  ITERATE(q, filetxtlist) {
141  char* s = STR(q);
142  fprintf(f, "%s", s);
143  }
144  fclose(f);
145 }
146 #endif
147  if (nmodl_text) {
148  Item* q;
149  fprintf(
150  fcout,
151  "\n#if NMODL_TEXT\nstatic void register_nmodl_text_and_filename(int mech_type) {\n");
152 #if !defined(NRN_AVOID_ABSOLUTE_PATHS)
153  fprintf(fcout, " const char* nmodl_filename = \"%s\";\n", fs::absolute(finname).c_str());
154 #else
155  fprintf(fcout,
156  " const char* nmodl_filename = \"%s\";\n",
157  fs::path(finname).filename().c_str());
158 #endif
159  fprintf(fcout, " const char* nmodl_file_text = \n");
160  ITERATE(q, filetxtlist) {
161  char* s = STR(q);
162  char* cp;
163  fprintf(fcout, " \"");
164  /* Escape double quote, backslash, and end each line with \n */
165  for (cp = s; *cp; ++cp) {
166  if (*cp == '"' || *cp == '\\') {
167  fprintf(fcout, "\\");
168  }
169  if (*cp == '\n') {
170  fprintf(fcout, "\\n\"");
171  }
172  fputc(*cp, fcout);
173  }
174  }
175  fprintf(fcout, " ;\n");
176  fprintf(fcout, " hoc_reg_nmodl_filename(mech_type, nmodl_filename);\n");
177  fprintf(fcout, " hoc_reg_nmodl_text(mech_type, nmodl_file_text);\n");
178  fprintf(fcout, "}\n");
179  fprintf(fcout, "#endif\n");
180  }
181 #endif
182 
183  IGNORE(fclose(fcout));
184 
185  if (vectorize) {
186  Fprintf(stderr, "Thread Safe\n");
187  }
188  if (usederivstatearray) {
189  fprintf(stderr,
190  "Derivatives of STATE array variables are not translated correctly and compile "
191  "time errors will be generated.\n");
192  fprintf(stderr, "The %s.cpp file may be manually edited to fix these errors.\n", modprefix);
193  }
194 
195 #if LINT
196  { /* for lex */
197  extern int yytchar, yylineno;
198  extern FILE* yyin;
199  IGNORE(yyin);
200  IGNORE(yytchar);
201  IGNORE(yylineno);
202  IGNORE(yyinput());
203  yyunput(ilint);
204  yyoutput(ilint);
205  }
206 #endif
207  free(modprefix); /* allocated in openfiles below */
208  return 0;
209 }
210 
211 static void openfiles(const char* given_filename, const char* output_dir) {
212  char output_filename[NRN_BUFSIZE];
213  char input_filename[NRN_BUFSIZE];
214  modprefix = strdup(given_filename); // we want to keep original string to open input file
215  // find last '.' after last '/' that delimit file name from extension
216  // we are not bothering to deal with filenames that begin with a . but do
217  // want to deal with paths like ../foo/hh
218  char* first_ext_char = strrchr(modprefix, '.');
219  if (strrchr(modprefix, '/') > first_ext_char) {
220  first_ext_char = NULL;
221  }
222 
223  Sprintf(input_filename, "%s", given_filename);
224 
225  if (first_ext_char)
226  *first_ext_char = '\0'; // effectively cut the extension from prefix if it exist in
227  // given_filename
228  if ((fin = fopen(input_filename, "r")) == (FILE*) 0) { // first try to open given_filename
229  Sprintf(input_filename, "%s.mod", given_filename); // if it dont work try to add ".mod"
230  // extension and retry
231  Sprintf(finname, "%s.mod", given_filename); // finname is still a global variable, so we
232  // need to update it
233  if ((fin = fopen(input_filename, "r")) == (FILE*) 0) {
234  diag("Can't open input file: ", input_filename);
235  }
236  }
237  if (output_dir) {
238  try {
239  fs::create_directories(output_dir);
240  } catch (...) {
241  fprintf(stderr, "Can't create output directory %s\n", output_dir);
242  exit(1);
243  }
244  char* basename = strrchr(modprefix, '/');
245  if (basename) {
246  Sprintf(output_filename, "%s%s.cpp", output_dir, basename);
247  } else {
248  Sprintf(output_filename, "%s/%s.cpp", output_dir, modprefix);
249  }
250  } else {
251  Sprintf(output_filename, "%s.cpp", modprefix);
252  }
253 
254  if ((fcout = fopen(output_filename, "w")) == (FILE*) 0) {
255  diag("Can't create C file: ", output_filename);
256  }
257  Fprintf(stderr, "Translating %s into %s\n", input_filename, output_filename);
258 }
259 
260 // Post-adjustments for VERBATIM blocks (i.e make them compatible with CPP).
261 void verbatim_adjust(char* q) {
262  Fprintf(fcout, "%s", q);
263 }
List * filetxtlist
Definition: modl.cpp:59
void verbatim_adjust(char *q)
Definition: modl.cpp:261
char * modprefix
Definition: modl.cpp:48
char finname[NRN_BUFSIZE]
Definition: modl.cpp:50
FILE * fin
Definition: modl.cpp:42
int nmodl_text
Definition: modl.cpp:58
#define assert(ex)
Definition: hocassrt.h:24
static int argc
Definition: inithoc.cpp:45
static char ** argv
Definition: inithoc.cpp:46
#define STR(q)
Definition: model.h:76
#define ITERATE(itm, lst)
Definition: model.h:18
#define IGNORE(arg)
Definition: model.h:224
#define NRN_BUFSIZE
Definition: model.h:6
FILE * fcout
Definition: modl.cpp:45
int vectorize
Definition: nocpout.cpp:78
int main(int argc, char **argv)
Definition: modl.cpp:70
const char * nmodl_version_
Definition: nocpout.cpp:12
int numlist
Definition: solve.cpp:33
int usederivstatearray
FILE * fparout
Definition: modl.cpp:44
int yyparse()
static void openfiles(const char *given_filename, const char *output_dir)
Definition: modl.cpp:211
void consistency()
Definition: consist.cpp:22
void init()
Definition: init.cpp:141
List * newlist()
The following routines support the concept of a list.
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
void netrec_discon()
void check_useion_variables()
Definition: nocpout.cpp:1534
void chk_thread_safe()
Definition: nocpout.cpp:3221
void chk_global_state()
Definition: nocpout.cpp:3235
void parout()
Definition: nocpout.cpp:207
void c_out()
Definition: noccout.cpp:69
void solvhandler()
Definition: solve.cpp:107
NMODL parser global flags / functions.
#define diag(s)
Definition: nonlin.cpp:19
size_t q
s
Definition: multisend.cpp:521
#define NULL
Definition: spdefs.h:105
Definition: model.h:8
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8