ArkScript
A small, fast, functional and scripting language for video games
Parser.cpp
Go to the documentation of this file.
2 
3 #include <optional>
4 #include <algorithm>
5 #include <sstream>
6 
7 #include <Ark/Files.hpp>
8 #include <Ark/Exceptions.hpp>
10 
11 namespace Ark::internal
12 {
13  Parser::Parser(unsigned debug, uint16_t options, const std::vector<std::string>& lib_env) noexcept :
14  m_debug(debug),
15  m_libenv(lib_env),
16  m_options(options),
17  m_lexer(debug),
18  m_file(ARK_NO_NAME_FILE)
19  {}
20 
21  void Parser::feed(const std::string& code, const std::string& filename)
22  {
23  // not the default value
24  if (filename != ARK_NO_NAME_FILE)
25  {
26  m_file = Utils::canonicalRelPath(filename);
27  if (m_debug >= 2)
28  std::cout << "New parser: " << m_file << '\n';
29  m_parent_include.push_back(m_file);
30  }
31 
32  m_code = code;
33 
34  m_lexer.feed(code);
35  // apply syntactic sugar
36  std::vector<Token>& t = m_lexer.tokens();
37  if (t.empty())
38  throw ParseError("empty file");
39  sugar(t);
40 
41  // create program
42  std::list<Token> tokens(t.begin(), t.end());
43  m_last_token = tokens.front();
44 
45  // accept every nodes in the file
48  m_ast.list().emplace_back(Keyword::Begin);
49  while (!tokens.empty())
50  m_ast.list().push_back(parse(tokens));
51  // include files if needed
53 
54  if (m_debug >= 3)
55  std::cout << "(Parser) AST\n"
56  << m_ast << "\n\n";
57  }
58 
59  const Node& Parser::ast() const noexcept
60  {
61  return m_ast;
62  }
63 
64  const std::vector<std::string>& Parser::getImports() const noexcept
65  {
66  return m_parent_include;
67  }
68 
69  void Parser::sugar(std::vector<Token>& tokens) noexcept
70  {
71  std::size_t i = 0;
72  while (true)
73  {
74  std::size_t line = tokens[i].line;
75  std::size_t col = tokens[i].col;
76 
77  if (tokens[i].token == "{")
78  {
79  tokens[i] = Token(TokenType::Grouping, "(", line, col);
80  // handle macros
81  if (i > 0 && tokens[i - 1].token != "!")
82  tokens.insert(tokens.begin() + i + 1, Token(TokenType::Keyword, "begin", line, col));
83  else if (i == 0)
84  tokens.insert(tokens.begin() + i + 1, Token(TokenType::Keyword, "begin", line, col));
85  }
86  else if (tokens[i].token == "}" || tokens[i].token == "]")
87  tokens[i] = Token(TokenType::Grouping, ")", line, col);
88  else if (tokens[i].token == "[")
89  {
90  tokens[i] = Token(TokenType::Grouping, "(", line, col);
91  tokens.insert(tokens.begin() + i + 1, Token(TokenType::Identifier, "list", line, col));
92  }
93 
94  ++i;
95 
96  if (i == tokens.size())
97  break;
98  }
99  }
100 
101  // sugar() was called before, so it's safe to assume we only have ( and )
102  Node Parser::parse(std::list<Token>& tokens, bool authorize_capture, bool authorize_field_read, bool in_macro)
103  {
104  using namespace std::string_literals;
105 
106  Token token = nextToken(tokens);
107 
108  bool previous_token_was_lparen = false;
109 
110  // parse block
111  if (token.token == "(")
112  {
113  previous_token_was_lparen = true;
114  // create a list node to host the block
115  Node block = make_node_list(token.line, token.col, m_file);
116 
117  // handle sub-blocks
118  if (tokens.front().token == "(")
119  {
120  block.push_back(parse(tokens, false, false, in_macro));
121  previous_token_was_lparen = false;
122  }
123 
124  // take next token, we don't want to play with a "("
125  token = nextToken(tokens);
126 
127  // return an empty block
128  if (token.token == ")")
129  return block;
130 
131  // check for unexpected keywords between expressions
132  if ((token.type == TokenType::Operator ||
133  token.type == TokenType::Identifier ||
134  token.type == TokenType::Number ||
135  token.type == TokenType::String) &&
136  tokens.front().type == TokenType::Keyword)
137  throwParseError("Unexpected keyword `" + tokens.front().token + "' in the middle of an expression", tokens.front());
138 
139  // loop until we reach the end of the block
140  do
141  {
142  Node atomized = atom(token);
143  checkForInvalidTokens(atomized, token, previous_token_was_lparen, authorize_capture, authorize_field_read);
144  block.push_back(atomized);
145 
146  expect(!tokens.empty(), "expected more tokens after `" + token.token + "'", m_last_token);
147  m_last_token = tokens.front();
148 
149  if (token.type == TokenType::Keyword)
150  {
151  if (token.token == "if")
152  parseIf(block, tokens, in_macro);
153  else if (token.token == "let" || token.token == "mut")
154  parseLetMut(block, token, tokens, in_macro);
155  else if (token.token == "set")
156  parseSet(block, token, tokens, in_macro);
157  else if (token.token == "fun")
158  parseFun(block, token, tokens, in_macro);
159  else if (token.token == "while")
160  parseWhile(block, token, tokens, in_macro);
161  else if (token.token == "begin")
162  parseBegin(block, tokens, in_macro);
163  else if (token.token == "import")
164  parseImport(block, tokens);
165  else if (token.token == "quote")
166  parseQuote(block, tokens, in_macro);
167  else if (token.token == "del")
168  parseDel(block, tokens);
169  else
170  throwParseError("unimplemented keyword `" + token.token + "'. If you see this error please report it on GitHub.", token);
171  }
172  else if (token.type == TokenType::Identifier || token.type == TokenType::Operator ||
173  (token.type == TokenType::Capture && authorize_capture) ||
174  (token.type == TokenType::GetField && authorize_field_read) ||
175  (token.type == TokenType::Spread && in_macro))
176  {
177  while (tokens.front().token != ")")
178  block.push_back(parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ true, in_macro));
179  }
180  } while (tokens.front().token != ")");
181 
182  // pop the ")"
183  tokens.pop_front();
184  return block;
185  }
186  else if (token.type == TokenType::Shorthand)
187  return parseShorthand(token, tokens, in_macro);
188  // error, we shouldn't have grouping token here
189  else if (token.type == TokenType::Grouping)
190  throwParseError("Found a lonely `" + token.token + "', you most likely have too much parenthesis.", token);
191  else if ((token.type == TokenType::Operator || token.type == TokenType::Identifier) &&
192  std::find(internal::operators.begin(), internal::operators.end(), token.token) != internal::operators.end())
193  throwParseError("Found a free flying operator, which isn't authorized. Operators should always immediatly follow a `('.", token);
194  else if ((token.type == TokenType::Number ||
195  token.type == TokenType::String) &&
196  tokens.front().type == TokenType::Keyword)
197  throwParseError("Unexpected keyword `" + tokens.front().token + "' in the middle of an expression", tokens.front());
198  else if (token.type == TokenType::Keyword &&
199  !previous_token_was_lparen)
200  throwParseError("Unexpected keyword `" + token.token + "' in the middle of an expression", token);
201  return atom(token);
202  }
203 
204  void Parser::parseIf(Node& block, std::list<Token>& tokens, bool in_macro)
205  {
206  auto temp = tokens.front();
207  // parse condition
208  if (temp.type == TokenType::Grouping)
209  block.push_back(parse(tokens, false, false, in_macro));
210  else if (temp.type == TokenType::Identifier || temp.type == TokenType::Number ||
211  temp.type == TokenType::String || (in_macro && temp.type == TokenType::Spread))
212  block.push_back(atom(nextToken(tokens)));
213  else
214  throwParseError("found invalid token after keyword `if', expected function call, value or Identifier", temp);
215  // parse 'then'
216  expect(!tokens.empty() && tokens.front().token != ")", "expected a statement after the condition", temp);
217  block.push_back(parse(tokens, false, false, in_macro));
218  // parse 'else', if there is one
219  if (tokens.front().token != ")")
220  {
221  block.push_back(parse(tokens, false, false, in_macro));
222  // error handling if the if is ill-formed
223  expect(tokens.front().token == ")", "if block is ill-formed, got more than the 3 required arguments (condition, then, else)", m_last_token);
224  }
225  }
226 
227  void Parser::parseLetMut(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
228  {
229  auto temp = tokens.front();
230  // parse identifier
231  if (temp.type == TokenType::Identifier)
232  block.push_back(atom(nextToken(tokens)));
233  else if (in_macro)
234  block.push_back(parse(tokens, false, false, in_macro));
235  else
236  throwParseError(std::string("missing identifier to define a ") + (token.token == "let" ? "constant" : "variable") + ", after keyword `" + token.token + "'", temp);
237  expect(!tokens.empty() && tokens.front().token != ")", "expected a value after the identifier", temp);
238  // value
239  while (tokens.front().token != ")")
240  block.push_back(parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ true, in_macro));
241 
242  // the block size can exceed 3 only if we have a serie of getfields
243  expect(
244  block.list().size() <= 3 || std::all_of(block.list().begin() + 3, block.list().end(), [](const Node& n) -> bool {
245  return n.nodeType() == NodeType::GetField;
246  }),
247  "too many arguments given to keyword `" + token.token + "', got " + std::to_string(block.list().size() - 1) + ", expected at most 3", m_last_token);
248  }
249 
250  void Parser::parseSet(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
251  {
252  auto temp = tokens.front();
253  // parse identifier
254  if (temp.type == TokenType::Identifier)
255  block.push_back(atom(nextToken(tokens)));
256  else if (in_macro)
257  block.push_back(parse(tokens, false, false, in_macro));
258  else
259  throwParseError("missing identifier to assign a value to, after keyword `set'", temp);
260  expect(!tokens.empty() && tokens.front().token != ")", "expected a value after the identifier", temp);
261  // set can not accept a.b...c as an identifier
262  if (tokens.front().type == TokenType::GetField)
263  throwParseError("found invalid token after keyword `set', expected an identifier, got a closure field reading expression", tokens.front());
264  // value
265  while (tokens.front().token != ")")
266  block.push_back(parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ true, in_macro));
267 
268  // the block size can exceed 3 only if we have a serie of getfields
269  expect(
270  block.list().size() <= 3 || std::all_of(block.list().begin() + 3, block.list().end(), [](const Node& n) -> bool {
271  return n.nodeType() == NodeType::GetField;
272  }),
273  "too many arguments given to keyword `" + token.token + "', got " + std::to_string(block.list().size() - 1) + ", expected at most 3", m_last_token);
274  }
275 
276  void Parser::parseFun(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
277  {
278  // parse arguments
279  if (tokens.front().type == TokenType::Grouping || in_macro)
280  block.push_back(parse(tokens, /* authorize_capture */ true, false, in_macro));
281  else
282  throwParseError("found invalid token after keyword `fun', expected a block to define the argument list of the function\nThe block can be empty if it doesn't have arguments: `()'", tokens.front());
283  // parse body
284  if (tokens.front().type == TokenType::Grouping || in_macro)
285  block.push_back(parse(tokens, false, false, in_macro));
286  else
287  throwParseError("the body of a function must be a block, even an empty one `()'", tokens.front());
288  expect(block.list().size() == 3, "got too many arguments after keyword `" + token.token + "', expected an argument list and a body", m_last_token);
289  }
290 
291  void Parser::parseWhile(Node& block, Token& token, std::list<Token>& tokens, bool in_macro)
292  {
293  auto temp = tokens.front();
294  // parse condition
295  if (temp.type == TokenType::Grouping)
296  block.push_back(parse(tokens, false, false, in_macro));
297  else if (temp.type == TokenType::Identifier || temp.type == TokenType::Number ||
298  temp.type == TokenType::String)
299  block.push_back(atom(nextToken(tokens)));
300  else
301  throwParseError("found invalid token after keyword `while', expected function call, value or Identifier", temp);
302  expect(!tokens.empty() && tokens.front().token != ")", "expected a body after the condition", temp);
303  // parse 'do'
304  block.push_back(parse(tokens, false, false, in_macro));
305  expect(block.list().size() == 3, "got too many arguments after keyword `" + token.token + "', expected a condition and a body", temp);
306  }
307 
308  void Parser::parseBegin(Node& block, std::list<Token>& tokens, bool in_macro)
309  {
310  while (true)
311  {
312  expect(!tokens.empty(), "a `begin' block was opened but never closed\nYou most likely forgot a `}' or `)'", m_last_token);
313  if (tokens.front().token == ")")
314  break;
315  m_last_token = tokens.front();
316 
317  block.push_back(parse(tokens, false, false, in_macro));
318  }
319  }
320 
321  void Parser::parseImport(Node& block, std::list<Token>& tokens)
322  {
323  if (tokens.front().type == TokenType::String)
324  block.push_back(atom(nextToken(tokens)));
325  else
326  throwParseError("found invalid token after keyword `import', expected String (path to the file or module to import)", tokens.front());
327  expect(tokens.front().token == ")", "got too many arguments after keyword `import', expected a single filename as String", tokens.front());
328  }
329 
330  void Parser::parseQuote(Node& block, std::list<Token>& tokens, bool in_macro)
331  {
332  block.push_back(parse(tokens, false, false, in_macro));
333  expect(tokens.front().token == ")", "got too many arguments after keyword `quote', expected a single block or value", tokens.front());
334  }
335 
336  void Parser::parseDel(Node& block, std::list<Token>& tokens)
337  {
338  if (tokens.front().type == TokenType::Identifier)
339  block.push_back(atom(nextToken(tokens)));
340  else
341  throwParseError("found invalid token after keyword `del', expected Identifier", tokens.front());
342  expect(tokens.front().token == ")", "got too many arguments after keyword `del', expected a single identifier", tokens.front());
343  }
344 
345  Node Parser::parseShorthand(Token& token, std::list<Token>& tokens, bool in_macro)
346  {
347  if (token.token == "'")
348  {
349  // create a list node to host the block
350  Node block = make_node_list(token.line, token.col, m_file);
351 
352  block.push_back(make_node(Keyword::Quote, token.line, token.col, m_file));
353  block.push_back(parse(tokens, false, false, in_macro));
354  return block;
355  }
356  else if (token.token == "!")
357  {
358  if (m_debug >= 2)
359  std::cout << "Found a macro at " << token.line << ':' << token.col << " in " << m_file << '\n';
360 
361  // macros
362  Node block = make_node(NodeType::Macro, token.line, token.col, m_file);
363 
364  Node parsed = parse(tokens, /* authorize_capture */ false, /* authorize_field_read */ false, /* in_macro */ true);
365  if (parsed.nodeType() != NodeType::List || parsed.list().size() < 2 || parsed.list().size() > 4)
366  throwParseError("Macros can only defined using the !{ name value } or !{ name (args) value } syntax", token);
367 
368  // append the nodes of the parsed node to the current macro node
369  for (std::size_t i = 0, end = parsed.list().size(); i < end; ++i)
370  block.push_back(parsed.list()[i]);
371  return block;
372  }
373 
374  throwParseError("unknown shorthand", token);
375  }
376 
377  void Parser::checkForInvalidTokens(Node& atomized, Token& token, bool previous_token_was_lparen, bool authorize_capture, bool authorize_field_read)
378  {
379  if ((atomized.nodeType() == NodeType::String || atomized.nodeType() == NodeType::Number ||
380  atomized.nodeType() == NodeType::List) &&
381  previous_token_was_lparen)
382  {
383  std::stringstream ss;
384  ss << "found invalid token after `(', expected Keyword, Identifier";
385  if (!authorize_capture && !authorize_field_read)
386  ss << " or Operator";
387  else
388  {
389  ss << ", Operator";
390  if (authorize_capture && !authorize_field_read)
391  ss << " or Capture";
392  else if (!authorize_capture && authorize_field_read)
393  ss << " or GetField";
394  else
395  ss << ", Capture or GetField";
396  }
397  throwParseError(ss.str(), token);
398  }
399  }
400 
401  Token Parser::nextToken(std::list<Token>& tokens)
402  {
403  expect(!tokens.empty(), "no more token to consume", m_last_token);
404  m_last_token = tokens.front();
405 
406  const Token out = std::move(tokens.front());
407  tokens.pop_front();
408  return out;
409  }
410 
411  Node Parser::atom(const Token& token)
412  {
413  switch (token.type)
414  {
415  case TokenType::Number:
416  return make_node(std::stod(token.token), token.line, token.col, m_file);
417 
418  case TokenType::String:
419  {
420  std::string str = token.token;
421  // remove the " at the beginning and at the end
422  str.erase(0, 1);
423  str.erase(token.token.size() - 2, 1);
424 
425  return make_node(str, token.line, token.col, m_file);
426  }
427 
428  case TokenType::Keyword:
429  {
430  std::optional<Keyword> kw;
431  if (token.token == "if")
432  kw = Keyword::If;
433  else if (token.token == "set")
434  kw = Keyword::Set;
435  else if (token.token == "let")
436  kw = Keyword::Let;
437  else if (token.token == "mut")
438  kw = Keyword::Mut;
439  else if (token.token == "fun")
440  kw = Keyword::Fun;
441  else if (token.token == "while")
442  kw = Keyword::While;
443  else if (token.token == "begin")
444  kw = Keyword::Begin;
445  else if (token.token == "import")
446  kw = Keyword::Import;
447  else if (token.token == "quote")
448  kw = Keyword::Quote;
449  else if (token.token == "del")
450  kw = Keyword::Del;
451 
452  if (kw)
453  return make_node(kw.value(), token.line, token.col, m_file);
454  throwParseError("unknown keyword", token);
455  }
456 
457  case TokenType::Capture:
458  case TokenType::GetField:
459  case TokenType::Spread:
460  {
462  n.setString(token.type != TokenType::Spread ? token.token : token.token.substr(3));
463  return n;
464  }
465 
467  throwParseError("got a shorthand to atomize, and that's not normal. If you see this error please report it on GitHub.", token);
468 
469  default:
470  {
471  // assuming it is a TokenType::Identifier, thus a Symbol
472  Node n = make_node(NodeType::Symbol, token.line, token.col, m_file);
473  n.setString(token.token);
474  return n;
475  }
476  }
477  }
478 
479  // high cpu cost
480  bool Parser::checkForInclude(Node& n, Node& parent, std::size_t pos)
481  {
482  namespace fs = std::filesystem;
483 
484  // if we have a list, we may find an import statement inside
485  if (n.nodeType() == NodeType::List)
486  {
487  if (n.constList().size() == 0)
488  return false;
489 
490  const Node& first = n.constList()[0];
491 
492  // if we found an import statement, inspect it
493  if (first.nodeType() == NodeType::Keyword && first.keyword() == Keyword::Import)
494  {
495  if (m_debug >= 2)
496  std::cout << "Import found in file: " << m_file << '\n';
497 
498  if (n.constList()[1].nodeType() != NodeType::String)
499  throw TypeError("Arguments of import must be of type String");
500 
501  // check if we are not loading a plugin
502  if (std::string file = n.constList()[1].string(); fs::path(file).extension().string() == ".ark")
503  {
504  // search for the source file everywhere
505  std::string included_file = seekFile(file);
506 
507  // if the file isn't in the include list, then we can include it
508  // this avoids cyclic includes
509  if (std::find(m_parent_include.begin(), m_parent_include.end(), Utils::canonicalRelPath(included_file)) != m_parent_include.end())
510  return true;
511 
512  // feed a new parser with our parent includes
514  for (auto const& pi : m_parent_include)
515  p.m_parent_include.push_back(pi); // new parser, we can assume that the parent include list is empty
516  p.m_parent_include.push_back(m_file); // add the current file to avoid importing it again
517  p.feed(Utils::readFile(included_file), included_file);
518 
519  // update our list of included files
520  for (auto const& inc : p.m_parent_include)
521  {
522  if (std::find(m_parent_include.begin(), m_parent_include.end(), inc) == m_parent_include.end())
523  m_parent_include.push_back(inc);
524  }
525 
526  for (std::size_t j = 1, end = p.ast().constList().size(); j < end; ++j)
527  parent.list().insert(parent.list().begin() + pos + j, p.ast().constList()[j]);
528 
529  return true;
530  }
531  }
532 
533  // inspect every other node in the list
534  for (std::size_t i = 0; i < n.list().size(); ++i)
535  {
536  if (checkForInclude(n.list()[i], n, i))
537  {
538  n.list().erase(n.list().begin() + i);
539  --i;
540  }
541  }
542  }
543 
544  return false;
545  }
546 
547  std::string Parser::seekFile(const std::string& file)
548  {
549  const std::string current_dir = Utils::getDirectoryFromPath(m_file) + "/";
550  const std::string path = (current_dir != "/") ? current_dir + file : file;
551 
552  if (m_debug >= 2)
553  {
554  std::cout << "path: " << path << " ; file: " << file << " ; libpath: ";
555  for (auto&& lib : m_libenv)
556  std::cout << lib << ":";
557  std::cout << "\nfilename: " << Utils::getFilenameFromPath(file) << '\n';
558  }
559 
560  // search in the current directory
561  if (Utils::fileExists(path))
562  return path;
563 
564  // search in all folders in environment path
565  for (auto const& p : m_libenv)
566  {
567  // then search in the standard library directory
568  if (std::string f = p + "/std/" + file; Utils::fileExists(f))
569  return f;
570  // then in the standard library root directory
571  else if (std::string f2 = p + "/" + file; Utils::fileExists(f2))
572  return f2;
573  }
574 
575  // fallback, we couldn't find the file
576  throw std::runtime_error("While processing file " + m_file + ", couldn't import " + file + ": file not found");
577  }
578 
579  void Parser::expect(bool pred, const std::string& message, internal::Token token)
580  {
581  if (!pred)
582  throwParseError(message, token);
583  }
584 
585  void Parser::throwParseError(const std::string& message, internal::Token token)
586  {
587  std::stringstream ss;
588  ss << message << "\nGot TokenType::" << internal::tokentype_string[static_cast<unsigned>(token.type)] << "\n";
589 
590  if (m_file != ARK_NO_NAME_FILE)
591  ss << "In file " << m_file << "\n";
592  ss << internal::makeTokenBasedErrorCtx(token.token, token.line, token.col, m_code);
593 
594  throw ParseError(ss.str());
595  }
596 
597  std::ostream& operator<<(std::ostream& os, const Parser& P) noexcept
598  {
599  os << "AST\n";
600  if (P.ast().nodeType() == NodeType::List)
601  {
602  int i = 0;
603  for (const auto& node : P.ast().constList())
604  std::cout << (i++) << ": " << node << '\n';
605  }
606  else
607  os << "Single item\n"
608  << P.m_ast << std::endl;
609  return os;
610  }
611 }
#define ARK_NO_NAME_FILE
Definition: Constants.hpp:26
ArkScript homemade exceptions.
Lots of utilities about the filesystem.
Parses a token stream into an AST by using the Ark::Node.
ParseError thrown by the parser.
Definition: Exceptions.hpp:95
A type error triggered when types don't match.
Definition: Exceptions.hpp:29
std::vector< Token > & tokens() noexcept
Return the list of tokens.
Definition: Lexer.cpp:257
void feed(const std::string &code)
Give code to tokenize and create the list of tokens.
Definition: Lexer.cpp:19
A node of an Abstract Syntax Tree for ArkScript.
Definition: Node.hpp:29
NodeType nodeType() const noexcept
Return the node type.
Definition: Node.cpp:126
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:119
Keyword keyword() const noexcept
Return the keyword held by the value (if the node type allows it)
Definition: Node.cpp:102
void setFilename(const std::string &filename) noexcept
Set the original Filename where the node was.
Definition: Node.cpp:159
void push_back(const Node &node) noexcept
Every node has a list as well as a value so we can push_back on all node no matter their type.
Definition: Node.cpp:109
void setString(const std::string &value) noexcept
Set the String object.
Definition: Node.cpp:136
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Definition: Node.cpp:114
The parser is responsible of constructing the Abstract Syntax Tree from a token list.
Definition: Parser.hpp:44
void parseImport(Node &, std::list< Token > &)
Definition: Parser.cpp:321
void parseBegin(Node &, std::list< Token > &, bool)
Definition: Parser.cpp:308
friend std::ostream & operator<<(std::ostream &os, const Parser &P) noexcept
Definition: Parser.cpp:597
std::string m_code
Definition: Parser.hpp:92
void parseLetMut(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:227
void parseDel(Node &, std::list< Token > &)
Definition: Parser.cpp:336
Node atom(const Token &token)
Convert a token to a node.
Definition: Parser.cpp:411
uint16_t m_options
Definition: Parser.hpp:84
void sugar(std::vector< Token > &tokens) noexcept
Applying syntactic sugar: {...} => (begin...), [...] => (list ...)
Definition: Parser.cpp:69
void expect(bool pred, const std::string &message, Token token)
Throw a parse exception is the given predicated is false.
Definition: Parser.cpp:579
const Node & ast() const noexcept
Return the generated AST.
Definition: Parser.cpp:59
void parseQuote(Node &, std::list< Token > &, bool)
Definition: Parser.cpp:330
void feed(const std::string &code, const std::string &filename=ARK_NO_NAME_FILE)
Give the code to parse.
Definition: Parser.cpp:21
void throwParseError(const std::string &message, Token token)
Throw a parse error related to a token (seek it in the related file and highlight the error)
Definition: Parser.cpp:585
std::string seekFile(const std::string &file)
Seek a file in the lib folder and everywhere.
Definition: Parser.cpp:547
std::vector< std::string > m_libenv
Definition: Parser.hpp:83
std::string m_file
Definition: Parser.hpp:90
void parseFun(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:276
void parseSet(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:250
void checkForInvalidTokens(Node &, Token &, bool, bool, bool)
Definition: Parser.cpp:377
void parseIf(Node &, std::list< Token > &, bool)
Definition: Parser.cpp:204
Node parse(std::list< Token > &tokens, bool authorize_capture=false, bool authorize_field_read=false, bool in_macro=false)
Parse a list of tokens recursively.
Definition: Parser.cpp:102
Parser(unsigned debug, uint16_t options, const std::vector< std::string > &lib_env) noexcept
Construct a new Parser object.
Definition: Parser.cpp:13
std::vector< std::string > m_parent_include
Definition: Parser.hpp:94
void parseWhile(Node &, Token &, std::list< Token > &, bool)
Definition: Parser.cpp:291
bool checkForInclude(Node &n, Node &parent, std::size_t pos=0)
Search for all the includes in a given node, in its sub-nodes and replace them by the code of the inc...
Definition: Parser.cpp:480
Node parseShorthand(Token &, std::list< Token > &, bool)
Definition: Parser.cpp:345
Token nextToken(std::list< Token > &tokens)
Get the next token if possible, from a list of tokens.
Definition: Parser.cpp:401
const std::vector< std::string > & getImports() const noexcept
Return the list of files imported by the code given to the parser.
Definition: Parser.cpp:64
Create string error context for AST errors.
std::string readFile(const std::string &name)
Helper to read a file.
Definition: Files.hpp:48
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
Definition: Files.hpp:29
std::string canonicalRelPath(const std::string &path)
Get the canonical relative path from a path.
Definition: Files.hpp:85
std::string getDirectoryFromPath(const std::string &path)
Get the directory from a path.
Definition: Files.hpp:63
std::string getFilenameFromPath(const std::string &path)
Get the filename from a path.
Definition: Files.hpp:74
constexpr std::array< std::string_view, 13 > tokentype_string
Definition: Token.hpp:39
Node make_node(T &&value, std::size_t line, std::size_t col, const std::string &file)
Definition: Node.hpp:251
std::string makeTokenBasedErrorCtx(const std::string &match, std::size_t line, std::size_t col, const std::string &code)
Construct an error message based on a given match in the code.
constexpr std::array< std::string_view, 25 > operators
Definition: Common.hpp:89
Node make_node_list(std::size_t line, std::size_t col, const std::string &file)
Definition: Node.hpp:259
NodeType similarNodetypeFromTokentype(TokenType tt)
Definition: Parser.hpp:27
std::size_t line
Definition: Token.hpp:59
std::size_t col
Definition: Token.hpp:60
TokenType type
Definition: Token.hpp:57
std::string token
Definition: Token.hpp:58