#ifndef IVL_PScope_H
#define IVL_PScope_H
/*
 * Copyright (c) 2008-2021 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

# include  "PNamedItem.h"
# include  "StringHeap.h"
# include  "pform_types.h"
# include  "ivl_target.h"
# include  <map>
# include  <set>
# include  <vector>

class PEvent;
class PExpr;
class PFunction;
class PPackage;
class AProcess;
class PProcess;
class PClass;
class PTask;
class PWire;
class Statement;
class PCallTask;

class Design;
class NetScope;

/*
 * The PScope class is a base representation of an object that
 * represents lexical scope. For example, a module, a function/task, a
 * named block is derived from a PScope.
 *
 * NOTE: This is not the same concept as the "scope" of an elaborated
 * hierarchy. That is represented by NetScope objects after elaboration.
 */

class LexicalScope {

    public:
      enum lifetime_t { INHERITED, STATIC, AUTOMATIC };

      explicit LexicalScope(LexicalScope*parent)
        : default_lifetime(INHERITED), has_parameter_port_list(false),
	  generate_counter(0), parent_(parent) { }
	// A virtual destructor is so that dynamic_cast can work.
      virtual ~LexicalScope() { }

      lifetime_t default_lifetime;

	// Symbols that are defined or declared in this scope.
      std::map<perm_string,PNamedItem*>local_symbols;

	// Symbols that are explicitly imported. Bind the imported name
	// to the package from which the name is imported.
      std::map<perm_string,PPackage*>explicit_imports;

	// Packages that are wildcard imported. When identifiers from
	// these packages are referenced, they will be added to the
	// explicit imports (IEEE 1800-2012 26.3).
      std::list<PPackage*>potential_imports;

	// A task or function call may reference a task or function defined
	// later in the scope. So here we stash the potential imports for
	// task and function calls. They will be added to the explicit
	// imports if we don't find a local definition.
      std::map<perm_string,PPackage*>possible_imports;

      struct range_t {
	      // True if this is an exclude
	    bool exclude_flag;
	      // lower bound
	      // If low_open_flag is false and low_expr=0, then use -inf
	    bool low_open_flag;
	    PExpr*low_expr;
	      // upper bound
	      // If high_open_flag is false and high_expr=0, then use +inf
	    bool high_open_flag;
	    PExpr*high_expr;
	      // Next range description in list
	    struct range_t*next;
      };

	/* The scope has parameters that are evaluated when the scope
	   is elaborated. During parsing, I put the parameters into
	   this map. */
      struct param_expr_t : public PNamedItem {
            inline param_expr_t() : data_type(0), expr(0), range(0),
				    local_flag(false), overridable(true) { }
	      // Type information.
	    data_type_t*data_type;
	      // Value expression
	    PExpr*expr;
	      // If there are range constraints, list them here
	    range_t*range;
	      // Whether it is a local parameter
	    bool local_flag;
	      // Whether the parameter can be overridden
	    bool overridable;
	      // Whether the parameter is a type parameter
	    bool type_flag = false;

	    SymbolType symbol_type() const;
      };
      std::map<perm_string,param_expr_t*>parameters;
      bool has_parameter_port_list;

	// Defined types in the scope.
      typedef std::map<perm_string,typedef_t*> typedef_map_t;
      typedef_map_t typedefs;

	// Named events in the scope.
      std::map<perm_string,PEvent*>events;

	// Nets and variables (wires) in the scope
      std::map<perm_string,PWire*>wires;
      PWire* wires_find(perm_string name);

        // Genvars in the scope. These will only be present in module
        // scopes, but are listed here to allow them to be found when
        // creating implicit nets.
      std::map<perm_string,LineInfo*> genvars;

	// Variable initializations in this scope
      std::vector<Statement*> var_inits;

	// Behaviors (processes) in this scope
      std::list<PProcess*> behaviors;
      std::list<AProcess*> analog_behaviors;

	// The elaboration tasks in this scope
      std::list<PCallTask*> elab_tasks;

	// Enumeration sets.
      std::vector<enum_type_t*> enum_sets;

        // A count of the generate constructs in this scope. This is
        // used to automatically name unnamed generate blocks, as
        // specified in the LRM.
      unsigned generate_counter;

      LexicalScope* parent_scope() const { return parent_; }

      virtual bool var_init_needs_explicit_lifetime() const;

    protected:
      void dump_typedefs_(std::ostream&out, unsigned indent) const;

      void dump_parameters_(std::ostream&out, unsigned indent) const;

      void dump_enumerations_(std::ostream&out, unsigned indent) const;

      void dump_events_(std::ostream&out, unsigned indent) const;

      void dump_wires_(std::ostream&out, unsigned indent) const;

      void dump_var_inits_(std::ostream&out, unsigned indent) const;

      bool elaborate_var_inits_(Design*des, NetScope*scope) const;

    private:
      LexicalScope*parent_;
};

class PScope : public LexicalScope {

    public:
	// When created, a scope has a name and a parent. The name is
	// the name of the definition. For example, if this is a
	// module declaration, the name is the name after the "module"
	// keyword, and if this is a task scope, the name is the task
	// name. The parent is the lexical parent of this scope. Since
	// modules do not nest in Verilog, the parent must be nil for
	// modules. Scopes for tasks and functions point to their
	// containing module.
      explicit PScope(perm_string name, LexicalScope*parent =0);
      virtual ~PScope();

      perm_string pscope_name() const { return name_; }

	/* These are the timescale for this scope. The value is
	   set by the `timescale directive or, in SystemVerilog,
	   by timeunit and timeprecision statements. */
      int time_unit, time_precision;

	/* Flags used to support warnings about timescales. */
      bool time_unit_is_default;
      bool time_prec_is_default;

      bool has_explicit_timescale() const {
	     return !(time_unit_is_default || time_prec_is_default);
	   }

    protected:
      bool elaborate_sig_wires_(Design*des, NetScope*scope) const;

      bool elaborate_behaviors_(Design*des, NetScope*scope) const;

    private:
      perm_string name_;
};

/*
 * Some scopes can carry definitions. These include Modules and PClass
 * scopes. These derive from PScopeExtra so that they hold the maps of
 * extra definitions.
 */
class PScopeExtra : public PScope {

    public:
      explicit PScopeExtra(perm_string, LexicalScope*parent =0);
      ~PScopeExtra();

	/* Task definitions within this module */
      std::map<perm_string,PTask*> tasks;
      std::map<perm_string,PFunction*> funcs;
	/* Class definitions within this module. */
      std::map<perm_string,PClass*> classes;
	/* This is the lexical order of the classes, and is used by
	   elaboration to choose an elaboration order. */
      std::vector<PClass*> classes_lexical;

	/* Flags used to support warnings about timescales. */
      bool time_unit_is_local;
      bool time_prec_is_local;

    protected:
      void dump_classes_(std::ostream&out, unsigned indent) const;
      void dump_tasks_(std::ostream&out, unsigned indent) const;
      void dump_funcs_(std::ostream&out, unsigned indent) const;
};

#endif /* IVL_PScope_H */
