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"
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 NRN_USE_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");
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");
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");
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);
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");
425  ast_to_nmodl(*ast, filepath("verbatim_rename"));
426  }
427 
428  if (nmodl_const_folding) {
429  logger->info("Running nmodl constant folding visitor");
431  ast_to_nmodl(*ast, filepath("constfold"));
432  }
433 
434  if (nmodl_unroll) {
435  logger->info("Running nmodl loop unroll visitor");
438  ast_to_nmodl(*ast, filepath("unroll"));
439  SymtabVisitor(update_symtab).visit_program(*ast);
440  }
441 
442  if (neuron_code) {
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");
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");
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");
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);
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");
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");
568  ast_to_nmodl(*ast, filepath("cnexp"));
569  }
570 
571  {
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  {
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 }
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 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 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:656
int run_nmodl(int argc, const char *argv[])
Definition: main.cpp:68
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.