// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//                                                                
// This program is free software;  you can redistribute it and/or 
// modify it under the terms of the GNU General Public License as 
// published by the Free Software Foundation; either version 2 of 
// the License, or (at your option) any later version.            
//                                                                
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
// GNU General Public License for more details.                   
//                                                                
// You should have received a copy of the GNU General Public      
// License along with this program; if not, write to the Free     
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
// MA  02111-1307  USA                                            

#include "Phase1.h"
#include "ModelBuilder.h"
#include "PointCutExprParser.h"
#include "ACModel/Utils.h"
#include "PointCutExpr.h"
#include "Naming.h" // TODO: get rid of this dependency
#include "ACBase/StringUtils.h"

using namespace std;
using namespace ACBase;

int Phase1::run () {

  // prepare the project model
  string tunit_filename = _tunit_name;
  int modi_time = _jpm.modification_time(tunit_filename);
  _jpm.setup_tunit(tunit_filename, tunit_len(), modi_time);

  // prepare the parser's scope hierarchy (of the underlying Phase1Sema object)
  init_sema(_jpm);

  // parse the elements of the global scope
  try {
    next_token(); // fetch the first token
    parse_scope (root_scope());
  }
  catch (ParseError) {
    // we simply return an error code if there was a parse error, indicated by an exception
    // the message has been printed on the error stream where the error was detected
    return -1;
  }

#if 0  // TODO: check this for Clang as well
  // special check for tokens after eoi
  PumaToken after_program = curr_token();
  if (after_program) { // error, e.g. too many closing brackets
    err() << sev_error << after_program.location()
        << "tokens after end of program (too many closing brackets?)"
        << endMessage;
  }
#endif

  // root_scope().dump();
  return (err().severity () < sev_error) ? 1 : -1;
}

void Phase1::skip_block (int open, int close, bool inclusive) {
  int token;
  int depth = 0;
  ACToken open_token = curr_token ();

  while (curr_token ().type () != ACToken::TOK_EOF) {
    token = curr_token ().type ();

    if (token == open)
      depth++;
    else if (token == close)
      depth--;
    else if (depth > 0 && token == ACToken::TOK_OPEN_ROUND) {
      skip_round_block ();
      continue;
    }
    else if (token == ACToken::TOK_ADVICE || token == ACToken::TOK_SLICE || token == ACToken::TOK_POINTCUT) {
      err() << sev_error << curr_token().location() << "keyword unexpected in this context" << endMessage;
      throw ParseError();
    }

    if (depth == 0 && !inclusive)
      break;

    next_token ();

    if (depth == 0)
      break;
  }

  if (depth > 0 && curr_token ().type () == ACToken::TOK_EOF) {
    err() << sev_error << open_token.location() << "missing closing bracket" << endMessage;
    throw ParseError();
  }
}

void Phase1::skip_round_block (bool inclusive) {
  skip_block (ACToken::TOK_OPEN_ROUND, ACToken::TOK_CLOSE_ROUND, inclusive);
}

void Phase1::skip_curly_block (bool inclusive) {
  skip_block (ACToken::TOK_OPEN_CURLY, ACToken::TOK_CLOSE_CURLY, inclusive);
}

void Phase1::skip_square_block (bool inclusive) {
  skip_block (ACToken::TOK_OPEN_SQUARE, ACToken::TOK_CLOSE_SQUARE, inclusive);
}

void Phase1::skip_template_params () {
  int depth = 0;
  next_token ();

  bool stop = false;
  while (!stop && curr_token ().type () != ACToken::TOK_EOF) {
    int token = curr_token ().type ();

    switch (token) {
      case ACToken::TOK_SEMI_COLON:
      case ACToken::TOK_CLOSE_ROUND:
      case ACToken::TOK_CLOSE_SQUARE:
      case ACToken::TOK_CLOSE_CURLY:
        stop = true;
        break;
      case ACToken::TOK_OPEN_ROUND:
        skip_round_block ();
        continue;
      case ACToken::TOK_OPEN_CURLY:
        skip_curly_block ();
        continue;
      case ACToken::TOK_OPEN_SQUARE:
        skip_block (ACToken::TOK_OPEN_SQUARE, ACToken::TOK_CLOSE_SQUARE);
        continue;
      case ACToken::TOK_LESS:
        depth++;
        break;
      case ACToken::TOK_GREATERGREATER:
        // TODO: This case should only be handled in C++1x and newer, but
        //       we have no easy way to determine the language version.
        if (depth == 0) {
          next_token ();
          stop = true;
          break;
        } else {
          depth--;
        }
        // fall through!!!
      case ACToken::TOK_GREATER:
        if (depth == 0) {
          next_token ();
          stop = true;
        } else {
          depth--;
        }
        break;
      default:
        break;
    }

    if (!stop)
      next_token ();
  }
}

bool Phase1::is_attribute_token (const ACToken &token, const ACToken &next) const {
  return is_gnu_attribute_token(token) || is_cxx11_attribute_token(token, next);
}

bool Phase1::is_cxx11_attribute_token (const ACToken &token, const ACToken &next) const {
  return token.type() == ACToken::TOK_OPEN_SQUARE &&
      next.type() == ACToken::TOK_OPEN_SQUARE;
}

bool Phase1::is_gnu_attribute_token (const ACToken &token) const {
  return token.type () == ACToken::TOK_ATTRIBUTE ||
      token.type () == ACToken::TOK_ALIGNAS;
}

void Phase1::skip_cxx11_attributes (bool inclusive) {
  while (is_cxx11_attribute_token(curr_token (), look_ahead ())) {
    skip_square_block(false);
    if (!is_cxx11_attribute_token(look_ahead (), look_ahead(2)))
      break;
    next_token ();
  }
  if (inclusive)
    next_token ();
}

void Phase1::skip_attributes (bool inclusive) {
  while (true) {
    if (is_gnu_attribute_token(curr_token()))
      skip_gnu_attributes(false);
    else if (is_cxx11_attribute_token(curr_token(), look_ahead()))
      skip_cxx11_attributes(false);
    else
      return;
    if (!is_attribute_token(look_ahead(1), look_ahead(2))) {
      if (inclusive)
        next_token();
      return;
    }
    next_token();
  }
}

void Phase1::skip_gnu_attributes (bool inclusive) {
  while (is_gnu_attribute_token(curr_token ())) {
    next_token();
    skip_round_block(false);
    if (!is_gnu_attribute_token(look_ahead ()))
      break;
    next_token ();
  }
  if (inclusive)
    next_token ();
}

void Phase1::set_slice_tokens (TU_ClassSlice *slice,
                               ACPreprocessor::TokenVector &pattern) {

  // Analyze the tokens.
  bool has_base_intro = false;
  bool has_member_intro = false;
  for (unsigned j = 0; j != pattern.size(); ++j) {
    if (pattern[j].type () == ACToken::TOK_SEMI_COLON)
      break;
    if (pattern[j].type () == ACToken::TOK_COLON)
      has_base_intro = true;
    else if (pattern[j].type () == ACToken::TOK_OPEN_CURLY) {
      has_member_intro = true;
      break;
    }
  }

  // Generate the base intro.
  // TODO: What about replacements in base intros?
  //       Example: ": public SomeBase<TargetName>"
  TU_ClassSlice::SliceBody base_intro;
  if (has_base_intro) {
    unsigned i = 1;
    while (i < pattern.size() && pattern[i].type () != ACToken::TOK_COLON)
      ++i;
    ++i; // skip ':'
    while (i < pattern.size() &&
        pattern[i].type () != ACToken::TOK_SEMI_COLON &&
        pattern[i].type () != ACToken::TOK_OPEN_CURLY) {
      base_intro.text += ' ';
      base_intro.text += token_text (pattern[i]);
      if (!base_intro.file.is_valid()) {
        base_intro.file = source_unit(pattern[i]);
        base_intro.line = token_line_number(pattern[i]);
      }
      ++i;
    }
  }

  // Generate the (inline) member intro
  TU_ClassSlice::SliceBody member_intro;
  if (has_member_intro) {
    unsigned i = 0;
    while (pattern[i].type () != ACToken::TOK_OPEN_CURLY)
      ++i;
    ++i;
    member_intro.file = source_unit(pattern[i]);
    member_intro.line = token_line_number(pattern[i]);
    unsigned line = member_intro.line;
    int level = 1;
    while (true) {
      if (pattern[i].type () == ACToken::TOK_CLOSE_CURLY) {
        level--;
        if (level == 0)
          break;
      }
      else if (pattern[i].type () == ACToken::TOK_OPEN_CURLY)
        level++;

      // insert whitespace or newline for proper formatting
      whitespace (line, pattern[i], member_intro.text);

      // TODO: this is a hack - make sure not to replace too often!
      if (level == 1 && pattern[i].type () == ACToken::TOK_ID &&
          slice->get_name() == pattern[i].text ())
        member_intro.positions.push_back(
            std::make_pair(member_intro.text.size(),
                TU_ClassSlice::SliceBody::TARGET_NAME));
      else if (pattern[i].type () == ACToken::TOK_ID &&
          string ("JoinPoint") == pattern[i].text ()) {
        member_intro.positions.push_back(
            std::make_pair(member_intro.text.size(),
                TU_ClassSlice::SliceBody::JP_NAME));
      }
      else
        member_intro.text += token_text (pattern[i]);
      ++i;
    }

    // Sort positions so we can easily do the insertions later.
    // TODO: Aren't those always sorted anyways?
    std::sort(member_intro.positions.begin(), member_intro.positions.end());
  }

  slice->set_tokens (member_intro, base_intro, has_base_intro, has_member_intro);
}

void replace_string_in_place (string& subject, const string& search, const string& replace) {
  size_t pos = 0;
  while ((pos = subject.find(search, pos)) != std::string::npos) {
    subject.replace(pos, search.length(), replace);
    pos += replace.length();
  }
}

void Phase1::parse_scope (Scope &scope, string *anon_member_class_copy) {
  static string prot_none("");
  static string prot_private("private");
  static string prot_protected("protected");
  static string prot_public("public");

  NewScope in_new_scope(&scope, *this); // RAII idiom; enter new scope and restore old in destructor

  bool old_in_template      = _in_template;
  bool in_friend            = false;
  bool in_slice             = false;
  bool in_slice_initializer = false;
  enum { TD_FLOATING, TD_GOT_ID, TD_GOT_TYPEDEF, TD_GOT_ALL } _td_state = TD_FLOATING;
  Scope *class_alias_target = 0;
  string *prot = &prot_none;
  if (scope.is_class() || scope.is_aspect())
    prot = &prot_private;
  else if (scope.is_struct())
    prot = &prot_public;
  bool is_anon_class = false;
  // Whether the construct we are currently parsing (union, ...) has static/internal
  // linkage
  bool has_static_linkage = false;
  // The static/internal linkage storage class specifier token (necessary, because
  // the token may get removed later)
  ACToken static_linkage_token;
  // Does the current construct creates a variable inside the current scope? (e.g.
  // "class { int i; } c;" creates variable c and "union { int i; };" does not
  // create a variable)
  bool is_creating_variable = false;
  ACToken anon_class_def_token;
  string anon_class_name;  // generated name for anon class
  string anon_class_alias; // as in "typedef struct { ... } alias_name;"
  ACToken start_token;

  // if we are in the definition of an anonymous class/struct/union it might be
  // that we are parsing the definition of an anonymous member. In this case we
  // need to create a modified copy of the code and thus start recording here.
  if (anon_member_class_copy)
    start_recording ();

  ACToken token = curr_token ();
  while (token.type () != ACToken::TOK_EOF &&
      token.type () != ACToken::TOK_CLOSE_CURLY) {

    int tt = token.type ();
    // handle sub-scope
    if (tt == ACToken::TOK_TEMPLATE) {
      // A declaration of a template class or function
      token = next_token ();
      if (token.type () != ACToken::TOK_EOF && token.type () == ACToken::TOK_LESS) {
        skip_template_params();
        token = curr_token();
        _in_template = true;
      }
    }
    else if (tt == ACToken::TOK_ENUM) { // handle C++ 11 enum classes
      token = next_token();
      if (token.type () == ACToken::TOK_CLASS)
        token = next_token(); // simply skip the class keyword if there is one
    }
    else if (tt == ACToken::TOK_PUBLIC) {
      token = next_token();
      if (token.type () == ACToken::TOK_COLON)
        prot = &prot_public;
      token = next_token();
    }
    else if (tt == ACToken::TOK_PROTECTED) {
      token = next_token();
      if (token.type () == ACToken::TOK_COLON)
        prot = &prot_protected;
      token = next_token();
    }
    else if (tt == ACToken::TOK_PRIVATE) {
      token = next_token();
      if (token.type () == ACToken::TOK_COLON)
        prot = &prot_private;
      token = next_token();
    }
    else if (scope.is_aspect() &&
        ((tt == ACToken::TOK_STATIC &&
        look_ahead ().type () == ACToken::TOK_ID &&
        scope._name == look_ahead().text ()) ||
        (tt == ACToken::TOK_ID && scope._name == token.text () &&
        look_ahead().type () == ACToken::TOK_STATIC))) {
      parse_aspectof_function (*prot);
      token = next_token ();
    }
    else if (tt == ACToken::TOK_STATIC) {
      // If the scope, we are in, is a namespace, and the current token
      // is "static" then the following construct has static/internal
      // linkage.
      if(scope.is_namespace()) {
        int type_of_next_token = look_ahead().type();
        // Ignore functions, normal variables (without a class/... definition inside
        // the variable declaration), ... because they are not considered/changed
        // during phase1 anyway. This makes resetting has_static_linkage easier.
        if (type_of_next_token == ACToken::TOK_CLASS ||
            type_of_next_token == ACToken::TOK_UNION ||
            type_of_next_token == ACToken::TOK_STRUCT) {
          has_static_linkage = true;
          // Remeber the "static" token, because we will maybe remove it later.
          static_linkage_token = token;
        }
      }
      token = next_token();
    }
    else if (tt == ACToken::TOK_TYPEDEF) {
      switch (_td_state) {
      case TD_GOT_TYPEDEF:
      case TD_GOT_ALL:
        err () << sev_error << token.location()
            << "'typedef' specifier used twice." << endMessage;
        break;
      case TD_GOT_ID:
        _td_state = TD_GOT_ALL;     break;
      case TD_FLOATING:
        _td_state = TD_GOT_TYPEDEF; break;
      }
      token = next_token();
    }
    else if (_td_state == TD_GOT_ALL && tt == ACToken::TOK_ID) {
      if (look_ahead(1).type () == ACToken::TOK_SEMI_COLON && class_alias_target) {
        create_class_alias(token.text(), class_alias_target);
      }
      token = next_token();
    }
    else if (tt == ACToken::TOK_FRIEND) {
      in_friend = true;
      token = next_token();
    }
    else if (tt == ACToken::TOK_POINTCUT) {
      parse_pointcut_def ();
      token = next_token ();
    }
    else if (tt == ACToken::TOK_ATTRDECL && _conf.attributes()) {
      parse_attribute_decl();
      token = next_token();
    }
    else if (is_cxx11_attribute_token(token, look_ahead())) {
      skip_cxx11_attributes();
      token = curr_token();
    }
    else if (scope.is_namespace() && tt == ACToken::TOK_USING) {
      parse_using_directive_or_declaration();
      token = next_token ();
    }
    else if (tt == ACToken::TOK_ADVICE) {
      parse_advice_def (*prot);
      token = next_token ();
    }
    else if (tt == ACToken::TOK_SLICE) {
      in_slice = true;
      start_token = token;
      start_recording ();
      token = next_token ();
    }
    else if (tt == ACToken::TOK_INLINE && look_ahead().type() == ACToken::TOK_NAMESPACE) {
      token = next_token();
      parse_namespace(true);
      token = next_token();
    }
    else if (tt == ACToken::TOK_NAMESPACE) {
      parse_namespace(false);
      token = next_token();
    }
    else if (!_in_template && !in_friend &&
        (tt == ACToken::TOK_ASPECT ||
        tt == ACToken::TOK_CLASS ||
        tt == ACToken::TOK_UNION ||
        tt == ACToken::TOK_STRUCT)) {
      bool is_struct    = (tt == ACToken::TOK_STRUCT);
      bool is_class     = (tt == ACToken::TOK_CLASS);
      bool is_aspect    = (tt == ACToken::TOK_ASPECT);
      bool is_union     = (tt == ACToken::TOK_UNION);
      anon_class_alias.clear();
      string pre_attr;
      // A namespace sub-scope?
      ACToken start = token;
      token = next_token ();

      // skip __attribute(...)
      if (is_attribute_token(token, look_ahead())) {
        // stop recording
        if (anon_member_class_copy) {
          ACPreprocessor::TokenVector rec = stop_recording ();
          string result = recording_to_string (rec);
          *anon_member_class_copy += result;
        }
        start_recording();
        skip_attributes(false);
        ACPreprocessor::TokenVector rec = stop_recording ();
        pre_attr = recording_to_string (rec);
        if (anon_member_class_copy) {
          *anon_member_class_copy += pre_attr;
          start_recording();
        }
        token = next_token();
      }

      bool root_qualified, contains_template_id;
      vector<string> names;
      parse_qual_name (names, root_qualified, contains_template_id);
      token = curr_token ();
      bool is_qualified = (root_qualified || names.size () > 1);
      string name;
      if (names.size () > 0)
        name = names[names.size () - 1];
      else {
        is_anon_class = false;
        if (in_slice) {
          name = anon_slice_name();
          err () << sev_error << start.location()
              << "Anonymous slice definition only allowed in advice." << endMessage;
        }
        else {
          name = Phase1Sema::anon_class_name();
          if (is_aspect)
            err () << sev_error << start.location()
                << "Anonymous aspects are not allowed." << endMessage;
          else {
            if (is_in_project(start)) {
              is_anon_class = true;
              anon_class_def_token = token;
              anon_class_name = name;
            }
          }
        }
      }

      Node *found_scope = 0;
      if (is_qualified)
        found_scope = lookup_name(scope, root_qualified, names);
      Scope *qual_scope = lookup_scope(scope, root_qualified, names);
      // TODO: handle other cases here (errors!)

      // check if this scope belongs to our project
      bool skip = (is_qualified && !found_scope && in_slice);
      bool in_model = true;
      bool is_declaration = (token.type () == ACToken::TOK_SEMI_COLON);
      Scope *inserted_scope = 0;

      // skip a 'final' token if there is one after the class name
      if (!is_declaration && token.type() == ACToken::TOK_ID && strcmp(token.text(), "final") == 0)
        token = next_token();

      if (!skip && (token.type () == ACToken::TOK_OPEN_CURLY || token.type () == ACToken::TOK_COLON ||
          is_declaration)) {
        if (!is_in_project(start) || !is_visible_scope(scope, name) ||
            !scope._jpm_link || starts_with(name, "<anon-") || starts_with(name, "__ac_anon")) {
          // TODO: shall we really filter-out anonymous classes?
          in_model = false;
        }

        // create new scope object
        Node::Kind kind = Node::UNKNOWN_KIND;
        switch (start.type ()) {
          case ACToken::TOK_ASPECT:    kind = Node::SCOPE_ASPECT; break;
          case ACToken::TOK_CLASS:     kind = Node::SCOPE_CLASS; break;
          case ACToken::TOK_STRUCT:    kind = Node::SCOPE_STRUCT; break;
          case ACToken::TOK_UNION:     kind = Node::SCOPE_UNION; break;
        }
        inserted_scope = create_new_scope(name, kind, qual_scope, names, in_model, in_slice);
      }

      // handle class alias typedef state machine
      if (!qual_scope && inserted_scope &&
          (_td_state == TD_FLOATING || _td_state == TD_GOT_TYPEDEF)) {
        _td_state = (_td_state == TD_FLOATING ? TD_GOT_ID : TD_GOT_ALL);
        class_alias_target = inserted_scope;
      }

      // stop recording after the classname
      if (anon_member_class_copy &&
          (token.type () == ACToken::TOK_COLON || token.type () == ACToken::TOK_OPEN_CURLY)) {
        ACPreprocessor::TokenVector rec = stop_recording ();
        string result = recording_to_string (rec);
        *anon_member_class_copy += result;
      }

      bool is_definition = false;
      if (token.type () != ACToken::TOK_EOF && token.type () == ACToken::TOK_COLON) {
        is_definition = true;
        if (in_slice) {
          // consume tokens until we find '{' or syntax error
          do {
            token = next_token();
          } while (token.type () != ACToken::TOK_EOF && token.type () != ACToken::TOK_OPEN_CURLY
              && token.type () != ACToken::TOK_SEMI_COLON);
        }
        else if (inserted_scope &&
            (inserted_scope->is_class_or_struct() || inserted_scope->is_aspect())) {
          // list of base classes
          parse_base_clause (*inserted_scope);
          if (in_model) register_base_classes (*inserted_scope);
          token = curr_token();
        }
      }

      if (token.type () != ACToken::TOK_EOF && token.type () == ACToken::TOK_OPEN_CURLY) {
        is_definition = true;
        if (in_slice) {
          skip_curly_block ();
          token = curr_token ();
        }
        else {
          // parse the nested scope
          next_token ();
          if (is_anon_class) {
            string nested_anon_member_class_copy;
            parse_scope (*inserted_scope, &nested_anon_member_class_copy);

            if (curr_token().type () != ACToken::TOK_EOF && (is_struct || is_class || is_union)) {
              // skip __attribute(...)
              string post_attr;
              if (is_gnu_attribute_token(look_ahead ())) {
                next_token ();
                start_recording (); // record attribute while being lexed
                skip_gnu_attributes(false);
                ACPreprocessor::TokenVector rec = stop_recording ();
                post_attr = recording_to_string (rec);
              }

              // Check whether we have found the definition of an unnamed strcut/union field, such as in
              // class C { struct { int i; }; } c; <-- here 'i' is visible in 'C'!
              // => check whether ';' follows '}' ...
              if (look_ahead().type () == ACToken::TOK_SEMI_COLON) {
                if (anon_member_class_copy)
                  *anon_member_class_copy += nested_anon_member_class_copy;

                // Transform the code: insert a copy of the original anonymous class behind the
                // de-anonymized class
                string tag;
                if (is_struct)
                  tag = "struct";
                else if (is_union)
                  tag = "union";
                else if (is_class)
                  tag = "class";

                string storage_class_specifier;
                if(has_static_linkage) {
                  storage_class_specifier = "static ";
                }

                replace_string_in_place (nested_anon_member_class_copy,
                    full_anon_class_name(scope) + "::", "");
                // Insert copy:
                _code_weaver.transform_anon_member_class (look_ahead(),
                    storage_class_specifier + tag + pre_attr
                    + " { " + nested_anon_member_class_copy + " }"
                    + post_attr + ";");
              }
              else {
                // Check for an id and a semicolon (or '=')
//                cout << _td_state << ":" << token_text(look_ahead()) << ":" << token_text(look_ahead(2)) << endl;
                // TODO: handle more (exotic) cases
                if (look_ahead().type() == ACToken::TOK_ID &&
                    (look_ahead(2).type() == ACToken::TOK_SEMI_COLON ||
                     look_ahead(2).type() == ACToken::TOK_ASSIGN)) {
                  if(_td_state == TD_GOT_ALL) {
                    // Found definition of class alias
                    anon_class_alias = look_ahead().text();
                  }
                  else {
                    // Found variable creation
                    is_creating_variable = true;
                  }
                }
                if (anon_member_class_copy) {
                  anon_member_class_copy->resize (anon_member_class_copy->size () - 1);
                  *anon_member_class_copy += full_anon_class_name(*inserted_scope);
                  next_token ();
                }
              }
            }
          }
          else {
            parse_scope (*inserted_scope);
          }
          if (curr_token ().type() == ACToken::TOK_CLOSE_CURLY &&
              (is_class || is_struct || is_aspect)) {
            // TODO: insert proper #line directive so that syntax errors are correctly displayed
//            _code_weaver.insert(_code_weaver.weave_pos(curr_token(), WeavePos::WP_BEFORE), "\n#pragma joinpoint\n");
          }

          // and start recording again
          if (anon_member_class_copy)
            start_recording ();
        }
        if (!skip && (in_model || in_slice)) {
          if (curr_token ().type() == ACToken::TOK_EOF)
            err () << sev_error << start.location()
            << "Scope '" << inserted_scope->_name.c_str() << "' not closed properly" << endMessage;
          else {
            if (inserted_scope->_jpm_link)
              _jpm.add_source_loc(inserted_scope->_jpm_link, start, curr_token (),
                  (inserted_scope->is_namespace() ? SLK_NONE : SLK_DEF));
          }
        }
      }
      else {
        if (inserted_scope && inserted_scope->_jpm_link && (in_model || in_slice)) {
          _jpm.add_source_loc(inserted_scope->_jpm_link, start, curr_token (), SLK_DECL);
        }
      }

      if (!skip && is_definition && qual_scope && !found_scope)
        err () << sev_error << start.location()
          << "Definition of undeclared scope '" << name.c_str() << "'." << endMessage;

      ACPreprocessor::TokenVector rec;
      if (in_slice) {
        in_slice = false;
        rec = stop_recording ();
      }
      if (inserted_scope && inserted_scope->_is_slice) {
        if (is_definition) {
          TU_ClassSlice *cs = (TU_ClassSlice*)inserted_scope->_jpm_link;
          cs->slice_unit(source_unit(start));
          if (!cs->get_tokens ().text.empty())
            cout << "tokens OVERWRITTEN" << endl;
          set_slice_tokens(cs, rec);
        }
        _code_weaver.transform_delete(start_token, curr_token ());
      }
      else if (!rec.empty ()) {
        handle_slice_member(rec, start_token, curr_token());
      }
      token = next_token ();
    }
    else if (in_slice && tt == ACToken::TOK_ASSIGN) {
      in_slice_initializer = true;
      token = next_token ();
    }
    else if (tt == ACToken::TOK_OPEN_CURLY) {
      skip_curly_block (false);
      if (_in_template) {
        // TODO: insert proper #line directive so that syntax errors are correctly displayed
//        _code_weaver.insert(_code_weaver.weave_pos(curr_token(), WeavePos::WP_BEFORE), "\n#pragma joinpoint\n");
      }
      if (in_slice && !in_slice_initializer) {
        in_slice = false;
        ACPreprocessor::TokenVector rec = stop_recording ();
        if (!rec.empty ())
          handle_slice_member(rec, start_token, curr_token());
      }
      _in_template = old_in_template;
      in_friend = false;
      token = next_token ();
    }
    else if (tt == ACToken::TOK_SEMI_COLON) {
      if (in_slice) {
        ACPreprocessor::TokenVector rec = stop_recording ();
        if (!rec.empty ())
          handle_slice_member(rec, start_token, curr_token());
        in_slice = false;
        in_slice_initializer = false;
      }
      _in_template = old_in_template;
      token = next_token ();
      in_friend = false;
      if (is_anon_class) {
        // We finished parsing an anonymous class/union/struct. De-anonymize it:
        if (anon_class_alias.empty())
          _code_weaver.transform_anon_class(anon_class_def_token, anon_class_name);
        else
          _code_weaver.transform_anon_class(anon_class_def_token, anon_class_alias);
        anon_class_alias.clear();

        if(has_static_linkage && !is_creating_variable) {
          // Because the anonymous class/union/struct was de-anonymized (by adding a
          // name), and because there is no variable created, the static/internal linkage
          // specifier is no longer allowed. Remove it:
          _code_weaver.kill(_code_weaver.weave_pos(static_linkage_token, WeavePos::WP_BEFORE),
                            _code_weaver.weave_pos(static_linkage_token, WeavePos::WP_AFTER));
        }
      }
      // Finished parsing of construct. Reset state (because beginning with the next token we
      // will look at the next construct inside the current scope):
      is_anon_class = false;
      has_static_linkage = false;
      is_creating_variable = false;
      _td_state = TD_FLOATING;
    }
    else if (tt == ACToken::TOK_COLON_COLON || tt == ACToken::TOK_ID) {
      bool root_qualified, contains_template_id;
      vector<string> names;
      parse_qual_name (names, root_qualified, contains_template_id);
      token = curr_token();
      if (_td_state == TD_GOT_ID || _td_state == TD_GOT_ALL ||
          contains_template_id || names.size () == 0)
        _td_state = TD_FLOATING;
      else {
        Node *found = lookup_name(scope, root_qualified, names);
        if (found && (found->is_class_or_struct() || found->is_aspect())) {
          _td_state = (_td_state == TD_FLOATING ? TD_GOT_ID : TD_GOT_ALL);
          class_alias_target = (Scope*)found;
        }
        else
          _td_state = TD_FLOATING;
      }
    }
    else {
      if (_td_state != TD_FLOATING && tt != ACToken::TOK_CONST && tt != ACToken::TOK_VOLATILE)
        _td_state = TD_FLOATING;
      token = next_token ();
    }
  }

  if (anon_member_class_copy) {
    ACPreprocessor::TokenVector rec = stop_recording ();
    string result = recording_to_string (rec);
    result.resize (result.size () - 1); // delete trailing '}'
    *anon_member_class_copy += result;

  }
}

void Phase1::parse_namespace(bool is_inline) {
  Scope &scope = curr_scope();
  ACToken start = curr_token();

  // skip 'namespace' token
  next_token ();

  // skip optional '__attribute(...)' or '[[attr]]'
  skip_attributes(true);
  ACToken name_token = curr_token();
  ACToken token = name_token;

  // parse name or qualified name
  bool root_qualified, contains_template_id;
  vector<string> names;
  parse_qual_name (names, root_qualified, contains_template_id);
  token = curr_token ();
  bool is_qualified = (root_qualified || names.size () > 1);
  string name = (names.size () > 0 ? names[names.size () - 1] : "<unnamed>");

  // skip optional '__attribute(...)'
  if (is_gnu_attribute_token(token)) {
    skip_gnu_attributes(false);
    token = next_token();
  }
  
  if (token.type () == ACToken::TOK_EOF) {
    err () << sev_error << start.location()
        << "Unexpected end of file in declaration of namespace '" << name.c_str() << "'" << endMessage;
    return;
  }

  // check if this definition belongs to our project
  bool in_model = true;
  if (!is_in_project(start) || !is_visible_scope(scope, name) || !scope._jpm_link)
    in_model = false;

  if (token.type () == ACToken::TOK_OPEN_CURLY) {

    // search for the given namespace name
    Scope *qual_scope = lookup_scope(scope, root_qualified, names);

    // create new namespace scope object; works also if the scope already exists
    Scope *inserted_scope = create_new_scope(name, Node::SCOPE_NAMESPACE, qual_scope, names, in_model, false);

    // if this is an inline namespace, add it to the search scopes of the enclosing scope
    if (is_inline)
      scope._search_scopes.push_back (inserted_scope);

    // parse the nested scope
    next_token ();
    parse_scope (*inserted_scope);
    if (curr_token ().type() == ACToken::TOK_EOF)
      err () << sev_error << start.location()
      << "Namespace '" << inserted_scope->_name.c_str() << "' not closed properly" << endMessage;
    else {
      if (in_model && inserted_scope && inserted_scope->_jpm_link)
        _jpm.add_source_loc(inserted_scope->_jpm_link, start, curr_token (), SLK_NONE);
    }
  }
  else if (token.type () == ACToken::TOK_ASSIGN) {

    // search for the given namespace alias name
    const Node *found_element = lookup_unqualified_name(scope, name);

      // skip the '=' token
    token = next_token();
    if (is_qualified) {
      err () << sev_error << token.location()
      << "Qualified name not allowed in namespace alias definition" << endMessage;
    }
    else if (found_element && !found_element->is_namespace_alias()) {
      err () << sev_error << token.location()
      << "Namespace alias '" << name.c_str() << "' redeclared as different kind of entity"<< endMessage;
    }
    else {
      bool target_root_qualified, target_contains_template_id;
      vector<string> target_names;
      parse_qual_name (target_names, target_root_qualified, target_contains_template_id);
      token = curr_token ();
      if (target_names.size () == 0) {
        err () << sev_error << name_token.location()
        << "Namespace alias '" << name.c_str() << "' has no valid target namespace"<< endMessage;
      }
      else {
        Node *target_entity = lookup_name(scope, target_root_qualified, target_names);
        if (!target_entity || !target_entity->is_namespace()) {
          err () << sev_error << token.location()
          << "Alias target '" << target_names[target_names.size () - 1].c_str() << "' is not a namespace"<< endMessage;
        }
        else if (found_element && ((Element*)found_element)->_referred != target_entity) {
          err () << sev_error << name_token.location()
          << "Definition of namespace alias '" << name.c_str() << "' conflicts with previous definition"<< endMessage;
        }
        else if (!found_element) {
          create_namespace_alias(name, (Scope*)target_entity);
        }
      }
    }
  }
  else {
    err () << sev_error << token.location()
        << "Syntax error in declaration of namespace '" << name.c_str() << "'" << endMessage;
  }
}

string Phase1::recording_to_string (const ACPreprocessor::TokenVector &vec) {
  string result;
  if (vec.size () > 0) {
    unsigned last_line = token_line_number(vec[0]);
    for (unsigned i = 0; i < vec.size(); i++) {
      unsigned line = token_line_number(vec[i]);
      bool new_line = false;
      while (line > last_line) {
        result += "\n";
        last_line++;
        new_line = true;
      }
      if (!new_line) result += " ";
      result += token_text (vec[i]);
    }
  }
  return result;
}

// Create a SliceBody structure out of the tokens in pattern.
TU_ClassSlice::SliceBody
Phase1::format_non_inline_member (ACPreprocessor::TokenVector &pattern,
                                  const std::string &name) {
  TU_ClassSlice::SliceBody res;

  int name_tokens = 0;
  unsigned line = token_line_number(pattern[0]);
  res.line = line;
  res.file = source_unit(pattern[0]);
  // skip 'slice' keyword
  for (unsigned i = 1; i < pattern.size(); ) {
    ACToken token = pattern[i];
    unsigned next = i+1;
    if (name_tokens == 0 && next < pattern.size() &&
        pattern[next].type() == ACToken::TOK_INVALID) {
      name_tokens = 1;
      next = i+2;
    }
    else if (name_tokens > 0 && token.type() == ACToken::TOK_INVALID) {
      if (res.text.size () > 0)
        res.text += ' '; // don't change the line number here
      res.positions.push_back(std::make_pair(res.text.size(),
                                   TU_ClassSlice::SliceBody::TARGET_QUAL_NAME));
      name_tokens = 0;
      if (next < pattern.size() && pattern[next].type () == ACToken::TOK_COLON_COLON) {
        token = pattern[next];
        ++next;
        res.text += token_text (token);
        if (next < pattern.size() && pattern[next].type () == ACToken::TOK_TILDE) {
          token = pattern[next];
          ++next;
          res.text += token_text (token);
        }
        if (next < pattern.size() && pattern[next].type () == ACToken::TOK_ID) {
          token = pattern[next];
          ++next;
          if (name == token.text()) {
            res.positions.push_back(std::make_pair(res.text.size(),
                                        TU_ClassSlice::SliceBody::TARGET_NAME));
          }
          else
            res.text += token_text (token);
        }
      }
    }
    else {
      if (name_tokens == 0) {
        // insert whitespace or newline for proper formatting
        whitespace (line, token, res.text);
        if (token.type () == ACToken::TOK_ID &&
            string ("JoinPoint") == token.text ()) {
          res.positions.push_back(std::make_pair(res.text.size(),
                                            TU_ClassSlice::SliceBody::JP_NAME));
        }
        else {
          res.text += token_text (token);
        }
      }
      else
        name_tokens++;
    }
    i = next;
  }
  res.text += "\n";

  // Sort positions so we can easily do the insertions later.
  // TODO: Aren't those always sorted anyways?
  std::sort(res.positions.begin(), res.positions.end());
  return res;
}

