{"id":37051996,"url":"https://github.com/antonbergaker/interpolatedparser","last_synced_at":"2026-01-14T06:00:19.790Z","repository":{"id":211610476,"uuid":"729589647","full_name":"AntonBergaker/InterpolatedParser","owner":"AntonBergaker","description":"Interpolated Strings but in reverse! A very cursed C# parser library.","archived":false,"fork":false,"pushed_at":"2025-02-24T08:02:42.000Z","size":80,"stargazers_count":286,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-10-14T11:11:31.241Z","etag":null,"topics":["csharp","cursed","parser","source-generation","source-generator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AntonBergaker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-09T17:53:49.000Z","updated_at":"2025-09-23T10:27:04.000Z","dependencies_parsed_at":"2024-08-25T00:21:47.062Z","dependency_job_id":"630d4a13-9b21-4fe6-bd49-8f4d18acb0f9","html_url":"https://github.com/AntonBergaker/InterpolatedParser","commit_stats":null,"previous_names":["antonbergaker/interpolatedparser"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AntonBergaker/InterpolatedParser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonBergaker%2FInterpolatedParser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonBergaker%2FInterpolatedParser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonBergaker%2FInterpolatedParser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonBergaker%2FInterpolatedParser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AntonBergaker","download_url":"https://codeload.github.com/AntonBergaker/InterpolatedParser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonBergaker%2FInterpolatedParser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28412180,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","cursed","parser","source-generation","source-generator"],"created_at":"2026-01-14T06:00:18.923Z","updated_at":"2026-01-14T06:00:19.704Z","avatar_url":"https://github.com/AntonBergaker.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# InterpolatedParser\n\nInterpolatedParser is a [nuget](https://www.nuget.org/packages/InterpolatedParser/) library enabling string interpolation, but in reverse.\n\nExample code:\n```csharp\nusing InterpolatedParsing;\n\nstring input = \"x is 69\";\n\nint x = 0;\nInterpolatedParser.Parse(input, $\"x is {x}\");\n\nConsole.WriteLine(x); // Prints 69.\n```\n\n## Usage\n\n### Warning\nThis library abuses [Unsafe.AsRef](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.unsafe.asref) in a way that violates the runtimes expectations. It is not recommended to use this in production code. This library is intended as a fun little example of how C# features and implementation details can be used to do something unexpected and interesting. If you want to use a libary with a very similar but not cursed API, you can try my other library, [OutParser](https://github.com/AntonBergaker/OutParser).\n\n### Supported types\nInterpolatedParser supports anything that implements `IParseable\u003cT\u003e` and `ISpanParseable\u003cT\u003e`, which includes many common types in .NET. This also means you can use your own types by having them implement either of the two interfaces.\n\n### Collections\nThe parser supports Lists and Arrays. A separator is provided as a format string. Format strings don't allow trailing whitespace, so if you need that enclose the format string in single quotes.\n\n```csharp\nusing InterpolatedParsing;\n\nList\u003cint\u003e numbers = null!;\n\nInterpolatedParser.Parse(\n\t\"Winning numbers are: 5,10,15,25\",\n\t$\"Winning numbers are: {numbers:,}\");\n\nList\u003cstring\u003e beans = null!;\nInterpolatedParser.Parse(\n\t\"Bean list: black, coffee, green\",\n\t$\"Bean list: {beans:', '}\"); // Add single quotes to support whitespace\n```\n\n\n## This is cursed, how does it do that?\nC# 10 added support for writing [custom interpolated string handlers](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handler). At compile time they translate interpolated strings into a series of calls to magic methods: `AppendLiteral` for literal strings, and `AppendFormatted` to the parameters of the string.\n\n```csharp\nvar str = $\"Hello {123}!\";\n\n// Becomes this code on compile time:\n\nvar handler = new DefaultInterpolatedStringHandler(7, 4);\nhandler.AppendLiteral(\"Hello \");\nhandler.AppendFormatted(123);\nhandler.AppendLiteral(\"!\");\nvar str = handler.ToStringAndClear();\n```\n\nThat's all good and normally doesn't enable the shenanigans we need. However we can abuse the [in](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/method-parameters#in-parameter-modifier) parameter modifier. The in parameter can be implicit, so the generated calls to `AppendFormatted` allow it. This means we're now passing down a read only reference to the value when we call `AppendFormatted`. This is where things become really cursed, using `Unsafe.AsRef` it's possible to cast it into a ref parameter, allowing the parser to change the parameters value.\n\n```csharp\n    public readonly void AppendFormatted(in int value) {\n        Unsafe.AsRef(in value) = 123;\n```\n\nThis is the main hack that makes this work, but when `AppendFormatted` is called, we don't yet have the information to extract what part of the input string we should parse. (Previous versions of this parser stored the ref as a pointer which was giga unsafe as it could not be pinned.) The information we need for that is getting the next part of the string, which is added with an `AppendLiteral` after the `AppendFormatted`. To get the upcoming literal string this library uses a [C# Source Generator](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) to make a list of every single call to the `Parse` method and what string components each `Parse` call will have. Since generated code lives in the user project, to make it accessible to the Parser the entire Parser is also code generated. This has some other benefits, like allowing code generation to support both `ISpanParsable` and `IParsable` types.\n\nOf course even with a list of all calls we still need a way to pick the right call out of this list. Somewhat surprisingly there's a set of slightly obscure attributes that will inject the [file path](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callerfilepathattribute) and the [line number](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callerlinenumberattribute) of the calling method at compile time.\nEven more surprisingly these attributes still work on the auto generated constructor of the custom string interpolater. One side effect of this is that the accuracy is limited to line number, so placing two calls to `Parse` on the same line will break the parser.\n\nThe input string also gets passed to the interpolater's constructor using another [attribute](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.interpolatedstringhandlerargumentattribute) and so we have all the information we need before any calls to `AppendFormatted`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonbergaker%2Finterpolatedparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantonbergaker%2Finterpolatedparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonbergaker%2Finterpolatedparser/lists"}