/* Copyright (c) 1993 by Sanjay Ghemawat */
#ifndef _SYMBOL_H
#define _SYMBOL_H

/*
 * Immutable strings with efficient comparison operations.
 */
class SymbolRep;

class Symbol {
  public:
    Symbol();
    // effects: Returns arbitrary symbol.

    Symbol(Symbol const&);
    // effects: Returns same symbol.

    Symbol(char const*);
    // effects: Returns symbol with same name as specified C string.

    Symbol(char const* str, int len);
    // requires: (len >= 0) and str[0..len-1] does not contain '\0'
    // effects:  Returns symbol with same name as the length-specified string.

    Symbol const& operator = (Symbol const&);
    // effects: Gives symbol a new name.

    Symbol operator + (Symbol const& s) const;
    // effects: Returns the result of contenating <s> onto <this>.

    int operator == (Symbol const&) const;
    // effects: Returns true iff symbols have same name.

    int operator != (Symbol const&) const;
    // effects: Returns false iff symbols have same name.

    int length() const;
    // effects: Returns symbol name length.

    char const* unparse() const;
    // effects: Returns symbol name as a C string.

    static unsigned int hash(Symbol const&);
    // effects: Two symbols with the same name always hash to the same value.

    static int equal(Symbol const&, Symbol const&);
    // effects: Returns true iff symbols have same name.
  private:
    Symbol(SymbolRep*);
    SymbolRep* rep;
};

// Symbol rep
class SymbolRep {
  public:
    /* Hide everything except to class Symbol. */
    friend Symbol;
  private:
    static SymbolRep* find_rep(char const* s, int len = -1);
    // requires: len >= -1
    // effects:  If len == -1 then returns rep for C string specified by s.
    //		 Else returns rep for str s of length len.

    int length;
    char const* contents;

    // Rep Invariant
    //
    // this->contents is a dynamically allocated string with length
    // this->length.
    //
    // this->contents does not contain embedded nulls.
    //
    // If a->contents and b->contents have same characters stored in them,
    // then a->contents and b->contents are the same pointer.

    // Abstraction Function
    //
    // The abstract state of a symbol rep is just a name.
    // A(r) = r->contents[0..r->length-1].
};

inline Symbol::Symbol() {
    rep = SymbolRep::find_rep("", 0);
}

inline Symbol::Symbol(SymbolRep* r) : rep(r) { }

inline Symbol::Symbol(char const* str) {
    rep = SymbolRep::find_rep(str);
}

inline Symbol::Symbol(Symbol const& n) {
    rep = n.rep;
}

inline Symbol const& Symbol::operator = (Symbol const& n) {
    rep = n.rep;
    return (*this);
}

inline char const* Symbol::unparse() const {
    return rep->contents;
}

inline int Symbol::length() const {
    return rep->length;
}

inline int Symbol::operator == (Symbol const& n) const {
    return rep == n.rep;
}

inline int Symbol::operator != (Symbol const& n) const {
    return rep != n.rep;
}

inline int Symbol::equal(Symbol const& a, Symbol const& b) {
    return a.rep == b.rep;
}

#endif /* _SYMBOL_H */
