Skip to content

Source generation support#292

Open
sebastienros wants to merge 68 commits intomainfrom
sebros/sourcegen
Open

Source generation support#292
sebastienros wants to merge 68 commits intomainfrom
sebros/sourcegen

Conversation

@sebastienros
Copy link
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.
@sebastienros
Copy link
Owner Author

@Genteure I fixed all the possible compilation issues, at least it works on NCalc which relies on compiled regexes, logging source gen and PolySharp. Just saying that Parlot's source generator will also invoke other source generator so it can build and run the host assembly so it can generate new code for this same host assembly. Not sure how to feel about it, but it seems to work. I am pushing a new version right now.

@Bykiev Made some progress and hitting a problem with the outer LeftAssociative parser that throws an exception, I don't understand what it does, neither does the source gen ;)

@Genteure
Copy link
Contributor

I was watching the repository so this is already in my inbox, but appreciate the update 🙂
Perhaps you meant to ping @gpetrou

@gpetrou
Copy link

gpetrou commented Dec 19, 2025

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
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
Copy link

gpetrou commented Dec 19, 2025

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
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
Copy link

gpetrou commented Jan 11, 2026

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
Copy link

slang25 commented Feb 17, 2026

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

@sebastienros
Copy link
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.

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