/// <summary> /// Parse une expression quelconque contenue dans un jeton, /// retournant un IGettable. /// </summary> /// <param name="token"> /// Le jeton peut être un jeton contenant une liste de jetons : /// - OperandTokens /// - OperandOperatorGroup /// - ParenthesisGroup /// </param> /// <returns></returns> static IGettable ParseExpression(Token token, GlobalContext mainContext) { if (token.Type == TokenType.OperandTokens) { TokenList tokens = ((OperandToken)token).Tokens; if (tokens.Count == 1 && tokens.First().Type == TokenType.ExpressionGroup) { return(ParseExpressionGroup((ExpressionGroupToken)tokens.First(), mainContext)); } return(ParseSubExpression(tokens, mainContext)); } else if (token.Type == TokenType.ExpressionGroup) { return(ParseExpressionGroup((ExpressionGroupToken)token, mainContext)); } else if (token.Type == TokenType.ParenthesisGroup) { return(ParseExpression(new OperandToken(((ParenthesisGroupToken)token).Tokens), mainContext)); } else if (token.Type == TokenType.EvaluableBlock) { return(ParseEvaluableBlock((EvaluableGroupToken)token, mainContext)); } else { throw new Exception("Invalid token in expression"); // return Project.RpgGameRessources.ToAssetNamenew TokenList() { token }, mainContext); } }
/// <summary> /// Parse un Statement suivi de son block. /// En général, on obtient : /// Statement + Operand + BlockGroup. /// Le statement if pouvant être composé différemment (avec else et elsif), il n'est pas traité /// dans cette fonction. /// </summary> /// <param name="tokens"></param> /// <returns></returns> public static Instruction ParseBlockStatement(TokenList tokens, GlobalContext mainContext) { // Patch if (tokens.Count == 4 && tokens.First().Type == TokenType.Statement) { PatchInstruction patchInstruction = new PatchInstruction(); InfoToken itoken = (InfoToken)tokens.First(); if (itoken.Content != "patch") { throw new Exception("Invalid statement format"); } patchInstruction.FuncName = ((InfoToken)((OperandToken)tokens[1]).Tokens[0]).Content; InfoToken keyToken = (InfoToken)((OperandToken)(((PrefixedOperatorToken)tokens[2]).Operand)).Tokens.First(); patchInstruction.Key = keyToken.Content; patchInstruction.Instructions = ParseBlock(((BlockGroupToken)tokens[3]).Tokens).Instructions; return(patchInstruction); } if (tokens.Count != 3) { throw new Exception("Invalid instruction format."); } // On récupère les jetons. InfoToken statementToken = tokens[0] as InfoToken; Token exprToken = tokens[1]; BlockGroupToken blockToken = tokens[2] as BlockGroupToken; if (statementToken == null || blockToken == null || statementToken.Type != TokenType.Statement) { throw new Exception("Invalid instruction format."); } Block block = ParseBlock(blockToken.Tokens, mainContext); switch (statementToken.Content) { case "return": throw new Exception(); case "function": throw new Exception(); /*TokenList exprTokens = ((OperandToken)exprToken).Tokens; * FunctionDeclarationInstruction declaration = new FunctionDeclarationInstruction(); * TokenList nameTokens = ((ParenthesisGroupToken)exprTokens[1]).Tokens; * * // Ici on parcours les jetons de exprToken (qui doit être un OperandToken) * // afin de trouver les noms des arguments. * * // Liste contenant les noms des arguments * List<string> argsNamesLists = new List<string>(); * string funName = ((InfoToken)(exprTokens[0])).Content; * * // Indique si le prochain jeton doit être une virgule * bool needComa = false; * foreach (Token tok in nameTokens) * { * if (needComa && tok.Type != TokenType.Separator) * throw new Exception("Expected ',' token in function declaration"); * else * needComa = false; * // Si c'est un nom : * if (tok.Type == TokenType.OperandTokens && ((OperandToken)tok).Tokens.First().Type == TokenType.Noun) * { * argsNamesLists.Add(((InfoToken)((OperandToken)tok).Tokens.First()).Content); * needComa = true; * } * } * // Setup de la déclaration de fonction. * declaration.Function = new Function(); * declaration.Function.ArgumentNames = argsNamesLists; * declaration.Function.Body = block; * declaration.FunctionName = funName; * * return declaration;*/ break; case "while": IGettable expr = ParseExpression(exprToken, mainContext); block = ParseBlock(blockToken.Tokens, mainContext); WhileStatement statement = new WhileStatement(); statement.Block = block; statement.Condition = expr; return(statement); case "for": // Dans le cas d'une boucle for, expr est une opérande, contenant // une instruction, une expression, et une autre instruction. // (bizarre, certes) TokenList initializationInstruction = new TokenList(); TokenList stepInstruction = new TokenList(); TokenList conditionExpr = new TokenList(); int step = 0; foreach (Token tok in ((OperandToken)exprToken).Tokens) { if (tok.Type == TokenType.EndOfInstruction) { step++; } else { switch (step) { case 0: initializationInstruction.Add(tok); break; case 1: conditionExpr.Add(tok); break; case 2: stepInstruction.Add(tok); break; } } } // On vérifie qu'on ait bien le bon nombre. if (step != 2) { throw new Exception("Incorrect for statement."); } // On crée et on retourne le for. ForStatement forStatement = new ForStatement(); forStatement.Initialisation = ParseInstruction(initializationInstruction, mainContext); forStatement.Condition = ParseExpression(new OperandToken(conditionExpr), mainContext); forStatement.Update = ParseInstruction(stepInstruction, mainContext); forStatement.Block = block; return(forStatement); default: throw new NotImplementedException("Not implemented statement"); } }
/// <summary> /// Parse une intruction basique : appel de méthode ou affectation. /// La liste doit comprendre un seul jeton et pas de ; /// </summary> /// <returns></returns> static Instruction ParseInstruction(TokenList tokens, GlobalContext mainContext) { // Traitement spécial si return. if ((tokens.First().Type == TokenType.Statement)) { if (tokens.Count == 2) { InfoToken firstToken = (InfoToken)tokens[0]; switch (firstToken.Content) { case "return": ReturnInstruction returnInst = new ReturnInstruction(); returnInst.Expression = ParseExpression(tokens[1], mainContext); return(returnInst); case "patchkey": PatchkeyInstruction patchkeyInst = new PatchkeyInstruction(); patchkeyInst.Key = ((InfoToken)((OperandToken)tokens[1]).Tokens.First()).Content; return(patchkeyInst); default: throw new Exception("Unexpected instruction"); } } else if (tokens.Count == 1) { return(new ReturnInstruction()); } else { throw new Exception("Invalid 'return' expression"); } } if (tokens.Count != 1) { // TODO : traiter les using, etc... throw new Exception("Unexpected token list"); } Token token = tokens.First(); // On commence les deux types d'instructions habituels. if (token.Type == TokenType.ExpressionGroup) { // OperandGroup + ";" : c'est une affectation, s'il n'y a pas de signe 'égal' dedans, // bah c'est rien du tout. ExpressionGroupToken tok = (ExpressionGroupToken)token; AffectationInstruction ins = new AffectationInstruction(); List <string> validTokens = new List <string>() { "=", "+=", "-=", "/=", "*=" }; // Si on a pas de "=", on a une expression. if (!validTokens.Contains(((InfoToken)tok.Operator).Content)) { throw new Exception("An expression can't be used as instruction"); } ins.LeftMember = ParseSubExpression(((OperandToken)tok.Operand1).Tokens, mainContext); switch (((InfoToken)tok.Operator).Content) { case "=": ins.RightMember = ParseExpression(tok.Operand2, mainContext); break; case "+=": case "-=": case "/=": case "*=": string opString = ((InfoToken)tok.Operator).Content.Remove(1); Operator op = Operators.Mapping[opString]; ins.RightMember = new ExpressionGroup((IGettable)ins.LeftMember, op, ParseExpression(tok.Operand2, mainContext)); break; default: throw new Exception(); } //ParseOperand((OperandToken)tok.Operand2); return(ins); } else if (token.Type == TokenType.OperandTokens) { TokenList internalTokens = ((TokenContainer)token).Tokens; Token first = internalTokens.First(); // Instructions pré-faites. if (first.Type == TokenType.Noun) { InfoToken itoken = (InfoToken)first; switch (itoken.Content) { case "using": if (internalTokens.Count != 2 || internalTokens[1].Type != TokenType.String) { throw new Exception("Invalid using instruction"); } InfoToken itoken2 = (InfoToken)internalTokens[1]; mainContext.LoadedNamespaces.Add(itoken2.Content); return(new UseNamespaceInstruction(itoken2.Content)); case "include": if (internalTokens.Count != 2 || internalTokens[1].Type != TokenType.String) { throw new Exception("Invalid using instruction"); } InfoToken itoken3 = (InfoToken)internalTokens[1]; mainContext.LoadedAssemblies.Add(itoken3.Content, Assembly.LoadWithPartialName(itoken3.Content)); return(new LoadAssemblyInstruction(itoken3.Content)); } } // C'est un appel de méthode. return(new MethodCallInstruction(ParseSubExpression(internalTokens, mainContext))); } throw new Exception("Uncorrect instruction"); }
/// <summary> /// Parse une partie de sous expression. /// Ex : machin(haha, truc), 1, truc... /// TODO : support indexation et génériques. /// </summary> /// <param name="tokens"> /// Les jetons acceptés : /// - Une liste d'un jeton pouvant être : Number, String => Constante /// OperandTokens, OperandOperatorGroup, ParenthesisGroup. => rééval des jetons sous-jacents. /// - Une liste de jetons commençant par new, avec un Noun et un ParenthesisGroup /// - Une liste d'un jeton Noun. (=> variable) /// - Une liste comprenant un jeton Noun et un jeton ParenthesisGroup (=> appel de méthode). /// </param> /// <param name="part"></param> /// <returns></returns> static SubExpressionPart ParseSubExpressionPart(TokenList tokens, GlobalContext mainContext) { // Là on a le choix : string, nombre. if (tokens.Count == 1 && tokens.First().Type != TokenType.Noun) { Token first = tokens.First(); if (first.Type == TokenType.Number) { InfoToken token = (InfoToken)first; return(new SubExpressionPart(token.Content, SubExpressionPart.ExpTypes.ConstantObject)); } else if (first.Type == TokenType.String) { InfoToken token = (InfoToken)first; return(new SubExpressionPart(token.Content, SubExpressionPart.ExpTypes.ConstantObject)); } else if (first.Type == TokenType.Bool) { InfoToken token = (InfoToken)first; return(new SubExpressionPart(token.Content, SubExpressionPart.ExpTypes.ConstantObject)); } else if (first.Type == TokenType.OperandTokens) { return(ParseSubExpressionPart(((OperandToken)first).Tokens, mainContext)); } else if (first.Type == TokenType.ExpressionGroup) { return(new SubExpressionPart(ParseExpressionGroup((ExpressionGroupToken)first, mainContext))); } else if (first.Type == TokenType.ParenthesisGroup) { return(ParseSubExpressionPart(((ParenthesisGroupToken)first).Tokens, mainContext)); } else { throw new Exception("Invalid token in operand-token"); } } else if (tokens.First().Type == TokenType.New) { // Création d'une instance. SubExpressionPart part = new SubExpressionPart("", SubExpressionPart.ExpTypes.NewObject); // Contiendra le nom du type, et donc le nom de la SubExpressionPart. StringBuilder typename = new StringBuilder(); // Etape 1 : on parse tous les jetons constituant le nom du type. int index = 1; foreach (Token token in tokens) { if (token == tokens.First()) { continue; } if (token.Type == TokenType.Noun) { InfoToken info = (InfoToken)token; typename.Append(info.Content); } else if (token.Type == TokenType.Dot) { typename.Append('.'); } else if (token.Type == TokenType.ParenthesisGroup || token.Type == TokenType.IndexingParametersGroup || token.Type == TokenType.GenericParametersGroup) { // On arrête si on tombe sur un groupe de parenthèses ou autre. break; } else { throw new Exception("Unexpected token in new expression : " + token.ToString()); } index++; } part.Name = typename.ToString(); // Etape 2 : on ajoute tous les groupes d'arguments. List <IGettable> arguments = new List <IGettable>(); List <List <IGettable> > indexingArguments = new List <List <IGettable> >(); List <IGettable> genericArguments = new List <IGettable>(); for (int i = index; i < tokens.Count; i++) { Token tok = tokens[i]; switch (tok.Type) { case TokenType.ParenthesisGroup: arguments = ParseArgExpression(((TokenContainer)tok).Tokens, mainContext); break; case TokenType.GenericParametersGroup: genericArguments = ParseArgExpression(((TokenContainer)tok).Tokens, mainContext); break; case TokenType.IndexingParametersGroup: indexingArguments.Add(ParseArgExpression(((TokenContainer)tok).Tokens, mainContext)); break; default: throw new Exception("Unexpected token in new expression : " + tok.ToString()); } } part.IndexingParameters = indexingArguments; part.GenericParameters = genericArguments; part.Parameters = arguments; return(part); } else { // Variable, méthode. // Si parenthèses : méthode et extraire params, sinon variable ou nom de type. if (tokens.First().Type != TokenType.Noun) { throw new Exception("Invalid token type in SubExpressionPart"); } // Nom du membre. string memberName = ((InfoToken)tokens.First()).Content; bool hasArguments = false; List <IGettable> arguments = new List <IGettable>(); List <List <IGettable> > indexingArguments = new List <List <IGettable> >(); List <IGettable> genericArguments = new List <IGettable>(); bool first = true; foreach (Token token in tokens) { if (first) { first = false; continue; } switch (token.Type) { case TokenType.ParenthesisGroup: hasArguments = true; arguments = ParseArgExpression(((TokenContainer)token).Tokens, mainContext); break; case TokenType.GenericParametersGroup: genericArguments = ParseArgExpression(((TokenContainer)token).Tokens, mainContext); break; case TokenType.IndexingParametersGroup: indexingArguments.Add(ParseArgExpression(((TokenContainer)token).Tokens, mainContext)); break; default: throw new Exception("Unexpected token in sub expression"); } } // Type de la sous expression. SubExpressionPart.ExpTypes subExprPartType = hasArguments ? SubExpressionPart.ExpTypes.Method : SubExpressionPart.ExpTypes.Variable; SubExpressionPart part = new SubExpressionPart(memberName, subExprPartType); part.Parameters = arguments; part.IndexingParameters = indexingArguments; part.GenericParameters = genericArguments; return(part); } throw new Exception(); }