datalust / superpower

A C# parser construction toolkit with high-quality error reporting
Apache License 2.0
1.08k stars 99 forks source link

Add index/range support for TextSpan #163

Closed binarycow closed 2 months ago

binarycow commented 2 months ago

This PR adds to TextSpan, an indexer, and support for the range operator (..)

Exactly how well this is supported depends on the target framework of the application that uses this library:

nblumhardt commented 2 months ago

Thanks for the PR! Are there any places in the Superpower codebase, or typical usage patterns, where slicing is useful with TextSpan? (Just a standard consideration when adding any feature, to keep the library nice and compact.) Thanks!

binarycow commented 2 months ago

Are there any places in the Superpower codebase, or typical usage patterns, where slicing is useful with TextSpan?

Well...

Primarily, it should be supported because other similar types* support slicing, such as ArraySegment<T>, ReadOnlySpan<T>, Span<T>, Memory<T>, ReadOnlyMemory<T>, ReadOnlySequence<T>. Even ImmutableArray<T> supports slicing.

One benefit to meeting the slicing "interface" (it's duck-typed, not a true interface), aside from using the range operator (..) is providing an API that is familiar to developers who are not used to dealing with TextSpan. They don't need to figure out that they want First and Skip. They don't need to read the xmldocs to see what the parameters mean. It's Slice, just like every other slicable type.


My specific use case:

I "reached for" the non-existant slice capability in a parser I was writing. This language has three different kinds of strings (unquoted, single-quoted, double-quoted). Those strings can be appended. Double quoted strings support escaping and trimming (the trimming is much like C#'s raw strings).

I found it easiest to, in my parsing method, just accept the token values - quotes and all - and then pass the final result to another method that would handle concatenation, escaping, trimming, etc. That method would use the quotes to know how to handle that portion of the string. It would then strip the quotes off, with a simple span[1..^1]. Instead, I have to do this: span.First(span.Length - 1).Skip(1).


* Notably, StringSegment in Microsoft.Extensions.Primitives (the most direct comparison to TextSpan) does not support slicing, but this is probably because it's fairly new, is trying to match the string API (so it uses Substring instead of Slice), and it is not special-cased by the compiler, like string is (for string, the compiler will use Substring instead of Slice, and for arrays, the compiler will use System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray instead of Slice). I have a feeling that eventually, especially if StringSegment catches on as a non-ref-struct counterpart to ReadOnlySpan<char>, they'll either special case StringSegment, or support SubString in addition to Slice.

nblumhardt commented 2 months ago

Thanks @binarycow 👍