// insert whitespace or newline for proper formatting
void Phase1::whitespace (unsigned &line, const ACToken &token, string &str) {
  unsigned new_line = token_line_number(token);
  if (!token.is_macro_generated () && line != new_line) {
    if (new_line > line)
      str.append(new_line - line, '\n');
    line = new_line;
  }
  else {
    if (str.size () > 0)
      str += ' ';
  }
}

void Phase1::handle_slice_member (ACPreprocessor::TokenVector &rec, ACToken from, ACToken to) {
  Scope *slice_scope = find_slice(rec);
  if (slice_scope && slice_scope->_jpm_link) {
    TU_ClassSlice *s = (TU_ClassSlice*)slice_scope->_jpm_link;
    s->non_inline_members().push_back (
        format_non_inline_member (rec, s->get_name ())
        );
    s->non_inline_member_units().push_back (source_unit(from));
  }
  else {
    err () << sev_error << from.location()
        << "Slice member definition cannot be associated with slice definition" << endMessage;
  }
  _code_weaver.transform_delete(from, to);
}

void Phase1::parse_aspectof_function (const string &prot) {
  ACToken start = curr_token ();
  next_token ();
  next_token ();
  if (curr_token ().type() != ACToken::TOK_MUL)
    return;
  next_token ();
  if (curr_token ().type() != ACToken::TOK_ID)
    return;
  ACToken name = curr_token ();
  string text = name.text ();
  if (text != "aspectOf" && text != "aspectof")
    return;
  ACToken arg_begin = next_token ();
  skip_round_block(false);
  next_token ();
  ACToken body_begin = curr_token ();
  AdviceCodeContext context = ACC_NONE;
  CodeWeaver::TypeUse uses_type;
  parse_advice_body( context, ACToken::TOK_OPEN_CURLY, ACToken::TOK_CLOSE_CURLY, uses_type, true);
  ACToken body_end = curr_token ();
  _code_weaver.transform_aspectof( context, uses_type, start, name, arg_begin, body_begin, body_end, prot );
}

void Phase1::parse_using_directive_or_declaration() {
  Scope &scope = curr_scope();
  ACToken token = next_token ();
  bool is_using_directive = (token.type() == ACToken::TOK_NAMESPACE);
  if (is_using_directive)
    token = next_token ();

  ACToken name_start = token;
  bool root_qualified, contains_template_id;
  vector<string> names;
  parse_qual_name (names, root_qualified, contains_template_id);
  if (names.empty()) {
    err() << sev_error << name_start.location()
        << "Invalid name in 'using directive' or 'using declaration'." << endMessage;
    return;
  }
  string name = names.back();

  // handle operator names
  token = curr_token();
  if (token.type() == ACToken::TOK_OPERATOR) {
    while (token.type() != ACToken::TOK_SEMI_COLON &&
        token.type() != ACToken::TOK_EOF && token.type() != ACToken::TOK_CLOSE_CURLY)
      token = next_token ();
    return;
  }

  Node *found_scope = lookup_name(scope, root_qualified, names);

  // Handle attributes for alias declarations and GNU-style using directives
  // TODO: Can we simply skip attributes here?
  if (is_attribute_token(curr_token(), look_ahead()))
    skip_attributes(true);
  token = curr_token();

  if (!is_using_directive && token.type () == ACToken::TOK_ASSIGN) {
    // TODO: C++11 type aliases are not supported, yet
    //       Example: using NewName = Oldname<42>;
    return;
  }
  if (token.type () != ACToken::TOK_SEMI_COLON) {
    err() << sev_error << token.location() 
        << "Expected ';' after name in 'using directive' or 'using declaration'." << endMessage;
    return;
  }
  if(is_using_directive) {
    if (!found_scope || !found_scope->is_namespace()) {
      err() << sev_error << name_start.location()
          << "'" << name.c_str() << "' is not a known namespace." << endMessage;
      return;
    }
    scope._search_scopes.push_back ((Scope*)found_scope);
  }
  else {
    // This is a 'using declaration', e.g. 'using Scope::SubScope::SomeClass;'.
    // => create an alias for SomeClass in the current scope
    if (!found_scope || !(found_scope->is_class_or_struct() || found_scope->is_aspect()))
      return;
    create_class_alias(name, (Scope*)found_scope);
  }
}

