[Cmake-commits] CMake branch, next, updated. v2.8.9-508-gf315bcb

Stephen Kelly steveire at gmail.com
Thu Sep 13 03:53:11 EDT 2012


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, next has been updated
       via  f315bcb5f34e54e5ab2bc6560323aaca7df77a2a (commit)
       via  1033647b46f5bfd883d497d0be9060cb79cb47f8 (commit)
       via  dea453208496a27d210c461f1f01e1935a185538 (commit)
      from  33a8c95f280f62e7e59db56815bf05acc71e338b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=f315bcb5f34e54e5ab2bc6560323aaca7df77a2a
commit f315bcb5f34e54e5ab2bc6560323aaca7df77a2a
Merge: 33a8c95 1033647
Author:     Stephen Kelly <steveire at gmail.com>
AuthorDate: Thu Sep 13 03:53:07 2012 -0400
Commit:     CMake Topic Stage <kwrobot at kitware.com>
CommitDate: Thu Sep 13 03:53:07 2012 -0400

    Merge topic 'generator-expression-refactor' into next
    
    1033647 Port current users of cmGeneratorExpression to two-stage processing.
    dea4532 Re-write the cmGeneratorExpression for multi-stage evaluation.


http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=1033647b46f5bfd883d497d0be9060cb79cb47f8
commit 1033647b46f5bfd883d497d0be9060cb79cb47f8
Author:     Stephen Kelly <steveire at gmail.com>
AuthorDate: Wed Sep 12 15:11:25 2012 +0200
Commit:     Stephen Kelly <steveire at gmail.com>
CommitDate: Thu Sep 13 09:28:27 2012 +0200

    Port current users of cmGeneratorExpression to two-stage processing.
    
    Removing the Process() API and removing the parameters from the
    constructor will allow cmGeneratorExpressions to be cached and
    evaluated with multiple configs for example, such as when evaluating
    target properties. This requires the creation of a new compiled
    representation of cmGeneratorExpression.
    
    The cmListFileBacktrace remains in the constructor so that we can
    record where a particular generator expression appeared in the
    CMakeLists file.

diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index a650129..07df7d5 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -21,7 +21,7 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(
   cmCustomCommand const& cc, const char* config, cmMakefile* mf):
   CC(cc), Config(config), Makefile(mf), LG(mf->GetLocalGenerator()),
   OldStyle(cc.GetEscapeOldStyle()), MakeVars(cc.GetEscapeAllowMakeVars()),
-  GE(new cmGeneratorExpression(mf, config, cc.GetBacktrace()))
+  GE(new cmGeneratorExpression(cc.GetBacktrace()))
 {
 }
 
@@ -47,7 +47,7 @@ std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const
     {
     return target->GetLocation(this->Config);
     }
-  return this->GE->Process(argv0);
+  return this->GE->Parse(argv0).Evaluate(this->Makefile, this->Config);
 }
 
 //----------------------------------------------------------------------------
@@ -58,7 +58,8 @@ cmCustomCommandGenerator
   cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c];
   for(unsigned int j=1;j < commandLine.size(); ++j)
     {
-    std::string arg = this->GE->Process(commandLine[j]);
+    std::string arg = this->GE->Parse(commandLine[j]).Evaluate(this->Makefile,
+                                                               this->Config);
     cmd += " ";
     if(this->OldStyle)
       {
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index 4483a33..731938d 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -22,48 +22,40 @@
 
 //----------------------------------------------------------------------------
 cmGeneratorExpression::cmGeneratorExpression(
-  cmMakefile* mf, const char* config,
-  cmListFileBacktrace const& backtrace, bool quiet):
-  Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet),
-  NeedsParsing(true)
+  cmListFileBacktrace const& backtrace):
+  Backtrace(backtrace)
 {
 }
 
 //----------------------------------------------------------------------------
-const char* cmGeneratorExpression::Process(std::string const& input)
+const cmCompiledGeneratorExpression
+cmGeneratorExpression::Parse(std::string const& input)
 {
-  return this->Process(input.c_str());
+  return this->Parse(input.c_str());
 }
 
 //----------------------------------------------------------------------------
-const char* cmGeneratorExpression::Process(const char* input)
+const cmCompiledGeneratorExpression
+cmGeneratorExpression::Parse(const char* input)
 {
-  this->Parse(input);
-  return this->Evaluate(this->Makefile, this->Config, this->Quiet);
-}
-
-//----------------------------------------------------------------------------
-void cmGeneratorExpression::Parse(const char* input)
-{
-  this->Evaluators.clear();
-
-  this->Input = input;
   cmGeneratorExpressionLexer l;
-  std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
-  this->NeedsParsing = l.GetSawGeneratorExpression();
+  std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(input);
+  bool needsParsing = l.GetSawGeneratorExpression();
+  std::vector<cmGeneratorExpressionEvaluator*> evaluators;
 
-  if (!this->NeedsParsing)
+  if (needsParsing)
     {
-    return;
+    cmGeneratorExpressionParser p(tokens);
+    p.Parse(evaluators);
     }
 
-  cmGeneratorExpressionParser p(tokens);
-  p.Parse(this->Evaluators);
+  return cmCompiledGeneratorExpression(this->Backtrace, evaluators, input,
+                                       needsParsing);
 }
 
 //----------------------------------------------------------------------------
-const char *cmGeneratorExpression::Evaluate(
-  cmMakefile* mf, const char* config, bool quiet)
+const char *cmCompiledGeneratorExpression::Evaluate(
+  cmMakefile* mf, const char* config, bool quiet) const
 {
   if (!this->NeedsParsing)
     {
@@ -99,8 +91,19 @@ const char *cmGeneratorExpression::Evaluate(
   return this->Output.c_str();
 }
 
+cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
+                      cmListFileBacktrace const& backtrace,
+                      std::vector<cmGeneratorExpressionEvaluator*> evaluators,
+                      const char *input, bool needsParsing)
+  : Backtrace(backtrace), Evaluators(evaluators), Input(input),
+    NeedsParsing(needsParsing)
+{
+
+}
+
+
 //----------------------------------------------------------------------------
-cmGeneratorExpression::~cmGeneratorExpression()
+cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression()
 {
   std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
                                                   = this->Evaluators.begin();
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h
index 82da024..f29c9d3 100644
--- a/Source/cmGeneratorExpression.h
+++ b/Source/cmGeneratorExpression.h
@@ -21,6 +21,8 @@ class cmListFileBacktrace;
 
 class cmGeneratorExpressionEvaluator;
 
+class cmCompiledGeneratorExpression;
+
 /** \class cmGeneratorExpression
  * \brief Evaluate generate-time query expression syntax.
  *
@@ -33,34 +35,42 @@ class cmGeneratorExpressionEvaluator;
 class cmGeneratorExpression
 {
 public:
-  /** Construct with an evaluation context and configuration.  */
-  cmGeneratorExpression(cmMakefile* mf, const char* config,
-                        cmListFileBacktrace const& backtrace,
-                        bool quiet = false);
+  /** Construct. */
+  cmGeneratorExpression(cmListFileBacktrace const& backtrace);
 
-  ~cmGeneratorExpression();
+  const cmCompiledGeneratorExpression Parse(std::string const& input);
+  const cmCompiledGeneratorExpression Parse(const char* input);
 
-  /** Evaluate generator expressions in a string.  */
-  const char* Process(std::string const& input);
-  const char* Process(const char* input);
+private:
+  cmListFileBacktrace const& Backtrace;
+};
 
-  void Parse(const char* input);
+class cmCompiledGeneratorExpression
+{
+public:
   const char* Evaluate(cmMakefile* mf, const char* config,
-                        bool quiet = false);
+                        bool quiet = false) const;
 
   /** Get set of targets found during evaluations.  */
   std::set<cmTarget*> const& GetTargets() const
     { return this->Targets; }
+
+  ~cmCompiledGeneratorExpression();
+
+private:
+  cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
+                      std::vector<cmGeneratorExpressionEvaluator*> evaluators,
+                      const char *input, bool needsParsing);
+
+  friend class cmGeneratorExpression;
+
 private:
-  std::vector<cmGeneratorExpressionEvaluator*> Evaluators;
-  cmMakefile* Makefile;
-  const char* Config;
+  const std::vector<cmGeneratorExpressionEvaluator*> Evaluators;
   cmListFileBacktrace const& Backtrace;
-  bool Quiet;
 
-  std::set<cmTarget*> Targets;
-  const char* Input;
-  bool NeedsParsing;
+  mutable std::set<cmTarget*> Targets;
+  const char* const Input;
+  const bool NeedsParsing;
 
-  std::string Output;
+  mutable std::string Output;
 };
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 789713f..9f62586 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -1623,7 +1623,11 @@ cmTargetTraceDependencies
 {
   // Transform command names that reference targets built in this
   // project to corresponding target-level dependencies.
-  cmGeneratorExpression ge(this->Makefile, 0, cc.GetBacktrace(), true);
+  cmGeneratorExpression ge(cc.GetBacktrace());
+
+  // Add target-level dependencies referenced by generator expressions.
+  std::set<cmTarget*> targets;
+
   for(cmCustomCommandLines::const_iterator cit = cc.GetCommandLines().begin();
       cit != cc.GetCommandLines().end(); ++cit)
     {
@@ -1645,12 +1649,13 @@ cmTargetTraceDependencies
     for(cmCustomCommandLine::const_iterator cli = cit->begin();
         cli != cit->end(); ++cli)
       {
-      ge.Process(*cli);
+      cmCompiledGeneratorExpression cge = ge.Parse(*cli);
+      cge.Evaluate(this->Makefile, 0, true);
+      std::set<cmTarget*> geTargets = cge.GetTargets();
+      targets.insert(geTargets.begin(), geTargets.end());
       }
     }
 
-  // Add target-level dependencies referenced by generator expressions.
-  std::set<cmTarget*> targets = ge.GetTargets();
   for(std::set<cmTarget*>::iterator ti = targets.begin();
       ti != targets.end(); ++ti)
     {
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index e0892b2..2f650e7 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -91,8 +91,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   this->TestGenerated = true;
 
   // Set up generator expression evaluation context.
-  cmMakefile* mf = this->Test->GetMakefile();
-  cmGeneratorExpression ge(mf, config, this->Test->GetBacktrace());
+  cmGeneratorExpression ge(this->Test->GetBacktrace());
 
   // Start the test command.
   os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
@@ -103,6 +102,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   // Check whether the command executable is a target whose name is to
   // be translated.
   std::string exe = command[0];
+  cmMakefile* mf = this->Test->GetMakefile();
   cmTarget* target = mf->FindTargetToUse(exe.c_str());
   if(target && target->GetType() == cmTarget::EXECUTABLE)
     {
@@ -112,7 +112,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   else
     {
     // Use the command name given.
-    exe = ge.Process(exe.c_str());
+    exe = ge.Parse(exe.c_str()).Evaluate(mf, config);
     cmSystemTools::ConvertToUnixSlashes(exe);
     }
 
@@ -122,7 +122,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   for(std::vector<std::string>::const_iterator ci = command.begin()+1;
       ci != command.end(); ++ci)
     {
-    os << " " << lg->EscapeForCMake(ge.Process(*ci));
+    os << " " << lg->EscapeForCMake(ge.Parse(*ci).Evaluate(mf, config));
     }
 
   // Finish the test command.

http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=dea453208496a27d210c461f1f01e1935a185538
commit dea453208496a27d210c461f1f01e1935a185538
Author:     Stephen Kelly <steveire at gmail.com>
AuthorDate: Tue Sep 11 19:53:38 2012 +0200
Commit:     Stephen Kelly <steveire at gmail.com>
CommitDate: Thu Sep 13 09:27:09 2012 +0200

    Re-write the cmGeneratorExpression for multi-stage evaluation.
    
    The expressions may be parsed and then cached and evaluated multiple times.
    They are evaluated lazily so that literals such as ',' can be treated as
    universal parameter separators, and can be processed from results without
    appearing literally, and without interfering with the parsing/evaluation
    of the entire expression.

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index e79689b..ba71c8a 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -183,6 +183,12 @@ set(SRCS
   cmFileTimeComparison.cxx
   cmFileTimeComparison.h
   cmGeneratedFileStream.cxx
+  cmGeneratorExpressionEvaluator.cxx
+  cmGeneratorExpressionEvaluator.h
+  cmGeneratorExpressionLexer.cxx
+  cmGeneratorExpressionLexer.h
+  cmGeneratorExpressionParser.cxx
+  cmGeneratorExpressionParser.h
   cmGeneratorExpression.cxx
   cmGeneratorExpression.h
   cmGeneratorTarget.cxx
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx
index 92bbf1d..4483a33 100644
--- a/Source/cmGeneratorExpression.cxx
+++ b/Source/cmGeneratorExpression.cxx
@@ -16,18 +16,17 @@
 
 #include <cmsys/String.h>
 
+#include "cmGeneratorExpressionEvaluator.h"
+#include "cmGeneratorExpressionLexer.h"
+#include "cmGeneratorExpressionParser.h"
+
 //----------------------------------------------------------------------------
 cmGeneratorExpression::cmGeneratorExpression(
   cmMakefile* mf, const char* config,
   cmListFileBacktrace const& backtrace, bool quiet):
-  Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet)
+  Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet),
+  NeedsParsing(true)
 {
-  this->TargetInfo.compile("^\\$<TARGET"
-                           "(|_SONAME|_LINKER)"  // File with what purpose?
-                           "_FILE(|_NAME|_DIR):" // Filename component.
-                           "([A-Za-z0-9_.-]+)"   // Target name.
-                           ">$");
-  this->TestConfig.compile("^\\$<CONFIG:([A-Za-z0-9_]*)>$");
 }
 
 //----------------------------------------------------------------------------
@@ -39,210 +38,77 @@ const char* cmGeneratorExpression::Process(std::string const& input)
 //----------------------------------------------------------------------------
 const char* cmGeneratorExpression::Process(const char* input)
 {
-  this->Data.clear();
-
-  // We construct and evaluate expressions directly in the output
-  // buffer.  Each expression is replaced by its own output value
-  // after evaluation.  A stack of barriers records the starting
-  // indices of open (pending) expressions.
-  for(const char* c = input; *c; ++c)
-    {
-    if(c[0] == '$' && c[1] == '<')
-      {
-      this->Barriers.push(this->Data.size());
-      this->Data.push_back('$');
-      this->Data.push_back('<');
-      c += 1;
-      }
-    else if(c[0] == '>' && !this->Barriers.empty())
-      {
-      this->Data.push_back('>');
-      if(!this->Evaluate()) { break; }
-      this->Barriers.pop();
-      }
-    else
-      {
-      this->Data.push_back(c[0]);
-      }
-    }
-
-  // Return a null-terminated output value.
-  this->Data.push_back('\0');
-  return &*this->Data.begin();
+  this->Parse(input);
+  return this->Evaluate(this->Makefile, this->Config, this->Quiet);
 }
 
 //----------------------------------------------------------------------------
-bool cmGeneratorExpression::Evaluate()
+void cmGeneratorExpression::Parse(const char* input)
 {
-  // The top-most barrier points at the beginning of the expression.
-  size_t barrier = this->Barriers.top();
+  this->Evaluators.clear();
 
-  // Construct a null-terminated representation of the expression.
-  this->Data.push_back('\0');
-  const char* expr = &*(this->Data.begin()+barrier);
+  this->Input = input;
+  cmGeneratorExpressionLexer l;
+  std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
+  this->NeedsParsing = l.GetSawGeneratorExpression();
 
-  // Evaluate the expression.
-  std::string result;
-  if(this->Evaluate(expr, result))
+  if (!this->NeedsParsing)
     {
-    // Success.  Replace the expression with its evaluation result.
-    this->Data.erase(this->Data.begin()+barrier, this->Data.end());
-    this->Data.insert(this->Data.end(), result.begin(), result.end());
-    return true;
+    return;
     }
-  else if(!this->Quiet)
-    {
-    // Failure.  Report the error message.
-    cmOStringStream e;
-    e << "Error evaluating generator expression:\n"
-      << "  " << expr << "\n"
-      << result;
-    this->Makefile->GetCMakeInstance()
-      ->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
-                     this->Backtrace);
-    return false;
-    }
-  return true;
-}
 
-//----------------------------------------------------------------------------
-static bool cmGeneratorExpressionBool(const char* c, std::string& result,
-                                      const char* name,
-                                      const char* a, const char* b)
-{
-  result = a;
-  while((c[0] == '0' || c[0] == '1') && (c[1] == ',' || c[1] == '>'))
-    {
-    if(c[0] == b[0]) { result = b; }
-    c += 2;
-    }
-  if(c[0])
-    {
-    result = name;
-    result += " requires one or more comma-separated '0' or '1' values.";
-    return false;
-    }
-  return true;
+  cmGeneratorExpressionParser p(tokens);
+  p.Parse(this->Evaluators);
 }
 
 //----------------------------------------------------------------------------
-bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
+const char *cmGeneratorExpression::Evaluate(
+  cmMakefile* mf, const char* config, bool quiet)
 {
-  if(this->TargetInfo.find(expr))
+  if (!this->NeedsParsing)
     {
-    if(!this->EvaluateTargetInfo(result))
-      {
-      return false;
-      }
+    return this->Input;
     }
-  else if(strcmp(expr, "$<CONFIGURATION>") == 0)
-    {
-    result = this->Config? this->Config : "";
-    }
-  else if(strncmp(expr, "$<0:",4) == 0)
-    {
-    result = "";
-    }
-  else if(strncmp(expr, "$<1:",4) == 0)
-    {
-    result = std::string(expr+4, strlen(expr)-5);
-    }
-  else if(strncmp(expr, "$<NOT:",6) == 0)
+
+  this->Output.clear();
+
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                  = this->Evaluators.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                  = this->Evaluators.end();
+
+  cmGeneratorExpressionContext context;
+  context.Makefile = mf;
+  context.Config = config;
+  context.Quiet = quiet;
+  context.HadError = false;
+  context.Backtrace = this->Backtrace;
+
+  for ( ; it != end; ++it)
     {
-    const char* c = expr+6;
-    if((c[0] != '0' && c[0] != '1') || c[1] != '>' || c[2])
+    this->Output += (*it)->Evaluate(&context);
+    if (context.HadError)
       {
-      result = "NOT requires exactly one '0' or '1' value.";
-      return false;
+      this->Output.clear();
+      break;
       }
-    result = c[0] == '1'? "0" : "1";
-    }
-  else if(strncmp(expr, "$<AND:",6) == 0)
-    {
-    return cmGeneratorExpressionBool(expr+6, result, "AND", "1", "0");
-    }
-  else if(strncmp(expr, "$<OR:",5) == 0)
-    {
-    return cmGeneratorExpressionBool(expr+5, result, "OR", "0", "1");
-    }
-  else if(this->TestConfig.find(expr))
-    {
-    result = cmsysString_strcasecmp(this->TestConfig.match(1).c_str(),
-                                    this->Config? this->Config:"") == 0
-      ? "1":"0";
     }
-  else
-    {
-    result = "Expression syntax not recognized.";
-    return false;
-    }
-  return true;
+
+  this->Targets = context.Targets;
+  // TODO: Return a std::string from here instead?
+  return this->Output.c_str();
 }
 
 //----------------------------------------------------------------------------
-bool cmGeneratorExpression::EvaluateTargetInfo(std::string& result)
+cmGeneratorExpression::~cmGeneratorExpression()
 {
-  // Lookup the referenced target.
-  std::string name = this->TargetInfo.match(3);
-  cmTarget* target = this->Makefile->FindTargetToUse(name.c_str());
-  if(!target)
-    {
-    result = "No target \"" + name + "\"";
-    return false;
-    }
-  if(target->GetType() >= cmTarget::UTILITY &&
-     target->GetType() != cmTarget::UNKNOWN_LIBRARY)
-    {
-    result = "Target \"" + name + "\" is not an executable or library.";
-    return false;
-    }
-  this->Targets.insert(target);
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                  = this->Evaluators.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                  = this->Evaluators.end();
 
-  // Lookup the target file with the given purpose.
-  std::string purpose = this->TargetInfo.match(1);
-  if(purpose == "")
-    {
-    // The target implementation file (.so.1.2, .dll, .exe, .a).
-    result = target->GetFullPath(this->Config, false, true);
-    }
-  else if(purpose == "_LINKER")
-    {
-    // The file used to link to the target (.so, .lib, .a).
-    if(!target->IsLinkable())
-      {
-      result = ("TARGET_LINKER_FILE is allowed only for libraries and "
-                "executables with ENABLE_EXPORTS.");
-      return false;
-      }
-    result = target->GetFullPath(this->Config, target->HasImportLibrary());
-    }
-  else if(purpose == "_SONAME")
-    {
-    // The target soname file (.so.1).
-    if(target->IsDLLPlatform())
-      {
-      result = "TARGET_SONAME_FILE is not allowed for DLL target platforms.";
-      return false;
-      }
-    if(target->GetType() != cmTarget::SHARED_LIBRARY)
-      {
-      result = "TARGET_SONAME_FILE is allowed only for SHARED libraries.";
-      return false;
-      }
-    result = target->GetDirectory(this->Config);
-    result += "/";
-    result += target->GetSOName(this->Config);
-    }
-
-  // Extract the requested portion of the full path.
-  std::string part = this->TargetInfo.match(2);
-  if(part == "_NAME")
-    {
-    result = cmSystemTools::GetFilenameName(result);
-    }
-  else if(part == "_DIR")
+  for ( ; it != end; ++it)
     {
-    result = cmSystemTools::GetFilenamePath(result);
+    delete *it;
     }
-  return true;
 }
diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h
index a023eb0..82da024 100644
--- a/Source/cmGeneratorExpression.h
+++ b/Source/cmGeneratorExpression.h
@@ -19,6 +19,8 @@ class cmTarget;
 class cmMakefile;
 class cmListFileBacktrace;
 
+class cmGeneratorExpressionEvaluator;
+
 /** \class cmGeneratorExpression
  * \brief Evaluate generate-time query expression syntax.
  *
@@ -36,24 +38,29 @@ public:
                         cmListFileBacktrace const& backtrace,
                         bool quiet = false);
 
+  ~cmGeneratorExpression();
+
   /** Evaluate generator expressions in a string.  */
   const char* Process(std::string const& input);
   const char* Process(const char* input);
 
+  void Parse(const char* input);
+  const char* Evaluate(cmMakefile* mf, const char* config,
+                        bool quiet = false);
+
   /** Get set of targets found during evaluations.  */
   std::set<cmTarget*> const& GetTargets() const
     { return this->Targets; }
 private:
+  std::vector<cmGeneratorExpressionEvaluator*> Evaluators;
   cmMakefile* Makefile;
   const char* Config;
   cmListFileBacktrace const& Backtrace;
   bool Quiet;
-  std::vector<char> Data;
-  std::stack<size_t> Barriers;
-  cmsys::RegularExpression TargetInfo;
-  cmsys::RegularExpression TestConfig;
+
   std::set<cmTarget*> Targets;
-  bool Evaluate();
-  bool Evaluate(const char* expr, std::string& result);
-  bool EvaluateTargetInfo(std::string& result);
+  const char* Input;
+  bool NeedsParsing;
+
+  std::string Output;
 };
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
new file mode 100644
index 0000000..79aeb22
--- /dev/null
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -0,0 +1,464 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <steveire at gmail.com>
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#include "cmMakefile.h"
+
+#include "cmGeneratorExpressionEvaluator.h"
+#include "cmGeneratorExpressionParser.h"
+
+//----------------------------------------------------------------------------
+static void reportError(cmGeneratorExpressionContext *context,
+                        const std::string &expr, const std::string &result)
+{
+  context->HadError = true;
+  if (context->Quiet)
+    {
+    return;
+    }
+
+  cmOStringStream e;
+  e << "Error evaluating generator expression:\n"
+    << "  " << expr << "\n"
+    << result;
+  context->Makefile->GetCMakeInstance()
+    ->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
+                    context->Backtrace);
+}
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionNode
+{
+  virtual ~cmGeneratorExpressionNode() {}
+
+  virtual bool GeneratesContent() const { return true; }
+
+  virtual bool AcceptsSingleArbitraryContentParameter() const
+    { return false; }
+
+  virtual int NumExpectedParameters() const { return 1; }
+
+  virtual std::string Evaluate(const std::vector<std::string> &parameters,
+                               cmGeneratorExpressionContext *context,
+                               const GeneratorExpressionContent *content
+                              ) const = 0;
+};
+
+//----------------------------------------------------------------------------
+static const struct : public cmGeneratorExpressionNode
+{
+  virtual bool GeneratesContent() const { return false; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    // Unreachable
+    return std::string();
+  }
+} zeroNode;
+
+//----------------------------------------------------------------------------
+static const struct : public cmGeneratorExpressionNode
+{
+  virtual bool AcceptsSingleArbitraryContentParameter() const { return true; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    // Unreachable
+    return std::string();
+  }
+} oneNode;
+
+//----------------------------------------------------------------------------
+#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \
+static const struct : public cmGeneratorExpressionNode \
+{ \
+/* We let -1 carry the meaning 'at least one' */ \
+  virtual int NumExpectedParameters() const { return -1; } \
+ \
+  std::string Evaluate(const std::vector<std::string> &parameters, \
+                       cmGeneratorExpressionContext *context, \
+                       const GeneratorExpressionContent *content) const \
+  { \
+    std::vector<std::string>::const_iterator it = parameters.begin(); \
+    const std::vector<std::string>::const_iterator end = parameters.end(); \
+    for ( ; it != end; ++it) \
+      { \
+      if (*it == #FAILURE_VALUE) \
+        { \
+        return #FAILURE_VALUE; \
+        } \
+      else if (*it != #SUCCESS_VALUE) \
+        { \
+        reportError(context, content->GetOriginalExpression(), \
+        "Parameters to $<" #OP "> must resolve to either '0' or '1'."); \
+        return std::string(); \
+        } \
+      } \
+    return #SUCCESS_VALUE; \
+  } \
+} OPNAME;
+
+BOOLEAN_OP_NODE(andNode, AND, 1, 0)
+BOOLEAN_OP_NODE(orNode, OR, 0, 1)
+
+#undef BOOLEAN_OP_NODE
+
+//----------------------------------------------------------------------------
+static const struct : public cmGeneratorExpressionNode
+{
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    if (*parameters.begin() != "0" && *parameters.begin() != "1")
+      {
+      reportError(context, content->GetOriginalExpression(),
+            "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
+      return std::string();
+      }
+    return *parameters.begin() == "0" ? "1" : "0";
+  }
+} notNode;
+
+//----------------------------------------------------------------------------
+static const struct : public cmGeneratorExpressionNode
+{
+  virtual int NumExpectedParameters() const { return 0; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *) const
+  {
+    return context->Config ? context->Config : "";
+  }
+} configurationNode;
+
+//----------------------------------------------------------------------------
+static const struct : public cmGeneratorExpressionNode
+{
+  virtual int NumExpectedParameters() const { return 1; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    if (!context->Config)
+      {
+      return std::string();
+      }
+
+    cmsys::RegularExpression configValidator;
+    configValidator.compile("^[A-Za-z0-9_]*$");
+    if (!configValidator.find(parameters.begin()->c_str()))
+      {
+      reportError(context, content->GetOriginalExpression(),
+                  "Expression syntax not recognized.");
+      return std::string();
+      }
+    return *parameters.begin() == context->Config ? "1" : "0";
+  }
+} configurationTestNode;
+
+//----------------------------------------------------------------------------
+template<bool linker, bool soname, bool dirQual, bool nameQual>
+struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
+{
+  virtual int NumExpectedParameters() const { return 1; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    // Lookup the referenced target.
+    std::string name = *parameters.begin();
+
+    cmsys::RegularExpression targetValidator;
+    targetValidator.compile("^[A-Za-z0-9_]+$");
+    if (!targetValidator.find(name.c_str()))
+      {
+      reportError(context, content->GetOriginalExpression(),
+                  "Expression syntax not recognized.");
+      return std::string();
+      }
+    cmTarget* target = context->Makefile->FindTargetToUse(name.c_str());
+    if(!target)
+      {
+      reportError(context, content->GetOriginalExpression(),
+                  "No target \"" + name + "\"");
+      return std::string();
+      }
+    if(target->GetType() >= cmTarget::UTILITY &&
+      target->GetType() != cmTarget::UNKNOWN_LIBRARY)
+      {
+      reportError(context, content->GetOriginalExpression(),
+                  "Target \"" + name + "\" is not an executable or library.");
+      return std::string();
+      }
+    context->Targets.insert(target);
+
+    std::string result;
+
+    // TODO: static_assert(!(linker && soname))
+    if (!linker && !soname)
+      {
+        result = target->GetFullPath(context->Config, false, true);
+      }
+    else if (linker)
+      {
+      // The file used to link to the target (.so, .lib, .a).
+      if(!target->IsLinkable())
+        {
+        reportError(context, content->GetOriginalExpression(),
+                    "TARGET_LINKER_FILE is allowed only for libraries and "
+                    "executables with ENABLE_EXPORTS.");
+        return std::string();
+        }
+      result = target->GetFullPath(context->Config,
+                                   target->HasImportLibrary());
+      }
+    else if (soname)
+      {
+      // The target soname file (.so.1).
+      if(target->IsDLLPlatform())
+        {
+        reportError(context, content->GetOriginalExpression(),
+                    "TARGET_SONAME_FILE is not allowed "
+                    "for DLL target platforms.");
+        return std::string();
+        }
+      if(target->GetType() != cmTarget::SHARED_LIBRARY)
+        {
+        reportError(context, content->GetOriginalExpression(),
+                    "TARGET_SONAME_FILE is allowed only for "
+                    "SHARED libraries.");
+        return std::string();
+        }
+      result = target->GetDirectory(context->Config);
+      result += "/";
+      result += target->GetSOName(context->Config);
+      }
+
+  // TODO: static_assert(!(name && dir))
+  if(nameQual)
+    {
+    return cmSystemTools::GetFilenameName(result);
+    }
+  else if (dirQual)
+    {
+    return cmSystemTools::GetFilenamePath(result);
+    }
+  return result;
+  }
+};
+
+//----------------------------------------------------------------------------
+static const
+TargetFilesystemArtifact<false, false, false, false> targetFileNode;
+static const
+TargetFilesystemArtifact<true, false, false, false> targetLinkerFileNode;
+static const
+TargetFilesystemArtifact<false, true, false, false> targetSoNameFileNode;
+static const
+TargetFilesystemArtifact<false, false, false, true> targetFileNameNode;
+static const
+TargetFilesystemArtifact<true, false, false, true> targetLinkerFileNameNode;
+static const
+TargetFilesystemArtifact<false, true, false, true> targetSoNameFileNameNode;
+static const
+TargetFilesystemArtifact<false, false, true, false> targetFileDirNode;
+static const
+TargetFilesystemArtifact<true, false, true, false> targetLinkerFileDirNode;
+static const
+TargetFilesystemArtifact<false, true, true, false> targetSoNameFileDirNode;
+
+//----------------------------------------------------------------------------
+static const
+cmGeneratorExpressionNode* const GetNode(const std::string &identifier)
+{
+  if (identifier == "0")
+    return &zeroNode;
+  if (identifier == "1")
+    return &oneNode;
+  if (identifier == "AND")
+    return &andNode;
+  if (identifier == "OR")
+    return &orNode;
+  if (identifier == "NOT")
+    return &notNode;
+  else if (identifier == "CONFIGURATION")
+    return &configurationNode;
+  else if (identifier == "CONFIG")
+    return &configurationTestNode;
+  else if (identifier == "TARGET_FILE")
+    return &targetFileNode;
+  else if (identifier == "TARGET_LINKER_FILE")
+    return &targetLinkerFileNode;
+  else if (identifier == "TARGET_SONAME_FILE")
+    return &targetSoNameFileNode;
+  else if (identifier == "TARGET_FILE_NAME")
+    return &targetFileNameNode;
+  else if (identifier == "TARGET_LINKER_FILE_NAME")
+    return &targetLinkerFileNameNode;
+  else if (identifier == "TARGET_SONAME_FILE_NAME")
+    return &targetSoNameFileNameNode;
+  else if (identifier == "TARGET_FILE_DIR")
+    return &targetFileDirNode;
+  else if (identifier == "TARGET_LINKER_FILE_DIR")
+    return &targetLinkerFileDirNode;
+  else if (identifier == "TARGET_SONAME_FILE_DIR")
+    return &targetSoNameFileDirNode;
+  return 0;
+}
+
+//----------------------------------------------------------------------------
+GeneratorExpressionContent::GeneratorExpressionContent(
+                                                    const char *startContent,
+                                                    uint length)
+  : StartContent(startContent), ContentLength(length)
+{
+
+}
+
+//----------------------------------------------------------------------------
+std::string GeneratorExpressionContent::GetOriginalExpression() const
+{
+  return std::string(this->StartContent, this->ContentLength);
+}
+
+//----------------------------------------------------------------------------
+std::string GeneratorExpressionContent::Evaluate(
+                                  cmGeneratorExpressionContext *context) const
+{
+  std::string identifier;
+  {
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                          = this->IdentifierChildren.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                          = this->IdentifierChildren.end();
+  for ( ; it != end; ++it)
+    {
+    identifier += (*it)->Evaluate(context);
+    if (context->HadError)
+      {
+      return std::string();
+      }
+    }
+  }
+
+  const cmGeneratorExpressionNode *node = GetNode(identifier);
+
+  if (!node)
+    {
+    reportError(context, this->GetOriginalExpression(),
+              "Expression did not evaluate to a known generator expression");
+    return std::string();
+    }
+
+  if (!node->GeneratesContent())
+    {
+    return std::string();
+    }
+
+  if (node->AcceptsSingleArbitraryContentParameter())
+    {
+    std::string result;
+    std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pit = this->ParamChildren.begin();
+    const
+    std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pend = this->ParamChildren.end();
+    for ( ; pit != pend; ++pit)
+      {
+      if (!result.empty())
+        {
+        result += ",";
+        }
+
+      std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                              = pit->begin();
+      const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                                = pit->end();
+      for ( ; it != end; ++it)
+        {
+        result += (*it)->Evaluate(context);
+        if (context->HadError)
+          {
+          return std::string();
+          }
+        }
+      }
+    return result;
+    }
+
+  std::vector<std::string> parameters;
+  {
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pit = this->ParamChildren.begin();
+  const
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pend = this->ParamChildren.end();
+  for ( ; pit != pend; ++pit)
+    {
+    std::string parameter;
+    std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it =
+                                                              pit->begin();
+    const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end =
+                                                              pit->end();
+    for ( ; it != end; ++it)
+      {
+      parameter += (*it)->Evaluate(context);
+      if (context->HadError)
+        {
+        return std::string();
+        }
+      }
+    parameters.push_back(parameter);
+    }
+  }
+
+  int numExpected = node->NumExpectedParameters();
+  if ((numExpected != -1 && numExpected != parameters.size()))
+    {
+    if (numExpected == 0)
+      {
+      reportError(context, this->GetOriginalExpression(),
+                  "$<" + identifier + "> expression requires no parameters.");
+      }
+    else if (numExpected == 1)
+      {
+      reportError(context, this->GetOriginalExpression(),
+                  "$<" + identifier + "> expression requires "
+                  "exactly one parameter.");
+      }
+    else
+      {
+      cmOStringStream e;
+      e << "$<" + identifier + "> expression requires "
+        << numExpected
+        << " comma separated parameters, but got "
+        << parameters.size() << " instead.";
+      reportError(context, this->GetOriginalExpression(), e.str());
+      }
+    return std::string();
+    }
+
+  if (numExpected == -1 && parameters.empty())
+    {
+    reportError(context, this->GetOriginalExpression(), "$<" + identifier
+                      + "> expression requires at least one parameter.");
+    return std::string();
+    }
+
+  return node->Evaluate(parameters, context, this);
+}
diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h
new file mode 100644
index 0000000..1c10e07
--- /dev/null
+++ b/Source/cmGeneratorExpressionEvaluator.h
@@ -0,0 +1,111 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <steveire at gmail.com>
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#ifndef cmGeneratorExpressionEvaluator_h
+#define cmGeneratorExpressionEvaluator_h
+
+#include <vector>
+#include <string>
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionContext
+{
+  cmListFileBacktrace Backtrace;
+  std::set<cmTarget*> Targets;
+  cmMakefile *Makefile;
+  const char *Config;
+  cmTarget *Target;
+  bool Quiet;
+  bool HadError;
+};
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionEvaluator
+{
+  virtual ~cmGeneratorExpressionEvaluator() {}
+
+  enum Type
+  {
+    Text,
+    Generator
+  };
+
+  virtual Type GetType() const = 0;
+
+  virtual std::string Evaluate(cmGeneratorExpressionContext *context
+                              ) const = 0;
+};
+
+struct TextContent : public cmGeneratorExpressionEvaluator
+{
+  TextContent(const char *start, uint length)
+    : Content(start), Length(length)
+  {
+
+  }
+
+  std::string Evaluate(cmGeneratorExpressionContext *) const
+  {
+    return std::string(this->Content, this->Length);
+  }
+
+  Type GetType() const
+  {
+    return cmGeneratorExpressionEvaluator::Text;
+  }
+
+  void Extend(uint length)
+  {
+    this->Length += length;
+  }
+
+  uint GetLength()
+  {
+    return this->Length;
+  }
+
+private:
+  const char *Content;
+  uint Length;
+};
+
+//----------------------------------------------------------------------------
+struct GeneratorExpressionContent : public cmGeneratorExpressionEvaluator
+{
+  GeneratorExpressionContent(const char *startContent, uint length);
+  void SetIdentifier(std::vector<cmGeneratorExpressionEvaluator*> identifier)
+  {
+    this->IdentifierChildren = identifier;
+  }
+
+  void SetParameters(
+        std::vector<std::vector<cmGeneratorExpressionEvaluator*> > parameters)
+  {
+    this->ParamChildren = parameters;
+  }
+
+  Type GetType() const
+  {
+    return cmGeneratorExpressionEvaluator::Generator;
+  }
+
+  std::string Evaluate(cmGeneratorExpressionContext *context) const;
+
+  std::string GetOriginalExpression() const;
+
+private:
+  std::vector<cmGeneratorExpressionEvaluator*> IdentifierChildren;
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> > ParamChildren;
+  const char *StartContent;
+  uint ContentLength;
+};
+
+#endif
diff --git a/Source/cmGeneratorExpressionLexer.cxx b/Source/cmGeneratorExpressionLexer.cxx
new file mode 100644
index 0000000..cd71ec0
--- /dev/null
+++ b/Source/cmGeneratorExpressionLexer.cxx
@@ -0,0 +1,85 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <steveire at gmail.com>
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#include "cmGeneratorExpressionLexer.h"
+
+
+//----------------------------------------------------------------------------
+cmGeneratorExpressionLexer::cmGeneratorExpressionLexer()
+  : SawBeginExpression(false), SawGeneratorExpression(false)
+{
+
+}
+
+//----------------------------------------------------------------------------
+static void InsertText(const char *upto, const char *c,
+                        std::vector<cmGeneratorExpressionToken> &result)
+{
+  if (upto != c)
+    {
+    result.push_back(cmGeneratorExpressionToken(
+                          cmGeneratorExpressionToken::Text, upto, c - upto));
+    }
+}
+
+//----------------------------------------------------------------------------
+std::vector<cmGeneratorExpressionToken>
+cmGeneratorExpressionLexer::Tokenize(const char *input)
+{
+  std::vector<cmGeneratorExpressionToken> result;
+  if (!input)
+    return result;
+
+  const char *c = input;
+  const char *upto = c;
+
+  for ( ; *c; ++c)
+  {
+  if(c[0] == '$' && c[1] == '<')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                      cmGeneratorExpressionToken::BeginExpression, upto, 2));
+    upto = c + 2;
+    ++c;
+    SawBeginExpression = true;
+    }
+  else if(c[0] == '>')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                        cmGeneratorExpressionToken::EndExpression, upto, 1));
+    upto = c + 1;
+    SawGeneratorExpression = SawBeginExpression;
+    }
+  else if(c[0] == ':')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                        cmGeneratorExpressionToken::ColonSeparator, upto, 1));
+    upto = c + 1;
+    }
+  else if(c[0] == ',')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                        cmGeneratorExpressionToken::CommaSeparator, upto, 1));
+    upto = c + 1;
+    }
+  }
+  InsertText(upto, c, result);
+
+  return result;
+}
diff --git a/Source/cmGeneratorExpressionLexer.h b/Source/cmGeneratorExpressionLexer.h
new file mode 100644
index 0000000..bbd069f
--- /dev/null
+++ b/Source/cmGeneratorExpressionLexer.h
@@ -0,0 +1,58 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <steveire at gmail.com>
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#ifndef cmGeneratorExpressionLexer_h
+#define cmGeneratorExpressionLexer_h
+
+#include "cmStandardIncludes.h"
+
+#include <vector>
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionToken
+{
+  cmGeneratorExpressionToken(unsigned type, const char *c, unsigned l)
+    : TokenType(type), Content(c), Length(l)
+  {
+  }
+  enum {
+    Text,
+    BeginExpression,
+    EndExpression,
+    ColonSeparator,
+    CommaSeparator
+  };
+  unsigned TokenType;
+  const char *Content;
+  unsigned Length;
+};
+
+/** \class cmGeneratorExpressionLexer
+ *
+ */
+class cmGeneratorExpressionLexer
+{
+public:
+  cmGeneratorExpressionLexer();
+
+  std::vector<cmGeneratorExpressionToken> Tokenize(const char *input);
+
+  bool GetSawGeneratorExpression()
+  {
+    return this->SawGeneratorExpression;
+  }
+
+private:
+  bool SawGeneratorExpression;
+  bool SawBeginExpression;
+};
+
+#endif
diff --git a/Source/cmGeneratorExpressionParser.cxx b/Source/cmGeneratorExpressionParser.cxx
new file mode 100644
index 0000000..7e6c51e
--- /dev/null
+++ b/Source/cmGeneratorExpressionParser.cxx
@@ -0,0 +1,235 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <steveire at gmail.com>
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+
+#include "cmGeneratorExpressionParser.h"
+
+#include "cmGeneratorExpressionEvaluator.h"
+
+//----------------------------------------------------------------------------
+cmGeneratorExpressionParser::cmGeneratorExpressionParser(
+                      const std::vector<cmGeneratorExpressionToken> &tokens)
+  : Tokens(tokens), NestingLevel(0)
+{
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionParser::Parse(
+                        std::vector<cmGeneratorExpressionEvaluator*> &result)
+{
+  it = this->Tokens.begin();
+
+  while (this->it != this->Tokens.end())
+    {
+    this->ParseContent(result);
+    }
+}
+
+//----------------------------------------------------------------------------
+static void extendText(std::vector<cmGeneratorExpressionEvaluator*> &result,
+                  std::vector<cmGeneratorExpressionToken>::const_iterator it)
+{
+  if (result.size() > 0
+      && (*(result.end() - 1))->GetType()
+                                  == cmGeneratorExpressionEvaluator::Text)
+    {
+    TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1));
+    textContent->Extend(it->Length);
+    }
+  else
+    {
+    TextContent *textContent = new TextContent(it->Content, it->Length);
+    result.push_back(textContent);
+    }
+}
+
+//----------------------------------------------------------------------------
+static void extendResult(std::vector<cmGeneratorExpressionEvaluator*> &result,
+                const std::vector<cmGeneratorExpressionEvaluator*> &contents)
+{
+  if (result.size() > 0
+      && (*(result.end() - 1))->GetType()
+                                  == cmGeneratorExpressionEvaluator::Text
+      && (*contents.begin())->GetType()
+                                  == cmGeneratorExpressionEvaluator::Text)
+  {
+    TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1));
+    textContent->Extend(
+                  static_cast<TextContent*>(*contents.begin())->GetLength());
+    delete *contents.begin();
+    result.insert(result.end(), contents.begin() + 1, contents.end());
+  } else {
+    result.insert(result.end(), contents.begin(), contents.end());
+  }
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionParser::ParseGeneratorExpression(
+                        std::vector<cmGeneratorExpressionEvaluator*> &result)
+{
+  uint nestedLevel = this->NestingLevel;
+  ++this->NestingLevel;
+
+  std::vector<cmGeneratorExpressionToken>::const_iterator startToken
+                                                              = this->it - 1;
+
+  std::vector<cmGeneratorExpressionEvaluator*> identifier;
+  while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression
+      && this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator)
+    {
+    this->ParseContent(identifier);
+    if (this->it == this->Tokens.end())
+      {
+      break;
+      }
+    }
+  if (identifier.empty())
+    {
+    // ERROR
+    }
+
+  if (this->it->TokenType == cmGeneratorExpressionToken::EndExpression)
+    {
+    GeneratorExpressionContent *content = new GeneratorExpressionContent(
+                startToken->Content, this->it->Content
+                                    - startToken->Content
+                                    + this->it->Length);
+    ++this->it;
+    --this->NestingLevel;
+    content->SetIdentifier(identifier);
+    result.push_back(content);
+    return;
+    }
+
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> > parameters;
+  std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator>
+                                                            commaTokens;
+  std::vector<cmGeneratorExpressionToken>::const_iterator colonToken;
+  if (this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)
+    {
+    colonToken = this->it;
+    parameters.resize(parameters.size() + 1);
+    ++this->it;
+    while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression)
+      {
+      this->ParseContent(*(parameters.end() - 1));
+      if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator)
+        {
+        commaTokens.push_back(this->it);
+        parameters.resize(parameters.size() + 1);
+        ++this->it;
+        }
+      if (this->it == this->Tokens.end())
+        {
+        break;
+        }
+      }
+      if(this->it->TokenType == cmGeneratorExpressionToken::EndExpression)
+        {
+        --this->NestingLevel;
+        ++this->it;
+        }
+      if (parameters.empty())
+        {
+          // ERROR
+        }
+    }
+
+  if (nestedLevel != this->NestingLevel)
+  {
+    // There was a '$<' in the text, but no corresponding '>'. Rebuild to
+    // treat the '$<' as having been plain text, along with the
+    // corresponding : and , tokens that might have been found.
+    extendText(result, startToken);
+    extendResult(result, identifier);
+    if (!parameters.empty())
+      {
+      extendText(result, colonToken);
+
+      typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
+      typedef std::vector<cmGeneratorExpressionToken> TokenVector;
+      std::vector<EvaluatorVector>::const_iterator pit = parameters.begin();
+      const std::vector<EvaluatorVector>::const_iterator pend =
+                                                         parameters.end();
+      std::vector<TokenVector::const_iterator>::const_iterator commaIt =
+                                                         commaTokens.begin();
+      for ( ; pit != pend; ++pit, ++commaIt)
+        {
+        extendResult(result, *pit);
+        if (commaIt != commaTokens.end())
+          {
+          extendText(result, *commaIt);
+          }
+        }
+      }
+    return;
+  }
+
+  int contentLength = ((this->it - 1)->Content
+                    - startToken->Content)
+                    + (this->it - 1)->Length;
+  GeneratorExpressionContent *content = new GeneratorExpressionContent(
+                            startToken->Content, contentLength);
+  content->SetIdentifier(identifier);
+  content->SetParameters(parameters);
+  result.push_back(content);
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionParser::ParseContent(
+                        std::vector<cmGeneratorExpressionEvaluator*> &result)
+{
+  switch(this->it->TokenType)
+    {
+    case cmGeneratorExpressionToken::Text:
+    {
+      if (this->NestingLevel == 0)
+        {
+        if (result.size() > 0
+            && (*(result.end() - 1))->GetType()
+                                      == cmGeneratorExpressionEvaluator::Text)
+          {
+          // A comma in 'plain text' could have split text that should
+          // otherwise be continuous. Extend the last text content instead of
+          // creating a new one.
+          TextContent *textContent =
+                              static_cast<TextContent*>(*(result.end() - 1));
+          textContent->Extend(this->it->Length);
+          ++this->it;
+          return;
+          }
+        }
+      cmGeneratorExpressionEvaluator* n = new TextContent(this->it->Content,
+                                                          this->it->Length);
+      result.push_back(n);
+      ++this->it;
+      return ;
+    }
+    case cmGeneratorExpressionToken::BeginExpression:
+      ++this->it;
+      this->ParseGeneratorExpression(result);
+      return;
+    case cmGeneratorExpressionToken::EndExpression:
+    case cmGeneratorExpressionToken::ColonSeparator:
+    case cmGeneratorExpressionToken::CommaSeparator:
+      if (this->NestingLevel == 0)
+        {
+        extendText(result, this->it);
+        }
+      else
+        {
+        // TODO: Unreachable. Assert?
+        }
+      ++this->it;
+      return;
+    }
+  // Unreachable. Assert?
+}
diff --git a/Source/cmGeneratorExpressionParser.h b/Source/cmGeneratorExpressionParser.h
new file mode 100644
index 0000000..7cb1487
--- /dev/null
+++ b/Source/cmGeneratorExpressionParser.h
@@ -0,0 +1,45 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <steveire at gmail.com>
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#ifndef cmGeneratorExpressionParser_h
+#define cmGeneratorExpressionParser_h
+
+#include "cmGeneratorExpressionLexer.h"
+
+#include <set>
+#include <vector>
+
+#include "cmListFileCache.h"
+
+class cmMakefile;
+class cmTarget;
+class cmGeneratorExpressionEvaluator;
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionParser
+{
+  cmGeneratorExpressionParser(
+                      const std::vector<cmGeneratorExpressionToken> &tokens);
+
+  void Parse(std::vector<cmGeneratorExpressionEvaluator*> &result);
+
+private:
+  void ParseContent(std::vector<cmGeneratorExpressionEvaluator*> &);
+  void ParseGeneratorExpression(
+                              std::vector<cmGeneratorExpressionEvaluator*> &);
+
+private:
+  std::vector<cmGeneratorExpressionToken>::const_iterator it;
+  const std::vector<cmGeneratorExpressionToken> Tokens;
+  uint NestingLevel;
+};
+
+#endif
diff --git a/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt
index ced21d8..36302db 100644
--- a/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt
+++ b/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt
@@ -1,9 +1,18 @@
 CMake Error at BadAND.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
