/// <summary> /// Ajoute des déclarations à ce Container à partir d'un block Access. /// </summary> /// <param name="block"></param> void AddDeclarationsFromAccessBlock(Language.NamedBlockDeclaration block, Semantic.TypeTable table) { foreach (Language.Instruction instruction in block.Instructions) { if (instruction is Language.FunctionDeclaration) { // Vérifie que le type de retour de la fonction est public. Language.FunctionDeclaration decl = (Language.FunctionDeclaration)instruction; decl.Func.Owner = table.Types[Language.SemanticConstants.StateClass]; if (!decl.Func.IsPublic) { ParsingLog.AddWarning("Les fonctions contenues dans le block access doivent être publiques.", instruction.Line, instruction.Character, instruction.Source); } if (decl.Func.ReturnType.IsPrivateOrHasPrivateGenericArgs()) { ParsingLog.AddWarning("Les types de retour des déclaration de fonction du bloc access doivent être des types publics" + " et ne pas contenir de paramètre générique privé. (donné : " + decl.Func.ReturnType.GetFullName() + ")", instruction.Line, instruction.Character, instruction.Source); } // Vérification du return type. List <string> outReasons; if (!decl.Func.ReturnType.DoesSupportSerialization(out outReasons)) { string error; error = "Le type de retour de la fonction '" + decl.Func.GetFullName() + "' (" + decl.Func.ReturnType.GetFullName() + ") ne prend pas en charge la sérialisation. Raisons : "; error += Tools.StringUtils.Join(outReasons, ", ") + "."; ParsingLog.AddWarning(error, decl.Line, decl.Character, decl.Source); } // Vérification des types des arguments. foreach (Language.FunctionArgument arg in decl.Func.Arguments) { if (!arg.ArgType.DoesSupportSerialization(out outReasons)) { string error; error = "Le type de l'argument '" + arg.ArgName + "' (" + arg.ArgType + ") de la fonction " + decl.Func.GetFullName() + " ne prend pas en charge la sérialisation. Reasons : "; error += Tools.StringUtils.Join(outReasons, ", ") + "."; ParsingLog.AddWarning(error, decl.Line, decl.Character, decl.Source); } } Declarations.Add((Language.FunctionDeclaration)instruction); } else { ParsingLog.AddError("Instruction de type " + instruction.GetType().Name + " invalide dans un AccessBlock", instruction.Line, instruction.Character, instruction.Source ); } } }
/// <summary> /// Retourne une copie superficielle de cet objet et une copie profonde de la fonction encapsulée. /// </summary> /// <returns></returns> public FunctionDeclaration FuncCopy() { FunctionDeclaration func = new FunctionDeclaration(); func.Func = Func.DeepCopy(); func.Character = Character; func.Line = Line; func.Source = Source; func.Code = Code; func.Comment = Comment; return(func); }
/// <summary> /// Crée le dictionnaire associant nom du langage et nom de la fonction à partir des variables contenues dans la fonction. /// </summary> Dictionary <string, string> ParseLanguageTranslations(Language.FunctionDeclaration decl) { Dictionary <string, string> langToFuncName = new Dictionary <string, string>(); foreach (Language.Instruction instruction in decl.Code) { Language.VariableDeclarationAndAssignmentInstruction varDecl = instruction as Language.VariableDeclarationAndAssignmentInstruction; if (varDecl != null) { // Vérification du type de la variable. if (varDecl.Declaration.Var.Type.GetFullName() == "string") { // Vérification des doublons. if (!langToFuncName.ContainsKey(varDecl.Declaration.Var.Name)) { Language.StringLiteral literal = varDecl.Assignment.Expression.Operand2 as Language.StringLiteral; // Vérification de la rvalue. if (literal != null) { langToFuncName.Add(varDecl.Declaration.Var.Name, literal.Value); } else { ParsingLog.AddError("La valeur affectée à une variable dans une fonction d'un block macro doit être un litéral de string.", varDecl.Line, varDecl.Character, varDecl.Source); } } else { ParsingLog.AddWarning("Présente de doublon pour la déclaration de " + varDecl.Declaration.Var.Name + " dans la fonction " + decl.Func.Name + ".", varDecl.Line, varDecl.Character, varDecl.Source); } } else { ParsingLog.AddWarning("Les fonctions des blocs macro ne peuvent contenir que des variables de type string, obtenu : " + varDecl.Declaration.Var.Type.GetFullName(), varDecl.Line, varDecl.Character, varDecl.Source); } } else { ParsingLog.AddWarning("Instruction de type " + instruction.GetType() + " inatendue dans une déclaration de fonction macro", decl.Line, decl.Character, decl.Source); } } return(langToFuncName); }
/// <summary> /// Ajoute des déclarations à ce Container à partir d'un block Access. /// </summary> /// <param name="block"></param> void AddDeclarationsFromMacroBlock(Language.NamedBlockDeclaration block, Semantic.TypeTable table) { foreach (Language.Instruction instruction in block.Instructions) { if (instruction is Language.ClassDeclaration) { Language.ClassDeclaration classDecl = (Language.ClassDeclaration)instruction; MacroClass classMacro = new MacroClass(); // Récupération du type en language Clank.Core. Language.ClankType type = table.Types[classDecl.GetFullName()]; classMacro.Type = type; bool foundName = false; // Parse les instructions à la recherche de : // - une fonction Name qui retourne un string et qui contient des variables de type string // - d'autres fonctions qui ne contiennent que des variables de type string. foreach (Language.Instruction funcDeclInstruction in classDecl.Instructions) { Language.FunctionDeclaration funcDecl = funcDeclInstruction as Language.FunctionDeclaration; if (funcDecl != null) { if (funcDecl.Func.Name == "name") { // Nom de la classe foundName = true; classMacro.LanguageToTypeName = ParseLanguageTranslations(funcDecl); } else { // Ajout de la fonction. MacroFunction func = new MacroFunction(); func.Function = funcDecl.Func; func.LanguageToFunctionName = ParseLanguageTranslations(funcDecl); classMacro.Functions.Add(func.Function.GetFullName(), func); } } else if (funcDeclInstruction is Language.PlaceholderInstruction) { } else { ParsingLog.AddWarning("Seules les déclarations de fonctions sont autorisées dans les classes de block macro. Obtenu : " + funcDeclInstruction.GetType().Name + ".", instruction.Line, instruction.Character, instruction.Source); } } ClassDeclarations.Add(classMacro); // Erreur si pas de nom de classe trouvé : if (!foundName) { ParsingLog.AddError("Fonction 'string name()' attendue dans la déclaration de macro du type '" + type.Name + "'.", instruction.Line, instruction.Character, instruction.Source); } } else if (instruction is Language.FunctionDeclaration) { Language.FunctionDeclaration funcDecl = (Language.FunctionDeclaration)instruction; MacroFunction func = new MacroFunction(); func.Function = funcDecl.Func; func.LanguageToFunctionName = ParseLanguageTranslations(funcDecl); FunctionDeclarations.Add(func); } else { ParsingLog.AddWarning("Déclaration de classe attendue dans le block macro. Obtenu : " + instruction.GetType() + ".", instruction.Line, instruction.Character, instruction.Source); } } }
/// <summary> /// Génère un modèle abstrait représentant les classes à créer pour le serveur du projet cible. /// </summary> /// <returns></returns> public List <Language.Instruction> GenerateServerProject() { List <Language.Instruction> classes = new List <Language.Instruction>(); // Copie de la classe state Language.ClassDeclaration stateClass = State.StateClass.Copy(); stateClass.Modifiers.Add("public"); stateClass.Comment = "Contient toutes les informations concernant l'état du serveur."; // Copie des classes state List <Language.ClassDeclaration> stateClasses = new List <Language.ClassDeclaration>(); foreach (Language.ClassDeclaration decl in State.Classes) { stateClasses.Add(decl.Copy()); } ; // Ajoute les enums. classes.AddRange(stateClass.Instructions.Where((Language.Instruction inst) => { return(inst is Language.EnumDeclaration); })); // Ne conserve que les fonctions dans la classe state. stateClass.Instructions = stateClass.Instructions.Where((Language.Instruction inst) => { // Ne garde que les fonctions. return(inst is Language.FunctionDeclaration); }).ToList(); // Ajoute les méthodes access / write. foreach (Language.FunctionDeclaration method in Access.Declarations) { Language.FunctionDeclaration cpy = method.FuncCopy(); cpy.Func.Arguments.Add(new Language.FunctionArgument() { ArgType = Types.FetchInstancedType("int", new Semantic.TypeTable.Context()), ArgName = Language.SemanticConstants.ClientID }); stateClass.Instructions.Add(cpy); } foreach (Language.FunctionDeclaration method in Write.Declarations) { Language.FunctionDeclaration cpy = method.FuncCopy(); cpy.Func.Arguments.Add(new Language.FunctionArgument() { ArgType = Types.FetchInstancedType("int", new Semantic.TypeTable.Context()), ArgName = Language.SemanticConstants.ClientID }); stateClass.Instructions.Add(cpy); } // Ajoute la méthode de traitement des messages. // Le code effectif de cette méthode est créé par le générateur de code // du language cible. Language.Macros.ProcessMessageMacro processFunc = new Language.Macros.ProcessMessageMacro(Access, Write); processFunc.Comment = "Génère le code pour la fonction de traitement des messages."; stateClass.Instructions.Add(processFunc); // Classe de message //Language.ClassDeclaration messageClass = new Language.ClassDeclaration() { Name = "_Message" }; //messageClass.Instructions.Add(new Language.VariableDeclarationInstruction() { Var = new Language.Variable() { Name = "Type", Type = enumMessageTypeInstance } }); // Classes de message //List<Language.ClassDeclaration> messageClasses = new List<Language.ClassDeclaration>(); #if false // Ajout des fonctions de Write. #region Write foreach (Language.FunctionDeclaration ex in Write.Declarations) { // Membre de l'enum de message enumDecl.Members.Add("MessageType_" + ex.Func.Name); // Classe de message Language.ClankType klassType = new Language.ClankType() { Name = "Write_" + ex.Func.Name + "_Message" }; Language.ClassDeclaration klass = new Language.ClassDeclaration(); klass.Name = klassType.Name; // Création d'un constructeur. Language.Constructor cons = new Language.Constructor(); cons.Owner = klassType; // Création : // Des variables d'instance de la classe. // Des paramètres du constructeur, de l'initialisation de ces paramètres foreach (Language.FunctionArgument arg in ex.Func.Arguments) { Language.Variable argVar = new Language.Variable() { Name = "_" + arg.ArgName, Type = arg.ArgType }; // Déclaration de la variable klass.Instructions.Add(new Language.VariableDeclarationInstruction() { Var = argVar, Modifiers = new List <string>() { "public" } }); // Ajout de l'argument au constructeur. cons.Arguments.Add(arg); // Ajout de l'assignement de la variable cons.Code.Add(new Language.AffectationInstruction() { Expression = new Language.BinaryExpressionGroup() { Operand1 = argVar, Operand2 = new Language.Variable() { Name = arg.ArgName, Type = arg.ArgType }, Operator = Language.Operator.Affectation } }); } // Ajout de la nouvelle classe. klass.Instructions.Add(new Language.ConstructorDeclaration() { Func = cons }); messageClasses.Add(klass); // Déclaration de fonction dans State. stateClass.Instructions.Add(ex); } #endregion // Ajout des fonctions de Access. #region Access foreach (Language.FunctionDeclaration decl in Access.Declarations) { // Membre de l'enum de message enumDecl.Members.Add("MessageType_" + decl.Func.Name); // Classe de message Language.ClankType klassType = new Language.ClankType() { Name = "Access_" + decl.Func.Name + "_Message" }; Language.ClassDeclaration klass = new Language.ClassDeclaration(); klass.Name = klassType.Name; // Création d'un constructeur. Language.Constructor cons = new Language.Constructor(); cons.Owner = klassType; // Création : // Des variables d'instance de la classe. // Des paramètres du constructeur, de l'initialisation de ces paramètres foreach (Language.FunctionArgument arg in decl.Func.Arguments) { Language.Variable argVar = new Language.Variable() { Name = "_" + arg.ArgName, Type = arg.ArgType }; // Déclaration de la variable klass.Instructions.Add(new Language.VariableDeclarationInstruction() { Var = argVar, Modifiers = new List <string>() { "public" } }); // Ajout de l'argument au constructeur. cons.Arguments.Add(arg); // Ajout de l'assignement de la variable cons.Code.Add(new Language.AffectationInstruction() { Expression = new Language.BinaryExpressionGroup() { Operand1 = argVar, Operand2 = new Language.Variable() { Name = arg.ArgName, Type = arg.ArgType }, Operator = Language.Operator.Affectation } }); } // Ajout de la nouvelle classe. klass.Instructions.Add(new Language.ConstructorDeclaration() { Func = cons }); messageClasses.Add(klass); // Déclaration de fonction dans State. stateClass.Instructions.Add(decl); } #endregion #endif //classes.AddRange(messageClasses); classes.AddRange(stateClasses); classes.Add(stateClass); return(classes); }