# core/parsers/syntax.parser.ts

> 67 lines of code and 21 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-parsers-syntax-parser-ts
Source text: https://banes-lab.com/source/patterns/core/parsers/syntax.parser.ts.txt

Listed in [core/parsers](https://banes-lab.com/api/source/patterns/core/parsers.md), after [core/parsers/schema.parser.ts](https://banes-lab.com/source/patterns/core/parsers/schema.parser.ts.md).

## Definitions

- `codeRecords` (lexical_declaration, line 54, exported)
- `recordFor` (lexical_declaration, line 29)
- `snippetOf` (lexical_declaration, line 21)
- `SNIPPET_MAX` (lexical_declaration, line 5)
- `HIGH_SURROGATE_MIN` (lexical_declaration, line 6)
- `HIGH_SURROGATE_MAX` (lexical_declaration, line 7)
- `LINE_BREAK` (lexical_declaration, line 8)
- `WalkContext` (interface_declaration, line 10)
- `WalkFrame` (interface_declaration, line 16)
- `raw` (lexical_declaration, line 22)
- `firstLine` (lexical_declaration, line 23)
- `clipped` (lexical_declaration, line 24)
- `lastCode` (lexical_declaration, line 25)
- `childTypes` (lexical_declaration, line 30)
- `contextBelow` (lexical_declaration, line 46)
- `out` (lexical_declaration, line 55, exported)
- `stack` (lexical_declaration, line 56, exported)
- `frame` (lexical_declaration, line 57, exported)
- `{ context, node }` (lexical_declaration, line 59, exported)
- `role` (lexical_declaration, line 60, exported)
- `below` (lexical_declaration, line 64, exported)

## Contained in

- [core/parsers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-parsers.md)

## Uses

- [core/selectors/syntax.selector.ts](https://banes-lab.com/source/patterns/core/selectors/syntax.selector.ts.md)

## Used by

- [core/parsers/code.parser.ts](https://banes-lab.com/source/patterns/core/parsers/code.parser.ts.md)

## Linked from

- [core/parsers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-parsers.md)
- [core/selectors](https://banes-lab.com/anatomy/patterns/folder-patterns-core-selectors.md)

## Source

```typescript
import { ROLE_DEFINITION, ROLE_NODE, roleOf } from "#core/classifiers/syntax.classifier";
import { childNodes, lineOf } from "#core/selectors/syntax.selector";
import type { CstNode } from "@govlab/code-parse";
import { MODULE_SCOPE } from "#configuration/constants/report.constants";
const SNIPPET_MAX = 76;
const HIGH_SURROGATE_MIN = 0xD8_00;
const HIGH_SURROGATE_MAX = 0xDB_FF;
const LINE_BREAK = "\n";

interface WalkContext {
    depth: number;
    language: string;
    caller: string;
}

interface WalkFrame {
    context: WalkContext;
    node: CstNode;
}

const snippetOf = function snippetOf(node: CstNode): string {
    const raw = node.text ?? "";
    const firstLine = raw.split(LINE_BREAK)[0] ?? raw;
    const clipped = firstLine.trim().slice(0, SNIPPET_MAX);
    const lastCode = clipped.codePointAt(clipped.length - 1) ?? 0;
    return lastCode >= HIGH_SURROGATE_MIN && lastCode <= HIGH_SURROGATE_MAX ? clipped.slice(0, -1) : clipped;
};

const recordFor = function recordFor(node: CstNode, context: WalkContext, role: string): Record<string, unknown> {
    const childTypes = childNodes(node)
        .filter((child) => child.isNamed)
        .map((child) => child.type);
    return {
        caller: context.caller,
        childCount: childTypes.length,
        childTypes,
        depth: context.depth,
        language: context.language,
        line: lineOf(node),
        nodeType: node.type,
        role,
        text: snippetOf(node),
    };
};

const contextBelow = function contextBelow(node: CstNode, context: WalkContext, role: string): WalkContext {
    return {
        caller: node.isNamed && role === ROLE_DEFINITION ? node.type : context.caller,
        depth: node.isNamed ? context.depth + 1 : context.depth,
        language: context.language,
    };
};

export const codeRecords = function codeRecords(root: CstNode, language: string): Record<string, unknown>[] {
    const out: Record<string, unknown>[] = [];
    const stack: WalkFrame[] = [{ context: { caller: MODULE_SCOPE, depth: 0, language }, node: root }];
    let frame = stack.pop();
    while (frame !== undefined) {
        const { context, node } = frame;
        const role = node.isNamed ? roleOf(node.type) : ROLE_NODE;
        if (node.isNamed) {
            out.push(recordFor(node, context, role));
        }
        const below = contextBelow(node, context, role);
        stack.push(
            ...childNodes(node)
                .toReversed()
                .map((child) => ({ context: below, node: child })),
        );
        frame = stack.pop();
    }
    return out;
};
```
