# core/parsers/code.go.parser.ts

> 121 lines of code and 37 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-parsers-code-go-parser-ts
Source text: https://banes-lab.com/source/docs/core/parsers/code.go.parser.ts.txt

Listed in [core/parsers](https://banes-lab.com/api/source/docs/core/parsers.md), after [core/parsers/binding.parser.ts](https://banes-lab.com/source/docs/core/parsers/binding.parser.ts.md) and before [core/parsers/diagram.parser.ts](https://banes-lab.com/source/docs/core/parsers/diagram.parser.ts.md).

## Definitions

- `isFuncKeyword` (lexical_declaration, line 39)
- `callAt` (lexical_declaration, line 104)
- `isCallName` (lexical_declaration, line 100)
- `funcFrom` (lexical_declaration, line 43)
- `parseFuncs` (lexical_declaration, line 74, exported)
- `qualifierBefore` (lexical_declaration, line 89)
- `callsIn` (lexical_declaration, line 121, exported)
- `funcAt` (lexical_declaration, line 66)
- `stepCalls` (lexical_declaration, line 114)
- `receiverAt` (lexical_declaration, line 23)
- `signatureEnd` (lexical_declaration, line 31)
- `PARENS` (lexical_declaration, line 15)
- `BRACES` (lexical_declaration, line 16)
- `OPEN_PAREN` (lexical_declaration, line 17)
- `OPEN_BRACE` (lexical_declaration, line 18)
- `CLOSE_BRACE` (lexical_declaration, line 19)
- `DOT` (lexical_declaration, line 20)
- `FUNC_HEAD` (lexical_declaration, line 21)
- `close` (lexical_declaration, line 27)
- `position` (lexical_declaration, line 32)
- `nameEnd` (lexical_declaration, line 48)
- `bodyStart` (lexical_declaration, line 49)
- `bodyEnd` (lexical_declaration, line 53)
- `receiver` (lexical_declaration, line 70)
- `funcs` (lexical_declaration, line 75, exported)
- `hit` (lexical_declaration, line 78, exported)
- `dot` (lexical_declaration, line 90)
- `qualifierEnd` (lexical_declaration, line 94)
- `identStart` (lexical_declaration, line 95)
- `qualifier` (lexical_declaration, line 96)
- `{ src, start, end }` (lexical_declaration, line 105)
- `next` (lexical_declaration, line 106)
- `name` (lexical_declaration, line 107)
- `scan` (lexical_declaration, line 122, exported)
- `calls` (lexical_declaration, line 123, exported)
- `at` (lexical_declaration, line 124, exported)
- `step` (lexical_declaration, line 126, exported)

## Contained in

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

## Uses

- [core/lexers/code.go.lexer.ts](https://banes-lab.com/source/docs/core/lexers/code.go.lexer.ts.md)

## Used by

- [core/analyzers/code.go.analyzer.ts](https://banes-lab.com/source/docs/core/analyzers/code.go.analyzer.ts.md)

## Linked from

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

## Source

```typescript
import {
    FUNC,
    isIdentPart,
    isIdentStart,
    lastIdent,
    skipBalanced,
    skipIdent,
    skipIdentBack,
    skipSpaces,
    skipSpacesBack,
} from "#core/lexers/code.go.lexer";
import { GO_BUILTINS, GO_KEYWORDS } from "#configuration/constants/code.go.constants";
import type { GoCall, GoCallHit, GoCallScan, GoFuncHit, GoReceiver, ParsedFunc } from "#types/code.types";

const PARENS = "()";
const BRACES = "{}";
const OPEN_PAREN = "(";
const OPEN_BRACE = "{";
const CLOSE_BRACE = "}";
const DOT = ".";
const FUNC_HEAD = "f";

const receiverAt = function receiverAt(src: string, at: number, bound: number): GoReceiver {
    if (src[at] !== OPEN_PAREN) {
        return { next: at, receiver: null };
    }
    const close = skipBalanced(src, at, PARENS);
    return { next: skipSpaces(src, close, bound), receiver: lastIdent(src.slice(at + 1, close - 1)) };
};

const signatureEnd = function signatureEnd(src: string, at: number, bound: number): number {
    let position = src[at] === OPEN_PAREN ? skipBalanced(src, at, PARENS) : at;
    while (position < bound && src[position] !== OPEN_BRACE && src[position] !== CLOSE_BRACE) {
        position = src[position] === OPEN_PAREN ? skipBalanced(src, position, PARENS) : position + 1;
    }
    return position;
};

const isFuncKeyword = function isFuncKeyword(src: string, at: number): boolean {
    return src.startsWith(FUNC, at) && !isIdentPart(src[at - 1]) && !isIdentPart(src[at + FUNC.length]);
};

const funcFrom = function funcFrom(
    src: string,
    receiver: GoReceiver & { sigPos: number },
    bound: number,
): GoFuncHit | null {
    const nameEnd = skipIdent(src, receiver.next, bound);
    const bodyStart = signatureEnd(src, skipSpaces(src, nameEnd, bound), bound);
    if (src[bodyStart] !== OPEN_BRACE) {
        return null;
    }
    const bodyEnd = skipBalanced(src, bodyStart, BRACES);
    return {
        func: {
            bodyEnd: bodyEnd - 1,
            bodyStart: bodyStart + 1,
            name: src.slice(receiver.next, nameEnd),
            receiver: receiver.receiver,
            sigPos: receiver.sigPos,
        },
        next: bodyEnd,
    };
};

const funcAt = function funcAt(src: string, at: number, bound: number): GoFuncHit | null {
    if (!isFuncKeyword(src, at)) {
        return null;
    }
    const receiver = receiverAt(src, skipSpaces(src, at + FUNC.length, bound), bound);
    return isIdentStart(src[receiver.next]) ? funcFrom(src, { ...receiver, sigPos: at }, bound) : null;
};

export const parseFuncs = function parseFuncs(src: string): ParsedFunc[] {
    const funcs: ParsedFunc[] = [];
    let at = 0;
    while (at < src.length) {
        const hit = src[at] === FUNC_HEAD ? funcAt(src, at, src.length) : null;
        if (hit === null) {
            at += 1;
        } else {
            funcs.push(hit.func);
            at = hit.next;
        }
    }
    return funcs;
};

const qualifierBefore = function qualifierBefore(src: string, at: number, start: number): string | null {
    const dot = skipSpacesBack(src, at - 1, start);
    if (src[dot] !== DOT) {
        return null;
    }
    const qualifierEnd = skipSpacesBack(src, dot - 1, start);
    const identStart = skipIdentBack(src, qualifierEnd, start);
    const qualifier = src.slice(identStart + 1, qualifierEnd + 1);
    return qualifier === "" ? null : qualifier;
};

const isCallName = function isCallName(src: string, next: number, end: number, name: string): boolean {
    return src[skipSpaces(src, next, end)] === OPEN_PAREN && !GO_KEYWORDS.has(name) && !GO_BUILTINS.has(name);
};

const callAt = function callAt(scan: GoCallScan, at: number): GoCallHit {
    const { src, start, end } = scan;
    const next = skipIdent(src, at, end);
    const name = src.slice(at, next);
    return {
        call: isCallName(src, next, end, name) ? { name, qualifier: qualifierBefore(src, at, start) } : null,
        next,
    };
};

const stepCalls = function stepCalls(scan: GoCallScan, at: number): GoCallHit {
    if (!isIdentStart(scan.src[at]) || isIdentPart(scan.src[at - 1])) {
        return { call: null, next: at + 1 };
    }
    return callAt(scan, at);
};

export const callsIn = function callsIn(src: string, start: number, end: number): GoCall[] {
    const scan: GoCallScan = { end, src, start };
    const calls: GoCall[] = [];
    let at = start;
    while (at < end) {
        const step = stepCalls(scan, at);
        if (step.call !== null) {
            calls.push(step.call);
        }
        at = step.next;
    }
    return calls;
};
```
