Skip to content

Instantly share code, notes, and snippets.

@Xevion
Created February 2, 2025 05:50
Show Gist options
  • Save Xevion/fe1fc6aa6b847ca4dfa92ba3e4bf0779 to your computer and use it in GitHub Desktop.
Save Xevion/fe1fc6aa6b847ca4dfa92ba3e4bf0779 to your computer and use it in GitHub Desktop.
SuperPower Token Positioning
using System.Linq;
using Microsoft.Diagnostics.Tracing.Parsers.MicrosoftWindowsTCPIP;
using Superpower;
namespace Netophone.Systems.Plot.Parsers;
public interface ILocated {
PositionSpan Span { set; }
}
public readonly record struct PositionSpan(int Start = -1, int End = -1) {
public bool HasValue => Start >= 0 && End >= 0;
/// <summary>
/// Apply the position span to acquire a substring of the source string.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public string Substring(string source) {
if (!HasValue) throw new InvalidOperationException("PositionSpan must have a value to be used.");
return source.Substring(Start, End - Start);
}
}
public static class ParserExtensions {
/// <summary>
/// When applied, ensures the parser's result embeds the positioning information into the results.
/// </summary>
/// <param name="parser"></param>
/// <typeparam name="T">An ILocated type</typeparam>
/// <returns></returns>
public static TextParser<T> WithLocation<T>(this TextParser<T> parser)
where T : ILocated {
return i => {
var inner = parser(i);
if (!inner.HasValue) return inner;
inner.Value.Span = new PositionSpan(
inner.Location.Position.Absolute, inner.Location.Position.Absolute + inner.Location.Length
);
return inner;
};
}
/// <summary>
/// When applied, ensures the parser's result embeds the positioning information into the results.
/// </summary>
/// <param name="parser"></param>
/// <typeparam name="TKind"></typeparam>
/// <typeparam name="T">An ILocated type</typeparam>
/// <returns></returns>
public static TokenListParser<TKind, T> WithLocation<TKind, T>(this TokenListParser<TKind, T> parser)
where T : ILocated {
return i => {
var inner = parser(i);
if (!inner.HasValue) return inner;
var last = inner.Remainder.Any()
// inner.Location contains both tokens used and the remainder, so get the last 'used' token
? inner.Location.ElementAt(inner.Location.Count() - inner.Remainder.Count() - 1)
// inner.Location is 100% used, just grab the last one
: inner.Location.Last();
inner.Value.Span = new PositionSpan(inner.Location.First().Position.Absolute,
last.Position.Absolute + last.Span.Length);
return inner;
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment