# shared/analyzers/segment.analyzer.ts

> 99 lines of code and 22 definitions.

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

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

## Definitions

- `arrayJoinObject` (lexical_declaration, line 32)
- `foldSegments` (lexical_declaration, line 87, exported)
- `foldParts` (lexical_declaration, line 20)
- `fold` (lexical_declaration, line 91, exported)
- `foldCall` (lexical_declaration, line 65)
- `foldTemplate` (lexical_declaration, line 40)
- `foldConcat` (lexical_declaration, line 56)
- `composedTokenOf` (lexical_declaration, line 95, exported)
- `BUILDERS` (lexical_declaration, line 16)
- `Folder` (type_alias_declaration, line 18)
- `parts` (lexical_declaration, line 21)
- `callee` (lexical_declaration, line 33)
- `holes` (lexical_declaration, line 41)
- `folded` (lexical_declaration, line 43)
- `left` (lexical_declaration, line 60)
- `right` (lexical_declaration, line 61)
- `foldLiteral` (lexical_declaration, line 72)
- `literal` (lexical_declaration, line 73)
- `FOLDERS` (lexical_declaration, line 77)
- `joinObject` (lexical_declaration, line 99, exported)
- `isBuilder` (lexical_declaration, line 100, exported)
- `segments` (lexical_declaration, line 104, exported)

## Uses

- [shared/analyzers/location.analyzer.ts](https://banes-lab.com/source/governance/shared/analyzers/location.analyzer.ts.md)
- [shared/registries/location.registry.ts](https://banes-lab.com/source/governance/shared/registries/location.registry.ts.md)
- [shared/selectors/syntax.selector.ts](https://banes-lab.com/source/governance/shared/selectors/syntax.selector.ts.md)

## Source

```typescript
import {
    calleeName,
    handlerKey,
    isType,
    literalString,
    nameOf,
    nodeAt,
    nodesAt,
    stringAt,
} from "../selectors/syntax.selector.ts";
import type { AstNode } from "../../types/syntax.types.ts";
import type { SegmentTable } from "../../types/location.types.ts";
import { cookedOf } from "./location.analyzer.ts";
import { tokenIn } from "../registries/location.registry.ts";

const BUILDERS = new Set(["join", "resolve"]);

type Folder = (node: AstNode, consts: SegmentTable) => string[] | null;

const foldParts = function foldParts(nodes: readonly AstNode[], consts: SegmentTable): string[] | null {
    const parts: string[] = [];
    for (const node of nodes) {
        const folded = foldSegments(node, consts);
        if (folded === null) {
            return null;
        }
        parts.push(...folded);
    }
    return parts;
};

const arrayJoinObject = function arrayJoinObject(node: AstNode): AstNode | null {
    const callee = nodeAt(node, "callee");
    if (!isType(callee, "MemberExpression") || nameOf(nodeAt(callee, "property")) !== "join") {
        return null;
    }
    return nodeAt(callee, "object");
};

const foldTemplate: Folder = function foldTemplate(node, consts) {
    const holes: string[] = [];
    for (const expression of nodesAt(node, "expressions")) {
        const folded = foldSegments(expression, consts);
        if (folded === null) {
            return null;
        }
        holes.push(folded.join("/"));
    }
    return [
        nodesAt(node, "quasis")
            .map((quasi, index) => cookedOf(quasi) + (holes[index] ?? ""))
            .join(""),
    ];
};

const foldConcat: Folder = function foldConcat(node, consts) {
    if (stringAt(node, "operator") !== "+") {
        return null;
    }
    const left = foldSegments(nodeAt(node, "left"), consts);
    const right = foldSegments(nodeAt(node, "right"), consts);
    return left === null || right === null ? null : [left.join("/") + right.join("/")];
};

const foldCall: Folder = function foldCall(node, consts) {
    if (BUILDERS.has(calleeName(node))) {
        return foldParts(nodesAt(node, "arguments"), consts);
    }
    return foldSegments(arrayJoinObject(node), consts);
};

const foldLiteral: Folder = function foldLiteral(node) {
    const literal = literalString(node);
    return literal === null ? null : [literal];
};

const FOLDERS: Record<string, Folder> = {
    arrayExpression: (node, consts) => foldParts(nodesAt(node, "elements"), consts),
    binaryExpression: foldConcat,
    callExpression: foldCall,
    identifier: (node, consts) => consts.get(nameOf(node)) ?? null,
    literal: foldLiteral,
    spreadElement: (node, consts) => foldSegments(nodeAt(node, "argument"), consts),
    templateLiteral: foldTemplate,
};

export const foldSegments = function foldSegments(node: AstNode | null, consts: SegmentTable): string[] | null {
    if (node === null) {
        return null;
    }
    const fold = FOLDERS[handlerKey(node.type)];
    return fold === undefined ? null : fold(node, consts);
};

export const composedTokenOf = function composedTokenOf(node: AstNode, consts: SegmentTable): string | null {
    if (node.type !== "CallExpression") {
        return null;
    }
    const joinObject = arrayJoinObject(node);
    const isBuilder = BUILDERS.has(calleeName(node));
    if (!isBuilder && joinObject === null) {
        return null;
    }
    const segments =
        joinObject === null ? foldParts(nodesAt(node, "arguments"), consts) : foldSegments(joinObject, consts);
    if (segments === null || segments.length < 2) {
        return null;
    }
    return tokenIn(segments.filter((part) => part.length > 0).join("/"));
};
```
