11543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner//===--- TokenLexer.h - Lex from a token buffer -----------------*- C++ -*-===// 25f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 35f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// The LLVM Compiler Infrastructure 45f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 50bc735ffcfb223c0186419547abaa5c84482663eChris Lattner// This file is distributed under the University of Illinois Open Source 60bc735ffcfb223c0186419547abaa5c84482663eChris Lattner// License. See LICENSE.TXT for details. 75f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 85f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===----------------------------------------------------------------------===// 95f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 10e5c8ffe09f8fec836b90f23c2a3b996bfc5b09c4Chris Lattner// This file defines the TokenLexer interface. 115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===----------------------------------------------------------------------===// 135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 14176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines#ifndef LLVM_CLANG_LEX_TOKENLEXER_H 15176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines#define LLVM_CLANG_LEX_TOKENLEXER_H 165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#include "clang/Basic/SourceLocation.h" 185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencernamespace clang { 205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer class MacroInfo; 215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer class Preprocessor; 22d217773f106856a11879ec79dc468efefaf2ee75Chris Lattner class Token; 23e5c8ffe09f8fec836b90f23c2a3b996bfc5b09c4Chris Lattner class MacroArgs; 241eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 25c30981a563a8947cb26b1e308d122fa2ef90fcebRichard Smith/// TokenLexer - This implements a lexer that returns tokens from a macro body 265d75de0f821023f4ed4815825bf3aea8a0b5e40dChris Lattner/// or token stream instead of lexing from a character buffer. This is used for 275d75de0f821023f4ed4815825bf3aea8a0b5e40dChris Lattner/// macro expansion and _Pragma handling, for example. 285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/// 291543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattnerclass TokenLexer { 305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// Macro - The macro we are expanding from. This is null if expanding a 315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// token stream. 325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer MacroInfo *Macro; 345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// ActualArgs - The actual arguments specified for a function-like macro, or 361543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner /// null. The TokenLexer owns the pointed-to object. 375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer MacroArgs *ActualArgs; 385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// PP - The current preprocessor object we are expanding for. 405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Preprocessor &PP; 425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 438d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner /// Tokens - This is the pointer to an array of tokens that the macro is 445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// defined to, with arguments expanded for function-like macros. If this is 4532fca722dd974b8202d0fb9c71b6c185c0767da6Chris Lattner /// a token stream, these are the tokens we are returning. This points into 465b3284a9275a27f5c4410e25eb8933be540601d6Argyrios Kyrtzidis /// the macro definition we are lexing from, a cache buffer that is owned by 475b3284a9275a27f5c4410e25eb8933be540601d6Argyrios Kyrtzidis /// the preprocessor, or some other buffer that we may or may not own 485b3284a9275a27f5c4410e25eb8933be540601d6Argyrios Kyrtzidis /// (depending on OwnsTokens). 495b3284a9275a27f5c4410e25eb8933be540601d6Argyrios Kyrtzidis /// Note that if it points into Preprocessor's cache buffer, the Preprocessor 505b3284a9275a27f5c4410e25eb8933be540601d6Argyrios Kyrtzidis /// may update the pointer as needed. 518d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner const Token *Tokens; 525b3284a9275a27f5c4410e25eb8933be540601d6Argyrios Kyrtzidis friend class Preprocessor; 531eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 548d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner /// NumTokens - This is the length of the Tokens array. 555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 568d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner unsigned NumTokens; 571eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// CurToken - This is the next token that Lex will return. 595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned CurToken; 611eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 62d0f5e443ceed37e1a72015bd109db8e5fc6206cfChandler Carruth /// ExpandLocStart/End - The source location range where this macro was 63d0f5e443ceed37e1a72015bd109db8e5fc6206cfChandler Carruth /// expanded. 64d0f5e443ceed37e1a72015bd109db8e5fc6206cfChandler Carruth SourceLocation ExpandLocStart, ExpandLocEnd; 651eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 66b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis /// \brief Source location pointing at the source location entry chunk that 67d0f5e443ceed37e1a72015bd109db8e5fc6206cfChandler Carruth /// was reserved for the current macro expansion. 68b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis SourceLocation MacroExpansionStart; 69b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis 70d0f5e443ceed37e1a72015bd109db8e5fc6206cfChandler Carruth /// \brief The offset of the macro expansion in the 71b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis /// "source location address space". 72b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis unsigned MacroStartSLocOffset; 73b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis 74499ea5550d6e2fc5cfbd33b47f06d92ce25d7a13Argyrios Kyrtzidis /// \brief Location of the macro definition. 75499ea5550d6e2fc5cfbd33b47f06d92ce25d7a13Argyrios Kyrtzidis SourceLocation MacroDefStart; 76499ea5550d6e2fc5cfbd33b47f06d92ce25d7a13Argyrios Kyrtzidis /// \brief Length of the macro definition. 77499ea5550d6e2fc5cfbd33b47f06d92ce25d7a13Argyrios Kyrtzidis unsigned MacroDefLength; 78b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis 795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// Lexical information about the expansion point of the macro: the identifier 805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// that the macro expanded from had these properties. 819c683062752a26014197df1c8792a19efa9e93cfChris Lattner bool AtStartOfLine : 1; 829c683062752a26014197df1c8792a19efa9e93cfChris Lattner bool HasLeadingSpace : 1; 831eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 84651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // NextTokGetsSpace - When this is true, the next token appended to the 85651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // output list during function argument expansion will get a leading space, 86651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // regardless of whether it had one to begin with or not. This is used for 87651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // placemarker support. If still true after function argument expansion, the 88651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // leading space will be applied to the first token following the macro 89651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // expansion. 90651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool NextTokGetsSpace : 1; 91651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 921543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner /// OwnsTokens - This is true if this TokenLexer allocated the Tokens 938d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner /// array, and thus needs to free it when destroyed. For simple object-like 948d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner /// macros (for example) we just point into the token buffer of the macro 958d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner /// definition, we don't make a copy of it. 968d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner bool OwnsTokens : 1; 971eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 986b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner /// DisableMacroExpansion - This is true when tokens lexed from the TokenLexer 996b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner /// should not be subject to further macro expansion. 1006b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner bool DisableMacroExpansion : 1; 1011eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1020e2c34f92f00628d48968dfea096d36381f494cbStephen Hines TokenLexer(const TokenLexer &) = delete; 1030e2c34f92f00628d48968dfea096d36381f494cbStephen Hines void operator=(const TokenLexer &) = delete; 1045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerpublic: 1051543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner /// Create a TokenLexer for the specified macro with the specified actual 1065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// arguments. Note that this ctor takes ownership of the ActualArgs pointer. 107e7fb48466afcbf2c4ccdfa658824282fdc3c512cChris Lattner /// ILEnd specifies the location of the ')' for a function-like macro or the 108e7fb48466afcbf2c4ccdfa658824282fdc3c512cChris Lattner /// identifier for an object-like macro. 109c30981a563a8947cb26b1e308d122fa2ef90fcebRichard Smith TokenLexer(Token &Tok, SourceLocation ILEnd, MacroInfo *MI, 110c30981a563a8947cb26b1e308d122fa2ef90fcebRichard Smith MacroArgs *ActualArgs, Preprocessor &pp) 1116bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines : Macro(nullptr), ActualArgs(nullptr), PP(pp), OwnsTokens(false) { 112c30981a563a8947cb26b1e308d122fa2ef90fcebRichard Smith Init(Tok, ILEnd, MI, ActualArgs); 1139594acf32de2939b15eafa8fe818607bfc56bf66Chris Lattner } 1141eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1151543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner /// Init - Initialize this TokenLexer to expand from the specified macro 1169594acf32de2939b15eafa8fe818607bfc56bf66Chris Lattner /// with the specified argument information. Note that this ctor takes 117e7fb48466afcbf2c4ccdfa658824282fdc3c512cChris Lattner /// ownership of the ActualArgs pointer. ILEnd specifies the location of the 118e7fb48466afcbf2c4ccdfa658824282fdc3c512cChris Lattner /// ')' for a function-like macro or the identifier for an object-like macro. 119c30981a563a8947cb26b1e308d122fa2ef90fcebRichard Smith void Init(Token &Tok, SourceLocation ILEnd, MacroInfo *MI, 120c30981a563a8947cb26b1e308d122fa2ef90fcebRichard Smith MacroArgs *ActualArgs); 1211eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 12221ec0e4c5db85e45b59cafabca66d640e0ee2dccChris Lattner /// Create a TokenLexer for the specified token stream. If 'OwnsTokens' is 12321ec0e4c5db85e45b59cafabca66d640e0ee2dccChris Lattner /// specified, this takes ownership of the tokens and delete[]'s them when 12421ec0e4c5db85e45b59cafabca66d640e0ee2dccChris Lattner /// the token lexer is empty. 1256b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner TokenLexer(const Token *TokArray, unsigned NumToks, bool DisableExpansion, 12621ec0e4c5db85e45b59cafabca66d640e0ee2dccChris Lattner bool ownsTokens, Preprocessor &pp) 1276bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines : Macro(nullptr), ActualArgs(nullptr), PP(pp), OwnsTokens(false) { 12821ec0e4c5db85e45b59cafabca66d640e0ee2dccChris Lattner Init(TokArray, NumToks, DisableExpansion, ownsTokens); 1299594acf32de2939b15eafa8fe818607bfc56bf66Chris Lattner } 1301eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1311543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner /// Init - Initialize this TokenLexer with the specified token stream. 1329594acf32de2939b15eafa8fe818607bfc56bf66Chris Lattner /// This does not take ownership of the specified token vector. 1336b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner /// 1341eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump /// DisableExpansion is true when macro expansion of tokens lexed from this 1356b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner /// stream should be disabled. 1366b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner void Init(const Token *TokArray, unsigned NumToks, 1376b884508c3bc97cc9df9516adb92fbf88dd0a2e4Chris Lattner bool DisableMacroExpansion, bool OwnsTokens); 1381eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1391543e9c69202b8e128c7fe34784ae7aa90964889Chris Lattner ~TokenLexer() { destroy(); } 1401eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// isNextTokenLParen - If the next token lexed will pop this macro off the 1425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// expansion stack, return 2. If the next unexpanded token is a '(', return 1435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// 1, otherwise return 0. 1445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer unsigned isNextTokenLParen() const; 1451eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// Lex - Lex and return a token from this macro stream. 147d2f9308220af22bfc1bcd3bc2cad118dbd8be013Eli Friedman bool Lex(Token &Tok); 1481eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 149b2eb53d9fd973a1a02e05e67a3307b3efd12eff2Peter Collingbourne /// isParsingPreprocessorDirective - Return true if we are in the middle of a 150b2eb53d9fd973a1a02e05e67a3307b3efd12eff2Peter Collingbourne /// preprocessor directive. 151b2eb53d9fd973a1a02e05e67a3307b3efd12eff2Peter Collingbourne bool isParsingPreprocessorDirective() const; 152b2eb53d9fd973a1a02e05e67a3307b3efd12eff2Peter Collingbourne 1535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerprivate: 1549594acf32de2939b15eafa8fe818607bfc56bf66Chris Lattner void destroy(); 1551eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// isAtEnd - Return true if the next lex call will pop this macro off the 1575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// include stack. 1585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer bool isAtEnd() const { 1598d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner return CurToken == NumTokens; 1605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 1611eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// PasteTokens - Tok is the LHS of a ## operator, and CurToken is the ## 1635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// operator. Read the ## and RHS, and paste the LHS/RHS together. If there 1645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// are is another ## after it, chomp it iteratively. Return the result as 1653f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner /// Tok. If this returns true, the caller should immediately return the 1663f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner /// token. 1673f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner bool PasteTokens(Token &Tok); 1681eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /// Expand the arguments of a function-like macro so that we can quickly 1708d896438031f5990cb42eb95a7bae233db2eb333Chris Lattner /// return preexpanded tokens from Tokens. 1715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer void ExpandFunctionArguments(); 1721eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1733f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner /// HandleMicrosoftCommentPaste - In microsoft compatibility mode, /##/ pastes 1743f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner /// together to form a comment that comments out everything in the current 1753f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner /// macro, other active macros, and anything left on the current physical 176d0f5e443ceed37e1a72015bd109db8e5fc6206cfChandler Carruth /// source line of the expanded buffer. Handle this by returning the 1773f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner /// first token on the next line. 1783f1cc838f9caf469990f23fccd0940263c0c61ffChris Lattner void HandleMicrosoftCommentPaste(Token &Tok); 179b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis 1801824d54df85a462ada812dadda18130f951d40f3Dmitri Gribenko /// \brief If \p loc is a FileID and points inside the current macro 181b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis /// definition, returns the appropriate source location pointing at the 182b73377eeb3eff76be134203aebb6068244b177f3Argyrios Kyrtzidis /// macro expansion source location entry. 183ec3b5389a5032433116ec2f2ee266b9666ad2eb4Argyrios Kyrtzidis SourceLocation getExpansionLocForMacroDefLoc(SourceLocation loc) const; 184d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis 185d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis /// \brief Creates SLocEntries and updates the locations of macro argument 186d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis /// tokens to their new expanded locations. 187d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis /// 188d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis /// \param ArgIdSpellLoc the location of the macro argument id inside the 189d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis /// macro definition. 190d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis void updateLocForMacroArgTokens(SourceLocation ArgIdSpellLoc, 191d60a34a4e514ec0dfddd05ef2744be104e111f45Argyrios Kyrtzidis Token *begin_tokens, Token *end_tokens); 192d2f9308220af22bfc1bcd3bc2cad118dbd8be013Eli Friedman 193651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines /// Remove comma ahead of __VA_ARGS__, if present, according to compiler 194651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines /// dialect settings. Returns true if the comma is removed. 195651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool MaybeRemoveCommaBeforeVaArgs(SmallVectorImpl<Token> &ResultToks, 196651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool HasPasteOperator, 197651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines MacroInfo *Macro, unsigned MacroArgNo, 198651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines Preprocessor &PP); 199651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 200d2f9308220af22bfc1bcd3bc2cad118dbd8be013Eli Friedman void PropagateLineStartLeadingSpaceInfo(Token &Result); 2015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer}; 2025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} // end namespace clang 2045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer#endif 206