User Guide
nmodl_driver.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 <filesystem>
9 #include <fstream>
10 #include <sstream>
11 
12 #include "lexer/nmodl_lexer.hpp"
13 #include "parser/nmodl_driver.hpp"
14 #include "utils/logger.hpp"
15 
16 namespace fs = std::filesystem;
17 
18 namespace nmodl {
19 namespace parser {
20 
21 NmodlDriver::NmodlDriver(bool strace, bool ptrace)
22  : trace_scanner(strace)
23  , trace_parser(ptrace) {}
24 
25 /// parse nmodl file provided as istream
26 std::shared_ptr<ast::Program> NmodlDriver::parse_stream(std::istream& in) {
27  NmodlLexer scanner(*this, &in);
28  NmodlParser parser(scanner, *this);
29 
30  parser_stream << "parser trace:" << std::endl;
31 
32  scanner.set_debug(trace_scanner);
33  parser.set_debug_level(trace_parser);
34  parser.set_debug_stream(parser_stream);
35 
36  parser.parse();
37 
38  logger->trace(parser_stream.str());
39 
40  return astRoot;
41 }
42 
43 std::shared_ptr<ast::Program> NmodlDriver::parse_file(const fs::path& filename,
44  const location* loc) {
45  if (fs::is_directory(filename)) {
46  throw std::runtime_error("NMODL Parser Error : path " + filename.string() +
47  " appears to be a directory, please provide a file instead");
48  }
49  std::ifstream in(filename);
50  if (!in.good()) {
51  std::ostringstream oss;
52  if (loc == nullptr) {
53  oss << "NMODL Parser Error : ";
54  }
55  oss << "can not open file : " << filename;
56  if (loc != nullptr) {
57  parse_error(*loc, oss.str());
58  } else {
59  throw std::runtime_error(oss.str());
60  }
61  }
62 
63  auto current_stream_name = stream_name;
64  stream_name = filename.string();
65  auto absolute_path = fs::absolute(filename);
66  {
67  if (filename.is_absolute()) {
68  const auto path_prefix = filename.parent_path();
69  library.push_current_directory(path_prefix);
70  absolute_path = filename.string();
71  } else if (!filename.has_parent_path()) {
72  library.push_current_directory(fs::current_path());
73  } else {
74  const auto path_prefix = filename.parent_path();
75  const auto path = fs::absolute(path_prefix);
77  }
78  }
79 
80  open_files.emplace(absolute_path.string(), loc);
81  parse_stream(in);
82  open_files.erase(absolute_path.string());
84  stream_name = current_stream_name;
85 
86  return astRoot;
87 }
88 
89 std::shared_ptr<ast::Program> NmodlDriver::parse_string(const std::string& input) {
90  std::istringstream iss(input);
91  parse_stream(iss);
92  return astRoot;
93 }
94 
95 std::shared_ptr<ast::Include> NmodlDriver::parse_include(const fs::path& name,
96  const location& loc) {
97  if (name.empty()) {
98  parse_error(loc, "empty filename");
99  }
100 
101  // Try to find directory containing the file to import
102  const auto directory_path = fs::path{library.find_file(name)};
103 
104  // Complete path of file (directory + filename).
105  auto absolute_path = name;
106 
107  if (!directory_path.empty()) {
108  absolute_path = directory_path / name;
109  }
110 
111  // Detect recursive inclusion.
112  auto already_included = open_files.find(absolute_path.string());
113  if (already_included != open_files.end()) {
114  std::ostringstream oss;
115  oss << name << ": recursive inclusion.\n";
116  if (already_included->second != nullptr) {
117  oss << *already_included->second << ": initial inclusion was here.";
118  }
119  parse_error(loc, oss.str());
120  }
121 
122  std::shared_ptr<ast::Program> program;
123  program.swap(astRoot);
124 
125  parse_file(absolute_path, &loc);
126 
127  program.swap(astRoot);
128  auto filename_node = std::shared_ptr<ast::String>(
129  new ast::String(fmt::format("\"{}\"", name.string())));
130  return std::shared_ptr<ast::Include>(new ast::Include(filename_node, program->get_blocks()));
131 }
132 
133 void NmodlDriver::add_defined_var(const std::string& name, int value) {
134  defined_var[name] = value;
135 }
136 
137 bool NmodlDriver::is_defined_var(const std::string& name) const {
138  return !(defined_var.find(name) == defined_var.end());
139 }
140 
141 int NmodlDriver::get_defined_var_value(const std::string& name) const {
142  const auto var_it = defined_var.find(name);
143  if (var_it != defined_var.end()) {
144  return var_it->second;
145  }
146  throw std::runtime_error("Trying to get undefined macro / define :" + name);
147 }
148 
149 void NmodlDriver::parse_error(const location& location, const std::string& message) {
150  std::ostringstream oss;
151  oss << "NMODL Parser Error : " << message << " [Location : " << location << "]";
152  throw std::runtime_error(oss.str());
153 }
154 
156  const location& location,
157  const std::string& message) {
158  std::ostringstream oss;
159  oss << "NMODL Parser Error : " << message << " [Location : " << location << "]";
160  oss << scanner.get_curr_line() << '\n';
161  oss << std::string(location.begin.column - 1, '-');
162  oss << "^\n";
163  // output the logs if running with `trace` verbosity
164  logger->trace(parser_stream.str());
165  throw std::runtime_error(oss.str());
166 }
167 
168 std::string NmodlDriver::check_include_argument(const location& location,
169  const std::string& filename) {
170  if (filename.empty()) {
171  parse_error(location, "empty filename in INCLUDE directive");
172  } else if (filename.front() != '"' && filename.back() != '"') {
173  parse_error(location, "filename may start and end with \" character");
174  } else if (filename.size() == 3) {
175  parse_error(location, "filename is empty");
176  }
177  return filename.substr(1, filename.size() - 2);
178 }
179 
180 } // namespace parser
181 } // namespace nmodl
nmodl::parser::NmodlDriver::NmodlDriver
NmodlDriver()=default
nmodl::ast::Include
Represents an INCLUDE statement in NMODL.
Definition: include.hpp:39
nmodl::FileLibrary::find_file
std::string find_file(const std::filesystem::path &file)
Search a file.
Definition: file_library.cpp:54
nmodl::FileLibrary::push_current_directory
void push_current_directory(const std::filesystem::path &path)
Definition: file_library.cpp:39
nmodl::parser::NmodlDriver::parse_include
std::shared_ptr< ast::Include > parse_include(const std::filesystem::path &filename, const location &loc)
Definition: nmodl_driver.cpp:95
nmodl::parser::NmodlDriver::stream_name
std::string stream_name
file or input stream name (used by scanner for position), see todo
Definition: nmodl_driver.hpp:96
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
nmodl::parser::NmodlDriver::parser_stream
std::ostringstream parser_stream
The stream where Bison will dump its logs.
Definition: nmodl_driver.hpp:92
nmodl::parser::NmodlDriver::trace_scanner
bool trace_scanner
enable debug output in the flex scanner
Definition: nmodl_driver.hpp:73
nmodl::parser::NmodlDriver::get_defined_var_value
int get_defined_var_value(const std::string &name) const
return variable's value defined as macro (always an integer)
Definition: nmodl_driver.cpp:141
nmodl::parser::NmodlDriver::add_defined_var
void add_defined_var(const std::string &name, int value)
add macro definition and it's value (DEFINE keyword of nmodl)
Definition: nmodl_driver.cpp:133
nmodl::parser::NmodlDriver::parse_stream
std::shared_ptr< ast::Program > parse_stream(std::istream &in)
parse nmodl file provided as istream
Definition: nmodl_driver.cpp:26
nmodl::logger
logger_type logger
Definition: logger.cpp:34
nmodl::parser::NmodlDriver::check_include_argument
std::string check_include_argument(const location &location, const std::string &filename)
Ensure file argument given to the INCLUDE directive is valid:
Definition: nmodl_driver.cpp:168
nmodl_lexer.hpp
nmodl::parser::NmodlLexer::set_debug
void set_debug(bool b)
Enable debug output (via yyout) if compiled into the scanner.
nmodl::parser::NmodlDriver::parse_file
std::shared_ptr< ast::Program > parse_file(const std::filesystem::path &filename, const location *loc=nullptr)
parse NMODL file
Definition: nmodl_driver.cpp:43
nmodl::parser::NmodlDriver::parse_error
void parse_error(const location &location, const std::string &message)
Emit a parsing error.
Definition: nmodl_driver.cpp:149
nmodl::parser::NmodlLexer
Represent Lexer/Scanner class for NMODL language parsing.
Definition: nmodl_lexer.hpp:60
nmodl::parser::NmodlDriver::trace_parser
bool trace_parser
enable debug output in the bison parser
Definition: nmodl_driver.hpp:76
nmodl::parser::NmodlDriver::is_defined_var
bool is_defined_var(const std::string &name) const
check if particular text is defined as macro
Definition: nmodl_driver.cpp:137
nmodl::FileLibrary::pop_current_directory
void pop_current_directory()
Definition: file_library.cpp:47
nmodl::parser::NmodlLexer::get_curr_line
std::string get_curr_line() const
Return current line as string.
nmodl::parser::NmodlDriver::library
FileLibrary library
The file library for IMPORT directives.
Definition: nmodl_driver.hpp:85
nmodl::parser::NmodlDriver::parse_string
std::shared_ptr< ast::Program > parse_string(const std::string &input)
parser nmodl provided as string (used for testing)
Definition: nmodl_driver.cpp:89
logger.hpp
Implement logger based on spdlog library.
nmodl::parser::NmodlDriver::astRoot
std::shared_ptr< ast::Program > astRoot
root of the ast
Definition: nmodl_driver.hpp:82
nmodl_driver.hpp
nmodl::parser::NmodlDriver::defined_var
std::unordered_map< std::string, int > defined_var
all macro defined in the mod file
Definition: nmodl_driver.hpp:70
nmodl::parser::NmodlDriver::open_files
std::unordered_map< std::string, const location * > open_files
The list of open files, and the location of the request.
Definition: nmodl_driver.hpp:89
nmodl::ast::String
Represents a string.
Definition: string.hpp:52