mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-01 05:11:09 +00:00
Merge pull request #212 from GreasySlug/feature/modifier-error
Modify error fomat
This commit is contained in:
commit
ae0cbe936a
15 changed files with 1223 additions and 269 deletions
|
@ -27,11 +27,7 @@ homepage = "https://erg-lang.org/"
|
|||
|
||||
[features]
|
||||
# when "debug" feature is turned on, that of the following crates will also be turned on.
|
||||
debug = [
|
||||
"erg_common/debug",
|
||||
"erg_parser/debug",
|
||||
"erg_compiler/debug",
|
||||
]
|
||||
debug = ["erg_common/debug", "erg_parser/debug", "erg_compiler/debug"]
|
||||
japanese = [
|
||||
"erg_common/japanese",
|
||||
"erg_parser/japanese",
|
||||
|
@ -47,6 +43,8 @@ traditional_chinese = [
|
|||
"erg_parser/traditional_chinese",
|
||||
"erg_compiler/traditional_chinese",
|
||||
]
|
||||
unicode = ["erg_common/unicode", "erg_parser/unicode", "erg_compiler/unicode"]
|
||||
pretty = ["erg_common/pretty", "erg_parser/pretty", "erg_compiler/unicode"]
|
||||
pre-commit = []
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -9,13 +9,14 @@ edition.workspace = true
|
|||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
japanese = []
|
||||
simplified_chinese = []
|
||||
traditional_chinese = []
|
||||
unicode = []
|
||||
pretty = []
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { version = "0.2", default-features = false }
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//! Escape sequences change the color of the terminal
|
||||
|
||||
pub const RESET: &str = "\x1b[m";
|
||||
pub const DEEP_RED: &str = "\x1b[31m";
|
||||
pub const RED: &str = "\x1b[91m";
|
||||
pub const GREEN: &str = "\x1b[92m";
|
||||
pub const YELLOW: &str = "\x1b[93m";
|
||||
pub const BLUE: &str = "\x1b[94m";
|
||||
pub const CYAN: &str = "\x1b[96m";
|
|
@ -3,14 +3,19 @@
|
|||
//! エラー処理に関する汎用的なコンポーネントを提供する
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::fmt::Write as _;
|
||||
use std::io::{stderr, BufWriter, Write as _};
|
||||
|
||||
use crate::astr::AtomicStr;
|
||||
use crate::color::*;
|
||||
use crate::config::Input;
|
||||
use crate::style::Attribute;
|
||||
use crate::style::Characters;
|
||||
use crate::style::Color;
|
||||
use crate::style::StyledStr;
|
||||
use crate::style::StyledString;
|
||||
use crate::style::StyledStrings;
|
||||
use crate::style::Theme;
|
||||
use crate::traits::{Locational, Stream};
|
||||
use crate::{fmt_option, impl_display_from_debug, switch_lang};
|
||||
use crate::{impl_display_from_debug, switch_lang};
|
||||
|
||||
/// ErrorKindと言っているが、ErrorだけでなくWarning, Exceptionも含まれる
|
||||
/// Numbering of this is not specifically related to ErrFmt.errno().
|
||||
|
@ -217,23 +222,72 @@ impl From<&str> for ErrorKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// points the location (of an error) in a code
|
||||
///
|
||||
/// Points the location (of an error) in a code.
|
||||
/// The beginning and end of each row and column where the error occurred.
|
||||
/// Basically, the beginning and end of each row and column where the error occurred is kept.
|
||||
///
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum Location {
|
||||
///
|
||||
/// Error used when the error is caused by a discrepancy with a code on another line
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Ownership error
|
||||
///
|
||||
/// ```erg
|
||||
/// a: Nat = 1
|
||||
/// a.consume_ownership() // move occurs
|
||||
///
|
||||
/// function(a) // borrowed after moved
|
||||
/// ```
|
||||
///
|
||||
/// `a` moves ownership in a method(or function) that are defined and consume it.
|
||||
///
|
||||
/// ```erg
|
||||
/// Location::RangePair {
|
||||
/// ln_first: (2, 2),
|
||||
/// col_first: (0, 1),
|
||||
/// ln_second: (4, 4),
|
||||
/// col_second: (9, 10),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
RangePair {
|
||||
ln_first: (usize, usize),
|
||||
col_first: (usize, usize),
|
||||
ln_second: (usize, usize),
|
||||
col_second: (usize, usize),
|
||||
},
|
||||
///
|
||||
/// Location used for basic errors
|
||||
/// ```erg
|
||||
/// // erg
|
||||
/// a = 1
|
||||
/// a = 2
|
||||
/// // Value assigned to the structure
|
||||
/// Location::Range {
|
||||
/// ln_begin: 2,
|
||||
/// col_begin: 0,
|
||||
/// ln_end: 2,
|
||||
/// col_end: 1,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
Range {
|
||||
ln_begin: usize,
|
||||
col_begin: usize,
|
||||
ln_end: usize,
|
||||
col_end: usize,
|
||||
},
|
||||
/// Used for loss of location information when desugared.
|
||||
/// If there are guaranteed to be multiple rows
|
||||
LineRange(usize, usize),
|
||||
/// Used when Location information is lost when desugared
|
||||
/// If it is guaranteed to be a single line
|
||||
Line(usize),
|
||||
/// Used by default in case of loss of Location information
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
@ -315,8 +369,8 @@ impl Location {
|
|||
}
|
||||
}
|
||||
|
||||
/// Erg内で使われるエラーの共通部分
|
||||
/// 使用する場合は必ずwrapすること
|
||||
/// In Erg, common parts used by error.
|
||||
/// Must be wrap when to use.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ErrorCore {
|
||||
pub errno: usize,
|
||||
|
@ -358,79 +412,112 @@ impl ErrorCore {
|
|||
}
|
||||
|
||||
pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
|
||||
const URL: StyledStr = StyledStr::new(
|
||||
"https://github.com/erg-lang/erg",
|
||||
Some(Color::White),
|
||||
Some(Attribute::Underline),
|
||||
);
|
||||
|
||||
Self::new(
|
||||
errno,
|
||||
CompilerSystemError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("これはErgのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg的bug,请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("这是Erg的bug,请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of Erg, please report it to https://github.com/erg-lang/erg\ncaused from: {fn_name}:{line}"),
|
||||
"japanese" => format!("これはErgのバグです、開発者に報告して下さい({URL})\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of Erg, please report it to {URL}\ncaused from: {fn_name}:{line}"),
|
||||
),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub const VBAR_UNICODE: &str = "│";
|
||||
pub const VBAR_BREAK_UNICODE: &str = "·";
|
||||
|
||||
fn format_code_and_pointer<E: ErrorDisplay + ?Sized>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn format_context<E: ErrorDisplay + ?Sized>(
|
||||
e: &E,
|
||||
ln_begin: usize,
|
||||
ln_end: usize,
|
||||
col_begin: usize,
|
||||
col_end: usize,
|
||||
err_color: Color,
|
||||
gutter_color: Color,
|
||||
// for formatting points
|
||||
chars: &Characters,
|
||||
// kinds of error for specify the color
|
||||
mark: char,
|
||||
) -> String {
|
||||
let mark = mark.to_string();
|
||||
let codes = if e.input().is_repl() {
|
||||
vec![e.input().reread()]
|
||||
} else {
|
||||
e.input().reread_lines(ln_begin, ln_end)
|
||||
};
|
||||
let mut res = CYAN.to_string();
|
||||
let mut context = StyledStrings::default();
|
||||
let final_step = ln_end - ln_begin;
|
||||
let max_digit = ln_end.to_string().len();
|
||||
let (vbreak, vbar) = chars.gutters();
|
||||
let offset = format!("{} {} ", &" ".repeat(max_digit), vbreak);
|
||||
for (i, lineno) in (ln_begin..=ln_end).enumerate() {
|
||||
let mut pointer = " ".repeat(lineno.to_string().len() + 2); // +2 means `| `
|
||||
context.push_str_with_color(
|
||||
&format!("{:<max_digit$} {vbar} ", lineno, vbar = vbar),
|
||||
gutter_color,
|
||||
);
|
||||
context.push_str(&codes[i]);
|
||||
context.push_str("\n");
|
||||
context.push_str_with_color(&offset, gutter_color);
|
||||
if i == 0 && i == final_step {
|
||||
pointer += &" ".repeat(col_begin);
|
||||
pointer += &"^".repeat(cmp::max(1, col_end.saturating_sub(col_begin)));
|
||||
context.push_str(&" ".repeat(col_begin));
|
||||
context.push_str_with_color(
|
||||
&mark.repeat(cmp::max(1, col_end.saturating_sub(col_begin))),
|
||||
err_color,
|
||||
);
|
||||
} else if i == 0 {
|
||||
pointer += &" ".repeat(col_begin);
|
||||
pointer += &"^".repeat(cmp::max(1, codes[i].len().saturating_sub(col_begin)));
|
||||
context.push_str(&" ".repeat(col_begin));
|
||||
context.push_str_with_color(
|
||||
&mark.repeat(cmp::max(1, codes[i].len().saturating_sub(col_begin))),
|
||||
err_color,
|
||||
);
|
||||
} else if i == final_step {
|
||||
pointer += &"^".repeat(col_end);
|
||||
context.push_str_with_color(&mark.repeat(col_end), err_color);
|
||||
} else {
|
||||
pointer += &"^".repeat(cmp::max(1, codes[i].len()));
|
||||
context.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), err_color);
|
||||
}
|
||||
writeln!(
|
||||
res,
|
||||
"{lineno}{VBAR_UNICODE} {code}\n{pointer}",
|
||||
code = codes.get(i).unwrap_or(&String::new()),
|
||||
)
|
||||
.unwrap();
|
||||
context.push_str("\n");
|
||||
}
|
||||
res + RESET
|
||||
context.push_str_with_color(&offset, gutter_color);
|
||||
context.push_str(&" ".repeat(col_end - 1));
|
||||
context.push_str_with_color(&chars.left_bottom_line(), err_color);
|
||||
context.to_string()
|
||||
}
|
||||
|
||||
/// format:
|
||||
/// ```console
|
||||
/// ```txt
|
||||
/// Error[#{.errno}]: File {file}, line {.loc (as line)}, in {.caused_by}
|
||||
/// {.loc (as line)}| {src}
|
||||
/// {pointer}
|
||||
/// {.kind}: {.desc}
|
||||
///
|
||||
/// {.hint}
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// example:
|
||||
/// ```console
|
||||
/// Error[#12]: File <stdin>, line 1, in <module>
|
||||
/// 1| 100 = i
|
||||
/// ^^^
|
||||
/// SyntaxError: cannot assign to 100
|
||||
/// ```txt
|
||||
/// Error[#2223]: File <stdin>, line 1, in <module>
|
||||
///
|
||||
/// 1 | 100 = i
|
||||
/// ---
|
||||
/// ╰─ SyntaxError: cannot assign to 100
|
||||
///
|
||||
/// hint: hint message here
|
||||
///
|
||||
/// ```
|
||||
pub trait ErrorDisplay {
|
||||
fn core(&self) -> &ErrorCore;
|
||||
fn input(&self) -> &Input;
|
||||
/// Colors and indication char for each type(error, warning, exception)
|
||||
fn theme(&self) -> &Theme;
|
||||
/// The block name the error caused.
|
||||
/// This will be None if the error occurred before semantic analysis.
|
||||
/// As for the internal error, do not put the fn name here.
|
||||
|
@ -453,27 +540,109 @@ pub trait ErrorDisplay {
|
|||
}
|
||||
|
||||
fn show(&self) -> String {
|
||||
let theme = self.theme();
|
||||
let ((color, mark), kind) = if self.core().kind.is_error() {
|
||||
(theme.error(), "Error")
|
||||
} else if self.core().kind.is_warning() {
|
||||
(theme.warning(), "Warning")
|
||||
} else {
|
||||
(theme.exception(), "Exception")
|
||||
};
|
||||
|
||||
let (gutter_color, chars) = theme.characters();
|
||||
let kind = StyledString::new(
|
||||
&chars.error_kind_format(kind, self.core().errno),
|
||||
Some(color),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
|
||||
// When hint is None, hint desc is "" and empty line is displayed, but hint is Some(...), hint desc is "..." and filled by text
|
||||
if let Some(hint) = self.core().hint.as_ref() {
|
||||
let (hint_color, _) = theme.hint();
|
||||
let mut hints = StyledStrings::default();
|
||||
hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold);
|
||||
hints.push_str(hint);
|
||||
format!(
|
||||
"{}{}{}: {}{}\n",
|
||||
self.format_header(),
|
||||
self.format_code_and_pointer(),
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
{}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), self.core().hint)
|
||||
hints,
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// for fmt::Display
|
||||
fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let theme = self.theme();
|
||||
let ((color, mark), kind) = if self.core().kind.is_error() {
|
||||
(theme.error(), "Error")
|
||||
} else if self.core().kind.is_warning() {
|
||||
(theme.warning(), "Warning")
|
||||
} else {
|
||||
(theme.exception(), "Exception")
|
||||
};
|
||||
let (gutter_color, chars) = theme.characters();
|
||||
let kind = StyledString::new(
|
||||
&chars.error_kind_format(kind, self.core().errno),
|
||||
Some(color),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
|
||||
// When hint is None, hint desc is "" and empty line is displayed, but hint is Some(...), hint desc is "..." and filled by text
|
||||
if let Some(hint) = self.core().hint.as_ref() {
|
||||
let (hint_color, _) = theme.hint();
|
||||
let mut hints = StyledStrings::default();
|
||||
hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold);
|
||||
hints.push_str(hint);
|
||||
writeln!(
|
||||
f,
|
||||
"{}{}{}: {}{}",
|
||||
self.format_header(),
|
||||
self.format_code_and_pointer(),
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
{}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), self.core().hint),
|
||||
hints,
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
f,
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
)?;
|
||||
}
|
||||
if let Some(inner) = self.ref_inner() {
|
||||
inner.format(f)
|
||||
} else {
|
||||
|
@ -481,14 +650,7 @@ pub trait ErrorDisplay {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_header(&self) -> String {
|
||||
let (color, err_or_warn) = if self.core().kind.is_warning() {
|
||||
(YELLOW, "Warning")
|
||||
} else if self.core().kind.is_exception() {
|
||||
("", "Exception")
|
||||
} else {
|
||||
(RED, "Error")
|
||||
};
|
||||
fn format_header(&self, kind: StyledString) -> String {
|
||||
let loc = match self.core().loc {
|
||||
Location::Range {
|
||||
ln_begin, ln_end, ..
|
||||
|
@ -511,27 +673,49 @@ pub trait ErrorDisplay {
|
|||
"".to_string()
|
||||
};
|
||||
format!(
|
||||
"{color}{err_or_warn}[#{errno:>04}]{RESET}: File {input}{loc}{caused_by}\n",
|
||||
errno = self.core().errno,
|
||||
"{kind}: File {input}{loc}{caused_by}\n",
|
||||
input = self.input().enclosed_name(),
|
||||
)
|
||||
}
|
||||
|
||||
fn format_code_and_pointer(&self) -> String {
|
||||
fn format_code_and_pointer(
|
||||
&self,
|
||||
err_color: Color,
|
||||
gutter_color: Color,
|
||||
mark: char,
|
||||
chars: &Characters,
|
||||
) -> String {
|
||||
match self.core().loc {
|
||||
// TODO: Current implementation does not allow for multiple descriptions of errors to be given at each location
|
||||
// In the future, this will be implemented in a different structure that can handle multiple lines and files
|
||||
Location::RangePair {
|
||||
ln_first,
|
||||
col_first,
|
||||
ln_second,
|
||||
col_second,
|
||||
} => {
|
||||
format_code_and_pointer(self, ln_first.0, ln_first.1, col_first.0, col_first.1)
|
||||
+ &format_code_and_pointer(
|
||||
format_context(
|
||||
self,
|
||||
ln_first.0,
|
||||
ln_first.1,
|
||||
col_first.0,
|
||||
col_first.1,
|
||||
err_color,
|
||||
gutter_color,
|
||||
chars,
|
||||
mark,
|
||||
) +
|
||||
"\n" // TODO: dealing with error chains
|
||||
+ &format_context(
|
||||
self,
|
||||
ln_second.0,
|
||||
ln_second.1,
|
||||
col_second.0,
|
||||
col_second.1,
|
||||
err_color,
|
||||
gutter_color,
|
||||
chars,
|
||||
mark,
|
||||
)
|
||||
}
|
||||
Location::Range {
|
||||
|
@ -539,40 +723,57 @@ pub trait ErrorDisplay {
|
|||
col_begin,
|
||||
ln_end,
|
||||
col_end,
|
||||
} => format_code_and_pointer(self, ln_begin, ln_end, col_begin, col_end),
|
||||
} => format_context(
|
||||
self,
|
||||
ln_begin,
|
||||
ln_end,
|
||||
col_begin,
|
||||
col_end,
|
||||
err_color,
|
||||
gutter_color,
|
||||
chars,
|
||||
mark,
|
||||
),
|
||||
Location::LineRange(ln_begin, ln_end) => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let mut cxt = StyledStrings::default();
|
||||
let codes = if self.input().is_repl() {
|
||||
vec![self.input().reread()]
|
||||
} else {
|
||||
self.input().reread_lines(ln_begin, ln_end)
|
||||
};
|
||||
let mut res = CYAN.to_string();
|
||||
let mark = mark.to_string();
|
||||
for (i, lineno) in (ln_begin..=ln_end).enumerate() {
|
||||
let mut pointer = " ".repeat(lineno.to_string().len() + 2); // +2 means `| `
|
||||
pointer += &"^".repeat(cmp::max(1, codes[i].len()));
|
||||
writeln!(
|
||||
res,
|
||||
"{lineno}{VBAR_UNICODE} {code}\n{pointer}",
|
||||
code = codes[i]
|
||||
)
|
||||
.unwrap();
|
||||
cxt.push_str_with_color(&format!("{lineno} {}", vbar), err_color);
|
||||
cxt.push_str(&codes[i]);
|
||||
cxt.push_str(&" ".repeat(lineno.to_string().len() + 3)); // +3 means ` | `
|
||||
cxt.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), gutter_color)
|
||||
}
|
||||
res + RESET
|
||||
cxt.to_string()
|
||||
}
|
||||
Location::Line(lineno) => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let code = if self.input().is_repl() {
|
||||
self.input().reread()
|
||||
} else {
|
||||
self.input().reread_lines(lineno, lineno).remove(0)
|
||||
};
|
||||
format!("{CYAN}{lineno}{VBAR_UNICODE} {code}\n{RESET}")
|
||||
let mut cxt = StyledStrings::default();
|
||||
cxt.push_str_with_color(&format!(" {lineno} {} ", vbar), gutter_color);
|
||||
cxt.push_str(&code);
|
||||
cxt.push_str("\n");
|
||||
cxt.to_string()
|
||||
}
|
||||
Location::Unknown => match self.input() {
|
||||
Input::File(_) => "\n".to_string(),
|
||||
other => format!(
|
||||
"{CYAN}?{VBAR_UNICODE} {code}\n{RESET}",
|
||||
code = other.reread()
|
||||
),
|
||||
|
||||
other => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let mut cxt = StyledStrings::default();
|
||||
cxt.push_str_with_color(&format!(" ? {}", vbar), gutter_color);
|
||||
cxt.push_str(&other.reread());
|
||||
cxt.to_string()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use std::fmt;
|
|||
|
||||
pub mod astr;
|
||||
pub mod cache;
|
||||
pub mod color;
|
||||
pub mod config;
|
||||
pub mod datetime;
|
||||
pub mod dict;
|
||||
|
@ -24,6 +23,7 @@ pub mod set;
|
|||
pub mod shared;
|
||||
pub mod stdin;
|
||||
pub mod str;
|
||||
pub mod style;
|
||||
pub mod traits;
|
||||
pub mod tsort;
|
||||
pub mod tty;
|
||||
|
|
|
@ -353,7 +353,7 @@ macro_rules! debug_enum_assert {
|
|||
macro_rules! debug_info {
|
||||
($output:ident) => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::color::{CYAN, RESET};
|
||||
use $crate::style::{CYAN, RESET};
|
||||
write!(
|
||||
$output,
|
||||
"[{}DEBUG{}] {}:{:04}: ",
|
||||
|
@ -366,7 +366,7 @@ macro_rules! debug_info {
|
|||
}};
|
||||
() => {{
|
||||
#[allow(unused_imports)]
|
||||
use $crate::color::{CYAN, RESET};
|
||||
use $crate::style::{CYAN, RESET};
|
||||
print!("[{}DEBUG{}] {}:{:04}: ", CYAN, RESET, file!(), line!());
|
||||
}};
|
||||
}
|
||||
|
@ -400,7 +400,7 @@ macro_rules! log {
|
|||
|
||||
(f $output: ident, $($arg: tt)*) => {{
|
||||
if cfg!(feature = "debug") {
|
||||
use $crate::color::RESET;
|
||||
use $crate::color::{RESET, GREEN, RED};
|
||||
$crate::debug_info!($output);
|
||||
write!($output, $($arg)*).unwrap();
|
||||
write!($output, "{}", RESET).unwrap(); // color color anyway
|
||||
|
@ -410,7 +410,7 @@ macro_rules! log {
|
|||
|
||||
(c $color:ident, $($arg: tt)*) => {{
|
||||
if cfg!(feature = "debug") {
|
||||
use $crate::color::*;
|
||||
use $crate::style::{RESET, GREEN, RED};
|
||||
$crate::debug_info!();
|
||||
print!("{}", $color);
|
||||
println!($($arg)*);
|
||||
|
@ -420,9 +420,9 @@ macro_rules! log {
|
|||
|
||||
(f+c $output:ident, $color:ident, $($arg: tt)*) => {{
|
||||
if cfg!(feature = "debug") {
|
||||
use $crate::color::*;
|
||||
use $crate::style::{RESET, GREEN};
|
||||
$crate::debug_info!($output);
|
||||
write!($output, "{}", $color).unwrap();
|
||||
write!($output, "{}{}{}", $color, $($arg)*, RESET).unwrap();
|
||||
write!($output, $($arg)*).unwrap();
|
||||
write!($output, "{}", RESET).unwrap(); // reset color anyway
|
||||
$output.flush().unwrap();
|
||||
|
@ -431,7 +431,7 @@ macro_rules! log {
|
|||
|
||||
($($arg: tt)*) => {{
|
||||
if cfg!(feature = "debug") {
|
||||
use $crate::color::*;
|
||||
use $crate::style::*;
|
||||
$crate::debug_info!();
|
||||
println!($($arg)*);
|
||||
print!("{}", RESET); // reset color anyway
|
||||
|
|
522
compiler/erg_common/style.rs
Normal file
522
compiler/erg_common/style.rs
Normal file
|
@ -0,0 +1,522 @@
|
|||
pub const ATTR_RESET: &str = "\x1b[0m";
|
||||
pub const BOLD: &str = "\x1b[1m";
|
||||
pub const UNDERLINE: &str = "\x1b[4m";
|
||||
pub const REVERSED: &str = "\x1b[7m";
|
||||
|
||||
// Escape sequences change the color of the terminal
|
||||
pub const RESET: &str = "\x1b[m";
|
||||
pub const BLACK: &str = "\x1b[30m";
|
||||
pub const BLUE: &str = "\x1b[94m";
|
||||
pub const CYAN: &str = "\x1b[96m";
|
||||
pub const GRAY: &str = "\x1b[37m";
|
||||
pub const GREEN: &str = "\x1b[92m";
|
||||
pub const MAGENTA: &str = "\x1b[95m";
|
||||
pub const RED: &str = "\x1b[91m";
|
||||
pub const WHITE: &str = "\x1b[97m";
|
||||
pub const YELLOW: &str = "\x1b[93m";
|
||||
// custom colors when use `pretty`
|
||||
pub const CUSTOM_RED: &str = "\x1b[38;2;185;64;71m";
|
||||
pub const CUSTOM_BLUE: &str = "\x1b[38;2;230;234;227m";
|
||||
pub const CUSTOM_GRAY: &str = "\x1b[38;2;244;0;25m";
|
||||
pub const CUSTOM_CYAN: &str = "\x1b[38;2;160;216;239m";
|
||||
pub const CUSTOM_MAGENTA: &str = "\x1b[38;2;103;65;150m";
|
||||
pub const CUSTOM_GREEN: &str = "\x1b[38;2;170;209;71m";
|
||||
pub const CUSTOM_YELLOW: &str = "\x1b[38;2;230;180;34m";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub enum Color {
|
||||
Reset,
|
||||
Black,
|
||||
Blue,
|
||||
Cyan,
|
||||
Gray,
|
||||
Green,
|
||||
Magenta,
|
||||
Red,
|
||||
White,
|
||||
Yellow,
|
||||
CustomRed,
|
||||
CustomBlue,
|
||||
CustomGray,
|
||||
CustomCyan,
|
||||
CustomMagenta,
|
||||
CustomGreen,
|
||||
CustomYellow,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Color::Reset => RESET,
|
||||
Color::Black => BLACK,
|
||||
Color::Blue => BLUE,
|
||||
Color::Cyan => CYAN,
|
||||
Color::Gray => GRAY,
|
||||
Color::Green => GREEN,
|
||||
Color::Magenta => MAGENTA,
|
||||
Color::Red => RED,
|
||||
Color::Yellow => YELLOW,
|
||||
Color::White => WHITE,
|
||||
Color::CustomRed => CUSTOM_RED,
|
||||
Color::CustomBlue => CUSTOM_BLUE,
|
||||
Color::CustomGray => CUSTOM_GRAY,
|
||||
Color::CustomCyan => CUSTOM_CYAN,
|
||||
Color::CustomMagenta => CUSTOM_MAGENTA,
|
||||
Color::CustomGreen => CUSTOM_GREEN,
|
||||
Color::CustomYellow => CUSTOM_YELLOW,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
||||
pub enum Attribute {
|
||||
Reset,
|
||||
Underline,
|
||||
Bold,
|
||||
Reversed,
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Attribute::Reset => ATTR_RESET,
|
||||
Attribute::Underline => UNDERLINE,
|
||||
Attribute::Bold => BOLD,
|
||||
Attribute::Reversed => REVERSED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ThemeColors {
|
||||
pub error: Color,
|
||||
pub warning: Color,
|
||||
pub exception: Color,
|
||||
pub gutter: Color,
|
||||
pub hint: Color,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pretty"))]
|
||||
pub const COLORS: ThemeColors = ThemeColors {
|
||||
error: Color::Red,
|
||||
warning: Color::Yellow,
|
||||
exception: Color::Magenta,
|
||||
gutter: Color::Cyan,
|
||||
hint: Color::Green,
|
||||
};
|
||||
|
||||
#[cfg(feature = "pretty")]
|
||||
pub const COLORS: ThemeColors = ThemeColors {
|
||||
error: Color::CustomRed,
|
||||
warning: Color::CustomYellow,
|
||||
exception: Color::CustomMagenta,
|
||||
gutter: Color::CustomCyan,
|
||||
hint: Color::CustomGreen,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Characters {
|
||||
hat: char, // error
|
||||
wave: char, // exception
|
||||
line: char, // warning and left bottom line
|
||||
vbar: char, // gutter separator
|
||||
lbot: char, // left bottom curve
|
||||
vbreak: char, // gutter omission
|
||||
lbrac: char, // error kind modifier left bracket
|
||||
rbrac: char, // error kind modifier right bracket
|
||||
}
|
||||
|
||||
impl Characters {
|
||||
pub fn mark(&self, kind: &str) -> String {
|
||||
let mark = match kind {
|
||||
"Error" => self.hat,
|
||||
"Warning" => self.line,
|
||||
"Exception" => self.wave,
|
||||
invalid => panic!("In Characters, Invalid parameter: {invalid}"),
|
||||
};
|
||||
mark.to_string()
|
||||
}
|
||||
|
||||
pub fn gutters(&self) -> (char, char) {
|
||||
(self.vbreak, self.vbar)
|
||||
}
|
||||
|
||||
// " `- "
|
||||
#[cfg(not(feature = "unicode"))]
|
||||
pub fn left_bottom_line(&self) -> String {
|
||||
format!(" {}{} ", self.lbot, self.line)
|
||||
}
|
||||
|
||||
// `╰─ `
|
||||
#[cfg(feature = "unicode")]
|
||||
pub fn left_bottom_line(&self) -> String {
|
||||
format!("{}{} ", self.lbot, self.line)
|
||||
}
|
||||
|
||||
// kind[padded error number]
|
||||
#[cfg(not(feature = "pretty"))]
|
||||
pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String {
|
||||
const PADDING: usize = 4;
|
||||
format!("{kind}{}#{err_num:>0PADDING$}{}", self.lbrac, self.rbrac,)
|
||||
}
|
||||
|
||||
#[cfg(feature = "pretty")]
|
||||
pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String {
|
||||
const PADDING: usize = 4;
|
||||
let emoji = if kind == "Error" {
|
||||
"🚫"
|
||||
} else if kind == "Warning" {
|
||||
"⚠"
|
||||
} else {
|
||||
"😱"
|
||||
};
|
||||
format!(
|
||||
"{emoji} {kind}{}#{err_num:>0PADDING$}{}",
|
||||
self.lbrac, self.rbrac,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Theme {
|
||||
pub colors: ThemeColors,
|
||||
pub characters: Characters,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
pub const fn characters(&self) -> (Color, &Characters) {
|
||||
(self.colors.gutter, &self.characters)
|
||||
}
|
||||
|
||||
pub const fn error(&self) -> (Color, char) {
|
||||
(self.colors.error, self.characters.hat)
|
||||
}
|
||||
|
||||
pub const fn warning(&self) -> (Color, char) {
|
||||
(self.colors.warning, self.characters.line)
|
||||
}
|
||||
|
||||
pub const fn exception(&self) -> (Color, char) {
|
||||
(self.colors.exception, self.characters.wave)
|
||||
}
|
||||
|
||||
pub const fn hint(&self) -> (Color, char) {
|
||||
(self.colors.hint, self.characters.wave)
|
||||
}
|
||||
}
|
||||
|
||||
pub const THEME: Theme = Theme {
|
||||
colors: COLORS,
|
||||
characters: CHARS,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "unicode"))]
|
||||
pub const CHARS: Characters = Characters {
|
||||
hat: '-',
|
||||
line: '-',
|
||||
vbar: '|',
|
||||
wave: '~',
|
||||
lbot: '`',
|
||||
vbreak: ':',
|
||||
lbrac: '[',
|
||||
rbrac: ']',
|
||||
};
|
||||
|
||||
#[cfg(feature = "unicode")]
|
||||
pub const CHARS: Characters = Characters {
|
||||
hat: '-',
|
||||
line: '─',
|
||||
vbar: '│',
|
||||
wave: '~',
|
||||
lbot: '╰',
|
||||
vbreak: '·',
|
||||
lbrac: '[',
|
||||
rbrac: ']',
|
||||
};
|
||||
|
||||
///
|
||||
/// `StyledStr` is for const color and attribute &str.
|
||||
/// It is an immutable string.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use erg_common::style::{Color, Attribute, StyledStr};
|
||||
/// const URL: StyledStr = StyledStr::new(
|
||||
/// "https://github.com/erg-lang/erg",
|
||||
/// Some(Color::White),
|
||||
/// Some(Attribute::Underline),
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct StyledStr<'a> {
|
||||
text: &'a str,
|
||||
color: Option<Color>,
|
||||
attribute: Option<Attribute>,
|
||||
}
|
||||
|
||||
impl<'a> StyledStr<'a> {
|
||||
pub const fn new<'b: 'a>(
|
||||
text: &'b str,
|
||||
color: Option<Color>,
|
||||
attribute: Option<Attribute>,
|
||||
) -> Self {
|
||||
Self {
|
||||
text,
|
||||
color,
|
||||
attribute,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StyledStr<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match (self.color, self.attribute) {
|
||||
(None, None) => todo!(),
|
||||
(None, Some(attr)) => write!(f, "{}{}{}", attr.as_str(), self.text, ATTR_RESET),
|
||||
(Some(color), None) => write!(f, "{}{}{}", color.as_str(), self.text, RESET),
|
||||
(Some(color), Some(attr)) => {
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}{}{}",
|
||||
color.as_str(),
|
||||
attr.as_str(),
|
||||
self.text,
|
||||
RESET,
|
||||
ATTR_RESET
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// `StyledString` is for coloring and attribute text.
|
||||
/// String, Color(&str) and Attribute(&str)
|
||||
///
|
||||
#[derive(Debug)]
|
||||
pub struct StyledString {
|
||||
text: String,
|
||||
color: Option<Color>,
|
||||
attribute: Option<Attribute>,
|
||||
}
|
||||
|
||||
impl StyledString {
|
||||
pub fn new(s: &str, color: Option<Color>, attribute: Option<Attribute>) -> Self {
|
||||
Self {
|
||||
text: String::from(s),
|
||||
color,
|
||||
attribute,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Methods for pushing additional &str for strings that already have attributes or colors.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use erg_common::style::{Color, Attribute, StyledString};
|
||||
/// let mut text = StyledString::new("sample text", None, Some(Attribute::Underline));
|
||||
/// text.push_str("\n");
|
||||
/// text.push_str("Next break line text");
|
||||
/// println!("{text}"); // Two lines of text underlined are displayed
|
||||
/// ```
|
||||
pub fn push_str(&mut self, s: &str) {
|
||||
self.text.push_str(s);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StyledString {
|
||||
fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
|
||||
match (self.color, self.attribute) {
|
||||
(None, None) => write!(f, "{}", self.text),
|
||||
(None, Some(attr)) => write!(f, "{}{}{}", attr.as_str(), self.text, ATTR_RESET),
|
||||
(Some(color), None) => write!(f, "{}{}{}", color.as_str(), self.text, RESET),
|
||||
(Some(color), Some(attr)) => write!(
|
||||
f,
|
||||
"{}{}{}{}{}",
|
||||
attr.as_str(),
|
||||
color.as_str(),
|
||||
self.text,
|
||||
RESET,
|
||||
ATTR_RESET
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// `StyledStrings` is vector of `StyledString` and almost the same as Vec\<String\>.
|
||||
/// It is possible to change the color and attribute of each String.
|
||||
/// That's why, if you don't change any color or attribute, you should use 'StyledString' not `StyledStrings`
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use erg_common::style::{Color, Attribute, StyledStrings};
|
||||
/// let mut texts = StyledStrings::default();
|
||||
/// texts.push_str("Default color is gray, ");
|
||||
/// texts.push_str_with_color("and it is possible to color text.\n", Color::Red);
|
||||
/// texts.push_str("Basically, this `StyledStrings` is one sentence, ");
|
||||
/// texts.push_str_with_color("so if you want to multiline sentences, you need to add `\n`.", Color::Magenta);
|
||||
/// println!("{}", texts); // Pushed colored text are displayed
|
||||
/// ```
|
||||
/// Basically,initialize by default with mutable.
|
||||
/// Then, &str(s) are pushed to the Vec, specifying colors or attributes.
|
||||
///
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StyledStrings {
|
||||
texts: Vec<StyledString>,
|
||||
}
|
||||
|
||||
impl StyledStrings {
|
||||
///
|
||||
/// It is possible push &str type with gray color to Vector.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use erg_common::style::StyledStrings;
|
||||
/// let mut texts = StyledStrings::default();
|
||||
/// texts.push_str("sample text");
|
||||
/// texts.push_str("\n");
|
||||
/// texts.push_str("new text here");
|
||||
/// println!("{}", texts);
|
||||
/// /*
|
||||
/// sample text
|
||||
/// new text here
|
||||
/// */
|
||||
///
|
||||
/// ```
|
||||
pub fn push_str(&mut self, s: &str) {
|
||||
if self.is_same_color(Color::Gray) {
|
||||
self.texts.last_mut().unwrap().text.push_str(s);
|
||||
} else {
|
||||
self.texts.push(StyledString::new(s, None, None));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// It is possible to push &str type with specify color to Vector.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use erg_common::style::{Color, Attribute, StyledStrings};
|
||||
/// let mut texts = StyledStrings::default();
|
||||
/// texts.push_str_with_color("Cyan color text", Color::Cyan);
|
||||
/// texts.push_str_with_color("Red color text", Color::Red);
|
||||
/// texts.push_str_with_color(", pushed texts become a single String.", Color::Yellow);
|
||||
/// texts.push_str_with_color("\n If you want to add break lines, you should add `\n`.", Color::Magenta);
|
||||
/// println!("{}", texts);
|
||||
/// ```
|
||||
pub fn push_str_with_color(&mut self, s: &str, color: Color) {
|
||||
if self.is_same_color(color) {
|
||||
self.texts.last_mut().unwrap().text.push_str(s);
|
||||
} else {
|
||||
self.texts.push(StyledString::new(s, Some(color), None));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Text can be pushed color and attribute to Vector.
|
||||
/// When color or attribute are different, it will be pushed as different String.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use erg_common::style::{Color, Attribute, StyledStrings};
|
||||
/// let mut texts = StyledStrings::default();
|
||||
/// texts.push_str_with_color_and_attribute("Magenta and bold text\n", Color::Magenta, Attribute::Bold);
|
||||
/// texts.push_str_with_color_and_attribute("White and underlined text", Color::White, Attribute::Underline);
|
||||
/// // texts.push_str_with_color_and_attribute("Must be specify the color and attribute", None, Attribute::Underline);
|
||||
/// println!("{}", texts);
|
||||
/// ```
|
||||
pub fn push_str_with_color_and_attribute(&mut self, s: &str, color: Color, attr: Attribute) {
|
||||
if self.is_same_color(color) && self.is_same_attribute(attr) {
|
||||
self.texts.last_mut().unwrap().text.push_str(s);
|
||||
} else {
|
||||
self.texts
|
||||
.push(StyledString::new(s, Some(color), Some(attr)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_color(&self, color: Color) -> bool {
|
||||
if let Some(text) = self.texts.last() {
|
||||
return text.color == Some(color);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_same_attribute(&self, attr: Attribute) -> bool {
|
||||
if let Some(text) = self.texts.last() {
|
||||
if let Some(text_attr) = text.attribute {
|
||||
return text_attr == attr;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StyledStrings {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for text in self.texts.iter() {
|
||||
write!(f, "{}", text)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn text_fg_colorings() {
|
||||
println!("{YELLOW}Hello{RESET}, {RED}World{RESET}");
|
||||
println!("{BLUE}Hello{RESET}, {GREEN}World{RESET}");
|
||||
println!("{MAGENTA}Hello{RESET}, {BLACK}World{RESET}");
|
||||
println!("{GRAY}Hello{RESET}, {WHITE}World{RESET}");
|
||||
println!("{CUSTOM_BLUE}Hello{RESET}, {CUSTOM_CYAN}World{RESET}");
|
||||
println!("{CUSTOM_GRAY}Hello{RESET}, {CUSTOM_GREEN}World{RESET}");
|
||||
println!("{CUSTOM_MAGENTA}Hello{RESET}, {CUSTOM_RED}World{RESET}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_attribute() {
|
||||
println!("{BOLD}BOLD{ATTR_RESET}");
|
||||
println!("{UNDERLINE}UNDERLINED{ATTR_RESET}");
|
||||
println!("{REVERSED}REVERSED{ATTR_RESET}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str_texts_test() {
|
||||
let mut texts = StyledStrings::default();
|
||||
texts.push_str("Gray is the default color\n");
|
||||
texts.push_str("If you specify the color, ");
|
||||
texts.push_str("you should use `push_str_with_color()`\n");
|
||||
|
||||
texts.push_str_with_color(
|
||||
"It is possible to change text foreground color...\n",
|
||||
Color::White,
|
||||
);
|
||||
texts.push_str_with_color("Cyan text, ", Color::Cyan);
|
||||
texts.push_str_with_color("Black text, ", Color::Black);
|
||||
texts.push_str_with_color("Blue text, ", Color::Blue);
|
||||
texts.push_str_with_color("Red text, ", Color::Red);
|
||||
texts.push_str_with_color("pushed texts become a String.", Color::Yellow);
|
||||
texts.push_str_with_color(
|
||||
"\nIf you want to add break lines, you should add `\\n`.\n",
|
||||
Color::Magenta,
|
||||
);
|
||||
|
||||
texts.push_str_with_color(
|
||||
"It is also possible to change text attribute...\n",
|
||||
Color::White,
|
||||
);
|
||||
texts.push_str_with_color_and_attribute(
|
||||
"Green and bold text\n",
|
||||
Color::Green,
|
||||
Attribute::Bold,
|
||||
);
|
||||
texts.push_str_with_color_and_attribute(
|
||||
"White and underlined text",
|
||||
Color::White,
|
||||
Attribute::Underline,
|
||||
);
|
||||
println!("{}", texts);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@ traditional_chinese = [
|
|||
"erg_common/traditional_chinese",
|
||||
"erg_parser/traditional_chinese",
|
||||
]
|
||||
unicode = ["erg_common/unicode"]
|
||||
pretty = ["erg_common/pretty"]
|
||||
|
||||
[dependencies]
|
||||
erg_common = { version = "0.5.11", path = "../erg_common" }
|
||||
|
|
|
@ -7,8 +7,8 @@ use crate::ty::constructors::{and, mono};
|
|||
use crate::ty::value::{EvalValueResult, GenTypeObj, TypeObj, ValueObj};
|
||||
use crate::ty::ValueArgs;
|
||||
use erg_common::astr::AtomicStr;
|
||||
use erg_common::color::{RED, RESET, YELLOW};
|
||||
use erg_common::error::{ErrorCore, ErrorKind, Location};
|
||||
use erg_common::style::{RED, RESET, YELLOW};
|
||||
|
||||
/// Requirement: Type, Impl := Type -> ClassType
|
||||
pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use erg_common::astr::AtomicStr;
|
||||
use erg_common::color::{GREEN, RED, RESET, YELLOW};
|
||||
use erg_common::config::Input;
|
||||
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
|
||||
use erg_common::set::Set;
|
||||
use erg_common::style::{Attribute, Color, StyledStr, StyledString, Theme, THEME};
|
||||
use erg_common::traits::{Locational, Stream};
|
||||
use erg_common::vis::Visibility;
|
||||
use erg_common::{
|
||||
|
@ -112,6 +112,7 @@ pub struct CompileError {
|
|||
pub core: Box<ErrorCore>, // ErrorCore is large, so box it
|
||||
pub input: Input,
|
||||
pub caused_by: AtomicStr,
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
impl_display_and_error!(CompileError);
|
||||
|
@ -122,6 +123,7 @@ impl From<ParserRunnerError> for CompileError {
|
|||
core: Box::new(err.core),
|
||||
input: err.input,
|
||||
caused_by: "".into(),
|
||||
theme: THEME,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +135,9 @@ impl ErrorDisplay for CompileError {
|
|||
fn input(&self) -> &Input {
|
||||
&self.input
|
||||
}
|
||||
fn theme(&self) -> &Theme {
|
||||
&self.theme
|
||||
}
|
||||
fn caused_by(&self) -> &str {
|
||||
&self.caused_by
|
||||
}
|
||||
|
@ -141,12 +146,19 @@ impl ErrorDisplay for CompileError {
|
|||
}
|
||||
}
|
||||
|
||||
const URL: StyledStr = StyledStr::new(
|
||||
"https://github.com/erg-lang/erg",
|
||||
Some(Color::White),
|
||||
Some(Attribute::Underline),
|
||||
);
|
||||
|
||||
impl CompileError {
|
||||
pub fn new(core: ErrorCore, input: Input, caused_by: AtomicStr) -> Self {
|
||||
Self {
|
||||
core: Box::new(core),
|
||||
input,
|
||||
caused_by,
|
||||
theme: THEME,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,10 +175,10 @@ impl CompileError {
|
|||
CompilerSystemError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg编译器的错误,请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("這是Erg編譯器的錯誤,請報告給https://github.com/erg-lang/erg\n原因來自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of the Erg compiler, please report it to https://github.com/erg-lang/erg\ncaused from: {fn_name}:{line}"),
|
||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg编译器的错误,请报告给{URL}\n\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("這是Erg編譯器的錯誤,請報告給{URL}\n\n原因來自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of the Erg compiler, please report it to {URL}\n\ncaused from: {fn_name}:{line}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -189,16 +201,16 @@ impl CompileError {
|
|||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\
|
||||
これはコンパイラのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n\
|
||||
これはコンパイラのバグです、開発者に報告して下さい ({URL})\n\
|
||||
{fn_name}より発生"),
|
||||
"simplified_chinese" => format!("堆栈中的元素数无效(元素数: {stack_len},块id: {block_id})\n\
|
||||
这是 Erg 编译器的一个错误,请报告它 (https://github.com/erg-lang/erg)\n\
|
||||
这是 Erg 编译器的一个错误,请报告它 ({URL})\n\
|
||||
起因于: {fn_name}"),
|
||||
"traditional_chinese" => format!("堆棧中的元素數無效(元素數: {stack_len},塊id: {block_id})\n\
|
||||
這是 Erg 編譯器的一個錯誤,請報告它 (https://github.com/erg-lang/erg)\n\
|
||||
這是 Erg 編譯器的一個錯誤,請報告它 ({URL})\n\
|
||||
起因於: {fn_name}"),
|
||||
"english" => format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\
|
||||
this is a bug of the Erg compiler, please report it (https://github.com/erg-lang/erg)\n\
|
||||
this is a bug of the Erg compiler, please report it ({URL})\n\
|
||||
caused from: {fn_name}"),
|
||||
),
|
||||
None,
|
||||
|
@ -249,6 +261,10 @@ impl CompileError {
|
|||
|
||||
pub type TyCheckError = CompileError;
|
||||
|
||||
const ERR: Color = THEME.colors.error;
|
||||
const WARNING: Color = THEME.colors.warning;
|
||||
const HINT: Color = THEME.colors.hint;
|
||||
|
||||
impl TyCheckError {
|
||||
pub fn dummy(input: Input, errno: usize) -> Self {
|
||||
Self::new(ErrorCore::dummy(errno), input, "".into())
|
||||
|
@ -271,10 +287,10 @@ impl TyCheckError {
|
|||
CompilerSystemError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg编译器的错误,请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("這是Erg編譯器的錯誤,請報告給https://github.com/erg-lang/erg\n原因來自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of the Erg compiler, please report it to https://github.com/erg-lang/erg\ncaused from: {fn_name}:{line}"),
|
||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg编译器的错误,请报告给{URL}\n\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("這是Erg編譯器的錯誤,請報告給{URL}\n\n原因來自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of the Erg compiler, please report it to {URL}\n\ncaused from: {fn_name}:{line}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -361,16 +377,23 @@ impl TyCheckError {
|
|||
),
|
||||
None => "".into(),
|
||||
};
|
||||
let name = StyledString::new(
|
||||
&format!("{}{}", name, ord),
|
||||
Some(WARNING),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
|
||||
let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{YELLOW}{name}{ord}{RESET}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "与えられた型の単一化候補:\n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"simplified_chinese" => format!("{YELLOW}{name}{ord}{RESET}的类型不匹配: \n预期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一类型的统一候选: \n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"traditional_chinese" => format!("{YELLOW}{name}{ord}{RESET}的類型不匹配: \n預期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "某一類型的統一候選\n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"english" => format!("the type of {YELLOW}{name}{ord}{RESET} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}\n{}", fmt_option_map!(pre "unification candidates of a given type:\n", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"japanese" => format!("{name}の型が違います\n\n予期した型: {expect}\n与えられた型: {found}{}", fmt_option_map!(pre "\n与えられた型の単一化候補: ", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"simplified_chinese" => format!("{name}的类型不匹配\n\n预期: {expect}\n但找到: {found}{}", fmt_option_map!(pre "\n某一类型的统一候选: ", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"traditional_chinese" => format!("{name}的類型不匹配\n\n預期: {expect}\n但找到: {found}{}", fmt_option_map!(pre "\n某一類型的統一候選: ", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
"english" => format!("the type of {name} is mismatched\n\nexpected: {expect}\nbut found: {found}{}", fmt_option_map!(pre "\nunification candidates of a given type: ", candidates, |x: &Set<Type>| x.folded_display())),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -388,16 +411,18 @@ impl TyCheckError {
|
|||
expect: &Type,
|
||||
found: &Type,
|
||||
) -> Self {
|
||||
let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
|
||||
let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{name}の戻り値の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"),
|
||||
"simplified_chinese" => format!("{name}的返回类型不匹配: \n预期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"),
|
||||
"traditional_chinese" => format!("{name}的返回類型不匹配: \n預期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"),
|
||||
"english" => format!("the return type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
|
||||
"japanese" => format!("{name}の戻り値の型が違います\n\n予期した型: {expect}\n与えられた型: {found}"),
|
||||
"simplified_chinese" => format!("{name}的返回类型不匹配\n\n预期: {expect}\n但找到: {found}"),
|
||||
"traditional_chinese" => format!("{name}的返回類型不匹配\n\n預期: {expect}\n但找到: {found}"),
|
||||
"english" => format!("the return type of {name} is mismatched\n\nexpected: {expect}\nbut found: {found}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -440,16 +465,18 @@ impl TyCheckError {
|
|||
expect: usize,
|
||||
found: usize,
|
||||
) -> Self {
|
||||
let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
|
||||
let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("ポジショナル引数の数が違います。\n予期した個数: {GREEN}{expect}{RESET}\n与えられた個数: {RED}{found}{RESET}"),
|
||||
"simplified_chinese" => format!("正则参数的数量不匹配: \n预期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"),
|
||||
"traditional_chinese" => format!("正則參數的數量不匹配: \n預期: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"),
|
||||
"english" => format!("the number of positional arguments is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
|
||||
"japanese" => format!("ポジショナル引数の数が違います\n\n予期した個数: {expect}\n与えられた個数: {found}"),
|
||||
"simplified_chinese" => format!("正则参数的数量不匹配\n\n预期: {expect}\n但找到: {found}"),
|
||||
"traditional_chinese" => format!("正則參數的數量不匹配\n\n預期: {expect}\n但找到: {found}"),
|
||||
"english" => format!("the number of positional arguments is mismatched\n\nexpected: {expect}\nbut found: {found}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -528,6 +555,21 @@ impl TyCheckError {
|
|||
kw_args_len: usize,
|
||||
) -> Self {
|
||||
let name = readable_name(callee_name);
|
||||
let expect = StyledString::new(
|
||||
&format!("{}", params_len),
|
||||
Some(HINT),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
let pos_args_len = StyledString::new(
|
||||
&format!("{}", pos_args_len),
|
||||
Some(ERR),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
let kw_args_len = StyledString::new(
|
||||
&format!("{}", kw_args_len),
|
||||
Some(ERR),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
|
@ -536,27 +578,29 @@ impl TyCheckError {
|
|||
switch_lang!(
|
||||
"japanese" => format!(
|
||||
"{name}に渡された引数の数が多すぎます
|
||||
必要な引数の合計数: {GREEN}{params_len}{RESET}個
|
||||
渡された引数の数: {RED}{pos_args_len}{RESET}個
|
||||
キーワード引数の数: {RED}{kw_args_len}{RESET}個"
|
||||
|
||||
必要な引数の合計数: {expect}個
|
||||
渡された引数の数: {pos_args_len}個
|
||||
キーワード引数の数: {kw_args_len}個"
|
||||
),
|
||||
"simplified_chinese" => format!("传递给{name}的参数过多
|
||||
所需参数总数: {GREEN}{params_len}{RESET}
|
||||
传递的参数数量: {RED}{pos_args_len}{RESET}
|
||||
关键字参数的数量: {RED}{kw_args_len}{RESET}
|
||||
"
|
||||
|
||||
: {expect}
|
||||
: {pos_args_len}
|
||||
: {kw_args_len}"
|
||||
),
|
||||
"traditional_chinese" => format!("傳遞給{name}的參數過多
|
||||
所需參數總數: {GREEN}{params_len}{RESET}
|
||||
傳遞的參數數量: {RED}{pos_args_len}{RESET}
|
||||
關鍵字參數的數量: {RED}{kw_args_len}{RESET}
|
||||
"
|
||||
|
||||
所需參數總數: {expect}
|
||||
遞的參數數量: {pos_args_len}
|
||||
字參數的數量: {kw_args_len}"
|
||||
),
|
||||
"english" => format!(
|
||||
"too many arguments for {name}:
|
||||
total expected params: {GREEN}{params_len}{RESET}
|
||||
passed positional args: {RED}{pos_args_len}{RESET}
|
||||
passed keyword args: {RED}{kw_args_len}{RESET}"
|
||||
"too many arguments for {name}
|
||||
|
||||
total expected params: {expect}
|
||||
passed positional args: {pos_args_len}
|
||||
passed keyword args: {kw_args_len}"
|
||||
),
|
||||
),
|
||||
None,
|
||||
|
@ -576,16 +620,21 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
missing_params: Vec<Str>,
|
||||
) -> Self {
|
||||
let name = readable_name(callee_name);
|
||||
let vec_cxt = StyledString::new(
|
||||
&fmt_vec(&missing_params),
|
||||
Some(WARNING),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{name}に渡された引数が{missing_len}個足りません({YELLOW}{}{RESET})。", fmt_vec(&missing_params)),
|
||||
"simplified_chinese" => format!("{name}的{missing_len}个位置参数不被传递。({YELLOW}{}{RESET})。", fmt_vec(&missing_params)),
|
||||
"traditional_chinese" => format!("{name}的{missing_len}個位置參數不被傳遞。({YELLOW}{}{RESET})。", fmt_vec(&missing_params)),
|
||||
"english" => format!("missing {missing_len} positional argument(s) for {name}: {YELLOW}{}{RESET}", fmt_vec(&missing_params)),
|
||||
"japanese" => format!("{name}に渡された引数が{missing_len}個足りません({vec_cxt})" ),
|
||||
"simplified_chinese" => format!("{name}的{missing_len}个位置参数不被传递({vec_cxt})"),
|
||||
"traditional_chinese" => format!("{name}的{missing_len}個位置參數不被傳遞({vec_cxt})"),
|
||||
"english" => format!("missing {missing_len} positional argument(s) for {name}: {vec_cxt}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -603,16 +652,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
arg_name: &str,
|
||||
) -> Self {
|
||||
let name = readable_name(callee_name);
|
||||
let found = StyledString::new(arg_name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{name}の引数{RED}{arg_name}{RESET}が複数回渡されています"),
|
||||
"simplified_chinese" => format!("{name}的参数{RED}{arg_name}{RESET}被多次传递"),
|
||||
"traditional_chinese" => format!("{name}的參數{RED}{arg_name}{RESET}被多次傳遞"),
|
||||
"english" => format!("{name}'s argument {RED}{arg_name}{RESET} is passed multiple times"),
|
||||
"japanese" => format!("{name}の引数{found}が複数回渡されています"),
|
||||
"simplified_chinese" => format!("{name}的参数{found}被多次传递"),
|
||||
"traditional_chinese" => format!("{name}的參數{found}被多次傳遞"),
|
||||
"english" => format!("{name}'s argument {found} is passed multiple times"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -630,16 +680,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
param_name: &str,
|
||||
) -> Self {
|
||||
let name = readable_name(callee_name);
|
||||
let found = StyledString::new(param_name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{name}には予期しないキーワード引数{RED}{param_name}{RESET}が渡されています"),
|
||||
"simplified_chinese" => format!("{name}得到了意外的关键字参数{RED}{param_name}{RESET}"),
|
||||
"traditional_chinese" => format!("{name}得到了意外的關鍵字參數{RED}{param_name}{RESET}"),
|
||||
"english" => format!("{name} got unexpected keyword argument {RED}{param_name}{RESET}"),
|
||||
"japanese" => format!("{name}には予期しないキーワード引数{found}が渡されています"),
|
||||
"simplified_chinese" => format!("{name}得到了意外的关键字参数{found}"),
|
||||
"traditional_chinese" => format!("{name}得到了意外的關鍵字參數{found}"),
|
||||
"english" => format!("{name} got unexpected keyword argument {found}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -656,16 +707,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
loc: Location,
|
||||
caused_by: AtomicStr,
|
||||
) -> Self {
|
||||
let lhs_t = StyledString::new(&format!("{}", lhs_t), Some(WARNING), Some(Attribute::Bold));
|
||||
let rhs_t = StyledString::new(&format!("{}", rhs_t), Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("型の単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}"),
|
||||
"simplified_chinese" => format!("类型统一失败: \n左边: {YELLOW}{lhs_t}{RESET}\n右边: {YELLOW}{rhs_t}{RESET}"),
|
||||
"traditional_chinese" => format!("類型統一失敗: \n左邊: {YELLOW}{lhs_t}{RESET}\n右邊: {YELLOW}{rhs_t}{RESET}"),
|
||||
"english" => format!("unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
|
||||
"japanese" => format!("型の単一化に失敗しました\n\n左辺: {lhs_t}\n右辺: {rhs_t}"),
|
||||
"simplified_chinese" => format!("类型统一失败\n\n左边: {lhs_t}\n右边: {rhs_t}"),
|
||||
"traditional_chinese" => format!("類型統一失敗\n\n左邊: {lhs_t}\n右邊: {rhs_t}"),
|
||||
"english" => format!("unification failed\n\nlhs: {lhs_t}\nrhs: {rhs_t}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -682,16 +735,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
loc: Location,
|
||||
caused_by: AtomicStr,
|
||||
) -> Self {
|
||||
let lhs_t = StyledString::new(&format!("{}", lhs_t), Some(WARNING), Some(Attribute::Bold));
|
||||
let rhs_t = StyledString::new(&format!("{}", rhs_t), Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("型の再単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}"),
|
||||
"simplified_chinese" => format!("重新统一类型失败: \n左边: {YELLOW}{lhs_t}{RESET}\n右边: {YELLOW}{rhs_t}{RESET}"),
|
||||
"traditional_chinese" => format!("重新統一類型失敗: \n左邊: {YELLOW}{lhs_t}{RESET}\n右邊: {YELLOW}{rhs_t}{RESET}"),
|
||||
"english" => format!("re-unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
|
||||
"japanese" => format!("型の再単一化に失敗しました\n\n左辺: {lhs_t}\n右辺: {rhs_t}"),
|
||||
"simplified_chinese" => format!("重新统一类型失败\n\n左边: {lhs_t}\n右边: {rhs_t}"),
|
||||
"traditional_chinese" => format!("重新統一類型失敗\n\n左邊: {lhs_t}\n右邊: {rhs_t}"),
|
||||
"english" => format!("re-unification failed\n\nlhs: {lhs_t}\nrhs: {rhs_t}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -708,16 +763,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
loc: Location,
|
||||
caused_by: AtomicStr,
|
||||
) -> Self {
|
||||
let sub_t = StyledString::new(&format!("{}", sub_t), Some(WARNING), Some(Attribute::Bold));
|
||||
let sup_t = StyledString::new(&format!("{}", sup_t), Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("この式の部分型制約を満たせません:\nサブタイプ: {YELLOW}{sub_t}{RESET}\nスーパータイプ: {YELLOW}{sup_t}{RESET}"),
|
||||
"simplified_chinese" => format!("无法满足此表达式中的子类型约束: \n子类型: {YELLOW}{sub_t}{RESET}\n超类型: {YELLOW}{sup_t}{RESET}"),
|
||||
"traditional_chinese" => format!("無法滿足此表達式中的子類型約束: \n子類型: {YELLOW}{sub_t}{RESET}\n超類型: {YELLOW}{sup_t}{RESET}"),
|
||||
"english" => format!("the subtype constraint in this expression cannot be satisfied:\nsubtype: {YELLOW}{sub_t}{RESET}\nsupertype: {YELLOW}{sup_t}{RESET}"),
|
||||
"japanese" => format!("この式の部分型制約を満たせません\n\nサブタイプ: {sub_t}\nスーパータイプ: {sup_t}"),
|
||||
"simplified_chinese" => format!("无法满足此表达式中的子类型约束\n\n子类型: {sub_t}\n超类型: {sup_t}"),
|
||||
"traditional_chinese" => format!("無法滿足此表達式中的子類型約束\n\n子類型: {sub_t}\n超類型: {sup_t}"),
|
||||
"english" => format!("the subtype constraint in this expression cannot be satisfied:\nsubtype: {sub_t}\nsupertype: {sup_t}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -733,16 +790,18 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
rhs: &Predicate,
|
||||
caused_by: AtomicStr,
|
||||
) -> Self {
|
||||
let lhs = StyledString::new(&format!("{}", lhs), Some(WARNING), Some(Attribute::Bold));
|
||||
let rhs = StyledString::new(&format!("{}", rhs), Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
Location::Unknown,
|
||||
switch_lang!(
|
||||
"japanese" => format!("述語式の単一化に失敗しました:\n左辺: {YELLOW}{lhs}{RESET}\n右辺: {YELLOW}{rhs}{RESET}"),
|
||||
"simplified_chinese" => format!("无法统一谓词表达式: \n左边: {YELLOW}{lhs}{RESET}\n左边: {YELLOW}{rhs}{RESET}"),
|
||||
"traditional_chinese" => format!("無法統一謂詞表達式: \n左邊: {YELLOW}{lhs}{RESET}\n左邊: {YELLOW}{rhs}{RESET}"),
|
||||
"english" => format!("predicate unification failed:\nlhs: {YELLOW}{lhs}{RESET}\nrhs: {YELLOW}{rhs}{RESET}"),
|
||||
"japanese" => format!("述語式の単一化に失敗しました\n\n左辺: {lhs}\n右辺: {rhs}"),
|
||||
"simplified_chinese" => format!("无法统一谓词表达式\n\n左边: {lhs}\n左边: {rhs}"),
|
||||
"traditional_chinese" => format!("無法統一謂詞表達式\n\n左邊: {lhs}\n左邊: {rhs}"),
|
||||
"english" => format!("predicate unification failed\n\nlhs: {lhs}\nrhs: {rhs}"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -812,6 +871,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
name: &str,
|
||||
hint: Option<AtomicStr>,
|
||||
) -> Self {
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
|
@ -819,16 +879,16 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!(
|
||||
"{RED}{name}{RESET}にメソッドを定義することはできません",
|
||||
"{found}にメソッドを定義することはできません",
|
||||
),
|
||||
"simplified_chinese" => format!(
|
||||
"{RED}{name}{RESET}不可定义方法",
|
||||
"{found}不可定义方法",
|
||||
),
|
||||
"traditional_chinese" => format!(
|
||||
"{RED}{name}{RESET}不可定義方法",
|
||||
"{found}不可定義方法",
|
||||
),
|
||||
"english" => format!(
|
||||
"cannot define methods for {RED}{name}{RESET}",
|
||||
"cannot define methods for {found}",
|
||||
),
|
||||
),
|
||||
hint,
|
||||
|
@ -850,16 +910,19 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
found: &Type,
|
||||
hint: Option<AtomicStr>,
|
||||
) -> Self {
|
||||
let member_name = StyledString::new(member_name, Some(WARNING), Some(Attribute::Bold));
|
||||
let expect = StyledString::new(&format!("{}", expect), Some(HINT), Some(Attribute::Bold));
|
||||
let found = StyledString::new(&format!("{}", found), Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{YELLOW}{member_name}{RESET}の型が違います。\n{trait_type}で宣言された型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"),
|
||||
"simplified_chinese" => format!("{YELLOW}{member_name}{RESET}的类型不匹配: \n在{trait_type}中声明的类型: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"),
|
||||
"traditional_chinese" => format!("{YELLOW}{member_name}{RESET}的類型不匹配: \n在{trait_type}中聲明的類型: {GREEN}{expect}{RESET}\n但找到: {RED}{found}{RESET}"),
|
||||
"english" => format!("the type of {YELLOW}{member_name}{RESET} is mismatched:\ndeclared in {trait_type}: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
|
||||
"japanese" => format!("{member_name}の型が違います\n\n{trait_type}で宣言された型: {expect}\n与えられた型: {found}"),
|
||||
"simplified_chinese" => format!("{member_name}的类型不匹配\n\n在{trait_type}中声明的类型: {expect}\n但找到: {found}"),
|
||||
"traditional_chinese" => format!("{member_name}的類型不匹配\n\n在{trait_type}中聲明的類型: {expect}\n但找到: {found}"),
|
||||
"english" => format!("the type of {member_name} is mismatched\n\ndeclared in {trait_type}: {expect}\nbut found: {found}"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -878,16 +941,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
class_type: &Type,
|
||||
hint: Option<AtomicStr>,
|
||||
) -> Self {
|
||||
let member_name = StyledString::new(member_name, Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
Location::Unknown,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{trait_type}の{YELLOW}{member_name}{RESET}が{class_type}で実装されていません"),
|
||||
"simplified_chinese" => format!("{trait_type}中的{YELLOW}{member_name}{RESET}没有在{class_type}中实现"),
|
||||
"traditional_chinese" => format!("{trait_type}中的{YELLOW}{member_name}{RESET}沒有在{class_type}中實現"),
|
||||
"english" => format!("{YELLOW}{member_name}{RESET} of {trait_type} is not implemented in {class_type}"),
|
||||
"japanese" => format!("{trait_type}の{member_name}が{class_type}で実装されていません"),
|
||||
"simplified_chinese" => format!("{trait_type}中的{member_name}没有在{class_type}中实现"),
|
||||
"traditional_chinese" => format!("{trait_type}中的{member_name}沒有在{class_type}中實現"),
|
||||
"english" => format!("{member_name} of {trait_type} is not implemented in {class_type}"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -906,16 +970,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
class_type: &Type,
|
||||
hint: Option<AtomicStr>,
|
||||
) -> Self {
|
||||
let member_name = StyledString::new(member_name, Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
Location::Unknown,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{class_type}の{YELLOW}{member_name}{RESET}は{trait_type}で宣言されていません"),
|
||||
"simplified_chinese" => format!("{class_type}中的{YELLOW}{member_name}{RESET}没有在{trait_type}中声明"),
|
||||
"traditional_chinese" => format!("{class_type}中的{YELLOW}{member_name}{RESET}沒有在{trait_type}中聲明"),
|
||||
"english" => format!("{YELLOW}{member_name}{RESET} of {class_type} is not declared in {trait_type}"),
|
||||
"japanese" => format!("{class_type}の{member_name}は{trait_type}で宣言されていません"),
|
||||
"simplified_chinese" => format!("{class_type}中的{member_name}没有在{trait_type}中声明"),
|
||||
"traditional_chinese" => format!("{class_type}中的{member_name}沒有在{trait_type}中聲明"),
|
||||
"english" => format!("{member_name} of {class_type} is not declared in {trait_type}"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -931,16 +996,17 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
loc: Location,
|
||||
caused_by: AtomicStr,
|
||||
) -> Self {
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("型変数{RED}{name}{RESET}が定義されていません"),
|
||||
"simplified_chinese" => format!("类型变量{RED}{name}{RESET}没有定义"),
|
||||
"traditional_chinese" => format!("類型變量{RED}{name}{RESET}沒有定義"),
|
||||
"english" => format!("type variable {RED}{name}{RESET} is not defined"),
|
||||
"japanese" => format!("型変数{found}が定義されていません"),
|
||||
"simplified_chinese" => format!("类型变量{found}没有定义"),
|
||||
"traditional_chinese" => format!("類型變量{found}沒有定義"),
|
||||
"english" => format!("type variable {found} is not defined"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -962,10 +1028,10 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
|
|||
TypeError,
|
||||
expr.loc(),
|
||||
switch_lang!(
|
||||
"japanese" => format!("{expr}の型を一意に決定できませんでした\n候補: {}", fmt_vec(candidates)),
|
||||
"simplified_chinese" => format!("无法确定{expr}的类型\n候选: {}", fmt_vec(candidates)),
|
||||
"traditional_chinese" => format!("無法確定{expr}的類型\n候選: {}", fmt_vec(candidates)),
|
||||
"english" => format!("cannot determine the type of {expr}\ncandidates: {}", fmt_vec(candidates)),
|
||||
"japanese" => format!("{expr}の型を一意に決定できませんでした\n\n候補: {}", fmt_vec(candidates)),
|
||||
"simplified_chinese" => format!("无法确定{expr}的类型\n\n候选: {}", fmt_vec(candidates)),
|
||||
"traditional_chinese" => format!("無法確定{expr}的類型\n\n候選: {}", fmt_vec(candidates)),
|
||||
"english" => format!("cannot determine the type of {expr}\n\ncandidates: {}", fmt_vec(candidates)),
|
||||
),
|
||||
Some(
|
||||
switch_lang!(
|
||||
|
@ -1111,6 +1177,7 @@ impl OwnershipError {
|
|||
moved_loc: Location,
|
||||
caused_by: S,
|
||||
) -> Self {
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
|
@ -1118,19 +1185,19 @@ impl OwnershipError {
|
|||
name_loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!(
|
||||
"{RED}{name}{RESET}は{}行目ですでに移動されています",
|
||||
"{found}は{}行目ですでに移動されています",
|
||||
moved_loc.ln_begin().unwrap_or(0)
|
||||
),
|
||||
"simplified_chinese" => format!(
|
||||
"{RED}{name}{RESET}已移至第{}行",
|
||||
"{found}已移至第{}行",
|
||||
moved_loc.ln_begin().unwrap_or(0)
|
||||
),
|
||||
"traditional_chinese" => format!(
|
||||
"{RED}{name}{RESET}已移至第{}行",
|
||||
"{found}已移至第{}行",
|
||||
moved_loc.ln_begin().unwrap_or(0)
|
||||
),
|
||||
"english" => format!(
|
||||
"{RED}{name}{RESET} was moved in line {}",
|
||||
"{found} was moved in line {}",
|
||||
moved_loc.ln_begin().unwrap_or(0)
|
||||
),
|
||||
),
|
||||
|
@ -1227,16 +1294,18 @@ impl LowerError {
|
|||
found_t: &Type,
|
||||
) -> Self {
|
||||
let name = readable_name(name);
|
||||
let expect = StyledString::new(&format!("{}", spec_t), Some(HINT), Some(Attribute::Bold));
|
||||
let found = StyledString::new(&format!("{}", found_t), Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{name}は{GREEN}{spec_t}{RESET}型として宣言されましたが、{RED}{found_t}{RESET}型のオブジェクトが代入されています"),
|
||||
"simplified_chinese" => format!("{name}被声明为{GREEN}{spec_t}{RESET},但分配了一个{RED}{found_t}{RESET}对象"),
|
||||
"traditional_chinese" => format!("{name}被聲明為{GREEN}{spec_t}{RESET},但分配了一個{RED}{found_t}{RESET}對象"),
|
||||
"english" => format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"),
|
||||
"japanese" => format!("{name}は{expect}型として宣言されましたが、{found}型のオブジェクトが代入されています"),
|
||||
"simplified_chinese" => format!("{name}被声明为{expect},但分配了一个{found}对象"),
|
||||
"traditional_chinese" => format!("{name}被聲明為{expect},但分配了一個{found}對象"),
|
||||
"english" => format!("{name} was declared as {expect}, but an {found} object is assigned"),
|
||||
),
|
||||
Option::<AtomicStr>::None,
|
||||
),
|
||||
|
@ -1263,16 +1332,17 @@ impl LowerError {
|
|||
)
|
||||
.into()
|
||||
});
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
NameError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{RED}{name}{RESET}という変数は定義されていません"),
|
||||
"simplified_chinese" => format!("{RED}{name}{RESET}未定义"),
|
||||
"traditional_chinese" => format!("{RED}{name}{RESET}未定義"),
|
||||
"english" => format!("{RED}{name}{RESET} is not defined"),
|
||||
"japanese" => format!("{found}という変数は定義されていません"),
|
||||
"simplified_chinese" => format!("{found}未定义"),
|
||||
"traditional_chinese" => format!("{found}未定義"),
|
||||
"english" => format!("{found} is not defined"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -1299,16 +1369,17 @@ impl LowerError {
|
|||
)
|
||||
.into()
|
||||
});
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
AttributeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{obj_t}型オブジェクトに{RED}{name}{RESET}という属性はありません"),
|
||||
"simplified_chinese" => format!("{obj_t}对象没有属性{RED}{name}{RESET}"),
|
||||
"traditional_chinese" => format!("{obj_t}對像沒有屬性{RED}{name}{RESET}"),
|
||||
"english" => format!("{obj_t} object has no attribute {RED}{name}{RESET}"),
|
||||
"japanese" => format!("{obj_t}型オブジェクトに{found}という属性はありません"),
|
||||
"simplified_chinese" => format!("{obj_t}对象没有属性{found}"),
|
||||
"traditional_chinese" => format!("{obj_t}對像沒有屬性{found}"),
|
||||
"english" => format!("{obj_t} object has no attribute {found}"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -1337,16 +1408,17 @@ impl LowerError {
|
|||
)
|
||||
.into()
|
||||
});
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
AttributeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{obj_name}(: {obj_t})に{RED}{name}{RESET}という属性はありません"),
|
||||
"simplified_chinese" => format!("{obj_name}(: {obj_t})没有属性{RED}{name}{RESET}"),
|
||||
"traditional_chinese" => format!("{obj_name}(: {obj_t})沒有屬性{RED}{name}{RESET}"),
|
||||
"english" => format!("{obj_name}(: {obj_t}) has no attribute {RED}{name}{RESET}"),
|
||||
"japanese" => format!("{obj_name}(: {obj_t})に{found}という属性はありません"),
|
||||
"simplified_chinese" => format!("{obj_name}(: {obj_t})没有属性{found}"),
|
||||
"traditional_chinese" => format!("{obj_name}(: {obj_t})沒有屬性{found}"),
|
||||
"english" => format!("{obj_name}(: {obj_t}) has no attribute {found}"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -1362,17 +1434,17 @@ impl LowerError {
|
|||
caused_by: AtomicStr,
|
||||
name: &str,
|
||||
) -> Self {
|
||||
let name = readable_name(name);
|
||||
let name = StyledString::new(readable_name(name), Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
AssignError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("変数{YELLOW}{name}{RESET}に複数回代入することはできません"),
|
||||
"simplified_chinese" => format!("不能为变量{YELLOW}{name}{RESET}分配多次"),
|
||||
"traditional_chinese" => format!("不能為變量{YELLOW}{name}{RESET}分配多次"),
|
||||
"english" => format!("variable {YELLOW}{name}{RESET} cannot be assigned more than once"),
|
||||
"japanese" => format!("変数{name}に複数回代入することはできません"),
|
||||
"simplified_chinese" => format!("不能为变量{name}分配多次"),
|
||||
"traditional_chinese" => format!("不能為變量{name}分配多次"),
|
||||
"english" => format!("variable {name} cannot be assigned more than once"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -1388,17 +1460,17 @@ impl LowerError {
|
|||
name: &str,
|
||||
caused_by: AtomicStr,
|
||||
) -> Self {
|
||||
let name = readable_name(name);
|
||||
let name = StyledString::new(readable_name(name), Some(WARNING), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
UnusedWarning,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{YELLOW}{name}{RESET}は使用されていません"),
|
||||
"simplified_chinese" => format!("{YELLOW}{name}{RESET}未使用"),
|
||||
"traditional_chinese" => format!("{YELLOW}{name}{RESET}未使用"),
|
||||
"english" => format!("{YELLOW}{name}{RESET} is not used"),
|
||||
"japanese" => format!("{name}は使用されていません"),
|
||||
"simplified_chinese" => format!("{name}未使用"),
|
||||
"traditional_chinese" => format!("{name}未使用"),
|
||||
"english" => format!("{name} is not used"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -1408,17 +1480,21 @@ impl LowerError {
|
|||
}
|
||||
|
||||
pub fn del_error(input: Input, errno: usize, ident: &Identifier, caused_by: AtomicStr) -> Self {
|
||||
let name = readable_name(ident.inspect());
|
||||
let name = StyledString::new(
|
||||
readable_name(ident.inspect()),
|
||||
Some(WARNING),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
NameError,
|
||||
ident.loc(),
|
||||
switch_lang!(
|
||||
"japanese" => format!("{YELLOW}{name}{RESET}は削除できません"),
|
||||
"simplified_chinese" => format!("{YELLOW}{name}{RESET}不能删除"),
|
||||
"traditional_chinese" => format!("{YELLOW}{name}{RESET}不能刪除"),
|
||||
"english" => format!("{YELLOW}{name}{RESET} cannot be deleted"),
|
||||
"japanese" => format!("{name}は削除できません"),
|
||||
"simplified_chinese" => format!("{name}不能删除"),
|
||||
"traditional_chinese" => format!("{name}不能刪除"),
|
||||
"english" => format!("{name} cannot be deleted"),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -1451,16 +1527,17 @@ impl LowerError {
|
|||
"english" => "public",
|
||||
)
|
||||
};
|
||||
let found = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
VisibilityError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{RED}{name}{RESET}は{visibility}変数です"),
|
||||
"simplified_chinese" => format!("{RED}{name}{RESET}是{visibility}变量",),
|
||||
"traditional_chinese" => format!("{RED}{name}{RESET}是{visibility}變量",),
|
||||
"english" => format!("{RED}{name}{RESET} is {visibility} variable",),
|
||||
"japanese" => format!("{found}は{visibility}変数です"),
|
||||
"simplified_chinese" => format!("{found}是{visibility}变量",),
|
||||
"traditional_chinese" => format!("{found}是{visibility}變量",),
|
||||
"english" => format!("{found} is {visibility} variable",),
|
||||
),
|
||||
None,
|
||||
),
|
||||
|
@ -1477,6 +1554,12 @@ impl LowerError {
|
|||
superclass: &Type,
|
||||
caused_by: S,
|
||||
) -> Self {
|
||||
let name = StyledString::new(name, Some(ERR), Some(Attribute::Bold));
|
||||
let superclass = StyledString::new(
|
||||
&format!("{}", superclass),
|
||||
Some(WARNING),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
|
@ -1484,16 +1567,16 @@ impl LowerError {
|
|||
name_loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!(
|
||||
"{RED}{name}{RESET}は{YELLOW}{superclass}{RESET}で既に定義されています",
|
||||
"{name}は{superclass}で既に定義されています",
|
||||
),
|
||||
"simplified_chinese" => format!(
|
||||
"{RED}{name}{RESET}已在{YELLOW}{superclass}{RESET}中定义",
|
||||
"{name}已在{superclass}中定义",
|
||||
),
|
||||
"traditional_chinese" => format!(
|
||||
"{RED}{name}{RESET}已在{YELLOW}{superclass}{RESET}中定義",
|
||||
"{name}已在{superclass}中定義",
|
||||
),
|
||||
"english" => format!(
|
||||
"{RED}{name}{RESET} is already defined in {YELLOW}{superclass}{RESET}",
|
||||
"{name} is already defined in {superclass}",
|
||||
),
|
||||
),
|
||||
Some(switch_lang!(
|
||||
|
@ -1574,11 +1657,21 @@ impl LowerError {
|
|||
similar_py_mod: Option<Str>,
|
||||
) -> Self {
|
||||
let hint = match (similar_erg_mod, similar_py_mod) {
|
||||
(Some(erg), Some(py)) => Some(format!(
|
||||
"similar name erg module {YELLOW}{erg}{RESET} and python module {YELLOW}{py}{RESET} exists (to import python modules, use `pyimport`)",
|
||||
)),
|
||||
(Some(erg), None) => Some(format!("similar name erg module exists: {YELLOW}{erg}{RESET}")),
|
||||
(None, Some(py)) => Some(format!("similar name python module exists: {YELLOW}{py}{RESET} (to import python modules, use `pyimport`)")),
|
||||
(Some(erg), Some(py)) => {
|
||||
let erg = StyledString::new(&erg, Some(WARNING), Some(Attribute::Bold));
|
||||
let py = StyledString::new(&py, Some(WARNING), Some(Attribute::Bold));
|
||||
Some(format!(
|
||||
"similar name erg module {erg} and python module {py} exists (to import python modules, use `pyimport`)",
|
||||
))
|
||||
}
|
||||
(Some(erg), None) => {
|
||||
let erg = StyledString::new(&erg, Some(WARNING), Some(Attribute::Bold));
|
||||
Some(format!("similar name erg module exists: {erg}"))
|
||||
}
|
||||
(None, Some(py)) => {
|
||||
let py = StyledString::new(&py, Some(WARNING), Some(Attribute::Bold));
|
||||
Some(format!("similar name python module exists: {py} (to import python modules, use `pyimport`)"))
|
||||
}
|
||||
(None, None) => None,
|
||||
};
|
||||
let hint = hint.map(AtomicStr::from);
|
||||
|
@ -1638,16 +1731,22 @@ impl LowerError {
|
|||
cast_to: &Type,
|
||||
hint: Option<AtomicStr>,
|
||||
) -> Self {
|
||||
let name = StyledString::new(name, Some(WARNING), Some(Attribute::Bold));
|
||||
let found = StyledString::new(
|
||||
&format!("{}", cast_to),
|
||||
Some(WARNING),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{YELLOW}{name}{RESET}の型を{RED}{cast_to}{RESET}にキャストすることはできません"),
|
||||
"simplified_chinese" => format!("{YELLOW}{name}{RESET}的类型无法转换为{RED}{cast_to}{RESET}"),
|
||||
"traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型無法轉換為{RED}{cast_to}{RESET}"),
|
||||
"english" => format!("the type of {YELLOW}{name}{RESET} cannot be cast to {RED}{cast_to}{RESET}"),
|
||||
"japanese" => format!("{name}の型を{found}にキャストすることはできません"),
|
||||
"simplified_chinese" => format!("{name}的类型无法转换为{found}"),
|
||||
"traditional_chinese" => format!("{name}的類型無法轉換為{found}"),
|
||||
"english" => format!("the type of {name} cannot be cast to {found}"),
|
||||
),
|
||||
hint,
|
||||
),
|
||||
|
@ -1692,3 +1791,128 @@ pub type SingleCompileResult<T> = Result<T, CompileError>;
|
|||
pub type CompileResult<T> = Result<T, CompileErrors>;
|
||||
pub type CompileWarning = CompileError;
|
||||
pub type CompileWarnings = CompileErrors;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use erg_common::{config::Input, error::Location};
|
||||
|
||||
use crate::ty::Type;
|
||||
|
||||
use super::TyCheckError;
|
||||
|
||||
#[test]
|
||||
fn default_arg_error_test() {
|
||||
let loc = Location::Range {
|
||||
ln_begin: 1,
|
||||
col_begin: 0,
|
||||
ln_end: 1,
|
||||
col_end: 5,
|
||||
};
|
||||
let input = Input::Pipe("a = 1".to_string());
|
||||
let caused_by = "File name here basically";
|
||||
let desc = "Some kinds of error description here";
|
||||
let hint = Some("Some hint massage here\n".into());
|
||||
|
||||
let err = TyCheckError::syntax_error(input, 0, loc, caused_by.into(), desc, hint);
|
||||
print!("{}", err);
|
||||
|
||||
let loc = Location::Range {
|
||||
ln_begin: 1,
|
||||
col_begin: 24,
|
||||
ln_end: 1,
|
||||
col_end: 27,
|
||||
};
|
||||
let input = Input::Pipe("Person = Class { name = Str }".to_string());
|
||||
let caused_by = "File name here basically";
|
||||
let err = TyCheckError::args_missing_error(
|
||||
input,
|
||||
0,
|
||||
loc,
|
||||
"\"Callee name here\"",
|
||||
caused_by.into(),
|
||||
0,
|
||||
vec!["sample".into(), "args".into(), "here".into()],
|
||||
);
|
||||
print!("{}", err);
|
||||
|
||||
let loc = Location::Range {
|
||||
ln_begin: 1,
|
||||
col_begin: 0,
|
||||
ln_end: 3,
|
||||
col_end: 5,
|
||||
};
|
||||
let input = Input::Pipe(
|
||||
"\
|
||||
if True:
|
||||
sample
|
||||
end
|
||||
"
|
||||
.to_string(),
|
||||
);
|
||||
let caused_by = "File name here basically";
|
||||
let err = TyCheckError::args_missing_error(
|
||||
input,
|
||||
0,
|
||||
loc,
|
||||
"\"Callee name here\"",
|
||||
caused_by.into(),
|
||||
0,
|
||||
vec!["sample".into(), "args".into(), "here".into()],
|
||||
);
|
||||
print!("{}", err);
|
||||
|
||||
let loc = Location::RangePair {
|
||||
ln_first: (1, 2),
|
||||
col_first: (0, 1),
|
||||
ln_second: (4, 4),
|
||||
col_second: (9, 10),
|
||||
};
|
||||
let input = Input::Pipe(
|
||||
"\
|
||||
a: Nat = 1
|
||||
a.ownership_is_moved()
|
||||
|
||||
function(a)
|
||||
"
|
||||
.to_string(),
|
||||
);
|
||||
let err = TyCheckError::checker_bug(input, 0, loc, "file_name", 0);
|
||||
print!("{}", err);
|
||||
|
||||
let loc = Location::Range {
|
||||
ln_begin: 1,
|
||||
col_begin: 0,
|
||||
ln_end: 1,
|
||||
col_end: 3,
|
||||
};
|
||||
let input = Input::Pipe("add(x, y):Nat = x - y".to_string());
|
||||
let err = TyCheckError::checker_bug(input, 0, loc, "file_name", 0);
|
||||
print!("{}", err);
|
||||
|
||||
let loc = Location::Range {
|
||||
ln_begin: 1,
|
||||
col_begin: 11,
|
||||
ln_end: 1,
|
||||
col_end: 14,
|
||||
};
|
||||
let expect = Type::Nat;
|
||||
let found = Type::Obj;
|
||||
let input = Input::Pipe("add(x, y): Nat = x - y".to_string());
|
||||
let caused_by = "File name here basically";
|
||||
let err = TyCheckError::return_type_error(
|
||||
input,
|
||||
0,
|
||||
loc,
|
||||
caused_by.into(),
|
||||
"name",
|
||||
&expect,
|
||||
&found,
|
||||
);
|
||||
print!("{}", err);
|
||||
|
||||
let input = Input::Pipe("Dummy code here".to_string());
|
||||
let err = TyCheckError::unreachable(input, "file name here", 1);
|
||||
print!("{}", err);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ debug = ["erg_common/debug"]
|
|||
japanese = ["erg_common/japanese"]
|
||||
simplified_chinese = ["erg_common/simplified_chinese"]
|
||||
traditional_chinese = ["erg_common/traditional_chinese"]
|
||||
unicode = ["erg_common/unicode"]
|
||||
pretty = ["erg_common/pretty"]
|
||||
|
||||
[dependencies]
|
||||
erg_common = { version = "0.5.11", path = "../erg_common" }
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
//!
|
||||
//! パーサーが出すエラーを定義
|
||||
use erg_common::astr::AtomicStr;
|
||||
use erg_common::color::{RED, RESET};
|
||||
use erg_common::config::Input;
|
||||
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
|
||||
use erg_common::style::{Attribute, Color, StyledStr, StyledString, Theme};
|
||||
use erg_common::traits::Stream;
|
||||
use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang};
|
||||
|
||||
|
@ -38,15 +38,20 @@ impl LexError {
|
|||
}
|
||||
|
||||
pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
|
||||
const URL: StyledStr = StyledStr::new(
|
||||
"https://github.com/erg-lang/erg",
|
||||
Some(Color::White),
|
||||
Some(Attribute::Underline),
|
||||
);
|
||||
Self::new(ErrorCore::new(
|
||||
errno,
|
||||
CompilerSystemError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/erg-lang/erg)\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg编译器的一个错误,请报告给https://github.com/erg-lang/erg\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("這是Erg編譯器的一個錯誤,請報告給https://github.com/erg-lang/erg\n原因來自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of the Erg compiler, please report it to https://github.com/erg-lang/erg\ncaused from: {fn_name}:{line}"),
|
||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg编译器的一个错误,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("這是Erg編譯器的一個錯誤,請報告給{URL}\n原因來自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of the Erg compiler, please report it to {URL}\ncaused from: {fn_name}:{line}"),
|
||||
),
|
||||
None,
|
||||
))
|
||||
|
@ -115,15 +120,16 @@ impl LexError {
|
|||
)
|
||||
.into()
|
||||
});
|
||||
let name = StyledString::new(name, Some(Color::Red), Some(Attribute::Underline));
|
||||
Self::new(ErrorCore::new(
|
||||
errno,
|
||||
NameError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("{RED}{name}{RESET}という変数は定義されていません"),
|
||||
"simplified_chinese" => format!("{RED}{name}{RESET}未定义"),
|
||||
"traditional_chinese" => format!("{RED}{name}{RESET}未定義"),
|
||||
"english" => format!("{RED}{name}{RESET} is not defined"),
|
||||
"japanese" => format!("{name}という変数は定義されていません"),
|
||||
"simplified_chinese" => format!("{name}未定义"),
|
||||
"traditional_chinese" => format!("{name}未定義"),
|
||||
"english" => format!("{name} is not defined"),
|
||||
),
|
||||
hint,
|
||||
))
|
||||
|
@ -158,6 +164,7 @@ pub type DesugaringResult<T> = Result<T, DesugaringError>;
|
|||
pub struct ParserRunnerError {
|
||||
pub core: ErrorCore,
|
||||
pub input: Input,
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
impl_display_and_error!(ParserRunnerError);
|
||||
|
@ -169,6 +176,9 @@ impl ErrorDisplay for ParserRunnerError {
|
|||
fn input(&self) -> &Input {
|
||||
&self.input
|
||||
}
|
||||
fn theme(&self) -> &Theme {
|
||||
&self.theme
|
||||
}
|
||||
fn caused_by(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
@ -178,8 +188,8 @@ impl ErrorDisplay for ParserRunnerError {
|
|||
}
|
||||
|
||||
impl ParserRunnerError {
|
||||
pub const fn new(core: ErrorCore, input: Input) -> Self {
|
||||
Self { core, input }
|
||||
pub const fn new(core: ErrorCore, input: Input, theme: Theme) -> Self {
|
||||
Self { core, input, theme }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,10 +201,10 @@ impl_stream_for_wrapper!(ParserRunnerErrors, ParserRunnerError);
|
|||
impl MultiErrorDisplay<ParserRunnerError> for ParserRunnerErrors {}
|
||||
|
||||
impl ParserRunnerErrors {
|
||||
pub fn convert(input: &Input, errs: ParseErrors) -> Self {
|
||||
pub fn convert(input: &Input, errs: ParseErrors, theme: Theme) -> Self {
|
||||
Self(
|
||||
errs.into_iter()
|
||||
.map(|err| ParserRunnerError::new(*err.0, input.clone()))
|
||||
.map(|err| ParserRunnerError::new(*err.0, input.clone(), theme))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::cmp::Ordering;
|
|||
use erg_common::cache::CacheSet;
|
||||
use erg_common::config::ErgConfig;
|
||||
use erg_common::config::Input;
|
||||
use erg_common::style::THEME;
|
||||
use erg_common::traits::{Locational, Runnable, Stream};
|
||||
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};
|
||||
|
||||
|
@ -41,7 +42,7 @@ impl Runnable for LexerRunner {
|
|||
let lexer = Lexer::from_str(self.input().read());
|
||||
let ts = lexer
|
||||
.lex()
|
||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?;
|
||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?;
|
||||
println!("{ts}");
|
||||
Ok(0)
|
||||
}
|
||||
|
@ -51,13 +52,13 @@ impl Runnable for LexerRunner {
|
|||
if cfg!(feature = "debug") {
|
||||
let ts = lexer
|
||||
.lex()
|
||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?;
|
||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?;
|
||||
println!("{ts}");
|
||||
Ok(ts.to_string())
|
||||
} else {
|
||||
Ok(lexer
|
||||
.lex()
|
||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?
|
||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use erg_common::error::Location;
|
|||
use erg_common::option_enum_unwrap;
|
||||
use erg_common::set::Set as HashSet;
|
||||
use erg_common::str::Str;
|
||||
use erg_common::style::THEME;
|
||||
use erg_common::traits::Runnable;
|
||||
use erg_common::traits::{Locational, Stream};
|
||||
use erg_common::{
|
||||
|
@ -206,16 +207,16 @@ impl ParserRunner {
|
|||
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> {
|
||||
Parser::new(ts)
|
||||
.parse()
|
||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
|
||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> {
|
||||
let ts = Lexer::new(Input::Str(src))
|
||||
.lex()
|
||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?;
|
||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))?;
|
||||
Parser::new(ts)
|
||||
.parse()
|
||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
|
||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use erg_common::config::{ErgConfig, Input};
|
||||
use erg_common::error::MultiErrorDisplay;
|
||||
use erg_common::style::THEME;
|
||||
use erg_common::traits::Runnable;
|
||||
|
||||
use erg_parser::error::ParserRunnerErrors;
|
||||
|
@ -58,7 +59,7 @@ fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerError
|
|||
match parser.parse_token_stream(
|
||||
lexer
|
||||
.lex()
|
||||
.map_err(|errs| ParserRunnerErrors::convert(&input, errs))?,
|
||||
.map_err(|errs| ParserRunnerErrors::convert(&input, errs, THEME))?,
|
||||
) {
|
||||
Ok(module) => {
|
||||
println!("{module}");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue