From 95bcf0192b9dc186f8eb9f56b167f940c96a141c Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sat, 13 Jan 2024 13:55:01 +0800 Subject: [PATCH] libnixf: parse string interpolation --- libnixf/include/nixf/Parse/Nodes.h | 22 +++++++++++++--------- libnixf/lib/Parse/Parser.cpp | 19 +++++++++++-------- libnixf/lib/Parse/test/Parser.cpp | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/libnixf/include/nixf/Parse/Nodes.h b/libnixf/include/nixf/Parse/Nodes.h index a229b6b9c..0634107ab 100644 --- a/libnixf/include/nixf/Parse/Nodes.h +++ b/libnixf/include/nixf/Parse/Nodes.h @@ -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 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) + explicit InterpolablePart(std::shared_ptr Expr) : Kind(SPK_Interpolation), Interpolation(std::move(Expr)) {} - StringPartKind kind() { return Kind; } + [[nodiscard]] InterpolablePartKind kind() const { return Kind; } }; class InterpolatedParts : public Node { - std::vector Fragments; + std::vector Fragments; public: - InterpolatedParts(OffsetRange Range, std::vector Fragments) + InterpolatedParts(OffsetRange Range, std::vector Fragments) : Node(NK_InterpolableParts, Range), Fragments(std::move(Fragments)) {} - [[nodiscard]] const std::vector &fragments() const { + [[nodiscard]] const std::vector &fragments() const { return Fragments; }; }; diff --git a/libnixf/lib/Parse/Parser.cpp b/libnixf/lib/Parse/Parser.cpp index 8f34020d4..b59abedbb 100644 --- a/libnixf/lib/Parse/Parser.cpp +++ b/libnixf/lib/Parse/Parser.cpp @@ -147,8 +147,8 @@ 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 parseStringParts() { - std::vector Parts; + std::shared_ptr parseInterpolableParts() { + std::vector Parts; RB.push(Lex.cur()); while (true) { assert(LookAheadBuf.empty()); // We are switching contexts. @@ -156,11 +156,14 @@ class Parser { 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: { @@ -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 Parts = parseStringParts(); + std::shared_ptr Parts = parseInterpolableParts(); if (Token EndTok = peek(); EndTok.kind() == tok_dquote) { consume(); return std::make_shared(RB.finish(EndTok.end()), diff --git a/libnixf/lib/Parse/test/Parser.cpp b/libnixf/lib/Parse/test/Parser.cpp index 88947d381..48d49cf67 100644 --- a/libnixf/lib/Parse/test/Parser.cpp +++ b/libnixf/lib/Parse/test/Parser.cpp @@ -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(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