Merge branch 'master' of ..\aspnetcore-tooling2\
This commit is contained in:
commit
7b56497d5e
|
|
@ -0,0 +1 @@
|
|||
[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark]
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
{
|
||||
public class CodeGenerationBenchmark
|
||||
{
|
||||
public CodeGenerationBenchmark()
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current != null && !File.Exists(Path.Combine(current.FullName, "MSN.cshtml")))
|
||||
{
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
var root = current;
|
||||
var fileSystem = RazorProjectFileSystem.Create(root.FullName);
|
||||
|
||||
ProjectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, b => RazorExtensions.Register(b)); ;
|
||||
|
||||
MSN = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"), FileKinds.Legacy);
|
||||
}
|
||||
|
||||
public RazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
public RazorProjectItem MSN { get; }
|
||||
|
||||
[Benchmark(Description = "Razor Design Time Code Generation of MSN.com")]
|
||||
public void CodeGeneration_DesignTime_LargeStaticFile()
|
||||
{
|
||||
var codeDocument = ProjectEngine.ProcessDesignTime(MSN);
|
||||
var generated = codeDocument.GetCSharpDocument();
|
||||
|
||||
if (generated.Diagnostics.Count != 0)
|
||||
{
|
||||
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, generated.Diagnostics));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Razor Runtime Code Generation of MSN.com")]
|
||||
public void CodeGeneration_Runtime_LargeStaticFile()
|
||||
{
|
||||
var codeDocument = ProjectEngine.Process(MSN);
|
||||
var generated = codeDocument.GetCSharpDocument();
|
||||
|
||||
if (generated.Diagnostics.Count != 0)
|
||||
{
|
||||
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, generated.Diagnostics));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,41 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\*.cs">
|
||||
<Link>Serialization\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestServices.cs">
|
||||
<Link>TestServices\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestWorkspace.cs">
|
||||
<Link>TestServices\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestLanguageServices.cs">
|
||||
<Link>TestServices\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestWorkspaceServices.cs">
|
||||
<Link>TestServices\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
{
|
||||
public class BackgroundCodeGenerationBenchmark : ProjectSnapshotManagerBenchmarkBase
|
||||
{
|
||||
[IterationSetup]
|
||||
public void Setup()
|
||||
{
|
||||
SnapshotManager = CreateProjectSnapshotManager();
|
||||
SnapshotManager.ProjectAdded(HostProject);
|
||||
SnapshotManager.Changed += SnapshotManager_Changed;
|
||||
}
|
||||
|
||||
[IterationCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
SnapshotManager.Changed -= SnapshotManager_Changed;
|
||||
|
||||
Tasks.Clear();
|
||||
}
|
||||
|
||||
private List<Task> Tasks { get; } = new List<Task>();
|
||||
|
||||
private DefaultProjectSnapshotManager SnapshotManager { get; set; }
|
||||
|
||||
[Benchmark(Description = "Generates the code for 100 files", OperationsPerInvoke = 100)]
|
||||
public async Task BackgroundCodeGeneration_Generate100Files()
|
||||
{
|
||||
for (var i = 0; i < Documents.Length; i++)
|
||||
{
|
||||
SnapshotManager.DocumentAdded(HostProject, Documents[i], TextLoaders[i % 4]);
|
||||
}
|
||||
|
||||
await Task.WhenAll(Tasks);
|
||||
}
|
||||
|
||||
private void SnapshotManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
// The real work happens here.
|
||||
var project = SnapshotManager.GetLoadedProject(e.ProjectFilePath);
|
||||
var document = project.GetDocument(e.DocumentFilePath);
|
||||
|
||||
Tasks.Add(document.GetGeneratedOutputAsync());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
{
|
||||
public class ProjectLoadBenchmark : ProjectSnapshotManagerBenchmarkBase
|
||||
{
|
||||
[IterationSetup]
|
||||
public void Setup()
|
||||
{
|
||||
SnapshotManager = CreateProjectSnapshotManager();
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshotManager SnapshotManager { get; set; }
|
||||
|
||||
[Benchmark(Description = "Initializes a project and 100 files", OperationsPerInvoke = 100)]
|
||||
public void ProjectLoad_AddProjectAnd100Files()
|
||||
{
|
||||
SnapshotManager.ProjectAdded(HostProject);
|
||||
|
||||
for (var i= 0; i < Documents.Length; i++)
|
||||
{
|
||||
SnapshotManager.DocumentAdded(HostProject, Documents[i], TextLoaders[i % 4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Serialization;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
{
|
||||
public class ProjectSnapshotManagerBenchmarkBase
|
||||
{
|
||||
public ProjectSnapshotManagerBenchmarkBase()
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current != null && !File.Exists(Path.Combine(current.FullName, "Razor.sln")))
|
||||
{
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
var root = current;
|
||||
var projectRoot = Path.Combine(root.FullName, "test", "testapps", "LargeProject");
|
||||
|
||||
HostProject = new HostProject(Path.Combine(projectRoot, "LargeProject.csproj"), FallbackRazorConfiguration.MVC_2_1, rootNamespace: null);
|
||||
|
||||
TextLoaders = new TextLoader[4];
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var filePath = Path.Combine(projectRoot, "Views", "Home", $"View00{i % 4}.cshtml");
|
||||
var text = SourceText.From(filePath, encoding: null);
|
||||
TextLoaders[i] = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create()));
|
||||
}
|
||||
|
||||
Documents = new HostDocument[100];
|
||||
for (var i = 0; i < Documents.Length; i++)
|
||||
{
|
||||
var filePath = Path.Combine(projectRoot, "Views", "Home", $"View00{i % 4}.cshtml");
|
||||
Documents[i] = new HostDocument(filePath, $"/Views/Home/View00{i}.cshtml", FileKinds.Legacy);
|
||||
}
|
||||
|
||||
var tagHelpers = Path.Combine(root.FullName, "benchmarks", "Microsoft.AspNetCore.Razor.Performance", "taghelpers.json");
|
||||
TagHelperResolver = new StaticTagHelperResolver(ReadTagHelpers(tagHelpers));
|
||||
}
|
||||
|
||||
internal HostProject HostProject { get; }
|
||||
|
||||
internal HostDocument[] Documents { get; }
|
||||
|
||||
internal TextLoader[] TextLoaders { get; }
|
||||
|
||||
internal TagHelperResolver TagHelperResolver { get; }
|
||||
|
||||
internal DefaultProjectSnapshotManager CreateProjectSnapshotManager()
|
||||
{
|
||||
var services = TestServices.Create(
|
||||
new IWorkspaceService[]
|
||||
{
|
||||
TagHelperResolver,
|
||||
new StaticProjectSnapshotProjectEngineFactory(),
|
||||
},
|
||||
new ILanguageService[]
|
||||
{
|
||||
});
|
||||
|
||||
return new DefaultProjectSnapshotManager(
|
||||
new TestForegroundDispatcher(),
|
||||
new TestErrorReporter(),
|
||||
Array.Empty<ProjectSnapshotChangeTrigger>(),
|
||||
new AdhocWorkspace(services));
|
||||
}
|
||||
|
||||
private static IReadOnlyList<TagHelperDescriptor> ReadTagHelpers(string filePath)
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Converters.Add(new RazorDiagnosticJsonConverter());
|
||||
serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
|
||||
|
||||
using (var reader = new JsonTextReader(File.OpenText(filePath)))
|
||||
{
|
||||
return serializer.Deserialize<IReadOnlyList<TagHelperDescriptor>>(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestForegroundDispatcher : ForegroundDispatcher
|
||||
{
|
||||
public override bool IsForegroundThread => true;
|
||||
|
||||
public override TaskScheduler ForegroundScheduler => TaskScheduler.Default;
|
||||
|
||||
public override TaskScheduler BackgroundScheduler => TaskScheduler.Default;
|
||||
}
|
||||
|
||||
private class TestErrorReporter : ErrorReporter
|
||||
{
|
||||
public override void ReportError(Exception exception)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, ProjectSnapshot project)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project workspaceProject)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class StaticTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
private readonly IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
|
||||
public StaticTagHelperResolver(IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
this._tagHelpers = tagHelpers;
|
||||
}
|
||||
|
||||
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, ProjectSnapshot projectSnapshot, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(new TagHelperResolutionResult(_tagHelpers, Array.Empty<RazorDiagnostic>()));
|
||||
}
|
||||
}
|
||||
|
||||
private class StaticProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
|
||||
{
|
||||
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
return RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
RazorExtensions.Register(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
{
|
||||
public class SyntaxTreeGenerationBenchmark
|
||||
{
|
||||
public SyntaxTreeGenerationBenchmark()
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current != null && !File.Exists(Path.Combine(current.FullName, "MSN.cshtml")))
|
||||
{
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
var root = current;
|
||||
var fileSystem = RazorProjectFileSystem.Create(root.FullName);
|
||||
|
||||
ProjectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, b => RazorExtensions.Register(b)); ;
|
||||
|
||||
var projectItem = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"), FileKinds.Legacy);
|
||||
MSN = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var directiveFeature = ProjectEngine.EngineFeatures.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||
Directives = directiveFeature?.Directives.ToArray() ?? Array.Empty<DirectiveDescriptor>();
|
||||
}
|
||||
|
||||
public RazorProjectEngine ProjectEngine { get; }
|
||||
|
||||
public RazorSourceDocument MSN { get; }
|
||||
|
||||
public DirectiveDescriptor[] Directives { get; }
|
||||
|
||||
[Benchmark(Description = "Razor Design Time Syntax Tree Generation of MSN.com")]
|
||||
public void SyntaxTreeGeneration_DesignTime_LargeStaticFile()
|
||||
{
|
||||
var options = RazorParserOptions.CreateDesignTime(o =>
|
||||
{
|
||||
foreach (var directive in Directives)
|
||||
{
|
||||
o.Directives.Add(directive);
|
||||
}
|
||||
});
|
||||
var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
|
||||
|
||||
if (syntaxTree.Diagnostics.Count != 0)
|
||||
{
|
||||
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Razor Runtime Syntax Tree Generation of MSN.com")]
|
||||
public void SyntaxTreeGeneration_Runtime_LargeStaticFile()
|
||||
{
|
||||
var options = RazorParserOptions.Create(o =>
|
||||
{
|
||||
foreach (var directive in Directives)
|
||||
{
|
||||
o.Directives.Add(directive);
|
||||
}
|
||||
});
|
||||
var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
|
||||
|
||||
if (syntaxTree.Diagnostics.Count != 0)
|
||||
{
|
||||
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.Serialization;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
{
|
||||
public class TagHelperSerializationBenchmark
|
||||
{
|
||||
private readonly byte[] _tagHelperBuffer;
|
||||
|
||||
public TagHelperSerializationBenchmark()
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current != null && !File.Exists(Path.Combine(current.FullName, "taghelpers.json")))
|
||||
{
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
var tagHelperFilePath = Path.Combine(current.FullName, "taghelpers.json");
|
||||
_tagHelperBuffer = File.ReadAllBytes(tagHelperFilePath);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Razor TagHelper Serialization")]
|
||||
public void TagHelper_Serialization_RoundTrip()
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Converters.Add(new RazorDiagnosticJsonConverter());
|
||||
serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
|
||||
|
||||
// Deserialize from json file.
|
||||
IReadOnlyList<TagHelperDescriptor> tagHelpers;
|
||||
using (var stream = new MemoryStream(_tagHelperBuffer))
|
||||
using (var reader = new JsonTextReader(new StreamReader(stream)))
|
||||
{
|
||||
tagHelpers = serializer.Deserialize<IReadOnlyList<TagHelperDescriptor>>(reader);
|
||||
}
|
||||
|
||||
// Serialize back to json.
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 4096))
|
||||
{
|
||||
serializer.Serialize(writer, tagHelpers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Compile the solution in Release mode (so binaries are available in release)
|
||||
|
||||
To run a specific benchmark add it as parameter.
|
||||
```
|
||||
dotnet run -c Release <benchmark_name>
|
||||
```
|
||||
|
||||
If you run without any parameters, you'll be offered the list of all benchmarks and get to choose.
|
||||
```
|
||||
dotnet run -c Release
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,358 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
internal abstract class AbstractFileWriter
|
||||
{
|
||||
private readonly TextWriter _writer;
|
||||
private readonly Tree _tree;
|
||||
private readonly IDictionary<string, string> _parentMap;
|
||||
private readonly ILookup<string, string> _childMap;
|
||||
private readonly IDictionary<string, Node> _nodeMap;
|
||||
private readonly IDictionary<string, TreeType> _typeMap;
|
||||
|
||||
private const int INDENT_SIZE = 4;
|
||||
private int _indentLevel;
|
||||
private bool _needIndent = true;
|
||||
|
||||
protected AbstractFileWriter(TextWriter writer, Tree tree)
|
||||
{
|
||||
_writer = writer;
|
||||
_tree = tree;
|
||||
_nodeMap = tree.Types.OfType<Node>().ToDictionary(n => n.Name);
|
||||
_typeMap = tree.Types.ToDictionary(n => n.Name);
|
||||
_parentMap = tree.Types.ToDictionary(n => n.Name, n => n.Base);
|
||||
_parentMap.Add(tree.Root, null);
|
||||
_childMap = tree.Types.ToLookup(n => n.Base, n => n.Name);
|
||||
}
|
||||
|
||||
protected IDictionary<string, string> ParentMap { get { return _parentMap; } }
|
||||
protected ILookup<string, string> ChildMap { get { return _childMap; } }
|
||||
protected Tree Tree { get { return _tree; } }
|
||||
|
||||
#region Output helpers
|
||||
|
||||
protected void Indent()
|
||||
{
|
||||
_indentLevel++;
|
||||
}
|
||||
|
||||
protected void Unindent()
|
||||
{
|
||||
if (_indentLevel <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot unindent from base level");
|
||||
}
|
||||
_indentLevel--;
|
||||
}
|
||||
|
||||
protected void Write(string msg)
|
||||
{
|
||||
WriteIndentIfNeeded();
|
||||
_writer.Write(msg);
|
||||
}
|
||||
|
||||
protected void Write(string msg, params object[] args)
|
||||
{
|
||||
WriteIndentIfNeeded();
|
||||
_writer.Write(msg, args);
|
||||
}
|
||||
|
||||
protected void WriteLine()
|
||||
{
|
||||
WriteLine("");
|
||||
}
|
||||
|
||||
protected void WriteLine(string msg)
|
||||
{
|
||||
WriteIndentIfNeeded();
|
||||
_writer.WriteLine(msg);
|
||||
_needIndent = true; //need an indent after each line break
|
||||
}
|
||||
|
||||
protected void WriteLine(string msg, params object[] args)
|
||||
{
|
||||
WriteIndentIfNeeded();
|
||||
_writer.WriteLine(msg, args);
|
||||
_needIndent = true; //need an indent after each line break
|
||||
}
|
||||
|
||||
private void WriteIndentIfNeeded()
|
||||
{
|
||||
if (_needIndent)
|
||||
{
|
||||
_writer.Write(new string(' ', _indentLevel * INDENT_SIZE));
|
||||
_needIndent = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void OpenBlock()
|
||||
{
|
||||
WriteLine("{");
|
||||
Indent();
|
||||
}
|
||||
|
||||
protected void CloseBlock()
|
||||
{
|
||||
Unindent();
|
||||
WriteLine("}");
|
||||
}
|
||||
|
||||
#endregion Output helpers
|
||||
|
||||
#region Node helpers
|
||||
|
||||
protected static string OverrideOrNewModifier(Field field)
|
||||
{
|
||||
return IsOverride(field) ? "override " : IsNew(field) ? "new " : "";
|
||||
}
|
||||
|
||||
protected static bool CanBeField(Field field)
|
||||
{
|
||||
return field.Type != "SyntaxToken" && !IsAnyList(field.Type) && !IsOverride(field) && !IsNew(field);
|
||||
}
|
||||
|
||||
protected static string GetFieldType(Field field, bool green)
|
||||
{
|
||||
if (IsAnyList(field.Type))
|
||||
{
|
||||
return green
|
||||
? "GreenNode"
|
||||
: "SyntaxNode";
|
||||
}
|
||||
|
||||
return field.Type;
|
||||
}
|
||||
|
||||
protected bool IsDerivedOrListOfDerived(string baseType, string derivedType)
|
||||
{
|
||||
return IsDerivedType(baseType, derivedType)
|
||||
|| ((IsNodeList(derivedType) || IsSeparatedNodeList(derivedType))
|
||||
&& IsDerivedType(baseType, GetElementType(derivedType)));
|
||||
}
|
||||
|
||||
protected static bool IsSeparatedNodeList(string typeName)
|
||||
{
|
||||
return typeName.StartsWith("SeparatedSyntaxList<", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
protected static bool IsNodeList(string typeName)
|
||||
{
|
||||
return typeName.StartsWith("SyntaxList<", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
protected static bool IsAnyNodeList(string typeName)
|
||||
{
|
||||
return IsNodeList(typeName) || IsSeparatedNodeList(typeName);
|
||||
}
|
||||
|
||||
protected bool IsNodeOrNodeList(string typeName)
|
||||
{
|
||||
return IsNode(typeName) || IsNodeList(typeName) || IsSeparatedNodeList(typeName) || typeName == "SyntaxNodeOrTokenList";
|
||||
}
|
||||
|
||||
protected static string GetElementType(string typeName)
|
||||
{
|
||||
if (!typeName.Contains("<"))
|
||||
return string.Empty;
|
||||
var iStart = typeName.IndexOf('<');
|
||||
var iEnd = typeName.IndexOf('>', iStart + 1);
|
||||
if (iEnd < iStart)
|
||||
return string.Empty;
|
||||
var sub = typeName.Substring(iStart + 1, iEnd - iStart - 1);
|
||||
return sub;
|
||||
}
|
||||
|
||||
protected static bool IsAnyList(string typeName)
|
||||
{
|
||||
return IsNodeList(typeName) || IsSeparatedNodeList(typeName) || typeName == "SyntaxNodeOrTokenList";
|
||||
}
|
||||
|
||||
protected bool IsDerivedType(string typeName, string derivedTypeName)
|
||||
{
|
||||
if (typeName == derivedTypeName)
|
||||
return true;
|
||||
if (derivedTypeName != null && _parentMap.TryGetValue(derivedTypeName, out var baseType))
|
||||
{
|
||||
return IsDerivedType(typeName, baseType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static bool IsRoot(Node n)
|
||||
{
|
||||
return n.Root != null && string.Compare(n.Root, "true", true) == 0;
|
||||
}
|
||||
|
||||
protected bool IsNode(string typeName)
|
||||
{
|
||||
return _parentMap.ContainsKey(typeName);
|
||||
}
|
||||
|
||||
protected Node GetNode(string typeName)
|
||||
=> _nodeMap.TryGetValue(typeName, out var node) ? node : null;
|
||||
|
||||
protected TreeType GetTreeType(string typeName)
|
||||
=> _typeMap.TryGetValue(typeName, out var node) ? node : null;
|
||||
|
||||
protected static bool IsOptional(Field f)
|
||||
{
|
||||
return f.Optional != null && string.Compare(f.Optional, "true", true) == 0;
|
||||
}
|
||||
|
||||
protected static bool IsOverride(Field f)
|
||||
{
|
||||
return f.Override != null && string.Compare(f.Override, "true", true) == 0;
|
||||
}
|
||||
|
||||
protected static bool IsNew(Field f)
|
||||
{
|
||||
return f.New != null && string.Compare(f.New, "true", true) == 0;
|
||||
}
|
||||
|
||||
protected static bool HasErrors(Node n)
|
||||
{
|
||||
return n.Errors == null || string.Compare(n.Errors, "true", true) == 0;
|
||||
}
|
||||
|
||||
protected static string CamelCase(string name)
|
||||
{
|
||||
if (char.IsUpper(name[0]))
|
||||
{
|
||||
name = char.ToLowerInvariant(name[0]) + name.Substring(1);
|
||||
}
|
||||
return FixKeyword(name);
|
||||
}
|
||||
|
||||
protected static string FixKeyword(string name)
|
||||
{
|
||||
if (IsKeyword(name))
|
||||
{
|
||||
return "@" + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
protected static string UnderscoreCamelCase(string name)
|
||||
{
|
||||
return "_" + CamelCase(name);
|
||||
}
|
||||
|
||||
protected string StripNode(string name)
|
||||
{
|
||||
return (_tree.Root.EndsWith("Node", StringComparison.Ordinal)) ? _tree.Root.Substring(0, _tree.Root.Length - 4) : _tree.Root;
|
||||
}
|
||||
|
||||
protected string StripRoot(string name)
|
||||
{
|
||||
var root = StripNode(_tree.Root);
|
||||
if (name.EndsWith(root, StringComparison.Ordinal))
|
||||
{
|
||||
return name.Substring(0, name.Length - root.Length);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
protected static string StripPost(string name, string post)
|
||||
{
|
||||
return name.EndsWith(post, StringComparison.Ordinal)
|
||||
? name.Substring(0, name.Length - post.Length)
|
||||
: name;
|
||||
}
|
||||
|
||||
protected static bool IsKeyword(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "bool":
|
||||
case "byte":
|
||||
case "sbyte":
|
||||
case "short":
|
||||
case "ushort":
|
||||
case "int":
|
||||
case "uint":
|
||||
case "long":
|
||||
case "ulong":
|
||||
case "double":
|
||||
case "float":
|
||||
case "decimal":
|
||||
case "string":
|
||||
case "char":
|
||||
case "object":
|
||||
case "typeof":
|
||||
case "sizeof":
|
||||
case "null":
|
||||
case "true":
|
||||
case "false":
|
||||
case "if":
|
||||
case "else":
|
||||
case "while":
|
||||
case "for":
|
||||
case "foreach":
|
||||
case "do":
|
||||
case "switch":
|
||||
case "case":
|
||||
case "default":
|
||||
case "lock":
|
||||
case "try":
|
||||
case "throw":
|
||||
case "catch":
|
||||
case "finally":
|
||||
case "goto":
|
||||
case "break":
|
||||
case "continue":
|
||||
case "return":
|
||||
case "public":
|
||||
case "private":
|
||||
case "internal":
|
||||
case "protected":
|
||||
case "static":
|
||||
case "readonly":
|
||||
case "sealed":
|
||||
case "const":
|
||||
case "new":
|
||||
case "override":
|
||||
case "abstract":
|
||||
case "virtual":
|
||||
case "partial":
|
||||
case "ref":
|
||||
case "out":
|
||||
case "in":
|
||||
case "where":
|
||||
case "params":
|
||||
case "this":
|
||||
case "base":
|
||||
case "namespace":
|
||||
case "using":
|
||||
case "class":
|
||||
case "struct":
|
||||
case "interface":
|
||||
case "delegate":
|
||||
case "checked":
|
||||
case "get":
|
||||
case "set":
|
||||
case "add":
|
||||
case "remove":
|
||||
case "operator":
|
||||
case "implicit":
|
||||
case "explicit":
|
||||
case "fixed":
|
||||
case "extern":
|
||||
case "event":
|
||||
case "enum":
|
||||
case "unsafe":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Node helpers
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class AbstractNode : TreeType
|
||||
{
|
||||
[XmlElement(ElementName = "Field", Type = typeof(Field))]
|
||||
public List<Field> Fields;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class Comment
|
||||
{
|
||||
[XmlAnyElement]
|
||||
public XmlElement[] Body;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class Field
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Name;
|
||||
|
||||
[XmlAttribute]
|
||||
public string Type;
|
||||
|
||||
[XmlAttribute]
|
||||
public string Optional;
|
||||
|
||||
[XmlAttribute]
|
||||
public string Override;
|
||||
|
||||
[XmlAttribute]
|
||||
public string New;
|
||||
|
||||
[XmlElement(ElementName = "Kind", Type = typeof(Kind))]
|
||||
public List<Kind> Kinds;
|
||||
|
||||
[XmlElement]
|
||||
public Comment PropertyComment;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class Kind
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class Node : TreeType
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Root;
|
||||
|
||||
[XmlAttribute]
|
||||
public string Errors;
|
||||
|
||||
[XmlElement(ElementName = "Kind", Type = typeof(Kind))]
|
||||
public List<Kind> Kinds;
|
||||
|
||||
[XmlElement(ElementName = "Field", Type = typeof(Field))]
|
||||
public List<Field> Fields;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class PredefinedNode : TreeType
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
[XmlRoot]
|
||||
public class Tree
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Root;
|
||||
|
||||
[XmlElement(ElementName = "Node", Type = typeof(Node))]
|
||||
[XmlElement(ElementName = "AbstractNode", Type = typeof(AbstractNode))]
|
||||
[XmlElement(ElementName = "PredefinedNode", Type = typeof(PredefinedNode))]
|
||||
public List<TreeType> Types;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class TreeType
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Name;
|
||||
|
||||
[XmlAttribute]
|
||||
public string Base;
|
||||
|
||||
[XmlElement]
|
||||
public Comment TypeComment;
|
||||
|
||||
[XmlElement]
|
||||
public Comment FactoryComment;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2 || args.Length > 2)
|
||||
{
|
||||
WriteUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
var inputFile = args[0];
|
||||
|
||||
if (!File.Exists(inputFile))
|
||||
{
|
||||
Console.WriteLine(Directory.GetCurrentDirectory());
|
||||
Console.WriteLine(inputFile + " not found.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var writeSource = true;
|
||||
var writeSignatures = false;
|
||||
string outputFile = null;
|
||||
|
||||
if (args.Length == 2)
|
||||
{
|
||||
if (args[1] == "/sig")
|
||||
{
|
||||
writeSignatures = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFile = args[1];
|
||||
}
|
||||
}
|
||||
|
||||
var reader = XmlReader.Create(inputFile, new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit });
|
||||
var serializer = new XmlSerializer(typeof(Tree));
|
||||
var tree = (Tree)serializer.Deserialize(reader);
|
||||
|
||||
if (writeSignatures)
|
||||
{
|
||||
SignatureWriter.Write(Console.Out, tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (writeSource)
|
||||
{
|
||||
var outputPath = outputFile.Trim('"');
|
||||
var prefix = Path.GetFileName(inputFile);
|
||||
var outputMainFile = Path.Combine(outputPath, $"{prefix}.Main.Generated.cs");
|
||||
var outputInternalFile = Path.Combine(outputPath, $"{prefix}.Internal.Generated.cs");
|
||||
var outputSyntaxFile = Path.Combine(outputPath, $"{prefix}.Syntax.Generated.cs");
|
||||
|
||||
WriteToFile(tree, SourceWriter.WriteMain, outputMainFile);
|
||||
WriteToFile(tree, SourceWriter.WriteInternal, outputInternalFile);
|
||||
WriteToFile(tree, SourceWriter.WriteSyntax, outputSyntaxFile);
|
||||
}
|
||||
//if (writeTests)
|
||||
//{
|
||||
// WriteToFile(tree, TestWriter.Write, outputFile);
|
||||
//}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void WriteUsage()
|
||||
{
|
||||
Console.WriteLine("Invalid usage");
|
||||
Console.WriteLine(typeof(Program).GetTypeInfo().Assembly.ManifestModule.Name + " input-file output-file [/write-test]");
|
||||
}
|
||||
|
||||
private static void WriteToFile(Tree tree, Action<TextWriter, Tree> writeAction, string outputFile)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
var writer = new StringWriter(stringBuilder);
|
||||
writeAction(writer, tree);
|
||||
|
||||
var text = stringBuilder.ToString();
|
||||
int length;
|
||||
do
|
||||
{
|
||||
length = text.Length;
|
||||
text = text.Replace("{\r\n\r\n", "{\r\n");
|
||||
} while (text.Length != length);
|
||||
|
||||
try
|
||||
{
|
||||
using (var outFile = new StreamWriter(File.Open(outputFile, FileMode.Create), Encoding.UTF8))
|
||||
{
|
||||
outFile.Write(text);
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
Console.WriteLine("Unable to access {0}. Is it checked out?", outputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Razor syntax generator
|
||||
|
||||
Syntax generator tool for the Razor syntax tree. This is a modified version of Roslyn's [CSharpSyntaxGenerator](https://github.com/dotnet/roslyn/tree/master/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator). For internal use only.
|
||||
|
||||
## Usage
|
||||
|
||||
dotnet run `full/path/to/Syntax.xml` `full/path/to/generated/output`
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Generates Razor syntax nodes from xml. For internal use only.</Description>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AssemblyName>dotnet-razorsyntaxgenerator</AssemblyName>
|
||||
<PackageId>RazorSyntaxGenerator</PackageId>
|
||||
<OutputType>Exe</OutputType>
|
||||
<EnableApiCheck>false</EnableApiCheck>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace RazorSyntaxGenerator
|
||||
{
|
||||
internal class SignatureWriter
|
||||
{
|
||||
private readonly TextWriter _writer;
|
||||
private readonly Tree _tree;
|
||||
private readonly Dictionary<string, string> _typeMap;
|
||||
|
||||
private SignatureWriter(TextWriter writer, Tree tree)
|
||||
{
|
||||
_writer = writer;
|
||||
_tree = tree;
|
||||
_typeMap = tree.Types.ToDictionary(n => n.Name, n => n.Base);
|
||||
_typeMap.Add(tree.Root, null);
|
||||
}
|
||||
|
||||
public static void Write(TextWriter writer, Tree tree)
|
||||
{
|
||||
new SignatureWriter(writer, tree).WriteFile();
|
||||
}
|
||||
|
||||
private void WriteFile()
|
||||
{
|
||||
_writer.WriteLine("using System;");
|
||||
_writer.WriteLine("using System.Collections;");
|
||||
_writer.WriteLine("using System.Collections.Generic;");
|
||||
_writer.WriteLine("using System.Linq;");
|
||||
_writer.WriteLine("using System.Threading;");
|
||||
_writer.WriteLine();
|
||||
_writer.WriteLine("namespace Microsoft.AspNetCore.Razor.Language.Syntax");
|
||||
_writer.WriteLine("{");
|
||||
|
||||
this.WriteTypes();
|
||||
|
||||
_writer.WriteLine("}");
|
||||
}
|
||||
|
||||
private void WriteTypes()
|
||||
{
|
||||
var nodes = _tree.Types.Where(n => !(n is PredefinedNode)).ToList();
|
||||
for (int i = 0, n = nodes.Count; i < n; i++)
|
||||
{
|
||||
var node = nodes[i];
|
||||
_writer.WriteLine();
|
||||
WriteType(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteType(TreeType node)
|
||||
{
|
||||
if (node is AbstractNode abstractNode)
|
||||
{
|
||||
_writer.WriteLine(" public abstract partial class {0} : {1}", node.Name, node.Base);
|
||||
_writer.WriteLine(" {");
|
||||
for (int i = 0, n = abstractNode.Fields.Count; i < n; i++)
|
||||
{
|
||||
var field = abstractNode.Fields[i];
|
||||
if (IsNodeOrNodeList(field.Type))
|
||||
{
|
||||
_writer.WriteLine(" public abstract {0}{1} {2} {{ get; }}", "", field.Type, field.Name);
|
||||
}
|
||||
}
|
||||
_writer.WriteLine(" }");
|
||||
}
|
||||
else if (node is Node nd)
|
||||
{
|
||||
_writer.WriteLine(" public partial class {0} : {1}", node.Name, node.Base);
|
||||
_writer.WriteLine(" {");
|
||||
|
||||
WriteKinds(nd.Kinds);
|
||||
|
||||
var valueFields = nd.Fields.Where(n => !IsNodeOrNodeList(n.Type)).ToList();
|
||||
var nodeFields = nd.Fields.Where(n => IsNodeOrNodeList(n.Type)).ToList();
|
||||
|
||||
for (int i = 0, n = nodeFields.Count; i < n; i++)
|
||||
{
|
||||
var field = nodeFields[i];
|
||||
_writer.WriteLine(" public {0}{1}{2} {3} {{ get; }}", "", "", field.Type, field.Name);
|
||||
}
|
||||
|
||||
for (int i = 0, n = valueFields.Count; i < n; i++)
|
||||
{
|
||||
var field = valueFields[i];
|
||||
_writer.WriteLine(" public {0}{1}{2} {3} {{ get; }}", "", "", field.Type, field.Name);
|
||||
}
|
||||
|
||||
_writer.WriteLine(" }");
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteKinds(List<Kind> kinds)
|
||||
{
|
||||
if (kinds.Count > 1)
|
||||
{
|
||||
foreach (var kind in kinds)
|
||||
{
|
||||
_writer.WriteLine(" // {0}", kind.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSeparatedNodeList(string typeName)
|
||||
{
|
||||
return typeName.StartsWith("SeparatedSyntaxList<", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private bool IsNodeList(string typeName)
|
||||
{
|
||||
return typeName.StartsWith("SyntaxList<", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public bool IsNodeOrNodeList(string typeName)
|
||||
{
|
||||
return IsNode(typeName) || IsNodeList(typeName) || IsSeparatedNodeList(typeName);
|
||||
}
|
||||
|
||||
private bool IsNode(string typeName)
|
||||
{
|
||||
return _typeMap.ContainsKey(typeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue