import type { CodePointSpan } from 'source-region'; import type { ParseError } from './parse_errors'; export type ConcreteInfo = { span: CodePointSpan }; export type ConcreteError = ConcreteErrorNode[] // Convention: can't be empty. export type ConcreteErrorNode = { span: CodePointSpan, error: ParseError, panickedOver?: CodePointSpan, } export namespace ConcreteError { export function single(node: ConcreteErrorNode): ConcreteError { return [node]; } } export type DelimiterToken = | { tag: "open-paren", span: CodePointSpan } | { tag: "close-paren", span: CodePointSpan } | { tag: "open-bracket", span: CodePointSpan } | { tag: "close-bracket", span: CodePointSpan } export namespace DelimiterToken { export function openParen(span: CodePointSpan): DelimiterToken { return { tag: "open-paren", span }; } export function closeParen(span: CodePointSpan): DelimiterToken { return { tag: "close-paren", span }; } export function openBracket(span: CodePointSpan): DelimiterToken { return { tag: "open-bracket", span }; } export function closeBracket(span: CodePointSpan): DelimiterToken { return { tag: "close-bracket", span }; } } export type Program = { tag: "program", expressions: Expr[], error?: Error, } & Info export type Expr = | Literal | List | { tag: "error-expression", error: Error } & Info // This is for errors that don't really correspond to any sort of node. Unknown errors. export type List = { tag: "list", open: DelimiterToken, items: ListItem[], close?: DelimiterToken, error?: Error } & Info export type ListItem = | Expr | { tag: "error-list-separator", error: Error } & Info export type Literal = // === number === | { tag: "number", value: number } & Info | { tag: "error-number", error: Error } & Info // === identifier === | { tag: "identifier", value: Identifier } & Info | { tag: "error-identifier", error: Error } & Info export type Identifier = string export namespace Program { export function make( expressions: Expr[], info: Info, error?: Error, ): Program { return error === undefined ? { tag: "program", expressions, ...info } : { tag: "program", expressions, error, ...info }; } export function show(program: Program): string { return program.expressions.map(Expr.show).join(" "); } } export namespace Expr { export function number(value: number, span: CodePointSpan): Expr { return { tag: "number", value, span }; } export function errorNumber(error: ConcreteError, span: CodePointSpan): Expr { return { tag: "error-number", error, span }; } export function identifier(value: Identifier, span: CodePointSpan): Expr { return { tag: "identifier", value, span }; } export function errorIdentifier(error: ConcreteError, span: CodePointSpan): Expr { return { tag: "error-identifier", error, span }; } export function list( open: DelimiterToken, items: ListItem[], span: CodePointSpan, close?: DelimiterToken, error?: ConcreteError, ): Expr { return { tag: "list", open, items, close, error, span }; } export function errorExpression(error: ConcreteError, span: CodePointSpan): Expr { return { tag: "error-expression", error, span }; } export function show(expr: Expr): string { switch (expr.tag) { case "number": return `${expr.value}`; case "identifier": return expr.value; case "error-number": return ""; case "error-identifier": return ""; case "error-expression": return ""; case "list": return showList(expr); } } function showList(list: List): string { const open = list.open.tag === "open-bracket" ? "[" : "("; const close = list.open.tag === "open-bracket" ? "]" : ")"; const sep = list.open.tag === "open-bracket" ? ", " : " "; return `${open}${list.items.map(ListItem.show).join(sep)}${close}`; } } export namespace ListItem { export function errorSeparator(error: ConcreteError, span: CodePointSpan): ListItem { return { tag: "error-list-separator", error, span }; } export function show(item: ListItem): string { if (item.tag === "error-list-separator") return ""; return Expr.show(item); } }