# tools/core/analyzers/vocabulary.analyzer.ts

> 142 lines of code and 36 definitions.

Tree: Coordination tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/coordination#file-coordination-tools-core-analyzers-vocabulary-analyzer-ts
Source text: https://banes-lab.com/source/coordination/tools/core/analyzers/vocabulary.analyzer.ts.txt

Listed in [tools/core/analyzers](https://banes-lab.com/api/source/coordination/tools/core/analyzers.md), after [tools/core/analyzers/source.analyzer.ts](https://banes-lab.com/source/coordination/tools/core/analyzers/source.analyzer.ts.md).

## Definitions

- `splitOn` (lexical_declaration, line 66)
- `declaredRow` (lexical_declaration, line 148)
- `definesTheAxis` (lexical_declaration, line 128)
- `columnsOf` (lexical_declaration, line 96)
- `statesTheSet` (lexical_declaration, line 113)
- `declaredIn` (lexical_declaration, line 157, exported)
- `axisNamedBy` (lexical_declaration, line 87)
- `CELL` (lexical_declaration, line 4)
- `CLOSED` (lexical_declaration, line 6, exported)
- `MARKUP` (lexical_declaration, line 12)
- `SEPARATORS` (lexical_declaration, line 14)
- `WORD_BREAK` (lexical_declaration, line 16)
- `SPACING` (lexical_declaration, line 18)
- `AXIS_COLUMN` (lexical_declaration, line 20)
- `VALUE_COLUMN` (lexical_declaration, line 22)
- `Columns` (interface_declaration, line 24)
- `bare` (lexical_declaration, line 29)
- `start` (lexical_declaration, line 30)
- `end` (lexical_declaration, line 31)
- `tokens` (lexical_declaration, line 43)
- `token` (lexical_declaration, line 52)
- `last` (lexical_declaration, line 59)
- `held` (lexical_declaration, line 68)
- `at` (lexical_declaration, line 100)
- `label` (lexical_declaration, line 101)
- `closed` (lexical_declaration, line 114)
- `parts` (lexical_declaration, line 115)
- `cells` (lexical_declaration, line 132)
- `trimmed` (lexical_declaration, line 133)
- `out` (lexical_declaration, line 138)
- `cell` (lexical_declaration, line 140)
- `axis` (lexical_declaration, line 149)
- `value` (lexical_declaration, line 150)
- `found` (lexical_declaration, line 158, exported)
- `columns` (lexical_declaration, line 159, exported)
- `row` (lexical_declaration, line 162, exported)

## Source

```typescript
import type { Declared } from "../types/vocabulary.types.ts";
import { lifetime } from "../../../config/surface.config.ts";

const CELL = "|";

export const CLOSED: Readonly<Record<string, readonly string[]>> = {
    mutability: lifetime.values.mutability,
    removal: lifetime.values.removal,
    retention: lifetime.values.retention,
};

const MARKUP = new Set(["`", "*", "_", " "]);

const SEPARATORS = new Set(["·", ",", "/"]);

const WORD_BREAK = new Set([" ", "\t", "-"]);

const SPACING = new Set([" ", "\t"]);

const AXIS_COLUMN = lifetime.columns.axis;

const VALUE_COLUMN = lifetime.columns.value;

interface Columns {
    readonly axis: number;
    readonly value: number;
}

const bare = function bare(cell: string): string {
    let start = 0;
    let end = cell.length;

    while (start < end && MARKUP.has(cell[start] ?? "")) {
        start += 1;
    }
    while (end > start && MARKUP.has(cell[end - 1] ?? "")) {
        end -= 1;
    }

    return cell.slice(start, end).toLowerCase();
};

const tokens = function tokens(value: string): string[] {
    const out: string[] = [];
    let held = "";

    for (const char of value) {
        if (!SEPARATORS.has(char)) {
            held += char;
            continue;
        }
        const token = bare(held);
        if (token.length > 0) {
            out.push(token);
        }
        held = "";
    }

    const last = bare(held);
    if (last.length > 0) {
        out.push(last);
    }
    return out;
};

const splitOn = function splitOn(text: string, breaks: ReadonlySet<string>): string[] {
    const out: string[] = [];
    let held = "";

    for (const char of text) {
        if (!breaks.has(char)) {
            held += char;
            continue;
        }
        if (held.length > 0) {
            out.push(held);
        }
        held = "";
    }

    if (held.length > 0) {
        out.push(held);
    }
    return out;
};

const axisNamedBy = function axisNamedBy(cell: string): string | null {
    for (const word of splitOn(cell, WORD_BREAK)) {
        if (CLOSED[word] !== undefined) {
            return word;
        }
    }
    return null;
};

const columnsOf = function columnsOf(row: readonly string[]): Columns | null {
    let axis = -1;
    let value = -1;

    for (let at = 0; at < row.length; at += 1) {
        const label = bare(row[at] ?? "");
        if (label === AXIS_COLUMN) {
            axis = at;
        }
        if (label === VALUE_COLUMN) {
            value = at;
        }
    }

    return axis === -1 || value === -1 ? null : { axis, value };
};

const statesTheSet = function statesTheSet(axis: string, value: string): boolean {
    const closed = CLOSED[axis] ?? [];
    const parts = tokens(value);
    if (parts.length < 2) {
        return false;
    }

    for (const part of parts) {
        if (!closed.includes(part)) {
            return false;
        }
    }
    return true;
};

const definesTheAxis = function definesTheAxis(value: string): boolean {
    return splitOn(value, SPACING).length > 1;
};

const cells = function cells(line: string): string[] {
    const trimmed = line.trim();
    if (!trimmed.startsWith(CELL)) {
        return [];
    }

    const out: string[] = [];
    for (const part of trimmed.split(CELL)) {
        const cell = part.trim();
        if (cell.length > 0) {
            out.push(cell);
        }
    }
    return out;
};

const declaredRow = function declaredRow(row: readonly string[], columns: Columns, line: number): Declared[] {
    const axis = axisNamedBy(bare(row[columns.axis] ?? ""));
    const value = bare(row[columns.value] ?? "");
    if (axis === null || value.length === 0 || statesTheSet(axis, value) || definesTheAxis(value)) {
        return [];
    }
    return [{ axis, line, value }];
};

export const declaredIn = function declaredIn(source: string): Declared[] {
    const found: Declared[] = [];
    let columns: Columns | null = null;

    for (const [index, line] of source.split("\n").entries()) {
        const row = cells(line);
        if (row.length === 0) {
            columns = null;
        } else if (columns === null) {
            columns = columnsOf(row);
        } else {
            found.push(...declaredRow(row, columns, index + 1));
        }
    }

    return found;
};
```
