Skip to content

Source generation support - #292

Merged
sebastienros merged 73 commits into
mainfrom
sebros/sourcegen
Jul 29, 2026
Merged

Source generation support#292
sebastienros merged 73 commits into
mainfrom
sebros/sourcegen

Conversation

@sebastienros

Copy link
Copy Markdown
Owner

@lahma @jbevain just to show you how it's done.
The logic is not part of the source generator and is driven by user code execution, not code syntax.
The source generator has to run the code first to get the object describing the grammar. Then evaluates the generated tree, not the code itself. That requires the code to be in a static method but that's totally fine. If the grammar needs options then one can generate different parser for each option, and reuse grammar portions across.

sebastienros and others added 30 commits December 5, 2025 09:19
Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
…ll arities)

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
…egistry (WIP - investigating duplicate generation issue)

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
…to use ParserHelperRegistry

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
* Initial plan

* Implement ISourceable for Always, Fail, Eof, Capture, Not, Discard, Optional

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>

* Implement ISourceable for Else, ElseError, Error family, and When

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>

* Implement ISourceable for Identifier, NonWhiteSpaceLiteral

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>

* Implement ISourceable for WhiteSpaceLiteral, WhiteSpaceParser, PatternLiteral, Seekable

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>

* Implement ISourceable for remaining parsers: If, StringLiteral, Separated, Select, Switch, WhenFollowedBy, WhenNotFollowedBy, TextBefore, WithWhiteSpaceParser, IParserAdapter, OneOf

Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
…d performance

- Updated various parser classes to replace tuple-based result handling with out parameters for success and value retrieval.
- This change simplifies the code by eliminating the need for intermediate variable assignments and enhances readability.
- Affected classes include Deferred, LeftAssociative, Not, OneOf, OneOrMany, Optional, Sequence, SequenceAndSkip, SequenceSkipAnd, SkipWhiteSpace, Then, Unary, When, ZeroOrMany, and ZeroOrOne.
@gpetrou

gpetrou commented Dec 19, 2025

Copy link
Copy Markdown

Should the new version work with System.Text.RegularExpressions.Generator?
There is a warning PARLOT012: Could not find generator assembly 'System.Text.RegularExpressions.Generator' specified in [IncludeGenerators]. Make sure the package is referenced in your project.
Since this is part of the framework, I assume that there is no need for a package.

@sebastienros

Copy link
Copy Markdown
Owner Author

@gpetrou ignore this one. Parlot stubs this one, and logger generator too. It should just work, my benchmark project had one too and it works.

@gpetrou

gpetrou commented Dec 19, 2025

Copy link
Copy Markdown

Oh, I see. OK thanks.
I found one more issue.
If I have something like the following (just for demonstration purposes):

private abstract class NodeBase
{
}

private class BasicNode<T> : NodeBase
{
    public BasicNode(T value)
    {
        Value = value;
    }

    public T Value { get; }
}

private static readonly Parser<long> Long = Terms.Number<long>(NumberOptions.Integer);
private static readonly Parser<string> Equal = Terms.Text("==");

private static Parser<NodeBase> CreatePropertyParser<TComparand>(
   string name,
   Parser<string> @operator,
   Parser<TComparand> comparand)
{
    return
        comparand
            .AndSkip(@operator)
            .AndSkip(Terms.Text(name, caseInsensitive: true))
            .And(comparand)
            .Then<NodeBase>(
                    items => new BasicNode<TComparand>(
                        items.Item2));
}

[GenerateParser]
private static Parser<NodeBase> Parser()
{
    return CreatePropertyParser(
        "long",
        Equal,
        Long);
}

It seems to be missing the <TComparand> in the generated code.

@sebastienros

Copy link
Copy Markdown
Owner Author

@gpetrou I added a test with you sample, it's working fine.

NCalc is also working correctly without crazy changes. All the things that needed to be altered were described with the analyzer so it was smooth. The most copmlicated was to make the grammar change based on external options, since this can't be done when the parser is constructed anymore, but it all works out with the available parsers combinators (Select). ncalc/ncalc#533

- Replace environment variable checks (MSBuildExtensionsPath, MSBUILD_EXE_PATH) with DesignTimeBuild property
- DesignTimeBuild is the official MSBuild property set during design-time builds (IDE IntelliSense)
- Simplifies detection logic and improves reliability
- Prevents assembly loading conflicts when running additional generators in IDE contexts
@gpetrou

gpetrou commented Jan 11, 2026

Copy link
Copy Markdown

I added a test with you sample, it's working fine.

Hmm, I still observe an error with the latest preview 8.
In the generated file, I see:

#line 36 "D:\Projects\ParlotWithSourceGenerator\ParlotWithSourceGenerator\MyParser.cs"
        private static global::ParlotWithSourceGenerator.MyParser.NodeBase _Parser_lambda0(global::System.ValueTuple<global::System.Int64, global::System.Int64> items) => new BasicNode<TComparand>(
                            items.Item2);
#line default

I guess the issue is that we have new BasicNode<TComparand> instead of new BasicNode<long>.

@slang25

slang25 commented Feb 17, 2026

Copy link
Copy Markdown

@sebastienros this looks awesome 🤩 what's the current state of this?

@sebastienros

Copy link
Copy Markdown
Owner Author

@slang25 this only thing to add, if anything, is to improve the fact that an hint is displayed in VS or VS Code on the method with the attribute, but couldn't find a way to detect it without impacting the local build (detecting when msbuild is used). It's a niche scenario, and not breaking at all, but I'd prefer to fix it before. This PR is already testable using the published preview.

sebastienros and others added 7 commits February 17, 2026 15:25
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb643661-4b53-4548-9dde-988a8d4c2b2d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb643661-4b53-4548-9dde-988a8d4c2b2d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb643661-4b53-4548-9dde-988a8d4c2b2d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb643661-4b53-4548-9dde-988a8d4c2b2d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bb643661-4b53-4548-9dde-988a8d4c2b2d
@sebastienros
sebastienros merged commit 217b32d into main Jul 29, 2026
1 check passed
@sebastienros
sebastienros deleted the sebros/sourcegen branch July 29, 2026 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants