# Module wordnet.py
#
# Original author: Oliver Steele <steele@osteele.com>
# Project Page: http://sourceforge.net/projects/pywordnet
#
# Copyright (c) 1998-2004 by Oliver Steele.  Use is permitted under
# the Artistic License
# <http://www.opensource.org/licenses/artistic-license.html>

"""An OO interface to the WordNet database.

Usage
-----
>>> from wordnet import *

>>> # Retrieve words from the database
>>> N['dog']
dog(n.)
>>> V['dog']
dog(v.)
>>> ADJ['clear']
clear(adj.)
>>> ADV['clearly']
clearly(adv.)

>>> # Examine a word's senses and pointers:
>>> N['dog'].getSenses()
('dog' in {noun: dog, domestic dog, Canis familiaris}, 'dog' in {noun: frump, dog}, 'dog' in {noun: dog}, 'dog' in {noun: cad, bounder, blackguard, dog, hound, heel}, 'dog' in {noun: frank, frankfurter, hotdog, hot dog, dog, wiener, wienerwurst, weenie}, 'dog' in {noun: pawl, detent, click, dog}, 'dog' in {noun: andiron, firedog, dog, dog-iron})
>>> # Extract the first sense
>>> dog = N['dog'][0]   # aka N['dog'].getSenses()[0]
>>> dog
'dog' in {noun: dog, domestic dog, Canis familiaris}
>>> dog.getPointers()[:5]
(hypernym -> {noun: canine, canid}, member meronym -> {noun: Canis, genus Canis}, member meronym -> {noun: pack}, hyponym -> {noun: pooch, doggie, doggy, barker, bow-wow}, hyponym -> {noun: cur, mongrel, mutt})
>>> dog.getPointerTargets(MEMBER_MERONYM)
[{noun: Canis, genus Canis}, {noun: pack}]
"""

__author__  = "Oliver Steele <steele@osteele.com>"
__version__ = "2.0.1"

import string
import os
from os import environ
from types import IntType, ListType, StringType, TupleType


#
# Configuration variables
#

WNHOME = environ.get('WNHOME', {
    'mac': ":",
    'dos': "C:\\wn16",
    'nt': "C:\\Program Files\\WordNet\\2.0"}
                     .get(os.name, "/usr/local/wordnet2.0"))

WNSEARCHDIR = environ.get('WNSEARCHDIR', os.path.join(WNHOME, {'mac': "Database"}.get(os.name, "dict")))

ReadableRepresentations = 1
"""If true, repr(word), repr(sense), and repr(synset) return
human-readable strings instead of strings that evaluate to an object
equal to the argument.

This breaks the contract for repr, but it makes the system much more
usable from the command line."""

_TraceLookups = 0

_FILE_OPEN_MODE = os.name in ('dos', 'nt') and 'rb' or 'r'  # work around a Windows Python bug


#
# Enumerated types
#

NOUN = 'noun'
VERB = 'verb'
ADJECTIVE = 'adjective'
ADVERB = 'adverb'
PartsOfSpeech = (NOUN, VERB, ADJECTIVE, ADVERB)

ANTONYM = 'antonym'
HYPERNYM = 'hypernym'
HYPONYM = 'hyponym'
ATTRIBUTE = 'attribute'
ALSO_SEE = 'also see'
ENTAILMENT = 'entailment'
CAUSE = 'cause'
VERB_GROUP = 'verb group'
MEMBER_MERONYM = 'member meronym'
SUBSTANCE_MERONYM = 'substance meronym'
PART_MERONYM = 'part meronym'
MEMBER_HOLONYM = 'member holonym'
SUBSTANCE_HOLONYM = 'substance holonym'
PART_HOLONYM = 'part holonym'
SIMILAR = 'similar'
PARTICIPLE_OF = 'participle of'
PERTAINYM = 'pertainym'
# New in wn 2.0:
FRAMES = 'frames'
CLASSIF_CATEGORY = 'domain category'
CLASSIF_USAGE = 'domain usage'
CLASSIF_REGIONAL = 'domain regional'
CLASS_CATEGORY = 'class category'
CLASS_USAGE = 'class usage'
CLASS_REGIONAL = 'class regional'

POINTER_TYPES = (
    ANTONYM,
    HYPERNYM,
    HYPONYM,
    ATTRIBUTE,
    ALSO_SEE,
    ENTAILMENT,
    CAUSE,
    VERB_GROUP,
    MEMBER_MERONYM,
    SUBSTANCE_MERONYM,
    PART_MERONYM,
    MEMBER_HOLONYM,
    SUBSTANCE_HOLONYM,
    PART_HOLONYM,
    SIMILAR,
    PARTICIPLE_OF,
    PERTAINYM,
    # New in wn 2.0:
    FRAMES,
    CLASSIF_CATEGORY,
    CLASSIF_USAGE,
    CLASSIF_REGIONAL,
    CLASS_CATEGORY,
    CLASS_USAGE,
    CLASS_REGIONAL,
    )

