tools/core/readers/agenda.reader.ts

tools/core/readers/agenda.reader.ts is a file in Coordination Surface. 101 lines of code and 31 definitions.

const AGENDA_MARK = "`";

const ROW_LEAD = "|";

const PLANNED_STATE = "planned";

const LETTER_SCALE = 100;

interface AgendaRow {
    readonly invariant: string;
    readonly ordinal: string;
    readonly planned: boolean;
}

interface DisorderedRow {
    readonly invariant: string;
    readonly ordinal: string;
    readonly after: string;
}

const stateCellOf = function stateCellOf(row: string): string {
    const cells = row.split(ROW_LEAD);
    if (cells.length < 3) {
        return "";
    }

    const cell = (cells.at(-2) ?? "").trim();
    if (!cell.startsWith(AGENDA_MARK)) {
        return cell;
    }

    const closes = cell.indexOf(AGENDA_MARK, 1);
    return closes === -1 ? cell.slice(1) : cell.slice(1, closes);
};

const agendaRowOf = function agendaRowOf(line: string): AgendaRow | null {
    const trimmed = line.trim();
    const opens = trimmed.startsWith(ROW_LEAD) ? trimmed.indexOf(AGENDA_MARK) : -1;
    const closes = opens === -1 ? -1 : trimmed.indexOf(AGENDA_MARK, opens + 1);
    const invariant = closes === -1 ? "" : trimmed.slice(opens + 1, closes).trim();
    if (invariant.length === 0) {
        return null;
    }

    const cell = trimmed.indexOf(ROW_LEAD, 1);
    const ordinal = cell === -1 ? "" : trimmed.slice(1, cell).trim();
    return { invariant, ordinal, planned: stateCellOf(trimmed).startsWith(PLANNED_STATE) };
};

export const agendaRows = function agendaRows(agenda: string): AgendaRow[] {
    return agenda
        .split("\n")
        .flatMap((line) => {
            const row = agendaRowOf(line);
            return row === null ? [] : [row];
        })
        .filter((row, index, rows) => rows.findIndex((held) => held.invariant === row.invariant) === index);
};

export const ordinalValue = function ordinalValue(ordinal: string): number | null {
    let digits = "";
    let cursor = 0;

    while (cursor < ordinal.length && ordinal.charAt(cursor) >= "0" && ordinal.charAt(cursor) <= "9") {
        digits += ordinal.charAt(cursor);
        cursor += 1;
    }

    if (digits.length === 0) {
        return null;
    }

    let suffix = 0;
    for (; cursor < ordinal.length; cursor += 1) {
        const char = ordinal.charAt(cursor);
        if (char < "a" || char > "z") {
            return null;
        }
        suffix = suffix * LETTER_SCALE + ((char.codePointAt(0) ?? 0) - ("a".codePointAt(0) ?? 0) + 1);
    }

    return Number(digits) + suffix / (LETTER_SCALE * LETTER_SCALE);
};

export const disorderedRows = function disorderedRows(agenda: string): DisorderedRow[] {
    const out: DisorderedRow[] = [];
    let held: { value: number; invariant: string } | null = null;

    for (const row of agendaRows(agenda)) {
        const value = ordinalValue(row.ordinal);
        if (value !== null) {
            if (held !== null && value < held.value) {
                out.push({ after: held.invariant, invariant: row.invariant, ordinal: row.ordinal });
            } else {
                held = { invariant: row.invariant, value };
            }
        }
    }

    return out;
};

export const agendaOrdinalOf = function agendaOrdinalOf(agenda: string, invariant: string): string {
    return agendaRows(agenda).find((row) => row.invariant === invariant)?.ordinal ?? "";
};

export const agendaInvariants = function agendaInvariants(agenda: string): string[] {
    return agendaRows(agenda).map((row) => row.invariant);
};

export const plannedInvariants = function plannedInvariants(agenda: string): string[] {
    return agendaRows(agenda)
        .filter((row) => row.planned)
        .map((row) => row.invariant);
};

export const admissibleInvariants = function admissibleInvariants(agenda: string, venues: readonly string[]): string[] {
    return agendaRows(agenda)
        .filter((row) => row.planned && !venues.some((venue) => venue.includes(row.invariant)))
        .map((row) => row.invariant);
};

export const nextUnraised = function nextUnraised(agenda: string, venues: readonly string[]): string | null {
    return admissibleInvariants(agenda, venues)[0] ?? null;
};