Added RazorSyntaxGenerator to the repo (dotnet/aspnetcore-tooling#313)
\n\nCommit migrated from cdafa554d7
This commit is contained in:
parent
740b1d3c27
commit
b2b8ca6479
|
|
@ -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,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,11 @@
|
|||
# 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 `path/to/Syntax.xml` `path/to/generated/output`
|
||||
|
||||
E.g,
|
||||
|
||||
`dotnet run ../Microsoft.AspNetCore.Razor.Language/Syntax/Syntax.xml ../Microsoft.AspNetCore.Razor.Language/Syntax/Generated/`
|
||||
|
|
@ -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