// 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                                            

#ifndef __Phase1Sema_h__
#define __Phase1Sema_h__

#include <string>
#include <vector>
#include <set>
#include <list>
#include <tuple>

#include "ACModel/ACModel.h"
#include "PointCutSearcher.h"

// Phase1 performance a simple and lightweight semantic analysis.
// We only collect scope names (classes, namespaces, etc.) so that
// pointcut lookup becomes feasible.

class Phase1Sema : public PointCutSearcher {
public:
  // Helper classes to create a simple syntax tree while parsing
  struct Node {
    enum Kind { UNKNOWN_KIND, ELEMENT_POINTCUT, ELEMENT_CLASS_ALIAS, ELEMENT_CXX11Attr,
      ELEMENT_NAMESPACE_ALIAS,
      SCOPE_NAMESPACE, SCOPE_CLASS, SCOPE_STRUCT, SCOPE_ASPECT,
      SCOPE_UNION } _kind;
    bool is_namespace_alias() const { return _kind == ELEMENT_NAMESPACE_ALIAS; }
    bool is_scope() const { return _kind >= SCOPE_NAMESPACE; }
    bool is_aspect() const { return _kind == SCOPE_ASPECT; }
    bool is_class() const { return _kind == SCOPE_CLASS; }
    bool is_struct() const { return _kind == SCOPE_STRUCT; }
    bool is_union() const { return _kind == SCOPE_UNION; }
    bool is_namespace() const { return _kind == SCOPE_NAMESPACE; }
    bool is_class_or_struct() const { return is_class() || is_struct(); }
    bool is_cxx11attr() const { return _kind == ELEMENT_CXX11Attr; }
    std::string _name;
    ACM_Name *_jpm_link;
    bool operator < (const Node &compared) const { return _name < compared._name; }
  };
  struct Element : Node {
    Node *_referred; // for class and namespace aliases
  };
  struct Scope : Node {
    Scope *_parent_scope;
    bool _is_slice;
    int _advice_no;
    int _order_no;
    int _intro_no;
    int _anon_slice_no;
    std::set<Scope> _sub_scopes;
    std::set<Element> _elements;
    std::list<Scope*> _search_scopes; // base classes for classes/aspects
                                      // namespaces for namespaces
    void dump (int indent = 0) const; // debug helper function
  };

  class NewScope {
    Scope *_old_scope;
    Phase1Sema &_sema;
  public:
    NewScope (Scope *new_scope, Phase1Sema &sema) : _sema(sema) {
      _old_scope = _sema._curr_scope;
      _sema._curr_scope = new_scope;
    }
    ~NewScope () {
      _sema._curr_scope = _old_scope;
    }
  };

private:

  ACModel *_model;
  Scope _root_scope;
  Scope *_curr_scope = 0;

  // ID of deanonymized class, union or struct. This must be shared
  // across all parse_scope calls. Otherwise, generated code may violate
  // ODR rule. E.g., if the anon class number is *not* shared, then
  //   namespace N { static union { int i; } }
  //   namespace N { static union { int i2; } }
  // will become
  //  namespace N { union __ac_anonN_0 { int i; } static union { int i; } }
  //  namespace N { union __ac_anonN_0 { int i2; } static union { int i2; } }
  // which defines N::__ac_anonN_0 twice.
  unsigned int _anon_class_no;

  // list of source-location-based filters for the next advice
  std::list<std::tuple<std::string, std::string>> _advice_filters;

  // attach collected advice filter patterns to the next advice (code, order, or intro)
  void attach_advice_filters(ACM_Advice *adv);

protected:
  // list of names returned by PointCutSearcher function implementations
  std::list<ACM_Name *> _lookup_record;

public:

  Scope &root_scope() { return _root_scope; }
  Scope &curr_scope() const { return *_curr_scope; }
  std::string curr_scope_name () const { return curr_scope()._name; }

  void init_sema (ACModel &model);

  // creation functions for model nodes
  Scope *create_new_scope(string name, Node::Kind kind, Scope *qual_scope,
    const std::vector<std::string> &names, bool in_model, bool in_slice);
  void create_class_alias(const string &name, Scope *target);
  void create_namespace_alias(const string &name, Scope *target);
  ACM_Attribute *create_attr(const string &name);
  ACM_Pointcut *create_pointcut(const string &name, bool is_virtual, const string &expr);
  ACM_Pointcut *create_anon_pointcut(const string &expr);
  ACM_AdviceCode *create_advice_code(const string &type, ACM_Pointcut *pct);
  ACM_Order *create_order(ACM_Pointcut *pct);
  ACM_Introduction *create_introduction(ACM_Pointcut *pct);
  ACM_ClassSlice *create_class_slice(const string &name, bool is_struct, Scope *qual_scope);
  
  // internal registration functions
  ACM_Name *register_scope (Scope &scope);
  void register_base_classes (Scope &cls);

  // name lookup functions
  Node *lookup_name (Scope &scope, bool root_qualified, const std::vector<std::string> &names,
    bool follow_alias = true);
  Node *lookup_name_in_scope (Scope &scope, const std::vector<std::string> &names,
      std::set<Scope*> &visited_scopes, bool follow_alias, int depth = 0);
  Scope *lookup_scope (Scope &scope, bool root_qualified, std::vector<std::string> &names);
  Node *lookup_unqualified_name (Scope &scope, const std::string &name);

  // functions needed for PointcutSearcher interface
  ACM_Pointcut *lookup_pct_func (bool root_qualified, std::vector<std::string> &qual_name);
  ACM_Attribute *lookup_pct_attr(bool root_qualified, std::vector<string> &qual_name);

  // other semantic stuff
  bool is_visible_scope (Scope &scope, const std::string &name);

  // name generation functions
  std::string full_anon_class_name (Scope &scope) const;
  std::string anon_slice_name();
  std::string anon_class_name();

  // register a new advice filter
  void add_advice_filter(const std::string &pattern_as_given, const std::string &pattern_as_regex) {
    _advice_filters.push_back({ pattern_as_given, pattern_as_regex });
  }

  // annotate a file in the project model with an "affect" pragma
  bool register_file_affects(const string &filename, const string &pattern_as_given, const string &pattern_as_regex);
};

#endif // __Phase1Sema_h__
