private IEnumerable<Field> DetermineMinimalFactoryFields(Node nd) { // special case to allow a single optional argument if there would have been no arguments // and we can determine a best single argument. Field allowOptionalField = null; var optionalCount = OptionalFactoryArgumentCount(nd); if (optionalCount == 0) { return null; // no fields... } var requiredCount = RequiredFactoryArgumentCount(nd, includeKind: false); if (requiredCount == 0 && optionalCount > 1) { allowOptionalField = DetermineMinimalOptionalField(nd); } return nd.Fields.Where(f => IsRequiredFactoryField(nd, f) || allowOptionalField == f); }
private Field DetermineMinimalOptionalField(Node nd) { // first if there is a single list, then choose the list because it would not have been optional int listCount = nd.Fields.Count(f => IsAnyNodeList(f.Type)); if (listCount == 1) { return nd.Fields.First(f => IsAnyNodeList(f.Type)); } else { // otherwise, if there is a single optional node, use that.. int nodeCount = nd.Fields.Count(f => IsNode(f.Type) && f.Type != "SyntaxToken"); if (nodeCount == 1) { return nd.Fields.First(f => IsNode(f.Type) && f.Type != "SyntaxToken"); } else { return null; } } }
private void WriteGreenUpdateMethod(Node node) { WriteLine(); Write(" public {0} Update(", node.Name); // parameters for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (f > 0) Write(", "); var type = field.Type == "SyntaxNodeOrTokenList" ? "Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList<CSharpSyntaxNode>" : field.Type == "SyntaxTokenList" ? "Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList<SyntaxToken>" : IsNodeList(field.Type) ? "Microsoft.CodeAnalysis.Syntax.InternalSyntax." + field.Type : IsSeparatedNodeList(field.Type) ? "Microsoft.CodeAnalysis.Syntax.InternalSyntax." + field.Type : field.Type; Write("{0} {1}", type, CamelCase(field.Name)); } WriteLine(")"); WriteLine(" {"); Write(" if ("); int nCompared = 0; for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (IsDerivedOrListOfDerived("SyntaxNode", field.Type) || IsDerivedOrListOfDerived("SyntaxToken", field.Type) || field.Type == "SyntaxNodeOrTokenList") { if (nCompared > 0) Write(" || "); Write("{0} != this.{1}", CamelCase(field.Name), field.Name); nCompared++; } } if (nCompared > 0) { WriteLine(")"); WriteLine(" {"); Write(" var newNode = SyntaxFactory.{0}(", StripPost(node.Name, "Syntax")); if (node.Kinds.Count > 1) { Write("this.Kind, "); } for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (f > 0) Write(", "); Write(CamelCase(field.Name)); } WriteLine(");"); WriteLine(" var diags = this.GetDiagnostics();"); WriteLine(" if (diags != null && diags.Length > 0)"); WriteLine(" newNode = newNode.WithDiagnosticsGreen(diags);"); WriteLine(" var annotations = this.GetAnnotations();"); WriteLine(" if (annotations != null && annotations.Length > 0)"); WriteLine(" newNode = newNode.WithAnnotationsGreen(annotations);"); WriteLine(" return newNode;"); WriteLine(" }"); } WriteLine(); WriteLine(" return this;"); WriteLine(" }"); }
private void WriteSetDiagnostics(Node node) { WriteLine(); WriteLine(" internal override GreenNode SetDiagnostics(DiagnosticInfo[] diagnostics)"); WriteLine(" {"); Write(" return new {0}(", node.Name); Write("this.Kind, "); for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (f > 0) Write(", "); Write("this.{0}", CamelCase(field.Name)); } WriteLine(", diagnostics, GetAnnotations());"); WriteLine(" }"); }
// creates a factory with only the required arguments (everything else is defaulted) private void WriteRedMinimalFactory(Node nd, bool withStringNames = false) { var optionalCount = OptionalFactoryArgumentCount(nd); if (optionalCount == 0) return; // already handled w/ general factory method var minimalFactoryfields = new HashSet<Field>(DetermineMinimalFactoryFields(nd)); if (withStringNames && minimalFactoryfields.Count(f => IsRequiredFactoryField(nd, f) && CanAutoConvertFromString(f)) == 0) return; // no string-name overload necessary this.WriteLine(); WriteComment(string.Format("<summary>Creates a new {0} instance.</summary>", nd.Name), " "); Write(" {0} static {1} {2}(", "public", nd.Name, StripPost(nd.Name, "Syntax")); bool hasPreviousParameter = false; if (nd.Kinds.Count > 1) { Write("SyntaxKind kind"); hasPreviousParameter = true; } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (minimalFactoryfields.Contains(field)) { var type = GetRedPropertyType(field); if (IsRequiredFactoryField(nd, field)) { if (hasPreviousParameter) Write(", "); if (withStringNames && CanAutoConvertFromString(field)) { type = "string"; } Write("{0} {1}", type, CamelCase(field.Name)); hasPreviousParameter = true; } else { if (hasPreviousParameter) Write(", "); Write("{0} {1} = default({0})", type, CamelCase(field.Name)); hasPreviousParameter = true; } } } WriteLine(")"); WriteLine(" {"); Write(" return SyntaxFactory.{0}(", StripPost(nd.Name, "Syntax")); bool hasPreviousArgument = false; if (nd.Kinds.Count > 1) { Write("kind"); hasPreviousArgument = true; } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (hasPreviousArgument) Write(", "); if (minimalFactoryfields.Contains(field)) { if (IsRequiredFactoryField(nd, field)) { if (withStringNames && CanAutoConvertFromString(field)) { Write("{0}({1})", GetStringConverterMethod(field), CamelCase(field.Name)); } else { Write("{0}", CamelCase(field.Name)); } } else { if (IsOptional(field) || IsAnyList(field.Type)) { Write("{0}", CamelCase(field.Name)); } else { Write("{0} ?? {1}", CamelCase(field.Name), GetDefaultValue(nd, field)); } } } else { var defaultValue = GetDefaultValue(nd, field); Write(defaultValue); } hasPreviousArgument = true; } WriteLine(");"); WriteLine(" }"); }
private IEnumerable<Field> DetermineRedFactoryWithNoAutoCreatableTokenFields(Node nd) { return nd.Fields.Where(f => !IsAutoCreatableToken(nd, f)); }
private string GetDefaultValue(Node nd, Field field) { System.Diagnostics.Debug.Assert(!IsRequiredFactoryField(nd, field)); if (IsOptional(field) || IsAnyList(field.Type)) { return string.Format("default({0})", GetRedPropertyType(field)); } else if (field.Type == "SyntaxToken") { // auto construct token? if (field.Kinds.Count == 1) { return string.Format("SyntaxFactory.Token(SyntaxKind.{0})", field.Kinds[0].Name); } else { return string.Format("SyntaxFactory.Token(Get{0}{1}Kind(kind))", StripPost(nd.Name, "Syntax"), StripPost(field.Name, "Opt")); } } else { var referencedNode = GetNode(field.Type); return string.Format("SyntaxFactory.{0}()", StripPost(referencedNode.Name, "Syntax")); } }
// full factory signature with nothing optional private void WriteRedFactory(Node nd) { this.WriteLine(); var valueFields = nd.Fields.Where(n => IsValueField(n)).ToList(); var nodeFields = nd.Fields.Where(n => !IsValueField(n)).ToList(); WriteComment(string.Format("<summary>Creates a new {0} instance.</summary>", nd.Name), " "); Write(" {0} static {1} {2}(", "public", nd.Name, StripPost(nd.Name, "Syntax")); WriteRedFactoryParameters(nd); WriteLine(")"); WriteLine(" {"); // validate kinds if (nd.Kinds.Count > 1) { WriteLine(" switch (kind)"); WriteLine(" {"); foreach (var kind in nd.Kinds) { WriteLine(" case SyntaxKind.{0}:", kind.Name); } WriteLine(" break;"); WriteLine(" default:"); WriteLine(" throw new ArgumentException(\"kind\");"); WriteLine(" }"); } // validate parameters for (int i = 0, n = nodeFields.Count; i < n; i++) { var field = nodeFields[i]; var pname = CamelCase(field.Name); if (field.Type == "SyntaxToken") { if (field.Kinds != null && field.Kinds.Count > 0) { WriteLine(" switch ({0}.Kind())", pname); WriteLine(" {"); foreach (var kind in field.Kinds) { WriteLine(" case SyntaxKind.{0}:", kind.Name); } if (IsOptional(field)) { WriteLine(" case SyntaxKind.None:"); } WriteLine(" break;"); WriteLine(" default:"); WriteLine(" throw new ArgumentException(\"{0}\");", pname); WriteLine(" }"); } } else if (!IsAnyList(field.Type) && !IsOptional(field)) { WriteLine(" if ({0} == null)", CamelCase(field.Name)); WriteLine(" throw new ArgumentNullException(nameof({0}));", CamelCase(field.Name)); } } Write(" return ({0})Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxFactory.{1}(", nd.Name, StripPost(nd.Name, "Syntax")); if (nd.Kinds.Count > 1) { Write("kind, "); } for (int i = 0, n = nodeFields.Count; i < n; i++) { var field = nodeFields[i]; if (i > 0) Write(", "); if (field.Type == "SyntaxToken") { Write("(Syntax.InternalSyntax.SyntaxToken){0}.Node", CamelCase(field.Name)); } else if (field.Type == "SyntaxList<SyntaxToken>") { Write("{0}.Node.ToGreenList<Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken>()", CamelCase(field.Name)); } else if (IsNodeList(field.Type)) { Write("{0}.Node.ToGreenList<Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.{1}>()", CamelCase(field.Name), GetElementType(field.Type)); } else if (IsSeparatedNodeList(field.Type)) { Write("{0}.Node.ToGreenSeparatedList<Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.{1}>()", CamelCase(field.Name), GetElementType(field.Type)); } else if (field.Type == "SyntaxNodeOrTokenList") { Write("{0}.Node.ToGreenList<Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode>()", CamelCase(field.Name)); } else { Write("{0} == null ? null : (Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.{1}){0}.Green", CamelCase(field.Name), field.Type); } } // values are at end for (int i = 0, n = valueFields.Count; i < n; i++) { var field = valueFields[i]; Write(", "); Write(CamelCase(field.Name)); } WriteLine(").CreateRed();"); WriteLine(" }"); this.WriteLine(); }
private int RequiredFactoryArgumentCount(Node nd, bool includeKind = true) { int count = 0; // kind must be specified in factory if (nd.Kinds.Count > 1 && includeKind) { count++; } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (IsRequiredFactoryField(nd, field)) { count++; } } return count; }
private void WriteRedUpdateMethod(Node node) { WriteLine(); Write(" {0} {1} Update(", "public", node.Name); // parameters for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (f > 0) Write(", "); var type = field.Type == "SyntaxList<SyntaxToken>" ? "SyntaxTokenList" : field.Type; Write("{0} {1}", type, CamelCase(field.Name)); } WriteLine(")"); WriteLine(" {"); Write(" if ("); int nCompared = 0; for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (IsDerivedOrListOfDerived("SyntaxNode", field.Type) || IsDerivedOrListOfDerived("SyntaxToken", field.Type) || field.Type == "SyntaxNodeOrTokenList") { if (nCompared > 0) Write(" || "); Write("{0} != this.{1}", CamelCase(field.Name), field.Name); nCompared++; } } if (nCompared > 0) { WriteLine(")"); WriteLine(" {"); Write(" var newNode = SyntaxFactory.{0}(", StripPost(node.Name, "Syntax")); if (node.Kinds.Count > 1) { Write("this.Kind(), "); } for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (f > 0) Write(", "); Write(CamelCase(field.Name)); } WriteLine(");"); WriteLine(" var annotations = this.GetAnnotations();"); WriteLine(" if (annotations != null && annotations.Length > 0)"); WriteLine(" return newNode.WithAnnotations(annotations);"); WriteLine(" return newNode;"); WriteLine(" }"); } WriteLine(); WriteLine(" return this;"); WriteLine(" }"); }
private void WriteRedAcceptMethod(Node node, bool genericArgument, bool genericResult) { string genericArgs = (genericResult && genericArgument) ? "<TArgument, TResult>" : genericResult ? "<TResult>" : ""; WriteLine(); WriteLine(" public override " + (genericResult ? "TResult" : "void") + " Accept" + genericArgs + "(CSharpSyntaxVisitor" + genericArgs + " visitor{0})", genericArgument ? ", TArgument argument" : ""); WriteLine(" {"); WriteLine(" " + (genericResult ? "return " : "") + "visitor.Visit{0}(this{1});", StripPost(node.Name, "Syntax"), genericArgument ? ", argument" : ""); WriteLine(" }"); }
private void WriteRedAcceptMethods(Node node) { //WriteRedAcceptMethod(node, true, true); WriteRedAcceptMethod(node, false, true); WriteRedAcceptMethod(node, false, false); }
private void WriteNodeGenerator(Node node, bool isGreen) { var valueFields = node.Fields.Where(n => !IsNodeOrNodeList(n.Type)); var nodeFields = node.Fields.Where(n => IsNodeOrNodeList(n.Type)); var internalNamespace = isGreen ? "Microsoft.CodeAnalysis.Syntax.InternalSyntax." : ""; var csharpNamespace = isGreen ? "Syntax.InternalSyntax." : ""; var syntaxFactory = isGreen ? "InternalSyntaxFactory" : "SyntaxFactory"; var strippedName = StripPost(node.Name, "Syntax"); WriteLine($"private static {csharpNamespace}{node.Name} Generate{strippedName}()"); Write($" => {syntaxFactory}.{strippedName}("); //instantiate node bool first = true; if (node.Kinds.Count > 1) { Write($"SyntaxKind.{node.Kinds[0].Name}"); //TODO: other kinds? first = false; } foreach (var field in nodeFields) { if (!first) { Write(", "); } first = false; if (IsOptional(field)) { if (isGreen) { Write("null"); } else { Write($"default({field.Type})"); } } else if (IsAnyList(field.Type)) { string typeName; if (isGreen) { typeName = internalNamespace + field.Type.Replace("<", "<" + csharpNamespace); } else { typeName = (field.Type == "SyntaxList<SyntaxToken>") ? "SyntaxTokenList" : field.Type; } Write($"new {typeName}()"); } else if (field.Type == "SyntaxToken") { var kind = ChooseValidKind(field); var leadingTrivia = isGreen ? "null, " : string.Empty; var trailingTrivia = isGreen ? ", null" : string.Empty; if (kind == "IdentifierToken") { Write($"{syntaxFactory}.Identifier(\"{field.Name}\")"); } else if (kind == "StringLiteralToken") { Write($"{syntaxFactory}.Literal({leadingTrivia}\"string\", \"string\"{trailingTrivia})"); } else if (kind == "CharacterLiteralToken") { Write($"{syntaxFactory}.Literal({leadingTrivia}\"a\", 'a'{trailingTrivia})"); } else if (kind == "NumericLiteralToken") { Write($"{syntaxFactory}.Literal({leadingTrivia}\"1\", 1{trailingTrivia})"); } else { Write($"{syntaxFactory}.Token(SyntaxKind.{ChooseValidKind(field)})"); } } else if (field.Type == "CSharpSyntaxNode") { Write($"{syntaxFactory}.IdentifierName({syntaxFactory}.Identifier(\"{field.Name}\"))"); } else { //drill down to a concrete type var type = field.Type; while (true) { var subTypes = ChildMap[type]; if (!subTypes.Any()) { break; } type = subTypes.First(); } Write($"Generate{StripPost(type, "Syntax")}()"); } } foreach (var field in valueFields) { if (!first) { Write(", "); } first = false; Write($"new {field.Type}()"); } WriteLine(");"); }
private void WriteFactoryPropertyTest(Node node, bool isGreen) { var valueFields = node.Fields.Where(n => !IsNodeOrNodeList(n.Type)); var nodeFields = node.Fields.Where(n => IsNodeOrNodeList(n.Type)); var strippedName = StripPost(node.Name, "Syntax"); WriteLine("[Fact]"); WriteLine($"public void Test{strippedName}FactoryAndProperties()"); OpenBlock(); WriteLine($"var node = Generate{strippedName}();"); WriteLine(); //check properties { string withStat = null; foreach (var field in nodeFields) { if (IsOptional(field)) { if (!isGreen && field.Type == "SyntaxToken") { WriteLine($"Assert.Equal(SyntaxKind.None, node.{field.Name}.Kind());"); } else { WriteLine($"Assert.Null(node.{field.Name});"); } } else if (field.Type == "SyntaxToken") { if (!isGreen) { WriteLine($"Assert.Equal(SyntaxKind.{ChooseValidKind(field)}, node.{field.Name}.Kind());"); } else { WriteLine($"Assert.Equal(SyntaxKind.{ChooseValidKind(field)}, node.{field.Name}.Kind);"); } } else { if (field.Type == "SyntaxToken") { WriteLine($"Assert.NotEqual(default, node.{field.Name});"); } else if ( field.Type == "SyntaxTokenList" || field.Type.StartsWith("SyntaxList<") || field.Type.StartsWith("SeparatedSyntaxList<")) { WriteLine($"Assert.Equal(default, node.{field.Name});"); } else { WriteLine($"Assert.NotNull(node.{field.Name});"); } } if (!isGreen) { withStat += $".With{field.Name}(node.{field.Name})"; } } foreach (var field in valueFields) { WriteLine($"Assert.Equal(new {field.Type}(), node.{field.Name});"); if (!isGreen) { withStat += $".With{field.Name}(node.{field.Name})"; } } if (!isGreen && withStat != null) { WriteLine($"var newNode = node{withStat};"); WriteLine("Assert.Equal(node, newNode);"); } } if (isGreen) { WriteLine(); WriteLine("AttachAndCheckDiagnostics(node);"); } CloseBlock(); }
private void WriteGreenFactoryParameters(Node nd) { if (nd.Kinds.Count > 1) { Write("SyntaxKind kind, "); } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (i > 0) Write(", "); var type = field.Type; if (type == "SyntaxNodeOrTokenList") { type = "Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList<CSharpSyntaxNode>"; } else if (IsSeparatedNodeList(field.Type) || IsNodeList(field.Type)) { type = "Microsoft.CodeAnalysis.Syntax.InternalSyntax." + type; } Write("{0} {1}", type, CamelCase(field.Name)); } }
private void WriteRedWithMethod(Node node) { WriteLine(); Write(" public {0} With(", node.Name); // parameters for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; var type = this.GetRedPropertyType(field); Write("Optional<{0}> {1} = default(Optional<{0}>)", type, CamelCase(field.Name)); if (f < node.Fields.Count - 1) Write(", "); } WriteLine(")"); WriteLine(" {"); Write(" return this.Update("); for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; var parameterName = CamelCase(field.Name); WriteLine(); Write(" {0}.HasValue ? {0}.Value : this.{1}", parameterName, field.Name); if (f < node.Fields.Count - 1) Write(","); } WriteLine(); WriteLine(" );"); WriteLine(" }"); }
private void WriteNodeGenerator(Node node, bool isGreen) { var valueFields = node.Fields.Where(n => !IsNodeOrNodeList(n.Type)); var nodeFields = node.Fields.Where(n => IsNodeOrNodeList(n.Type)); var namespaceQualification = isGreen ? "Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax." : ""; var strippedName = StripPost(node.Name, "Syntax"); WriteLine("private static {0}{1} Generate{2}()", namespaceQualification, node.Name, strippedName); OpenBlock(); //instantiate node { Write("return {0}SyntaxFactory.{1}(", namespaceQualification, strippedName); bool first = true; if (node.Kinds.Count > 1) { Write("SyntaxKind.{0}", node.Kinds[0].Name); //TODO: other kinds? first = false; } foreach (var field in nodeFields) { if (!first) { Write(", "); } first = false; if (IsOptional(field)) { if (isGreen) { Write("null"); } else { Write("default({0})", field.Type); } } else if (IsAnyList(field.Type)) { string typeName; if (isGreen) { typeName = namespaceQualification + field.Type.Replace("<", "<" + namespaceQualification); } else { typeName = (field.Type == "SyntaxList<SyntaxToken>") ? "SyntaxTokenList" : field.Type; } Write("new {0}()", typeName); } else if (field.Type == "SyntaxToken") { var kind = ChooseValidKind(field); var leadingTrivia = isGreen ? "null, " : ""; var trailingTrivia = isGreen ? ", null" : ""; if (kind == "IdentifierToken") { Write("{0}SyntaxFactory.Identifier(\"{1}\")", namespaceQualification, field.Name); } else if (kind == "StringLiteralToken") { Write("{0}SyntaxFactory.Literal({1}\"string\", \"string\"{2})", namespaceQualification, leadingTrivia, trailingTrivia); } else if (kind == "CharacterLiteralToken") { Write("{0}SyntaxFactory.Literal({1}\"a\", 'a'{2})", namespaceQualification, leadingTrivia, trailingTrivia); } else if (kind == "NumericLiteralToken") { Write("{0}SyntaxFactory.Literal({1}\"1\", 1{2})", namespaceQualification, leadingTrivia, trailingTrivia); } else { Write("{0}SyntaxFactory.Token(SyntaxKind.{1})", namespaceQualification, ChooseValidKind(field)); } } else if (field.Type == "CSharpSyntaxNode") { Write("{0}SyntaxFactory.IdentifierName({0}SyntaxFactory.Identifier(\"{1}\"))", namespaceQualification, field.Name); } else { //drill down to a concrete type var type = field.Type; while (true) { var subTypes = ChildMap[type]; if (!subTypes.Any()) { break; } type = subTypes.First(); } Write("Generate{0}()", StripPost(type, "Syntax")); } } foreach (var field in valueFields) { if (!first) { Write(", "); } first = false; Write("new {0}()", field.Type); } WriteLine(");"); } CloseBlock(); }
private void WriteRedSetters(Node node) { for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; var type = this.GetRedPropertyType(field); WriteLine(); WriteLine(" {0} {1} With{2}({3} {4})", "public", node.Name, StripPost(field.Name, "Opt"), type, CamelCase(field.Name)); WriteLine(" {"); // call update inside each setter Write(" return this.Update("); for (int f2 = 0; f2 < node.Fields.Count; f2++) { var field2 = node.Fields[f2]; if (f2 > 0) Write(", "); if (field2 == field) { this.Write("{0}", CamelCase(field2.Name)); } else { this.Write("this.{0}", field2.Name); } } WriteLine(");"); WriteLine(" }"); } }
private int OptionalFactoryArgumentCount(Node nd) { int count = 0; for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (IsOptional(field) || CanBeAutoCreated(nd, field) || IsAnyList(field.Type)) { count++; } } return count; }
private void WriteRedListHelperMethods(Node node) { for (int f = 0; f < node.Fields.Count; f++) { var field = node.Fields[f]; if (IsAnyList(field.Type)) { // write list helper methods for list properties WriteRedListHelperMethods(node, field); } else { Node referencedNode = GetNode(field.Type); if (referencedNode != null && (!IsOptional(field) || RequiredFactoryArgumentCount(referencedNode) == 0)) { // look for list members... for (int rf = 0; rf < referencedNode.Fields.Count; rf++) { var referencedNodeField = referencedNode.Fields[rf]; if (IsAnyList(referencedNodeField.Type)) { WriteRedNestedListHelperMethods(node, field, referencedNode, referencedNodeField); } } } } } }
private void WriteRedFactoryParameters(Node nd) { if (nd.Kinds.Count > 1) { Write("SyntaxKind kind, "); } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (i > 0) Write(", "); var type = this.GetRedPropertyType(field); Write("{0} {1}", type, CamelCase(field.Name)); } }
private void WriteRedListHelperMethods(Node node, Field field) { var argType = GetElementType(field.Type); WriteLine(); WriteLine(" public {0} Add{1}(params {2}[] items)", node.Name, field.Name, argType); WriteLine(" {"); WriteLine(" return this.With{0}(this.{1}.AddRange(items));", StripPost(field.Name, "Opt"), field.Name); WriteLine(" }"); }
// Writes Get<Property>Kind() methods for converting between node kind and member token kinds... private void WriteKindConverters(Node nd) { for (int f = 0; f < nd.Fields.Count; f++) { var field = nd.Fields[f]; if (field.Type == "SyntaxToken" && CanBeAutoCreated(nd, field) && field.Kinds.Count > 1) { WriteLine(); WriteLine(" private static SyntaxKind Get{0}{1}Kind(SyntaxKind kind)", StripPost(nd.Name, "Syntax"), StripPost(field.Name, "Opt")); WriteLine(" {"); WriteLine(" switch (kind)"); WriteLine(" {"); for (int k = 0; k < field.Kinds.Count; k++) { var nKind = nd.Kinds[k]; var pKind = field.Kinds[k]; WriteLine(" case SyntaxKind.{0}:", nKind.Name); WriteLine(" return SyntaxKind.{0};", pKind.Name); } WriteLine(" default:"); WriteLine(" throw new ArgumentOutOfRangeException();"); WriteLine(" }"); WriteLine(" }"); } } }
private void WriteRedNestedListHelperMethods(Node node, Field field, Node referencedNode, Field referencedNodeField) { var argType = GetElementType(referencedNodeField.Type); // AddBaseListTypes WriteLine(); WriteLine(" public {0} Add{1}{2}(params {3}[] items)", node.Name, StripPost(field.Name, "Opt"), referencedNodeField.Name, argType); WriteLine(" {"); if (IsOptional(field)) { var factoryName = StripPost(referencedNode.Name, "Syntax"); var varName = StripPost(CamelCase(field.Name), "Opt"); WriteLine(" var {0} = this.{1} ?? SyntaxFactory.{2}();", varName, field.Name, factoryName); WriteLine(" return this.With{0}({1}.With{2}({1}.{3}.AddRange(items)));", StripPost(field.Name, "Opt"), varName, StripPost(referencedNodeField.Name, "Opt"), referencedNodeField.Name); } else { WriteLine(" return this.With{0}(this.{1}.With{2}(this.{1}.{3}.AddRange(items)));", StripPost(field.Name, "Opt"), field.Name, StripPost(referencedNodeField.Name, "Opt"), referencedNodeField.Name); } WriteLine(" }"); }
// creates a factory without auto-creatable token arguments private void WriteRedFactoryWithNoAutoCreatableTokens(Node nd) { var nAutoCreatableTokens = nd.Fields.Count(f => IsAutoCreatableToken(nd, f)); if (nAutoCreatableTokens == 0) return; // already handled by general factory var factoryWithNoAutoCreatableTokenFields = new HashSet<Field>(DetermineRedFactoryWithNoAutoCreatableTokenFields(nd)); var minimalFactoryFields = DetermineMinimalFactoryFields(nd); if (minimalFactoryFields != null && factoryWithNoAutoCreatableTokenFields.SetEquals(minimalFactoryFields)) { return; // will be handled in minimal factory case } this.WriteLine(); WriteComment(string.Format("<summary>Creates a new {0} instance.</summary>", nd.Name), " "); Write(" {0} static {1} {2}(", "public", nd.Name, StripPost(nd.Name, "Syntax")); bool hasPreviousParameter = false; if (nd.Kinds.Count > 1) { Write("SyntaxKind kind"); hasPreviousParameter = true; } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (factoryWithNoAutoCreatableTokenFields.Contains(field)) { if (hasPreviousParameter) Write(", "); Write("{0} {1}", GetRedPropertyType(field), CamelCase(field.Name)); hasPreviousParameter = true; } } WriteLine(")"); WriteLine(" {"); Write(" return SyntaxFactory.{0}(", StripPost(nd.Name, "Syntax")); bool hasPreviousArgument = false; if (nd.Kinds.Count > 1) { Write("kind"); hasPreviousArgument = true; } for (int i = 0, n = nd.Fields.Count; i < n; i++) { var field = nd.Fields[i]; if (hasPreviousArgument) Write(", "); if (factoryWithNoAutoCreatableTokenFields.Contains(field)) { // pass supplied parameter on to general factory Write("{0}", CamelCase(field.Name)); } else { // pass an auto-created token to the general factory Write("{0}", GetDefaultValue(nd, field)); } hasPreviousArgument = true; } WriteLine(");"); WriteLine(" }"); }
protected bool CanBeAutoCreated(Node node, Field field) { return IsAutoCreatableToken(node, field) || IsAutoCreatableNode(node, field); }
private void WriteGreenSerialization(Node node) { var valueFields = node.Fields.Where(n => !IsNodeOrNodeList(n.Type)).ToList(); var nodeFields = node.Fields.Where(n => IsNodeOrNodeList(n.Type)).ToList(); // object reader constructor WriteLine(); WriteLine(" internal {0}(ObjectReader reader)", node.Name); WriteLine(" : base(reader)"); WriteLine(" {"); WriteLine(" this.SlotCount = {0};", nodeFields.Count); for (int i = 0, n = nodeFields.Count; i < n; i++) { var field = nodeFields[i]; string type = GetFieldType(field, green: true); WriteLine(" var {0} = ({1})reader.ReadValue();", CamelCase(field.Name), type); WriteLine(" if ({0} != null)", CamelCase(field.Name)); WriteLine(" {"); WriteLine(" AdjustFlagsAndWidth({0});", CamelCase(field.Name)); WriteLine(" this.{0} = {0};", CamelCase(field.Name), type); WriteLine(" }"); } for (int i = 0, n = valueFields.Count; i < n; i++) { var field = valueFields[i]; string type = GetFieldType(field, green: true); WriteLine(" this.{0} = ({1})reader.{2}();", CamelCase(field.Name), type, GetReaderMethod(type)); } WriteLine(" }"); // IWritable WriteLine(); WriteLine(" internal override void WriteTo(ObjectWriter writer)"); WriteLine(" {"); WriteLine(" base.WriteTo(writer);"); for (int i = 0, n = nodeFields.Count; i < n; i++) { var field = nodeFields[i]; string type = GetFieldType(field, green: true); WriteLine(" writer.WriteValue(this.{0});", CamelCase(field.Name)); } for (int i = 0, n = valueFields.Count; i < n; i++) { var field = valueFields[i]; var type = GetFieldType(field, green: true); WriteLine(" writer.{0}(this.{1});", GetWriterMethod(type), CamelCase(field.Name)); } WriteLine(" }"); // IReadable WriteLine(); WriteLine(" internal override Func<ObjectReader, object> GetReader()"); WriteLine(" {"); WriteLine(" return r => new {0}(r);", node.Name); WriteLine(" }"); }
private bool IsAutoCreatableToken(Node node, Field field) { return field.Type == "SyntaxToken" && field.Kinds != null && ((field.Kinds.Count == 1 && field.Kinds[0].Name != "IdentifierToken" && !field.Kinds[0].Name.EndsWith("LiteralToken", StringComparison.Ordinal)) || (field.Kinds.Count > 1 && field.Kinds.Count == node.Kinds.Count)); }
private void WriteGreenAcceptMethods(Node node) { //WriteLine(); //WriteLine(" public override TResult Accept<TArgument, TResult>(SyntaxVisitor<TArgument, TResult> visitor, TArgument argument)"); //WriteLine(" {"); //WriteLine(" return visitor.Visit{0}(this, argument);", StripPost(node.Name, "Syntax")); //WriteLine(" }"); WriteLine(); WriteLine(" public override TResult Accept<TResult>(CSharpSyntaxVisitor<TResult> visitor)"); WriteLine(" {"); WriteLine(" return visitor.Visit{0}(this);", StripPost(node.Name, "Syntax")); WriteLine(" }"); WriteLine(); WriteLine(" public override void Accept(CSharpSyntaxVisitor visitor)"); WriteLine(" {"); WriteLine(" visitor.Visit{0}(this);", StripPost(node.Name, "Syntax")); WriteLine(" }"); }
private bool IsAutoCreatableNode(Node node, Field field) { var referencedNode = GetNode(field.Type); return (referencedNode != null && RequiredFactoryArgumentCount(referencedNode) == 0); }
private void WriteGreenFactory(Node nd, bool withSyntaxFactoryContext = false) { var valueFields = nd.Fields.Where(n => !IsNodeOrNodeList(n.Type)).ToList(); var nodeFields = nd.Fields.Where(n => IsNodeOrNodeList(n.Type)).ToList(); Write(" public {0}{1} {2}(", withSyntaxFactoryContext ? "" : "static ", nd.Name, StripPost(nd.Name, "Syntax")); WriteGreenFactoryParameters(nd); WriteLine(")"); WriteLine(" {"); // validate kind if (nd.Kinds.Count > 1) { WriteLine(" switch (kind)"); WriteLine(" {"); foreach (var k in nd.Kinds) { WriteLine(" case SyntaxKind.{0}:", k.Name); } WriteLine(" break;"); WriteLine(" default:"); WriteLine(" throw new ArgumentException(\"kind\");"); WriteLine(" }"); } // validate parameters WriteLine("#if DEBUG"); for (int i = 0, n = nodeFields.Count; i < n; i++) { var field = nodeFields[i]; var pname = CamelCase(field.Name); if (!IsAnyList(field.Type) && !IsOptional(field)) { WriteLine(" if ({0} == null)", CamelCase(field.Name)); WriteLine(" throw new ArgumentNullException(nameof({0}));", CamelCase(field.Name)); } if (field.Type == "SyntaxToken" && field.Kinds != null && field.Kinds.Count > 0) { if (IsOptional(field)) { WriteLine(" if ({0} != null)", CamelCase(field.Name)); WriteLine(" {"); } WriteLine(" switch ({0}.Kind)", pname); WriteLine(" {"); foreach (var kind in field.Kinds) { WriteLine(" case SyntaxKind.{0}:", kind.Name); } //we need to check for Kind=None as well as node == null because that's what the red factory will pass if (IsOptional(field)) { WriteLine(" case SyntaxKind.None:"); } WriteLine(" break;"); WriteLine(" default:"); WriteLine(" throw new ArgumentException(\"{0}\");", pname); WriteLine(" }"); if (IsOptional(field)) { WriteLine(" }"); } } } WriteLine("#endif"); if (nd.Name != "SkippedTokensTriviaSyntax" && nd.Name != "DocumentationCommentTriviaSyntax" && nd.Name != "IncompleteMemberSyntax" && valueFields.Count + nodeFields.Count <= 3) { //int hash; //var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.IdentifierName, identifier, this.context, out hash); //if (cached != null) return (IdentifierNameSyntax)cached; //var result = new IdentifierNameSyntax(SyntaxKind.IdentifierName, identifier, this.context); //if (hash >= 0) //{ // SyntaxNodeCache.AddNode(result, hash); //} //return result; WriteLine(); //int hash; WriteLine(" int hash;"); //SyntaxNode cached = SyntaxNodeCache.TryGetNode(SyntaxKind.IdentifierName, identifier, this.context, out hash); if (withSyntaxFactoryContext) { Write(" var cached = CSharpSyntaxNodeCache.TryGetNode((int)"); } else { Write(" var cached = SyntaxNodeCache.TryGetNode((int)"); } WriteCtorArgList(nd, withSyntaxFactoryContext, valueFields, nodeFields); WriteLine(", out hash);"); // if (cached != null) return (IdentifierNameSyntax)cached; WriteLine(" if (cached != null) return ({0})cached;", nd.Name); WriteLine(); //var result = new IdentifierNameSyntax(SyntaxKind.IdentifierName, identifier); Write(" var result = new {0}(", nd.Name); WriteCtorArgList(nd, withSyntaxFactoryContext, valueFields, nodeFields); WriteLine(");"); //if (hash >= 0) WriteLine(" if (hash >= 0)"); //{ WriteLine(" {"); // SyntaxNodeCache.AddNode(result, hash); WriteLine(" SyntaxNodeCache.AddNode(result, hash);"); //} WriteLine(" }"); WriteLine(); //return result; WriteLine(" return result;"); } else { WriteLine(); Write(" return new {0}(", nd.Name); WriteCtorArgList(nd, withSyntaxFactoryContext, valueFields, nodeFields); WriteLine(");"); } WriteLine(" }"); }
private bool IsRequiredFactoryField(Node node, Field field) { return (!IsOptional(field) && !IsAnyList(field.Type) && !CanBeAutoCreated(node, field)) || IsValueField(field); }
private void WriteCtorArgList(Node nd, bool withSyntaxFactoryContext, List<Field> valueFields, List<Field> nodeFields) { if (nd.Kinds.Count == 1) { Write("SyntaxKind."); Write(nd.Kinds[0].Name); } else { Write("kind"); } for (int i = 0, n = nodeFields.Count; i < n; i++) { var field = nodeFields[i]; Write(", "); if (field.Type == "SyntaxList<SyntaxToken>" || IsAnyList(field.Type)) { Write("{0}.Node", CamelCase(field.Name)); } else { Write(CamelCase(field.Name)); } } // values are at end for (int i = 0, n = valueFields.Count; i < n; i++) { var field = valueFields[i]; Write(", "); Write(CamelCase(field.Name)); } if (withSyntaxFactoryContext) { Write(", this.context"); } }
private void WriteFactoryPropertyTest(Node node, bool isGreen) { var valueFields = node.Fields.Where(n => !IsNodeOrNodeList(n.Type)); var nodeFields = node.Fields.Where(n => IsNodeOrNodeList(n.Type)); var strippedName = StripPost(node.Name, "Syntax"); WriteLine("[Fact]"); WriteLine("public void Test{0}FactoryAndProperties()", strippedName); OpenBlock(); WriteLine("var node = Generate{0}();", strippedName); WriteLine(); //check properties { string withStat = null; foreach (var field in nodeFields) { if (IsOptional(field)) { if (!isGreen && field.Type == "SyntaxToken") { WriteLine("Assert.Equal(SyntaxKind.None, node.{0}.CSharpKind());", field.Name); } else { WriteLine("Assert.Null(node.{0});", field.Name); } } else if (field.Type == "SyntaxToken") { if (!isGreen) { WriteLine("Assert.Equal(SyntaxKind.{0}, node.{1}.CSharpKind());", ChooseValidKind(field), field.Name); } else { WriteLine("Assert.Equal(SyntaxKind.{0}, node.{1}.Kind);", ChooseValidKind(field), field.Name); } } else { WriteLine("Assert.NotNull(node.{0});", field.Name); } if (!isGreen) { withStat += string.Format(".With{0}(node.{0})", field.Name); } } foreach (var field in valueFields) { WriteLine("Assert.Equal(new {0}(), node.{1});", field.Type, field.Name); if (!isGreen) { withStat += string.Format(".With{0}(node.{0})", field.Name); } } if (!isGreen && withStat != null) { WriteLine("var newNode = node{0};", withStat); WriteLine("Assert.Equal(node, newNode);"); } } if (isGreen) { WriteLine(); WriteLine("AttachAndCheckDiagnostics(node);"); } CloseBlock(); }