8 BaseParser(), m_interpret(interpret), m_logger(
"Parser", debug),
9 m_ast(
NodeType::
List), m_imports({}), m_allow_macro_behavior(0)
60 if (
node.line() != 0 ||
node.col() != 0)
63 const auto [row, col] = cursor.value_or(
getCursor());
90 if (
auto result =
import_(); result.has_value())
94 if (
auto result =
block(); result.has_value())
102 if (
auto result =
macro(); result.has_value())
114 if (
auto result =
list(); result.has_value())
127 if (!
oneOf({
"let",
"mut",
"set" }, &token))
131 leaf->attachNearestCommentBefore(
comment);
135 else if (token ==
"mut")
143 if (
const auto value =
nodeOrValue(); value.has_value())
145 const auto sym = value.value();
147 leaf->push_back(sym);
149 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());
155 if (leaf->constList().size() == 1)
169 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
181 if (!
oneOf({
"del" }))
193 leaf->list().back().attachNearestCommentBefore(
comment);
204 if (!
oneOf({
"if" }))
220 if (
auto value_if_true =
nodeOrValue(); value_if_true.has_value())
221 leaf->push_back(value_if_true.value().attachNearestCommentBefore(
comment));
228 if (
auto value_if_false =
nodeOrValue(); value_if_false.has_value())
230 leaf->push_back(value_if_false.value().attachNearestCommentBefore(
comment));
233 leaf->list().back().attachCommentAfter(
comment);
236 leaf->attachCommentAfter(
comment);
247 if (!
oneOf({
"while" }))
264 leaf->push_back(body.value().attachNearestCommentBefore(
comment));
281 leaf->attachNearestCommentBefore(
comment);
283 if (!
oneOf({
"import" }))
295 if (import_data.
prefix.size() > 255)
298 errorWithNextToken(fmt::format(
"Import name too long, expected at most 255 characters, got {}", import_data.
prefix.size()));
303 import_data.
col = col;
304 import_data.
line = row;
323 import_data.
package.push_back(path);
324 import_data.
prefix = path;
326 if (path.size() > 255)
329 errorWithNextToken(fmt::format(
"Import name too long, expected at most 255 characters, got {}", path.size()));
335 leaf->push_back(packageNode);
369 error(fmt::format(
"Glob patterns can not be separated from the package, use (import {}:*) instead", import_data.
toPackageString()),
symbol);
374 error(
"Glob pattern can not follow a symbol to import",
":*");
395 leaf->push_back(packageNode);
396 leaf->push_back(symbols);
402 leaf->list().back().attachCommentAfter(
comment);
413 bool alt_syntax =
false;
418 if (!
oneOf({
"begin" }))
435 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
446 leaf->list().back().attachCommentAfter(
comment);
458 args->attachNearestCommentBefore(
comment);
460 bool has_captures =
false;
473 args->push_back(
node);
484 error(
"Captured variables should be at the end of the argument list",
symbol);
489 args->push_back(
node);
506 if (!
oneOf({
"fun" }))
510 std::string comment_before_args;
518 if (
const auto value =
nodeOrValue(); value.has_value())
522 Node args = value.value();
527 leaf->push_back(args);
539 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
548 leaf->push_back(args.value().attachNearestCommentBefore(comment_before_args));
554 leaf->push_back(value.value().attachNearestCommentBefore(comment_before_args));
563 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
576 if (!
oneOf({
"$if" }))
582 leaf->attachNearestCommentBefore(
comment);
592 if (
auto value_if_true =
nodeOrValue(); value_if_true.has_value())
593 leaf->push_back(value_if_true.value().attachNearestCommentBefore(
comment));
600 if (
auto value_if_false =
nodeOrValue(); value_if_false.has_value())
602 leaf->push_back(value_if_false.value().attachNearestCommentBefore(
comment));
605 leaf->list().back().attachCommentAfter(
comment);
622 args->attachNearestCommentBefore(
comment);
624 std::vector<std::string> names;
629 std::string arg_name;
630 if (!
name(&arg_name))
636 if (std::ranges::find(names, arg_name) != names.end())
639 errorWithNextToken(fmt::format(
"Argument names must be unique, can not reuse `{}'", arg_name));
641 names.push_back(arg_name);
647 std::string spread_name;
648 if (!
name(&spread_name))
654 args->list().back().attachCommentAfter(
comment);
656 if (std::ranges::find(names, spread_name) != names.end())
659 errorWithNextToken(fmt::format(
"Argument names must be unique, can not reuse `{}'", spread_name));
668 if (args->list().empty())
669 args->attachCommentAfter(
comment);
671 args->list().back().attachCommentAfter(
comment);
690 leaf->attachNearestCommentBefore(
comment);
701 if (
const auto args =
macroArgs(); args.has_value())
702 leaf->push_back(args.value());
711 if (value.has_value())
712 leaf->push_back(value.value());
725 if (value.has_value())
726 leaf->push_back(value.value());
727 else if (leaf->list().size() == 2)
732 leaf->list().back().attachCommentAfter(
comment);
746 leaf->list().back().attachCommentAfter(
comment);
760 std::optional<Node> func;
763 else if (
auto nested =
node(); nested.has_value())
764 func = nested->attachNearestCommentBefore(
comment);
773 leaf->push_back(func.value());
779 leaf->push_back(arg.value().attachNearestCommentBefore(
comment));
787 leaf->list().back().attachCommentAfter(
comment);
791 leaf->list().back().attachCommentAfter(
comment);
808 leaf->attachNearestCommentBefore(
comment);
815 leaf->push_back(value.value().attachNearestCommentBefore(
comment));
822 leaf->list().back().attachCommentAfter(
comment);
862 if (
auto value =
atom(); value.has_value())
865 for (
const auto type : types)
867 if (value->nodeType() == type)
877 if (
auto value =
atom(); value.has_value())
882 if (
auto sub_node =
node(); sub_node.has_value())
899 if (
auto result = (this->*parser)(); result.has_value())
901 result->attachNearestCommentBefore(result->comment() +
comment);
906 result.value().attachCommentAfter(
comment);
908 if (result->isListLike())
915 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)
bool oneOf(std::initializer_list< std::string > words, std::string *s=nullptr)
Fetch a token and try to match one of the given words.
void error(const std::string &error, std::string exp)
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)
Fetch the next token (space and paren delimited) to generate an error.
void errorMissingSuffix(char suffix, const std::string &node_name)
Generate an error for a given node when a suffix is missing.
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::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::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.
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)