void Phase1::parse_pointcut_def () {
  ACToken start = curr_token ();
  ACToken token = next_token ();
  bool is_virtual = false; // neither virtual nor pure virtual
  if (token.type () == ACToken::TOK_VIRTUAL) {
    is_virtual = true; // either virtual or pure virtual
    token = next_token();
  }
  if (!(token.type () == ACToken::TOK_ID)) {
    err () << sev_error << token.location()
        << "expect pointcut name after pointcut keyword." << endMessage;
    return;
  }
  string name = token.text ();
  token = next_token ();
  if (!(token.type () == ACToken::TOK_OPEN_ROUND)) {
    err () << sev_error << token.location()
        << "expect argument list in pointcut definition." << endMessage;
    return;
  }
  skip_round_block();
  token = curr_token();
  if (!(token.type () == ACToken::TOK_ASSIGN)) {
    err () << sev_error << token.location()
        << "expect '=' in pointcut definition." << endMessage;
    return;
  }
  token = next_token();
  string expr;
  ACToken pct_end_token = token;
  while (token.type () != ACToken::TOK_EOF && token.type () != ACToken::TOK_SEMI_COLON) {
    expr += token_text (token);
    pct_end_token = token;
    token = next_token();
  }
  // check whether the definition is semantically correct
  if (expr == "0" && !is_virtual) {
    err () << sev_error << start.location()
        << "pure virtual pointcut defined without 'virtual' keyword." << endMessage;
    is_virtual = true;
  }

  TU_Pointcut *pct = (TU_Pointcut*)create_pointcut(name, is_virtual, expr);
  if (!pct) {
    err () << sev_error << start.location()
        << "pointcut '" << name.c_str() << "' already defined." << endMessage;
    return;
  }
  create_pct_expr_tree(pct, start, curr_token());
}

void Phase1::parse_attribute_decl() {
  Scope &scope = curr_scope();
  ACToken start = curr_token ();

  if(!scope.is_namespace() && !scope.is_class() && !scope.is_aspect()) {
    err() << sev_error << curr_token().location()
          << "attribute declaration not allowed in this scope - daclare in namespace or class" << endMessage;
    return;
  }

  ACToken nameToken = next_token ();

  if (nameToken.type () != ACToken::TOK_ID) {
    err() << sev_error << curr_token().location()
          << "attribute keyword used without identifier." << endMessage;
    return;
  }

  string name = nameToken.text();

  ACToken token = next_token();

  if (token.type() != ACToken::TOK_OPEN_ROUND) {
    err() << sev_error << curr_token().location()
          << "expected '(' after attribute-identifier" << endMessage;
    return;
  }

  token = next_token();
  start_recording();

  int openBracket = 1;

  while (true) {
    if(token.type() == ACToken::TOK_OPEN_ROUND)
      openBracket++;
    else if(token.type() == ACToken::TOK_CLOSE_ROUND) {
      openBracket--;
      if(openBracket == 0)
        break;
    }
    else if(token.type() == ACToken::TOK_EOF) {
      err() << sev_error << curr_token().location()
            << "Missing ';' after attribute-identifier" << endMessage;
      return;
    }

    token = next_token();
  }

  ACPreprocessor::TokenVector paramTokens = stop_recording ();
  paramTokens.pop_back();
  string attrParams = recording_to_string(paramTokens);        

  //check if any params are declared for attribute - this is not allowed in first stage of implementation!
  if(!attrParams.empty()) {
//    err() << sev_error << curr_token().location() << "attributes with parameters are not supported, yet" << endMessage;
  }

  token = next_token();

  if (token.type() != ACToken::TOK_SEMI_COLON) {
    err() << sev_error << curr_token().location()
          << "expected ';' after attribute-declaration" << endMessage;
    return;
  }

  TU_Attribute *attr = (TU_Attribute*)create_attr(name);
  if (attr) {
    attr->setParamStr(attrParams);
#ifdef FRONTEND_CLANG
    attr->set_location(clang::FullSourceLoc (start.location(), err().get_compiler_instance ().getSourceManager()));
#endif
    _jpm.add_source_loc(attr, start, curr_token ());
  }
  else {
    //    err() << sev_error << curr_token().location()
    //          << "redeclaration of attribute '" << name.c_str() << "'" << endMessage;

    //TODO:check whether Arguments fit!
    return;
  }
}