+    \$<AND>
+
+  \$<AND> expression requires at least one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
     \$<AND:>
 
-  AND requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<AND> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -12,6 +21,24 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\):
 
     \$<AND:,>
 
-  AND requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<AND> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND:01>
+
+  Parameters to \$<AND> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND:nothing>
+
+  Parameters to \$<AND> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/GeneratorExpression/BadAND.cmake b/Tests/RunCMake/GeneratorExpression/BadAND.cmake
index 7926540..265e414 100644
--- a/Tests/RunCMake/GeneratorExpression/BadAND.cmake
+++ b/Tests/RunCMake/GeneratorExpression/BadAND.cmake
@@ -1,4 +1,7 @@
 add_custom_target(check ALL COMMAND check
+  $<AND>
   $<AND:>
   $<AND:,>
+  $<AND:01>
+  $<AND:nothing>
   VERBATIM)
diff --git a/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt
index 7c86b25..1cfbf40 100644
--- a/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt
+++ b/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt
@@ -1,8 +1,44 @@
 CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
+    \$<CONFIG>
+
+  \$<CONFIG> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
     \$<CONFIG:.>
 
   Expression syntax not recognized.
 Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG:Foo,Bar>
+
+  \$<CONFIG> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG:Foo-Bar>
+
+  Expression syntax not recognized.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG:Foo-Nested>
+
+  Expression syntax not recognized.
+Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/GeneratorExpression/BadCONFIG.cmake b/Tests/RunCMake/GeneratorExpression/BadCONFIG.cmake
index 0c13f89..c27ea5f 100644
--- a/Tests/RunCMake/GeneratorExpression/BadCONFIG.cmake
+++ b/Tests/RunCMake/GeneratorExpression/BadCONFIG.cmake
@@ -1,3 +1,7 @@
 add_custom_target(check ALL COMMAND check
+  $<CONFIG>
   $<CONFIG:.>
+  $<CONFIG:Foo,Bar>
+  $<CONFIG:Foo-Bar>
+  $<$<CONFIG:Foo-Nested>:foo>
   VERBATIM)
