8 BaseParser(), m_interpret(interpret), m_logger(
"Parser", debug),
9 m_ast(
NodeType::
List), m_imports({}), m_allow_macro_behavior(0),
43 return functionCall();
78 std::string out =
peek();
81 message =
"Unexpected closing paren";
83 message =
"Unexpected closing bracket";
85 message =
"Unexpected closing square bracket";
107 if (
node.line() != 0 ||
node.col() != 0)
110 const auto [row, col] = cursor.value_or(
getCursor());
121 errorWithNextToken(fmt::format(
"Too many nested node while parsing, exceeds limit of {}. Consider rewriting your code by breaking it in functions and macros.",
MaxNestedNodes));
125 std::optional<Node> result = std::nullopt;
147 if (!
oneOf({
"let",
"mut",
"set" }, &token))
151 leaf->attachNearestCommentBefore(
comment);
155 else if (token ==
"mut")
163 if (
const auto value =
nodeOrValue(); value.has_value())
165 const auto sym = value.value();
167 leaf->push_back(sym);
169 error(fmt::format(
"Can not use a {} as a symbol name, even in a macro",
nodeTypes[
static_cast<std::size_t
>(sym.nodeType())]), sym.repr());
175 if (leaf->constList().size() == 1)
178 std::string symbol_name;
179 if (!
name(&symbol_name))
189 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
201 if (!
oneOf({
"del" }))
208 std::string symbol_name;
209 if (!
name(&symbol_name))
213 leaf->list().back().attachNearestCommentBefore(
comment);
224 if (!
oneOf({
"if" }))
232 if (
auto cond_expr =
nodeOrValue(); cond_expr.has_value())
233 leaf->push_back(cond_expr.value().attachNearestCommentBefore(
comment));
240 if (
auto value_if_true =
nodeOrValue(); value_if_true.has_value())
241 leaf->push_back(value_if_true.value().attachNearestCommentBefore(
comment));
248 if (
auto value_if_false =
nodeOrValue(); value_if_false.has_value())
250 leaf->push_back(value_if_false.value().attachNearestCommentBefore(
comment));
253 leaf->list().back().attachCommentAfter(
comment);
256 leaf->attachCommentAfter(
comment);
267 if (!
oneOf({
"while" }))
275 if (
auto cond_expr =
nodeOrValue(); cond_expr.has_value())
276 leaf->push_back(cond_expr.value().attachNearestCommentBefore(
comment));
284 leaf->push_back(body.value().attachNearestCommentBefore(
comment));
303 leaf->attachNearestCommentBefore(
comment);
305 if (!
oneOf({
"import" }))
317 if (import_data.
prefix.size() > 255)
320 errorWithNextToken(fmt::format(
"Import name too long, expected at most 255 characters, got {}", import_data.
prefix.size()));
325 import_data.
col = col;
326 import_data.
line = row;
345 import_data.
package.push_back(path);
346 import_data.
prefix = path;
348 if (path.size() > 255)
351 errorWithNextToken(fmt::format(
"Import name too long, expected at most 255 characters, got {}", path.size()));
357 leaf->push_back(packageNode);
387 std::string symbol_name;
388 if (!
name(&symbol_name))
390 if (symbol_name ==
"*")
391 error(fmt::format(
"Glob patterns can not be separated from the package, use (import {}:*) instead", import_data.
toPackageString()), symbol_name);
393 if (symbol_name.size() >= 2 && symbol_name[symbol_name.size() - 2] ==
':' && symbol_name.back() ==
'*')
396 error(
"Glob pattern can not follow a symbol to import",
":*");
402 import_data.
symbols.push_back(symbol_name);
417 leaf->push_back(packageNode);
418 leaf->push_back(symbols);
424 leaf->list().back().attachCommentAfter(
comment);
436 bool alt_syntax =
false;
441 if (!
oneOf({
"begin" }))
458 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
469 leaf->list().back().attachCommentAfter(
comment);
481 args->attachNearestCommentBefore(
comment);
483 bool has_captures =
false;
496 args->push_back(capture_node);
501 std::string symbol_name;
502 if (!
name(&symbol_name))
507 error(
"Captured variables should be at the end of the argument list", symbol_name);
512 args->push_back(arg_node);
529 if (!
oneOf({
"fun" }))
533 std::string comment_before_args;
541 if (
const auto value =
nodeOrValue(); value.has_value())
545 Node args = value.value();
550 leaf->push_back(args);
562 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
571 leaf->push_back(args.value().attachNearestCommentBefore(comment_before_args));
577 leaf->push_back(value.value().attachNearestCommentBefore(comment_before_args));
586 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
599 if (!
oneOf({
"$if" }))
605 leaf->attachNearestCommentBefore(
comment);
607 if (
const auto cond_expr =
nodeOrValue(); cond_expr.has_value())
608 leaf->push_back(cond_expr.value());
615 if (
auto value_if_true =
nodeOrValue(); value_if_true.has_value())
616 leaf->push_back(value_if_true.value().attachNearestCommentBefore(
comment));
623 if (
auto value_if_false =
nodeOrValue(); value_if_false.has_value())
625 leaf->push_back(value_if_false.value().attachNearestCommentBefore(
comment));
628 leaf->list().back().attachCommentAfter(
comment);
645 args->attachNearestCommentBefore(
comment);
647 std::vector<std::string> names;
652 std::string arg_name;
653 if (!
name(&arg_name))
659 if (std::ranges::find(names, arg_name) != names.end())
662 errorWithNextToken(fmt::format(
"Argument names must be unique, can not reuse `{}'", arg_name));
664 names.push_back(arg_name);
670 std::string spread_name;
671 if (!
name(&spread_name))
677 args->list().back().attachCommentAfter(
comment);
679 if (std::ranges::find(names, spread_name) != names.end())
682 errorWithNextToken(fmt::format(
"Argument names must be unique, can not reuse `{}'", spread_name));
691 if (args->list().empty())
692 args->attachCommentAfter(
comment);
694 args->list().back().attachCommentAfter(
comment);
711 if (!
oneOf({
"macro" }))
714 leaf->attachNearestCommentBefore(
comment);
716 std::string symbol_name;
717 if (!
name(&symbol_name))
725 if (
const auto args =
macroArgs(); args.has_value())
726 leaf->push_back(args.value());
735 if (value.has_value())
736 leaf->push_back(value.value());
738 errorWithNextToken(fmt::format(
"Expected an argument list, atom or node while defining macro `{}'", symbol_name));
749 if (value.has_value())
750 leaf->push_back(value.value());
751 else if (leaf->list().size() == 2)
756 leaf->list().back().attachCommentAfter(
comment);
764 errorWithNextToken(fmt::format(
"Expected a value while defining macro `{}'", symbol_name), context);
770 leaf->list().back().attachCommentAfter(
comment);
785 std::optional<Node> func;
787 func = sym_or_field->attachNearestCommentBefore(
comment);
788 else if (
auto nested =
node(); nested.has_value())
789 func = nested->attachNearestCommentBefore(
comment);
798 leaf->push_back(func.value());
804 leaf->push_back(arg.value().attachNearestCommentBefore(
comment));
812 leaf->list().back().attachCommentAfter(
comment);
816 leaf->list().back().attachCommentAfter(
comment);
818 expectSuffixOrError(
')', fmt::format(
"in function call to `{}'", func.value().repr()), context);
834 leaf->attachNearestCommentBefore(
comment);
841 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
848 leaf->list().back().attachCommentAfter(
comment);
888 if (
auto value =
atom(); value.has_value())
891 for (
const auto type : types)
893 if (value->nodeType() == type)
903 if (
auto value =
atom(); value.has_value())
908 if (
auto sub_node =
node(); sub_node.has_value())
926 if (
auto result = (this->*parser)(); result.has_value())
928 result->attachNearestCommentBefore(result->comment() +
comment);
933 result.value().attachCommentAfter(
comment);
935 if (result->isListLike())
941 result.value().attachCommentAfter(
comment);
Parse ArkScript code, but do not handle any import declarations.
bool sequence(const std::string &s)
bool spaceComment(std::string *s=nullptr)
void initParser(const std::string &filename, const std::string &code)
bool expect(const CharPred &t, std::string *s=nullptr)
heck if a Character Predicate was able to parse, call next() if matching ; throw a CodeError if it do...
bool accept(const CharPred &t, std::string *s=nullptr)
check if a Character Predicate was able to parse, call next() if matching
bool newlineOrComment(std::string *s=nullptr)
void backtrack(long n)
Backtrack to a given position (this is NOT an offset!)
CodeErrorContext generateErrorContext(const std::string &expr)
bool oneOf(std::initializer_list< std::string > words, std::string *s=nullptr)
Fetch a token and try to match one of the given words.
bool space(std::string *s=nullptr)
bool name(std::string *s=nullptr)
bool comment(std::string *s=nullptr)
bool packageName(std::string *s=nullptr)
void errorWithNextToken(const std::string &message, const std::optional< CodeErrorContext > &additional_context=std::nullopt)
Fetch the next token (space and paren delimited) to generate an error.
void error(const std::string &error, std::string exp, const std::optional< CodeErrorContext > &additional_context=std::nullopt)
void expectSuffixOrError(char suffix, const std::string &context, const std::optional< CodeErrorContext > &additional_context=std::nullopt)
Check for a closing char or generate an error.
FilePosition getCursor() const
void traceStart(std::string &&trace_name)
A node of an Abstract Syntax Tree for ArkScript.
NodeType nodeType() const noexcept
Return the node type.
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
void setPos(std::size_t line, std::size_t col) noexcept
Set the Position of the node in the text.
Node & attachNearestCommentBefore(const std::string &comment)
Set the comment field with the nearest comment before this node.
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.
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
std::vector< std::function< std::optional< Node >()> > m_parsers
std::optional< Node > functionArgs()
std::optional< Node > macro()
std::optional< Node > wrapped(std::optional< Node >(Parser::*parser)(), const std::string &name)
Try to parse using a given parser, prefixing and suffixing it with (...), handling comments around th...
std::optional< Node > atom()
Try to parse an atom (number, string, spread, field, symbol, nil)
void process(const std::string &filename, const std::string &code)
Parse the given code.
std::optional< Node > loop()
Parser(unsigned debug, bool interpret=true)
Constructs a new Parser object.
std::optional< Node > nil()
std::optional< Node > symbol()
std::optional< Node > number()
std::optional< Node > macroArgs()
const Node & ast() const noexcept
std::optional< Node > nodeOrValue()
Try to parse an atom first, if it fails try to parse a node.
std::optional< Node > import_()
const std::vector< Import > & imports() const
std::optional< Node > string()
std::optional< Node > macroCondition()
std::optional< Node > spread()
std::optional< Node > condition()
Node & setNodePosAndFilename(Node &node, const std::optional< FilePosition > &cursor=std::nullopt) const
Update a node given a file position.
std::optional< Node > node()
std::optional< Node > functionCall()
std::optional< Node > field()
unsigned m_allow_macro_behavior
Toggled on when inside a macro definition, off afterward.
std::optional< Node > letMutSet()
std::optional< Node > anyAtomOf(std::initializer_list< NodeType > types)
Try to parse an atom, if any, match its type against the given list.
std::vector< Import > m_imports
std::optional< Node > function()
std::optional< Node > list()
std::size_t m_nested_nodes
Nested node counter.
std::optional< Node > block()
std::optional< Node > del()
constexpr std::array< std::string_view, 11 > nodeTypes
Node types as string, in the same order as the enum NodeType.
NodeType
The different node types available.
constexpr std::size_t MaxNestedNodes
Maximum number of nodes that can be nested while parsing code.
std::vector< std::string > symbols
List of symbols to import, can be empty if none provided. (import package :a :b)
std::size_t col
Position in the source file.
std::string prefix
The filename without the extension.
bool is_glob
Import as glob (import package:*)
std::string toPackageString() const
std::vector< std::string > package
Package with all the segments.
bool with_prefix
Import with prefix (import package)