ATTRIBUTIVE = 'attributive'
PREDICATIVE = 'predicative'
IMMEDIATE_POSTNOMINAL = 'immediate postnominal'
ADJECTIVE_POSITIONS = (ATTRIBUTIVE, PREDICATIVE, IMMEDIATE_POSTNOMINAL, None)

VERB_FRAME_STRINGS = (
    None,
    "Something %s",
    "Somebody %s",
    "It is %sing",
    "Something is %sing PP",
    "Something %s something Adjective/Noun",
    "Something %s Adjective/Noun",
    "Somebody %s Adjective",
    "Somebody %s something",
    "Somebody %s somebody",
    "Something %s somebody",
    "Something %s something",
    "Something %s to somebody",
    "Somebody %s on something",
    "Somebody %s somebody something",
    "Somebody %s something to somebody",
    "Somebody %s something from somebody",
    "Somebody %s somebody with something",
    "Somebody %s somebody of something",
    "Somebody %s something on somebody",
    "Somebody %s somebody PP",
    "Somebody %s something PP",
    "Somebody %s PP",
    "Somebody's (body part) %s",
    "Somebody %s somebody to INFINITIVE",
    "Somebody %s somebody INFINITIVE",
    "Somebody %s that CLAUSE",
    "Somebody %s to somebody",
    "Somebody %s to INFINITIVE",
    "Somebody %s whether INFINITIVE",
    "Somebody %s somebody into V-ing something",
    "Somebody %s something with something",
    "Somebody %s INFINITIVE",
    "Somebody %s VERB-ing",
    "It %s that CLAUSE",
    "Something %s INFINITIVE")


#
# Domain classes
#
class Word:
    """An index into the database.
    
    Each word has one or more Senses, which can be accessed via
    ``word.getSenses()`` or through the index notation, ``word[n]``.

    Fields
    ------
      form : string
          The orthographic representation of the word.
      pos : string
          The part of speech -- one of NOUN, VERB, ADJECTIVE, ADVERB.
      string : string
          Same as form (for compatability with version 1.0).
      taggedSenseCount : integer
          The number of senses that are tagged.
    
    Examples
    --------
    >>> N['dog'].pos
    'noun'
    >>> N['dog'].form
    'dog'
    >>> N['dog'].taggedSenseCount
    1
    """
    
    def __init__(self, line):
        """Initialize the word from a line of a WN POS file."""
	tokens = string.split(line)
	ints = map(int, tokens[int(tokens[3]) + 4:])
	self.form = string.replace(tokens[0], '_', ' ')
        "Orthographic representation of the word."
	self.pos = _normalizePOS(tokens[1])
        "Part of speech.  One of NOUN, VERB, ADJECTIVE, ADVERB."
	self.taggedSenseCount = ints[1]
        "Number of senses that are tagged."
	self._synsetOffsets = ints[2:ints[0]+2]
    
    def getPointers(self, pointerType=None):
        """Pointers connect senses and synsets, not words.
        Try word[0].getPointers() instead."""
        raise self.getPointers.__doc__

    def getPointerTargets(self, pointerType=None):
        """Pointers connect senses and synsets, not words.
        Try word[0].getPointerTargets() instead."""
        raise self.getPointers.__doc__

    def getSenses(self):
	"""Return a sequence of senses.
	
	>>> N['dog'].getSenses()
	('dog' in {noun: dog, domestic dog, Canis familiaris}, 'dog' in {noun: frump, dog}, 'dog' in {noun: dog}, 'dog' in {noun: cad, bounder, blackguard, dog, hound, heel}, 'dog' in {noun: frank, frankfurter, hotdog, hot dog, dog, wiener, wienerwurst, weenie}, 'dog' in {noun: pawl, detent, click, dog}, 'dog' in {noun: andiron, firedog, dog, dog-iron})
	"""
	if not hasattr(self, '_senses'):
	    def getSense(offset,
 pos=self.pos, form=self.form):
		return getSynset(pos, offset)[form]
	    self._senses = tuple(map(getSense, self._synsetOffsets))
	    del self._synsetOffsets
	return se
lf._senses

    # Deprecated.  Present for backwards compatability.
    def senses(self):
        import wordnet
        #warningKey = 'SENSE_DEPRECATION_WARNING'
        #if not wordnet.has_key(warningKey):
        #    print 'Word.senses() has been deprecated.  Use Word.sense() instead.'
        #    wordnet[warningKey] = 1
        return self.getSense()
    
    def isTagged(self):
	"""Return 1 if any sense is tagged.
	
	>>> N['dog'].isTagged()
	1
	"""
	return self.taggedSenseCount > 0
    
    def getAdjectivePositions(self):
	"""Return a sequence of adjective positions that this word can
	appear in.  These are elements of ADJECTIVE_POSITIONS.
	
	>>> ADJ['clear'].getAdjectivePositions()
	[None, 'predicative']
	"""
	positions = {}
	for sense in self.getSenses():
	    positions[sense.position] = 1
	return positions.keys()

    adjectivePositions = getAdjectivePositions # backwards compatability
    
    def __cmp__(self