diff --git a/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt
index 5721f5f..32169c5 100644
--- a/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt
+++ b/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt
@@ -1,9 +1,17 @@
 CMake Error at BadNOT.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
+    \$<NOT>
+
+  \$<NOT> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
     \$<NOT:>
 
-  NOT requires exactly one '0' or '1' value.
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -12,7 +20,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\):
 
     \$<NOT:,>
 
-  NOT requires exactly one '0' or '1' value.
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -21,6 +29,24 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\):
 
     \$<NOT:0,1>
 
-  NOT requires exactly one '0' or '1' value.
+  \$<NOT> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:01>
+
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:nothing>
+
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/GeneratorExpression/BadNOT.cmake b/Tests/RunCMake/GeneratorExpression/BadNOT.cmake
index 452293b..c2dada3 100644
--- a/Tests/RunCMake/GeneratorExpression/BadNOT.cmake
+++ b/Tests/RunCMake/GeneratorExpression/BadNOT.cmake
@@ -1,5 +1,8 @@
 add_custom_target(check ALL COMMAND check
+  $<NOT>
   $<NOT:>
   $<NOT:,>
   $<NOT:0,1>
+  $<NOT:01>
+  $<NOT:nothing>
   VERBATIM)
diff --git a/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt
index 72ef2dd..d4ccab7 100644
--- a/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt
+++ b/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt
@@ -1,9 +1,18 @@
 CMake Error at BadOR.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