void Phase1::parse_advice_def (const string &prot) {
  Scope &scope = curr_scope();
  if (!scope.is_aspect()) {
    err() << sev_error << curr_token ().location()
        << "'advice' keyword used outside an aspect." << endMessage;
    return;
  }

  ACToken start = curr_token ();
  ACToken token = next_token ();
  ACToken pct_start = token, pct_end = token;
  string expr;
  while (token.type () != ACToken::TOK_EOF && token.type () != ACToken::TOK_COLON) {
    // TODO: handle errors, e.g. ';' or '}' token
    expr += token_text(token);
    pct_end = token;
    token = next_token();
  }
  // create an anonymous pointcut object, ignore arguments for now
  TU_Pointcut *pct = (TU_Pointcut*)create_anon_pointcut(expr);
  create_pct_expr_tree(pct, pct_start, pct_end);

//  cout << "advice on " << expr << endl;
  token = next_token ();
  if (token.type() == ACToken::TOK_EOF) return;
  string text = token.text ();
  if (token.type () == ACToken::TOK_ID &&
      (text == "before" || text == "after" || text == "around")) {
    // ACToken advice_kind_token = token;
    next_token ();
    // ACToken arg_begin = curr_token();
    ACToken next = look_ahead ();
    if (next.type () == ACToken::TOK_EOF) return; // TODO: error handling
    bool has_args = (next.type() != ACToken::TOK_CLOSE_ROUND);
    skip_round_block(false);
    next_token ();
    // ACToken body_begin = curr_token ();
    AdviceCodeContext context = ACC_NONE;
    CodeWeaver::TypeUse uses_type;
    parse_advice_body (context, ACToken::TOK_OPEN_CURLY, ACToken::TOK_CLOSE_CURLY, uses_type );
    if (has_args)
      context = (AdviceCodeContext)(context | ACC_VARS);
    ACM_AdviceCode *new_elem = create_advice_code(text, pct);
    ACToken body_end = curr_token();
    _jpm.add_source_loc(new_elem, start, body_end);
//    _code_weaver.transform_delete(start, advice_kind_token);
//    _code_weaver.transform_advice_code( advice_no, text, context, has_args, uses_type, arg_begin, body_begin, body_end, prot );
    curr_scope()._advice_no++; // increment number of code advice block per aspect
  }
  else if (token.type () == ACToken::TOK_ID && text == "order") {
    ACToken order_start = token;
    token = next_token ();
    if (token.type() != ACToken::TOK_OPEN_ROUND) {
      err() << sev_error << order_start.location ()
          << "Syntax error in order advice, expected '(' after 'order'" << endMessage;
      return;
    }
    TU_Order *order = (TU_Order*)create_order(pct);
    while (true) {
      token = next_token ();
      ACToken pct_start = token, pct_end = token;
      string expr;
      int bracket_depth = 0;
      while (token.type () != ACToken::TOK_EOF && token.type () != ACToken::TOK_COMMA &&
          !(token.type() == ACToken::TOK_CLOSE_ROUND && bracket_depth == 0)) {
        // TODO: handle errors, e.g. ';' or '}' token
        if (token.type () == ACToken::TOK_OPEN_ROUND)
          bracket_depth++;
        else if (token.type () == ACToken::TOK_CLOSE_ROUND)
          bracket_depth--;
        expr += token_text (token);
        pct_end = token;
        token = next_token();
      }
      TU_Pointcut *pct = (TU_Pointcut*)create_anon_pointcut(expr);
      create_pct_expr_tree(pct, pct_start, pct_end);
      order->get_aspect_exprs().insert(pct);
      if (token.type () == ACToken::TOK_CLOSE_ROUND)
        break;
    }
    token = next_token();
    if (token.type () != ACToken::TOK_SEMI_COLON)
      return;
    _jpm.add_source_loc(order, start, token);
//    _code_weaver.transform_delete(start, token);
  }
  else if (token.type () == ACToken::TOK_SLICE) {
    ACToken slice_start = token;
    TU_ClassSlice *slice_obj = 0;
    ACToken next = look_ahead ();
    if (token.type () == ACToken::TOK_EOF) return; // TODO: error handling
    TU_Introduction *intro = (TU_Introduction*)create_introduction(pct);
    bool is_anon_slice = false;
    if (next.type () == ACToken::TOK_CLASS || next.type () == ACToken::TOK_STRUCT) {
      bool is_struct = (next.type () == ACToken::TOK_STRUCT);
      start_recording ();
      token = next_token ();
      token = next_token ();
      bool root_qualified, contains_template_id;
      vector<string> names;
      parse_qual_name (names, root_qualified, contains_template_id);
      token = curr_token ();
      Node *found_scope = 0;
      Scope *qual_scope = 0;
      bool is_qualified = (root_qualified || names.size () > 1);
      if (is_qualified) {
        found_scope = lookup_name(scope, root_qualified, names);
        qual_scope = lookup_scope(scope, root_qualified, names);
      }
      string name = (names.size () > 0) ? names[names.size () - 1] : anon_slice_name();
      if (names.size() == 0)
        is_anon_slice = true;

      if (token.type () == ACToken::TOK_SEMI_COLON) {
        ACPreprocessor::TokenVector rec = stop_recording ();
        slice_obj = (TU_ClassSlice*)create_class_slice(name, is_struct, qual_scope);
        _jpm.add_source_loc(slice_obj, slice_start, curr_token(), SLK_DECL);
      }
      else {
        while (token.type () != ACToken::TOK_EOF && token.type () != ACToken::TOK_SEMI_COLON) {
          if (token.type () == ACToken::TOK_OPEN_CURLY) {
            skip_curly_block (false);
            if (look_ahead(1).type() != ACToken::TOK_SEMI_COLON)
              err() << sev_error << curr_token().location()
                  << "Semicolon after class slice introduction missing." << endMessage;
            break;
          }
          token = next_token ();
        }
        ACPreprocessor::TokenVector rec = stop_recording ();
        if (token.type () == ACToken::TOK_EOF) return; // TODO: error handling
        if (qual_scope && !found_scope) {
          err () << sev_warning << slice_start.location()
              << "Definition of undeclared slice" << endMessage;
        }
        slice_obj = (TU_ClassSlice*)create_class_slice(name, is_struct, qual_scope);
        slice_obj->slice_unit(source_unit(slice_start));
        _jpm.add_source_loc(slice_obj, slice_start, curr_token());
        if (!slice_obj->get_tokens ().text.empty())
          err() << sev_error << slice_start.location()
            << "Slice '" << name.c_str () << "' was already defined." << endMessage;
        set_slice_tokens(slice_obj, rec);
      }
    }
    else {
      token = next_token ();
      ACToken name_start = token;
      bool root_qualified, contains_template_id;
      vector<string> names;
      parse_qual_name (names, root_qualified, contains_template_id);
      if (names.size () == 0) {
        err() << sev_error << name_start.location()
            << "expected slice name instead of '"
            << token_text(name_start).c_str()
            << "' token." << endMessage;
        return;
      }
      Node *found_scope = lookup_name(scope, root_qualified, names);
      // TODO: handle error here
      string name ("<unnamed>");
      if (names.size () > 0)
        name = names[names.size () - 1];
      token = curr_token ();
      if (token.type () != ACToken::TOK_SEMI_COLON) return; // TODO: handle syntax errors
      if (!found_scope || !found_scope->is_class_or_struct() ||
          !((Scope*)found_scope)->_is_slice ||
          !found_scope->_jpm_link) {
        err() << sev_error << name_start.location()
            << "'" << name.c_str() << "' is not a known class slice." << endMessage;
      }
      else
        slice_obj = (TU_ClassSlice*)found_scope->_jpm_link;
    }
    if (slice_obj) {
      if (is_anon_slice)
        intro->set_anon_slice(slice_obj);
      else
        intro->set_named_slice(slice_obj);
    }
    _jpm.add_source_loc(intro, start, curr_token(), SLK_NONE);
    _code_weaver.transform_delete(start, curr_token ());
  }
  else if (token.type () == ACToken::TOK_ID && text == "baseclass") {
    err() << sev_error << token.location()
        << "Deprecated introduction syntax used in aspect, use slice instead." << endMessage;
    while (token.type () != ACToken::TOK_EOF && token.type () != ACToken::TOK_SEMI_COLON)
      token = next_token();
  }
  else {
    err() << sev_error << token.location()
        << "Deprecated introduction syntax used in aspect, use slice instead." << endMessage;
    // skip the code;
    bool objdecl = false;
    while (token.type () != ACToken::TOK_EOF) {
      if (token.type () == ACToken::TOK_SEMI_COLON)
        break;
      if (token.type () == ACToken::TOK_CLASS ||
          token.type () == ACToken::TOK_STRUCT ||
          token.type () == ACToken::TOK_UNION ||
          token.type () == ACToken::TOK_ENUM) {
        token = next_token();
        if (token.type () == ACToken::TOK_ID)
          token = next_token();
        if (token.type () == ACToken::TOK_OPEN_CURLY)
          objdecl = true;
      }
      if (token.type () == ACToken::TOK_OPEN_ROUND) {
        skip_round_block(true);
        token = curr_token();
        continue;
      }
      if (token.type () == ACToken::TOK_OPEN_CURLY) {
        skip_curly_block(false);
        if (!objdecl)
          break;
        token = next_token();
      }
      if (token.type () == ACToken::TOK_ASSIGN)
        objdecl = true;
      token = next_token();
    }
  }
}

void Phase1::parse_advice_body( AdviceCodeContext &context, int open, int close,
    CodeWeaver::TypeUse &uses_type, bool transform) {
  ACToken current;
  int token;
  int depth = 0;
  ACToken first = curr_token();

  while (curr_token ().type () != ACToken::TOK_EOF) {
    current = curr_token ();
    token = current.type ();

    if (token == open)
      depth++;
    else if (token == close)
      depth--;
    else if (depth > 0 && token == ACToken::TOK_OPEN_ROUND) {
      parse_advice_body (context, ACToken::TOK_OPEN_ROUND, ACToken::TOK_CLOSE_ROUND, uses_type, transform );
      if (curr_token ().type () == ACToken::TOK_EOF) {
        err() << sev_error << first.location() << "unexpected end of file (missing bracket?)" << endMessage;
        throw ParseError();
      }
      next_token ();
      continue;
    }

    if (depth == 0)
      break;

    if (current.type () == ACToken::TOK_ID) {
      string text = current.text ();
      if (text == "JoinPoint") {
        context = (AdviceCodeContext)(context | ACC_TYPE);
        ACToken qualname_start = current;
        if (look_ahead ().type () == ACToken::TOK_COLON_COLON) {
          next_token (); // skip "::"
          current = next_token ();
          bool have_template_kw = false;
          if (current.type () == ACToken::TOK_TEMPLATE) {
            current = next_token ();
            have_template_kw = true;
          }
          if (current.type() == ACToken::TOK_ID) {
            string name = current.text ();
            if( name == "That" || name == "Result" || name == "Target" ||
                name == "Entity" || name == "MemberPtr" || name == "Array" ) {
              if (transform) {
                ostringstream tn;
                Naming::tjp_typedef(tn, name.c_str ());
                _code_weaver.replace (_code_weaver.weave_pos(qualname_start, WeavePos::WP_BEFORE),
                    _code_weaver.weave_pos(current, WeavePos::WP_AFTER), tn.str ());
              }
              if (name == "That")
                uses_type.that = true;
              else if (name == "Target")
                uses_type.target = true;
              else if (name == "Result")
                uses_type.result = true;
              else if (name == "Entity")
                uses_type.entity = true;
              else if (name == "MemberPtr")
                uses_type.memberptr = true;
              else if( name == "Array" )
                uses_type.array = true;
            }
            else if( name == "Arg" || name == "Dim" ) {
              // generate "template" keyword in front of Arg<i> or Dim<i> if the user hasn't provided it
              if (transform) {
                if (!have_template_kw)
                  _code_weaver.insert (_code_weaver.weave_pos(current, WeavePos::WP_BEFORE),
                      "template ");
              }
              current = next_token ();
              skip_template_params();
              current = curr_token ();
              if (current.type () == ACToken::TOK_COLON_COLON) {
                current = next_token ();
                if (current.type () == ACToken::TOK_ID) {
                  current = next_token ();
                  if (transform) {
                    if (current.type () != ACToken::TOK_COLON_COLON)
                      _code_weaver.insert (_code_weaver.weave_pos(qualname_start, WeavePos::WP_BEFORE),
                          "typename ");
                  }
                }
              }
            }
          }
        }
      }
      else if (text == "tjp" || text == "thisJoinPoint") {
        context = (AdviceCodeContext)(context | ACC_TYPE_OBJ);
        if (transform) {
          if (text == "thisJoinPoint") {
            // replace every occurrence of "thisJoinPoint" with "tjp"
            _code_weaver.replace(_code_weaver.weave_pos(current, WeavePos::WP_BEFORE),
                _code_weaver.weave_pos(current, WeavePos::WP_AFTER), "tjp");
          }
          if (look_ahead().type() == ACToken::TOK_PTS) {
            next_token (); // skip "->"
            current = next_token ();
            if (current.type() == ACToken::TOK_ID &&
                ( string( current.text() ) == "arg" || ( string( current.text() ) == "idx" ) ) &&
                look_ahead ().type () == ACToken::TOK_LESS) {
              _code_weaver.insert(_code_weaver.weave_pos(current, WeavePos::WP_BEFORE), "template ");
            }
          }
        }
      }
    }
    next_token ();
  }
}


