/// <summary> /// Retourne vrai si les jetons contenus dans token matchent ce pattern. /// </summary> /// <param name="tokens"></param> /// <returns></returns> public Match MatchPattern(List <Token> tokens) { Match m = new Match() { MatchPattern = false, MatchUnits = new List <MatchUnit>() }; for (int i = 0; i < tokens.Count; i++) { Token token = tokens[i]; if (MatchPattern(token)) { m.MatchUnits.Add(new MatchUnit() { Identifier = this.Identifier, MatchedToken = token }); Match nextMatch; // Si répétition autorisée, on regarde si ce motif se répète. if (Repeats) { nextMatch = MatchPattern(tokens.GetRange(i + 1, tokens.Count - i - 1)); if (nextMatch.MatchPattern) { m.MatchUnits.AddRange(nextMatch.MatchUnits); m.MatchPattern = true; break; } // Pas de répétition : on passe au suivant. } // Motif suivant if (Next != null) { nextMatch = Next.MatchPattern(tokens.GetRange(i + 1, tokens.Count - i - 1)); if (nextMatch.MatchPattern) { m.MatchUnits.AddRange(nextMatch.MatchUnits); m.MatchPattern = true; break; } } else { // Si on a fini, on veut que l'instruction ne contienne plus rien. if (i == tokens.Count - 1) { m.MatchPattern = true; } } } else if (Optional && Next != null) { return(Next.MatchPattern(tokens.GetRange(i, tokens.Count - i))); } } return(m); }
/// <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; } } } }
/// <summary> /// Retourne vrai si le jeton donné match avec ce bout de pattern. /// </summary> /// <param name="token"></param> /// <returns></returns> public bool MatchPattern(Token token) { return((Content == null || Content.Count == 0 || Content.Contains(token.Content)) && (TkType.Contains(token.TkType))); }