Skip to content

Commit

Permalink
libnixf: parse string interpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Jan 13, 2024
1 parent d4e3a04 commit 95bcf01
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 17 deletions.
22 changes: 13 additions & 9 deletions libnixf/include/nixf/Parse/Nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,33 +70,37 @@ class ExprFloat : public Expr {
[[nodiscard]] NixFloat value() const { return Value; }
};

class StringPart {
enum StringPartKind {
class InterpolablePart {
public:
enum InterpolablePartKind {
SPK_Escaped,
SPK_Interpolation,
} Kind;
};

private:
InterpolablePartKind Kind;
std::string Escaped;
std::shared_ptr<Expr> Interpolation;

public:
explicit StringPart(std::string Escaped)
explicit InterpolablePart(std::string Escaped)
: Kind(SPK_Escaped), Escaped(std::move(Escaped)), Interpolation(nullptr) {
}

explicit StringPart(std::shared_ptr<Expr> Expr)
explicit InterpolablePart(std::shared_ptr<Expr> Expr)
: Kind(SPK_Interpolation), Interpolation(std::move(Expr)) {}

StringPartKind kind() { return Kind; }
[[nodiscard]] InterpolablePartKind kind() const { return Kind; }
};

class InterpolatedParts : public Node {
std::vector<StringPart> Fragments;
std::vector<InterpolablePart> Fragments;

public:
InterpolatedParts(OffsetRange Range, std::vector<StringPart> Fragments)
InterpolatedParts(OffsetRange Range, std::vector<InterpolablePart> Fragments)
: Node(NK_InterpolableParts, Range), Fragments(std::move(Fragments)) {}

[[nodiscard]] const std::vector<StringPart> &fragments() const {
[[nodiscard]] const std::vector<InterpolablePart> &fragments() const {
return Fragments;
};
};
Expand Down
19 changes: 11 additions & 8 deletions libnixf/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,23 @@ class Parser {
///
/// They are strings, ind-strings, paths, in nix language.
/// \note This needs context-switching so look-ahead buf should be cleared.
std::shared_ptr<InterpolatedParts> parseStringParts() {
std::vector<StringPart> Parts;
std::shared_ptr<InterpolatedParts> parseInterpolableParts() {
std::vector<InterpolablePart> Parts;
RB.push(Lex.cur());
while (true) {
assert(LookAheadBuf.empty()); // We are switching contexts.
switch (Token Tok = peek(0); Tok.kind()) {
case tok_dollar_curly: {
consume();
assert(LastToken);
// interpolation, we need to parse a subtree then.
if (auto Expr = parseExpr())
Parts.emplace_back(std::move(Expr));
else
diagNullExpr(Diag, LastToken->end(), "interpolation");
/* with(PS_Expr) */ {
auto ExprState = withState(PS_Expr);
// interpolation, we need to parse a subtree then.
if (auto Expr = parseExpr())
Parts.emplace_back(std::move(Expr));
else
diagNullExpr(Diag, LastToken->end(), "interpolation");
} // with(PS_Expr)
continue;
}
case tok_string_part: {
Expand Down Expand Up @@ -195,7 +198,7 @@ class Parser {
assert(LastToken && "LastToken should be set after consume()");
/* with(PS_String) */ {
auto StringState = withState(PS_String);
std::shared_ptr<InterpolatedParts> Parts = parseStringParts();
std::shared_ptr<InterpolatedParts> Parts = parseInterpolableParts();
if (Token EndTok = peek(); EndTok.kind() == tok_dquote) {
consume();
return std::make_shared<ExprString>(RB.finish(EndTok.end()),
Expand Down
18 changes: 18 additions & 0 deletions libnixf/lib/Parse/test/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,22 @@ TEST(Parser, StringMissingDQuote) {
ASSERT_EQ(Expr->range().view(), Src);
}

TEST(Parser, StringInterpolation) {
auto Src = R"("aaa ${1} bbb")"sv;
DiagnosticEngine Diags;
auto Expr = nixf::parse(Src, Diags);
ASSERT_TRUE(Expr);
ASSERT_EQ(Expr->kind(), Node::NK_ExprString);
auto Parts = static_cast<ExprString *>(Expr.get())->parts();
ASSERT_EQ(Parts->range().view(), "aaa ${1} bbb");
ASSERT_EQ(Parts->fragments().size(), 3);

ASSERT_EQ(Parts->fragments()[0].kind(), InterpolablePart::SPK_Escaped);
ASSERT_EQ(Parts->fragments()[1].kind(), InterpolablePart::SPK_Interpolation);
ASSERT_EQ(Parts->fragments()[2].kind(), InterpolablePart::SPK_Escaped);

ASSERT_EQ(Diags.diags().size(), 0);
ASSERT_EQ(Expr->range().view(), Src);
}

} // namespace

0 comments on commit 95bcf01

Please sign in to comment.