#ifndef TYPE_CHECKING_HH
#define TYPE_CHECKING_HH

#include "visitor.hh"

#include <set>
#include <iostream>
#include <typeinfo>
#include <cassert>

namespace type_checking {

    class TypeVisitor;
    class TypeInfo {
	const char        *_name;

	TypeInfo();
	TypeInfo(TypeInfo&);

    public: 
	TypeInfo(const char *name) : _name(name)
	{}
	virtual ~TypeInfo() {}
	
	const char        *name()  const { return _name; }
	virtual int        arity() const = 0;

	virtual void print(std::ostream &os) const = 0;
	virtual void accept(TypeVisitor &v) = 0;

	bool operator==(const TypeInfo &other) const
	{
	    return (typeid(*this) == typeid(other)
		  && (strcmp(name(),other.name()) == 0)
		  && (arity() == other.arity()));
	}
	bool operator!=(const TypeInfo &other) const
	{ return !((*this)==other); }

    };

    class MatrixType   : public TypeInfo {
	const std::list<ast::Expr*> *_dim_exprs;
	ast::basic_type_t _cell_type;

    public:
	MatrixType(const char *name,
		   const std::list<ast::Expr*> *dim_exprs,
		   ast::basic_type_t cell_type = ast::TYPE_INT)
	    : TypeInfo(name), _dim_exprs(dim_exprs), _cell_type(cell_type)
	{
	    assert(dim_exprs != 0);
	};

	MatrixType(const char *name, int arity,
		   ast::basic_type_t cell_type = ast::TYPE_INT)
	    : TypeInfo(name),
	      _dim_exprs(new std::list<ast::Expr*>(arity,
						   new ast::IntegerExpr(0,0))),
	      _cell_type(cell_type)
	{};

	const std::list<ast::Expr*> *dim_exprs() const { return _dim_exprs; }
	ast::basic_type_t            cell_type() const { return _cell_type; }

	virtual int arity() const { return _dim_exprs->size(); }

	virtual void print(std::ostream &os) const;
	virtual void accept(TypeVisitor &v);
    };

    class FunctionType : public TypeInfo {
	const std::list<ast::basic_type_t> *_parameter_types;
	ast::basic_type_t _return_type;

    public:
	FunctionType(const char *name,
		     const std::list<ast::basic_type_t> *parameter_types,
		     ast::basic_type_t return_type = ast::TYPE_INT)
	    : TypeInfo(name),
	      _parameter_types(parameter_types),
	      _return_type(return_type)
	{ assert(parameter_types != 0); };

	FunctionType(const char *name, int arity,
		     ast::basic_type_t return_type = ast::TYPE_INT)
	    : TypeInfo(name),
	      _parameter_types(new std::list<ast::basic_type_t>(arity, ast::TYPE_INT)),
	      _return_type(return_type)
	{ };

	const std::list<ast::basic_type_t> *parameter_types() const
	{ return _parameter_types; }
	ast::basic_type_t return_type() const { return _return_type; }

	virtual int arity() const { return _parameter_types->size(); }

	virtual void print(std::ostream &os) const;
	virtual void accept(TypeVisitor &v);
    };

    class ValType : public TypeInfo {
	ast::basic_type_t _type;

    public:
	ValType(const char *name, ast::basic_type_t type)
	    : TypeInfo(name), _type(type) {};

	ast::basic_type_t type() const { return _type; }

	virtual int arity() const { return 0; }

	virtual void print(std::ostream &os) const;
	virtual void accept(TypeVisitor &v);
    };

    class IndexType : public TypeInfo {
    public:
	IndexType(const char *name)
	    : TypeInfo(name) {};

	virtual int arity() const { return 0; }

	virtual void print(std::ostream &os) const;
	virtual void accept(TypeVisitor &v);
    };

    class TypeVisitor {
    public:
	virtual void visit(MatrixType   &matrix_type) = 0;
	virtual void visit(FunctionType &fun_type)    = 0;	
	virtual void visit(ValType      &val_type)    = 0;	
	virtual void visit(IndexType    &idx_type)    = 0;	
    };

    // Exceptions
    class UndefinedSymbol : public ast::Exception {
	const char *_symbol;
    public:
	UndefinedSymbol(const ast::Ast &ast, const char *symbol)
	    : ast::Exception(ast), _symbol(symbol)
	{}
	const char     *symbol() const { return _symbol; }
	virtual void print_error_msg(std::ostream &os);
    };

    class BuiltinNotDefined : public ast::Exception {
	const char *_symbol;
    public:
	BuiltinNotDefined(const ast::Ast &ast, const char *symbol)
	    : ast::Exception(ast), _symbol(symbol)
	{}

	const char     *symbol() const { return _symbol; }

	virtual void print_error_msg(std::ostream &os);
    };


    class WrongType : public ast::Exception {
	const TypeInfo *_expected;
	const TypeInfo *_actual;

    public:
	WrongType(const ast::Ast &ast,
		  const TypeInfo *expected,
		  const TypeInfo *actual)
	    : ast::Exception(ast), _expected(expected), _actual(actual)
	{}
	virtual ~WrongType()
	{ delete _expected; }	// `actual' points into a symbol table
				// and shouldn't be deleted.

	const TypeInfo *expected() const { return _expected; }
	const TypeInfo *actual()   const { return _actual; }

	virtual void print_error_msg(std::ostream &os);
    };

	

    // Type-checking functions
    void check(ast::DProg* dprog);
    std::set<TypeInfo*> *global_symbols();
    std::set<TypeInfo*> *parameters();
};

inline std::ostream &
operator<<(std::ostream &os, const type_checking::TypeInfo &tinfo)
{
    tinfo.print(os);
    return os; 
}


#endif // TYPE_CHECKING_HH


syntax highlighted by Code2HTML, v. 0.9.1