+    \$<OR>
+
+  \$<OR> expression requires at least one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
     \$<OR:>
 
-  OR requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<OR> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -12,6 +21,24 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\):
 
     \$<OR:,>
 
-  OR requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<OR> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR:01>
+
+  Parameters to \$<OR> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR:nothing>
+
+  Parameters to \$<OR> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/GeneratorExpression/BadOR.cmake b/Tests/RunCMake/GeneratorExpression/BadOR.cmake
index f16f56a..0813400 100644
--- a/Tests/RunCMake/GeneratorExpression/BadOR.cmake
+++ b/Tests/RunCMake/GeneratorExpression/BadOR.cmake
@@ -1,4 +1,7 @@
 add_custom_target(check ALL COMMAND check
+  $<OR>
   $<OR:>
   $<OR:,>
+  $<OR:01>
+  $<OR:nothing>
   VERBATIM)
diff --git a/bootstrap b/bootstrap
index ec5745d..23134d0 100755
--- a/bootstrap
+++ b/bootstrap
@@ -202,6 +202,9 @@ CMAKE_CXX_SOURCES="\
   cmInstallDirectoryGenerator \
   cmGeneratedFileStream \
   cmGeneratorTarget \
+  cmGeneratorExpressionEvaluator \
+  cmGeneratorExpressionLexer \
+  cmGeneratorExpressionParser \
   cmGeneratorExpression \
   cmGlobalGenerator \
   cmLocalGenerator \

