{"id":29027344,"url":"https://github.com/rameel/ramstack.parsing","last_synced_at":"2025-06-26T06:05:28.149Z","repository":{"id":276217225,"uuid":"928611883","full_name":"rameel/ramstack.parsing","owner":"rameel","description":"A blazing-fast, lightweight, and intuitive parser combinator library for .NET","archived":false,"fork":false,"pushed_at":"2025-05-24T18:39:19.000Z","size":279,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-24T18:47:15.033Z","etag":null,"topics":["parser","parser-combinators","peg"],"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/rameel.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,"zenodo":null}},"created_at":"2025-02-06T23:20:22.000Z","updated_at":"2025-05-24T18:38:36.000Z","dependencies_parsed_at":"2025-05-08T22:26:04.188Z","dependency_job_id":null,"html_url":"https://github.com/rameel/ramstack.parsing","commit_stats":null,"previous_names":["rameel/ramstack.parsing"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/rameel/ramstack.parsing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rameel%2Framstack.parsing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rameel%2Framstack.parsing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rameel%2Framstack.parsing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rameel%2Framstack.parsing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rameel","download_url":"https://codeload.github.com/rameel/ramstack.parsing/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rameel%2Framstack.parsing/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262010866,"owners_count":23244414,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["parser","parser-combinators","peg"],"created_at":"2025-06-26T06:05:27.567Z","updated_at":"2025-06-26T06:05:28.132Z","avatar_url":"https://github.com/rameel.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ramstack.Parsing\r\n[![NuGet](https://img.shields.io/nuget/v/Ramstack.Parsing.svg)](https://nuget.org/packages/Ramstack.Parsing)\r\n[![MIT](https://img.shields.io/github/license/rameel/ramstack.parsing)](https://github.com/rameel/ramstack.parsing/blob/main/LICENSE)\r\n\r\nA blazing-fast, lightweight, and intuitive parser combinator library for .NET.\r\n\r\n## Getting Started\r\n\r\nTo install the `Ramstack.Parsing` [NuGet package](https://www.nuget.org/packages/Ramstack.Parsing) to your project, run the following command:\r\n```shell\r\ndotnet add package Ramstack.Parsing\r\n```\r\n\r\n## Usage\r\n\r\nHere's how to define a simple expression parser that parses and evaluates mathematical expressions in one step:\r\n\r\n```csharp\r\npublic static Parser\u003cdouble\u003e Calc { get; } = CreateParser();\r\n\r\nprivate static Parser\u003cdouble\u003e CreateParser()\r\n{\r\n    // Grammar:\r\n    // ----------------------------------------\r\n    // Start       :  S Sum $\r\n    // Sum         :  Product ([+-] S Product)*\r\n    // Product     :  Unary ([*/] S Unary)*\r\n    // Unary       :  '-'? S Primary\r\n    // Primary     :  Parenthesis / Value\r\n    // Parenthesis :  '(' S Sum ')' S\r\n    // Value       :  Number S\r\n    // S           :  [ \\r\\n\\t]*\r\n\r\n    var sum = Deferred\u003cdouble\u003e();\r\n    var value = Literal.Number\u003cdouble\u003e().ThenIgnore(S);\r\n\r\n    var parenthesis = sum.Between(\r\n        Seq(L('('), S),\r\n        Seq(L(')'), S)\r\n        );\r\n\r\n    var primary = parenthesis.Or(value);\r\n\r\n    var unary = Seq(\r\n        L('-').Optional(),\r\n        S,\r\n        primary\r\n        ).Do((u, _, d) =\u003e u.HasValue ? -d : d);\r\n\r\n    var product = unary.Fold(\r\n        OneOf(\"*/\").ThenIgnore(S),\r\n        (l, r, op) =\u003e op == '*' ? l * r : l / r);\r\n\r\n    sum.Parser = product.Fold(\r\n        OneOf(\"+-\").ThenIgnore(S),\r\n        (l, r, op) =\u003e op == '+' ? l + r : l - r);\r\n\r\n    return sum.Between(S, Eof);\r\n}\r\n```\r\n\r\nAs you can see, the parser is highly readable and easy to define. Using it is just as simple:\r\n\r\n```csharp\r\nvar result = Calc.Parse(expression);\r\nif (result.Success)\r\n{\r\n    var value = result.Value;\r\n    ...\r\n}\r\nelse\r\n{\r\n    Console.WriteLine(result.ErrorMessage);\r\n\r\n    //\r\n    // result.ToString() prints the parsed value or an error message\r\n    // depending on the parsing status\r\n    //\r\n    // Console.WriteLine(result);\r\n    //\r\n}\r\n```\r\n\r\nThe parser provides detailed error messages when the input does not conform to the specified grammar.\r\nFor example, when parsing the expression `\"1 + (\"`, the parser expects `'-'`, `'('`, or a `number` after the opening parenthesis.\r\nThe error message clearly indicates this:\r\n\r\n```\r\n(1:6) Expected '-', '(' or number\r\n```\r\n\r\nIn addition to the main `Parse` method, there is also a lightweight `bool TryParse(ReadOnlySpan\u003cchar\u003e source, out T value)` method.\r\nThis method suppresses diagnostic messages, returning only the parsing success status and the parsed value if successful.\r\n\r\nIf detailed error messages are not required, using `TryParse` is the preferred choice, as it reduces memory allocations\r\nand slightly improves performance by avoiding the overhead associated with collecting diagnostic data.\r\n\r\n## Documentation\r\n\r\n- Documentation can be found in [docs/documentation.md](docs/documentation.md) (Work in Progress).\r\n- Additional usage samples, see the [samples/Samples](samples) directory.\r\n\r\n## Performance\r\n\r\n`Ramstack.Parsing` stands out as an exceptionally fast and efficient parsing library, outperforming many alternatives in both speed and memory usage.\r\n\r\n### Benchmarks: Regex\r\n\r\nHere's a comparison between a regex-based email matcher and an equivalent parser combinator implementation:\r\n\r\n**Regular Expression:**\r\n```csharp\r\n[GeneratedRegex(@\"^[\\w.+-]+@[\\w-]+\\.\\w{2,}$\")]\r\nprivate static partial Regex EmailRegexGenerated();\r\n```\r\n\r\n**Parser**:\r\n```csharp\r\nvar parser = Seq(\r\n    Choice(LetterOrDigit, OneOf(\"_-+.\")).OneOrMore(),\r\n    L('@'),\r\n    Choice(LetterOrDigit, L('-')).OneOrMore(),\r\n    L('.'),\r\n    LetterOrDigit.AtLeast(2)\r\n).Text();\r\n```\r\nThe library supports nearly full character class definitions from regular expressions, allowing grammars to be written in a similar way\r\nas it written for regular expression. Optimizations ensure that both definitions are internally normalized to an equivalent representation:\r\n```csharp\r\nvar parser = Seq(\r\n    Set(\"\\\\w+.-\").OneOrMore(),\r\n    L('@'),\r\n    Set(\"\\\\w-\").OneOrMore(),\r\n    L('.'),\r\n    Set(\"\\\\w\").AtLeast(2)\r\n).Text();\r\n```\r\n\r\nThe benchmark results show that `Ramstack.Parsing` outperforms even highly optimized Regex implementations,\r\nincluding compiled and source-generated regexes.\r\n\r\n```\r\nBenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.3037)\r\nAMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores\r\n.NET SDK 9.0.200-preview.0.25057.12\r\n  [Host]     : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2\r\n  DefaultJob : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2\r\n\r\n\r\n| Method          | Mean        | Error    | StdDev   | Op/s         | Gen0   | Allocated |\r\n|---------------- |------------:|---------:|---------:|-------------:|-------:|----------:|\r\n| Ramstack        |    34.47 ns | 0.073 ns | 0.065 ns | 29,008,779.8 |      - |         - |\r\n| Regex           |   107.35 ns | 0.125 ns | 0.117 ns |  9,315,658.6 |      - |         - |\r\n| Regex:Compiled  |    44.63 ns | 0.093 ns | 0.083 ns | 22,408,327.6 |      - |         - |\r\n| Regex:Generated |    41.85 ns | 0.282 ns | 0.250 ns | 23,893,269.4 |      - |         - |\r\n| Parlot          |   295.05 ns | 0.601 ns | 0.533 ns |  3,389,302.6 | 0.0229 |     384 B |\r\n| Parlot:Compiled |   228.98 ns | 0.446 ns | 0.417 ns |  4,367,155.0 | 0.0091 |     152 B |\r\n| Pidgin          | 1,602.00 ns | 9.016 ns | 8.434 ns |    624,219.5 | 0.0572 |     976 B |\r\n```\r\n\r\nIn this specific scenario, the library simply checks for a match against the email pattern, similar to what `Regex.IsMatch` does,\r\nwithout extracting the match result itself. Like with regular expressions, this approach avoids unnecessary memory allocations.\r\n\r\n### Benchmarks: Expressions\r\n\r\nThis benchmarks tests 2 expressions:\r\n- **Small**: `2.5 * 4.7 - 8.1 / 3`\r\n- **Large**: `((3.14159 * 2.3 * 4) / (1.5 - 0.3) + (2.71828 * 2.5)) * ((8.1 * 3.2 + 4.7) / 2.1 - ((15.7 + 2.3 * 4) / (1.5 - 0.3))) - (((3.5 * 1.41421) * (2.71828 + 3.14159)) / 2)`\r\n\r\n```\r\nBenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.3037)\r\nAMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores\r\n.NET SDK 9.0.200-preview.0.25057.12\r\n  [Host]     : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2\r\n  DefaultJob : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2\r\n\r\n\r\n| Method                | Mean        | Error     | StdDev    | Op/s        | Gen0   | Allocated |\r\n|---------------------- |------------:|----------:|----------:|------------:|-------:|----------:|\r\n| Ramstack:Large        |  1,600.2 ns |   9.54 ns |   7.96 ns |   624,925.9 |      - |         - |\r\n| Ramstack:Diag:Large   |  1,699.6 ns |   3.53 ns |   3.13 ns |   588,379.8 | 0.0057 |     104 B |\r\n| Parlot:Large          |  3,520.1 ns |  11.08 ns |  10.37 ns |   284,081.7 | 0.1984 |    3344 B |\r\n| Parlot:Compiled:Large |  3,356.7 ns |   7.08 ns |   6.27 ns |   297,910.3 | 0.1984 |    3344 B |\r\n| Pidgin:Large          | 39,197.3 ns | 150.13 ns | 140.43 ns |    25,511.9 | 0.2441 |    4464 B |\r\n|                       |             |           |           |             |        |           |\r\n| Ramstack:Small        |    209.5 ns |   0.28 ns |   0.25 ns | 4,772,894.8 |      - |         - |\r\n| Ramstack:Diag:Small   |    260.6 ns |   0.57 ns |   0.50 ns | 3,838,032.3 | 0.0062 |     104 B |\r\n| Parlot:Small          |    410.7 ns |   1.46 ns |   1.36 ns | 2,434,653.7 | 0.0391 |     656 B |\r\n| Parlot:Compiled:Small |    385.1 ns |   1.22 ns |   0.95 ns | 2,596,891.9 | 0.0391 |     656 B |\r\n| Pidgin:Small          |  4,322.2 ns |  18.28 ns |  17.10 ns |   231,363.9 | 0.0381 |     736 B |\r\n```\r\n\r\n- `Ramstack` diagnostic messages disabled.\r\n- `Ramstack:Diag` includes diagnostic messages.\r\n\r\nEven with diagnostics enabled, `Ramstack.Parsing` outperforms other popular libraries.\r\n\r\n### Benchmarks: JSON\r\n\r\nThis benchmark evaluates JSON parsing performance, including object model construction, across different file sizes.\r\n\r\n- **Small**: 1.16KB\r\n- **Medium**: 11KB\r\n- **Large**: `twitter.json` (617KB) from [serde-rs/json-benchmark](https://github.com/serde-rs/json-benchmark)\r\n\r\n```\r\nBenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.3037)\r\nAMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores\r\n.NET SDK 9.0.200-preview.0.25057.12\r\n  [Host]     : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2\r\n  DefaultJob : .NET 9.0.1 (9.0.124.61010), X64 RyuJIT AVX2\r\n\r\n\r\n| Method           | Input  | Mean          | Error      | StdDev     | Op/s       | Gen0     | Gen1     | Gen2     | Allocated  |\r\n|----------------- |------- |--------------:|-----------:|-----------:|-----------:|---------:|---------:|---------:|-----------:|\r\n| Ramstack         | big    |  1,307.929 us | 12.6067 us | 11.7923 us |     764.57 | 152.3438 | 130.8594 |        - | 2498.88 KB |\r\n| Parlot           | big    |  3,102.953 us |  9.3441 us |  8.7405 us |     322.27 | 156.2500 | 132.8125 |        - | 2554.06 KB |\r\n| Parlot:Compiled  | big    |  3,078.931 us |  7.4028 us |  5.7796 us |     324.79 | 156.2500 | 132.8125 |        - | 2554.06 KB |\r\n| Pidgin           | big    | 16,675.788 us | 40.7050 us | 36.0839 us |      59.97 | 156.2500 |  93.7500 |        - | 2570.33 KB |\r\n| Newtonsoft.Json  | big    |  3,878.753 us | 18.3372 us | 16.2554 us |     257.81 | 343.7500 | 320.3125 |        - | 5638.63 KB |\r\n| System.Text.Json | big    |  1,492.658 us | 10.2347 us |  9.0728 us |     669.95 | 216.7969 | 216.7969 | 216.7969 |  963.67 KB |\r\n\r\n| Ramstack         | medium |     20.163 us |  0.0968 us |  0.0906 us |  49,595.14 |   2.6245 |   0.2747 |        - |   42.94 KB |\r\n| Parlot           | medium |     72.128 us |  0.1193 us |  0.1058 us |  13,864.18 |   2.5635 |   0.2441 |        - |   43.09 KB |\r\n| Parlot:Compiled  | medium |     70.931 us |  0.2758 us |  0.2445 us |  14,098.14 |   2.5635 |   0.2441 |        - |   43.09 KB |\r\n| Pidgin           | medium |    266.283 us |  0.4172 us |  0.3698 us |   3,755.41 |   2.4414 |        - |        - |   44.45 KB |\r\n| Newtonsoft.Json  | medium |     52.036 us |  0.4550 us |  0.4257 us |  19,217.47 |   6.0425 |   1.6479 |        - |   99.44 KB |\r\n| System.Text.Json | medium |     24.471 us |  0.0968 us |  0.0756 us |  40,865.15 |   1.0376 |        - |        - |   17.03 KB |\r\n\r\n| Ramstack         | small  |      3.016 us |  0.0064 us |  0.0053 us | 331,565.28 |   0.3815 |   0.0038 |        - |    6.24 KB |\r\n| Parlot           | small  |      6.723 us |  0.0313 us |  0.0292 us | 148,732.16 |   0.3891 |        - |        - |    6.39 KB |\r\n| Parlot:Compiled  | small  |      6.661 us |  0.0210 us |  0.0175 us | 150,120.62 |   0.3891 |        - |        - |    6.39 KB |\r\n| Pidgin           | small  |     33.369 us |  0.3909 us |  0.3656 us |  29,968.03 |   0.3662 |        - |        - |    6.49 KB |\r\n| Newtonsoft.Json  | small  |      7.940 us |  0.0226 us |  0.0201 us | 125,951.32 |   1.0529 |   0.0458 |        - |   17.26 KB |\r\n| System.Text.Json | small  |      3.477 us |  0.0133 us |  0.0111 us | 287,598.87 |   0.1373 |        - |        - |    2.25 KB |\r\n```\r\n\r\n`Ramstack.Parsing` demonstrated superior performance, outperforming other parser libraries by at least 2-3 times,\r\nand even exceeded specialized JSON parsers such as `Newtonsoft.Json` and `System.Text.Json` in terms of both speed and memory efficiency.\r\n\r\n### Benchmarked Libraries\r\n\r\n1. [x] Parlot 1.3.2\r\n2. [x] Pidgin 3.3.0\r\n3. [x] Newtonsoft.Json 13.0.3\r\n4. [x] System.Text.Json 9.0.0\r\n\r\n## Supported Versions\r\n\r\n|      | Version    |\r\n|------|------------|\r\n| .NET | 6, 7, 8, 9 |\r\n\r\n## Contributions\r\n\r\nBug reports and contributions are welcome.\r\n\r\n## License\r\n\r\nThis package is released under the **MIT License**.\r\nSee the [LICENSE](https://github.com/rameel/ramstack.parsing/blob/main/LICENSE) file for details.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frameel%2Framstack.parsing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frameel%2Framstack.parsing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frameel%2Framstack.parsing/lists"}