# core/parsers/constants.parser.ts

> 77 lines of code and 14 definitions.

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

Listed in [core/parsers](https://banes-lab.com/api/source/quality/core/parsers.md), after [core/parsers/config.yaml.parser.ts](https://banes-lab.com/source/quality/core/parsers/config.yaml.parser.ts.md) and before [core/parsers/css.parser.ts](https://banes-lab.com/source/quality/core/parsers/css.parser.ts.md).

## Definitions

- `appendChar` (lexical_declaration, line 14)
- `scanChar` (lexical_declaration, line 44)
- `scanInQuote` (lexical_declaration, line 22)
- `depthDelta` (lexical_declaration, line 37)
- `captureValue` (lexical_declaration, line 66, exported)
- `QUOTE_CHARS` (lexical_declaration, line 1)
- `OPENER_CHARS` (lexical_declaration, line 2)
- `CLOSER_CHARS` (lexical_declaration, line 3)
- `ScanState` (interface_declaration, line 5)
- `eq` (lexical_declaration, line 70, exported)
- `state` (lexical_declaration, line 71, exported)
- `li` (lexical_declaration, line 72, exported)
- `line` (lexical_declaration, line 73, exported)
- `ci` (lexical_declaration, line 74, exported)

## Contained in

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

## Linked from

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

## Source

```typescript
const QUOTE_CHARS: ReadonlySet<string> = new Set(['"', "'", "`"]);
const OPENER_CHARS: ReadonlySet<string> = new Set(["(", "[", "{"]);
const CLOSER_CHARS: ReadonlySet<string> = new Set([")", "]", "}"]);

interface ScanState {
    depth: number;
    done: boolean;
    escaped: boolean;
    out: string;
    pendingSpace: boolean;
    quote: string;
}

const appendChar = function appendChar(state: ScanState, ch: string): void {
    if (state.pendingSpace) {
        state.out += " ";
        state.pendingSpace = false;
    }
    state.out += ch;
};

const scanInQuote = function scanInQuote(state: ScanState, ch: string): void {
    state.out += ch;
    if (state.escaped) {
        state.escaped = false;
        return;
    }
    if (ch === "\\") {
        state.escaped = true;
        return;
    }
    if (ch === state.quote) {
        state.quote = "";
    }
};

const depthDelta = function depthDelta(ch: string): number {
    if (OPENER_CHARS.has(ch)) {
        return 1;
    }
    return CLOSER_CHARS.has(ch) ? -1 : 0;
};

const scanChar = function scanChar(state: ScanState, ch: string): void {
    if (state.quote !== "") {
        scanInQuote(state, ch);
        return;
    }
    if (QUOTE_CHARS.has(ch)) {
        appendChar(state, ch);
        state.quote = ch;
        return;
    }
    if (ch === " " || ch === "\t") {
        state.pendingSpace = state.out.length > 0;
        return;
    }
    if (ch === ";" && state.depth === 0) {
        state.done = true;
        return;
    }
    state.depth += depthDelta(ch);
    appendChar(state, ch);
};

export const captureValue = function captureValue(
    lines: readonly string[],
    startIndex: number,
): { endIndex: number; value: string } {
    const eq = (lines[startIndex] ?? "").indexOf("=");
    const state: ScanState = { depth: 0, done: false, escaped: false, out: "", pendingSpace: false, quote: "" };
    for (let li = startIndex; li < lines.length; li += 1) {
        const line = lines[li] ?? "";
        for (let ci = li === startIndex ? eq + 1 : 0; ci < line.length; ci += 1) {
            scanChar(state, line[ci] ?? "");
            if (state.done) {
                return { endIndex: li, value: state.out.trim() };
            }
        }
        state.pendingSpace = state.out.length > 0;
    }
    return { endIndex: lines.length - 1, value: state.out.trim() };
};
```
