From 509eada031874902084e8cd8bb41280d09e97f76 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Tue, 9 Apr 2024 11:52:55 +0800 Subject: [PATCH] libnixf: support SPath () (#392) --- libnixf/include/nixf/Basic/NodeKinds.inc | 1 + libnixf/include/nixf/Basic/Nodes/Simple.h | 12 ++++++ libnixf/include/nixf/Basic/TokenKinds.inc | 4 ++ libnixf/src/Bytecode/Write.cpp | 18 +++++++++ libnixf/src/Parse/Lexer.cpp | 45 +++++++++++++++++++++++ libnixf/src/Parse/Lexer.h | 2 + libnixf/src/Parse/ParseSimple.cpp | 5 +++ libnixf/src/Parse/Parser.h | 1 + libnixf/test/Bytecode/Write.cpp | 14 +++++++ libnixf/test/Parse/ParseSimple.cpp | 26 +++++++++++++ 10 files changed, 128 insertions(+) diff --git a/libnixf/include/nixf/Basic/NodeKinds.inc b/libnixf/include/nixf/Basic/NodeKinds.inc index 2bec0f0e8..aab66a31d 100644 --- a/libnixf/include/nixf/Basic/NodeKinds.inc +++ b/libnixf/include/nixf/Basic/NodeKinds.inc @@ -29,6 +29,7 @@ EXPR(ExprFloat) EXPR(ExprVar) EXPR(ExprString) EXPR(ExprPath) +EXPR(ExprSPath) EXPR(ExprParen) EXPR(ExprAttrs) EXPR(ExprSelect) diff --git a/libnixf/include/nixf/Basic/Nodes/Simple.h b/libnixf/include/nixf/Basic/Nodes/Simple.h index 3b0832b0b..3d0a1b9b5 100644 --- a/libnixf/include/nixf/Basic/Nodes/Simple.h +++ b/libnixf/include/nixf/Basic/Nodes/Simple.h @@ -157,6 +157,18 @@ class ExprPath : public Expr { [[nodiscard]] ChildVector children() const override { return {}; } }; +class ExprSPath : public Expr { + std::string Text; + +public: + ExprSPath(LexerCursorRange Range, std::string Text) + : Expr(NK_ExprSPath, Range), Text(std::move(Text)) {} + + [[nodiscard]] ChildVector children() const override { return {}; } + + [[nodiscard]] const std::string &text() const { return Text; } +}; + class ExprParen : public Expr { const std::shared_ptr E; const std::shared_ptr LParen; diff --git a/libnixf/include/nixf/Basic/TokenKinds.inc b/libnixf/include/nixf/Basic/TokenKinds.inc index adce6e852..035cc2c64 100644 --- a/libnixf/include/nixf/Basic/TokenKinds.inc +++ b/libnixf/include/nixf/Basic/TokenKinds.inc @@ -40,6 +40,10 @@ TOK(quote2) // '' // Path. TOK(path_fragment) + +// +TOK(spath) + // URI. TOK(uri) diff --git a/libnixf/src/Bytecode/Write.cpp b/libnixf/src/Bytecode/Write.cpp index dfc632f86..2d3faaecb 100644 --- a/libnixf/src/Bytecode/Write.cpp +++ b/libnixf/src/Bytecode/Write.cpp @@ -116,6 +116,21 @@ void writeExprPath(std::ostream &OS, const ExprPath &Path) { } } +void writeExprSPath(std::ostream &OS, const ExprSPath &SPath) { + // Spath -> (Call __findFile, __nixPath, path) + writeBytecode(OS, mkHeader(EK_Call, SPath)); + + writeBytecode(OS, mkHeader(EK_Var, SPath)); + writeBytecode(OS, std::string_view("__findFile")); + + writeBytecode(OS, std::size_t(2)); + writeBytecode(OS, mkHeader(EK_Var, SPath)); + writeBytecode(OS, std::string_view("__nixPath")); + + writeBytecode(OS, mkHeader(EK_String, SPath)); + writeBytecode(OS, SPath.text()); +} + void writeExprVar(std::ostream &OS, const ExprVar &Var) { writeBytecode(OS, mkHeader(EK_Var, Var)); writeBytecode(OS, Var.id().name()); @@ -447,6 +462,9 @@ void nixf::writeBytecode(std::ostream &OS, const Node *N) { case Node::NK_ExprPath: writeExprPath(OS, static_cast(*N)); break; + case Node::NK_ExprSPath: + writeExprSPath(OS, static_cast(*N)); + break; case Node::NK_ExprVar: writeExprVar(OS, static_cast(*N)); break; diff --git a/libnixf/src/Parse/Lexer.cpp b/libnixf/src/Parse/Lexer.cpp index 01331effc..eaa9b0aee 100644 --- a/libnixf/src/Parse/Lexer.cpp +++ b/libnixf/src/Parse/Lexer.cpp @@ -270,6 +270,41 @@ bool Lexer::consumeURI() { return false; } +bool Lexer::consumeSPath() { + // \<{PATH_CHAR}+(\/{PATH_CHAR}+)*\> + LexerCursor Saved = cur(); + + if (peek() == '<') + consume(); + + if (!eof() && isPathChar(peekUnwrap())) { + // {PATH_CHAR}+ + while (!eof() && isPathChar(peekUnwrap())) + consume(); + // (\/{PATH_CHAR}+)* + while (true) { + // \/ + if (peek() == '/') { + consume(); + // {PATH_CHAR}+ + if (!eof() && isPathChar(peekUnwrap())) { + while (!eof() && isPathChar(peekUnwrap())) + consume(); + continue; + } + } + break; + } + if (peek() == '>') { + consume(); + return true; + } + } + + Cur = Saved; + return false; +} + void Lexer::lexIdentifier() { // identifier: [a-zA-Z_][a-zA-Z0-9_\'\-]*, consume(); @@ -446,6 +481,16 @@ Token Lexer::lex() { return finishToken(); } + if (*Ch == '<') { + // Perhaps this is an "SPATH". + // e.g. + // \<{PATH_CHAR}+(\/{PATH_CHAR}+)*\> + if (consumeSPath()) { + Tok = tok_spath; + return finishToken(); + } + } + switch (*Ch) { case '\'': if (consumePrefix("''")) diff --git a/libnixf/src/Parse/Lexer.h b/libnixf/src/Parse/Lexer.h index f5e4378d7..fbefffd6c 100644 --- a/libnixf/src/Parse/Lexer.h +++ b/libnixf/src/Parse/Lexer.h @@ -90,6 +90,8 @@ class Lexer { bool consumeURI(); + bool consumeSPath(); + /// Should be called after lexing a "raw" identifier, we check if it is a /// keyword and make assignment: `Tok <- tok_kw_*` void maybeKW(); diff --git a/libnixf/src/Parse/ParseSimple.cpp b/libnixf/src/Parse/ParseSimple.cpp index a09c546a4..7cf7f262d 100644 --- a/libnixf/src/Parse/ParseSimple.cpp +++ b/libnixf/src/Parse/ParseSimple.cpp @@ -81,6 +81,11 @@ std::shared_ptr Parser::parseExprSimple() { NixFloat N = std::strtof(std::string(Tok.view()).c_str(), nullptr); return std::make_shared(Tok.range(), N); } + case tok_spath: { + consume(); + return std::make_shared( + Tok.range(), std::string(Tok.view().substr(1, Tok.view().size() - 2))); + } case tok_dquote: // " - normal strings return parseString(/*IsIndented=*/false); case tok_quote2: // '' - indented strings diff --git a/libnixf/src/Parse/Parser.h b/libnixf/src/Parse/Parser.h index 648f2860b..5587e2999 100644 --- a/libnixf/src/Parse/Parser.h +++ b/libnixf/src/Parse/Parser.h @@ -233,6 +233,7 @@ class Parser { /// | string /// | indented_string /// | path + /// | spath /// | hpath /// | uri /// | '(' expr ')' diff --git a/libnixf/test/Bytecode/Write.cpp b/libnixf/test/Bytecode/Write.cpp index 3a04f5b0b..11e13fd6b 100644 --- a/libnixf/test/Bytecode/Write.cpp +++ b/libnixf/test/Bytecode/Write.cpp @@ -116,6 +116,20 @@ TEST_F(WriteTest, SelectNullAttr) { ASSERT_EQ(Kind, nixbc::ExprKind::EK_Select); } +TEST_F(WriteTest, SPath) { + auto AST = nixf::parse(R"()", Diags); + ASSERT_TRUE(AST); + nixf::writeBytecode(OS, AST.get()); + std::string Str = OS.str(); + std::string_view Data(Str); + + ASSERT_EQ(Str.size(), 154); + + auto Kind = bc::eat(Data); + + ASSERT_EQ(Kind, nixbc::ExprKind::EK_Call); +} + const char *AllGrammar = R"( let x = 1; diff --git a/libnixf/test/Parse/ParseSimple.cpp b/libnixf/test/Parse/ParseSimple.cpp index 15e07a118..962924ba7 100644 --- a/libnixf/test/Parse/ParseSimple.cpp +++ b/libnixf/test/Parse/ParseSimple.cpp @@ -294,6 +294,32 @@ TEST(Parser, PathOK) { ASSERT_EQ(Parts.fragments()[2].escaped(), "/d"); } +TEST(Parser, SPath) { + auto Src = R"()"sv; + + std::vector Diags; + auto AST = nixf::parse(Src, Diags); + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprSPath); + + auto &SPath = static_cast(*AST); + ASSERT_EQ(SPath.text(), "nixpkgs"); +} + +TEST(Parser, SPath2) { + auto Src = R"()"sv; + + std::vector Diags; + auto AST = nixf::parse(Src, Diags); + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprSPath); + + auto &SPath = static_cast(*AST); + ASSERT_EQ(SPath.text(), "nixpkgs/a/b"); +} + TEST(Parser, ParenExpr) { auto Src = R"((1))"sv;