NEURON
main.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Blue Brain Project, EPFL.
3  * See the top-level LICENSE file for details.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <string>
9 #include <vector>
10 
11 #include <CLI/CLI.hpp>
12 #include <filesystem>
13 
14 #include "ast/program.hpp"
20 #include "config/config.h"
21 #include "parser/nmodl_driver.hpp"
22 #include "pybind/pyembed.hpp"
23 #include "utils/common_utils.hpp"
24 #include "utils/logger.hpp"
26 #include "visitors/ast_visitor.hpp"
59 
60 /**
61  * \dir
62  * \brief Main NMODL code generation program
63  */
64 
65 namespace fs = std::filesystem;
66 using namespace nmodl;
67 using namespace codegen;
68 using namespace visitor;
70 
71 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
72 int run_nmodl(int argc, const char* argv[]) {
73  CLI::App app{fmt::format("NMODL : Source-to-Source Code Generation Framework [{}]",
75 
76  /// list of mod files to process
77  std::vector<fs::path> mod_files;
78 
79  /// true if debug logger statements should be shown
80  std::string verbose("warning");
81 
82  /// true if code is to be generated for NEURON
83  bool neuron_code(false);
84 
85  /// true if code is to be generated for CoreNEURON
86  bool coreneuron_code(true);
87 
88  /// true if serial C++ code to be generated
89  bool cpp_backend(true);
90 
91  /// true if c code with openacc to be generated
92  bool oacc_backend(false);
93 
94  /// true if sympy should be used for solving ODEs analytically
95  bool sympy_analytic(false);
96 
97  /// true if Pade approximation to be used
98  bool sympy_pade(false);
99 
100  /// true if CSE (temp variables) to be used
101  bool sympy_cse(false);
102 
103  /// true if conductance keyword can be added to breakpoint
104  bool sympy_conductance(false);
105 
106  /// true if inlining at nmodl level to be done
107  bool nmodl_inline(false);
108 
109  /// true if unroll at nmodl level to be done
110  bool nmodl_unroll(false);
111 
112  /// true if perform constant folding at nmodl level to be done
113  bool nmodl_const_folding(false);
114 
115  /// true if range variables to be converted to local
116  bool nmodl_localize(false);
117 
118  /// true if global variables to be converted to range
119  bool nmodl_global_to_range(false);
120 
121  /// true if top level local variables to be converted to range
122  bool nmodl_local_to_range(false);
123 
124  /// true if CVODE should be emitted
125  bool codegen_cvode(false);
126 
127  /// true if localize variables even if verbatim block is used
128  bool localize_verbatim(false);
129 
130  /// true if local variables to be renamed
131  bool local_rename(true);
132 
133  /// true if inline even if verbatim block exist
134  bool verbatim_inline(false);
135 
136  /// true if verbatim blocks
137  bool verbatim_rename(true);
138 
139  /// true if code generation is forced to happen even if there
140  /// is any incompatibility
141  bool force_codegen(false);
142 
143  /// true if we want to only check compatibility without generating code
144  bool only_check_compatibility(false);
145 
146  /// true if ion variable copies should be avoided
147  bool optimize_ionvar_copies_codegen(false);
148 
149  /// directory where code will be generated
150  std::string output_dir(".");
151 
152  /// directory where intermediate file will be generated
153  std::string scratch_dir("tmp");
154 
155  /// directory where units lib file is located
156  std::string units_dir(NrnUnitsLib::get_path());
157 
158  /// true if ast should be converted to json
159  bool json_ast(false);
160 
161  /// true if ast should be converted to nmodl
162  bool nmodl_ast(false);
163 
164  /// true if performance stats should be converted to json
165  bool json_perfstat(false);
166 
167  /// true if symbol table should be printed
168  bool show_symtab(false);
169 
170  /// floating point data type
171  std::string data_type("double");
172 
173  /// which line to run blame for
174  size_t blame_line = 0; // lines are 1-based.
175  bool detailed_blame = false;
176 
177  // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
178  app.get_formatter()->column_width(40);
179  app.set_help_all_flag("-H,--help-all", "Print this help message including all sub-commands");
180 
181  app.add_option("--verbose", verbose, "Verbosity of logger output")
182  ->capture_default_str()
183  ->ignore_case()
184  ->check(CLI::IsMember({"trace", "debug", "info", "warning", "error", "critical", "off"}));
185 
186  app.add_option("file", mod_files, "One or more MOD files to process")
187  ->ignore_case()
188  ->required()
189  ->check(CLI::ExistingFile);
190 
191  app.add_option("-o,--output", output_dir, "Directory for backend code output")
192  ->capture_default_str()
193  ->ignore_case();
194  app.add_option("--scratch", scratch_dir, "Directory for intermediate code output")
195  ->capture_default_str()
196  ->ignore_case();
197  app.add_option("--units", units_dir, "Directory of units lib file")
198  ->capture_default_str()
199  ->ignore_case();
200  app.add_flag("--neuron", neuron_code, "Generate C++ code for NEURON");
201  app.add_flag("--coreneuron", coreneuron_code, "Generate C++ code for CoreNEURON (Default)");
202  app.add_flag(
203  "--version",
204  [](std::size_t count) {
205  std::cout << Version::to_string() << std::endl;
206  exit(0);
207  },
208  "Print the version and exit");
209  auto host_opt = app.add_subcommand("host", "HOST/CPU code backends")->ignore_case();
210  host_opt->add_flag("--c,--cpp", cpp_backend, fmt::format("C++ backend ({})", cpp_backend))
211  ->ignore_case();
212 
213  auto acc_opt = app.add_subcommand("acc", "Accelerator code backends")->ignore_case();
214  acc_opt
215  ->add_flag("--oacc",
216  oacc_backend,
217  fmt::format("C++ backend with OpenACC ({})", oacc_backend))
218  ->ignore_case();
219 
220  // clang-format off
221  auto sympy_opt = app.add_subcommand("sympy", "SymPy based analysis and optimizations")->ignore_case();
222  sympy_opt->add_flag("--analytic",
223  sympy_analytic,
224  fmt::format("Solve ODEs using SymPy analytic integration ({})", sympy_analytic))->ignore_case();
225  sympy_opt->add_flag("--pade",
226  sympy_pade,
227  fmt::format("Pade approximation in SymPy analytic integration ({})", sympy_pade))->ignore_case();
228  sympy_opt->add_flag("--cse",
229  sympy_cse,
230  fmt::format("CSE (Common Subexpression Elimination) in SymPy analytic integration ({})", sympy_cse))->ignore_case();
231  sympy_opt->add_flag("--conductance",
232  sympy_conductance,
233  fmt::format("Add CONDUCTANCE keyword in BREAKPOINT ({})", sympy_conductance))->ignore_case();
234 
235  auto passes_opt = app.add_subcommand("passes", "Analyse/Optimization passes")->ignore_case();
236  passes_opt->add_flag("--inline",
237  nmodl_inline,
238  fmt::format("Perform inlining at NMODL level ({})", nmodl_inline))->ignore_case();
239  passes_opt->add_flag("--unroll",
240  nmodl_unroll,
241  fmt::format("Perform loop unroll at NMODL level ({})", nmodl_unroll))->ignore_case();
242  passes_opt->add_flag("--const-folding",
243  nmodl_const_folding,
244  fmt::format("Perform constant folding at NMODL level ({})", nmodl_const_folding))->ignore_case();
245  passes_opt->add_flag("--localize",
246  nmodl_localize,
247  fmt::format("Convert RANGE variables to LOCAL ({})", nmodl_localize))->ignore_case();
248  passes_opt->add_flag("--global-to-range",
249  nmodl_global_to_range,
250  fmt::format("Convert GLOBAL variables to RANGE ({})", nmodl_global_to_range))->ignore_case();
251  passes_opt->add_flag("--local-to-range",
252  nmodl_local_to_range,
253  fmt::format("Convert top level LOCAL variables to RANGE ({})", nmodl_local_to_range))->ignore_case();
254  passes_opt->add_flag("--localize-verbatim",
255  localize_verbatim,
256  fmt::format("Convert RANGE variables to LOCAL even if verbatim block exist ({})", localize_verbatim))->ignore_case();
257  passes_opt->add_flag("--local-rename",
258  local_rename,
259  fmt::format("Rename LOCAL variable if variable of same name exist in global scope ({})", local_rename))->ignore_case();
260  passes_opt->add_flag("--verbatim-inline",
261  verbatim_inline,
262  fmt::format("Inline even if verbatim block exist ({})", verbatim_inline))->ignore_case();
263  passes_opt->add_flag("--verbatim-rename",
264  verbatim_rename,
265  fmt::format("Rename variables in verbatim block ({})", verbatim_rename))->ignore_case();
266  passes_opt->add_flag("--json-ast",
267  json_ast,
268  fmt::format("Write AST to JSON file ({})", json_ast))->ignore_case();
269  passes_opt->add_flag("--nmodl-ast",
270  nmodl_ast,
271  fmt::format("Write the intermediate AST after each pass as a NMODL file to the scratch directory ({})", nmodl_ast))->ignore_case();
272  passes_opt->add_flag("--json-perf",
273  json_perfstat,
274  fmt::format("Write performance statistics to JSON file ({})", json_perfstat))->ignore_case();
275  passes_opt->add_flag("--show-symtab",
276  show_symtab,
277  fmt::format("Write symbol table to stdout ({})", show_symtab))->ignore_case();
278 
279  auto codegen_opt = app.add_subcommand("codegen", "Code generation options")->ignore_case();
280  codegen_opt->add_option("--datatype",
281  data_type,
282  "Data type for floating point variables")->capture_default_str()->ignore_case()->check(CLI::IsMember({"float", "double"}));
283  codegen_opt->add_flag("--force",
284  force_codegen,
285  "Force code generation even if there is any incompatibility");
286  codegen_opt->add_flag("--only-check-compatibility",
287  only_check_compatibility,
288  "Check compatibility and return without generating code");
289  codegen_opt->add_flag("--opt-ionvar-copy",
290  optimize_ionvar_copies_codegen,
291  fmt::format("Optimize copies of ion variables ({})", optimize_ionvar_copies_codegen))->ignore_case();
292  codegen_opt->add_flag("--cvode",
293  codegen_cvode,
294  fmt::format("Print code for CVODE ({})", codegen_cvode))->ignore_case();
295 
296 #if NRN_USE_BACKWARD
297  auto blame_opt = app.add_subcommand("blame", "Blame NMODL code that generated some code.");
298  blame_opt->add_option("--line", blame_line, "Justify why this line was generated.");
299  blame_opt->add_flag("--detailed", detailed_blame, "Justify by printing full backtraces.");
300 #endif
301 
302  // clang-format on
303 
304  CLI11_PARSE(app, argc, argv);
305 
306  std::string simulator_name = neuron_code ? "neuron" : "coreneuron";
307  verbatim_rename = neuron_code ? false : verbatim_rename;
308 
309  fs::create_directories(output_dir);
310  fs::create_directories(scratch_dir);
311 
312  logger->set_level(spdlog::level::from_str(verbose));
313 
314  /// write ast to nmodl
315  const auto ast_to_nmodl = [nmodl_ast](ast::Program& ast, const std::string& filepath) {
316  if (nmodl_ast) {
317  NmodlPrintVisitor(filepath).visit_program(ast);
318  logger->info("AST to NMODL transformation written to {}", filepath);
319  }
320  };
321 
322  for (const auto& file: mod_files) {
323  logger->info("Processing {}", file.string());
324 
325  const auto modfile = file.stem().string();
326 
327  /// create file path for nmodl file
328  auto filepath = [scratch_dir, modfile](const std::string& suffix) {
329  static int count = 0;
330 
331  auto filename = fmt::format("{}.{:02d}.{}.mod", modfile, count++, suffix);
332  return (std::filesystem::path(scratch_dir) / filename).string();
333  };
334 
335  /// driver object creates lexer and parser, just call parser method
337 
338  /// parse mod file and construct ast
339  const auto& ast = driver.parse_file(file);
340 
341  /// whether to update existing symbol table or create new
342  /// one whenever we run symtab visitor.
343  bool update_symtab = false;
344 
345  {
346  logger->info("Running argument renaming visitor");
348  }
349 
350  /// merge all INITIAL blocks into one (this needs to run before SymtabVisitor)
351  {
352  logger->info("Running INITIAL block merge visitor");
354  .visit_program(*ast);
355  ast_to_nmodl(*ast, filepath("merge_initial_block"));
356  }
357 
358  /// merge all BREAKPOINT blocks into one (this needs to run before SymtabVisitor)
359  {
360  logger->info("Running BREAKPOINT block merge visitor");
362  .visit_program(*ast);
363  ast_to_nmodl(*ast, filepath("merge_breakpoint_block"));
364  }
365 
366  /// construct symbol table
367  {
368  logger->info("Running symtab visitor");
369  SymtabVisitor(update_symtab).visit_program(*ast);
370  }
371 
372  /// Check some rules that ast should follow
373  {
374  logger->info("Running semantic analysis visitor");
375  if (SemanticAnalysisVisitor(oacc_backend).check(*ast)) {
376  return 1;
377  }
378  }
379 
380  /// convert calls to `state_discontinuity(A, B)` in NET_RECEIVE blocks to `A = B`
381  {
382  logger->info("Running state discontinuity visitor");
384  ast_to_nmodl(*ast, filepath("state_discontinuity"));
385  }
386 
387 
388  /// use cnexp instead of after_cvode solve method
389  if (codegen_cvode) {
390  logger->info("Running CVode to cnexp visitor");
392  ast_to_nmodl(*ast, filepath("after_cvode_to_cnexp"));
393  }
394 
395  /// GLOBAL to RANGE rename visitor
396  if (nmodl_global_to_range) {
397  // make sure to run perf visitor because code generator
398  // looks for read/write counts const/non-const declaration
399  PerfVisitor().visit_program(*ast);
400  // make sure to run the GlobalToRange visitor after all the
401  // reinitializations of Symtab
402  logger->info("Running GlobalToRange visitor");
404  SymtabVisitor(update_symtab).visit_program(*ast);
405  ast_to_nmodl(*ast, filepath("global_to_range"));
406  }
407 
408  /// LOCAL to ASSIGNED visitor
409  if (nmodl_local_to_range) {
410  logger->info("Running LOCAL to ASSIGNED visitor");
411  PerfVisitor().visit_program(*ast);
413  SymtabVisitor(update_symtab).visit_program(*ast);
414  ast_to_nmodl(*ast, filepath("local_to_assigned"));
415  }
416 
417  {
418  // Compatibility Checking
419  logger->info("Running code compatibility checker");
420  // run perfvisitor to update read/write counts
421  PerfVisitor().visit_program(*ast);
422 
423  auto compatibility_visitor = CodegenCompatibilityVisitor(simulator_name);
424  // If we want to just check compatibility we return the result
425  if (only_check_compatibility) {
426  return compatibility_visitor.find_unhandled_ast_nodes(*ast);
427  }
428 
429  // If there is an incompatible construct and code generation is not forced exit NMODL
430  if (compatibility_visitor.find_unhandled_ast_nodes(*ast) && !force_codegen) {
431  return 1;
432  }
433  }
434 
435  if (show_symtab) {
436  logger->info("Printing symbol table");
437  auto symtab = ast->get_model_symbol_table();
438  symtab->print(std::cout);
439  }
440 
441  ast_to_nmodl(*ast, filepath("ast"));
442 
443  if (json_ast) {
444  std::filesystem::path file{scratch_dir};
445  file /= modfile + ".ast.json";
446  logger->info("Writing AST into {}", file.string());
447  JSONVisitor(file.string()).write(*ast);
448  }
449 
450  if (verbatim_rename) {
451  logger->info("Running verbatim rename visitor");
453  ast_to_nmodl(*ast, filepath("verbatim_rename"));
454  }
455 
456  if (nmodl_const_folding) {
457  logger->info("Running nmodl constant folding visitor");
459  ast_to_nmodl(*ast, filepath("constfold"));
460  }
461 
462  if (nmodl_unroll) {
463  logger->info("Running nmodl loop unroll visitor");
466  ast_to_nmodl(*ast, filepath("unroll"));
467  SymtabVisitor(update_symtab).visit_program(*ast);
468  }
469 
470  if (neuron_code) {
472  ast_to_nmodl(*ast, filepath("londifus"));
473  SymtabVisitor(update_symtab).visit_program(*ast);
474  }
475 
476  /// insert an explicit method to SOLVE blocks (if required)
477  {
478  logger->info("Running SOLVE without METHOD visitor");
480  ast_to_nmodl(*ast, filepath("solve_without_method"));
481  }
482 
483  if (solver_exists(*ast, "matexp")) {
484  logger->info("Running matexp solver visitor");
486  SymtabVisitor(update_symtab).visit_program(*ast);
487  ast_to_nmodl(*ast, filepath("matexp"));
488  }
489 
490  /// note that we can not symtab visitor in update mode as we
491  /// replace kinetic block with derivative block of same name
492  /// in global scope
493  {
494  logger->info("Running KINETIC block visitor");
495  auto kineticBlockVisitor = KineticBlockVisitor();
496  kineticBlockVisitor.visit_program(*ast);
497  SymtabVisitor(update_symtab).visit_program(*ast);
498  const auto filename = filepath("kinetic");
499  ast_to_nmodl(*ast, filename);
500  if (nmodl_ast && kineticBlockVisitor.get_conserve_statement_count()) {
501  logger->warn(
502  fmt::format("{} presents non-standard CONSERVE statements in DERIVATIVE "
503  "blocks. Use it only for debugging/developing",
504  filename));
505  }
506  }
507 
508  {
509  logger->info("Running STEADYSTATE visitor");
511  SymtabVisitor(update_symtab).visit_program(*ast);
512  ast_to_nmodl(*ast, filepath("steadystate"));
513  }
514 
515  /// Parsing units fron "nrnunits.lib" and mod files
516  {
517  logger->info("Parsing Units");
519  }
520 
521  /// once we start modifying (especially removing) older constructs
522  /// from ast then we should run symtab visitor in update mode so
523  /// that old symbols (e.g. prime variables) are not lost
524  update_symtab = true;
525 
526  if (nmodl_inline) {
527  logger->info("Running nmodl inline visitor");
529  SymtabVisitor(update_symtab).visit_program(*ast);
530  ast_to_nmodl(*ast, filepath("inline"));
531  }
532 
533  if (local_rename) {
534  logger->info("Running local variable rename visitor");
536  SymtabVisitor(update_symtab).visit_program(*ast);
537  ast_to_nmodl(*ast, filepath("local_rename"));
538  }
539 
540  if (nmodl_localize) {
541  // localize pass must follow rename pass to avoid conflict
542  logger->info("Running localize visitor");
543  LocalizeVisitor(localize_verbatim).visit_program(*ast);
545  SymtabVisitor(update_symtab).visit_program(*ast);
546  ast_to_nmodl(*ast, filepath("localize"));
547  }
548 
549  // Even if `sympy --analytic` wasn't requested by the user, some constructs can't be
550  // implemented without. If they're present we assume that SymPy is present; and force
551  // `sympy --analytic`.
552  if (!sympy_analytic) {
553  auto enable_sympy = [&sympy_analytic](bool enable, const std::string& reason) {
554  if (!enable) {
555  return;
556  }
557 
558  if (!sympy_analytic) {
559  logger->info("Automatically enabling sympy_analytic.");
560  logger->info("Required by: {}.", reason);
561  }
562 
563  sympy_analytic = true;
564  };
565 
566  enable_sympy(solver_exists(*ast, "derivimplicit"), "'SOLVE ... METHOD derivimplicit'");
567  enable_sympy(node_exists(*ast, ast::AstNodeType::LINEAR_BLOCK), "'LINEAR' block");
568  enable_sympy(neuron_code && node_exists(*ast, ast::AstNodeType::DERIVATIVE_BLOCK),
569  "'DERIVATIVE' block");
571  "'NONLINEAR' block");
572  enable_sympy(solver_exists(*ast, "sparse"), "'SOLVE ... METHOD sparse'");
573  }
574 
575 
576  if (sympy_conductance || sympy_analytic) {
578  .api()
580 
581  if (neuron_code && codegen_cvode) {
582  logger->info("Running CVODE visitor");
583  CvodeVisitor().visit_program(*ast);
584  SymtabVisitor(update_symtab).visit_program(*ast);
585  ast_to_nmodl(*ast, filepath("cvode"));
586  }
587 
588  if (sympy_conductance) {
589  logger->info("Running sympy conductance visitor");
591  SymtabVisitor(update_symtab).visit_program(*ast);
592  ast_to_nmodl(*ast, filepath("sympy_conductance"));
593  }
594 
595  if (sympy_analytic) {
596  logger->info("Running sympy solve visitor");
597  SympySolverVisitor(sympy_pade, sympy_cse).visit_program(*ast);
598  SymtabVisitor(update_symtab).visit_program(*ast);
599  ast_to_nmodl(*ast, filepath("sympy_solve"));
600  }
602  .api()
604  }
605 
606  {
607  logger->info("Running cnexp visitor");
609  ast_to_nmodl(*ast, filepath("cnexp"));
610  }
611 
612  {
614  SymtabVisitor(update_symtab).visit_program(*ast);
615  ast_to_nmodl(*ast, filepath("solveblock"));
616  }
617 
618  if (json_perfstat) {
619  std::string file{scratch_dir};
620  file.append("/");
621  file.append(modfile);
622  file.append(".perf.json");
623  logger->info("Writing performance statistics to {}", file);
624  PerfVisitor(file).visit_program(*ast);
625  }
626 
627  // Add implicit arguments (like celsius, nt) to NEURON functions (like
628  // nrn_ghk, at_time) whose signatures we have to massage.
629  ImplicitArgumentVisitor{simulator_name}.visit_program(*ast);
630  SymtabVisitor(update_symtab).visit_program(*ast);
631 
632  {
633  // make sure to run perf visitor because code generator
634  // looks for read/write counts const/non-const declaration
635  PerfVisitor().visit_program(*ast);
636  }
637 
638  {
640  ast_to_nmodl(*ast, filepath("TransformVisitor"));
641  SymtabVisitor(update_symtab).visit_program(*ast);
642  }
643 
644  {
646  ast_to_nmodl(*ast, filepath("FunctionCallpathVisitor"));
647  SymtabVisitor(update_symtab).visit_program(*ast);
648  }
649 
650  {
651  auto output_stream = std::ofstream(std::filesystem::path(output_dir) /
652  (modfile + ".cpp"));
653  auto blame_level = detailed_blame ? utils::BlameLevel::Detailed
655  if (coreneuron_code && oacc_backend) {
656  logger->info("Running OpenACC backend code generator for CoreNEURON");
657  CodegenAccVisitor visitor(modfile,
658  output_stream,
659  data_type,
660  optimize_ionvar_copies_codegen,
661  utils::make_blame(blame_line, blame_level));
662  visitor.visit_program(*ast);
663  }
664 
665  else if (coreneuron_code && !neuron_code && cpp_backend) {
666  logger->info("Running C++ backend code generator for CoreNEURON");
667  CodegenCoreneuronCppVisitor visitor(modfile,
668  output_stream,
669  data_type,
670  optimize_ionvar_copies_codegen,
671  utils::make_blame(blame_line, blame_level));
672  visitor.visit_program(*ast);
673  }
674 
675  else if (neuron_code && cpp_backend) {
676  logger->info("Running C++ backend code generator for NEURON");
677  CodegenNeuronCppVisitor visitor(modfile,
678  output_stream,
679  data_type,
680  optimize_ionvar_copies_codegen,
681  codegen_cvode,
682  utils::make_blame(blame_line, blame_level));
683  visitor.visit_program(*ast);
684  }
685 
686  else {
687  throw std::runtime_error(
688  "Non valid code generation configuration. Code generation with NMODL is "
689  "supported for NEURON with C++ backend or CoreNEURON with C++/OpenACC "
690  "backends");
691  }
692  }
693  }
694  return EXIT_SUCCESS;
695 }
696 
697 int main(int argc, const char* argv[]) {
698  try {
699  return run_nmodl(argc, argv);
700  } catch (const std::runtime_error& e) {
701  std::cerr << "[FATAL] NMODL encountered an unhandled exception.\n";
702  std::cerr << " cwd = " << std::filesystem::current_path() << "\n";
703  std::cerr << " ";
704  for (int i = 0; i < argc; ++i) {
705  std::cerr << argv[i] << " ";
706  }
707  std::cerr << std::endl;
708 
709  throw e;
710  }
711 
712  return EXIT_SUCCESS;
713 }
Visitor to change usage of after_cvode solver to cnexp.
Concrete visitor for all AST classes.
Visitor to make last transformation to AST before codegen.
Represents top level AST node for whole NMODL input.
Definition: program.hpp:39
symtab::ModelSymbolTable * get_model_symbol_table()
Return global symbol table for the mod file.
Definition: program.hpp:159
Class that binds all pieces together for parsing nmodl file.
static EmbeddedPythonLoader & get_instance()
Construct (if not already done) and get the only instance of this class.
Definition: pyembed.hpp:29
const pybind_wrap_api & api()
Get a pointer to the pybind_wrap_api struct.
Definition: pyembed.cpp:136
void print(std::ostream &ostr) const
pretty print
Visitor to change usage of after_cvode solver to cnexp.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Perform constant folding of integer/float/double expressions.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor used for generating the necessary AST nodes for CVODE.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for traversing FunctionBlock s and ProcedureBlocks through their FunctionCall s
void visit_program(const ast::Program &node) override
visit node of type ast::Program
Visitor to convert GLOBAL variables to RANGE variables.
Visitor to inline local procedure and function calls
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for printing AST in JSON format
JSONVisitor & write(const ast::Program &program)
Visitor for kinetic block statements
Visitor to convert top level LOCAL variables to ASSIGNED variables.
void visit_program(ast::Program &node) override
Visit ast::Program node to transform top level LOCAL variables to ASSIGNED if they are written in the...
Visitor to rename local variables conflicting with global scope
Visitor to transform global variable usage to local
void visit_program(const ast::Program &node) override
visit node of type ast::Program
Visitor used for generating the necessary AST nodes for matexp solver.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor which merges given top-level blocks into one.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor that solves ODEs using old solvers of NEURON
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for printing AST back to NMODL
void visit_program(const ast::Program &node) override
visit node of type ast::Program
Visitor for measuring performance related information
void visit_program(const ast::Program &node) override
visit node of type ast::Program
Visitor to check some semantic rules on the AST
Replace solve block statements with actual solution node in the AST.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for adding an explicit method to a SOLVE block which has an implicit one
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor used for replacing literal calls to state_discontinuity in a NET_RECEIVE block.
Visitor for STEADYSTATE solve statements
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for generating CONDUCTANCE statements for ions
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for systems of algebraic and differential equations
void visit_program(ast::Program &node) override
visit node of type ast::Program
Concrete visitor for constructing symbol table from AST.
void visit_program(ast::Program &node) override
visit node of type ast::Program
Visitor for Units blocks of AST.
void visit_program(ast::Program &node) override
Override visit_program function to parse the contents of the nrnunits.lib unit file before starting v...
Visitor for printing C++ code with OpenACC backend
Visitor for printing compatibility issues of the mod file
Visitor for printing C++ code compatible with legacy api of CoreNEURON
Visitor for printing C++ code compatible with legacy api of NEURON
Common utility functions for file/dir manipulation.
Perform constant folding of integer/float/double expressions.
#define i
Definition: md1redef.h:19
Visitor used for generating the necessary AST nodes for CVODE.
Visitor for traversing FunctionBlock s and ProcedureBlocks through their FunctionCall s
Visitor to convert GLOBAL variables to RANGE variables.
@ DERIVATIVE_BLOCK
type of ast::DerivativeBlock
@ NON_LINEAR_BLOCK
type of ast::NonLinearBlock
@ LINEAR_BLOCK
type of ast::LinearBlock
bool parse_file(const std::string &filename)
parse Units file
Definition: unit_driver.cpp:29
Visitor for adding implicit arguments to [Core]NEURON functions.
Get node name with indexed for the IndexedName node and the dependencies of DiffEqExpression node.
static int argc
Definition: inithoc.cpp:45
static char ** argv
Definition: inithoc.cpp:46
Visitor to inline local procedure and function calls
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.
Visitor for kinetic block statements
Visitor to convert top level LOCAL variables to ASSIGNED variables.
Visitor to rename local variables conflicting with global scope
Visitor to transform global variable usage to local
Unroll for loop in the AST.
Visitor used for generating the necessary AST nodes for matexp solver.
Visitor which merges given top-level blocks into one.
static void check(VecTNode &)
Definition: cellorder1.cpp:401
std::unique_ptr< Blame > make_blame(size_t blame_line, BlameLevel blame_level)
Definition: blame.cpp:128
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
logger_type logger
Definition: logger.cpp:34
bool node_exists(const ast::Ast &node, ast::AstNodeType ast_type)
Whether a node of type ast_type exists as a subnode of node.
bool solver_exists(const ast::Ast &node, const std::string &name)
Whether or not a solver of type name exists in the AST.
Visitor that solves ODEs using old solvers of NEURON
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.
static char suffix[256]
Definition: nocpout.cpp:135
Visitor for measuring performance related information
Auto generated AST classes declaration.
Visitor to check some semantic rules on the AST
Replace solve block statements with actual solution node in the AST.
Visitor for adding an explicit method to a SOLVE block which has an implicit one
int main(int argc, const char *argv[])
Definition: main.cpp:697
int run_nmodl(int argc, const char *argv[])
Definition: main.cpp:72
Visitor used for replacing literal calls to state_discontinuity in a NET_RECEIVE block.
Visitor for STEADYSTATE solve statements
static std::string get_path()
Return path of units database file.
Definition: config.h:72
static std::string get_content(const std::string &path)
Return content of units database file.
Definition: config.h:58
static std::string to_string()
return version string (version + git id) as a string
Definition: config.h:41
decltype(&initialize_interpreter_func) initialize_interpreter
Definition: wrapper.hpp:61
decltype(&finalize_interpreter_func) finalize_interpreter
Definition: wrapper.hpp:62
Visitor for adding implicit arguments to [Core]NEURON functions.
Visitor for generating CONDUCTANCE statements for ions
Visitor for systems of algebraic and differential equations
THIS FILE IS GENERATED AT BUILD TIME AND SHALL NOT BE EDITED.
nmodl::parser::UnitDriver driver
Definition: parser.cpp:28
Visitor for Units blocks of AST.
Rename variable in verbatim block.
Visitor for verbatim blocks of AST
Utility functions for visitors implementation.