-----------------------------------------------------------------------

Summary of changes:
 Source/CMakeLists.txt                              |    6 +
 Source/cmCustomCommandGenerator.cxx                |    7 +-
 Source/cmGeneratorExpression.cxx                   |  259 +++--------
 Source/cmGeneratorExpression.h                     |   53 ++-
 Source/cmGeneratorExpressionEvaluator.cxx          |  464 ++++++++++++++++++++
 Source/cmGeneratorExpressionEvaluator.h            |  111 +++++
 Source/cmGeneratorExpressionLexer.cxx              |   85 ++++
 Source/cmGeneratorExpressionLexer.h                |   58 +++
 Source/cmGeneratorExpressionParser.cxx             |  235 ++++++++++
 Source/cmGeneratorExpressionParser.h               |   45 ++
 Source/cmTarget.cxx                                |   13 +-
 Source/cmTestGenerator.cxx                         |    8 +-
 .../RunCMake/GeneratorExpression/BadAND-stderr.txt |   31 ++-
 Tests/RunCMake/GeneratorExpression/BadAND.cmake    |    3 +
 .../GeneratorExpression/BadCONFIG-stderr.txt       |   36 ++
 Tests/RunCMake/GeneratorExpression/BadCONFIG.cmake |    4 +
 .../RunCMake/GeneratorExpression/BadNOT-stderr.txt |   32 ++-
 Tests/RunCMake/GeneratorExpression/BadNOT.cmake    |    3 +
 .../RunCMake/GeneratorExpression/BadOR-stderr.txt  |   31 ++-
 Tests/RunCMake/GeneratorExpression/BadOR.cmake     |    3 +
 bootstrap                                          |    3 +
 21 files changed, 1259 insertions(+), 231 deletions(-)
 create mode 100644 Source/cmGeneratorExpressionEvaluator.cxx
 create mode 100644 Source/cmGeneratorExpressionEvaluator.h
 create mode 100644 Source/cmGeneratorExpressionLexer.cxx
 create mode 100644 Source/cmGeneratorExpressionLexer.h
 create mode 100644 Source/cmGeneratorExpressionParser.cxx
 create mode 100644 Source/cmGeneratorExpressionParser.h


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list