Commit 7fc38ddd by alokp@chromium.org

Implemented macro expansion.

Review URL: https://codereview.appspot.com/6303052 git-svn-id: https://angleproject.googlecode.com/svn/trunk@1148 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 69ab2993
......@@ -33,6 +33,9 @@ class Diagnostics
UNEXPECTED_TOKEN,
MACRO_NAME_RESERVED,
MACRO_REDEFINED,
MACRO_UNTERMINATED_INVOCATION,
MACRO_TOO_FEW_ARGS,
MACRO_TOO_MANY_ARGS,
INVALID_EXTENSION_NAME,
INVALID_EXTENSION_BEHAVIOR,
INVALID_EXTENSION_DIRECTIVE,
......
......@@ -120,11 +120,6 @@ void DirectiveParser::parseDirective(Token* token)
parseLine(token);
}
if ((token->type != '\n') && (token->type != 0))
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
token->location,
token->value);
while (token->type != '\n')
{
if (token->type == 0) {
......@@ -172,7 +167,7 @@ void DirectiveParser::parseDefine(Token* token)
break;
macro.parameters.push_back(token->value);
mTokenizer->lex(token); // Get comma.
mTokenizer->lex(token); // Get ','.
} while (token->type == ',');
if (token->type != ')')
......@@ -182,6 +177,7 @@ void DirectiveParser::parseDefine(Token* token)
token->value);
return;
}
mTokenizer->lex(token); // Get ')'.
}
while ((token->type != '\n') && (token->type != Token::LAST))
......@@ -193,6 +189,12 @@ void DirectiveParser::parseDefine(Token* token)
macro.replacements.push_back(*token);
mTokenizer->lex(token);
}
if (!macro.replacements.empty())
{
// Whitespace preceding the replacement list is not considered part of
// the replacement list for either form of macro.
macro.replacements.front().setHasLeadingSpace(false);
}
// Check for macro redefinition.
MacroSet::const_iterator iter = mMacroSet->find(macro.name);
......
......@@ -23,13 +23,17 @@ struct Macro
kTypeObj,
kTypeFunc
};
typedef std::vector<std::string> Parameters;
typedef std::vector<Token> Replacements;
Macro() : disabled(false), type(kTypeObj) { }
bool equals(const Macro& other) const;
mutable bool disabled;
Type type;
std::string name;
std::vector<std::string> parameters;
std::vector<Token> replacements;
Parameters parameters;
Replacements replacements;
};
typedef std::map<std::string, Macro> MacroSet;
......
......@@ -6,9 +6,46 @@
#include "MacroExpander.h"
#include <algorithm>
#include <cassert>
#include "Diagnostics.h"
#include "Token.h"
namespace pp
{
class TokenLexer : public Lexer
{
public:
typedef std::vector<Token> TokenVector;
TokenLexer(TokenVector* tokens)
{
tokens->swap(mTokens);
mIter = mTokens.begin();
}
virtual void lex(Token* token)
{
if (mIter == mTokens.end())
{
token->reset();
token->type = Token::LAST;
}
else
{
*token = *mIter++;
}
}
private:
PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer);
TokenVector mTokens;
TokenVector::const_iterator mIter;
};
MacroExpander::MacroExpander(Lexer* lexer,
MacroSet* macroSet,
Diagnostics* diagnostics) :
......@@ -18,10 +55,292 @@ MacroExpander::MacroExpander(Lexer* lexer,
{
}
MacroExpander::~MacroExpander()
{
assert(!mReserveToken.get());
assert(mContextStack.empty());
}
void MacroExpander::lex(Token* token)
{
// TODO(alokp): Implement me.
mLexer->lex(token);
while (true)
{
getToken(token);
if (token->type != Token::IDENTIFIER)
break;
if (token->expansionDisabled())
break;
MacroSet::const_iterator iter = mMacroSet->find(token->value);
if (iter == mMacroSet->end())
break;
const Macro& macro = iter->second;
if (macro.disabled)
{
// If a particular token is not expanded, it is never expanded.
token->setExpansionDisabled(true);
break;
}
if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
{
// If the token immediately after the macro name is not a '(',
// this macro should not be expanded.
break;
}
pushMacro(macro, *token);
}
}
void MacroExpander::getToken(Token* token)
{
if (mReserveToken.get())
{
*token = *mReserveToken;
mReserveToken.reset();
return;
}
// First pop all empty macro contexts.
while (!mContextStack.empty() && mContextStack.back()->empty())
{
popMacro();
}
if (!mContextStack.empty())
{
*token = mContextStack.back()->get();
}
else
{
mLexer->lex(token);
}
}
void MacroExpander::ungetToken(const Token& token)
{
if (!mContextStack.empty())
{
MacroContext* context = mContextStack.back();
context->unget();
assert(context->index >= 0);
assert(context->replacements[context->index] == token);
}
else
{
assert(!mReserveToken.get());
mReserveToken.reset(new Token(token));
}
}
bool MacroExpander::isNextTokenLeftParen()
{
Token token;
getToken(&token);
bool lparen = token.type == '(';
ungetToken(token);
return lparen;
}
bool MacroExpander::pushMacro(const Macro& macro, const Token& identifier)
{
assert(!macro.disabled);
assert(!identifier.expansionDisabled());
assert(identifier.type == Token::IDENTIFIER);
assert(identifier.value == macro.name);
std::vector<Token> replacements;
if (!expandMacro(macro, identifier, &replacements))
return false;
// Macro is disabled for expansion until it is popped off the stack.
macro.disabled = true;
MacroContext* context = new MacroContext;
context->macro = &macro;
context->replacements.swap(replacements);
mContextStack.push_back(context);
return true;
}
void MacroExpander::popMacro()
{
assert(!mContextStack.empty());
MacroContext* context = mContextStack.back();
mContextStack.pop_back();
assert(context->empty());
assert(context->macro->disabled);
context->macro->disabled = false;
delete context;
}
bool MacroExpander::expandMacro(const Macro& macro,
const Token& identifier,
std::vector<Token>* replacements)
{
if (macro.type == Macro::kTypeObj)
{
replacements->assign(macro.replacements.begin(),
macro.replacements.end());
}
else
{
assert(macro.type == Macro::kTypeFunc);
std::vector<MacroArg> args;
args.reserve(macro.parameters.size());
if (!collectMacroArgs(macro, identifier, &args))
return false;
replaceMacroParams(macro, args, replacements);
}
for (size_t i = 0; i < replacements->size(); ++i)
{
Token& repl = replacements->at(i);
if (i == 0)
{
// The first token in the replacement list inherits the padding
// properties of the identifier token.
repl.setAtStartOfLine(identifier.atStartOfLine());
repl.setHasLeadingSpace(identifier.hasLeadingSpace());
}
repl.location = identifier.location;
}
return true;
}
bool MacroExpander::collectMacroArgs(const Macro& macro,
const Token& identifier,
std::vector<MacroArg>* args)
{
Token token;
getToken(&token);
assert(token.type == '(');
args->push_back(MacroArg());
for (int openParens = 1; openParens != 0; )
{
getToken(&token);
if (token.type == Token::LAST)
{
mDiagnostics->report(Diagnostics::MACRO_UNTERMINATED_INVOCATION,
identifier.location, identifier.value);
// Do not lose EOF token.
ungetToken(token);
return false;
}
bool isArg = false; // True if token is part of the current argument.
switch (token.type)
{
case '(':
++openParens;
isArg = true;
break;
case ')':
--openParens;
isArg = openParens != 0;
break;
case ',':
// The individual arguments are separated by comma tokens, but
// the comma tokens between matching inner parentheses do not
// seperate arguments.
if (openParens == 1) args->push_back(MacroArg());
isArg = openParens != 1;
break;
default:
isArg = true;
break;
}
if (isArg)
{
MacroArg& arg = args->back();
// Initial whitespace is not part of the argument.
if (arg.empty()) token.setHasLeadingSpace(false);
arg.push_back(token);
}
}
const Macro::Parameters& params = macro.parameters;
// If there is only one empty argument, it is equivalent to no argument.
if (params.empty() && (args->size() == 1) && args->front().empty())
{
args->clear();
}
// Validate the number of arguments.
if (args->size() != params.size())
{
Diagnostics::ID id = args->size() < macro.parameters.size() ?
Diagnostics::MACRO_TOO_FEW_ARGS :
Diagnostics::MACRO_TOO_MANY_ARGS;
mDiagnostics->report(id, identifier.location, identifier.value);
return false;
}
// Pre-expand each argument before substitution.
// This step expands each argument individually before they are
// inserted into the macro body.
for (size_t i = 0; i < args->size(); ++i)
{
MacroArg& arg = args->at(i);
TokenLexer lexer(&arg);
MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
arg.clear();
expander.lex(&token);
while (token.type != Token::LAST)
{
arg.push_back(token);
expander.lex(&token);
}
}
return true;
}
void MacroExpander::replaceMacroParams(const Macro& macro,
const std::vector<MacroArg>& args,
std::vector<Token>* replacements)
{
for (size_t i = 0; i < macro.replacements.size(); ++i)
{
const Token& repl = macro.replacements[i];
if (repl.type != Token::IDENTIFIER)
{
replacements->push_back(repl);
continue;
}
// TODO(alokp): Optimize this.
// There is no need to search for macro params every time.
// The param index can be cached with the replacement token.
Macro::Parameters::const_iterator iter = std::find(
macro.parameters.begin(), macro.parameters.end(), repl.value);
if (iter == macro.parameters.end())
{
replacements->push_back(repl);
continue;
}
size_t iArg = std::distance(macro.parameters.begin(), iter);
const MacroArg& arg = args[iArg];
if (arg.empty())
{
continue;
}
size_t iRepl = replacements->size();
replacements->insert(replacements->end(), arg.begin(), arg.end());
// The replacement token inherits padding properties from
// macro replacement token.
replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
}
}
} // namespace pp
......
......@@ -7,6 +7,8 @@
#ifndef COMPILER_PREPROCESSOR_MACRO_EXPANDER_H_
#define COMPILER_PREPROCESSOR_MACRO_EXPANDER_H_
#include <vector>
#include "Lexer.h"
#include "Macro.h"
#include "pp_utils.h"
......@@ -20,15 +22,50 @@ class MacroExpander : public Lexer
{
public:
MacroExpander(Lexer* lexer, MacroSet* macroSet, Diagnostics* diagnostics);
virtual ~MacroExpander();
virtual void lex(Token* token);
private:
PP_DISALLOW_COPY_AND_ASSIGN(MacroExpander);
void getToken(Token* token);
void ungetToken(const Token& token);
bool isNextTokenLeftParen();
bool pushMacro(const Macro& macro, const Token& identifier);
void popMacro();
bool expandMacro(const Macro& macro,
const Token& identifier,
std::vector<Token>* replacements);
typedef std::vector<Token> MacroArg;
bool collectMacroArgs(const Macro& macro,
const Token& identifier,
std::vector<MacroArg>* args);
void replaceMacroParams(const Macro& macro,
const std::vector<MacroArg>& args,
std::vector<Token>* replacements);
struct MacroContext
{
const Macro* macro;
size_t index;
std::vector<Token> replacements;
MacroContext() : macro(0), index(0) { }
bool empty() const { return index == replacements.size(); }
const Token& get() { return replacements[index++]; }
void unget() { --index; }
};
Lexer* mLexer;
MacroSet* mMacroSet;
Diagnostics* mDiagnostics;
std::auto_ptr<Token> mReserveToken;
std::vector<MacroContext*> mContextStack;
};
} // namespace pp
......
......@@ -41,6 +41,14 @@ void Token::setHasLeadingSpace(bool space)
flags &= ~HAS_LEADING_SPACE;
}
void Token::setExpansionDisabled(bool disable)
{
if (disable)
flags |= EXPANSION_DISABLED;
else
flags &= ~EXPANSION_DISABLED;
}
std::ostream& operator<<(std::ostream& out, const Token& token)
{
if (token.hasLeadingSpace())
......
......@@ -50,8 +50,9 @@ struct Token
};
enum Flags
{
AT_START_OF_LINE = 1 << 0,
HAS_LEADING_SPACE = 1 << 1
AT_START_OF_LINE = 1 << 0,
HAS_LEADING_SPACE = 1 << 1,
EXPANSION_DISABLED = 1 << 2
};
Token() : type(0), flags(0) { }
......@@ -67,8 +68,11 @@ struct Token
bool hasLeadingSpace() const { return (flags & HAS_LEADING_SPACE) != 0; }
void setHasLeadingSpace(bool space);
bool expansionDisabled() const { return (flags & EXPANSION_DISABLED) != 0; }
void setExpansionDisabled(bool disable);
int type;
int flags;
unsigned int flags;
SourceLocation location;
std::string value;
};
......
......@@ -2305,6 +2305,7 @@ bool Tokenizer::init(int count, const char* const string[], const int length[])
void Tokenizer::lex(Token* token)
{
token->type = pplex(&token->value,&token->location,mHandle);
token->flags = 0;
token->setAtStartOfLine(mContext.lineStart);
mContext.lineStart = token->type == '\n';
......
......@@ -289,6 +289,7 @@ bool Tokenizer::init(int count, const char* const string[], const int length[])
void Tokenizer::lex(Token* token)
{
token->type = yylex(&token->value, &token->location, mHandle);
token->flags = 0;
token->setAtStartOfLine(mContext.lineStart);
mContext.lineStart = token->type == '\n';
......
......@@ -44,6 +44,7 @@
'../third_party/googlemock/src/gmock_main.cc',
'preprocessor_tests/char_test.cpp',
'preprocessor_tests/comment_test.cpp',
'preprocessor_tests/define_test.cpp',
'preprocessor_tests/error_test.cpp',
'preprocessor_tests/extension_test.cpp',
'preprocessor_tests/identifier_test.cpp',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment