{"id":17061051,"url":"https://github.com/chalcolith/ironmeta","last_synced_at":"2026-03-12T14:39:43.830Z","repository":{"id":8885817,"uuid":"10603956","full_name":"chalcolith/ironmeta","owner":"chalcolith","description":"The IronMeta parser generator provides a programming language and application for generating pattern matchers on arbitrary streams of objects. It is an implementation of Alessandro Warth's OMeta system for C#.","archived":false,"fork":false,"pushed_at":"2022-06-25T20:09:55.000Z","size":11882,"stargazers_count":82,"open_issues_count":3,"forks_count":16,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-01-14T14:01:27.999Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chalcolith.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"License.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-06-10T15:57:02.000Z","updated_at":"2025-12-23T22:39:39.000Z","dependencies_parsed_at":"2022-08-28T04:55:15.886Z","dependency_job_id":null,"html_url":"https://github.com/chalcolith/ironmeta","commit_stats":null,"previous_names":["kulibali/ironmeta"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/chalcolith/ironmeta","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chalcolith%2Fironmeta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chalcolith%2Fironmeta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chalcolith%2Fironmeta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chalcolith%2Fironmeta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chalcolith","download_url":"https://codeload.github.com/chalcolith/ironmeta/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chalcolith%2Fironmeta/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30428518,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T14:34:45.044Z","status":"ssl_error","status_checked_at":"2026-03-12T14:09:33.793Z","response_time":114,"last_error":"SSL_read: 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":[],"created_at":"2024-10-14T10:45:55.532Z","updated_at":"2026-03-12T14:39:43.814Z","avatar_url":"https://github.com/chalcolith.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"|       |       |\n| ----- | ----- |\n| Build | [![CI](https://github.com/kulibali/ironmeta/actions/workflows/ci.yml/badge.svg)](https://github.com/kulibali/ironmeta/actions/workflows/ci.yml) |\n| NuGet | [![NuGet](https://img.shields.io/nuget/dt/IronMeta.svg)](https://www.nuget.org/packages/IronMeta) |\n\n\nThe IronMeta parser generator provides a programming language and application for generating pattern matchers on arbitrary streams of objects. It is an implementation of Alessandro Warth's [OMeta](http://tinlizzie.org/ometa/) system in C#.\n\nIronMeta is available under the terms of the [BSD License](https://github.com/kulibali/ironmeta/wiki/License).\n\n## Changelog\n\nThe changelog is available in the repo: [CHANGELOG](https://github.com/kulibali/ironmeta/blob/master/CHANGELOG.md).\n\n## Using IronMeta\n\nIronMeta is available on [NuGet](https://www.nuget.org/packages/IronMeta/).  To install it, open the NuGet shell and type `Install-Package IronMeta`, or use the NuGet tools for Visual Studio.  This will install the IronMeta library in your package.\n\nOnce you have installed the NuGet package, add a grammar file with the extension .ironmeta to your project.  Then generate a C# class from it.  You can do this in two ways:\n\n- You can install a [Visual Studio extension](https://marketplace.visualstudio.com/items?itemName=GordonTisher.IronMetaVisualStudioExtension) that provides a custom tool for generating C# code from IronMeta files.  You must set the \"Custom Tool\" property of your IronMeta file to be `IronMetaGenerator`.  Then the C# code will be generated whenever your grammar file changes.  Syntax errors will appear in your Error List.\n- `IronMeta.Library.dll` contains an MsBuild task called \"IronMetaGenerate\".  A simple example of how to use this:\n\n```\n      \u003cUsingTask TaskName=\"IronMetaGenerate\" AssemblyFile=\"path_to\\IronMeta.Library.dll\" /\u003e\n      \u003cTarget Name=\"BeforeBuild\"\u003e\n        \u003cIronMetaGenerate Input=\"MyParser.ironmeta\" Output=\"MyParser.g.cs\" Namespace=\"MyNamespace\" Force=\"true\" /\u003e\n      \u003c/Target\u003e\n```\n\n- A command-line program `IronMetaApp.exe` is included in the NuGet package, in the `tools` directory.  The program takes the following arguments:\n  - `-o {output}` (optional): Specify the output file name (defaults to `{input}_.g.cs`).\n  - `-n {namespace}` (optional): Specify the namespace to use for the generated parser (defaults to the name of the directory the input file is in).\n  - `-f` (optional): Force generation even if the input file is older than the output file.\n  - `{input}`: Specify the input file name (must end in `.ironmeta`.)\n\nTo use an IronMeta-generated parser in your C# program, create a new instance of the generated parser class. Then call the function `GetMatch()` with the input you wish to parse, and the method of the generated parser object that corresponds to the top-level rule you wish to use. This returns an object of type `IronMeta.Matcher.MatchResult`, which contains information about the result of the match, as well as errors that might have ocurred.\n\nThe following is a small sample program that uses the Calc demo parser that is included in the source code:\n\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using System.Text;\n\n    namespace MyCalcProject\n    {\n        class Program\n        {\n            static void Main(string[] args)\n            {\n                var parser = new Calc();\n                var match = parser.GetMatch(\"2 * 7\", parser.Expression);\n\n                if (match.Success)\n                    Console.WriteLine(\"result: {0}\", match.Result); // should print \"14\"\n                else\n                    Console.WriteLine(\"error: {0}\", match.Error); // shouldn't happen\n            }\n        }\n    }\n\n## Building from Source\n\nWhen you have checked out the GitHub repository, you will need to generate your own signing key in `Source\\IronMeta.snk`; open a Visual Studio Developer Command Prompt and type:\n\n    sn -k Source\\IronMeta.snk\n\n## Features\n\nAlthough the most common use for IronMeta is to build parsers on streams of text for use in compiling or other text processing, IronMeta can generate pattern matchers (more accurately, transducers) for any input and output type. You can use C# syntax directly in grammar rules to specify objects to match.\n\n- IronMeta-generated parsers use strict Parsing Expression Grammar semantics; they are greedy and use committed choice.\n- Generated parsers are implemented as C# partial classes, allowing you to keep ancillary code in a separate file from your grammar.\n- You can use anonymously-typed object literals in rules; they are matched by comparing their properties with the input objects'.\n- Unrestricted use of C# in semantic conditions and match actions.\n- Higher-order rules: you can pass rules (or arbitrary patterns) as parameters, and then use them in a pattern.\n- Pattern matching on rule arguments: you can apply different rule bodies depending on the number and types of parameters.\n- Flexible variables: variables in an IronMeta rule may be used to:\n  - get the input of an expression they are bound to.\n  - get the result or result list of an expression they are bound to.\n  - match a rule passed as a parameter.\n  - pass a rule on to another rule.\n- As an enhancement over the base OMeta, IronMeta allows direct and indirect left recursion, using Sérgio Medeiros et al's [algorithm](http://arxiv.org/abs/1207.0443) for all rules, even within parameter matching.\n\n### Current limitations\n\nError reporting is currently quite rudimentary, only reporting the last error that ocurred at the rightmost position in the input.\n\nPerformance is quite slow, as not much optimization has been done to date.\n\n## The IronMeta Language\n\nThis section is an informal introduction to the features of the IronMeta language.\n\nIt uses the following IronMeta file named `Calc.ironmeta`, which is included in the IronMeta distribution. It can also be found in the `Samples/Calc` directory in the source.\n\nThe Calc grammar is much more complex than it needs to be in order to demonstrate some of the advanced functionality of IronMeta.\n\n    // IronMeta Calculator Example\n\n    using System;\n    using System.Linq;\n\n    ironmeta Calc\u003cchar, int\u003e : Matcher\u003cchar, int\u003e\n    {\n        Expression = Additive;\n\n        Additive = Add | Sub | Multiplicative;\n\n        Add = BinaryOp(Additive, '+', Multiplicative) -\u003e { return _IM_Result.Results.Aggregate((total, n) =\u003e total + n); };\n        Sub = BinaryOp(Additive, '-', Multiplicative) -\u003e { return _IM_Result.Results.Aggregate((total, n) =\u003e total - n); };\n\n        Multiplicative = Multiply | Divide;\n        Multiplicative = Number(DecimalDigit);\n\n        Multiply = BinaryOp(Multiplicative, \"*\", Number, DecimalDigit) -\u003e { return _IM_Result.Results.Aggregate((p, n) =\u003e p * n); };\n        Divide = BinaryOp(Multiplicative, \"/\", Number, DecimalDigit) -\u003e { return _IM_Result.Results.Aggregate((q, n) =\u003e q / n); };\n\n        BinaryOp :first :op :second .?:type = first:a KW(op) second(type):b -\u003e { return new List\u003cint\u003e { a, b }; };\n\n        Number :type = Digits(type):n WS* -\u003e { return n; };\n\n        Digits :type = Digits(type):a type:b -\u003e { return a*10 + b; };\n        Digits :type = type;\n\n        DecimalDigit = .:c ?( (char)c \u003e= '0' \u0026\u0026 (char)c \u003c= '9' ) -\u003e { return (char)c - '0'; };\n        KW :str = str WS*;\n        WS = ' ' | '\\n' | '\\r' | '\\t';\n    }\n\nWe will go through this example line by line to introduce the IronMeta language:\n\n## Comments\n\n    // IronMeta Calculator Example\n\nYou may include comments anywhere in the IronMeta file. They may also be in the C-style form:\n\n    /* C-Style Comment */\n\n## Preamble\n\n    using System;\n    using System.Linq;\n\nYou can include C# `using `statements at the beginning of an IronMeta file. IronMeta will automatically add using statements to its output to include the namespaces it needs.\n\n    ## Parser Declaration\n\n    ironmeta Calc\u003cchar, int\u003e : Matcher\u003cint\u003e\n\nAn IronMeta parser always starts with the keyword `ironmeta`. Then comes the name of the parser (`Calc`, in this case), and the input and output types. The generated parser will take as input an `IEnumerable` of the input type, and return as output an object of the output type.\n\nIn this case, the Calc parser will operate on a stream of `char` values, and output an `int` value.\n\n\u003e Note: you must always include the input and output types.\n\nYou may also optionally include a base class:\n\n    : Matcher\u003cint\u003e\n\nIf you do not include a base class, your parser will inherit directly from `IronMeta.Matcher.Matcher`.\n\n## Rules\n\n    Expression = Additive;\n\nAn IronMeta rule consists of a name, an pattern for matching parameters, a `=`, a pattern for matching against the main input, and a terminating semicolon `;` (for folks used to C#) or comma `,` (for folks used to OMeta):\n\nIn this case, the rule `Expression` has no parameters, and matches by calling another rule, `Additive`.\n\n## Matching Input\n\nYou can use the period `.` to match any item of input, or you can use arbitrary C# expressions. The C# expressions may be a string literal, a character literal, a regular expression, an object created using the `new` keyword, or any other expression that is surrounded by curly braces:\n\n    MyPattern = 'a' \"b\" {3.14159} {new MyClass()};\n\nIronMeta will use the standard C# `object.Equals()` method to match the inputs.\n\n### Regular Expressions\n\nIf your input type is `char`, you can use simple regular expressions:\n\n    MyPattern = /a?bc(def+|ghi)(kl)*/;\n\nYou can use the following constructs in regular expressions:\n\n- One or more single characters, e.g. `/abc/`.  The following syntax characters must be escaped if you want to match them: `|`, `(`, `)`, `[`, `]`, `\\`, `+`, `*`.\n- Categories: `\\s` matches whitespace, `\\d` matches any Unicode digit, `\\w` matches any Unicode letter, `\\p{Cc}` matches any character in given Unicode general category, e.g. `Lu`, `Nd`.\n- Disjunctions: `/abc|def/`.\n- Classes: `/[abcd-g]/` (syntax characters must be escaped here as well).  You can use negative categories: `/[^xyz]/`.\n- `+` matches one or more elements, star `*` matches zero or more, and `?` matches zero or one.  As usual, these bind tighter than disjunction but looser than sequence.\n- `()` will group sequences.\n\n### Matching Anonymous Objects\n\nYou can also use anonymous object syntax (you don't need to surround the whole new expression with braces in this case):\n\n    MyPattern = new { Name=\"MyName\", Value=\"MyValue\" }  new { Name=\"MyName\", Value=\"MySecondValue\" };\n\nLiterals that you define with anonymous types will be matched according to their public properties; if an input object has the same properties with the same values, it will be considered equal to the anonymous object.\n\n### Matching Sequences\n\nThe pattern literal can also be an `IEnumerable` of the input type, including C# strings for sequences of characters.\n\nThis eliminates the need for the OMeta `token` function; just use a string literal, or if you are matching on something other than characters, use a list:\n\n    MyPattern = {new List\u003cMyInputType\u003e{ a, b, c }};\n\n## Sequence and Disjunction\n\n    Additive = Add | Sub | Multiplicative;\n\nAs is probably obvious from the other rules, you write a sequence of patterns by simply writing them one after the other, separated by whitespace.\n\nTo specify a choice between alternatives, separate them with `|`.\n\n\u003e Note: unlike in other parser generator formalisms, separating expressions with a carriage return does NOT mean they are alternatives! You must always use the `|`.\n\n## Other Operators\n\nYou can modify the meaning of patterns with the following operators that appear after an expression:\n\n- `?` will match zero or one time.\n- `*` will match zero or more times.\n- `+` will match one or more times.\n- `{N}` will match N times.\n- `{Min,Max}` will match at least Min times, and at most Max times.\n\nThese operators are all greedy –- they will match as many times a possible and then return that result.\n\nYou can stop them from matching by using the prefix operators:\n\n- `\u0026` as a prefix will match an expression but NOT advance the match position. This allows for unlimited lookahead.\n- `~` as a prefix will match if the expression does NOT match. It will not advance the match position.\n\n## Conditions and Actions\n\n    DecimalDigit = .:c ?( (char)c \u003e= '0' \u0026\u0026 (char)c \u003c= '9' ) -\u003e { return (char)c - '0'; };\n\nHere things get more interesting. This rule has only one expression, the period `.`. This will match a single item of input. It is then bound to the variable `c` by means of the colon `:`.\n\n\u003e Note: you can leave out the period if you are binding to a variable; that is, `:c` is equivalent to `.:c`. However, this rule will not necessarily match any character, because it contains a condition. A condition is written with `?` followed immediately by a C# expression in parentheses. The C# expression must evaluate to a bool value. Once the expression matches (in this case it will match anything), it is bound to the variable `c`, which is then available for use in your C# code.\n\nThe rule also contains an action. Actions are written with `-\u003e` followed by a C# block surrounded by curly braces. This block must contain a return statement that returns a value of the output type, or a `List\u003c\u003e` of the output type.\n\n\u003e Note: if you do not provide an action for the expression, it will simply return the results of its patterns, as a list. Matching a single item will return `default(TResult)` by default, or you can pass a delegate or lambda function to the matcher when you create it that will convert values of the input type to the output type.\n  Be aware that an action only applies to the last expression in an OR expression. So the action in the following:\n\n    MyRule = One | Two | Three -\u003e { my action };\n\nwill only run if the expression `Three `matches! If you want an action to apply on an OR, use parentheses:\n\n    MyRule = (One | Two | Three) -\u003e { my action };\n\n## Variables\n\n    Digits :type = Digits(type):a type:b -\u003e { return a*10 + b; };\n\nUpon a successful match, variables will contain information about the results of the match of the expression they are bound to. In this example, because `a` \u0026 `b` are used in an expression containing an integer, they will automatically evaluate to the results of their expressions, because the result type of the Calc grammar is `int`.\n\nIronMeta variables are very flexible. They contain implicit cast operators to:\n\n- A single value of the input type: this will return the last item in the list of results of the expression that the variable is bound to.\n- A single value of the output type.\n- A `List\u003c\u003e` of the input type.\n- A `List\u003c\u003e` of the output type.\n\nIf your input and output types are the same, the implicit cast operators will only return the inputs, and you will need to use the explicit variable properties:\n\n- `c.Inputs` returns the list of inputs that the parse pattern matched.\n- `c.Results` returns the list of results that resulted from the match.\n- `c.StartIndex` returns the index in the input stream at which the pattern started matching.\n- `c.NextIndex` returns the first index in the input stream after the pattern match ended.\n\nYou can also use variables in a pattern, in which case they will match whatever input they matched when they were bound. Or, if they were bound to a rule in a parameter pattern (see below), they will call that rule. You can even pass parameters to them.\n\n### Built-In Variables\n\nIronMeta automatically defines a variable for use in your C# code: `_IM_Result` is bound to the entire expression that your condition or action applies to.\n\n## Multiple Rule Bodies\n\n    Multiplicative = Multiply | Divide;\n    Multiplicative = Number(DecimalDigit);\n\nYou can have multiple rule bodies; their patterns will be combined in one overall OR when that rule is called.\n\n## Parameters\n\n    Add = BinaryOp(Additive, '+', Multiplicative) -\u003e { return _IM_Result.Results.Aggregate((total, n) =\u003e total + n); };\n\nThis rule shows that you can pass parameters to a rule. You can pass literal match patterns, rule names, or variables.\n\n    BinaryOp :first :op :second .?:type = first:a KW(op) second(type):b -\u003e { return new List\u003cint\u003e { a, b }; };\n\nThis rule demonstrates how to match parameters. The parameter part of a rule is actually a matching pattern no different than that on the right-hand side of the `=`. Using this fact, plus the ability to specify multiple rules with the same name, you can write rules that match differently depending on the number and kind of parameters they are passed.\n\n## Rules as Arguments\n\n    Add = BinaryOp(Additive, '+', Multiplicative) -\u003e { return _IM_Result.Results.Aggregate((total, n) =\u003e total + n); };\n    BinaryOp :first :op :second .?:type = first:a KW(op) second(type):b -\u003e { return new List\u003cint\u003e { a, b }; };\n\nThese rules show that you can pass rules as parameters to other rules. To match against them, just capture them in a variable in your parameter pattern, and then use the variable as an expression in your pattern. You can pass parameters as usual.\n\n## Patterns as Arguments\n\nYou can also pass arbitrary patterns as arguments.  Variables from the outer rule that you use in the argument pattern will be passed to the inner pattern when matching.\n\n## List Folding\n\n    KW :str = str Whitespace*;\n\nIf you look at the rules that call this rule (indirectly through the BinaryOp rule), you'll see that they pass both a single character and a string:\n\n    Sub = BinaryOp(Additive, '-', Multiplicative) -\u003e { return _IM_Result.Results.Aggregate((total, n) =\u003e total - n); };\n    Divide = BinaryOp(Multiplicative, \"/\", Number, DecimalDigit) -\u003e { return _IM_Result.Results.Aggregate((q, n) =\u003e q / n); };\n\nWhen matching against variables captured in parameters, variables containing single items or variables containing lists will match correctly.\n\n## Rule Inheritance\n\nIronMeta parsers are regular C# classes, so they can inherit from other parsers and call their rules. You must preface rules you wish to override by the C# keyword `virtual`. You can override rules in a base class by prefixing the rule definition by the keyword `override`, or hide non-virtual rules by means of the `new` keyword.\n\n    ironmeta DerivedParser\u003cchar, Node\u003e : BaseParser\u003cNode\u003e\n    {\n        virtual Expression1 = ...;\n        override Expression2 = ...;\n        new Expression3 = ...;\n    }\n\n## Rule Encapsulation\n\nYou can also refer to rules in a completely unrelated grammar (as long as the input and output types are the same) by declaring initialized members of the other grammar's class and referring to those members' rule functions.\n\n    public partial class MyParser\n    {\n        private OtherParser other_parser = new OtherParser();\n    }\n\n    ironmeta MyParser\u003cchar, int\u003e: IronMeta.Matcher.Matcher\u003cchar, int\u003e\n    {\n        Rule = \"foo\" other_parser.OtherRule \"bar\";\n    }\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchalcolith%2Fironmeta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchalcolith%2Fironmeta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchalcolith%2Fironmeta/lists"}