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