void Phase1::create_pct_expr_tree(TU_Pointcut *pct, ACToken start_pos, ACToken end_pos) {
  const string &expr = pct->get_expr();
  PointCutExpr *parsed_expr = nullptr; // if anything goes wrong, the result must be 'nullptr'
  // set location information in any case
#ifdef FRONTEND_CLANG
  pct->set_location(clang::FullSourceLoc (start_pos.location(), err().get_compiler_instance().getSourceManager()));
#endif
  _jpm.add_source_loc(pct, start_pos, end_pos);
  // don't call the parser if the input is empty
  if (expr == "") {
    err() << sev_error << start_pos.location() << "Empty pointcut expression." << endMessage;
  }
  else if (expr == "0") { // special handling for '0' -> cannot be parsed by the parser
    // only pure virtual pointcuts can have the expression '0'
    if (pct->get_kind() != PT_PURE_VIRTUAL) {
      err() << sev_error << start_pos.location()
          << "Only pure virtual pointcuts can be '0'." << endMessage;
    }
  }
  else {
    // parse the pointcut expression
    PointCutExprParser *pce_parser = PointCutExprParser::instance( _conf );
    try {
      parsed_expr = pce_parser->parse(expr, *this);
      // store the list of names that where lookup during parsing in the project model, too
      // this allows a reader to reconstruct the pointcut expression tree more easily
      for (auto lookup_name : _lookup_record)
        pct->get_refs().insert(lookup_name);
      _lookup_record.clear();
    }
    catch (const std::exception &e) {
      err () << sev_error << start_pos.location()
        << "Invalid pointcut expression: " << e.what() << "." << endMessage;
    }
  }
  pct->get_parsed_expr().set (parsed_expr);
}

void Phase1::parse_base_clause (Scope &class_scope) {
  ACToken token = curr_token();
  int count = 0;
  while (token.type () != ACToken::TOK_EOF &&
      ((count == 0 && token.type () == ACToken::TOK_COLON) ||
          (count > 0 && token.type () == ACToken::TOK_COMMA))) {
    parse_base_spec (class_scope);
    token = curr_token();
    count++;
  }
}

void Phase1::parse_qual_name (vector<string> &names,
    bool &root_qualified, bool &contains_template_id) {
  contains_template_id = false;
  root_qualified = (curr_token ().type () == ACToken::TOK_COLON_COLON);
  if (root_qualified) next_token();

  while (curr_token ().type () == ACToken::TOK_ID) {
    string name = curr_token().text ();
    names.push_back (name);
    next_token ();
    if (curr_token().type () == ACToken::TOK_LESS) {
      contains_template_id = true;
      skip_template_params ();
    }
    if (curr_token().type() == ACToken::TOK_COLON_COLON)
      next_token ();
    else
      break;
  }
}

void Phase1::parse_base_spec (Scope &class_scope) {
  // skip ':' or ','
  next_token();
  // int count_public = 0, count_private = 0, count_protected = 0, count_virtual = 0;
  while (true) {
    int token = curr_token().type();
    // if (token == ACToken::TOK_PUBLIC) count_public++;
    // else if (token == ACToken::TOK_PRIVATE) count_private++;
    // else if (token == ACToken::TOK_PROTECTED) count_protected++;
    // else if (token == ACToken::TOK_VIRTUAL) count_virtual++;
    if (token == ACToken::TOK_PUBLIC || token == ACToken::TOK_PRIVATE ||
      token == ACToken::TOK_PROTECTED || token == ACToken::TOK_VIRTUAL) {
      // do nothing
    }
    else if (token == ACToken::TOK_ID || token == ACToken::TOK_COLON_COLON) break;
    else return;
    next_token ();
  }

  bool contains_template_id, root_qualified;
  vector<string> names;
  parse_qual_name (names, root_qualified, contains_template_id);

//  cout << "base of " << class_scope._name << "(" << root_qualified << " " << contains_template_id << "):";
//  for (vector<string>::const_iterator i = names.begin(); i != names.end(); ++i)
//    cout << " " << *i;
//  cout << endl;

  if (!contains_template_id && names.size () > 0) {
    Node *base = lookup_name (*class_scope._parent_scope, root_qualified, names);
    if (base && (base->is_class_or_struct() || base->is_aspect()))
      class_scope._search_scopes.push_back ((Scope*)base);
  }
}

Phase1::Scope *Phase1::find_slice (ACPreprocessor::TokenVector &rec) {
  Scope &scope = curr_scope();
  enum { EXP_SEP, EXP_ID, FLOATING } state = FLOATING;
  vector<string> names;
  bool root_qualified = false;
  unsigned name_start = -1U;
  Scope *result = 0;
  for (unsigned i = 1; i != rec.size(); ) {
    ACToken token = rec[i];
    if (state == FLOATING && token.type() == ACToken::TOK_COLON_COLON) {
      root_qualified = true;
      name_start = i;
      state = EXP_ID;
    }
    else if (state == EXP_SEP && token.type() == ACToken::TOK_COLON_COLON) {
      state = EXP_ID;
    }
    else if (state != EXP_SEP && token.type() == ACToken::TOK_ID) {
      if (state == FLOATING)
        name_start = i;
      names.push_back (token.text ());
      Scope *lookup_result = 0;
      Node *lr = lookup_name(scope, root_qualified, names);
      if (lr && lr->is_class_or_struct())
        lookup_result = (Scope*)lr;
      if (lookup_result) {
        if (lookup_result->_is_slice) {
          if (result && lookup_result != result) {
            // TODO: generate real error message
            cout << "ERROR conflicting slice names referenced" << endl;
            cout << result->_name << " <--> " << lookup_result->_name << endl;
            //cout << "SLICE: " << *rec << endl;
            cout << "SCOPE: " << scope._name << endl;
            scope.dump();
          }
          else {
            result = lookup_result;
            clang::Token tok;
            tok.startToken ();
            ACToken marker(tok);
            rec.insert(rec.begin() + name_start + 1, marker);
            i++;
            rec.insert(rec.begin() + i + 1, marker);
            i++;
            // TODO: continue to consume tokens while we are in a qualified name
          }
        }
      }
      state = EXP_SEP;
    }
    else {
      state = FLOATING;
      names.clear ();
      root_qualified = false;
      name_start = -1U;
      if (token.type () == ACToken::TOK_ID)
        continue; // this ID could be the start of the (qualified) slice name
    }
    ++i;
  }
  return result;
}
