mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 12:24:45 +00:00
Merge pull request #232 from erg-lang/split-err-msg
Split error messages
This commit is contained in:
commit
2caa6b6ec9
15 changed files with 1652 additions and 1006 deletions
|
@ -10,9 +10,9 @@ use crate::style::Attribute;
|
||||||
use crate::style::Characters;
|
use crate::style::Characters;
|
||||||
use crate::style::Color;
|
use crate::style::Color;
|
||||||
use crate::style::StyledStr;
|
use crate::style::StyledStr;
|
||||||
use crate::style::StyledString;
|
|
||||||
use crate::style::StyledStrings;
|
use crate::style::StyledStrings;
|
||||||
use crate::style::Theme;
|
use crate::style::Theme;
|
||||||
|
use crate::style::THEME;
|
||||||
use crate::traits::{Locational, Stream};
|
use crate::traits::{Locational, Stream};
|
||||||
use crate::{impl_display_from_debug, switch_lang};
|
use crate::{impl_display_from_debug, switch_lang};
|
||||||
|
|
||||||
|
@ -228,37 +228,6 @@ impl From<&str> for ErrorKind {
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
pub enum Location {
|
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
|
/// Location used for basic errors
|
||||||
/// ```erg
|
/// ```erg
|
||||||
|
@ -310,128 +279,39 @@ impl Location {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pair(lhs: Self, rhs: Self) -> Self {
|
|
||||||
Self::RangePair {
|
|
||||||
ln_first: (lhs.ln_begin().unwrap(), lhs.ln_end().unwrap()),
|
|
||||||
col_first: (lhs.col_begin().unwrap(), lhs.col_end().unwrap()),
|
|
||||||
ln_second: (rhs.ln_begin().unwrap(), rhs.ln_end().unwrap()),
|
|
||||||
col_second: (rhs.col_begin().unwrap(), rhs.col_end().unwrap()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn ln_begin(&self) -> Option<usize> {
|
pub const fn ln_begin(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Self::RangePair {
|
Self::Range { ln_begin, .. } | Self::LineRange(ln_begin, _) | Self::Line(ln_begin) => {
|
||||||
ln_first: (ln_begin, _),
|
Some(*ln_begin)
|
||||||
..
|
|
||||||
}
|
}
|
||||||
| Self::Range { ln_begin, .. }
|
|
||||||
| Self::LineRange(ln_begin, _)
|
|
||||||
| Self::Line(ln_begin) => Some(*ln_begin),
|
|
||||||
Self::Unknown => None,
|
Self::Unknown => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn ln_end(&self) -> Option<usize> {
|
pub const fn ln_end(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Self::RangePair {
|
Self::Range { ln_end, .. } | Self::LineRange(ln_end, _) | Self::Line(ln_end) => {
|
||||||
ln_second: (_, ln_end),
|
Some(*ln_end)
|
||||||
..
|
|
||||||
}
|
}
|
||||||
| Self::Range { ln_end, .. }
|
|
||||||
| Self::LineRange(ln_end, _)
|
|
||||||
| Self::Line(ln_end) => Some(*ln_end),
|
|
||||||
Self::Unknown => None,
|
Self::Unknown => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn col_begin(&self) -> Option<usize> {
|
pub const fn col_begin(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Self::RangePair {
|
Self::Range { col_begin, .. } => Some(*col_begin),
|
||||||
col_first: (col_begin, _),
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| Self::Range { col_begin, .. } => Some(*col_begin),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn col_end(&self) -> Option<usize> {
|
pub const fn col_end(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Self::RangePair {
|
Self::Range { col_end, .. } => Some(*col_end),
|
||||||
col_second: (_, col_end),
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| Self::Range { col_end, .. } => Some(*col_end),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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,
|
|
||||||
pub kind: ErrorKind,
|
|
||||||
pub loc: Location,
|
|
||||||
pub desc: String,
|
|
||||||
pub hint: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ErrorCore {
|
|
||||||
pub fn new<S: Into<String>>(
|
|
||||||
errno: usize,
|
|
||||||
kind: ErrorKind,
|
|
||||||
loc: Location,
|
|
||||||
desc: S,
|
|
||||||
hint: Option<String>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
errno,
|
|
||||||
kind,
|
|
||||||
loc,
|
|
||||||
desc: desc.into(),
|
|
||||||
hint,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dummy(errno: usize) -> Self {
|
|
||||||
Self::new(
|
|
||||||
errno,
|
|
||||||
DummyError,
|
|
||||||
Location::Line(errno as usize),
|
|
||||||
"<dummy>",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unreachable(fn_name: &str, line: u32) -> Self {
|
|
||||||
Self::bug(line as usize, Location::Line(line as usize), fn_name, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
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のバグです、開発者に報告して下さい({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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn format_context<E: ErrorDisplay + ?Sized>(
|
fn format_context<E: ErrorDisplay + ?Sized>(
|
||||||
e: &E,
|
e: &E,
|
||||||
|
@ -445,6 +325,8 @@ fn format_context<E: ErrorDisplay + ?Sized>(
|
||||||
chars: &Characters,
|
chars: &Characters,
|
||||||
// kinds of error for specify the color
|
// kinds of error for specify the color
|
||||||
mark: char,
|
mark: char,
|
||||||
|
sub_msg: &[String],
|
||||||
|
hint: Option<&String>,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mark = mark.to_string();
|
let mark = mark.to_string();
|
||||||
let codes = if e.input().is_repl() {
|
let codes = if e.input().is_repl() {
|
||||||
|
@ -484,20 +366,326 @@ fn format_context<E: ErrorDisplay + ?Sized>(
|
||||||
}
|
}
|
||||||
context.push_str("\n");
|
context.push_str("\n");
|
||||||
}
|
}
|
||||||
context.push_str_with_color(&offset, gutter_color);
|
|
||||||
context.push_str(&" ".repeat(col_end - 1));
|
let msg_num = sub_msg.len().saturating_sub(1);
|
||||||
context.push_str_with_color(&chars.left_bottom_line(), err_color);
|
for (i, msg) in sub_msg.iter().enumerate() {
|
||||||
context.to_string()
|
context.push_str_with_color(&offset, gutter_color);
|
||||||
|
context.push_str(&" ".repeat(col_end - 1));
|
||||||
|
if i == msg_num && hint.is_none() {
|
||||||
|
context.push_str_with_color(&chars.left_bottom_line(), err_color);
|
||||||
|
} else {
|
||||||
|
context.push_str_with_color(&chars.left_cross(), err_color);
|
||||||
|
}
|
||||||
|
context.push_str(msg);
|
||||||
|
context.push_str("\n")
|
||||||
|
}
|
||||||
|
if let Some(hint) = hint {
|
||||||
|
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.push_str(hint);
|
||||||
|
context.push_str("\n")
|
||||||
|
}
|
||||||
|
context.to_string() + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SubMessage {
|
||||||
|
pub loc: Location,
|
||||||
|
msg: Vec<String>,
|
||||||
|
hint: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubMessage {
|
||||||
|
///
|
||||||
|
/// Used when the msg or hint si empty.
|
||||||
|
/// `msg` is Vec\<String\> instead of Option\<String\> because it can be used when there are multiple `msg`s as well as multiple lines.
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// let msg = SubMessage::ambiguous_new(loc, vec![], None); // this code same as only_loc()
|
||||||
|
///
|
||||||
|
/// let hint = Some("hint message here".to_string())
|
||||||
|
/// let msg = SubMessage::ambiguous_new(loc, vec![], hint);
|
||||||
|
/// /* example
|
||||||
|
/// -------
|
||||||
|
/// `- hint message here
|
||||||
|
/// */
|
||||||
|
///
|
||||||
|
/// let hint = Some("hint here".to_string())
|
||||||
|
/// let first = StyledString::new("1th message", Color::Red, None);
|
||||||
|
/// let second = StyledString::new("2th message", Color::White, None);
|
||||||
|
/// :
|
||||||
|
/// let nth = StyledString::new("nth message", Color::Green, None);
|
||||||
|
/// let msg = SubMessage::ambiguous_new(
|
||||||
|
/// loc,
|
||||||
|
/// vec![
|
||||||
|
/// first.to_string(),
|
||||||
|
/// second.to_string(),
|
||||||
|
/// ...,
|
||||||
|
/// nth.to_string(),
|
||||||
|
/// ],
|
||||||
|
/// hint);
|
||||||
|
/// /* example
|
||||||
|
/// -------
|
||||||
|
/// :- 1th message
|
||||||
|
/// :- 2th message
|
||||||
|
/// :
|
||||||
|
/// :- nth message
|
||||||
|
/// `- hint here
|
||||||
|
/// */
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub fn ambiguous_new(loc: Location, msg: Vec<String>, hint: Option<String>) -> Self {
|
||||||
|
Self { loc, msg, hint }
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Used when only Location is fixed.
|
||||||
|
/// In this case, error position is just modified
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// let sub_msg = SubMessage::only_loc(loc);
|
||||||
|
/// ```
|
||||||
|
pub fn only_loc(loc: Location) -> Self {
|
||||||
|
Self {
|
||||||
|
loc,
|
||||||
|
msg: Vec::new(),
|
||||||
|
hint: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_hint<S: Into<String>>(&mut self, hint: S) {
|
||||||
|
self.hint = Some(hint.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_hint(self) -> Option<String> {
|
||||||
|
self.hint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line breaks are not included except for line breaks that signify the end of a sentence.
|
||||||
|
// In other words, do not include blank lines for formatting purposes.
|
||||||
|
fn format_code_and_pointer<E: ErrorDisplay + ?Sized>(
|
||||||
|
&self,
|
||||||
|
e: &E,
|
||||||
|
err_color: Color,
|
||||||
|
gutter_color: Color,
|
||||||
|
mark: char,
|
||||||
|
chars: &Characters,
|
||||||
|
) -> String {
|
||||||
|
match self.loc {
|
||||||
|
Location::Range {
|
||||||
|
ln_begin,
|
||||||
|
col_begin,
|
||||||
|
ln_end,
|
||||||
|
col_end,
|
||||||
|
} => format_context(
|
||||||
|
e,
|
||||||
|
ln_begin,
|
||||||
|
ln_end,
|
||||||
|
col_begin,
|
||||||
|
col_end,
|
||||||
|
err_color,
|
||||||
|
gutter_color,
|
||||||
|
chars,
|
||||||
|
mark,
|
||||||
|
&self.msg,
|
||||||
|
self.hint.as_ref(),
|
||||||
|
),
|
||||||
|
Location::LineRange(ln_begin, ln_end) => {
|
||||||
|
let input = e.input();
|
||||||
|
let (vbreak, vbar) = chars.gutters();
|
||||||
|
let mut cxt = StyledStrings::default();
|
||||||
|
let codes = if input.is_repl() {
|
||||||
|
vec![input.reread()]
|
||||||
|
} else {
|
||||||
|
input.reread_lines(ln_begin, ln_end)
|
||||||
|
};
|
||||||
|
let mark = mark.to_string();
|
||||||
|
for (i, lineno) in (ln_begin..=ln_end).enumerate() {
|
||||||
|
cxt.push_str_with_color(&format!("{lineno} {vbar} "), gutter_color);
|
||||||
|
cxt.push_str(&codes[i]);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
cxt.push_str_with_color(
|
||||||
|
&format!("{} {}", &" ".repeat(lineno.to_string().len()), vbreak),
|
||||||
|
gutter_color,
|
||||||
|
);
|
||||||
|
cxt.push_str(&" ".repeat(lineno.to_string().len()));
|
||||||
|
cxt.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), err_color);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
cxt.push_str("\n");
|
||||||
|
for msg in self.msg.iter() {
|
||||||
|
cxt.push_str(msg);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
if let Some(hint) = self.hint.as_ref() {
|
||||||
|
cxt.push_str(hint);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
cxt.to_string()
|
||||||
|
}
|
||||||
|
Location::Line(lineno) => {
|
||||||
|
let input = e.input();
|
||||||
|
let (_, vbar) = chars.gutters();
|
||||||
|
let code = if input.is_repl() {
|
||||||
|
input.reread()
|
||||||
|
} else {
|
||||||
|
input.reread_lines(lineno, lineno).remove(0)
|
||||||
|
};
|
||||||
|
let mut cxt = StyledStrings::default();
|
||||||
|
cxt.push_str_with_color(&format!(" {lineno} {} ", vbar), gutter_color);
|
||||||
|
cxt.push_str(&code);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
for msg in self.msg.iter() {
|
||||||
|
cxt.push_str(msg);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
if let Some(hint) = self.hint.as_ref() {
|
||||||
|
cxt.push_str(hint);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
cxt.push_str("\n");
|
||||||
|
cxt.to_string()
|
||||||
|
}
|
||||||
|
Location::Unknown => match e.input() {
|
||||||
|
Input::File(_) => "\n".to_string(),
|
||||||
|
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.push_str("\n");
|
||||||
|
for msg in self.msg.iter() {
|
||||||
|
cxt.push_str(msg);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
if let Some(hint) = self.hint.as_ref() {
|
||||||
|
cxt.push_str(hint);
|
||||||
|
cxt.push_str("\n");
|
||||||
|
}
|
||||||
|
cxt.push_str("\n");
|
||||||
|
cxt.to_string()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In Erg, common parts used by error.
|
||||||
|
/// Must be wrap when to use.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ErrorCore {
|
||||||
|
pub sub_messages: Vec<SubMessage>,
|
||||||
|
pub main_message: String,
|
||||||
|
pub errno: usize,
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
pub loc: Location,
|
||||||
|
theme: Theme,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorCore {
|
||||||
|
pub fn new<S: Into<String>>(
|
||||||
|
sub_messages: Vec<SubMessage>,
|
||||||
|
main_message: S,
|
||||||
|
errno: usize,
|
||||||
|
kind: ErrorKind,
|
||||||
|
loc: Location,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
sub_messages,
|
||||||
|
main_message: main_message.into(),
|
||||||
|
errno,
|
||||||
|
kind,
|
||||||
|
loc,
|
||||||
|
theme: THEME,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dummy(errno: usize) -> Self {
|
||||||
|
Self::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Line(errno as usize))],
|
||||||
|
"<dummy>",
|
||||||
|
errno,
|
||||||
|
DummyError,
|
||||||
|
Location::Line(errno as usize),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unreachable(fn_name: &str, line: u32) -> Self {
|
||||||
|
Self::bug(line as usize, Location::Line(line as usize), fn_name, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
|
||||||
|
let m_msg = switch_lang!(
|
||||||
|
"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}"),
|
||||||
|
);
|
||||||
|
Self::new(
|
||||||
|
vec![SubMessage::only_loc(loc)],
|
||||||
|
&m_msg,
|
||||||
|
errno,
|
||||||
|
CompilerSystemError,
|
||||||
|
loc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmt_header(&self, color: Color, caused_by: &str, input: &str) -> String {
|
||||||
|
let loc = match self.loc {
|
||||||
|
Location::Range {
|
||||||
|
ln_begin, ln_end, ..
|
||||||
|
} if ln_begin == ln_end => format!(", line {ln_begin}"),
|
||||||
|
Location::Range {
|
||||||
|
ln_begin, ln_end, ..
|
||||||
|
}
|
||||||
|
| Location::LineRange(ln_begin, ln_end) => format!(", line {ln_begin}..{ln_end}"),
|
||||||
|
Location::Line(lineno) => format!(", line {lineno}"),
|
||||||
|
Location::Unknown => "".to_string(),
|
||||||
|
};
|
||||||
|
let kind = if self.kind.is_error() {
|
||||||
|
"Error"
|
||||||
|
} else if self.kind.is_warning() {
|
||||||
|
"Warning"
|
||||||
|
} else {
|
||||||
|
"Exception"
|
||||||
|
};
|
||||||
|
let kind = self.theme.characters.error_kind_format(kind, self.errno);
|
||||||
|
format!(
|
||||||
|
"{kind}: File {input}{loc}, {caused_by}",
|
||||||
|
kind = StyledStr::new(&kind, Some(color), Some(Attribute::Bold))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn specified_theme(&self) -> (Color, char) {
|
||||||
|
let (color, mark) = if self.kind.is_error() {
|
||||||
|
self.theme.error()
|
||||||
|
} else if self.kind.is_warning() {
|
||||||
|
self.theme.warning()
|
||||||
|
} else {
|
||||||
|
self.theme.exception()
|
||||||
|
};
|
||||||
|
(color, mark)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// format:
|
/// format:
|
||||||
/// ```txt
|
/// ```txt
|
||||||
/// Error[#{.errno}]: File {file}, line {.loc (as line)}, in {.caused_by}
|
/// Error[#{.errno}]: File {file}, line {.loc (as line)}, in {.caused_by}
|
||||||
/// {.loc (as line)}| {src}
|
|
||||||
/// {pointer}
|
|
||||||
/// {.kind}: {.desc}
|
|
||||||
///
|
///
|
||||||
/// {.hint}
|
/// {.loc (as line)}| {src}
|
||||||
|
/// {offset} : {pointer}
|
||||||
|
/// {offset} : {sub_msgs}
|
||||||
|
/// {offset} : {.hint}
|
||||||
|
///
|
||||||
|
/// {.kind}: {.desc}
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -505,21 +693,20 @@ fn format_context<E: ErrorDisplay + ?Sized>(
|
||||||
/// ```txt
|
/// ```txt
|
||||||
/// Error[#2223]: File <stdin>, line 1, in <module>
|
/// Error[#2223]: File <stdin>, line 1, in <module>
|
||||||
///
|
///
|
||||||
/// 1 | 100 = i
|
/// 1 │ 100 = i
|
||||||
/// ---
|
/// · ---
|
||||||
/// ╰─ SyntaxError: cannot assign to 100
|
/// · │─ sub_msg1: first sub message here
|
||||||
|
/// · │─ sub_msg2: second sub message here
|
||||||
|
/// · ╰─ hint: hint message here
|
||||||
///
|
///
|
||||||
/// hint: hint message here
|
/// SyntaxError: cannot assign to 100
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
pub trait ErrorDisplay {
|
pub trait ErrorDisplay {
|
||||||
fn core(&self) -> &ErrorCore;
|
fn core(&self) -> &ErrorCore;
|
||||||
fn input(&self) -> &Input;
|
fn input(&self) -> &Input;
|
||||||
/// Colors and indication char for each type(error, warning, exception)
|
|
||||||
fn theme(&self) -> &Theme;
|
|
||||||
/// The block name the error caused.
|
/// The block name the error caused.
|
||||||
/// This will be None if the error occurred before semantic analysis.
|
/// This will be None if the error occurred before semantic analysis.
|
||||||
/// As for the internal error, do not put the fn name here.
|
|
||||||
fn caused_by(&self) -> &str;
|
fn caused_by(&self) -> &str;
|
||||||
/// the previous error that caused this error.
|
/// the previous error that caused this error.
|
||||||
fn ref_inner(&self) -> Option<&Self>;
|
fn ref_inner(&self) -> Option<&Self>;
|
||||||
|
@ -539,248 +726,44 @@ pub trait ErrorDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&self) -> String {
|
fn show(&self) -> String {
|
||||||
let theme = self.theme();
|
let core = self.core();
|
||||||
let ((color, mark), kind) = if self.core().kind.is_error() {
|
let (color, mark) = core.specified_theme();
|
||||||
(theme.error(), "Error")
|
let (gutter_color, chars) = core.theme.characters();
|
||||||
} else if self.core().kind.is_warning() {
|
let mut msg = String::new();
|
||||||
(theme.warning(), "Warning")
|
msg += &core.fmt_header(color, self.caused_by(), self.input().enclosed_name());
|
||||||
} else {
|
msg += "\n\n";
|
||||||
(theme.exception(), "Exception")
|
for sub_msg in &core.sub_messages {
|
||||||
};
|
msg += &sub_msg.format_code_and_pointer(self, color, gutter_color, mark, chars);
|
||||||
|
|
||||||
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!(
|
|
||||||
"\
|
|
||||||
{}
|
|
||||||
{}{}: {}
|
|
||||||
|
|
||||||
{}
|
|
||||||
|
|
||||||
",
|
|
||||||
self.format_header(kind),
|
|
||||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
|
||||||
self.core().kind,
|
|
||||||
self.core().desc,
|
|
||||||
hints,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"\
|
|
||||||
{}
|
|
||||||
{}{}: {}
|
|
||||||
|
|
||||||
",
|
|
||||||
self.format_header(kind),
|
|
||||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
|
||||||
self.core().kind,
|
|
||||||
self.core().desc,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
msg += &core.main_message;
|
||||||
|
msg += "\n\n";
|
||||||
|
msg
|
||||||
}
|
}
|
||||||
|
|
||||||
/// for fmt::Display
|
/// for fmt::Display
|
||||||
fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let theme = self.theme();
|
let core = self.core();
|
||||||
let ((color, mark), kind) = if self.core().kind.is_error() {
|
let (color, mark) = core.specified_theme();
|
||||||
(theme.error(), "Error")
|
let (gutter_color, chars) = core.theme.characters();
|
||||||
} else if self.core().kind.is_warning() {
|
write!(
|
||||||
(theme.warning(), "Warning")
|
f,
|
||||||
} else {
|
"{}\n\n",
|
||||||
(theme.exception(), "Exception")
|
core.fmt_header(color, self.caused_by(), self.input().enclosed_name())
|
||||||
};
|
)?;
|
||||||
let (gutter_color, chars) = theme.characters();
|
for sub_msg in &core.sub_messages {
|
||||||
let kind = StyledString::new(
|
write!(
|
||||||
&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,
|
f,
|
||||||
"\
|
"{}",
|
||||||
{}
|
&sub_msg.format_code_and_pointer(self, color, gutter_color, mark, chars)
|
||||||
{}{}: {}
|
|
||||||
|
|
||||||
{}
|
|
||||||
|
|
||||||
",
|
|
||||||
self.format_header(kind),
|
|
||||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
|
||||||
self.core().kind,
|
|
||||||
self.core().desc,
|
|
||||||
hints,
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"\
|
|
||||||
{}
|
|
||||||
{}{}: {}
|
|
||||||
|
|
||||||
",
|
|
||||||
self.format_header(kind),
|
|
||||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
|
||||||
self.core().kind,
|
|
||||||
self.core().desc,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
write!(f, "{}\n\n", core.main_message)?;
|
||||||
if let Some(inner) = self.ref_inner() {
|
if let Some(inner) = self.ref_inner() {
|
||||||
inner.format(f)
|
inner.format(f)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_header(&self, kind: StyledString) -> String {
|
|
||||||
let loc = match self.core().loc {
|
|
||||||
Location::Range {
|
|
||||||
ln_begin, ln_end, ..
|
|
||||||
} if ln_begin == ln_end => format!(", line {ln_begin}"),
|
|
||||||
Location::Range {
|
|
||||||
ln_begin, ln_end, ..
|
|
||||||
}
|
|
||||||
| Location::LineRange(ln_begin, ln_end) => format!(", line {ln_begin}..{ln_end}"),
|
|
||||||
Location::RangePair {
|
|
||||||
ln_first: (l1, l2),
|
|
||||||
ln_second: (l3, l4),
|
|
||||||
..
|
|
||||||
} => format!(", line {l1}..{l2}, {l3}..{l4}"),
|
|
||||||
Location::Line(lineno) => format!(", line {lineno}"),
|
|
||||||
Location::Unknown => "".to_string(),
|
|
||||||
};
|
|
||||||
let caused_by = if self.caused_by() != "" {
|
|
||||||
format!(", in {}", self.caused_by())
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
format!(
|
|
||||||
"{kind}: File {input}{loc}{caused_by}\n",
|
|
||||||
input = self.input().enclosed_name(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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_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 {
|
|
||||||
ln_begin,
|
|
||||||
col_begin,
|
|
||||||
ln_end,
|
|
||||||
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 mark = mark.to_string();
|
|
||||||
for (i, lineno) in (ln_begin..=ln_end).enumerate() {
|
|
||||||
cxt.push_str_with_color(&format!("{lineno} {}", vbar), err_color);
|
|
||||||
cxt.push_str(&codes[i]);
|
|
||||||
cxt.push_str("\n");
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
cxt.push_str("\n");
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
};
|
|
||||||
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 => {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::borrow::Borrow;
|
use std::borrow::{Borrow, Cow};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, Deref};
|
use std::ops::{Add, Deref};
|
||||||
|
@ -61,6 +61,15 @@ impl From<Str> for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Str> for Cow<'a, str> {
|
||||||
|
fn from(s: Str) -> Self {
|
||||||
|
match s {
|
||||||
|
Str::Static(s) => Cow::Owned(s.to_owned()),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// &'static str -> &strになってしまわないように
|
// &'static str -> &strになってしまわないように
|
||||||
// あえて`impl<S: Into<Str>> From<S> for Str { ... }`はしない
|
// あえて`impl<S: Into<Str>> From<S> for Str { ... }`はしない
|
||||||
impl From<&'static str> for Str {
|
impl From<&'static str> for Str {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub const ATTR_RESET: &str = "\x1b[0m";
|
pub const ATTR_RESET: &str = "\x1b[0m";
|
||||||
pub const BOLD: &str = "\x1b[1m";
|
pub const BOLD: &str = "\x1b[1m";
|
||||||
pub const UNDERLINE: &str = "\x1b[4m";
|
pub const UNDERLINE: &str = "\x1b[4m";
|
||||||
|
@ -15,15 +17,15 @@ pub const RED: &str = "\x1b[91m";
|
||||||
pub const WHITE: &str = "\x1b[97m";
|
pub const WHITE: &str = "\x1b[97m";
|
||||||
pub const YELLOW: &str = "\x1b[93m";
|
pub const YELLOW: &str = "\x1b[93m";
|
||||||
// custom colors when use `pretty`
|
// custom colors when use `pretty`
|
||||||
pub const CUSTOM_RED: &str = "\x1b[38;2;185;64;71m";
|
pub const CUSTOM_RED: &str = "\x1b[38;2;255;76;76m";
|
||||||
pub const CUSTOM_BLUE: &str = "\x1b[38;2;230;234;227m";
|
pub const CUSTOM_BLUE: &str = "\x1b[38;2;76;76;255m";
|
||||||
pub const CUSTOM_GRAY: &str = "\x1b[38;2;244;0;25m";
|
pub const CUSTOM_GRAY: &str = "\x1b[38;2;231;231;235m";
|
||||||
pub const CUSTOM_CYAN: &str = "\x1b[38;2;160;216;239m";
|
pub const CUSTOM_CYAN: &str = "\x1b[38;2;76;255;255m";
|
||||||
pub const CUSTOM_MAGENTA: &str = "\x1b[38;2;103;65;150m";
|
pub const CUSTOM_MAGENTA: &str = "\x1b[38;2;165;76;255m";
|
||||||
pub const CUSTOM_GREEN: &str = "\x1b[38;2;170;209;71m";
|
pub const CUSTOM_GREEN: &str = "\x1b[38;2;76;255;76m";
|
||||||
pub const CUSTOM_YELLOW: &str = "\x1b[38;2;230;180;34m";
|
pub const CUSTOM_YELLOW: &str = "\x1b[38;2;255;255;76m";
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
Reset,
|
Reset,
|
||||||
Black,
|
Black,
|
||||||
|
@ -68,7 +70,7 @@ impl Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
|
||||||
pub enum Attribute {
|
pub enum Attribute {
|
||||||
Reset,
|
Reset,
|
||||||
Underline,
|
Underline,
|
||||||
|
@ -87,13 +89,14 @@ impl Attribute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ThemeColors {
|
pub struct ThemeColors {
|
||||||
pub error: Color,
|
pub error: Color,
|
||||||
pub warning: Color,
|
pub warning: Color,
|
||||||
pub exception: Color,
|
pub exception: Color,
|
||||||
pub gutter: Color,
|
pub gutter: Color,
|
||||||
pub hint: Color,
|
pub hint: Color,
|
||||||
|
pub accent: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "pretty"))]
|
#[cfg(not(feature = "pretty"))]
|
||||||
|
@ -103,6 +106,7 @@ pub const COLORS: ThemeColors = ThemeColors {
|
||||||
exception: Color::Magenta,
|
exception: Color::Magenta,
|
||||||
gutter: Color::Cyan,
|
gutter: Color::Cyan,
|
||||||
hint: Color::Green,
|
hint: Color::Green,
|
||||||
|
accent: Color::White,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "pretty")]
|
#[cfg(feature = "pretty")]
|
||||||
|
@ -112,9 +116,10 @@ pub const COLORS: ThemeColors = ThemeColors {
|
||||||
exception: Color::CustomMagenta,
|
exception: Color::CustomMagenta,
|
||||||
gutter: Color::CustomCyan,
|
gutter: Color::CustomCyan,
|
||||||
hint: Color::CustomGreen,
|
hint: Color::CustomGreen,
|
||||||
|
accent: Color::CustomGray,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Characters {
|
pub struct Characters {
|
||||||
hat: char, // error
|
hat: char, // error
|
||||||
wave: char, // exception
|
wave: char, // exception
|
||||||
|
@ -141,10 +146,10 @@ impl Characters {
|
||||||
(self.vbreak, self.vbar)
|
(self.vbreak, self.vbar)
|
||||||
}
|
}
|
||||||
|
|
||||||
// " `- "
|
// "`- "
|
||||||
#[cfg(not(feature = "unicode"))]
|
#[cfg(not(feature = "unicode"))]
|
||||||
pub fn left_bottom_line(&self) -> String {
|
pub fn left_bottom_line(&self) -> String {
|
||||||
format!(" {}{} ", self.lbot, self.line)
|
format!("{}{} ", self.lbot, self.line)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `╰─ `
|
// `╰─ `
|
||||||
|
@ -153,6 +158,18 @@ impl Characters {
|
||||||
format!("{}{} ", self.lbot, self.line)
|
format!("{}{} ", self.lbot, self.line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "|- "
|
||||||
|
#[cfg(not(feature = "unicode"))]
|
||||||
|
pub fn left_cross(&self) -> String {
|
||||||
|
format!("{}{} ", self.vbar, self.line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "│─ "
|
||||||
|
#[cfg(feature = "unicode")]
|
||||||
|
pub fn left_cross(&self) -> String {
|
||||||
|
format!("{}{} ", self.vbar, self.line)
|
||||||
|
}
|
||||||
|
|
||||||
// kind[padded error number]
|
// kind[padded error number]
|
||||||
#[cfg(not(feature = "pretty"))]
|
#[cfg(not(feature = "pretty"))]
|
||||||
pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String {
|
pub fn error_kind_format(&self, kind: &str, err_num: usize) -> String {
|
||||||
|
@ -177,7 +194,7 @@ impl Characters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub colors: ThemeColors,
|
pub colors: ThemeColors,
|
||||||
pub characters: Characters,
|
pub characters: Characters,
|
||||||
|
@ -254,11 +271,7 @@ pub struct StyledStr<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StyledStr<'a> {
|
impl<'a> StyledStr<'a> {
|
||||||
pub const fn new<'b: 'a>(
|
pub const fn new(text: &'a str, color: Option<Color>, attribute: Option<Attribute>) -> Self {
|
||||||
text: &'b str,
|
|
||||||
color: Option<Color>,
|
|
||||||
attribute: Option<Attribute>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
text,
|
text,
|
||||||
color,
|
color,
|
||||||
|
@ -300,9 +313,22 @@ pub struct StyledString {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledString {
|
impl StyledString {
|
||||||
pub fn new(s: &str, color: Option<Color>, attribute: Option<Attribute>) -> Self {
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// let s = String::from("Hello, world");
|
||||||
|
/// StyledString::new(s, None, None);
|
||||||
|
/// let s = "Hello, world";
|
||||||
|
/// StyledString::new(s, None, None);
|
||||||
|
/// ```
|
||||||
|
pub fn new<'a, S: Into<Cow<'a, str>>>(
|
||||||
|
s: S,
|
||||||
|
color: Option<Color>,
|
||||||
|
attribute: Option<Attribute>,
|
||||||
|
) -> Self {
|
||||||
|
let text: Cow<'a, str> = s.into();
|
||||||
Self {
|
Self {
|
||||||
text: String::from(s),
|
text: text.into_owned(),
|
||||||
color,
|
color,
|
||||||
attribute,
|
attribute,
|
||||||
}
|
}
|
||||||
|
@ -320,7 +346,11 @@ impl StyledString {
|
||||||
/// println!("{text}"); // Two lines of text underlined are displayed
|
/// println!("{text}"); // Two lines of text underlined are displayed
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push_str(&mut self, s: &str) {
|
pub fn push_str(&mut self, s: &str) {
|
||||||
self.text.push_str(s);
|
self.text.push_str(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.text.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,9 +435,10 @@ impl StyledStrings {
|
||||||
/// texts.push_str_with_color("\n If you want to add break lines, you should add `\n`.", Color::Magenta);
|
/// texts.push_str_with_color("\n If you want to add break lines, you should add `\n`.", Color::Magenta);
|
||||||
/// println!("{}", texts);
|
/// println!("{}", texts);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push_str_with_color(&mut self, s: &str, color: Color) {
|
pub fn push_str_with_color<'a, S: Into<Cow<'a, str>>>(&mut self, s: S, color: Color) {
|
||||||
if self.is_same_color(color) {
|
if self.is_same_color(color) {
|
||||||
self.texts.last_mut().unwrap().text.push_str(s);
|
let text = s.into();
|
||||||
|
self.texts.last_mut().unwrap().text.push_str(&text);
|
||||||
} else {
|
} else {
|
||||||
self.texts.push(StyledString::new(s, Some(color), None));
|
self.texts.push(StyledString::new(s, Some(color), None));
|
||||||
}
|
}
|
||||||
|
@ -426,23 +457,37 @@ impl StyledStrings {
|
||||||
/// // texts.push_str_with_color_and_attribute("Must be specify the color and attribute", None, Attribute::Underline);
|
/// // texts.push_str_with_color_and_attribute("Must be specify the color and attribute", None, Attribute::Underline);
|
||||||
/// println!("{}", texts);
|
/// println!("{}", texts);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push_str_with_color_and_attribute(&mut self, s: &str, color: Color, attr: Attribute) {
|
pub fn push_str_with_color_and_attribute<'a, S: Into<Cow<'a, str>>>(
|
||||||
|
&mut self,
|
||||||
|
s: S,
|
||||||
|
color: Color,
|
||||||
|
attr: Attribute,
|
||||||
|
) {
|
||||||
if self.is_same_color(color) && self.is_same_attribute(attr) {
|
if self.is_same_color(color) && self.is_same_attribute(attr) {
|
||||||
self.texts.last_mut().unwrap().text.push_str(s);
|
let text = s.into();
|
||||||
|
self.texts.last_mut().unwrap().text.push_str(&text);
|
||||||
} else {
|
} else {
|
||||||
self.texts
|
self.texts
|
||||||
.push(StyledString::new(s, Some(color), Some(attr)));
|
.push(StyledString::new(s, Some(color), Some(attr)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_same_color(&self, color: Color) -> bool {
|
///
|
||||||
|
/// Determine if all strings in Vec are empty
|
||||||
|
/// Returns False if any string is present.
|
||||||
|
///
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.texts.iter().all(|s| s.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_same_color(&self, color: Color) -> bool {
|
||||||
if let Some(text) = self.texts.last() {
|
if let Some(text) = self.texts.last() {
|
||||||
return text.color == Some(color);
|
return text.color == Some(color);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_same_attribute(&self, attr: Attribute) -> bool {
|
fn is_same_attribute(&self, attr: Attribute) -> bool {
|
||||||
if let Some(text) = self.texts.last() {
|
if let Some(text) = self.texts.last() {
|
||||||
if let Some(text_attr) = text.attribute {
|
if let Some(text_attr) = text.attribute {
|
||||||
return text_attr == attr;
|
return text_attr == attr;
|
||||||
|
@ -461,6 +506,12 @@ impl std::fmt::Display for StyledStrings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<StyledStrings> for String {
|
||||||
|
fn from(s: StyledStrings) -> Self {
|
||||||
|
s.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -513,10 +564,15 @@ mod tests {
|
||||||
Attribute::Bold,
|
Attribute::Bold,
|
||||||
);
|
);
|
||||||
texts.push_str_with_color_and_attribute(
|
texts.push_str_with_color_and_attribute(
|
||||||
"White and underlined text",
|
"Blue and underlined text\n",
|
||||||
Color::White,
|
Color::Blue,
|
||||||
Attribute::Underline,
|
Attribute::Underline,
|
||||||
);
|
);
|
||||||
|
texts.push_str_with_color_and_attribute(
|
||||||
|
"Red and reversed text",
|
||||||
|
Color::Red,
|
||||||
|
Attribute::Reversed,
|
||||||
|
);
|
||||||
println!("{}", texts);
|
println!("{}", texts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,12 +477,7 @@ pub trait Locational {
|
||||||
|
|
||||||
fn ln_begin(&self) -> Option<usize> {
|
fn ln_begin(&self) -> Option<usize> {
|
||||||
match self.loc() {
|
match self.loc() {
|
||||||
Location::RangePair {
|
Location::Range { ln_begin, .. } | Location::LineRange(ln_begin, _) => Some(ln_begin),
|
||||||
ln_first: (ln_begin, _),
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| Location::Range { ln_begin, .. }
|
|
||||||
| Location::LineRange(ln_begin, _) => Some(ln_begin),
|
|
||||||
Location::Line(lineno) => Some(lineno),
|
Location::Line(lineno) => Some(lineno),
|
||||||
Location::Unknown => None,
|
Location::Unknown => None,
|
||||||
}
|
}
|
||||||
|
@ -490,12 +485,7 @@ pub trait Locational {
|
||||||
|
|
||||||
fn ln_end(&self) -> Option<usize> {
|
fn ln_end(&self) -> Option<usize> {
|
||||||
match self.loc() {
|
match self.loc() {
|
||||||
Location::RangePair {
|
Location::Range { ln_end, .. } | Location::LineRange(_, ln_end) => Some(ln_end),
|
||||||
ln_second: (_, ln_end),
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| Location::Range { ln_end, .. }
|
|
||||||
| Location::LineRange(_, ln_end) => Some(ln_end),
|
|
||||||
Location::Line(lineno) => Some(lineno),
|
Location::Line(lineno) => Some(lineno),
|
||||||
Location::Unknown => None,
|
Location::Unknown => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,8 @@ impl Context {
|
||||||
match subr {
|
match subr {
|
||||||
ConstSubr::User(_user) => todo!(),
|
ConstSubr::User(_user) => todo!(),
|
||||||
ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| {
|
ConstSubr::Builtin(builtin) => builtin.call(args, self).map_err(|mut e| {
|
||||||
e.0.loc = loc;
|
// TODO: Is it possible to get 0?
|
||||||
|
e.0.sub_messages.get_mut(0).unwrap().loc = loc;
|
||||||
EvalErrors::from(EvalError::new(
|
EvalErrors::from(EvalError::new(
|
||||||
*e.0,
|
*e.0,
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
|
|
|
@ -6,29 +6,39 @@ use crate::context::Context;
|
||||||
use crate::ty::constructors::{and, mono};
|
use crate::ty::constructors::{and, mono};
|
||||||
use crate::ty::value::{EvalValueResult, GenTypeObj, TypeObj, ValueObj};
|
use crate::ty::value::{EvalValueResult, GenTypeObj, TypeObj, ValueObj};
|
||||||
use crate::ty::ValueArgs;
|
use crate::ty::ValueArgs;
|
||||||
use erg_common::error::{ErrorCore, ErrorKind, Location};
|
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
|
||||||
use erg_common::style::{RED, RESET, YELLOW};
|
use erg_common::style::{Color, StyledStr, StyledString, THEME};
|
||||||
|
|
||||||
|
const ERR: Color = THEME.colors.error;
|
||||||
|
const WARN: Color = THEME.colors.warning;
|
||||||
|
|
||||||
|
const SUP_ERR: StyledStr = StyledStr::new("Super", Some(ERR), None);
|
||||||
|
const SUP_WARN: StyledStr = StyledStr::new("Super", Some(WARN), None);
|
||||||
|
const CLASS_ERR: StyledStr = StyledStr::new("Class", Some(ERR), None);
|
||||||
|
const REQ_ERR: StyledStr = StyledStr::new("Requirement", Some(ERR), None);
|
||||||
|
const REQ_WARN: StyledStr = StyledStr::new("Requirement", Some(WARN), None);
|
||||||
|
|
||||||
/// Requirement: Type, Impl := Type -> ClassType
|
/// Requirement: Type, Impl := Type -> ClassType
|
||||||
pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||||
let require = args.remove_left_or_key("Requirement").ok_or_else(|| {
|
let require = args.remove_left_or_key("Requirement").ok_or_else(|| {
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("{REQ_ERR} is not passed"),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::TypeError,
|
ErrorKind::TypeError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("{RED}Requirement{RESET} is not passed"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let Some(require) = require.as_type() else {
|
let Some(require) = require.as_type() else {
|
||||||
|
let require = StyledString::new(&format!("{}", require), Some(ERR), None);
|
||||||
return Err(ErrorCore::new(
|
return Err(ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!(
|
||||||
|
"non-type object {require} is passed to {REQ_WARN}",
|
||||||
|
),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::TypeError,
|
ErrorKind::TypeError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!(
|
|
||||||
"non-type object {RED}{require}{RESET} is passed to {YELLOW}Requirement{RESET}",
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
).into());
|
).into());
|
||||||
};
|
};
|
||||||
let impls = args.remove_left_or_key("Impl");
|
let impls = args.remove_left_or_key("Impl");
|
||||||
|
@ -40,23 +50,25 @@ pub fn class_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueOb
|
||||||
/// Super: ClassType, Impl := Type, Additional := Type -> ClassType
|
/// Super: ClassType, Impl := Type, Additional := Type -> ClassType
|
||||||
pub fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
pub fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||||
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
|
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
|
||||||
|
let sup = StyledStr::new("Super", Some(ERR), None);
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("{sup} is not passed"),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::KeyError,
|
ErrorKind::KeyError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("{RED}Super{RESET} is not passed"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let Some(sup) = sup.as_type() else {
|
let Some(sup) = sup.as_type() else {
|
||||||
|
let sup_ty = StyledString::new(&format!("{}", sup), Some(ERR), None);
|
||||||
return Err(ErrorCore::new(
|
return Err(ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!(
|
||||||
|
"non-class object {sup_ty} is passed to {SUP_WARN}",
|
||||||
|
),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::TypeError,
|
ErrorKind::TypeError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!(
|
|
||||||
"non-class object {RED}{sup}{RESET} is passed to {YELLOW}Super{RESET}",
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
).into());
|
).into());
|
||||||
};
|
};
|
||||||
let impls = args.remove_left_or_key("Impl");
|
let impls = args.remove_left_or_key("Impl");
|
||||||
|
@ -74,11 +86,11 @@ pub fn inherit_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<Value
|
||||||
pub fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
|
pub fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||||
let class = args.remove_left_or_key("Class").ok_or_else(|| {
|
let class = args.remove_left_or_key("Class").ok_or_else(|| {
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("{CLASS_ERR} is not passed"),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::KeyError,
|
ErrorKind::KeyError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("{RED}Class{RESET} is not passed"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
match class {
|
match class {
|
||||||
|
@ -106,22 +118,23 @@ pub fn inheritable_func(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult<
|
||||||
pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||||
let require = args.remove_left_or_key("Requirement").ok_or_else(|| {
|
let require = args.remove_left_or_key("Requirement").ok_or_else(|| {
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("{REQ_ERR} is not passed"),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::KeyError,
|
ErrorKind::KeyError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("{RED}Requirement{RESET} is not passed"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let Some(require) = require.as_type() else {
|
let Some(require) = require.as_type() else {
|
||||||
|
let require = StyledString::new(&format!("{}", require), Some(ERR), None);
|
||||||
return Err(ErrorCore::new(
|
return Err(ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!(
|
||||||
|
"non-type object {require} is passed to {REQ_WARN}",
|
||||||
|
),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::TypeError,
|
ErrorKind::TypeError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!(
|
|
||||||
"non-type object {RED}{require}{RESET} is passed to {YELLOW}Requirement{RESET}",
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
).into());
|
).into());
|
||||||
};
|
};
|
||||||
let impls = args.remove_left_or_key("Impl");
|
let impls = args.remove_left_or_key("Impl");
|
||||||
|
@ -134,22 +147,23 @@ pub fn trait_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueOb
|
||||||
pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
pub fn subsume_func(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<ValueObj> {
|
||||||
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
|
let sup = args.remove_left_or_key("Super").ok_or_else(|| {
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("{SUP_ERR} is not passed"),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::KeyError,
|
ErrorKind::KeyError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("{RED}Super{RESET} is not passed"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let Some(sup) = sup.as_type() else {
|
let Some(sup) = sup.as_type() else {
|
||||||
|
let sup = StyledString::new(&format!("{}", sup), Some(ERR), None);
|
||||||
return Err(ErrorCore::new(
|
return Err(ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!(
|
||||||
|
"non-trait object {sup} is passed to {SUP_WARN}",
|
||||||
|
),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::TypeError,
|
ErrorKind::TypeError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!(
|
|
||||||
"non-trait object {RED}{sup}{RESET} is passed to {YELLOW}Super{RESET}",
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
).into());
|
).into());
|
||||||
};
|
};
|
||||||
let impls = args.remove_left_or_key("Impl");
|
let impls = args.remove_left_or_key("Impl");
|
||||||
|
@ -171,16 +185,16 @@ pub fn __array_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<
|
||||||
Ok(v.clone())
|
Ok(v.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorCore::new(
|
Err(ErrorCore::new(
|
||||||
line!() as usize,
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
ErrorKind::IndexError,
|
|
||||||
Location::Unknown,
|
|
||||||
format!(
|
format!(
|
||||||
"[{}] has {} elements, but accessed {}th element",
|
"[{}] has {} elements, but accessed {}th element",
|
||||||
erg_common::fmt_vec(&slf),
|
erg_common::fmt_vec(&slf),
|
||||||
slf.len(),
|
slf.len(),
|
||||||
index
|
index
|
||||||
),
|
),
|
||||||
None,
|
line!() as usize,
|
||||||
|
ErrorKind::IndexError,
|
||||||
|
Location::Unknown,
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
@ -210,11 +224,11 @@ pub fn __dict_getitem__(mut args: ValueArgs, ctx: &Context) -> EvalValueResult<V
|
||||||
Ok(v.clone())
|
Ok(v.clone())
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorCore::new(
|
Err(ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("{slf} has no key {index}"),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::IndexError,
|
ErrorKind::IndexError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("{slf} has no key {index}"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
@ -235,11 +249,11 @@ pub fn __range_getitem__(mut args: ValueArgs, _ctx: &Context) -> EvalValueResult
|
||||||
Ok(ValueObj::Nat(start + index))
|
Ok(ValueObj::Nat(start + index))
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorCore::new(
|
Err(ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
format!("Index out of range: {}", index),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
ErrorKind::IndexError,
|
ErrorKind::IndexError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
format!("Index out of range: {index}"),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use erg_common::config::Input;
|
use erg_common::config::Input;
|
||||||
use erg_common::env::erg_pystd_path;
|
use erg_common::env::erg_pystd_path;
|
||||||
use erg_common::error::{ErrorCore, ErrorKind, Location};
|
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
|
||||||
use erg_common::levenshtein::get_similar_name;
|
use erg_common::levenshtein::get_similar_name;
|
||||||
use erg_common::set::Set;
|
use erg_common::set::Set;
|
||||||
use erg_common::traits::{Locational, Stream};
|
use erg_common::traits::{Locational, Stream};
|
||||||
|
@ -756,13 +756,21 @@ impl Context {
|
||||||
TyCheckErrors::new(
|
TyCheckErrors::new(
|
||||||
errs.into_iter()
|
errs.into_iter()
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
// HACK: dname.loc()はダミーLocationしか返さないので、エラーならop.loc()で上書きする
|
let mut sub_msges = Vec::new();
|
||||||
|
for sub_msg in e.core.sub_messages {
|
||||||
|
sub_msges.push(SubMessage::ambiguous_new(
|
||||||
|
// HACK: dname.loc()はダミーLocationしか返さないので、エラーならop.loc()で上書きする
|
||||||
|
bin.loc(),
|
||||||
|
vec![],
|
||||||
|
sub_msg.get_hint(),
|
||||||
|
));
|
||||||
|
}
|
||||||
let core = ErrorCore::new(
|
let core = ErrorCore::new(
|
||||||
|
sub_msges,
|
||||||
|
e.core.main_message,
|
||||||
e.core.errno,
|
e.core.errno,
|
||||||
e.core.kind,
|
e.core.kind,
|
||||||
bin.loc(),
|
e.core.loc,
|
||||||
e.core.desc,
|
|
||||||
e.core.hint,
|
|
||||||
);
|
);
|
||||||
TyCheckError::new(core, self.cfg.input.clone(), e.caused_by)
|
TyCheckError::new(core, self.cfg.input.clone(), e.caused_by)
|
||||||
})
|
})
|
||||||
|
@ -797,12 +805,20 @@ impl Context {
|
||||||
TyCheckErrors::new(
|
TyCheckErrors::new(
|
||||||
errs.into_iter()
|
errs.into_iter()
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
|
let mut sub_msges = Vec::new();
|
||||||
|
for sub_msg in e.core.sub_messages {
|
||||||
|
sub_msges.push(SubMessage::ambiguous_new(
|
||||||
|
unary.loc(),
|
||||||
|
vec![],
|
||||||
|
sub_msg.get_hint(),
|
||||||
|
));
|
||||||
|
}
|
||||||
let core = ErrorCore::new(
|
let core = ErrorCore::new(
|
||||||
|
sub_msges,
|
||||||
|
e.core.main_message,
|
||||||
e.core.errno,
|
e.core.errno,
|
||||||
e.core.kind,
|
e.core.kind,
|
||||||
unary.loc(),
|
e.core.loc,
|
||||||
e.core.desc,
|
|
||||||
e.core.hint,
|
|
||||||
);
|
);
|
||||||
TyCheckError::new(core, self.cfg.input.clone(), e.caused_by)
|
TyCheckError::new(core, self.cfg.input.clone(), e.caused_by)
|
||||||
})
|
})
|
||||||
|
@ -1052,7 +1068,8 @@ impl Context {
|
||||||
TyCheckError::type_mismatch_error(
|
TyCheckError::type_mismatch_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
e.core.loc,
|
// TODO: Is it possible to get 0?
|
||||||
|
e.core.sub_messages.get(0).unwrap().loc,
|
||||||
e.caused_by,
|
e.caused_by,
|
||||||
&name[..],
|
&name[..],
|
||||||
Some(nth),
|
Some(nth),
|
||||||
|
@ -1107,7 +1124,8 @@ impl Context {
|
||||||
TyCheckError::type_mismatch_error(
|
TyCheckError::type_mismatch_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
e.core.loc,
|
// TODO: Is it possible to get 0?
|
||||||
|
e.core.sub_messages.get(0).unwrap().loc,
|
||||||
e.caused_by,
|
e.caused_by,
|
||||||
&name[..],
|
&name[..],
|
||||||
Some(nth),
|
Some(nth),
|
||||||
|
@ -1164,7 +1182,8 @@ impl Context {
|
||||||
TyCheckError::type_mismatch_error(
|
TyCheckError::type_mismatch_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
e.core.loc,
|
// TODO: Is it possible to get 0?
|
||||||
|
e.core.sub_messages.get(0).unwrap().loc,
|
||||||
e.caused_by,
|
e.caused_by,
|
||||||
&name[..],
|
&name[..],
|
||||||
Some(nth),
|
Some(nth),
|
||||||
|
|
|
@ -416,7 +416,8 @@ impl Context {
|
||||||
TyCheckError::return_type_error(
|
TyCheckError::return_type_error(
|
||||||
self.cfg.input.clone(),
|
self.cfg.input.clone(),
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
e.core.loc,
|
// TODO: is it possible to get 0?
|
||||||
|
e.core.sub_messages.get(0).unwrap().loc,
|
||||||
e.caused_by,
|
e.caused_by,
|
||||||
readable_name(name.inspect()),
|
readable_name(name.inspect()),
|
||||||
spec_ret_t,
|
spec_ret_t,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -191,7 +191,7 @@ impl ASTLowerer {
|
||||||
"traditional_chinese" => "如果您不想使用該值,請使用discard函數",
|
"traditional_chinese" => "如果您不想使用該值,請使用discard函數",
|
||||||
"english" => "if you don't use the value, use discard function",
|
"english" => "if you don't use the value, use discard function",
|
||||||
)
|
)
|
||||||
.into(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -266,7 +266,8 @@ impl ASTLowerer {
|
||||||
"simplified_chinese" => "数组元素必须全部是相同类型",
|
"simplified_chinese" => "数组元素必须全部是相同类型",
|
||||||
"traditional_chinese" => "數組元素必須全部是相同類型",
|
"traditional_chinese" => "數組元素必須全部是相同類型",
|
||||||
"english" => "all elements of an array must be of the same type",
|
"english" => "all elements of an array must be of the same type",
|
||||||
),
|
)
|
||||||
|
.to_owned(),
|
||||||
Some(
|
Some(
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => "Int or Strなど明示的に型を指定してください",
|
"japanese" => "Int or Strなど明示的に型を指定してください",
|
||||||
|
@ -274,7 +275,7 @@ impl ASTLowerer {
|
||||||
"traditional_chinese" => "請明確指定類型,例如: Int or Str",
|
"traditional_chinese" => "請明確指定類型,例如: Int or Str",
|
||||||
"english" => "please specify the type explicitly, e.g. Int or Str",
|
"english" => "please specify the type explicitly, e.g. Int or Str",
|
||||||
)
|
)
|
||||||
.into(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -412,7 +413,8 @@ impl ASTLowerer {
|
||||||
"simplified_chinese" => "集合元素必须全部是相同类型",
|
"simplified_chinese" => "集合元素必须全部是相同类型",
|
||||||
"traditional_chinese" => "集合元素必須全部是相同類型",
|
"traditional_chinese" => "集合元素必須全部是相同類型",
|
||||||
"english" => "all elements of a set must be of the same type",
|
"english" => "all elements of a set must be of the same type",
|
||||||
),
|
)
|
||||||
|
.to_owned(),
|
||||||
Some(
|
Some(
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => "Int or Strなど明示的に型を指定してください",
|
"japanese" => "Int or Strなど明示的に型を指定してください",
|
||||||
|
@ -420,7 +422,7 @@ impl ASTLowerer {
|
||||||
"traditional_chinese" => "明確指定類型,例如: Int or Str",
|
"traditional_chinese" => "明確指定類型,例如: Int or Str",
|
||||||
"english" => "please specify the type explicitly, e.g. Int or Str",
|
"english" => "please specify the type explicitly, e.g. Int or Str",
|
||||||
)
|
)
|
||||||
.into(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -538,7 +540,8 @@ impl ASTLowerer {
|
||||||
"simplified_chinese" => "Dict的值必须是同一类型",
|
"simplified_chinese" => "Dict的值必须是同一类型",
|
||||||
"traditional_chinese" => "Dict的值必須是同一類型",
|
"traditional_chinese" => "Dict的值必須是同一類型",
|
||||||
"english" => "Values of Dict must be the same type",
|
"english" => "Values of Dict must be the same type",
|
||||||
),
|
)
|
||||||
|
.to_owned(),
|
||||||
Some(
|
Some(
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => "Int or Strなど明示的に型を指定してください",
|
"japanese" => "Int or Strなど明示的に型を指定してください",
|
||||||
|
@ -546,7 +549,7 @@ impl ASTLowerer {
|
||||||
"traditional_chinese" => "明確指定類型,例如: Int or Str",
|
"traditional_chinese" => "明確指定類型,例如: Int or Str",
|
||||||
"english" => "please specify the type explicitly, e.g. Int or Str",
|
"english" => "please specify the type explicitly, e.g. Int or Str",
|
||||||
)
|
)
|
||||||
.into(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -692,7 +695,7 @@ impl ASTLowerer {
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
call.args.loc(),
|
call.args.loc(),
|
||||||
self.ctx.caused_by(),
|
self.ctx.caused_by(),
|
||||||
"invalid assert casting type",
|
"invalid assert casting type".to_owned(),
|
||||||
None,
|
None,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -751,7 +754,7 @@ impl ASTLowerer {
|
||||||
line!() as usize,
|
line!() as usize,
|
||||||
other.loc(),
|
other.loc(),
|
||||||
self.ctx.caused_by(),
|
self.ctx.caused_by(),
|
||||||
"",
|
"".to_owned(),
|
||||||
None,
|
None,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::string::FromUtf8Error;
|
||||||
use erg_common::cache::CacheSet;
|
use erg_common::cache::CacheSet;
|
||||||
use erg_common::config::{ErgConfig, Input};
|
use erg_common::config::{ErgConfig, Input};
|
||||||
use erg_common::dict::Dict;
|
use erg_common::dict::Dict;
|
||||||
use erg_common::error::{ErrorCore, ErrorKind, Location};
|
use erg_common::error::{ErrorCore, ErrorKind, Location, SubMessage};
|
||||||
use erg_common::python_util::PythonVersion;
|
use erg_common::python_util::PythonVersion;
|
||||||
use erg_common::serialize::DataTypePrefix;
|
use erg_common::serialize::DataTypePrefix;
|
||||||
use erg_common::{fn_name, switch_lang};
|
use erg_common::{fn_name, switch_lang};
|
||||||
|
@ -39,11 +39,11 @@ impl From<FromUtf8Error> for DeserializeError {
|
||||||
impl From<DeserializeError> for ErrorCore {
|
impl From<DeserializeError> for ErrorCore {
|
||||||
fn from(err: DeserializeError) -> Self {
|
fn from(err: DeserializeError) -> Self {
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
|
vec![SubMessage::only_loc(Location::Unknown)],
|
||||||
|
err.desc,
|
||||||
err.errno,
|
err.errno,
|
||||||
ErrorKind::ImportError,
|
ErrorKind::ImportError,
|
||||||
Location::Unknown,
|
Location::Unknown,
|
||||||
err.desc,
|
|
||||||
Option::<String>::None,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use erg_common::config::Input;
|
use erg_common::config::Input;
|
||||||
use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay};
|
use erg_common::error::{
|
||||||
use erg_common::style::{Attribute, Color, StyledStr, StyledString, Theme};
|
ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage,
|
||||||
|
};
|
||||||
|
use erg_common::style::{Attribute, Color, StyledStr, StyledString, THEME};
|
||||||
use erg_common::traits::Stream;
|
use erg_common::traits::Stream;
|
||||||
use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang};
|
use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang};
|
||||||
|
|
||||||
|
@ -29,62 +31,68 @@ pub struct LexErrors(Vec<LexError>);
|
||||||
|
|
||||||
impl_stream_for_wrapper!(LexErrors, LexError);
|
impl_stream_for_wrapper!(LexErrors, LexError);
|
||||||
|
|
||||||
|
const ERR: Color = THEME.colors.error;
|
||||||
|
const HINT: Color = THEME.colors.hint;
|
||||||
|
const ACCENT: Color = THEME.colors.accent;
|
||||||
|
|
||||||
impl LexError {
|
impl LexError {
|
||||||
pub fn new(core: ErrorCore) -> Self {
|
pub fn new(core: ErrorCore) -> Self {
|
||||||
Self(Box::new(core))
|
Self(Box::new(core))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_hint<S: Into<String>>(&mut self, hint: S) {
|
pub fn set_hint<S: Into<String>>(&mut self, hint: S) {
|
||||||
self.0.hint = Some(hint.into());
|
if let Some(sub_msg) = self.0.sub_messages.get_mut(0) {
|
||||||
|
sub_msg.set_hint(hint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
|
pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
|
||||||
const URL: StyledStr = StyledStr::new(
|
const URL: StyledStr = StyledStr::new(
|
||||||
"https://github.com/erg-lang/erg",
|
"https://github.com/erg-lang/erg",
|
||||||
Some(Color::White),
|
Some(ACCENT),
|
||||||
Some(Attribute::Underline),
|
Some(Attribute::Underline),
|
||||||
);
|
);
|
||||||
Self::new(ErrorCore::new(
|
Self::new(ErrorCore::new(
|
||||||
errno,
|
vec![SubMessage::only_loc(loc)],
|
||||||
CompilerSystemError,
|
|
||||||
loc,
|
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n{fn_name}:{line}より発生"),
|
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい ({URL})\n{fn_name}:{line}より発生"),
|
||||||
"simplified_chinese" => format!("这是Erg编译器的一个错误,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
"simplified_chinese" => format!("这是Erg编译器的一个错误,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||||
"traditional_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}"),
|
"english" => format!("this is a bug of the Erg compiler, please report it to {URL}\ncaused from: {fn_name}:{line}"),
|
||||||
),
|
),
|
||||||
None,
|
errno,
|
||||||
|
CompilerSystemError,
|
||||||
|
loc,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn feature_error(errno: usize, loc: Location, name: &str) -> Self {
|
pub fn feature_error(errno: usize, loc: Location, name: &str) -> Self {
|
||||||
Self::new(ErrorCore::new(
|
Self::new(ErrorCore::new(
|
||||||
errno,
|
vec![SubMessage::only_loc(loc)],
|
||||||
FeatureError,
|
|
||||||
loc,
|
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
|
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
|
||||||
"simplified_chinese" => format!("此功能({name})尚未实现"),
|
"simplified_chinese" => format!("此功能({name})尚未实现"),
|
||||||
"traditional_chinese" => format!("此功能({name})尚未實現"),
|
"traditional_chinese" => format!("此功能({name})尚未實現"),
|
||||||
"english" => format!("this feature({name}) is not implemented yet"),
|
"english" => format!("this feature({name}) is not implemented yet"),
|
||||||
),
|
),
|
||||||
None,
|
errno,
|
||||||
|
FeatureError,
|
||||||
|
loc,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_syntax_error(errno: usize, loc: Location) -> Self {
|
pub fn simple_syntax_error(errno: usize, loc: Location) -> Self {
|
||||||
Self::new(ErrorCore::new(
|
Self::new(ErrorCore::new(
|
||||||
errno,
|
vec![SubMessage::only_loc(loc)],
|
||||||
SyntaxError,
|
|
||||||
loc,
|
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => "不正な構文です",
|
"japanese" => "不正な構文です",
|
||||||
"simplified_chinese" => "无效的语法",
|
"simplified_chinese" => "无效的语法",
|
||||||
"traditional_chinese" => "無效的語法",
|
"traditional_chinese" => "無效的語法",
|
||||||
"english" => "invalid syntax",
|
"english" => "invalid syntax",
|
||||||
),
|
),
|
||||||
None,
|
errno,
|
||||||
|
SyntaxError,
|
||||||
|
loc,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +102,13 @@ impl LexError {
|
||||||
desc: S,
|
desc: S,
|
||||||
hint: Option<String>,
|
hint: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(ErrorCore::new(errno, SyntaxError, loc, desc, hint))
|
Self::new(ErrorCore::new(
|
||||||
|
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
||||||
|
desc,
|
||||||
|
errno,
|
||||||
|
SyntaxError,
|
||||||
|
loc,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syntax_warning<S: Into<String>>(
|
pub fn syntax_warning<S: Into<String>>(
|
||||||
|
@ -103,7 +117,13 @@ impl LexError {
|
||||||
desc: S,
|
desc: S,
|
||||||
hint: Option<String>,
|
hint: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(ErrorCore::new(errno, SyntaxWarning, loc, desc, hint))
|
Self::new(ErrorCore::new(
|
||||||
|
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
||||||
|
desc,
|
||||||
|
errno,
|
||||||
|
SyntaxWarning,
|
||||||
|
loc,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn no_var_error(
|
pub fn no_var_error(
|
||||||
|
@ -113,6 +133,7 @@ impl LexError {
|
||||||
similar_name: Option<String>,
|
similar_name: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let hint = similar_name.map(|n| {
|
let hint = similar_name.map(|n| {
|
||||||
|
let n = StyledString::new(&n, Some(HINT), Some(Attribute::Bold));
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => format!("似た名前の変数があります: {n}"),
|
"japanese" => format!("似た名前の変数があります: {n}"),
|
||||||
"simplified_chinese" => format!("存在相同名称变量: {n}"),
|
"simplified_chinese" => format!("存在相同名称变量: {n}"),
|
||||||
|
@ -120,18 +141,18 @@ impl LexError {
|
||||||
"english" => format!("exists a similar name variable: {n}"),
|
"english" => format!("exists a similar name variable: {n}"),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let name = StyledString::new(name, Some(Color::Red), Some(Attribute::Underline));
|
let name = StyledString::new(name, Some(ERR), Some(Attribute::Underline));
|
||||||
Self::new(ErrorCore::new(
|
Self::new(ErrorCore::new(
|
||||||
errno,
|
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
||||||
NameError,
|
|
||||||
loc,
|
|
||||||
switch_lang!(
|
switch_lang!(
|
||||||
"japanese" => format!("{name}という変数は定義されていません"),
|
"japanese" => format!("{name}という変数は定義されていません"),
|
||||||
"simplified_chinese" => format!("{name}未定义"),
|
"simplified_chinese" => format!("{name}未定义"),
|
||||||
"traditional_chinese" => format!("{name}未定義"),
|
"traditional_chinese" => format!("{name}未定義"),
|
||||||
"english" => format!("{name} is not defined"),
|
"english" => format!("{name} is not defined"),
|
||||||
),
|
),
|
||||||
hint,
|
errno,
|
||||||
|
NameError,
|
||||||
|
loc,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +185,6 @@ pub type DesugaringResult<T> = Result<T, DesugaringError>;
|
||||||
pub struct ParserRunnerError {
|
pub struct ParserRunnerError {
|
||||||
pub core: ErrorCore,
|
pub core: ErrorCore,
|
||||||
pub input: Input,
|
pub input: Input,
|
||||||
pub theme: Theme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_display_and_error!(ParserRunnerError);
|
impl_display_and_error!(ParserRunnerError);
|
||||||
|
@ -176,9 +196,6 @@ impl ErrorDisplay for ParserRunnerError {
|
||||||
fn input(&self) -> &Input {
|
fn input(&self) -> &Input {
|
||||||
&self.input
|
&self.input
|
||||||
}
|
}
|
||||||
fn theme(&self) -> &Theme {
|
|
||||||
&self.theme
|
|
||||||
}
|
|
||||||
fn caused_by(&self) -> &str {
|
fn caused_by(&self) -> &str {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
@ -188,8 +205,8 @@ impl ErrorDisplay for ParserRunnerError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserRunnerError {
|
impl ParserRunnerError {
|
||||||
pub const fn new(core: ErrorCore, input: Input, theme: Theme) -> Self {
|
pub const fn new(core: ErrorCore, input: Input) -> Self {
|
||||||
Self { core, input, theme }
|
Self { core, input }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,10 +226,10 @@ impl fmt::Display for ParserRunnerErrors {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserRunnerErrors {
|
impl ParserRunnerErrors {
|
||||||
pub fn convert(input: &Input, errs: ParseErrors, theme: Theme) -> Self {
|
pub fn convert(input: &Input, errs: ParseErrors) -> Self {
|
||||||
Self(
|
Self(
|
||||||
errs.into_iter()
|
errs.into_iter()
|
||||||
.map(|err| ParserRunnerError::new(*err.0, input.clone(), theme))
|
.map(|err| ParserRunnerError::new(*err.0, input.clone()))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::cmp::Ordering;
|
||||||
use erg_common::cache::CacheSet;
|
use erg_common::cache::CacheSet;
|
||||||
use erg_common::config::ErgConfig;
|
use erg_common::config::ErgConfig;
|
||||||
use erg_common::config::Input;
|
use erg_common::config::Input;
|
||||||
use erg_common::style::THEME;
|
|
||||||
use erg_common::traits::{Locational, Runnable, Stream};
|
use erg_common::traits::{Locational, Runnable, Stream};
|
||||||
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};
|
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};
|
||||||
|
|
||||||
|
@ -43,7 +42,7 @@ impl Runnable for LexerRunner {
|
||||||
let lexer = Lexer::from_str(self.input().read());
|
let lexer = Lexer::from_str(self.input().read());
|
||||||
let ts = lexer
|
let ts = lexer
|
||||||
.lex()
|
.lex()
|
||||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?;
|
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?;
|
||||||
println!("{ts}");
|
println!("{ts}");
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
@ -53,13 +52,13 @@ impl Runnable for LexerRunner {
|
||||||
if cfg!(feature = "debug") {
|
if cfg!(feature = "debug") {
|
||||||
let ts = lexer
|
let ts = lexer
|
||||||
.lex()
|
.lex()
|
||||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?;
|
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?;
|
||||||
println!("{ts}");
|
println!("{ts}");
|
||||||
Ok(ts.to_string())
|
Ok(ts.to_string())
|
||||||
} else {
|
} else {
|
||||||
Ok(lexer
|
Ok(lexer
|
||||||
.lex()
|
.lex()
|
||||||
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs, THEME))?
|
.map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?
|
||||||
.to_string())
|
.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ use erg_common::error::Location;
|
||||||
use erg_common::option_enum_unwrap;
|
use erg_common::option_enum_unwrap;
|
||||||
use erg_common::set::Set as HashSet;
|
use erg_common::set::Set as HashSet;
|
||||||
use erg_common::str::Str;
|
use erg_common::str::Str;
|
||||||
use erg_common::style::THEME;
|
|
||||||
use erg_common::traits::Runnable;
|
use erg_common::traits::Runnable;
|
||||||
use erg_common::traits::{Locational, Stream};
|
use erg_common::traits::{Locational, Stream};
|
||||||
use erg_common::{
|
use erg_common::{
|
||||||
|
@ -207,16 +206,16 @@ impl ParserRunner {
|
||||||
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> {
|
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> {
|
||||||
Parser::new(ts)
|
Parser::new(ts)
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))
|
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> {
|
pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> {
|
||||||
let ts = Lexer::new(Input::Str(src))
|
let ts = Lexer::new(Input::Str(src))
|
||||||
.lex()
|
.lex()
|
||||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))?;
|
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?;
|
||||||
Parser::new(ts)
|
Parser::new(ts)
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs, THEME))
|
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use erg_common::config::{ErgConfig, Input};
|
use erg_common::config::{ErgConfig, Input};
|
||||||
use erg_common::error::MultiErrorDisplay;
|
use erg_common::error::MultiErrorDisplay;
|
||||||
use erg_common::style::THEME;
|
|
||||||
use erg_common::traits::Runnable;
|
use erg_common::traits::Runnable;
|
||||||
|
|
||||||
use erg_parser::error::ParserRunnerErrors;
|
use erg_parser::error::ParserRunnerErrors;
|
||||||
|
@ -64,7 +63,7 @@ fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerError
|
||||||
match parser.parse_token_stream(
|
match parser.parse_token_stream(
|
||||||
lexer
|
lexer
|
||||||
.lex()
|
.lex()
|
||||||
.map_err(|errs| ParserRunnerErrors::convert(&input, errs, THEME))?,
|
.map_err(|errs| ParserRunnerErrors::convert(&input, errs))?,
|
||||||
) {
|
) {
|
||||||
Ok(module) => {
|
Ok(module) => {
|
||||||
println!("{module}");
|
println!("{module}");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue