diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..fe41059ce --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +patreon: inclyc diff --git a/libnixf/include/nixf/Basic/NodeKinds.inc b/libnixf/include/nixf/Basic/NodeKinds.inc index 124eaa878..dabecdb63 100644 --- a/libnixf/include/nixf/Basic/NodeKinds.inc +++ b/libnixf/include/nixf/Basic/NodeKinds.inc @@ -10,7 +10,7 @@ NODE(InterpolableParts) /// \see Misc NODE(Misc) NODE(Dot) -NODE(Identifer) +NODE(Identifier) NODE(AttrName) NODE(AttrPath) NODE(Binding) diff --git a/libnixf/include/nixf/Basic/Nodes/Basic.h b/libnixf/include/nixf/Basic/Nodes/Basic.h index 4a65686e2..23988d48b 100644 --- a/libnixf/include/nixf/Basic/Nodes/Basic.h +++ b/libnixf/include/nixf/Basic/Nodes/Basic.h @@ -116,7 +116,7 @@ class Identifier : public Node { public: Identifier(LexerCursorRange Range, std::string Name) - : Node(NK_Identifer, Range), Name(std::move(Name)) {} + : Node(NK_Identifier, Range), Name(std::move(Name)) {} [[nodiscard]] const std::string &name() const { return Name; } [[nodiscard]] ChildVector children() const override { return {}; } diff --git a/libnixf/include/nixf/Basic/TokenKinds.inc b/libnixf/include/nixf/Basic/TokenKinds.inc index 035cc2c64..b04fc57d6 100644 --- a/libnixf/include/nixf/Basic/TokenKinds.inc +++ b/libnixf/include/nixf/Basic/TokenKinds.inc @@ -93,4 +93,8 @@ TOK_BIN_OP(mul) // * TOK_BIN_OP(div) // / TOK_BIN_OP(concat) // ++ +// [RFC 0418 Pipe operator](https://github.com/NixOS/rfcs/pull/148) +TOK_BIN_OP(pipe_into) // |> +TOK_BIN_OP(pipe_from) // <| + #endif // TOK_BIN_OP diff --git a/libnixf/src/Basic/diagnostic.py b/libnixf/src/Basic/diagnostic.py index c55e11795..58f82e9cc 100644 --- a/libnixf/src/Basic/diagnostic.py +++ b/libnixf/src/Basic/diagnostic.py @@ -87,6 +87,12 @@ class Diagnostic(TypedDict): "severity": "Error", "message": "extra `@` for lambda arg", }, + { + "sname": "parse-operator-noassoc", + "cname": "OperatorNotAssociative", + "severity": "Error", + "message": "operator is non-associative", + }, { "sname": "let-dynamic", "cname": "LetDynamic", @@ -198,14 +204,14 @@ class Diagnostic(TypedDict): { "sname": "sema-unused-def-lambda-witharg-formal", "cname": "UnusedDefLambdaWithArg_Formal", - "severity": "Warning", - "message": "argument `{}` in `@`-pattern is not used", + "severity": "Hint", + "message": "attribute `{}` of `@`-pattern argument is not used, but may be referenced from the argument", }, { "sname": "sema-unused-def-lambda-witharg-arg", "cname": "UnusedDefLambdaWithArg_Arg", - "severity": "Hint", - "message": "attribute `{}` of `@`-pattern argument is not used, but may be referenced from the argument", + "severity": "Warning", + "message": "argument `{}` in `@`-pattern is not used", }, { "sname": "sema-extra-rec", diff --git a/libnixf/src/Parse/Lexer.cpp b/libnixf/src/Parse/Lexer.cpp index 9042c440b..971377bbd 100644 --- a/libnixf/src/Parse/Lexer.cpp +++ b/libnixf/src/Parse/Lexer.cpp @@ -526,6 +526,8 @@ Token Lexer::lex() { case '|': if (consumePrefix("||")) Tok = tok_op_or; + if (consumePrefix("|>")) + Tok = tok_op_pipe_into; break; case '!': if (consumePrefix("!=")) { @@ -538,6 +540,8 @@ Token Lexer::lex() { case '<': if (consumePrefix("<=")) { Tok = tok_op_le; + } else if (consumePrefix("<|")) { + Tok = tok_op_pipe_from; } else { consume(); Tok = tok_op_lt; diff --git a/libnixf/src/Parse/ParseOp.cpp b/libnixf/src/Parse/ParseOp.cpp index 45e7f0b77..772a6cf7e 100644 --- a/libnixf/src/Parse/ParseOp.cpp +++ b/libnixf/src/Parse/ParseOp.cpp @@ -3,6 +3,7 @@ #include "Parser.h" +#include "nixf/Basic/Diagnostic.h" #include "nixf/Basic/Nodes/Op.h" #include @@ -16,6 +17,7 @@ namespace { /// Binary operators: /// +/// %left |> | %right <| /// %right -> /// %left || /// %left && @@ -30,31 +32,35 @@ namespace { /// %nonassoc NEGATE std::pair getBP(TokenKind Kind) { switch (Kind) { - case tok_op_impl: // %right -> + case tok_op_pipe_from: + return {1, 2}; + case tok_op_pipe_into: return {2, 1}; + case tok_op_impl: // %right -> + return {4, 3}; case tok_op_or: // %left || - return {3, 4}; - case tok_op_and: // %left && return {5, 6}; + case tok_op_and: // %left && + return {7, 8}; case tok_op_eq: // %nonassoc == != case tok_op_neq: - return {7, 7}; + return {9, 9}; case tok_op_lt: // %nonassoc < > <= >= case tok_op_le: case tok_op_ge: case tok_op_gt: - return {8, 8}; + return {10, 10}; case tok_op_update: // %right // - return {10, 9}; - // %left NOT - 11 + return {12, 11}; + // %left NOT - 13 case tok_op_add: // %left + - case tok_op_negate: - return {12, 13}; - case tok_op_mul: // %left * / return {14, 15}; + case tok_op_mul: // %left * / + return {16, 17}; case tok_op_div: case tok_op_concat: // %right ++ - return {17, 16}; + return {19, 18}; // % op_negate default: __builtin_unreachable(); @@ -115,7 +121,9 @@ std::shared_ptr Parser::parseExprOpBP(unsigned LeftRBP) { if (LeftRBP > LBP) return Prefix; if (LeftRBP == LBP) { - // TODO: noassoc + // Report error, operator OP and expr_op is not associative. + Diags.emplace_back(Diagnostic::DK_OperatorNotAssociative, + Tok.range()); } consume(); assert(LastToken && "consume() should have set LastToken"); diff --git a/libnixf/test/Basic/Nodes.cpp b/libnixf/test/Basic/Nodes.cpp index c50be5a0c..71796fa24 100644 --- a/libnixf/test/Basic/Nodes.cpp +++ b/libnixf/test/Basic/Nodes.cpp @@ -15,7 +15,7 @@ TEST(Node, Descend) { std::vector Diag; auto Root = parse(Src, Diag); - ASSERT_EQ(Root->descend({{0, 2}, {0, 2}})->kind(), Node::NK_Identifer); + ASSERT_EQ(Root->descend({{0, 2}, {0, 2}})->kind(), Node::NK_Identifier); ASSERT_EQ(Root->descend({{0, 2}, {0, 4}})->kind(), Node::NK_Binding); } diff --git a/libnixf/test/Parse/ParseOp.cpp b/libnixf/test/Parse/ParseOp.cpp index 840f7fb91..1661297da 100644 --- a/libnixf/test/Parse/ParseOp.cpp +++ b/libnixf/test/Parse/ParseOp.cpp @@ -2,6 +2,7 @@ #include "Parser.h" +#include "nixf/Basic/Diagnostic.h" #include "nixf/Basic/Nodes/Op.h" namespace { @@ -71,4 +72,88 @@ TEST(Parser, OpHasAttr_empty) { ASSERT_EQ(Diags.size(), 0); } +TEST(Parser, Op_NonAssociative) { + auto Src = R"(1 == 1 == 1)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + ASSERT_EQ(Diags.size(), 1); + ASSERT_EQ(Diags[0].kind(), Diagnostic::DK_OperatorNotAssociative); +} + +TEST(Parser, Op_PipeOperator_Forward) { + auto Src = R"(a |> b)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_EQ(AST->kind(), Node::NK_ExprBinOp); + ASSERT_EQ(Diags.size(), 0); +} + +TEST(Parser, Op_PipeOperator_Forward_LeftAssosiative) { + auto Src = R"(a |> b |> c)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprBinOp); + + const auto &BinOp = static_cast(*AST); + ASSERT_EQ(BinOp.lhs()->kind(), Node::NK_ExprVar); + ASSERT_EQ(Diags.size(), 0); +} + +TEST(Parser, Op_PipeOperator_Backward) { + auto Src = R"(a <| b)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprBinOp); + ASSERT_EQ(Diags.size(), 0); +} + +TEST(Parser, Op_PipeOperator_Forward_RightAssosiative) { + auto Src = R"(a <| b <| c)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprBinOp); + + const auto &BinOp = static_cast(*AST); + ASSERT_EQ(BinOp.lhs()->kind(), Node::NK_ExprBinOp); + ASSERT_EQ(BinOp.rhs()->kind(), Node::NK_ExprVar); + ASSERT_EQ(Diags.size(), 0); +} + +TEST(Parser, Op_PipeOperator_NonAssociative) { + auto Src = R"(a <| b |> c)"sv; + + std::vector Diags; + Parser P(Src, Diags); + auto AST = P.parseExpr(); + + ASSERT_TRUE(AST); + + ASSERT_EQ(AST->kind(), Node::NK_ExprBinOp); + + ASSERT_EQ(Diags.size(), 1); + ASSERT_EQ(Diags[0].kind(), nixf::Diagnostic::DK_OperatorNotAssociative); +} + } // namespace diff --git a/libnixf/test/Sema/ParentMap.cpp b/libnixf/test/Sema/ParentMap.cpp index 2a73451e5..29a31932c 100644 --- a/libnixf/test/Sema/ParentMap.cpp +++ b/libnixf/test/Sema/ParentMap.cpp @@ -19,7 +19,7 @@ TEST_F(ParentMapTest, Basic) { PMA.runOnAST(*AST); const Node *ID = AST->descend({{0, 0}, {0, 1}}); - ASSERT_EQ(ID->kind(), Node::NK_Identifer); + ASSERT_EQ(ID->kind(), Node::NK_Identifier); const Node *Var = PMA.upExpr(*ID); diff --git a/libnixf/test/Sema/VariableLookup.cpp b/libnixf/test/Sema/VariableLookup.cpp index 89e596bd5..2b1c3c48a 100644 --- a/libnixf/test/Sema/VariableLookup.cpp +++ b/libnixf/test/Sema/VariableLookup.cpp @@ -226,7 +226,7 @@ TEST_F(VLATest, ToDefLambda) { ASSERT_TRUE(AST); const Node *ID = AST->descend({{0, 1}, {0, 1}}); - ASSERT_EQ(ID->kind(), Node::NK_Identifer); + ASSERT_EQ(ID->kind(), Node::NK_Identifier); const auto *Def = VLA.toDef(*ID); ASSERT_TRUE(Def); diff --git a/nixd/docs/configuration.md b/nixd/docs/configuration.md index f13866b94..d162b25af 100644 --- a/nixd/docs/configuration.md +++ b/nixd/docs/configuration.md @@ -235,4 +235,14 @@ In our option system, you need to specify which option set you'd like to use. } ``` +If you aren't a flakes user with standalone home-manager with a vanilla install then the following expression should make home-manager options appear: +```jsonc +{ + "options": { + "home-manager": { + "expr": "(import { configuration = ~/.config/home-manager/home.nix; pkgs = import {}; }).options" + } + } +} +``` diff --git a/nixd/docs/editors/nvim-lsp.nix b/nixd/docs/editors/nvim-lsp.nix index b2d0e2f57..e768a0d45 100644 --- a/nixd/docs/editors/nvim-lsp.nix +++ b/nixd/docs/editors/nvim-lsp.nix @@ -11,7 +11,9 @@ let ''; packages.myPlugins.start = with pkgs.vimPlugins; [ - (nvim-treesitter.withPlugins (parsers: [ parsers.nix ])) + (nvim-treesitter.withPlugins ( + parsers: builtins.attrValues { inherit (parsers) nix markdown markdown_inline; } + )) friendly-snippets luasnip nvim-cmp diff --git a/nixd/lib/Controller/AST.cpp b/nixd/lib/Controller/AST.cpp index e941b1319..a8a2200f0 100644 --- a/nixd/lib/Controller/AST.cpp +++ b/nixd/lib/Controller/AST.cpp @@ -272,7 +272,7 @@ nixd::Selector nixd::idioms::mkSelector(const nixf::ExprSelect &Sel, std::pair, std::string> nixd::getScopeAndPrefix(const Node &N, const ParentMapAnalysis &PM) { - if (N.kind() != Node::NK_Identifer) + if (N.kind() != Node::NK_Identifier) return {}; // FIXME: impl scoped packages diff --git a/nixd/lspserver/include/lspserver/Connection.h b/nixd/lspserver/include/lspserver/Connection.h index 84978ab29..e71e0a6c9 100644 --- a/nixd/lspserver/include/lspserver/Connection.h +++ b/nixd/lspserver/include/lspserver/Connection.h @@ -18,7 +18,7 @@ enum class JSONStreamStyle { }; /// Parsed & classfied messages are dispatched to this handler class -/// LSP Servers should inherit from this hanlder and dispatch +/// LSP Servers should inherit from this handler and dispatch /// notify/call/reply to implementations. class MessageHandler { public: @@ -60,7 +60,7 @@ class InboundPort { /// Dispatch messages to on{Notify,Call,Reply} ( \p Handlers) /// Return values should be forwarded from \p Handlers /// i.e. returns true to keep processing messages, or false to shut down. - bool dispatch(llvm::json::Value Message, MessageHandler &Hanlder); + bool dispatch(llvm::json::Value Message, MessageHandler &Handler); void loop(MessageHandler &Handler); }; diff --git a/nixd/tools/nixd/test/hover/1.md b/nixd/tools/nixd/test/hover/1.md index 9bdd4e05b..7bc1950d5 100644 --- a/nixd/tools/nixd/test/hover/1.md +++ b/nixd/tools/nixd/test/hover/1.md @@ -61,7 +61,7 @@ CHECK-NEXT: "jsonrpc": "2.0", CHECK-NEXT: "result": { CHECK-NEXT: "contents": { CHECK-NEXT: "kind": "markdown", -CHECK-NEXT: "value": "`Identifer`" +CHECK-NEXT: "value": "`Identifier`" CHECK-NEXT: }, CHECK-NEXT: "range": { CHECK-NEXT: "end": {