/// <summary> /// Retourne la fonction statique pour un type donné dont le nom est passé en paramètre. /// </summary> /// <param name="functionName"></param> /// <param name="context"></param> /// <returns></returns> public Language.Function GetStaticFunction(Language.ClankTypeInstance instancedType, string functionName, List <Language.Evaluable> args, Context context) { // Obtient le nom complet de la fonction (avec les args). Language.ClankType owner = instancedType.BaseType; string fullName = owner.GetFullName() + "." + functionName + Language.Evaluable.GetArgTypesString(args); var instanciatedOverloads = InstanciatedFunc(instancedType); if (instanciatedOverloads.ContainsKey(fullName)) { Language.Function func = instanciatedOverloads[fullName]; // Vérification : la fonction est-elle statique ? if (!func.IsStatic) { throw new SemanticError("La fonction " + functionName + " n'est pas une méthode statique de " + owner.GetFullName() + "."); } return(instanciatedOverloads[fullName]); } return(null); }
/// <summary> /// Retourne le constructeur (instancié) pour un type donné. /// </summary> public Language.Function GetConstructor(Language.ClankType owner, Language.ClankTypeInstance inst, List <Language.Evaluable> args, Context context) { string fullName = owner.GetFullName() + "." + Language.SemanticConstants.New + Language.Evaluable.GetArgTypesString(args); var instanciedOverloads = InstanciatedFunc(inst); if (instanciedOverloads.ContainsKey(fullName)) { Language.Function func = instanciedOverloads[fullName]; // Vérification : la fonction est-elle bien marquée comme constructeur ? if (!func.IsConstructor) { throw new SemanticError("La fonction " + Language.SemanticConstants.New + " n'est pas un constructeur de " + owner.GetFullName() + "."); } return(func.Instanciate(inst.GenericArguments)); } if (args.Count != 0) { throw new SemanticError("Aucun constructeur de '" + owner.GetFullName() + "' ne matche les arguments de type : " + Language.Evaluable.GetArgTypesString(args) + ". Candidats possibles : " + GetCandidatesStr(inst, Language.SemanticConstants.New)); } // Si le constructeur n'existe pas, on crée le constructeur par défaut. Language.Function cons = new Language.Function() { Modifiers = new List <string>() { Language.SemanticConstants.Public, Language.SemanticConstants.Constructor }, ReturnType = inst, Arguments = new List <Language.FunctionArgument>(), Code = new List <Language.Instruction>(), Owner = owner, Type = inst, Name = Language.SemanticConstants.New }; return(cons); }
/// <summary> /// Parse le jeton pour en extraire une instance de type de base en fonction du contexte. /// </summary> public Language.ClankTypeInstance FetchInstancedType(Token token, Context context, bool containsType = false) { Language.ClankType baseType = null; bool isGeneric = false; List <Language.ClankTypeInstance> genericArguments = new List <Language.ClankTypeInstance>(); if (token.TkType == TokenType.List) { if (token.ListTokens.Count != 1) { throw new InvalidOperationException(); } return(FetchInstancedType(token.ListTokens[0], context, containsType)); } else if (token.TkType == TokenType.ArrayType) { // On crée un array avec comme param générique le type de cette array. baseType = Types["Array"]; Language.ClankTypeInstance inst = FetchInstancedType(token.ArrayTypeIdentifier, context, containsType); if (inst == null) { string error = "Le type " + token.ArrayTypeIdentifier.Content + " n'existe pas."; if (OnLog != null) { OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, error, token.Line, token.Character, token.Source)); } throw new SemanticError(error); } genericArguments.Add(inst); isGeneric = true; } else if (token.TkType == TokenType.GenericType) { if (!Types.ContainsKey(token.GenericTypeIdentifier.Content)) { // Erreur type inconnu. string error = "Le type générique '" + token.GenericTypeIdentifier.Content + "' est n'existe pas."; OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, error, token.Line, token.Character, token.Source)); throw new SemanticError(error); } baseType = Types[token.GenericTypeIdentifier.Content]; isGeneric = true; foreach (Token tok in token.GenericTypeArgs.ListTokens) { Language.ClankTypeInstance inst = FetchInstancedType(tok, context, true); if (inst == null) { string error = "Le type '" + tok.ToReadableCode() + "' n'existe pas."; if (OnLog != null) { OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, error, token.Line, token.Character, token.Source)); } throw new SemanticError(error); } genericArguments.Add(inst); } // Vérification : le nombre d'arguments du type générique est-il correct ? if (baseType.GenericArgumentNames.Count != token.GenericTypeArgs.ListTokens.Count) { string error = "Nombre d'arguments pour le type '" + baseType.GetFullName() + "' incorrect. Attendu : " + baseType.GetFullNameAndGenericArgs() + ". Obtenu " + baseType.GetFullName() + "<" + token.GenericTypeArgs.ToReadableCode() + "> (" + token.GenericTypeArgs.ListTokens.Count + " args)."; if (OnLog != null) { OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, error, token.Line, token.Character, token.Source)); } } } else if (token.TkType == TokenType.Name) { if (TypeInstances.ContainsKey(token.Content)) { return(TypeInstances[token.Content]); } else { Context c = context; while (c != null) { string contextPrefix = c.GetContextPrefix().Trim('.') + "'"; if (TypeInstances.ContainsKey(contextPrefix + token.Content)) { // Instance de type générique return(TypeInstances[contextPrefix + token.Content]); } c = c.ParentContext; } } baseType = new Language.ClankType() { Name = token.Content }; // FIXME : enlever true va créer une nouvelle instance de type basé sur un nouveau // type (baseType du dessus là) pas encore déclaré. -> on casse des références. if (!containsType || true) { // Rien n'a été trouvé, on retourne null. (ce n'est pas un type) return(null); } } Language.ClankTypeInstance type = new Language.ClankTypeInstance() { BaseType = baseType, GenericArguments = genericArguments }; string fullName = type.GetFullName(); if (!TypeInstances.ContainsKey(fullName)) { TypeInstances.Add(fullName, type); return(type); } else { return(TypeInstances[fullName]); } }
/// <summary> /// Effectue une recherche des types déclarés dans le script passé en paramètre. /// /// Seule la section "State" du script doit être passée. /// </summary> void FetchTypes(List <Token> tokens, Context context) { bool foundClass = false; bool isPublic = false; bool foundEnum = false; foreach (Token token in tokens) { if (foundClass) { #region FoundClass if (token.TkType == TokenType.NamedCodeBlock) { // SECU : vérifier que CodeBlockIdentifier est un Name. // Crée le type de base. Language.ClankType newType = new Language.ClankType() { Name = token.NamedCodeBlockIdentifier.Content, IsPublic = isPublic, IsMacro = context.BlockName == Language.SemanticConstants.MacroBk }; // Crée l'instance associée. Language.ClankTypeInstance newTypeInstance = new Language.ClankTypeInstance() { BaseType = newType, GenericArguments = new List <Language.ClankTypeInstance>(), }; string fullname = newTypeInstance.GetFullName(); if (TypeInstances.ContainsKey(fullname)) { TypeInstances[fullname] = newTypeInstance; } else { TypeInstances.Add(newTypeInstance.GetFullName(), newTypeInstance); } // Si le type existe déjà : on le remplace. if (Types.ContainsKey(newType.Name)) { OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, "Le type '" + newType.Name + "' est déjà défini.", token.Line, token.Character, token.Source)); } Types.Add(newType.Name, newType); } // On cherche les paramètres génériques et on en crée des instances. else if (token.TkType == TokenType.NamedGenericCodeBlock) { List <string> genericArgsNames = new List <string>(); // Arguments du type générique foreach (Token t in token.NamedGenericCodeBlockArgs.ListTokens) { if (t.ListTokens.Count != 1 && t.ChildToken.TkType != TokenType.Name) { throw new InvalidOperationException("Déclaration d'argument générique invalide"); } // Identificateur du type : contient son nom. genericArgsNames.Add(t.ChildToken.Content); } // Nouveau type générique Language.ClankType newType = new Language.ClankType() { Name = token.NamedGenericCodeBlockNameIdentifier.Content, GenericArgumentNames = genericArgsNames, IsPublic = isPublic, IsMacro = context.BlockName == Language.SemanticConstants.MacroBk }; // Override pour le type array. if (newType.Name == "Array" && newType.GenericArgumentNames.Count == 1 && newType.IsMacro) { newType = Types[newType.Name]; } else { Types.Add(newType.Name, newType); } // Création du contexte fils Context childContext = new Context(); childContext.ParentContext = context; childContext.Container = newType; // Création des types génériques et de leur instance. string contextPrefix = childContext.GetContextPrefix().Trim('.') + "'"; for (int i = 0; i < genericArgsNames.Count; i++) { Language.GenericParameterType genericType = new Language.GenericParameterType() { ParamId = i, Name = genericArgsNames[i], Prefix = contextPrefix }; Types.Add(genericType.GetFullName(), genericType); TypeInstances.Add(contextPrefix + genericArgsNames[i], new Language.ClankTypeInstance() { BaseType = genericType, }); } // Recherche d'autres types dans les instructions. FetchTypes(token.NamedGenericCodeBlockInstructions.ListTokens, childContext); } else { // throw new InvalidOperationException("Déclaration de classe attendue."); continue; } #endregion foundClass = false; isPublic = false; } else if (foundEnum) { #region Found enum if (token.TkType == TokenType.NamedCodeBlock) { // Crée le type de base. Language.ClankType newType = new Language.ClankType() { Name = token.NamedCodeBlockIdentifier.Content, IsPublic = isPublic, IsEnum = true, JType = Language.JSONType.Int, SupportSerialization = true, SupportSerializationAsGeneric = true }; // Crée l'instance associée. Language.ClankTypeInstance newTypeInstance = new Language.ClankTypeInstance() { BaseType = newType, GenericArguments = new List <Language.ClankTypeInstance>(), }; // Ajoute les membres de l'enum. string currentName = null; int currentValue = 0; foreach (Token tk in token.NamedCodeBlockInstructions.ListTokens) { if (tk.TkType != TokenType.List || tk.ChildToken.TkType != TokenType.ExpressionGroup) { string error = "Erreur de syntaxe dans la déclaration d'enum : jeton '" + tk.ToReadableCode() + "' inattendu."; OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, error, tk.Line, tk.Character, tk.Source)); throw new Tokenizers.SyntaxError(error, tk.Line, tk.Source); } Token expr = tk.ChildToken; Token name = expr.Operands1.ChildToken; Token value = expr.Operands2.ChildToken; Token op = expr.Operator; if (name.TkType == TokenType.Name && value.TkType == TokenType.NumberLiteral && op.Content == "=") { currentValue = Int32.Parse(value.Content); currentName = name.Content; // Erreur si doublon. if (newType.EnumValues.ContainsKey(currentName)) { string error = "La valeur '" + currentName + "' est déjà présente dans l'énumération '" + newType.Name + "'."; OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, error, tk.Line, tk.Character, tk.Source)); throw new Tokenizers.SyntaxError(error, tk.Line, tk.Source); } newType.EnumValues.Add(currentName, currentValue); } } // Ajoute le type dans la table des types. string fullname = newTypeInstance.GetFullName(); if (TypeInstances.ContainsKey(fullname)) { TypeInstances[fullname] = newTypeInstance; } else { TypeInstances.Add(newTypeInstance.GetFullName(), newTypeInstance); } // Si le type existe déjà : on le remplace. if (Types.ContainsKey(newType.Name)) { OnLog(new Tools.EventLog.Entry(Tools.EventLog.EntryType.Error, "Le type énuméré'" + newType.Name + "' est déjà défini.", token.Line, token.Character, token.Source)); } Types.Add(newType.Name, newType); } #endregion foundEnum = false; isPublic = false; } else // if !foundClass { switch (token.TkType) { // Parsing récursif des Listes case TokenType.ArgList: case TokenType.CodeBlock: case TokenType.List: case TokenType.InstructionList: FetchTypes(token.ListTokens, context); break; case TokenType.NamedCodeBlock: context.BlockName = token.NamedCodeBlockIdentifier.Content; FetchTypes(token.ListTokens, context); break; // Vérification du mot clef class case TokenType.Name: if (token.Content == Language.SemanticConstants.Class) { foundClass = true; } else if (token.Content == Language.SemanticConstants.Enum) { foundEnum = true; } else if (token.Content == Language.SemanticConstants.Public) { isPublic = true; } break; } } } }