13 Parser::Parser(
unsigned debug, uint16_t options,
const std::vector<std::string>& lib_env) noexcept :
21 void Parser::feed(
const std::string& code,
const std::string& filename)
28 std::cout <<
"New parser: " <<
m_file <<
'\n';
42 std::list<Token> tokens(t.begin(), t.end());
49 while (!tokens.empty())
55 std::cout <<
"(Parser) AST\n"
74 std::size_t line = tokens[i].line;
75 std::size_t col = tokens[i].col;
77 if (tokens[i].token ==
"{")
81 if (i > 0 && tokens[i - 1].token !=
"!")
86 else if (tokens[i].token ==
"}" || tokens[i].token ==
"]")
88 else if (tokens[i].token ==
"[")
96 if (i == tokens.size())
102 Node Parser::parse(std::list<Token>& tokens,
bool authorize_capture,
bool authorize_field_read,
bool in_macro)
104 using namespace std::string_literals;
108 bool previous_token_was_lparen =
false;
111 if (token.
token ==
"(")
113 previous_token_was_lparen =
true;
118 if (tokens.front().token ==
"(")
121 previous_token_was_lparen =
false;
128 if (token.
token ==
")")
137 throwParseError(
"Unexpected keyword `" + tokens.front().token +
"' in the middle of an expression", tokens.front());
143 checkForInvalidTokens(atomized, token, previous_token_was_lparen, authorize_capture, authorize_field_read);
151 if (token.
token ==
"if")
152 parseIf(block, tokens, in_macro);
153 else if (token.
token ==
"let" || token.
token ==
"mut")
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")
161 else if (token.
token ==
"begin")
163 else if (token.
token ==
"import")
165 else if (token.
token ==
"quote")
167 else if (token.
token ==
"del")
170 throwParseError(
"unimplemented keyword `" + token.
token +
"'. If you see this error please report it on GitHub.", token);
177 while (tokens.front().token !=
")")
180 }
while (tokens.front().token !=
")");
190 throwParseError(
"Found a lonely `" + token.
token +
"', you most likely have too much parenthesis.", token);
193 throwParseError(
"Found a free flying operator, which isn't authorized. Operators should always immediatly follow a `('.", token);
197 throwParseError(
"Unexpected keyword `" + tokens.front().token +
"' in the middle of an expression", tokens.front());
199 !previous_token_was_lparen)
200 throwParseError(
"Unexpected keyword `" + token.
token +
"' in the middle of an expression", token);
206 auto temp = tokens.front();
214 throwParseError(
"found invalid token after keyword `if', expected function call, value or Identifier", temp);
216 expect(!tokens.empty() && tokens.front().token !=
")",
"expected a statement after the condition", temp);
219 if (tokens.front().token !=
")")
223 expect(tokens.front().token ==
")",
"if block is ill-formed, got more than the 3 required arguments (condition, then, else)",
m_last_token);
229 auto temp = tokens.front();
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);
239 while (tokens.front().token !=
")")
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;
247 "too many arguments given to keyword `" + token.
token +
"', got " + std::to_string(block.
list().size() - 1) +
", expected at most 3",
m_last_token);
252 auto temp = tokens.front();
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);
263 throwParseError(
"found invalid token after keyword `set', expected an identifier, got a closure field reading expression", tokens.front());
265 while (tokens.front().token !=
")")
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;
273 "too many arguments given to keyword `" + token.
token +
"', got " + std::to_string(block.
list().size() - 1) +
", expected at most 3",
m_last_token);
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());
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);
293 auto temp = tokens.front();
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);
305 expect(block.
list().size() == 3,
"got too many arguments after keyword `" + token.
token +
"', expected a condition and a body", temp);
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 ==
")")
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());
333 expect(tokens.front().token ==
")",
"got too many arguments after keyword `quote', expected a single block or value", tokens.front());
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());
347 if (token.
token ==
"'")
356 else if (token.
token ==
"!")
359 std::cout <<
"Found a macro at " << token.
line <<
':' << token.
col <<
" in " <<
m_file <<
'\n';
364 Node parsed =
parse(tokens,
false,
false,
true);
366 throwParseError(
"Macros can only defined using the !{ name value } or !{ name (args) value } syntax", token);
369 for (std::size_t i = 0, end = parsed.
list().size(); i < end; ++i)
381 previous_token_was_lparen)
383 std::stringstream ss;
384 ss <<
"found invalid token after `(', expected Keyword, Identifier";
385 if (!authorize_capture && !authorize_field_read)
386 ss <<
" or Operator";
390 if (authorize_capture && !authorize_field_read)
392 else if (!authorize_capture && authorize_field_read)
393 ss <<
" or GetField";
395 ss <<
", Capture or GetField";
406 const Token out = std::move(tokens.front());
420 std::string str = token.
token;
423 str.erase(token.
token.size() - 2, 1);
430 std::optional<Keyword> kw;
431 if (token.
token ==
"if")
433 else if (token.
token ==
"set")
435 else if (token.
token ==
"let")
437 else if (token.
token ==
"mut")
439 else if (token.
token ==
"fun")
441 else if (token.
token ==
"while")
443 else if (token.
token ==
"begin")
445 else if (token.
token ==
"import")
447 else if (token.
token ==
"quote")
449 else if (token.
token ==
"del")
467 throwParseError(
"got a shorthand to atomize, and that's not normal. If you see this error please report it on GitHub.", token);
482 namespace fs = std::filesystem;
496 std::cout <<
"Import found in file: " <<
m_file <<
'\n';
499 throw TypeError(
"Arguments of import must be of type String");
502 if (std::string file = n.
constList()[1].string(); fs::path(file).extension().string() ==
".ark")
505 std::string included_file =
seekFile(file);
526 for (std::size_t j = 1, end = p.
ast().
constList().size(); j < end; ++j)
534 for (std::size_t i = 0; i < n.
list().size(); ++i)
538 n.
list().erase(n.
list().begin() + i);
550 const std::string path = (current_dir !=
"/") ? current_dir + file : file;
554 std::cout <<
"path: " << path <<
" ; file: " << file <<
" ; libpath: ";
556 std::cout << lib <<
":";
576 throw std::runtime_error(
"While processing file " +
m_file +
", couldn't import " + file +
": file not found");
587 std::stringstream ss;
591 ss <<
"In file " <<
m_file <<
"\n";
603 for (
const auto& node : P.ast().constList())
604 std::cout << (i++) <<
": " << node <<
'\n';
607 os <<
"Single item\n"
608 << P.m_ast << std::endl;
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.
A type error triggered when types don't match.
std::vector< Token > & tokens() noexcept
Return the list of tokens.
void feed(const std::string &code)
Give code to tokenize and create the list of tokens.
A node of an Abstract Syntax Tree for ArkScript.
NodeType nodeType() const noexcept
Return the node type.
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Keyword keyword() const noexcept
Return the keyword held by the value (if the node type allows it)
void setFilename(const std::string &filename) noexcept
Set the original Filename where the node was.
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.
void setString(const std::string &value) noexcept
Set the String object.
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
The parser is responsible of constructing the Abstract Syntax Tree from a token list.
void parseImport(Node &, std::list< Token > &)
void parseBegin(Node &, std::list< Token > &, bool)
void parseLetMut(Node &, Token &, std::list< Token > &, bool)
void parseDel(Node &, std::list< Token > &)
Node atom(const Token &token)
Convert a token to a node.
void sugar(std::vector< Token > &tokens) noexcept
Applying syntactic sugar: {...} => (begin...), [...] => (list ...)
void expect(bool pred, const std::string &message, Token token)
Throw a parse exception is the given predicated is false.
const Node & ast() const noexcept
Return the generated AST.
void parseQuote(Node &, std::list< Token > &, bool)
void feed(const std::string &code, const std::string &filename=ARK_NO_NAME_FILE)
Give the code to parse.
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)
std::string seekFile(const std::string &file)
Seek a file in the lib folder and everywhere.
std::vector< std::string > m_libenv
void parseFun(Node &, Token &, std::list< Token > &, bool)
void parseSet(Node &, Token &, std::list< Token > &, bool)
void checkForInvalidTokens(Node &, Token &, bool, bool, bool)
void parseIf(Node &, std::list< Token > &, bool)
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.
Parser(unsigned debug, uint16_t options, const std::vector< std::string > &lib_env) noexcept
Construct a new Parser object.
std::vector< std::string > m_parent_include
void parseWhile(Node &, Token &, std::list< Token > &, bool)
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...
Node parseShorthand(Token &, std::list< Token > &, bool)
Token nextToken(std::list< Token > &tokens)
Get the next token if possible, from a list of tokens.
const std::vector< std::string > & getImports() const noexcept
Return the list of files imported by the code given to the parser.
Create string error context for AST errors.
std::string readFile(const std::string &name)
Helper to read a file.
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
std::string canonicalRelPath(const std::string &path)
Get the canonical relative path from a path.
std::string getDirectoryFromPath(const std::string &path)
Get the directory from a path.
std::string getFilenameFromPath(const std::string &path)
Get the filename from a path.
std::ostream & operator<<(std::ostream &os, const std::vector< Node > &N) noexcept
constexpr std::array< std::string_view, 13 > tokentype_string
Node make_node(T &&value, std::size_t line, std::size_t col, const std::string &file)
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
Node make_node_list(std::size_t line, std::size_t col, const std::string &file)
NodeType similarNodetypeFromTokentype(TokenType tt)