3 Commits

Author SHA1 Message Date
Patrick Stevens
50b4dc89fc Downgrade FSharp.Core (#22) 2024-06-23 12:22:37 +01:00
Patrick Stevens
2655682e32 Change namespace, and attest contents of NuGet package (#21) 2024-06-17 23:22:27 +01:00
Patrick Stevens
4d4aa25825 Add logo (#19) 2024-06-03 23:34:26 +00:00
15 changed files with 141 additions and 45 deletions

14
.github/workflows/assert-contents.sh vendored Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
echo "Unzipping version from NuGet"
ls from-nuget.nupkg
mkdir from-nuget && cp from-nuget.nupkg from-nuget/zip.zip && cd from-nuget && unzip zip.zip && rm zip.zip && cd - || exit 1
echo "Unzipping version from local build"
ls packed/
mkdir from-local && cp packed/*.nupkg from-local/zip.zip && cd from-local && unzip zip.zip && rm zip.zip && cd - || exit 1
cd from-local && find . -type f -exec sha256sum {} \; | sort > ../from-local.txt && cd .. || exit 1
cd from-nuget && find . -type f -and -not -name '.signature.p7s' -exec sha256sum {} \; | sort > ../from-nuget.txt && cd .. || exit 1
diff from-local.txt from-nuget.txt

View File

@@ -182,11 +182,34 @@ jobs:
steps:
- run: echo "All required checks complete."
attestation:
runs-on: ubuntu-latest
needs: [all-required-checks-complete]
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
permissions:
id-token: write
attestations: write
contents: read
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0
with:
subject-path: "packed/WoofWare.PrattParser.*.nupkg"
nuget-publish:
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete]
environment: main-deploy
permissions:
id-token: write
attestations: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Install Nix
@@ -200,7 +223,25 @@ jobs:
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
id: publish-success
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: 'nix develop --command bash ./.github/workflows/nuget-push.sh "packed/WoofWare.PrattParser.*.nupkg"'
- name: Wait for availability
if: steps.publish-success.outputs.result == 'published'
env:
PACKAGE_VERSION: ${{ steps.publish-success.outputs.version }}
run: 'echo "$PACKAGE_VERSION" && while ! curl -L --fail -o from-nuget.nupkg "https://www.nuget.org/api/v2/package/WoofWare.PrattParser/$PACKAGE_VERSION" ; do sleep 10; done'
# Astonishingly, NuGet.org considers it to be "more secure" to tamper with my package after upload (https://devblogs.microsoft.com/nuget/introducing-repository-signatures/).
# So we have to *re-attest* it after it's uploaded. Mind-blowing.
- name: Assert package contents
if: steps.publish-success.outputs.result == 'published'
run: 'bash ./.github/workflows/assert-contents.sh'
- name: Attest Build Provenance
if: steps.publish-success.outputs.result == 'published'
uses: actions/attest-build-provenance@897ed5eab6ed058a474202017ada7f40bfa52940 # v1.0.0
with:
subject-path: "from-nuget.nupkg"
github-release:
runs-on: ubuntu-latest

24
.github/workflows/nuget-push.sh vendored Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
SOURCE_NUPKG=$(find . -type f -name '*.nupkg')
PACKAGE_VERSION=$(basename "$SOURCE_NUPKG" | rev | cut -d '.' -f 2-4 | rev)
echo "version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
tmp=$(mktemp)
if ! dotnet nuget push "$SOURCE_NUPKG" --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json > "$tmp" ; then
cat "$tmp"
if grep 'already exists and cannot be modified' "$tmp" ; then
echo "result=skipped" >> "$GITHUB_OUTPUT"
exit 0
else
echo "Unexpected failure to upload"
exit 1
fi
fi
cat "$tmp"
echo "result=published" >> "$GITHUB_OUTPUT"

View File

@@ -2,7 +2,7 @@ namespace ParseExample
open System
open System.Globalization
open PrattParser
open WoofWare.PrattParser
[<RequireQualifiedAccess>]
module Example =

View File

@@ -1,4 +1,4 @@
namespace PrattParser.Test
namespace WoofWare.PrattParser.Test
open ParseExample
open NUnit.Framework

View File

@@ -1,7 +1,7 @@
namespace PrattParser.Test
namespace WoofWare.PrattParser.Test
open ParseExample
open PrattParser
open WoofWare.PrattParser
open NUnit.Framework
open FsUnitTyped

View File

@@ -1,7 +1,7 @@
namespace PrattParser.Test
namespace WoofWare.PrattParser.Test
open NUnit.Framework
open PrattParser
open WoofWare.PrattParser
open ApiSurface
[<TestFixture>]

9
PrattParser/Map.fs Normal file
View File

@@ -0,0 +1,9 @@
namespace WoofWare.PrattParser
[<RequireQualifiedAccess>]
module internal Map =
// For compat reasons, we target a very low FSharp.Core.
let change k f m =
match f (Map.tryFind k m) with
| None -> Map.remove k m
| Some v -> Map.add k v m

View File

@@ -1,4 +1,4 @@
namespace PrattParser
namespace WoofWare.PrattParser
/// Specification of how to parse things which act like brackets: that is, they start with a token,
/// then consume some stuff, then there's another token to mark the end.
@@ -252,8 +252,8 @@ module Parser =
| Some token -> token, rest
| None ->
match parser.BracketLike.TryGetValue (parser.GetTag firstToken) with
| true, parse ->
match Map.tryFind (parser.GetTag firstToken) parser.BracketLike with
| Some parse ->
// This is an ambiguous parse if multiple parsers genuinely matched.
// (We already filter to the longest possible matching parser.)
match parseBracketLike parser inputString parse [] rest with
@@ -263,13 +263,13 @@ module Parser =
failwithf
"Ambiguous parse for bracket-like construct. You should restrict the grammar. %+A"
firstToken
| false, _ ->
| None ->
match parser.UnaryPrefix.TryGetValue (parser.GetTag firstToken) with
| true, (((), precedence), assemble) ->
match Map.tryFind (parser.GetTag firstToken) parser.UnaryPrefix with
| Some (((), precedence), assemble) ->
let rhs, rest = parseInner parser inputString rest precedence
assemble rhs, rest
| false, _ -> failwithf "didn't get an atom or prefix, got: %+A" firstToken
| None -> failwithf "didn't get an atom or prefix, got: %+A" firstToken
let rec go (lhs : 'expr) (tokens : 'token list) : 'expr * 'token list =
match tokens with
@@ -277,30 +277,30 @@ module Parser =
| op :: rest ->
let fromBracketed =
match parser.BracketLike.TryGetValue (parser.GetTag op) with
| true, parse ->
match Map.tryFind (parser.GetTag op) parser.BracketLike with
| Some parse ->
let parse = parse |> List.filter _.ConsumeBeforeInitialToken
match parseBracketLike parser inputString parse [ lhs ] rest with
| [ result ] -> Some result
| _ :: _ -> failwithf "Ambiguous parse (multiple matches) at token %+A" op
| [] -> None
| false, _ -> None
| None -> None
match fromBracketed with
| Some (lhs, rest) -> go lhs rest
| None ->
match parser.UnaryPostfix.TryGetValue (parser.GetTag op) with
| true, ((precedence, ()), construct) ->
match Map.tryFind (parser.GetTag op) parser.UnaryPostfix with
| Some ((precedence, ()), construct) ->
if precedence < minBinding then
lhs, rest
else
go (construct lhs) rest
| false, _ ->
| None ->
match parser.Infix.TryGetValue (parser.GetTag op) with
| true, ((leftBinding, rightBinding), construct) ->
match Map.tryFind (parser.GetTag op) parser.Infix with
| Some ((leftBinding, rightBinding), construct) ->
if leftBinding < minBinding then
lhs, op :: rest
else
@@ -308,7 +308,7 @@ module Parser =
let rhs, remainingTokens = parseInner parser inputString rest rightBinding
go (construct lhs rhs) remainingTokens
| false, _ ->
| None ->
// TODO: This could be function application!
lhs, op :: rest

View File

@@ -14,9 +14,11 @@
<PackageTags>fsharp;pratt-parser;parsing;parser;top-down;precedence;recursive;descent</PackageTags>
<WarnOn>FS3559</WarnOn>
<PackageId>WoofWare.PrattParser</PackageId>
<PackageIcon>logo.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<Compile Include="Map.fs" />
<Compile Include="Parser.fs"/>
<EmbeddedResource Include="version.json" />
<EmbeddedResource Include="SurfaceBaseline.txt"/>
@@ -24,10 +26,14 @@
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="logo.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="6.0.0" />
<PackageReference Update="FSharp.Core" Version="4.3.4" />
</ItemGroup>
</Project>

View File

@@ -1,18 +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
WoofWare.PrattParser.BracketLikeParser`2 inherit obj
WoofWare.PrattParser.BracketLikeParser`2..ctor [constructor]: (bool, bool, 'tokenTag list, 'expr list -> 'expr)
WoofWare.PrattParser.BracketLikeParser`2.BoundaryTokens [property]: [read-only] 'tokenTag list
WoofWare.PrattParser.BracketLikeParser`2.Construct [property]: [read-only] 'expr list -> 'expr
WoofWare.PrattParser.BracketLikeParser`2.ConsumeAfterFinalToken [property]: [read-only] bool
WoofWare.PrattParser.BracketLikeParser`2.ConsumeBeforeInitialToken [property]: [read-only] bool
WoofWare.PrattParser.BracketLikeParser`2.get_BoundaryTokens [method]: unit -> 'tokenTag list
WoofWare.PrattParser.BracketLikeParser`2.get_Construct [method]: unit -> ('expr list -> 'expr)
WoofWare.PrattParser.BracketLikeParser`2.get_ConsumeAfterFinalToken [method]: unit -> bool
WoofWare.PrattParser.BracketLikeParser`2.get_ConsumeBeforeInitialToken [method]: unit -> bool
WoofWare.PrattParser.Parser inherit obj
WoofWare.PrattParser.Parser.execute [static method]: WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr> -> string -> 'token list -> ('expr * 'token list)
WoofWare.PrattParser.Parser.make [static method]: ('token -> 'tokenTag) -> (string -> 'token -> 'expr option) -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr>
WoofWare.PrattParser.Parser.withBracketLike [static method]: 'tokenTag -> WoofWare.PrattParser.BracketLikeParser<'tokenTag, 'expr> -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr> -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr>
WoofWare.PrattParser.Parser.withInfix [static method]: 'tokenTag -> (int, int) -> ('expr -> 'expr -> 'expr) -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr> -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr>
WoofWare.PrattParser.Parser.withUnaryPostfix [static method]: 'tokenTag -> (int, unit) -> ('expr -> 'expr) -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr> -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr>
WoofWare.PrattParser.Parser.withUnaryPrefix [static method]: 'tokenTag -> (unit, int) -> ('expr -> 'expr) -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr> -> WoofWare.PrattParser.Parser<'tokenTag, 'token, 'expr>
WoofWare.PrattParser.Parser`3 inherit obj

BIN
PrattParser/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -1,5 +1,5 @@
{
"version": "0.1",
"version": "0.2",
"publicReleaseRefSpec": [
"^refs/heads/main$"
],

View File

@@ -1,5 +1,7 @@
# WoofWare.PrattParser
![Project logo: the face of a cartoon Shiba Inu wearing glasses, looking directly at the viewer, with a background of cartoon basic mathematical symbols. The dog is holding a pencil and a notepad with more symbols written on it. At the bottom is the slogan "EXPRESSION PAWS-ER".](./PrattParser/logo.png)
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.

View File

@@ -18,8 +18,8 @@
})
(fetchNuGet {
pname = "FSharp.Core";
version = "6.0.0";
sha256 = "1hjhvr39c1vpgrdmf8xln5q86424fqkvy9nirkr29vl2461d2039";
version = "4.3.4";
sha256 = "1sg6i4q5nwyzh769g76f6c16876nvdpn83adqjr2y9x6xsiv5p5j";
})
(fetchNuGet {
pname = "FSharp.Core";