/// <summary> /// Called when a define token is hit /// </summary> /// <remarks></remarks> private void ProcessPoundDefine(PreprocessorLine line) { // Get the non whitespace tokens List <Token> list = line.GetValidTokens(); ThrowIfFalse(list[0].TokenType == TokenType.PoundDefine); Macro macro = null; if (list.Count == 3 && list[1].TokenType == TokenType.Word) { string name = list[1].Value; macro = new Macro(name, list[2].Value); } else if (list.Count == 2 && list[1].TokenType == TokenType.Word) { string name = list[1].Value; macro = new Macro(name, string.Empty); } else if (list.Count == 1) { _scanner.AddWarning("Encountered an empty #define"); } else if (list.Count > 3 && list[1].TokenType == TokenType.Word && list[2].TokenType == TokenType.ParenOpen) { macro = ProcessPoundDefineMethod(line); } else { macro = ProcessPoundDefineComplexMacro(line); } if (macro != null) { Macro oldMacro = null; if (_macroMap.TryGetValue(macro.Name, out oldMacro) && oldMacro.IsPermanent) { TraceToStream("Kept: {0} -> {1} Attempted Value {2}", oldMacro.Name, oldMacro.Value, macro.Value); } else { _macroMap[macro.Name] = macro; if (macro.IsMethod) { MethodMacro method = (MethodMacro)macro; TraceToStream("Defined: {0} -> {1}", macro.Name, method.MethodSignature); } else { TraceToStream("Defined: {0} -> {1}", macro.Name, macro.Value); } } } }
/// <summary> /// Called for a #undef line /// </summary> /// <param name="line"></param> /// <remarks></remarks> private void ProcessPoundUndefine(PreprocessorLine line) { // Get the none whitespace tokens List <Token> list = line.GetValidTokens(); ThrowIfFalse(list[0].TokenType == TokenType.PoundUnDef); if (list.Count != 2 || list[1].TokenType != TokenType.Word) { _scanner.AddWarning("Error processing #undef"); } else { string name = list[1].Value; if (_macroMap.ContainsKey(name)) { _macroMap.Remove(name); TraceToStream("Undefined: {0}", name); } } }
private void CollapseExpandedPreprocessorLines(ref PreprocessorLine line) { if (line.FirstValidToken != null && line.FirstValidToken.TokenType == TokenType.Pound) { List <Token> list = line.GetValidTokens(); Token possibleToken = list[1]; Token poundToken = null; if (list.Count >= 2 && TokenHelper.TryConvertToPoundToken(possibleToken.Value, out poundToken)) { // Strip out everything # -> define List <Token> newList = new List <Token>(line.TokenList); bool done = false; while (!done) { if (newList.Count == 0) { Debug.Fail("Non-crititcal error reducing the preprocessor line"); return; } else if (object.ReferenceEquals(newList[0], possibleToken)) { newList.RemoveAt(0); newList.Insert(0, poundToken); done = true; } else { newList.RemoveAt(0); } } PreprocessorLine formattedLine = new PreprocessorLine(); formattedLine.FirstValidToken = poundToken; formattedLine.IsPreProcessorDirectiveLine = true; formattedLine.TokenList = newList; line = formattedLine; } } }
/// <summary> /// Evaluate a preprocessor conditional statement and return whether or not it is true /// </summary> /// <param name="line"></param> /// <returns></returns> /// <remarks></remarks> private bool EvalauteConditional(PreprocessorLine line) { List <Token> list = line.GetValidTokens(); // Remove the #pound token. We don't care what type of conditional this is, this just serves // to evaluate it and let the caller interpret the result list.RemoveAt(0); // Make sure that all "defined" expressions wrap the next value in () Int32 i = 0; while (i + 1 < list.Count) { Token cur = list[i]; if (cur.TokenType == TokenType.Word && 0 == string.CompareOrdinal("defined", cur.Value) && list[i + 1].TokenType == TokenType.Word) { list.Insert(i + 1, new Token(TokenType.ParenOpen, "(")); list.Insert(i + 3, new Token(TokenType.ParenClose, ")")); i += 3; } i += 1; } ExpressionValue value = null; if (!_eval.TryEvaluate(list, out value)) { _errorProvider.AddError("Could not evaluate expression {0}", line.ToString()); return(false); } return(value.ConvertToBool()); }
/// <summary> /// Process a #include line. These take typically two forms /// #include "foo.h" /// #include >foo.h> /// /// </summary> /// <param name="line"></param> /// <remarks></remarks> private void ProcessPoundInclude(PreprocessorLine line) { if (!_options.FollowIncludes) { return; } // if the user did a <> include then there won't be any quotes around the string // so go ahead and redo the include to look like a "filename.h" include List <Token> list = new List <Token>(line.GetValidTokens()); // Get rid of the #include ThrowIfFalse(list[1].TokenType == TokenType.PoundInclude); list.RemoveAt(0); string name = null; if (list[1].TokenType == TokenType.OpLessThan) { name = string.Empty; list.RemoveAt(0); while (list[1].TokenType != TokenType.OpGreaterThan) { name += list[1].Value; list.RemoveAt(0); } list.RemoveAt(0); } else if (list[1].IsQuotedString) { name = TokenHelper.ConvertToString(list[1]); } else { name = null; } if (name == null) { _scanner.AddWarning("Invalid #include statement"); return; } // Now actually try and find the file. First check the custom list bool found = false; if (File.Exists(name)) { found = true; TraceToStream("include {0} followed -> {0}", name); TraceToStream("include {0} start", name); using (StreamReader reader = new StreamReader(name)) { ProcessCore(new TextReaderBag(name, reader)); } TraceToStream("include {0} end", name); } else if (_options.IncludePathList.Count > 0) { // Search through the path list found = false; foreach (string prefix in _options.IncludePathList) { string fullPath = Path.Combine(prefix, name); if (File.Exists(fullPath)) { found = true; TraceToStream("include {0} followed -> {1}", name, fullPath); TraceToStream("include {0} start", name); using (StreamReader reader = new StreamReader(fullPath)) { ProcessCore(new TextReaderBag(fullPath, reader)); } TraceToStream("include {0} end", name); break; } } } else { found = false; } if (!found) { _scanner.AddWarning("Could not locate include file {0}", name); TraceToStream("include {0} not followed", name); } }
/// <summary> /// Process a #define that is actually a function /// </summary> /// <param name="line"></param> /// <returns></returns> /// <remarks></remarks> private Macro ProcessPoundDefineMethod(PreprocessorLine line) { // First step is to parse out the name and parameters List <Token> list = line.GetValidTokens(); string name = list[1].Value; list.RemoveRange(0, 3); List <string> paramList = new List <string>(); while ((list[0].TokenType != TokenType.ParenClose)) { if (list[0].TokenType == TokenType.Word) { paramList.Add(list[0].Value); } else if (list[0].TokenType == TokenType.ParenOpen) { // ( is not legal inside a parameter list. This is a simple macro return(ProcessPoundDefineComplexMacro(line)); } list.RemoveAt(0); } // Now get the fullBody. We need the actual text for the fullBody so search through the true token list Int32 index = 0; while ((line.TokenList[index].TokenType != TokenType.ParenClose)) { index += 1; } index += 1; List <Token> fullBody = line.TokenList.GetRange(index, line.TokenList.Count - index); // Strip the trailing and ending whitespace on the fullBody while (fullBody.Count > 0 && (fullBody[0].TokenType == TokenType.WhiteSpace || fullBody[0].TokenType == TokenType.NewLine)) { fullBody.RemoveAt(0); } // Don't be fooled by a simple #define that simply wraps the entire fullBody inside a // set of (). if ((fullBody.Count == 0)) { return(ProcessPoundDefineComplexMacro(line)); } while (fullBody.Count > 0 && (fullBody[fullBody.Count - 1].TokenType == TokenType.WhiteSpace || fullBody[fullBody.Count - 1].TokenType == TokenType.NewLine)) { fullBody.RemoveAt(fullBody.Count - 1); } // Coy the body token list since we are about to change the data List <Token> body = new List <Token>(fullBody); // Collapse the whitespace around ## entries int i = 0; while (i + 1 < body.Count) { Token left = body[i]; Token right = body[i + 1]; if (left.TokenType == TokenType.Pound && right.TokenType == TokenType.Pound) { // First look at the right if (i + 2 < body.Count && body[i + 2].TokenType == TokenType.WhiteSpace) { body.RemoveAt(i + 2); } // Now look at the left if (i > 0 && body[i - 1].TokenType == TokenType.WhiteSpace) { body.RemoveAt(i - 1); } } i += 1; } index += 1; return(new MethodMacro(name, paramList, body, fullBody)); }