diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index e2ed838..063fce2 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,16 +3,16 @@
"isRoot": true,
"tools": {
"fantomas": {
- "version": "6.3.0-alpha-008",
+ "version": "6.3.7",
"commands": [
"fantomas"
]
},
"fsharp-analyzers": {
- "version": "0.25.0",
+ "version": "0.26.0",
"commands": [
"fsharp-analyzers"
]
}
}
-}
\ No newline at end of file
+}
diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml
index 1371cae..d454a97 100644
--- a/.github/workflows/dotnet.yaml
+++ b/.github/workflows/dotnet.yaml
@@ -1,3 +1,4 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
name: .NET
on:
@@ -146,7 +147,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: nuget-package
- path: PrattParser/bin/Release/PrattParser.*.nupkg
+ path: PrattParser/bin/Release/WoofWare.PrattParser.*.nupkg
expected-pack:
needs: [nuget-pack]
@@ -158,11 +159,63 @@ jobs:
name: nuget-package
- name: Check NuGet contents
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
- run: if [[ $(find . -maxdepth 1 -name 'PrattParser.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
+ run: if [[ $(find . -maxdepth 1 -name 'WoofWare.PrattParser.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
+
+ github-release-plugin-dry-run:
+ needs: [nuget-pack]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Download NuGet artifact (plugin)
+ uses: actions/download-artifact@v4
+ with:
+ name: nuget-package
+ - name: Tag and release
+ env:
+ DRY_RUN: 1
+ GITHUB_TOKEN: mock-token
+ run: sh .github/workflows/tag.sh
all-required-checks-complete:
- needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack]
+ needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack, github-release-plugin-dry-run]
runs-on: ubuntu-latest
steps:
- run: echo "All required checks complete."
+ nuget-publish:
+ runs-on: ubuntu-latest
+ if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
+ needs: [all-required-checks-complete]
+ environment: main-deploy
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install Nix
+ uses: cachix/install-nix-action@V27
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Download NuGet artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: nuget-package
+ path: packed
+ - name: Publish to NuGet
+ run: nix develop --command dotnet nuget push "packed/WoofWare.PrattParser.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+
+ github-release:
+ runs-on: ubuntu-latest
+ if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
+ needs: [all-required-checks-complete]
+ environment: main-deploy
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - name: Download NuGet artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: nuget-package
+ - name: Tag and release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: sh .github/workflows/tag.sh
diff --git a/.github/workflows/tag.sh b/.github/workflows/tag.sh
new file mode 100644
index 0000000..f70483e
--- /dev/null
+++ b/.github/workflows/tag.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+
+echo "Dry-run? $DRY_RUN!"
+
+find . -maxdepth 1 -type f ! -name "$(printf "*\n*")" -name '*.nupkg' | while IFS= read -r file
+do
+ tag=$(basename "$file" .nupkg)
+ git tag "$tag"
+ ${DRY_RUN:+echo} git push origin "$tag"
+done
+
+export TAG
+TAG=$(find . -maxdepth 1 -type f -name 'WoofWare.PrattParser.*.nupkg' -exec sh -c 'basename "$1" .nupkg' shell {} \; | grep -v Attributes)
+
+case "$TAG" in
+ *"
+"*)
+ echo "Error: TAG contains a newline; multiple plugins found."
+ exit 1
+ ;;
+esac
+
+# target_commitish empty indicates the repo default branch
+curl_body='{"tag_name":"'"$TAG"'","target_commitish":"","name":"'"$TAG"'","draft":false,"prerelease":false,"generate_release_notes":false}'
+
+echo "cURL body: $curl_body"
+
+failed_output=$(cat <<'EOF'
+{
+ "message": "Validation Failed",
+ "errors": [
+ {
+ "resource": "Release",
+ "code": "already_exists",
+ "field": "tag_name"
+ }
+ ],
+ "documentation_url": "https://docs.github.com/rest/releases/releases#create-a-release"
+}
+EOF
+)
+
+success_output=$(cat <<'EOF'
+{
+ "url": "https://api.github.com/repos/Smaug123/fsharp-prattparser/releases/158152116",
+ "assets_url": "https://api.github.com/repos/Smaug123/fsharp-prattparser/releases/158152116/assets",
+ "upload_url": "https://uploads.github.com/repos/Smaug123/fsharp-prattparser/releases/158152116/assets{?name,label}",
+ "html_url": "https://github.com/Smaug123/fsharp-prattparser/releases/tag/WoofWare.PrattParser.2.1.30",
+ "id": 158152116,
+ "author": {
+ "login": "github-actions[bot]",
+ "id": 41898282,
+ "node_id": "MDM6Qm90NDE4OTgyODI=",
+ "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/github-actions%5Bbot%5D",
+ "html_url": "https://github.com/apps/github-actions",
+ "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
+ "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
+ "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
+ "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
+ "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
+ "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
+ "type": "Bot",
+ "site_admin": false
+ },
+ "node_id": "RE_kwDOJfksgc4JbTW0",
+ "tag_name": "WoofWare.PrattParser.2.1.30",
+ "target_commitish": "main",
+ "name": "WoofWare.PrattParser.2.1.30",
+ "draft": false,
+ "prerelease": false,
+ "created_at": "2024-05-30T11:00:55Z",
+ "published_at": "2024-05-30T11:03:02Z",
+ "assets": [
+
+ ],
+ "tarball_url": "https://api.github.com/repos/Smaug123/fsharp-prattparser/tarball/WoofWare.PrattParser.2.1.30",
+ "zipball_url": "https://api.github.com/repos/Smaug123/fsharp-prattparser/zipball/WoofWare.PrattParser.2.1.30",
+ "body": null
+}
+EOF
+)
+
+HANDLE_OUTPUT=''
+handle_error() {
+ ERROR_OUTPUT="$1"
+ exit_message=$(echo "$ERROR_OUTPUT" | jq -r --exit-status 'if .errors | length == 1 then .errors[0].code else null end')
+ if [ "$exit_message" = "already_exists" ] ; then
+ HANDLE_OUTPUT="Did not create GitHub release because it already exists at this version."
+ else
+ echo "Unexpected error output from curl: $(cat curl_output.json)"
+ echo "JQ output: $(exit_message)"
+ exit 2
+ fi
+}
+
+run_tests() {
+ handle_error "$failed_output"
+ if [ "$HANDLE_OUTPUT" != "Did not create GitHub release because it already exists at this version." ]; then
+ echo "Bad output from handler: $HANDLE_OUTPUT"
+ exit 3
+ fi
+ HANDLE_OUTPUT=''
+ echo "Tests passed."
+}
+
+run_tests
+
+if [ "$DRY_RUN" != 1 ] ; then
+ if curl --fail-with-body -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/Smaug123/fsharp-prattparser/releases -d "$curl_body" > curl_output.json; then
+ echo "Curl succeeded."
+ else
+ handle_error "$(cat curl_output.json)"
+ echo "$HANDLE_OUTPUT"
+ fi
+fi
diff --git a/PrattParser.Test/PrattParser.Test.fsproj b/PrattParser.Test/PrattParser.Test.fsproj
index 40ea26b..137e84e 100644
--- a/PrattParser.Test/PrattParser.Test.fsproj
+++ b/PrattParser.Test/PrattParser.Test.fsproj
@@ -11,9 +11,11 @@
+
+
diff --git a/PrattParser.Test/TestSurface.fs b/PrattParser.Test/TestSurface.fs
new file mode 100644
index 0000000..5a8238b
--- /dev/null
+++ b/PrattParser.Test/TestSurface.fs
@@ -0,0 +1,26 @@
+namespace PrattParser.Test
+
+open NUnit.Framework
+open PrattParser
+open ApiSurface
+
+[]
+module TestSurface =
+ let assembly = typedefof>.Assembly
+
+ []
+ let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
+
+ []
+ []
+ // https://github.com/nunit/nunit3-vs-adapter/issues/876
+ let CheckVersionAgainstRemote () =
+ MonotonicVersion.validate assembly "WoofWare.PrattParser"
+
+ []
+ let ``Update API surface`` () =
+ ApiSurface.writeAssemblyBaseline assembly
+
+ []
+ let ``Ensure public API is fully documented`` () =
+ DocCoverage.assertFullyDocumented assembly
diff --git a/PrattParser/PrattParser.fsproj b/PrattParser/PrattParser.fsproj
index c461129..03c9a46 100644
--- a/PrattParser/PrattParser.fsproj
+++ b/PrattParser/PrattParser.fsproj
@@ -1,12 +1,33 @@
-
+
- net8.0
+ netstandard2.0
true
+ true
+ Patrick Stevens
+ Copyright (c) Patrick Stevens 2024
+ For the easy construction of Pratt parsers.
+ git
+ https://github.com/Smaug123/fsharp-prattparser
+ MIT
+ README.md
+ fsharp;pratt-parser;parsing;parser;top-down;precedence;recursive;descent
+ FS3559
+ WoofWare.PrattParser
+
+
+
+ True
+ \
+
+
+
+
+
diff --git a/PrattParser/SurfaceBaseline.txt b/PrattParser/SurfaceBaseline.txt
new file mode 100644
index 0000000..08bf0cf
--- /dev/null
+++ b/PrattParser/SurfaceBaseline.txt
@@ -0,0 +1,18 @@
+PrattParser.BracketLikeParser`2 inherit obj
+PrattParser.BracketLikeParser`2..ctor [constructor]: (bool, bool, 'tokenTag list, 'expr list -> 'expr)
+PrattParser.BracketLikeParser`2.BoundaryTokens [property]: [read-only] 'tokenTag list
+PrattParser.BracketLikeParser`2.Construct [property]: [read-only] 'expr list -> 'expr
+PrattParser.BracketLikeParser`2.ConsumeAfterFinalToken [property]: [read-only] bool
+PrattParser.BracketLikeParser`2.ConsumeBeforeInitialToken [property]: [read-only] bool
+PrattParser.BracketLikeParser`2.get_BoundaryTokens [method]: unit -> 'tokenTag list
+PrattParser.BracketLikeParser`2.get_Construct [method]: unit -> ('expr list -> 'expr)
+PrattParser.BracketLikeParser`2.get_ConsumeAfterFinalToken [method]: unit -> bool
+PrattParser.BracketLikeParser`2.get_ConsumeBeforeInitialToken [method]: unit -> bool
+PrattParser.Parser inherit obj
+PrattParser.Parser.execute [static method]: PrattParser.Parser<'tokenTag, 'token, 'expr> -> string -> 'token list -> ('expr * 'token list)
+PrattParser.Parser.make [static method]: ('token -> 'tokenTag) -> (string -> 'token -> 'expr option) -> PrattParser.Parser<'tokenTag, 'token, 'expr>
+PrattParser.Parser.withBracketLike [static method]: 'tokenTag -> PrattParser.BracketLikeParser<'tokenTag, 'expr> -> PrattParser.Parser<'tokenTag, 'token, 'expr> -> PrattParser.Parser<'tokenTag, 'token, 'expr>
+PrattParser.Parser.withInfix [static method]: 'tokenTag -> (int, int) -> ('expr -> 'expr -> 'expr) -> PrattParser.Parser<'tokenTag, 'token, 'expr> -> PrattParser.Parser<'tokenTag, 'token, 'expr>
+PrattParser.Parser.withUnaryPostfix [static method]: 'tokenTag -> (int, unit) -> ('expr -> 'expr) -> PrattParser.Parser<'tokenTag, 'token, 'expr> -> PrattParser.Parser<'tokenTag, 'token, 'expr>
+PrattParser.Parser.withUnaryPrefix [static method]: 'tokenTag -> (unit, int) -> ('expr -> 'expr) -> PrattParser.Parser<'tokenTag, 'token, 'expr> -> PrattParser.Parser<'tokenTag, 'token, 'expr>
+PrattParser.Parser`3 inherit obj
\ No newline at end of file
diff --git a/PrattParser/version.json b/PrattParser/version.json
new file mode 100644
index 0000000..e0b6712
--- /dev/null
+++ b/PrattParser/version.json
@@ -0,0 +1,12 @@
+{
+ "version": "0.1",
+ "publicReleaseRefSpec": [
+ "^refs/heads/main$"
+ ],
+ "pathFilters": [
+ ":/Directory.Build.props",
+ ":/README.md",
+ ":/global.json",
+ ":/PrattParser/"
+ ]
+}
diff --git a/README.md b/README.md
index 6ae05e0..8adae46 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,27 @@
-# PrattParser
+# WoofWare.PrattParser
-A Pratt parser, based on https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html .
+A [Pratt parser](https://langdev.stackexchange.com/q/3254/1025), based on [Matklad's tutorial](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html).
+
+Bug reports welcome; I wouldn't exactly say this is well-tested, although it has worked correctly on the two things I've used it for so far.
+
+See [the example](PrattParser.Example/) for how you should use this.
+In brief:
+
+* [Define a lexer somehow](PrattParser.Example/Lexer.fs) (which produces a stream of `Token`s, where a `Token` is [a type you define](PrattParser.Example/Domain.fs)).
+* Define what it means for a lexeme to be *atomic* (this is [`Expr.atom`](PrattParser.Example/Example.fs)). Atomic tokens can appear on their own to form an expression all by themselves, or they can be combined or modified using operations specified by non-atomic tokens.
+* Define how the various non-atomic lexemes behave (this is `Parser.withUnaryPrefix` and friends) to combine subexpressions into larger expressions.
+
+We supply:
+
+* `Parser.withUnaryPrefix`, which specifies e.g. that the token `!` can appear as a unary prefix of another expression, and that when it does, it indicates (e.g.) negation.
+* `Parser.withUnaryPostfix`, which specifies e.g. that the token `!` can appear as a unary suffix of another expression, and that when it does, it indicates (e.g.) the factorial.
+* `Parser.withInfix`, which specifies e.g. that the token `+` can appear between two expressions, and that when it does, it indicates (e.g.) addition.
+* `Parser.withBracketLike`, which specifies e.g. that the token `(` can appear before an expression, and that it's terminated by e.g. `)`. You can also implement `if/then/else` this way: that's just a weird kind of bracket and comma (and the `else` is kind of mixfix: it needs to consume one expression *after* itself, by contrast with the closing bracket `)` which does not).
+
+When specifying how expressions combine, you also provide precedences; numerically larger precedences are higher-precedence (so they bind more tightly).
+Precedences are given as pairs, so that you can define e.g. left-associativity (resp. right-associativity) by having the infix operator bind more strongly to the right (resp. left).
+For example, if `+` has precedence (10, 5), so it binds strongly on the left and weakly on the right:
+
+* `a + b + c` is morally `a+ b+ c` because the `+` binds strongly to the left;
+* `a+ b+ c` can only be bracketed as `a+ (b+ c)` because the rightmost `+` wants to stick to the `b` more than it wants to stick to the `c`;
+* so `a + b + c` is equal to `a + (b + c)`, i.e. `+` is right-associative.
diff --git a/analyzers/analyzers.fsproj b/analyzers/analyzers.fsproj
index 8a28bd1..8c59f41 100644
--- a/analyzers/analyzers.fsproj
+++ b/analyzers/analyzers.fsproj
@@ -10,7 +10,7 @@
-
+
diff --git a/nix/deps.nix b/nix/deps.nix
index e0c59c8..432fe92 100644
--- a/nix/deps.nix
+++ b/nix/deps.nix
@@ -1,15 +1,25 @@
# This file was automatically generated by passthru.fetch-deps.
# Please dont edit it manually, your changes might get overwritten!
{fetchNuGet}: [
+ (fetchNuGet {
+ pname = "ApiSurface";
+ version = "4.0.40";
+ sha256 = "1c9z0b6minlripwrjmv4yd5w8zj4lcpak4x41izh7ygx8kgmbvx0";
+ })
(fetchNuGet {
pname = "fantomas";
- version = "6.3.0-alpha-008";
- sha256 = "075mxkk7gac41aqp4l7v22c5gwi3f1lrfq1gv1r91w53kfxgi3xc";
+ version = "6.3.7";
+ sha256 = "1z1a5bw7vwz6g8nvfgkvx66jnm4hmvn62vbyq0as60nw0jlvaidl";
})
(fetchNuGet {
pname = "fsharp-analyzers";
- version = "0.25.0";
- sha256 = "01i9yhqs7b0p9s1j9m8g3yd8w3a3xp9bp8791zmxp31l5ricjdwy";
+ version = "0.26.0";
+ sha256 = "0xgv5kvbwfdvcp6s8x7xagbbi4s3mqa4ixni6pazqvyflbgnah7b";
+ })
+ (fetchNuGet {
+ pname = "FSharp.Core";
+ version = "6.0.0";
+ sha256 = "1hjhvr39c1vpgrdmf8xln5q86424fqkvy9nirkr29vl2461d2039";
})
(fetchNuGet {
pname = "FSharp.Core";
@@ -36,6 +46,16 @@
version = "17.9.0";
sha256 = "1lls1fly2gr1n9n1xyl9k33l2v4pwfmylyzkq8v4v5ldnwkl1zdb";
})
+ (fetchNuGet {
+ pname = "Microsoft.NETCore.Platforms";
+ version = "1.1.0";
+ sha256 = "08vh1r12g6ykjygq5d3vq09zylgb84l63k49jc4v8faw9g93iqqm";
+ })
+ (fetchNuGet {
+ pname = "Microsoft.NETCore.Platforms";
+ version = "2.0.0";
+ sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0";
+ })
(fetchNuGet {
pname = "Microsoft.SourceLink.Common";
version = "8.0.0";
@@ -61,11 +81,51 @@
version = "3.6.133";
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80";
})
+ (fetchNuGet {
+ pname = "NETStandard.Library";
+ version = "2.0.3";
+ sha256 = "1fn9fxppfcg4jgypp2pmrpr6awl3qz1xmnri0cygpkwvyx27df1y";
+ })
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.1";
sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb";
})
+ (fetchNuGet {
+ pname = "Newtonsoft.Json";
+ version = "13.0.3";
+ sha256 = "0xrwysmrn4midrjal8g2hr1bbg38iyisl0svamb11arqws4w2bw7";
+ })
+ (fetchNuGet {
+ pname = "NuGet.Common";
+ version = "6.10.0";
+ sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh";
+ })
+ (fetchNuGet {
+ pname = "NuGet.Configuration";
+ version = "6.10.0";
+ sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz";
+ })
+ (fetchNuGet {
+ pname = "NuGet.Frameworks";
+ version = "6.10.0";
+ sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk";
+ })
+ (fetchNuGet {
+ pname = "NuGet.Packaging";
+ version = "6.10.0";
+ sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4";
+ })
+ (fetchNuGet {
+ pname = "NuGet.Protocol";
+ version = "6.10.0";
+ sha256 = "0hmv4q0ks9i34mfgpb13l01la9v3jjllfh1qd3aqv105xrqrdxac";
+ })
+ (fetchNuGet {
+ pname = "NuGet.Versioning";
+ version = "6.10.0";
+ sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g";
+ })
(fetchNuGet {
pname = "NUnit";
version = "4.1.0";
@@ -76,9 +136,54 @@
version = "4.5.0";
sha256 = "1srx1629s0k1kmf02nmz251q07vj6pv58mdafcr5dr0bbn1fh78i";
})
+ (fetchNuGet {
+ pname = "System.Formats.Asn1";
+ version = "6.0.0";
+ sha256 = "1vvr7hs4qzjqb37r0w1mxq7xql2b17la63jwvmgv65s1hj00g8r9";
+ })
+ (fetchNuGet {
+ pname = "System.IO.Abstractions";
+ version = "4.2.13";
+ sha256 = "0s784iphsmj4vhkrzq9q3w39vsn76w44zclx3hsygsw458zbyh4y";
+ })
+ (fetchNuGet {
+ pname = "System.IO.FileSystem.AccessControl";
+ version = "4.5.0";
+ sha256 = "1gq4s8w7ds1sp8f9wqzf8nrzal40q5cd2w4pkf4fscrl2ih3hkkj";
+ })
(fetchNuGet {
pname = "System.Reflection.Metadata";
version = "1.6.0";
sha256 = "1wdbavrrkajy7qbdblpbpbalbdl48q3h34cchz24gvdgyrlf15r4";
})
+ (fetchNuGet {
+ pname = "System.Security.AccessControl";
+ version = "4.5.0";
+ sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0";
+ })
+ (fetchNuGet {
+ pname = "System.Security.Cryptography.Pkcs";
+ version = "6.0.4";
+ sha256 = "0hh5h38pnxmlrnvs72f2hzzpz4b2caiiv6xf8y7fzdg84r3imvfr";
+ })
+ (fetchNuGet {
+ pname = "System.Security.Cryptography.ProtectedData";
+ version = "4.4.0";
+ sha256 = "1q8ljvqhasyynp94a1d7jknk946m20lkwy2c3wa8zw2pc517fbj6";
+ })
+ (fetchNuGet {
+ pname = "System.Security.Principal.Windows";
+ version = "4.5.0";
+ sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86";
+ })
+ (fetchNuGet {
+ pname = "System.Text.Encodings.Web";
+ version = "7.0.0";
+ sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl";
+ })
+ (fetchNuGet {
+ pname = "System.Text.Json";
+ version = "7.0.3";
+ sha256 = "0zjrnc9lshagm6kdb9bdh45dmlnkpwcpyssa896sda93ngbmj8k9";
+ })
]