Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.1.0 Release #160

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8dbb41b
NuGet.org key
nblumhardt Sep 6, 2021
2ca3caa
Point to newer example project
nblumhardt Sep 24, 2021
d0dc90b
Link to PromQL.Parser project
nblumhardt Nov 7, 2021
3f8bbe5
Formatting fix
nblumhardt Nov 7, 2021
36c29f8
target net6.0
KodrAus Nov 22, 2021
de17e0a
update to VS 2022 build workers
KodrAus Nov 22, 2021
7e13b82
support path-like branch names
KodrAus Nov 22, 2021
8f47ff8
Merge pull request #140 from datalust/chore/net6.0
nblumhardt Dec 21, 2021
e6682b3
removed gitter link
liammclennan Dec 24, 2021
9e05de1
remove gitter some more
liammclennan Dec 24, 2021
833d4f7
Merge pull request #143 from liammclennan/remove-gitter
nblumhardt Dec 24, 2021
e734a02
Fix arrays memory allocation.
sq735 Jan 26, 2022
002b2b4
Merge pull request #144 from sq735/fix-memory-allocation
nblumhardt Jan 26, 2022
9f0ec58
Fix divide example string
randomC0der Jun 23, 2022
598d1a2
Merge pull request #149 from randomC0der/dev
nblumhardt Jun 23, 2022
e06a482
Minor version bump - missed earlier
nblumhardt Jun 10, 2024
7653d37
Update appveyor.yml
nblumhardt Jun 10, 2024
cc58a07
changed SDK version from 6.0.100 to 8.0.300
marklauter Jun 12, 2024
465f824
added dotnet 8 TFM
marklauter Jun 12, 2024
62b3694
added dotnet 8 TFM
marklauter Jun 12, 2024
59cd73b
Merge pull request #161 from marklauter/dotnet-8
nblumhardt Jun 12, 2024
c99d1a2
Minor README formatting improvement [skip ci]
nblumhardt Jun 17, 2024
c09aad2
Add slice support for TextSpan
binarycow Aug 31, 2024
116c4e0
Remove extra newline
binarycow Sep 3, 2024
0d3af97
Merge pull request #163 from binarycow/dev
nblumhardt Sep 4, 2024
f243176
reintroduce TextCombinator as an easy shortcut for converting a char[…
Philipp-Binder Sep 30, 2024
ea59437
add the same behavior for TextSpan-parsers, to have this easy shortcu…
Philipp-Binder Sep 30, 2024
92a1909
Merge pull request #165 from Philipp-Binder/feature/reintroduce-TextC…
nblumhardt Sep 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if(Test-Path .\artifacts) {

$branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL];
$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL];
$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"]
$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)).Replace("/", "-"))-$revision"}[$branch -eq "main" -and $revision -ne "local"]

