# shared/analyzers/sentence.analyzer.ts

> 94 lines of code and 17 definitions.

Tree: Governance tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-analyzers-sentence-analyzer-ts
Source text: https://banes-lab.com/source/governance/shared/analyzers/sentence.analyzer.ts.txt

Listed in [shared/analyzers](https://banes-lab.com/api/source/governance/shared/analyzers.md), after [shared/analyzers/segment.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/segment.analyzer.ts.md) and before [shared/analyzers/sink.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/sink.analyzer.ts.md).

## Definitions

- `sentencesOf` (lexical_declaration, line 16, exported)
- `isParticiple` (lexical_declaration, line 53, exported)
- `shapesOf` (lexical_declaration, line 98, exported)
- `sentenceWordsOf` (lexical_declaration, line 34, exported)
- `isAdverb` (lexical_declaration, line 63)
- `isAgentlessPassive` (lexical_declaration, line 76, exported)
- `joinsOf` (lexical_declaration, line 89, exported)
- `participleAfter` (lexical_declaration, line 67)
- `shapeOf` (lexical_declaration, line 93, exported)
- `sentences` (lexical_declaration, line 17, exported)
- `current` (lexical_declaration, line 36, exported)
- `next` (lexical_declaration, line 68)
- `after` (lexical_declaration, line 72)
- `index` (lexical_declaration, line 77, exported)
- `participle` (lexical_declaration, line 81, exported)
- `words` (lexical_declaration, line 94, exported)
- `longestOf` (lexical_declaration, line 102, exported)

## Used by

- [rules/eslint/strings-sentence-shape.eslint.rule.ts](https://banes-lab.com/source/governance/rules/eslint/strings-sentence-shape.eslint.rule.ts.md)
- [shared/analyzers/composition.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/composition.analyzer.ts.md)
- [shared/analyzers/document.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/document.analyzer.ts.md)

## Source

```typescript
import {
    AGENT_MARKER,
    COORDINATORS,
    IRREGULAR_PARTICIPLES,
    NOT_PARTICIPLES,
    PARTICIPLE_MIN_LENGTH,
    PARTICIPLE_SUFFIX,
    PASSIVE_ADVERBS,
    PASSIVE_ADVERB_SUFFIX,
    PASSIVE_AUXILIARIES,
    SENTENCE_TERMINALS,
    SENTENCE_WORD_STOPS,
} from "../manifests/sentence.manifest.ts";
import type { SentenceShape } from "../../types/writing.types.ts";

export const sentencesOf = function sentencesOf(text: string): string[] {
    const sentences: string[] = [];
    let current = "";
    for (const char of text) {
        current += char;
        if (SENTENCE_TERMINALS.has(char)) {
            if (current.trim().length > 0) {
                sentences.push(current.trim());
            }
            current = "";
        }
    }
    if (current.trim().length > 0) {
        sentences.push(current.trim());
    }
    return sentences;
};

export const sentenceWordsOf = function sentenceWordsOf(sentence: string): string[] {
    const words: string[] = [];
    let current = "";
    for (const char of sentence) {
        if (SENTENCE_WORD_STOPS.has(char)) {
            if (current.length > 0) {
                words.push(current.toLowerCase());
            }
            current = "";
        } else {
            current += char;
        }
    }
    if (current.length > 0) {
        words.push(current.toLowerCase());
    }
    return words;
};

export const isParticiple = function isParticiple(word: string): boolean {
    if (IRREGULAR_PARTICIPLES.has(word)) {
        return true;
    }
    if (NOT_PARTICIPLES.has(word) || word.length < PARTICIPLE_MIN_LENGTH) {
        return false;
    }
    return word.endsWith(PARTICIPLE_SUFFIX) && !word.endsWith(`e${PARTICIPLE_SUFFIX}`);
};

const isAdverb = function isAdverb(word: string): boolean {
    return PASSIVE_ADVERBS.has(word) || word.endsWith(PASSIVE_ADVERB_SUFFIX);
};

const participleAfter = function participleAfter(words: readonly string[], auxiliary: number): number {
    const next = words[auxiliary + 1] ?? "";
    if (isParticiple(next)) {
        return auxiliary + 1;
    }
    const after = words[auxiliary + 2] ?? "";
    return isAdverb(next) && isParticiple(after) ? auxiliary + 2 : -1;
};

export const isAgentlessPassive = function isAgentlessPassive(words: readonly string[]): boolean {
    for (let index = 0; index < words.length; index += 1) {
        if (!PASSIVE_AUXILIARIES.has(words[index] ?? "")) {
            continue;
        }
        const participle = participleAfter(words, index);
        if (participle !== -1) {
            return !words.slice(participle + 1).includes(AGENT_MARKER);
        }
    }
    return false;
};

export const joinsOf = function joinsOf(words: readonly string[]): number {
    return words.filter((word) => COORDINATORS.has(word)).length;
};

export const shapeOf = function shapeOf(sentence: string): SentenceShape {
    const words = sentenceWordsOf(sentence);
    return { agentlessPassive: isAgentlessPassive(words), joins: joinsOf(words), words: words.length };
};

export const shapesOf = function shapesOf(text: string): SentenceShape[] {
    return sentencesOf(text).map(shapeOf);
};

export const longestOf = function longestOf(shapes: readonly SentenceShape[]): number {
    return shapes.reduce((longest, shape) => Math.max(longest, shape.words), 0);
};
```
