<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def tokenize_sentences(self):
 """Apply sentence tokenization to this Text instance. Creates ``sentences`` layer. Automatically tokenizes paragraphs, if they are not already tokenized. Also, if word tokenization has already been performed, tries to fit the sentence tokenization into the existing word tokenization; """

        if not self.is_tagged(PARAGRAPHS):
            self.tokenize_paragraphs()
        tok  = self.__sentence_tokenizer
        text = self.text
        dicts = []
        for paragraph in self[PARAGRAPHS]:
            para_start, para_end = paragraph[START], paragraph[END]
            para_text = text[para_start:para_end]
            if not self.is_tagged(WORDS):
                # Non-hack variant: word tokenization has not been applied yet,
                # so we proceed in natural order (first sentences, then words)
                spans = tok.span_tokenize(para_text)
                for start, end in spans:
                    dicts.append({'start': start+para_start, 'end': end+para_start})
            else:
                # A hack variant: word tokenization has already been made, so
                # we try to use existing word tokenization (first words, then sentences)
                para_words = \
                    [ w for w in self[WORDS] if w[START]>=para_start and w[END]<=para_end ]
                para_word_texts = \
                    [ w[TEXT] for w in para_words ]
                try:
                    # Apply sentences_from_tokens method (if available)
                    sents = tok.sentences_from_tokens( para_word_texts )
                except AttributeError as e:
                    raise
                # Align result of the sentence tokenization with the initial word tokenization
                # in order to determine the sentence boundaries
                i = 0
                for sentence in sents:
                    j = 0
                    firstToken = None
                    lastToken  = None
                    while i < len(para_words):
                        if para_words[i][TEXT] != sentence[j]:
                            raise Exception('Error on aligning: ', para_word_texts,' and ',sentence,' at positions ',i,j)
                        if j == 0:
                            firstToken = para_words[i]
                        if j == len(sentence) - 1:
                            lastToken = para_words[i]
                            i+=1
                            break
                        j+=1
                        i+=1
                    sentenceDict = \
                        {'start': firstToken[START], 'end': lastToken[END]}
                    dicts.append( sentenceDict )
                # Note: We also need to invalidate the cached properties providing the
                #       sentence information, as otherwise, if the properties have been
                #       called already, new calls would return the old state of sentence 
                #       tokenization;
                for sentence_attrib in ['sentences', 'sentence_texts', 'sentence_spans', \
                                        'sentence_starts', 'sentence_ends']:
                    try:
                        # invalidate the cache
                        delattr(self, sentence_attrib)
                    except AttributeError:
                        # it's ok, if the cached property has not been called yet
                        pass
        self[SENTENCES] = dicts
        return self