From 25a5b20d5f089e5fcd19693e795b9cf343073cd7 Mon Sep 17 00:00:00 2001 From: Araozu Date: Mon, 12 Aug 2024 20:31:19 -0500 Subject: [PATCH] feat: improve error rendering on code snippets --- src/components/docs/CodeError.astro | 2 +- src/lexer/highlighter.ts | 55 +++++++++++++++++++--- src/pages/learn/basics/datatypes.mdx | 3 ++ src/pages/learn/basics/operators.mdx | 3 +- src/pages/learn/functions/declaration.mdx | 6 +-- src/pages/learn/functions/higher-order.mdx | 2 +- 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/components/docs/CodeError.astro b/src/components/docs/CodeError.astro index 6492ac4..3334ae9 100644 --- a/src/components/docs/CodeError.astro +++ b/src/components/docs/CodeError.astro @@ -4,5 +4,5 @@ const { error_type = "Unknown", error_message } = Astro.props;
{error_type} error: - {error_message} + {error_message}
diff --git a/src/lexer/highlighter.ts b/src/lexer/highlighter.ts index 0eef368..2bb2e28 100644 --- a/src/lexer/highlighter.ts +++ b/src/lexer/highlighter.ts @@ -1,6 +1,12 @@ import { spawn } from "node:child_process"; import { leftTrimDedent } from "../components/utils"; +export type ReferenceItem = { + symbol_start: number + symbol_end: number + reference: string +} + export interface Token { token_type: TokenType value: string @@ -55,13 +61,13 @@ export interface SemanticError { } export interface TokenizeResult { - Ok?: Token[], + Ok?: [Array, Array], SyntaxOnly?: [Token[], Err], TokensOnly?: [Token[], Err], Err?: Err, } -const error_classes = "underline decoration-wavy decoration-red-500"; +const error_classes = "underline underline-offset-4 decoration-wavy decoration-red-500"; export async function native_highlighter(code: string): Promise<[string, string, string | null]> { let formatted_code = leftTrimDedent(code).join("\n"); @@ -85,7 +91,12 @@ export async function native_highlighter(code: string): Promise<[string, string, return semantic_error_highlighter(formatted_code, tokens, error.Semantic!); } - const tokens = result.Ok!; + const tokens = result.Ok! as unknown as Array; + // TODO: this is disable because the compiler has not + // implemented this feature yet + // const [tokens, references] = result.Ok!; + // console.log("refs:"); + // console.log(references); const output = highlight_tokens(formatted_code, tokens); @@ -107,22 +118,25 @@ function lex_error_highlighter(code: string, error: LexError): [string, string, const token = `${err_str}`; const all = `${before_err}${token}${after_err}`; + const [error_line, error_column] = absolute_to_line_column(code, error.position); // TODO: Transform absolute posijion (error.position) into line:column - return [all, "Lexical", error.reason + " at position " + error.position] + return [all, "Lexical", error.reason + ` at line ${error_line}:${error_column} `] } function syntax_error_highlighter(code: string, tokens: Array, error: SyntaxError): [string, string, string] { const highlighted = highlight_tokens(code, tokens, error.error_start, error.error_end); + const [error_line, error_column] = absolute_to_line_column(code, error.error_start); - const error_message = `${error.reason} from position ${error.error_start} to ${error.error_end}`; + const error_message = `${error.reason} at line ${error_line}:${error_column}`; return [highlighted, "Syntax", error_message]; } function semantic_error_highlighter(code: string, tokens: Array, error: SyntaxError): [string, string, string] { const highlighted = highlight_tokens(code, tokens, error.error_start, error.error_end); + const [error_line, error_column] = absolute_to_line_column(code, error.error_start); - const error_message = `${error.reason} from position ${error.error_start} to ${error.error_end}`; + const error_message = `${error.reason} at line ${error_line}:${error_column}`; return [highlighted, "Semantic", error_message]; } @@ -149,7 +163,7 @@ function highlight_tokens(input: string, tokens: Array, error_start = -1, const token_start = t.position; const token_end = t.position + t.value.length; - let is_errored = (token_start == error_start); + let is_errored = (token_start >= error_start && token_end <= error_end); // Some tokens require processing (like multiline comments) @@ -172,6 +186,33 @@ function highlight_tokens(input: string, tokens: Array, error_start = -1, return output; } +/** + * Transform an absolute position in source code to a line:column combination. + * + * Both line and column are 1-based + * + * @param input the source code + * @param absolute the absolute position + */ +function absolute_to_line_column(input: string, absolute: number): [number, number] { + let line_count = 1; + let last_newline_pos = 0; + + // Count lines + for (let i = 0; i < input.length; i += 1) { + if (i === absolute) { + break; + } + + if (input[i] === "\n") { + line_count += 1; + last_newline_pos = i; + } + } + + return [line_count, absolute - last_newline_pos]; +} + /** * Certain tokens store values that differ from the source code representation. * For example, the multiline comment token stores the content of the comment diff --git a/src/pages/learn/basics/datatypes.mdx b/src/pages/learn/basics/datatypes.mdx index 403444e..6cef198 100644 --- a/src/pages/learn/basics/datatypes.mdx +++ b/src/pages/learn/basics/datatypes.mdx @@ -30,6 +30,9 @@ Int char_code = 0b01000110 Int not_octal = 032 // This is 32, not 26 `} /> +// TODO: Make it a compile error to have leading zeroes, +and force users to use `0o` for octal + ## Float diff --git a/src/pages/learn/basics/operators.mdx b/src/pages/learn/basics/operators.mdx index 5fc8cbb..32e47cf 100644 --- a/src/pages/learn/basics/operators.mdx +++ b/src/pages/learn/basics/operators.mdx @@ -27,7 +27,8 @@ number /= 1 number %= 2 `} /> -**There are no prefix/postfix increment**, use `+=` or `-=`. +**There are no prefix/postfix increment operators** (`++`, `--`), +use `+=` or `-=` instead. () -() -> Int -(Int, Int) -> Int -[T](Array[T]) -> T +() -> (Int) +(Int, Int) -> (Int) +[T](Array[T]) -> (T) `} /> diff --git a/src/pages/learn/functions/higher-order.mdx b/src/pages/learn/functions/higher-order.mdx index 790ac40..d24276f 100644 --- a/src/pages/learn/functions/higher-order.mdx +++ b/src/pages/learn/functions/higher-order.mdx @@ -10,7 +10,7 @@ import Code from "../../../components/Code.astro" ## Function as parameter B function) -> Array[B] +fun map[A, B](Array[A] input, (A) -> (B) function) -> Array[B] { // implementation }