static async Task <SyntaxNode> GenerateStateMachineCode(Document document, StateMachineModel model, CancellationToken cancellationToken) { var funicularGeneratorsReferenced = document.FunicularGeneratorsReferenced(); var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); documentRoot = AddBaseInterfaceIfNotExists(documentRoot, model.BaseInterfaceName); foreach (var vertex in model.VertexClasses) { documentRoot = AddVertexClassDeclarationIfNotExists(documentRoot, vertex, model.BaseInterfaceName); } documentRoot = AddBaseParameterInterfaceIfNotExists(documentRoot, model.ParameterInterfaceName); documentRoot = AddParameterTypeIfNotExists(documentRoot, model.OuterParameterClassName); documentRoot = GenerateStateClassesRewriter.UpdateStateClasses(documentRoot, model); documentRoot = AddOrUpdateStateEnum(documentRoot, model, funicularGeneratorsReferenced); documentRoot = AddOrUpdateTriggerEnum(documentRoot, model, funicularGeneratorsReferenced); documentRoot = UpdateTransitionMethods(documentRoot, model); documentRoot = AddOrUpdateTransitionResultClass(documentRoot, model); documentRoot = AddOrUpdateExtensionClass(documentRoot, model, funicularGeneratorsReferenced); return(documentRoot); }
static SyntaxNode AddOrUpdateTriggerEnum(SyntaxNode documentRoot, StateMachineModel names, bool addUnionTypeAttribute) { var nestedEnumTypeName = StateMachineModel.NestedEnumTypeName; var enumMemberNames = names.VertexClasses.SelectMany(v => v.Transitions.Select(t => t.MethodName)).Distinct(); return(AddOrUpdateEnumClass(documentRoot, names.OuterTriggerClassName, nestedEnumTypeName, enumMemberNames, addUnionTypeAttribute)); }
static SyntaxNode AddOrUpdateStateEnum(SyntaxNode documentRoot, StateMachineModel names, bool addUnionTypeAttribute) { var stateClassName = names.OuterStateClassName; var nestedEnumTypeName = StateMachineModel.NestedEnumTypeName; var enumMemberNames = names.VertexClasses.Select(v => v.StateName); return(AddOrUpdateEnumClass(documentRoot, stateClassName, nestedEnumTypeName, enumMemberNames, addUnionTypeAttribute)); }
static MethodDeclarationSyntax GenerateDoTransitionMethod(StateMachineModel names) { return(GenerateApplyMethod( methodName: StateMachineModel.DoTransitionMethodName, names: names, returnType: SyntaxFactory.ParseTypeName(names.TransitionResultClassName), generateSwitchStatement: (vertex, transition, baseTypeParameterName, parameterParameterName) => SyntaxFactory.ParseStatement( $"return new {names.TransitionResultTransitionClassName}({baseTypeParameterName}, (({vertex.ClassName}){baseTypeParameterName}).{transition.MethodName}(({transition.FullParameterClassName}){parameterParameterName}), {parameterParameterName});") , generateDefaultStatement: (vertex, baseTypeParamName, parameterParamName) => SyntaxFactory.ParseStatement($"return new {names.TransitionResultInvalidTriggerClassName}({baseTypeParamName}, {parameterParamName});"))); }
static MethodDeclarationSyntax GenerateApplyMethod(StateMachineModel names) { var returnType = SyntaxFactory.ParseTypeName(names.BaseInterfaceName); return(GenerateApplyMethod( methodName: StateMachineModel.ApplyMethodName, names: names, returnType: returnType, generateSwitchStatement: (vertex, transition, baseTypeParameterName, parameterParameterName) => SyntaxFactory.ParseStatement( $"return (({vertex.ClassName}){baseTypeParameterName}).{transition.MethodName}(({transition.FullParameterClassName}){parameterParameterName});") , generateDefaultStatement: (vertex, baseTypeParamName, parameterParamName) => SyntaxFactory.ParseStatement($"return {baseTypeParamName};"))); }
static bool TryParseDotGraph(string dotFileName, out StateMachineModel model) { try { var graph = AntlrParserAdapter <string> .GetParser().Parse(File.ReadAllText(dotFileName)); model = new StateMachineModel(dotFileName, graph); return(true); } catch (Exception) { model = null; return(false); } }
static SyntaxNode UpdateTransitionMethods(SyntaxNode documentRoot, StateMachineModel names) { var outerParameterClass = names.TryGetOuterParameterClass(documentRoot).GetValueOrThrow(); foreach (var _ in names.VertexClasses .SelectMany(vertex => vertex.Transitions.Select(transition => new { vertex, transition }))) { var parameterClass = outerParameterClass.TryGetFirstDescendant <ClassDeclarationSyntax>(n => n.Name() == _.transition.NestedParameterClassName).GetValueOrThrow(); var parameterClassProperties = parameterClass.DescendantNodes() .OfType <PropertyDeclarationSyntax>() .Where(p => p.Type.Name() != names.OuterTriggerClassName) .ToImmutableList(); var vertexClass = _.vertex.TryGetVertexClass(documentRoot).GetValueOrThrow(); var origOverloadWithFlattenedParams = _.transition.TryGetTransitionMethod(vertexClass, false); var overloadWithFlattenedParams = origOverloadWithFlattenedParams.GetValueOrDefault(() => SyntaxFactory.MethodDeclaration( SyntaxFactory.ParseTypeName(_.transition.ReturnType), _.transition.MethodName) .Public() .WithBody(SyntaxFactory.Block() .AddStatements(SyntaxFactory.ParseStatement($"return new {_.transition.ReturnType}();")) )) .WithParameterList(SyntaxFactory.ParameterList(new SeparatedSyntaxList <ParameterSyntax>().AddRange( parameterClassProperties.Select(p => SyntaxFactory.Parameter(SyntaxFactory.ParseToken(p.Identifier.ToString().ToParameterName())).WithType(p.Type)) )) ); var newVertexClass = vertexClass.AddOrUpdateNode(origOverloadWithFlattenedParams, overloadWithFlattenedParams); var origOverloadWithParamClass = _.transition.TryGetTransitionMethod(newVertexClass, true); var overloadWithParamClass = origOverloadWithParamClass.GetValueOrDefault(() => SyntaxFactory.MethodDeclaration( SyntaxFactory.ParseTypeName(_.transition.ReturnType), _.transition.MethodName) .AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.ParseToken(StateMachineModel.ParametersParameterName)) .WithType(SyntaxFactory.ParseTypeName(_.transition.FullParameterClassName))) .Public() .WithBody(SyntaxFactory.Block()) ).WithBody(SyntaxFactory.Block( SyntaxFactory.ParseStatement($"return {_.transition.MethodName}({string.Join(",", parameterClassProperties.Select(p => $"{StateMachineModel.ParametersParameterName}.{p.Identifier.ToString()}"))});") )); newVertexClass = newVertexClass.AddOrUpdateNode(origOverloadWithParamClass, overloadWithParamClass); documentRoot = documentRoot.ReplaceNode(vertexClass, newVertexClass); } return(documentRoot); }
static SyntaxNode AddOrUpdateExtensionClass(SyntaxNode documentRoot, StateMachineModel names, bool funicularGeneratorsReferenced) { var applyMethod = GenerateApplyMethod(names); var doTransitionMethod = GenerateDoTransitionMethod(names); var classDeclaration = names.TryGetExtensionClass(documentRoot) .Match(ext => ext, () => { var extensionClass = SyntaxFactory.ClassDeclaration(names.ExtensionClassName) .Public() .Static(); documentRoot = documentRoot.AddMemberToNamespace(extensionClass); return(extensionClass); }); classDeclaration = classDeclaration.AddOrUpdateMethod(m => m.Identifier.ToString() == StateMachineModel.ApplyMethodName && m.ParameterList.Parameters.Count == 2, applyMethod); classDeclaration = classDeclaration.AddOrUpdateMethod(m => m.Identifier.ToString() == StateMachineModel.DoTransitionMethodName && m.ParameterList.Parameters.Count == 2, doTransitionMethod); if (!funicularGeneratorsReferenced) { classDeclaration = classDeclaration .AddMatchMethods( QualifiedTypeName.NoParents(names.BaseInterfaceName), names.BaseName.ToParameterName(), $"{StateMachineModel.StatePropertyName}.{StateMachineModel.EnumPropertyName}", names.VertexClasses.Select(v => new MatchMethods.DerivedType(v.ClassName, v.StateName.ToParameterName(), $"{names.OuterStateClassName}.{StateMachineModel.NestedEnumTypeName}.{v.StateName}")) .ToImmutableList()) .AddMatchMethods( QualifiedTypeName.NoParents(names.ParameterInterfaceName), "parameter", $"{StateMachineModel.TriggerPropertyName}.{StateMachineModel.EnumPropertyName}", names.VertexClasses.SelectMany(v => v.Transitions .Select(t => new MatchMethods.DerivedType(t.FullParameterClassName, t.MethodName.ToParameterName(), $"{names.OuterTriggerClassName}.{StateMachineModel.NestedEnumTypeName}.{t.MethodName}"))) .Distinct().ToImmutableList() ); } return(documentRoot.ReplaceNode(names.TryGetExtensionClass(documentRoot).GetValueOrThrow(), classDeclaration)); }
static SyntaxNode AddOrUpdateTransitionResultClass(SyntaxNode documentRoot, StateMachineModel names) { documentRoot = documentRoot.AddOrUpdateClass(names.TransitionResultClassName, c => c.Public().Abstract().AddMembers(), c => c); var baseList = SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(names.TransitionResultClassName)); documentRoot = documentRoot.AddOrUpdateClass(names.TransitionResultTransitionClassName, c => c.Public().AddBaseListTypes(baseList), c => c .AddPropertyIfNotExists(names.BaseInterfaceName, "Source", p => p.WithGetter()) .AddPropertyIfNotExists(names.BaseInterfaceName, "Destination", p => p.WithGetter()) .AddPropertyIfNotExists(names.ParameterInterfaceName, "Trigger", p => p.WithGetter()) .WithConstructorFromGetOnlyProperties() ); documentRoot = documentRoot.AddOrUpdateClass(names.TransitionResultInvalidTriggerClassName, c => c.Public().AddBaseListTypes(baseList), c => c .AddPropertyIfNotExists(names.BaseInterfaceName, "Source", p => p.WithGetter()) .AddPropertyIfNotExists(names.ParameterInterfaceName, "Trigger", p => p.WithGetter()) .WithConstructorFromGetOnlyProperties() ); return(documentRoot); }
public GenerateStateClassesRewriter(StateMachineModel names) => m_Names = names;
static MethodDeclarationSyntax GenerateApplyMethod(string methodName, StateMachineModel names, TypeSyntax returnType, Func <StateMachineModel.VertexClass, StateMachineModel.TransitionMethod, string, string, StatementSyntax> generateSwitchStatement, Func <StateMachineModel.VertexClass, string, string, StatementSyntax> generateDefaultStatement) { var baseTypeParameterName = names.BaseName.ToParameterName(); var parameterParameterName = "parameter"; var baseType = SyntaxFactory.ParseTypeName(names.BaseInterfaceName); var applyMethod = SyntaxFactory.MethodDeclaration(returnType, methodName) .Public() .Static() .WithParameterList(SyntaxFactory.ParameterList() .AddParameters( SyntaxFactory.Parameter(SyntaxFactory.ParseToken(baseTypeParameterName)) .WithModifiers(SyntaxTokenList.Create(SyntaxFactory.Token(SyntaxKind.ThisKeyword))) .WithType(baseType), SyntaxFactory.Parameter(SyntaxFactory.ParseToken(parameterParameterName)) .WithType(SyntaxFactory.ParseTypeName(names.ParameterInterfaceName)) ) ) .WithBody(SyntaxFactory.Block() .AddStatements( SyntaxFactory.SwitchStatement(SyntaxFactory.ParseExpression( $"{baseTypeParameterName}.{StateMachineModel.StatePropertyName}.{StateMachineModel.EnumPropertyName}")) .AddSections(names.VertexClasses.Select(vertex => SyntaxFactory.SwitchSection() .AddLabels(SyntaxFactory.CaseSwitchLabel(SyntaxFactory.ParseExpression( $"{names.OuterStateClassName}.{StateMachineModel.NestedEnumTypeName}.{vertex.StateName}"))) .AddStatements(SyntaxFactory.Block( SyntaxFactory .SwitchStatement(SyntaxFactory.ParseExpression( $"{parameterParameterName}.{StateMachineModel.TriggerPropertyName}.{StateMachineModel.EnumPropertyName}")) .AddSections(vertex.Transitions.Select(transition => SyntaxFactory.SwitchSection() .AddLabels(SyntaxFactory.CaseSwitchLabel( SyntaxFactory.ParseExpression( $"{names.OuterTriggerClassName}.{StateMachineModel.NestedEnumTypeName}.{transition.MethodName}"))) .AddStatements(generateSwitchStatement(vertex, transition, baseTypeParameterName, parameterParameterName)) ) .Concat(new[] { SyntaxFactory.SwitchSection() .AddLabels(SyntaxFactory.DefaultSwitchLabel()) .AddStatements(generateDefaultStatement(vertex, baseTypeParameterName, parameterParameterName)) }) .ToArray() ) )) ) .Concat(new[] { SyntaxFactory.SwitchSection() .AddLabels(SyntaxFactory.DefaultSwitchLabel()) .AddStatements(SyntaxFactory.ParseStatement( $"throw new ArgumentException($\"Unknown type implementing {names.BaseInterfaceName}: {{{baseTypeParameterName}.GetType().Name}}\");")) }) .ToArray() ) )); return(applyMethod); }