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

> 56 lines of code and 18 definitions.

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

## Definitions

- `slotsIn` (lexical_declaration, line 42, exported)
- `slotNamesOf` (lexical_declaration, line 22)
- `slotStates` (lexical_declaration, line 46, exported)
- `isSlotName` (lexical_declaration, line 10)
- `SlotUse` (interface_declaration, line 1, exported)
- `SLOT_OPEN` (lexical_declaration, line 6)
- `SLOT_CLOSE` (lexical_declaration, line 7)
- `SLOT_CHARACTERS` (lexical_declaration, line 8)
- `close` (lexical_declaration, line 27)
- `name` (lexical_declaration, line 28)
- `RESOLUTION_STATES` (lexical_declaration, line 33, exported)
- `Resolution` (type_alias_declaration, line 35, exported)
- `earliestState` (lexical_declaration, line 37)
- `found` (lexical_declaration, line 38)
- `out` (lexical_declaration, line 47, exported)
- `state` (lexical_declaration, line 49, exported)
- `WILDCARD_SLOT` (lexical_declaration, line 62)
- `enumeratesVocabulary` (lexical_declaration, line 64, exported)

## Used by

- [tools/rules/binding.rule.ts](https://banes-lab.com/source/coordination/tools/rules/binding.rule.ts.md)

## Source

```typescript
export interface SlotUse {
    readonly name: string;
    readonly line: number;
}

const SLOT_OPEN = "{";
const SLOT_CLOSE = "}";
const SLOT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.";

const isSlotName = function isSlotName(name: string): boolean {
    if (name.length === 0) {
        return false;
    }
    for (const character of name) {
        if (!SLOT_CHARACTERS.includes(character)) {
            return false;
        }
    }
    return true;
};

const slotNamesOf = function slotNamesOf(line: string): string[] {
    return line
        .split(SLOT_OPEN)
        .slice(1)
        .flatMap((segment) => {
            const close = segment.indexOf(SLOT_CLOSE);
            const name = close === -1 ? "" : segment.slice(0, close);
            return isSlotName(name) ? [SLOT_OPEN + name + SLOT_CLOSE] : [];
        });
};

export const RESOLUTION_STATES = ["RESOLVED", "ABSENT", "DEFERRED"] as const;

export type Resolution = (typeof RESOLUTION_STATES)[number];

const earliestState = function earliestState(line: string): Resolution | undefined {
    const found = RESOLUTION_STATES.map((state) => ({ at: line.indexOf(state), state })).filter(({ at }) => at !== -1);
    return found.toSorted((left, right) => left.at - right.at)[0]?.state;
};

export const slotsIn = function slotsIn(source: string): SlotUse[] {
    return source.split("\n").flatMap((line, index) => slotNamesOf(line).map((name) => ({ line: index + 1, name })));
};

export const slotStates = function slotStates(source: string): Map<string, Resolution> {
    const out = new Map<string, Resolution>();
    for (const line of source.split("\n")) {
        const state = earliestState(line);
        if (state === undefined) {
            continue;
        }
        for (const use of slotsIn(line)) {
            if (!out.has(use.name)) {
                out.set(use.name, state);
            }
        }
    }
    return out;
};

const WILDCARD_SLOT = ".*}";

export const enumeratesVocabulary = function enumeratesVocabulary(line: string): boolean {
    return line.includes(WILDCARD_SLOT);
};
```
