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