Created
February 4, 2022 00:43
-
-
Save andrewabest/84ea268ab9e5b43803929c1d774467b5 to your computer and use it in GitHub Desktop.
A basic function-applying token replacement implementation with Sprache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Main() | |
{ | |
var input = "Hello #{Greeting | Upper} #{Greeting | Upper}, How do you like #{DayOfWeek | Lower}"; | |
var variables = new Dictionary<string, string>() | |
{ | |
{ "Greeting", "hello" }, | |
{ "DayOfWeek", "Wednesday" } | |
}; | |
var tokens = ParseInput(input); | |
tokens.Dump(); | |
foreach (var token in tokens) | |
{ | |
input = token.Replace(input, variables[token.Id]); | |
} | |
input.Dump(); | |
} | |
public interface IPipeFunction | |
{ | |
string Apply(string input); | |
} | |
public class Upper : IPipeFunction | |
{ | |
public string Apply(string input) | |
{ | |
return input.ToUpperInvariant(); | |
} | |
} | |
public class Lower : IPipeFunction | |
{ | |
public string Apply(string input) | |
{ | |
return input.ToLowerInvariant(); | |
} | |
} | |
IEnumerable<Token> ParseInput(string input) | |
{ | |
var pipeFunctions = new Dictionary<string, Func<IPipeFunction>>() | |
{ | |
{ "Upper", () => new Upper() }, | |
{ "Lower", () => new Lower() }, | |
}; | |
var tokenStart = Parse.String("#{"); | |
var tokenEnd = Parse.Char('}'); | |
var pipe = Parse.Char('|'); | |
var functionParser = | |
from space in Parse.WhiteSpace.Once() | |
from separator in pipe | |
from anotherSpace in Parse.WhiteSpace.Once() | |
from functionName in Parse.Letter.Many().Text() | |
select pipeFunctions[functionName](); | |
var tokenParser = | |
from start in tokenStart | |
from token in Parse.AnyChar.Except(functionParser).Many().Text() | |
from function in functionParser | |
from end in tokenEnd | |
select new Token() { Id = token, PipeFunction = function }; | |
var inputParser = | |
from preamble in Parse.AnyChar.Except(tokenStart).Many().Optional() | |
from token in tokenParser | |
select token; | |
return inputParser.Many().Parse(input); | |
} | |
public class Token | |
{ | |
public string Id {get;set;} | |
public IPipeFunction? PipeFunction {get;set;} | |
string Format => PipeFunction == null ? $"#{{{Id}}}" : $"#{{{Id} | {PipeFunction.GetType().Name}}}"; | |
public string Replace(string input, string value) | |
{ | |
return input.Replace(Format, PipeFunction != null ? PipeFunction.Apply(value) : value); | |
} | |
} |
Another way to define types is to change this:
var pipeFunctions = new Dictionary<string, Func<IPipeFunction>>()
{
{ "Upper", () => new Upper() },
{ "Lower", () => new Lower() },
};
to
var pipeFunctions = new Dictionary<string, Func<string, string>>()
{
{ "Upper", s => s.ToUpperInvariant() },
{ "Lower", s => s.ToLowerInvariant() },
};
and then (with a bit of modification here and there) we don't need to define IPipeFunction
and Upper
and Lower
any more.
If we do want to give Func<string, string>
a special name, we can add this to the top:
using PipeFunction = System.Func<string, string>;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To use with vscode notebooks + .net interactive you can replace the Main function with: