import { ParseError } from "src/lang/parser/parser"; import { renderSpan, SourceText } from "src/lang/parser/source_text"; import { DisplayLineViews } from "./LineView"; export function formatErrorMesage(err: ParseError): string { switch (err.tag) { case "UnexpectedCharacter": return `Unexpected character: ${formatChar(err.char)}`; case "UnexpectedEOF": return "Unexpected end of file."; case "ExpectedNumber": return "Expected a number here."; case "InvalidNumber": switch (err.reason) { case "NotFinite": return "Number is too large or invalid."; case "MissingFractionalDigits": return "Invalid number format (missing fractional digits?)."; } case "InvalidEscape": switch (err.reason.tag) { case "UnknownEscapeSequence": return `Unknown escape sequence: \\${formatChar(err.reason.char)}`; case "UnicodeMissingBrace": return "Unicode escape missing opening brace '{'."; case "UnicodeNoDigits": return "Unicode escape missing hex digits."; case "UnicodeUnclosed": return "Unicode escape missing closing brace '}'."; case "UnicodeOverflow": return `Unicode code point ${err.reason.value.toString(16)} is out of bounds.`; } case "InvalidIdentifier": { let identifierKind = ""; switch (err.kind) { case "variable_use": identifierKind = "variable name"; break; case "field_name": identifierKind = "field name "; break; case "tag_construction": identifierKind = "tag"; break; case "function_call": identifierKind = "function name"; break; case "signal_read": identifierKind = "signal name"; break; case "pattern_binding": identifierKind = "pattern variable"; break; } let reason = ""; switch (err.reason.tag) { case "Empty": reason = "It's empty"; break; case "StartsWithDigit": reason = "Can't start with a digit" break; case "IsKeyword": reason = "I'ts a keyword"; break; } return `Invalid ${identifierKind} '${err.text}' ${reason}.`; } case "UnexpectedIdentifier": return `Unexpected identifier encountered '${err.identifier}'`; case "UnexpectedToken": return `Unexpected token. Expected: ${err.expected}`; case "UnexpectedTokenWhileParsingSequence": return `Unexpected token in sequence. Expected delimiter ${formatChar(err.expectedDelimiter)} or terminator ${formatChar(err.expectedTerminator)}, but found ${formatChar(err.received)}.`; // Context specific errors case "ExpectedExpression": return "Expected an expression here."; case "ExpectedSignalExpression": return "Expected a signal expression here."; case "ExpectedFieldAssignmentSymbol": return "Expected '=' for field assignment."; case "ExpectedPatternAssignmentSymbol": return "Expected '=' for pattern assignment."; case "ExpectedPatternBindingSymbol": return "Expected '.' for pattern binding."; case "ExpectedFunctionCallStart": return "Expected '(' to start function call."; case "ExpectedRecordOpen": return "Expected '(' to start record."; case "ExpectedLetBlockOpen": return "Expected '{' to start let-block."; case "ExpectedLetBlockClose": return "Expected '}' to close let-block."; case "ExpectedLetSignalBlockOpen": return "Expected '{' to start let-signal-block."; case "ExpectedLetSignalBlockClose": return "Expected '}' to close let-signal-block."; case "ExpectedMatchBlockOpen": return "Expected '{' to start match-block."; case "ExpectedMatchBlockClose": return "Expected '}' to close match-block."; case "ExpectedLambdaBlockOpen": return "Expected '{' to start lambda body."; case "ExpectedLambdaBlockClose": return "Expected '}' to close lambda body."; case "ExpectedApplyStart": return "Expected '(' after 'apply'."; case "ExpectedApplySeparator": return "Expected '!' inside 'apply'."; case "UnexpectedTagPattern": return "Unexpected tag pattern (expected product pattern)."; case "ExpectedPattern": return "Expected a pattern here."; case "ExpectedRecordPatternOpen": return "Expected a ':(' at start of record pattern here."; case "ExpectedRecordField": return "Expected a field name in record pattern."; } } // Helper to safely print code points (handling special chars like \n) function formatChar(cp: number | undefined): string { // Handle EOF (undefined) or invalid numbers safely if (cp === undefined || Number.isNaN(cp)) { return "EOF"; } const s = String.fromCodePoint(cp); if (s === '\n') return "\\n"; if (s === '\r') return "\\r"; if (s === '\t') return "\\t"; return `'${s}'`; } export function ShowParseError(props: { text: SourceText, err: ParseError }) { const msg = () => formatErrorMesage(props.err); const views = () => renderSpan(props.text, props.err.span, 3); // Parse Error: Expected '(' to start function call. // 1 | +(23, x) // ^ return (
Parse Error: {msg()}
); }