Skip to content

Commit

Permalink
Add statically initialized pattern tree
Browse files Browse the repository at this point in the history
Example of what's generated:

    static readonly Pattern Pattern =
        new Required(ImmutableArray.Create<Pattern>(
            new Either(ImmutableArray.Create<Pattern>(
                new Required(ImmutableArray.Create<Pattern>(
                    new Command("ship"),
                    new Command("new"),
                    new OneOrMore(
                        new Argument("<name>", null)))),
                new Required(ImmutableArray.Create<Pattern>(
                    new Command("ship"),
                    new Argument("<name>", null),
                    new Command("move"),
                    new Argument("<x>", null),
                    new Argument("<y>", null),
                    new Optional(ImmutableArray.Create<Pattern>(
                        new Option("", "--speed", 1, null))))),
                new Required(ImmutableArray.Create<Pattern>(
                    new Command("ship"),
                    new Command("shoot"),
                    new Argument("<x>", null),
                    new Argument("<y>", null))),
                new Required(ImmutableArray.Create<Pattern>(
                    new Command("mine"),
                    new Required(ImmutableArray.Create<Pattern>(
                        new Either(ImmutableArray.Create<Pattern>(
                            new Command("set"),
                            new Command("remove"))))),
                    new Argument("<x>", null),
                    new Argument("<y>", null),
                    new Optional(ImmutableArray.Create<Pattern>(
                        new Either(ImmutableArray.Create<Pattern>(
                            new Option("", "--moored", 0, null),
                            new Option("", "--drifting", 0, null))))))),
                new Required(ImmutableArray.Create<Pattern>(
                    new Required(ImmutableArray.Create<Pattern>(
                        new Option("-h", "--help", 0, null))))),
                new Required(ImmutableArray.Create<Pattern>(
                    new Option("", "--version", 0, null)))))));
  • Loading branch information
atifaziz committed Apr 5, 2021
1 parent 41bf0c0 commit 54825a7
Showing 1 changed file with 88 additions and 2 deletions.
90 changes: 88 additions & 2 deletions src/DocoptNet.CodeGeneration/SourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
namespace DocoptNet.CodeGeneration
{
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

[Generator]
public sealed class SourceGenerator : ISourceGenerator
Expand Down Expand Up @@ -71,19 +73,45 @@ into t
where t is not null
select t;

var added = false;

foreach (var t in templates)
{
try
{
if (Generate(ns, t.Name, t.Text) is { Length: > 0 } source)
{
added = true;
context.AddSource(t.Name + ".cs", source);
}
}
catch (DocoptLanguageErrorException e)
{
var args = Regex.Replace(e.Message, @"\r?\n", @"\n");
context.ReportDiagnostic(Diagnostic.Create(SyntaxError, Location.None, args));
}
}

if (added)
{
context.AddSource("Common.cs", @"
using System.Collections.Immutable;
abstract record Pattern;
abstract record BranchPattern(ImmutableArray<Pattern> Children) : Pattern;
record Required(ImmutableArray<Pattern> Children) : BranchPattern(Children);
record Optional(ImmutableArray<Pattern> Children) : BranchPattern(Children);
record OptionsShortcut() : Optional(ImmutableArray<Pattern>.Empty);
record Either(ImmutableArray<Pattern> Children) : BranchPattern(Children);
record OneOrMore(Pattern Pattern) : BranchPattern(ImmutableArray.Create(Pattern));
abstract record LeftPattern() : Pattern;
record Command(string Name) : LeftPattern;
record Argument(string Name, string Value) : LeftPattern;
record Option(string ShortName, string LongName, int ArgCount, object Value) : LeftPattern;
");
}
}

static readonly SourceText EmptySourceText = SourceText.From(string.Empty);
Expand All @@ -100,7 +128,8 @@ public static SourceText Generate(string? ns, string name, SourceText text, Enco
var usage = text.ToString();
var sb = new IndentingStringBuilder();

sb.Append("using System.Collections;").AppendLine()
sb.Append("using System.Collections;").AppendLine();
sb.Append("using System.Collections.Immutable;").AppendLine()
.AppendLine();

var isNamespaced = !string.IsNullOrEmpty(ns);
Expand Down Expand Up @@ -134,9 +163,66 @@ void AppendTree(Pattern pattern, int level = 0)
}
}

AppendTree(new Docopt().ParsePattern(usage));
sb.AppendLine();
var pattern = new Docopt().ParsePattern(usage);
AppendTree(pattern);

void AppendTreeCode(Pattern pattern, int level = 0)
{
sb.Append(' ', level * 4);
switch (pattern)
{
case OneOrMore { Children: { Count: 1 } children }:
sb.Append("new OneOrMore(").AppendLine();
AppendTreeCode(children[0], level + 1);
sb.Append(")");
break;
case BranchPattern { Children: { Count: > 0 } children } branch:
sb.Append("new ").Append(branch.GetType().Name).Append("(ImmutableArray.Create<Pattern>(").AppendLine();
var i = 0;
foreach (var child in children)
{
AppendTreeCode(child, level + 1);
if (++i < children.Count)
{
sb.Append(',');
sb.AppendLine();
}
}
sb.Append("))");
break;
case Command command:
sb.Append("new Command(")
.Append(Literal(command.Name).ToString())
.Append(')');
break;
case Argument { Name: var name }:
sb.Append("new Argument(")
.Append(Literal(name).ToString())
.Append(", null)");
break;
case Option option:
sb.Append("new Option(")
.Append(Literal(option.ShortName ?? string.Empty).ToString())
.Append(", ")
.Append(Literal(option.LongName ?? string.Empty).ToString())
.Append(", ")
.Append(option.ArgCount.ToString(CultureInfo.InvariantCulture))
.Append(", null")
.Append(')');
break;
}
}

sb.AppendLine();
sb.Append("static readonly Pattern Pattern =").AppendLine();
sb.Indent();
AppendTreeCode(pattern);
sb.Append(';');
sb.AppendLine();
sb.Outdent();

sb.AppendLine();
using (var reader = new StringReader(new Docopt().GenerateCode(usage)))
{
while (reader.ReadLine() is { } line)
Expand Down

0 comments on commit 54825a7

Please sign in to comment.