public void GenerateCode(State rootState, TextWriter output, string namespaceName, UsingList usings, string[] genericArguments) { _syntaxTypes.Clear(); var code = new CodeCompileUnit(); var ns = new CodeNamespace(namespaceName); code.Namespaces.Add(ns); ns.Imports.Add(new CodeNamespaceImport("System")); foreach (var u in usings) { ns.Imports.Add(new CodeNamespaceImport(u.Namespace)); } GenerateCode(t => ns.Types.Add(t), rootState, new HashSet<string>(), genericArguments); ns.Types.Add(new CodeTypeDeclaration(_syntaxEnd) { Attributes = MemberAttributes.Public, TypeAttributes = TypeAttributes.Interface | TypeAttributes.Public, IsPartial = true, }); var codeProvider = new CSharpCodeProvider(); codeProvider.GenerateCodeFromCompileUnit(code, output, new CodeGeneratorOptions { BracingStyle = "C", IndentString = "\t", }); }
private State BuildGraph(string name, OperationList operations, ICollection<State> allStates) { var entryState = new State(name, true, Enumerable.Empty<Parameter>()); allStates.Add(entryState); var previousStates = new List<State> { entryState }; foreach (var operation in operations) { BuildGraph(previousStates, operation, allStates); } return entryState; }
private static void TraverseStateGraph(State currentState, HashSet<State> visitedStates) { if (visitedStates.Add(currentState)) { foreach (var nextState in currentState.NextStates) { TraverseStateGraph(nextState, visitedStates); } if (currentState.InnerState != null) { TraverseStateGraph(currentState.InnerState, visitedStates); } } }
private void BuildGraph(IList<State> previousStates, Operation operation, ICollection<State> allStates) { var currentState = new State(operation.Name, false, operation.Parameters); allStates.Add(currentState); foreach (var previousState in previousStates) { previousState.NextStates.Add(currentState); } switch (operation.Multiplicity) { case Multiplicity.One: previousStates.Clear(); previousStates.Add(currentState); break; case Multiplicity.ZeroOrOne: previousStates.Add(currentState); currentState.IsOptional = true; break; case Multiplicity.OneOrMany: previousStates.Clear(); previousStates.Add(currentState); currentState.NextStates.Add(currentState); break; case Multiplicity.ZeroOrMany: previousStates.Add(currentState); currentState.IsOptional = true; currentState.NextStates.Add(currentState); break; } if (operation.Operations.Any()) { currentState.InnerState = BuildGraph(operation.Name + "Inner", operation.Operations, allStates); } }
private void GenerateCode(Action<CodeTypeDeclaration> addType, State entryState, HashSet<string> usedNames, string[] genericArguments) { var stateType = AddGenericArguments(new CodeTypeDeclaration(GenerateName("{0}{1}Builder", entryState.Name, usedNames)) { IsPartial = true, }, genericArguments); addType(stateType); var states = new HashSet<State>(); FlattenStateTree(entryState, states); foreach (var state in states) { // Create syntax interface type var interfaceType = AddGenericArguments(new CodeTypeDeclaration(GenerateName("{0}{1}Syntax", state.Name, usedNames)) { Attributes = MemberAttributes.Public, TypeAttributes = TypeAttributes.Interface | TypeAttributes.Public, IsPartial = true, }, genericArguments); addType(interfaceType); stateType.BaseTypes.Add(AddGenericArguments(interfaceType.Name, genericArguments)); var syntaxType = stateType; // Create data container if (!state.IsRoot) { syntaxType = AddGenericArguments(new CodeTypeDeclaration(GenerateName("{0}{1}Data", state.Name, usedNames)) { Attributes = MemberAttributes.Final | MemberAttributes.Public, TypeAttributes = TypeAttributes.Sealed | TypeAttributes.Public, IsPartial = true, }, genericArguments); addType(syntaxType); foreach (var parameter in state.Parameters) { syntaxType.Members.Add( new CodeMemberField( parameter.Type, parameter.Name ) { Attributes = MemberAttributes.Public } ); } // Add field for each subsequent state var allowMultiple = state.NextStates.Contains(state); CodeMemberField nextStateField; if (allowMultiple) { var ilistType = new CodeTypeReference("System.Collections.Generic.IList"); ilistType.TypeArguments.Add(AddGenericArguments(syntaxType.Name, genericArguments)); var listType = new CodeTypeReference("System.Collections.Generic.List"); listType.TypeArguments.Add(AddGenericArguments(syntaxType.Name, genericArguments)); nextStateField = new CodeMemberField(ilistType, state.Name) { Attributes = MemberAttributes.Public, InitExpression = new CodeObjectCreateExpression(listType) }; } else { nextStateField = new CodeMemberField( AddGenericArguments(syntaxType.Name, genericArguments), state.Name ) { Attributes = MemberAttributes.Public }; } stateType.Members.Add(nextStateField); if (state.InnerState != null) { StateData innerStateData; if (!_syntaxTypes.TryGetValue(state.InnerState, out innerStateData)) { GenerateCode(addType, state.InnerState, usedNames, genericArguments); innerStateData = _syntaxTypes[state.InnerState]; } syntaxType.Members.Add(new CodeMemberField( AddGenericArguments(innerStateData.SyntaxType.Name, genericArguments), _inner ) { Attributes = MemberAttributes.Public }); } } _syntaxTypes.Add(state, new StateData { SyntaxType = syntaxType, InterfaceType = interfaceType }); } foreach (var state in states) { var stateData = _syntaxTypes[state]; foreach (var nextState in state.NextStates) { var nextStateData = _syntaxTypes[nextState]; // Add state transition method to interface var stateTransitionInterfaceMethod = new CodeMemberMethod { Name = nextState.Name, }; stateData.InterfaceType.Members.Add(stateTransitionInterfaceMethod); stateTransitionInterfaceMethod.ReturnType = AddGenericArguments(nextStateData.InterfaceType.Name, genericArguments); // Add state transition method to type var stateTransitionMethod = new CodeMemberMethod { Name = nextState.Name, PrivateImplementationType = AddGenericArguments(stateData.InterfaceType.Name, genericArguments), }; stateType.Members.Add(stateTransitionMethod); stateTransitionMethod.ReturnType = AddGenericArguments(nextStateData.InterfaceType.Name, genericArguments); var variable = new CodeVariableDeclarationStatement( AddGenericArguments(nextStateData.SyntaxType.Name, genericArguments), "_" + nextState.Name, new CodeObjectCreateExpression(AddGenericArguments(nextStateData.SyntaxType.Name, genericArguments)) ); stateTransitionMethod.Statements.Add(variable); foreach (var parameter in nextState.Parameters) { var methodParameter = new CodeParameterDeclarationExpression( parameter.Type, parameter.Name ); stateTransitionMethod.Parameters.Add(methodParameter); stateTransitionInterfaceMethod.Parameters.Add(methodParameter); stateTransitionMethod.Statements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeVariableReferenceExpression(variable.Name), parameter.Name ), new CodeSnippetExpression(methodParameter.Name) ) ); } var allowMultiple = nextState.NextStates.Contains(nextState); if (allowMultiple) { stateTransitionMethod.Statements.Add( new CodeMethodInvokeExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), nextState.Name ), "Add", new CodeSnippetExpression(variable.Name) ) ); } else { stateTransitionMethod.Statements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), nextState.Name ), new CodeSnippetExpression(variable.Name) ) ); } // Add parameter for inner state if (nextState.InnerState != null) { var innerStateData = _syntaxTypes[nextState.InnerState]; var methodParameterType = new CodeTypeReference("Func"); methodParameterType.TypeArguments.Add(AddGenericArguments(innerStateData.InterfaceType.Name, genericArguments)); methodParameterType.TypeArguments.Add(_syntaxEnd); var methodParameter = new CodeParameterDeclarationExpression(methodParameterType, _inner); if (nextState.InnerState.IsTerminal) { methodParameter.CustomAttributes.Add(new CodeAttributeDeclaration( new CodeTypeReference(typeof(OptionalAttribute)) )); } stateTransitionMethod.Parameters.Add(methodParameter); stateTransitionInterfaceMethod.Parameters.Add(methodParameter); stateTransitionMethod.Statements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeSnippetExpression("_" + nextState.Name), _inner ), new CodeObjectCreateExpression(AddGenericArguments(innerStateData.SyntaxType.Name, genericArguments)) ) ); stateTransitionMethod.Statements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( new CodeArgumentReferenceExpression(_inner), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null) ), new[] { new CodeExpressionStatement( new CodeDelegateInvokeExpression( new CodeSnippetExpression(methodParameter.Name), new CodeFieldReferenceExpression( new CodeSnippetExpression("_" + nextState.Name), _inner ) ) ) }, nextState.InnerState.IsTerminal ? new CodeStatement[0] : new[] { new CodeThrowExceptionStatement( new CodeObjectCreateExpression( typeof(ArgumentNullException), new CodePrimitiveExpression(_inner) ) ) } ) ); } stateTransitionMethod.Statements.Add( new CodeMethodReturnStatement( new CodeThisReferenceExpression() ) ); } if (state.IsTerminal) { stateData.InterfaceType.BaseTypes.Add(_syntaxEnd); } } }
private void FlattenStateTree(State entryState, HashSet<State> visitedStates) { if (visitedStates.Add(entryState)) { foreach (var state in entryState.NextStates) { FlattenStateTree(state, visitedStates); } } }