echo "build: Version suffix is $suffix"

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Superpower [![Build status](https://ci.appveyor.com/api/projects/status/7bj6if6tyc68urpy?svg=true)](https://ci.appveyor.com/project/datalust/superpower) [![Join the chat at https://gitter.im/datalust/superpower](https://img.shields.io/gitter/room/datalust/superpower.svg)](https://gitter.im/datalust/superpower) [![NuGet Version](https://img.shields.io/nuget/vpre/Superpower.svg?style=flat)](https://www.nuget.org/packages/Superpower/) [![Stack Overflow](https://img.shields.io/badge/stackoverflow-superpower-orange.svg)](http://stackoverflow.com/questions/tagged/superpower)
# Superpower [![Build status](https://ci.appveyor.com/api/projects/status/7bj6if6tyc68urpy?svg=true)](https://ci.appveyor.com/project/datalust/superpower) [![NuGet Version](https://img.shields.io/nuget/vpre/Superpower.svg?style=flat)](https://www.nuget.org/packages/Superpower/) [![Stack Overflow](https://img.shields.io/badge/stackoverflow-superpower-orange.svg)](http://stackoverflow.com/questions/tagged/superpower)

A [parser combinator](https://en.wikipedia.org/wiki/Parser_combinator) library based on
[Sprache](https://github.com/sprache/Sprache). Superpower generates friendlier error messages through its support for
Expand Down Expand Up @@ -71,7 +71,7 @@ public enum ArithmeticExpressionToken
```

A major benefit of driving parsing from tokens, instead of individual characters, is that errors can be reported in
terms of tokens - _unexpected identifier \`frm\`, expected keyword \`from\`_ - instead of the cryptic _unexpected `m`_.
terms of tokens - _unexpected identifier \`frm\`, expected keyword \`from\`_ - instead of the cryptic _unexpected \`m\`_.

Token-driven parsing takes place in two distinct steps:

Expand Down Expand Up @@ -170,8 +170,8 @@ class ArithmeticExpressionParser
static readonly TokenListParser<ArithmeticExpressionToken, Expression> Expr =
Parse.Chain(Add.Or(Subtract), Term, Expression.MakeBinary);

public static readonly TokenListParser<ArithmeticExpressionToken, Expression<Func<int>>> Lambda =
Expr.AtEnd().Select(body => Expression.Lambda<Func<int>>(body));
public static readonly TokenListParser<ArithmeticExpressionToken, Expression<Func<int>>>
Lambda = Expr.AtEnd().Select(body => Expression.Lambda<Func<int>>(body));
}
```

Expand Down Expand Up @@ -244,14 +244,15 @@ Superpower is introduced, with a worked example, in [this blog post](https://nbl

**Real-world** projects built with Superpower:

* [_Serilog.Filters.Expressions_](https://github.com/serilog/serilog-filters-expressions) uses Superpower to implement a filtering language for structured log events
* [_Serilog.Expressions_](https://github.com/serilog/serilog-expressions) uses Superpower to implement an expression and templating language for structured log events
* The query language of [Seq](https://datalust.co/seq) is implemented using Superpower
* `seqcli` [extraction patterns](https://github.com/datalust/seqcli#extraction-patterns) use Superpower for plain-text log parsing
* [_PromQL.Parser_](https://github.com/djluck/PromQL.Parser) is a parser for the Prometheus Query Language

_Have an example we can add to this list? [Let us know](https://github.com/datalust/superpower/issues/new)._

### Getting help

Please post issues [to the issue tracker](https://github.com/datalust/superpower/issues), visit our [Gitter chat](https://gitter.im/datalust/superpower), or tag your [question on StackOverflow](http://stackoverflow.com/questions/tagged/superpower) with `superpower`.
Please post issues [to the issue tracker](https://github.com/datalust/superpower/issues), or tag your [question on StackOverflow](http://stackoverflow.com/questions/tagged/superpower) with `superpower`.

_The repository's title arose out of a talk_ "Parsing Text: the Programming Superpower You Need at Your Fingertips" _given at [DDD Brisbane](http://dddbrisbane.com/) 2015._
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: '{build}'
skip_tags: true
image: Visual Studio 2019
image: Visual Studio 2022
build_script:
- ps: ./Build.ps1
test: off
Expand All @@ -9,7 +9,7 @@ artifacts:
deploy:
- provider: NuGet
api_key:
secure: h13+FoLtFJdwNtYdIUsr0xJZCtxwTBKsFKCp5jFjyx5DxqeZN4MwWZhgL+enTOJq
secure: kkMZ/vegQUkZw6TKvA+zdG3o9sI7bY7IwtDQzxD37wFyoMC+qg5rIsuUyHbq1Qcb
skip_symbols: true
on:
branch: /^(main|dev)$/
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "5.0.100",
"version": "8.0.300",
"rollForward": "latestFeature"
}
}
2 changes: 1 addition & 1 deletion sample/DateTimeTextParser/DateTimeParser.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion sample/IntCalc/ArithmeticExpressionToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ enum ArithmeticExpressionToken
[Token(Category = "operator", Example = "*")]
Times,

[Token(Category = "operator", Example = "-")]
[Token(Category = "operator", Example = "/")]
Divide,

[Token(Example = "(")]
Expand Down
2 changes: 1 addition & 1 deletion sample/IntCalc/IntCalc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion sample/JsonParser/JsonParser.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

Expand Down
26 changes: 21 additions & 5 deletions src/Superpower/Combinators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public static TokenListParser<TKind, T[]> Repeat<TKind, T>(this TokenListParser<
remainder = r.Remainder;
}

return TokenListParserResult.Value(result ?? new T[0], input, remainder);
return TokenListParserResult.Value(result ?? Array.Empty<T>(), input, remainder);
};
}

Expand Down Expand Up @@ -257,7 +257,7 @@ public static TextParser<T[]> Repeat<T>(this TextParser<T> parser, int count)
remainder = r.Remainder;
}

return Result.Value(result ?? new T[0], input, remainder);
return Result.Value(result ?? Array.Empty<T>(), input, remainder);
};
}

Expand Down Expand Up @@ -507,11 +507,11 @@ public static TokenListParser<TKind, T[]> ManyDelimitedBy<TKind, T, U>(
return parser
.AtLeastOnceDelimitedBy(delimiter)
.Then(p => end.Value(p))
.Or(end.Value(new T[0]));
.Or(end.Value(Array.Empty<T>()));

return parser
.Then(first => delimiter.IgnoreThen(parser).Many().Select(rest => ArrayEnumerable.Cons(first, rest)))
.OptionalOrDefault(new T[0]);
.OptionalOrDefault(Array.Empty<T>());
}

/// <summary>
Expand All @@ -528,9 +528,25 @@ public static TextParser<T[]> ManyDelimitedBy<T, U>(this TextParser<T> parser, T
if (delimiter == null) throw new ArgumentNullException(nameof(delimiter));

return parser.Then(first => delimiter.IgnoreThen(parser).Many().Select(rest => ArrayEnumerable.Cons(first, rest)))
.OptionalOrDefault(new T[0]);
.OptionalOrDefault(Array.Empty<T>());
}

/// <summary>
/// Constructs a parser that converts a char[]-parser to a string-parser.
/// </summary>
/// <param name="parser">The parser.</param>
/// <returns>The resulting parser.</returns>
public static TextParser<string> Text(this TextParser<char[]> parser)
=> parser.Select(chars => new string(chars));

/// <summary>
/// Constructs a parser that converts a TextSpan-parser to a string-parser.
/// </summary>
/// <param name="parser">The parser.</param>
/// <returns>The resulting parser.</returns>
public static TextParser<string> Text(this TextParser<TextSpan> parser)
=> parser.Select(textSpan => textSpan.ToStringValue());

/// <summary>
/// Construct a parser that fails with error message <paramref name="errorMessage"/> when <paramref name="parser"/> fails.
/// </summary>
Expand Down
47 changes: 47 additions & 0 deletions src/Superpower/Model/TextSpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,5 +270,52 @@ public bool EqualsValueIgnoreCase(string otherValue)
}
return true;
}

/// <summary>
/// Gets the character at the specified index in the text span.
/// </summary>
/// <param name="index">
/// The zero-based index of the character to get.
/// </param>
/// <returns>
/// The character at the specified index in the text span.
/// </returns>
public char this[int index]
{
get
{
this.EnsureHasValue();
#if CHECKED
if ((uint)index >= (uint)Length)
throw new ArgumentOutOfRangeException(nameof(index), index, "Index exceeds the source span's length.");
#endif
return Source![Position.Absolute + index];
}
}

/// <summary>
/// Forms a slice out of the current text span starting at the specified index.
/// </summary>
/// <param name="index">
/// The index at which to begin the slice.
/// </param>
/// <returns>
/// An text span that consists of all elements of the current array segment from <paramref name="index"/> to the end of the text span.
/// </returns>
public TextSpan Slice(int index)
{
return Skip(index);
}

/// <summary>
/// Forms a slice of the specified length out of the current text span starting at the specified index.
/// </summary>
/// <param name="index">The index at which to begin the slice.</param>
/// <param name="count">The desired length of the slice.</param>
/// <returns>An text span of <paramref name="count"/> elements starting at <paramref name="index"/>.</returns>
public TextSpan Slice(int index, int count)
{
return Skip(index).First(count);
}
}
}
4 changes: 2 additions & 2 deletions src/Superpower/Superpower.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<Description>A parser combinator library for C#</Description>
<VersionPrefix>3.0.0</VersionPrefix>
<VersionPrefix>3.1.0</VersionPrefix>
<Authors>Datalust;Superpower Contributors;Sprache Contributors</Authors>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
2 changes: 1 addition & 1 deletion test/Superpower.Benchmarks/Superpower.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
20 changes: 20 additions & 0 deletions test/Superpower.Tests/Combinators/TextCombinatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Superpower.Parsers;
using Superpower.Tests.Support;
using Xunit;

namespace Superpower.Tests.Combinators;

public class TextCombinatorTests
{
[Fact]
public void TextSucceedsWithAnyCharArrayInput()
{
AssertParser.SucceedsWith(Character.AnyChar.Many().Text(), "ab", "ab");
}

[Fact]
public void TextSucceedsWithTextSpanInput()
{
AssertParser.SucceedsWith(Span.Length(2).Text(), "ab", "ab");
}
}
33 changes: 33 additions & 0 deletions test/Superpower.Tests/StringSpanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,38 @@ public void ASpanIsNotEqualToADifferentString(string str, int offset, int length
var span = new TextSpan(str, new Position(offset, 1, offset + 1), length);
Assert.False(span.EqualsValue(value));
}

[Theory]
[InlineData("Hello", 0, 5)]
[InlineData("Hello", 0, 3)]
[InlineData("Hello", 1, 3)]
public void SliceWithLengthExtractsCorrectCharacters(string input, int index, int end)
{
var inputSpan = new TextSpan(input, new Position(0, 1, 1), input.Length);
var slice = inputSpan[index..end];
Assert.Equal(expected: input[index..end], actual: slice.ToStringValue());
}

[Theory]
[InlineData("Hello", 0)]
[InlineData("Hello", 2)]
[InlineData("Hello", 5)]
public void SliceWithoutLengthExtractsCorrectCharacters(string input, int index)
{
var inputSpan = new TextSpan(input, new Position(0, 1, 1), input.Length);
var slice = inputSpan[index..];
Assert.Equal(expected: input[index..], actual: slice.ToStringValue());
}

[Theory]
[InlineData("Hello", 0)]
[InlineData("Hello", 2)]
[InlineData("Hello", 4)]
public void IndexerExtractsCorrectCharacter(string input, int index)
{
var inputSpan = new TextSpan(input, new Position(0, 1, 1), input.Length);
var ch = inputSpan[index];
Assert.Equal(expected: input[index], actual: ch);
}
}
}
2 changes: 1 addition & 1 deletion test/Superpower.Tests/Superpower.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<AssemblyName>Superpower.Tests</AssemblyName>
<AssemblyOriginatorKeyFile>../../asset/Superpower.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
Expand Down