/// <summary> /// RTCOPファイルのプリプロセッサ /// </summary> /// <param name="macros">レイヤコンパイラに与えるマクロ定義</param> /// <param name="includePaths">ヘッダファイルのディレクトリパス</param> /// <param name="encoding">入出力ファイルの文字コード</param> public RTCOPPreprocessor(IEnumerable <string> macros, List <string> includePaths, Encoding encoding) { DefinedMacros = new List <PreprocessDirective>(macros.Count()); foreach (var macro in macros) { var item = new PreprocessDirective(DirectiveKind.Define, macro); DefinedMacros.Add(item); } IncludePaths = includePaths; Encoding = encoding; }
/// <summary> /// プリプロセス実行 /// </summary> /// <param name="inputFile">入力ファイル</param> /// <param name="macros">ファイル内で定義されたマクロ</param> /// <returns>実行後のファイル</returns> private RTCOPSourceFile Run_Private(RTCOPSourceFile inputFile, List <PreprocessDirective> macros) { // ファイルから全てのディレクティブ、行を取得 var allObjs = PreprocessParser.IfSection.Or(PreprocessParser.DirectiveOrLineForIfSection).TokenWithSkipCommentForPreprocessParser().Many().Parse(inputFile.Text); var importingFiles = new List <RTCOPSourceFile>(); // 実際のプリプロセス処理を行う Func <IEnumerable <object>, IEnumerable <object> > runPreprocess = null; runPreprocess = (objs) => { var newObjs = new List <object>(); foreach (object obj in objs) { // ifセクション if (obj is IfSection) { var ifSection = (IfSection)obj; foreach (var group in ifSection.Groups) { // 条件が合えば追加 bool isMatch = EvaluateIfDirectiveExpression(group.First() as PreprocessDirective, macros); if (isMatch) { var additionalGroup = runPreprocess(group.Skip(1)); newObjs.AddRange(additionalGroup); break; } } } // ディレクティブ else if (obj is PreprocessDirective) { var directive = (PreprocessDirective)obj; switch (directive.Kind) { case DirectiveKind.Define: macros.Add(directive); //newObjs.Add(directive); break; case DirectiveKind.Undef: var removingMacro = macros.Find((macro) => macro.Param1 == directive.Param1); if (removingMacro != null) { macros.Remove(removingMacro); } //newObjs.Add(directive); break; case DirectiveKind.ImportLayerHeader: case DirectiveKind.ImportBaseClassHeader: RTCOPSourceFile importingFile = null; foreach (var incpath in IncludePaths) { string path = incpath + directive.Param1; string fullpath = Path.GetFullPath(path); if (File.Exists(fullpath)) { if (_ImportedFileNames.Contains(fullpath)) { // 同じパスのファイルは二度読み込まないようにする goto EndImport; } else { _ImportedFileNames.Add(fullpath); } // ファイルオープン RTCOPSourceFile src = null; using (StreamReader sr = new StreamReader(fullpath, Encoding)) { string text = sr.ReadToEnd(); src = new RTCOPSourceFile(fullpath, text); } importingFile = Run_Private(src, macros); break; } } if (importingFile == null) { throw new Exception("ファイル: " + directive.Param1 + "が見つかりません"); } importingFiles.Add(importingFile); if (directive.Kind == DirectiveKind.ImportBaseClassHeader) { // includeに変換して追加 PreprocessDirective incdirective = new PreprocessDirective(DirectiveKind.Include, directive.Param1); newObjs.Add(incdirective); } else { newObjs.Add(directive); } EndImport: break; default: newObjs.Add(directive); break; } } // 通常の行 else { // マクロ展開して追加 var line = (NonDirectiveLine)obj; var newLine = ExpandMacro(line.Contents, macros, false); newObjs.Add(newLine); } } return(newObjs); }; // 実行開始 var result = runPreprocess(allObjs); // 結果を返す StringBuilder sb = new StringBuilder(); foreach (object obj in result) { sb.Append(obj.ToString()); sb.Append("\r\n"); } RTCOPSourceFile outputFile = new RTCOPSourceFile(inputFile.FilePath, sb.ToString(), importingFiles); return(outputFile); }
/// <summary> /// ifディレクティブの評価 /// </summary> /// <param name="directive">ディレクティブ</param> /// <param name="macros">defineマクロ</param> /// <returns>条件を満たしているかどうか</returns> private bool EvaluateIfDirectiveExpression(PreprocessDirective directive, List <PreprocessDirective> macros) { bool result = false; switch (directive.Kind) { case DirectiveKind.If: case DirectiveKind.Elif: // 式のトークン取得 var tokens = new List <Token>(TokenParser.RTCOPToken.TokenWithSkipComment().Many().Parse(directive.Param1)); // definedを置き換える var newTokens = new List <Token>(); for (int i = 0; i < tokens.Count; ++i) { var token = tokens[i]; if (token.ToString() == "defined") { if ((tokens[i + 1].ToString() == "(") && (tokens[i + 2] is Identifier) && (tokens[i + 3].ToString() == ")")) { IntegerLiteral il = null; if (macros.Exists((macro) => macro.Param1 == tokens[i + 2].ToString())) { il = new IntegerLiteral("1", "1", "", ""); } else { il = new IntegerLiteral("0", "0", "", ""); } newTokens.Add(il); i += 3; } else if (tokens[i + 1] is Identifier) { IntegerLiteral il = null; if (macros.Exists((macro) => macro.Param1 == tokens[i + 1].ToString())) { il = new IntegerLiteral("1", "1", "", ""); } else { il = new IntegerLiteral("0", "0", "", ""); } newTokens.Add(il); i += 1; } else { throw new Exception("definedの書き方が間違っています"); } } else { newTokens.Add(token); } } // マクロ展開 var newLine = ExpandMacro(newTokens, macros, true); // 式を評価 IfDirectiveExpression expression = PreprocessParser.IfDirectiveExpression.TokenWithSkipComment().Parse(newLine.ToString()); Literal exResult = expression.Evaluate(); if (exResult is IntegerLiteral) { result = ((IntegerLiteral)exResult).Value != 0; } else if (exResult is CharacterLiteral) { result = ((CharacterLiteral)exResult).Value != 0; } else if (exResult is BooleanLiteral) { result = ((BooleanLiteral)exResult).Value; } else if (exResult is PointerLiteral) { result = false; } else { result = ((FloatingLiteral)exResult).Value != 0; } break; case DirectiveKind.Ifdef: result = macros.Exists((macro) => macro.Param1 == directive.Param1); break; case DirectiveKind.Ifndef: result = !macros.Exists((macro) => macro.Param1 == directive.Param1); break; case DirectiveKind.Else: result = true; break; default: break; } return(result); }