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