mirror of
				https://github.com/Dummi26/mers.git
				synced 2025-10-31 11:46:15 +01:00 
			
		
		
		
	improved error messages
- some small bugs are now fixed - include comments in error messages (if this causes issues, use --hide-comments) - colors should make more sense now - error-related things moved to mers_lib/src/errors/
This commit is contained in:
		
							parent
							
								
									c39e784939
								
							
						
					
					
						commit
						12925fed67
					
				| @ -14,6 +14,9 @@ struct Args { | |||||||
|     /// perform checks to avoid runtime crashes
 |     /// perform checks to avoid runtime crashes
 | ||||||
|     #[arg(long, default_value_t = Check::Yes)] |     #[arg(long, default_value_t = Check::Yes)] | ||||||
|     check: Check, |     check: Check, | ||||||
|  |     /// in error messages, hide comments and only show actual code
 | ||||||
|  |     #[arg(long)] | ||||||
|  |     hide_comments: bool, | ||||||
| } | } | ||||||
| #[derive(Subcommand)] | #[derive(Subcommand)] | ||||||
| enum Command { | enum Command { | ||||||
| @ -81,7 +84,7 @@ fn main() { | |||||||
|     let parsed = match parse(&mut source) { |     let parsed = match parse(&mut source) { | ||||||
|         Ok(v) => v, |         Ok(v) => v, | ||||||
|         Err(e) => { |         Err(e) => { | ||||||
|             eprintln!("{}", e.display(&source)); |             eprintln!("{}", e.display(&source).show_comments(!args.hide_comments)); | ||||||
|             exit(20); |             exit(20); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| @ -90,7 +93,7 @@ fn main() { | |||||||
|     let run = match parsed.compile(&mut info_parsed, Default::default()) { |     let run = match parsed.compile(&mut info_parsed, Default::default()) { | ||||||
|         Ok(v) => v, |         Ok(v) => v, | ||||||
|         Err(e) => { |         Err(e) => { | ||||||
|             eprintln!("{}", e.display(&source)); |             eprintln!("{}", e.display(&source).show_comments(!args.hide_comments)); | ||||||
|             exit(24); |             exit(24); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| @ -104,7 +107,7 @@ fn main() { | |||||||
|             let return_type = match run.check(&mut info_check, None) { |             let return_type = match run.check(&mut info_check, None) { | ||||||
|                 Ok(v) => v, |                 Ok(v) => v, | ||||||
|                 Err(e) => { |                 Err(e) => { | ||||||
|                     eprint!("{}", e.display(&source)); |                     eprint!("{}", e.display(&source).show_comments(!args.hide_comments)); | ||||||
|                     exit(28); |                     exit(28); | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|  | |||||||
| @ -4,7 +4,10 @@ use std::{ | |||||||
|     sync::{Arc, Mutex}, |     sync::{Arc, Mutex}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use crate::program::run::{CheckError, CheckInfo, Info}; | use crate::{ | ||||||
|  |     errors::CheckError, | ||||||
|  |     program::run::{CheckInfo, Info}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{Data, MersData, MersType, Type}; | use super::{Data, MersData, MersType, Type}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										308
									
								
								mers_lib/src/errors/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								mers_lib/src/errors/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,308 @@ | |||||||
|  | use std::fmt::Display; | ||||||
|  | 
 | ||||||
|  | use colored::Colorize; | ||||||
|  | use line_span::LineSpans; | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "parse")] | ||||||
|  | use crate::parsing::Source; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy, Debug)] | ||||||
|  | pub struct SourcePos(pub(crate) usize); | ||||||
|  | impl SourcePos { | ||||||
|  |     pub fn pos(&self) -> usize { | ||||||
|  |         self.0 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Copy, Debug)] | ||||||
|  | pub struct SourceRange { | ||||||
|  |     start: SourcePos, | ||||||
|  |     end: SourcePos, | ||||||
|  | } | ||||||
|  | impl From<(SourcePos, SourcePos)> for SourceRange { | ||||||
|  |     fn from(value: (SourcePos, SourcePos)) -> Self { | ||||||
|  |         SourceRange { | ||||||
|  |             start: value.0, | ||||||
|  |             end: value.1, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl SourceRange { | ||||||
|  |     pub fn start(&self) -> SourcePos { | ||||||
|  |         self.start | ||||||
|  |     } | ||||||
|  |     pub fn end(&self) -> SourcePos { | ||||||
|  |         self.end | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub struct CheckError(Vec<CheckErrorComponent>); | ||||||
|  | #[allow(non_upper_case_globals)] | ||||||
|  | pub mod error_colors { | ||||||
|  |     use colored::Color; | ||||||
|  | 
 | ||||||
|  |     pub const UnknownVariable: Color = Color::Red; | ||||||
|  | 
 | ||||||
|  |     pub const WhitespaceAfterHashtag: Color = Color::Red; | ||||||
|  |     pub const HashUnknown: Color = Color::Red; | ||||||
|  |     pub const HashIncludeCantLoadFile: Color = Color::Red; | ||||||
|  |     pub const HashIncludeNotAString: Color = Color::Red; | ||||||
|  |     pub const HashIncludeErrorInIncludedFile: Color = Color::Red; | ||||||
|  | 
 | ||||||
|  |     pub const BackslashEscapeUnknown: Color = Color::Red; | ||||||
|  |     pub const BackslashEscapeEOF: Color = Color::Red; | ||||||
|  |     pub const StringEOF: Color = Color::Red; | ||||||
|  | 
 | ||||||
|  |     pub const IfConditionNotBool: Color = Color::Red; | ||||||
|  |     pub const ChainWithNonFunction: Color = Color::Yellow; | ||||||
|  | 
 | ||||||
|  |     pub const Function: Color = Color::BrightMagenta; | ||||||
|  |     pub const FunctionArgument: Color = Color::BrightBlue; | ||||||
|  | 
 | ||||||
|  |     pub const InitFrom: Color = Color::BrightCyan; | ||||||
|  |     pub const InitTo: Color = Color::Green; | ||||||
|  |     pub const AssignFrom: Color = InitFrom; | ||||||
|  |     pub const AssignTo: Color = InitTo; | ||||||
|  |     pub const AssignTargetNonReference: Color = Color::BrightYellow; | ||||||
|  | } | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | enum CheckErrorComponent { | ||||||
|  |     Message(String), | ||||||
|  |     Error(CheckError), | ||||||
|  |     Source(Vec<(SourceRange, Option<colored::Color>)>), | ||||||
|  | } | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct CheckErrorHRConfig { | ||||||
|  |     indent_start: String, | ||||||
|  |     indent_default: String, | ||||||
|  |     indent_end: String, | ||||||
|  |     /// if true, shows "original" source code, if false, shows source with comments removed (this is what the parser uses internally)
 | ||||||
|  |     show_comments: bool, | ||||||
|  | } | ||||||
|  | #[cfg(feature = "parse")] | ||||||
|  | pub struct CheckErrorDisplay<'a> { | ||||||
|  |     e: &'a CheckError, | ||||||
|  |     src: Option<&'a Source>, | ||||||
|  |     pub show_comments: bool, | ||||||
|  | } | ||||||
|  | #[cfg(feature = "parse")] | ||||||
|  | impl<'a> CheckErrorDisplay<'a> { | ||||||
|  |     pub fn show_comments(mut self, show_comments: bool) -> Self { | ||||||
|  |         self.show_comments = show_comments; | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #[cfg(feature = "parse")] | ||||||
|  | impl Display for CheckErrorDisplay<'_> { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|  |         self.e.human_readable( | ||||||
|  |             f, | ||||||
|  |             self.src, | ||||||
|  |             &CheckErrorHRConfig { | ||||||
|  |                 indent_start: String::new(), | ||||||
|  |                 indent_default: String::new(), | ||||||
|  |                 indent_end: String::new(), | ||||||
|  |                 show_comments: self.show_comments, | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl CheckError { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         CheckError(vec![]) | ||||||
|  |     } | ||||||
|  |     fn add(mut self, v: CheckErrorComponent) -> Self { | ||||||
|  |         self.0.push(v); | ||||||
|  |         self | ||||||
|  |     } | ||||||
|  |     pub(crate) fn msg(self, s: String) -> Self { | ||||||
|  |         self.add(CheckErrorComponent::Message(s)) | ||||||
|  |     } | ||||||
|  |     pub(crate) fn err(self, e: Self) -> Self { | ||||||
|  |         self.add(CheckErrorComponent::Error(e)) | ||||||
|  |     } | ||||||
|  |     pub(crate) fn src(self, s: Vec<(SourceRange, Option<colored::Color>)>) -> Self { | ||||||
|  |         self.add(CheckErrorComponent::Source(s)) | ||||||
|  |     } | ||||||
|  |     #[cfg(feature = "parse")] | ||||||
|  |     pub fn display<'a>(&'a self, src: &'a Source) -> CheckErrorDisplay<'a> { | ||||||
|  |         CheckErrorDisplay { | ||||||
|  |             e: self, | ||||||
|  |             src: Some(src), | ||||||
|  |             show_comments: true, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     #[cfg(feature = "parse")] | ||||||
|  |     pub fn display_no_src<'a>(&'a self) -> CheckErrorDisplay<'a> { | ||||||
|  |         CheckErrorDisplay { | ||||||
|  |             e: self, | ||||||
|  |             src: None, | ||||||
|  |             show_comments: true, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// will, unless empty, end in a newline
 | ||||||
|  |     #[cfg(feature = "parse")] | ||||||
|  |     fn human_readable( | ||||||
|  |         &self, | ||||||
|  |         f: &mut std::fmt::Formatter<'_>, | ||||||
|  |         src: Option<&Source>, | ||||||
|  |         cfg: &CheckErrorHRConfig, | ||||||
|  |     ) -> std::fmt::Result { | ||||||
|  |         let len = self.0.len(); | ||||||
|  |         for (i, component) in self.0.iter().enumerate() { | ||||||
|  |             macro_rules! indent { | ||||||
|  |                 () => { | ||||||
|  |                     if i + 1 == len { | ||||||
|  |                         &cfg.indent_end | ||||||
|  |                     } else if i == 0 { | ||||||
|  |                         &cfg.indent_start | ||||||
|  |                     } else { | ||||||
|  |                         &cfg.indent_default | ||||||
|  |                     } | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |             match component { | ||||||
|  |                 CheckErrorComponent::Message(msg) => writeln!(f, "{}{msg}", indent!())?, | ||||||
|  |                 CheckErrorComponent::Error(err) => { | ||||||
|  |                     let mut cfg = cfg.clone(); | ||||||
|  |                     cfg.indent_start.push_str("│"); | ||||||
|  |                     cfg.indent_default.push_str("│"); | ||||||
|  |                     cfg.indent_end.push_str("└"); | ||||||
|  |                     err.human_readable(f, src, &cfg)?; | ||||||
|  |                 } | ||||||
|  |                 CheckErrorComponent::Source(highlights) => { | ||||||
|  |                     if let Some(src) = src { | ||||||
|  |                         let start = highlights.iter().map(|v| v.0.start.pos()).min(); | ||||||
|  |                         let end = highlights.iter().map(|v| v.0.end.pos()).max(); | ||||||
|  |                         if let (Some(start_in_line), Some(end_in_line)) = (start, end) { | ||||||
|  |                             let start = src.get_line_start(start_in_line); | ||||||
|  |                             let end = src.get_line_end(end_in_line); | ||||||
|  |                             let (start_with_comments, end_with_comments) = ( | ||||||
|  |                                 src.pos_in_og(start_in_line, true), | ||||||
|  |                                 src.pos_in_og(end_in_line, false), | ||||||
|  |                             ); | ||||||
|  |                             let (mut start, mut end) = if cfg.show_comments { | ||||||
|  |                                 (src.pos_in_og(start, true), src.pos_in_og(end, false)) | ||||||
|  |                             } else { | ||||||
|  |                                 (start, end) | ||||||
|  |                             }; | ||||||
|  |                             let mut first_line_start = 0; | ||||||
|  |                             let first_line_nr = src | ||||||
|  |                                 .src_og() | ||||||
|  |                                 .line_spans() | ||||||
|  |                                 .take_while(|l| { | ||||||
|  |                                     if l.start() <= start_with_comments { | ||||||
|  |                                         first_line_start = l.start(); | ||||||
|  |                                         true | ||||||
|  |                                     } else { | ||||||
|  |                                         false | ||||||
|  |                                     } | ||||||
|  |                                 }) | ||||||
|  |                                 .count(); | ||||||
|  |                             if cfg.show_comments && first_line_start < start { | ||||||
|  |                                 start = first_line_start; | ||||||
|  |                             } | ||||||
|  |                             let mut last_line_start = 0; | ||||||
|  |                             let last_line_nr = src | ||||||
|  |                                 .src_og() | ||||||
|  |                                 .line_spans() | ||||||
|  |                                 .take_while(|l| { | ||||||
|  |                                     if l.start() <= end_with_comments { | ||||||
|  |                                         last_line_start = l.start(); | ||||||
|  |                                         if cfg.show_comments && l.end() > end { | ||||||
|  |                                             end = l.end(); | ||||||
|  |                                         } | ||||||
|  |                                         true | ||||||
|  |                                     } else { | ||||||
|  |                                         false | ||||||
|  |                                     } | ||||||
|  |                                 }) | ||||||
|  |                                 .count(); | ||||||
|  |                             if first_line_nr == last_line_nr { | ||||||
|  |                                 writeln!( | ||||||
|  |                                     f, | ||||||
|  |                                     "{}Line {first_line_nr} ({}..{})", | ||||||
|  |                                     indent!(), | ||||||
|  |                                     start_with_comments + 1 - first_line_start, | ||||||
|  |                                     end_with_comments - last_line_start, | ||||||
|  |                                 )?; | ||||||
|  |                             } else { | ||||||
|  |                                 writeln!( | ||||||
|  |                                     f, | ||||||
|  |                                     "{}Lines {first_line_nr}-{last_line_nr} ({}..{})", | ||||||
|  |                                     indent!(), | ||||||
|  |                                     start_with_comments + 1 - first_line_start, | ||||||
|  |                                     end_with_comments - last_line_start, | ||||||
|  |                                 )?; | ||||||
|  |                             } | ||||||
|  |                             let lines = if cfg.show_comments { | ||||||
|  |                                 src.src_og()[start..end].line_spans().collect::<Vec<_>>() | ||||||
|  |                             } else { | ||||||
|  |                                 src.src()[start..end].line_spans().collect::<Vec<_>>() | ||||||
|  |                             }; | ||||||
|  |                             for line in lines { | ||||||
|  |                                 let line_start = line.start(); | ||||||
|  |                                 let line_end = line.end(); | ||||||
|  |                                 let line = line.as_str(); | ||||||
|  |                                 writeln!(f, "{} {line}", indent!())?; | ||||||
|  |                                 let mut right = 0; | ||||||
|  |                                 for (pos, color) in highlights { | ||||||
|  |                                     if let Some(color) = color { | ||||||
|  |                                         let (highlight_start, highlight_end) = if cfg.show_comments | ||||||
|  |                                         { | ||||||
|  |                                             ( | ||||||
|  |                                                 src.pos_in_og(pos.start.pos(), true), | ||||||
|  |                                                 src.pos_in_og(pos.end.pos(), false), | ||||||
|  |                                             ) | ||||||
|  |                                         } else { | ||||||
|  |                                             (pos.start.pos(), pos.end.pos()) | ||||||
|  |                                         }; | ||||||
|  |                                         let highlight_start = highlight_start - start; | ||||||
|  |                                         let highlight_end = highlight_end - start; | ||||||
|  |                                         if highlight_start < line_end && highlight_end > line_start | ||||||
|  |                                         { | ||||||
|  |                                             // where the highlight starts in this line
 | ||||||
|  |                                             let hl_start = | ||||||
|  |                                                 highlight_start.saturating_sub(line_start); | ||||||
|  |                                             // highlight would be further left than cursor, so we need a new line
 | ||||||
|  |                                             if hl_start < right { | ||||||
|  |                                                 right = 0; | ||||||
|  |                                                 writeln!(f)?; | ||||||
|  |                                             } | ||||||
|  |                                             // length of the highlight
 | ||||||
|  |                                             let hl_len = highlight_end | ||||||
|  |                                                 .saturating_sub(line_start) | ||||||
|  |                                                 .saturating_sub(hl_start); | ||||||
|  |                                             let hl_space = hl_start - right; | ||||||
|  |                                             let print_indent = right == 0; | ||||||
|  |                                             let hl_len = hl_len.min(line.len() - right); | ||||||
|  |                                             right += hl_space + hl_len; | ||||||
|  |                                             if print_indent && right != 0 { | ||||||
|  |                                                 write!(f, "{} ", indent!())?; | ||||||
|  |                                             } | ||||||
|  |                                             write!( | ||||||
|  |                                                 f, | ||||||
|  |                                                 "{}{}", | ||||||
|  |                                                 " ".repeat(hl_space), | ||||||
|  |                                                 "~".repeat(hl_len).color(*color) | ||||||
|  |                                             )?; | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                                 if right != 0 { | ||||||
|  |                                     writeln!(f)?; | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl From<String> for CheckError { | ||||||
|  |     fn from(value: String) -> Self { | ||||||
|  |         Self::new().msg(value) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,5 +1,7 @@ | |||||||
| /// data and types in mers
 | /// data and types in mers
 | ||||||
| pub mod data; | pub mod data; | ||||||
|  | /// struct to represent errors the user may face
 | ||||||
|  | pub mod errors; | ||||||
| /// shared code handling scopes to guarantee that compiler and runtime scopes match
 | /// shared code handling scopes to guarantee that compiler and runtime scopes match
 | ||||||
| pub mod info; | pub mod info; | ||||||
| /// parser implementation.
 | /// parser implementation.
 | ||||||
|  | |||||||
| @ -1 +0,0 @@ | |||||||
| 
 |  | ||||||
| @ -1,8 +1,10 @@ | |||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| use crate::program::{self, parsed::block::Block, run::CheckError}; | use crate::{ | ||||||
|  |     errors::{CheckError, SourcePos}, | ||||||
|  |     program::{self, parsed::block::Block}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| pub mod errors; |  | ||||||
| pub mod statements; | pub mod statements; | ||||||
| pub mod types; | pub mod types; | ||||||
| 
 | 
 | ||||||
| @ -20,7 +22,7 @@ pub struct Source { | |||||||
|     src_raw_len: usize, |     src_raw_len: usize, | ||||||
|     src_og: String, |     src_og: String, | ||||||
|     src: String, |     src: String, | ||||||
|     /// (start, content) of each comment, including start/end (//, \n, /* and */)
 |     /// (start, content) of each comment, including start/end (//, /* and */), but NOT newline after //
 | ||||||
|     comments: Vec<(usize, String)>, |     comments: Vec<(usize, String)>, | ||||||
|     i: usize, |     i: usize, | ||||||
|     sections: Vec<SectionMarker>, |     sections: Vec<SectionMarker>, | ||||||
| @ -36,13 +38,15 @@ impl Source { | |||||||
|             if let Some((i, ch)) = chars.next() { |             if let Some((i, ch)) = chars.next() { | ||||||
|                 match in_comment { |                 match in_comment { | ||||||
|                     Some(false) => { |                     Some(false) => { | ||||||
|                         comment.1.push(ch); |  | ||||||
|                         if ch == '\n' { |                         if ch == '\n' { | ||||||
|  |                             src.push('\n'); | ||||||
|                             in_comment = None; |                             in_comment = None; | ||||||
|                             comments.push(( |                             comments.push(( | ||||||
|                                 comment.0, |                                 comment.0, | ||||||
|                                 std::mem::replace(&mut comment.1, String::new()), |                                 std::mem::replace(&mut comment.1, String::new()), | ||||||
|                             )); |                             )); | ||||||
|  |                         } else { | ||||||
|  |                             comment.1.push(ch); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     Some(true) => { |                     Some(true) => { | ||||||
| @ -223,6 +227,20 @@ impl Source { | |||||||
|         } |         } | ||||||
|         o |         o | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn pos_in_og(&self, mut pos: usize, inclusive: bool) -> usize { | ||||||
|  |         for (start, comment) in &self.comments { | ||||||
|  |             if *start < pos || (inclusive && *start == pos) { | ||||||
|  |                 pos += comment.len(); | ||||||
|  |             } else { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         pos | ||||||
|  |     } | ||||||
|  |     pub fn src_og(&self) -> &String { | ||||||
|  |         &self.src_og | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Drop for Source { | impl Drop for Source { | ||||||
| @ -261,14 +279,3 @@ impl SectionMarker { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[derive(Clone, Copy, Debug)] |  | ||||||
| pub struct SourcePos(usize); |  | ||||||
| impl SourcePos { |  | ||||||
|     pub fn pos(&self) -> usize { |  | ||||||
|         self.0 |  | ||||||
|     } |  | ||||||
|     fn diff(&self, rhs: &Self) -> usize { |  | ||||||
|         rhs.0 - self.0 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -3,11 +3,8 @@ use std::fs; | |||||||
| use super::{Source, SourcePos}; | use super::{Source, SourcePos}; | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::Data, |     data::Data, | ||||||
|     program::{ |     errors::{error_colors, CheckError}, | ||||||
|         self, |     program::{self, parsed::MersStatement}, | ||||||
|         parsed::MersStatement, |  | ||||||
|         run::{error_colors, CheckError}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub fn parse( | pub fn parse( | ||||||
| @ -19,6 +16,7 @@ pub fn parse( | |||||||
|     } else { |     } else { | ||||||
|         return Ok(None); |         return Ok(None); | ||||||
|     }; |     }; | ||||||
|  |     let mut pos_after_first = src.get_pos(); | ||||||
|     src.skip_whitespace(); |     src.skip_whitespace(); | ||||||
|     match src.peek_word() { |     match src.peek_word() { | ||||||
|         ":=" => { |         ":=" => { | ||||||
| @ -68,15 +66,15 @@ pub fn parse( | |||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|         _ => loop { |         _ => loop { | ||||||
|             let pos_in_src = src.get_pos(); |  | ||||||
|             src.skip_whitespace(); |             src.skip_whitespace(); | ||||||
|  |             let dot_in_src = src.get_pos(); | ||||||
|             if let Some('.') = src.peek_char() { |             if let Some('.') = src.peek_char() { | ||||||
|                 src.next_char(); |                 src.next_char(); | ||||||
|                 let chained = match parse_no_chain(src) { |                 let chained = match parse_no_chain(src) { | ||||||
|                     Ok(Some(v)) => v, |                     Ok(Some(v)) => v, | ||||||
|                     Ok(None) => { |                     Ok(None) => { | ||||||
|                         return Err(CheckError::new() |                         return Err(CheckError::new() | ||||||
|                             .src(vec![((pos_in_src, src.get_pos()).into(), None)]) |                             .src(vec![((dot_in_src, src.get_pos()).into(), None)]) | ||||||
|                             .msg(format!("EOF after `.`"))) |                             .msg(format!("EOF after `.`"))) | ||||||
|                     } |                     } | ||||||
|                     Err(e) => return Err(e), |                     Err(e) => return Err(e), | ||||||
| @ -91,11 +89,13 @@ pub fn parse( | |||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
|                 first = Box::new(program::parsed::chain::Chain { |                 first = Box::new(program::parsed::chain::Chain { | ||||||
|                     pos_in_src: (pos_in_src, src.get_pos()).into(), |                     pos_in_src: (first.source_range().start(), src.get_pos()).into(), | ||||||
|                     first, |                     first, | ||||||
|                     chained, |                     chained, | ||||||
|                 }); |                 }); | ||||||
|  |                 pos_after_first = src.get_pos(); | ||||||
|             } else { |             } else { | ||||||
|  |                 src.set_pos(pos_after_first); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -5,7 +5,8 @@ use std::{ | |||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{self, Data, MersType, Type}, |     data::{self, Data, MersType, Type}, | ||||||
|     program::run::{CheckError, CheckInfo, Info}, |     errors::CheckError, | ||||||
|  |     program::run::{CheckInfo, Info}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::Config; | use super::Config; | ||||||
|  | |||||||
| @ -9,10 +9,8 @@ use crate::{ | |||||||
|         function::{Function, FunctionT}, |         function::{Function, FunctionT}, | ||||||
|         Data, MersData, MersType, Type, |         Data, MersData, MersType, Type, | ||||||
|     }, |     }, | ||||||
|     program::{ |     errors::CheckError, | ||||||
|         self, |     program::{self, run::CheckInfo}, | ||||||
|         run::{CheckError, CheckInfo}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::Config; | use super::Config; | ||||||
| @ -140,13 +138,6 @@ impl Config { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn iter_out( |  | ||||||
|     a: &Type, |  | ||||||
|     name: &str, |  | ||||||
|     func: impl Fn(&FunctionT) -> ItersT + Sync + Send, |  | ||||||
| ) -> Result<Type, CheckError> { |  | ||||||
|     iter_out_arg(a, name, func) |  | ||||||
| } |  | ||||||
| fn iter_out_arg<T: MersType>( | fn iter_out_arg<T: MersType>( | ||||||
|     a: &Type, |     a: &Type, | ||||||
|     name: &str, |     name: &str, | ||||||
|  | |||||||
| @ -135,7 +135,7 @@ impl Config { | |||||||
|                 Data::new(data::function::Function { |                 Data::new(data::function::Function { | ||||||
|                     info: Arc::new(program::run::Info::neverused()), |                     info: Arc::new(program::run::Info::neverused()), | ||||||
|                     info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                     info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                     out: Arc::new(|a, i| { |                     out: Arc::new(|a, _i| { | ||||||
|                         if let Some(v) = a.iterable() { |                         if let Some(v) = a.iterable() { | ||||||
|                             Ok(Type::new(ListT(v))) |                             Ok(Type::new(ListT(v))) | ||||||
|                         } else { |                         } else { | ||||||
|  | |||||||
| @ -2,10 +2,8 @@ use std::sync::{Arc, Mutex}; | |||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{self, Data, MersType, Type}, |     data::{self, Data, MersType, Type}, | ||||||
|     program::{ |     errors::CheckError, | ||||||
|         self, |     program::{self, run::CheckInfo}, | ||||||
|         run::{CheckError, CheckInfo}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::Config; | use super::Config; | ||||||
|  | |||||||
| @ -6,10 +6,8 @@ use std::{ | |||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{self, Data, MersData, MersType, Type}, |     data::{self, Data, MersData, MersType, Type}, | ||||||
|     program::{ |     errors::CheckError, | ||||||
|         self, |     program::{self, run::CheckInfo}, | ||||||
|         run::{CheckError, CheckInfo}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::Config; | use super::Config; | ||||||
|  | |||||||
| @ -3,8 +3,11 @@ use std::{ | |||||||
|     sync::{Arc, Mutex}, |     sync::{Arc, Mutex}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | use colored::Colorize; | ||||||
|  | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{self, Data, Type}, |     data::{self, Data, Type}, | ||||||
|  |     errors::error_colors, | ||||||
|     program::{self, run::CheckInfo}, |     program::{self, run::CheckInfo}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -23,7 +26,17 @@ impl Config { | |||||||
|             Data::new(data::function::Function { |             Data::new(data::function::Function { | ||||||
|                 info: Arc::new(program::run::Info::neverused()), |                 info: Arc::new(program::run::Info::neverused()), | ||||||
|                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                 out: Arc::new(|a, i| Ok(Type::new(data::string::StringT))), |                 out: Arc::new(|a, _i| { | ||||||
|  |                     if a.is_zero_tuple() { | ||||||
|  |                         Ok(Type::new(data::string::StringT)) | ||||||
|  |                     } else { | ||||||
|  |                         Err(format!( | ||||||
|  |                             "expected (), got {}", | ||||||
|  |                             a.to_string().color(error_colors::FunctionArgument) | ||||||
|  |                         ) | ||||||
|  |                         .into()) | ||||||
|  |                     } | ||||||
|  |                 }), | ||||||
|                 run: Arc::new(|_a, _i| { |                 run: Arc::new(|_a, _i| { | ||||||
|                     let mut line = String::new(); |                     let mut line = String::new(); | ||||||
|                     _ = std::io::stdin().read_line(&mut line); |                     _ = std::io::stdin().read_line(&mut line); | ||||||
| @ -36,7 +49,7 @@ impl Config { | |||||||
|             Data::new(data::function::Function { |             Data::new(data::function::Function { | ||||||
|                 info: Arc::new(program::run::Info::neverused()), |                 info: Arc::new(program::run::Info::neverused()), | ||||||
|                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                 out: Arc::new(|a, i| Ok(Type::empty_tuple())), |                 out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), | ||||||
|                 run: Arc::new(|a, _i| { |                 run: Arc::new(|a, _i| { | ||||||
|                     eprintln!("{:#?}", a.get()); |                     eprintln!("{:#?}", a.get()); | ||||||
|                     Data::empty_tuple() |                     Data::empty_tuple() | ||||||
| @ -48,10 +61,10 @@ impl Config { | |||||||
|             Data::new(data::function::Function { |             Data::new(data::function::Function { | ||||||
|                 info: Arc::new(program::run::Info::neverused()), |                 info: Arc::new(program::run::Info::neverused()), | ||||||
|                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                 out: Arc::new(|a, i| Ok(Type::empty_tuple())), |                 out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), | ||||||
|                 run: Arc::new(|a, _i| { |                 run: Arc::new(|a, _i| { | ||||||
|                     eprint!("{}", a.get()); |                     eprint!("{}", a.get()); | ||||||
|                     std::io::stderr().lock().flush(); |                     _ = std::io::stderr().lock().flush(); | ||||||
|                     Data::empty_tuple() |                     Data::empty_tuple() | ||||||
|                 }), |                 }), | ||||||
|             }), |             }), | ||||||
| @ -61,7 +74,7 @@ impl Config { | |||||||
|             Data::new(data::function::Function { |             Data::new(data::function::Function { | ||||||
|                 info: Arc::new(program::run::Info::neverused()), |                 info: Arc::new(program::run::Info::neverused()), | ||||||
|                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                 out: Arc::new(|a, i| Ok(Type::empty_tuple())), |                 out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), | ||||||
|                 run: Arc::new(|a, _i| { |                 run: Arc::new(|a, _i| { | ||||||
|                     eprintln!("{}", a.get()); |                     eprintln!("{}", a.get()); | ||||||
|                     Data::empty_tuple() |                     Data::empty_tuple() | ||||||
| @ -73,10 +86,10 @@ impl Config { | |||||||
|             Data::new(data::function::Function { |             Data::new(data::function::Function { | ||||||
|                 info: Arc::new(program::run::Info::neverused()), |                 info: Arc::new(program::run::Info::neverused()), | ||||||
|                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                 out: Arc::new(|a, i| Ok(Type::empty_tuple())), |                 out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), | ||||||
|                 run: Arc::new(|a, _i| { |                 run: Arc::new(|a, _i| { | ||||||
|                     print!("{}", a.get()); |                     print!("{}", a.get()); | ||||||
|                     std::io::stdout().lock().flush(); |                     _ = std::io::stdout().lock().flush(); | ||||||
|                     Data::empty_tuple() |                     Data::empty_tuple() | ||||||
|                 }), |                 }), | ||||||
|             }), |             }), | ||||||
| @ -86,7 +99,7 @@ impl Config { | |||||||
|             Data::new(data::function::Function { |             Data::new(data::function::Function { | ||||||
|                 info: Arc::new(program::run::Info::neverused()), |                 info: Arc::new(program::run::Info::neverused()), | ||||||
|                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), |                 info_check: Arc::new(Mutex::new(CheckInfo::neverused())), | ||||||
|                 out: Arc::new(|a, i| Ok(Type::empty_tuple())), |                 out: Arc::new(|_a, _i| Ok(Type::empty_tuple())), | ||||||
|                 run: Arc::new(|a, _i| { |                 run: Arc::new(|a, _i| { | ||||||
|                     println!("{}", a.get()); |                     println!("{}", a.get()); | ||||||
|                     Data::empty_tuple() |                     Data::empty_tuple() | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| use crate::program::{ | use crate::{ | ||||||
|     self, |     errors::{CheckError, SourceRange}, | ||||||
|     run::{CheckError, SourceRange}, |     program::{self}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|     info, |     info, | ||||||
|     program::{ |     program::{self}, | ||||||
|         self, |  | ||||||
|         run::{CheckError, SourceRange}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| use crate::program::run::{CheckError, SourceRange}; | use crate::{ | ||||||
| use crate::{info, program}; |     errors::{CheckError, SourceRange}, | ||||||
|  |     info, program, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| use crate::program::run::{CheckError, SourceRange}; |  | ||||||
| use std::sync::{Arc, Mutex}; | use std::sync::{Arc, Mutex}; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data, |     data, | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|     program::{self, run::CheckInfo}, |     program::{self, run::CheckInfo}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -29,8 +29,8 @@ impl MersStatement for Function { | |||||||
|         let arg_target = Arc::new(self.arg.compile(info, comp)?); |         let arg_target = Arc::new(self.arg.compile(info, comp)?); | ||||||
|         comp.is_init = false; |         comp.is_init = false; | ||||||
|         let run = Arc::new(self.run.compile(info, comp)?); |         let run = Arc::new(self.run.compile(info, comp)?); | ||||||
|         let arg2 = Arc::clone(&arg_target); |         let arg2: Arc<Box<dyn crate::program::run::MersStatement>> = Arc::clone(&arg_target); | ||||||
|         let run2 = Arc::clone(&run); |         let run2: Arc<Box<dyn crate::program::run::MersStatement>> = Arc::clone(&run); | ||||||
|         Ok(Box::new(program::run::function::Function { |         Ok(Box::new(program::run::function::Function { | ||||||
|             pos_in_src: self.pos_in_src, |             pos_in_src: self.pos_in_src, | ||||||
|             func_no_info: data::function::Function { |             func_no_info: data::function::Function { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| use crate::program::{ | use crate::{ | ||||||
|     self, |     errors::{CheckError, SourceRange}, | ||||||
|     run::{CheckError, SourceRange}, |     program::{self}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
|  | |||||||
| @ -4,11 +4,9 @@ use colored::Colorize; | |||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{self, Data}, |     data::{self, Data}, | ||||||
|  |     errors::{error_colors, CheckError, SourceRange}, | ||||||
|     info::{self, Local}, |     info::{self, Local}, | ||||||
|     program::{ |     program::{self}, | ||||||
|         self, |  | ||||||
|         run::{error_colors, CheckError, SourceRange}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
| @ -27,7 +25,7 @@ impl MersStatement for IncludeMers { | |||||||
|         info: &mut info::Info<super::Local>, |         info: &mut info::Info<super::Local>, | ||||||
|         comp: CompInfo, |         comp: CompInfo, | ||||||
|     ) -> Result<Box<dyn program::run::MersStatement>, CheckError> { |     ) -> Result<Box<dyn program::run::MersStatement>, CheckError> { | ||||||
|         let compiled = match self.include.compile(info, comp) { |         let compiled: Arc<Box<dyn crate::program::run::MersStatement>> = match self.include.compile(info, comp) { | ||||||
|             Ok(v) => Arc::new(v), |             Ok(v) => Arc::new(v), | ||||||
|             Err(e) => { |             Err(e) => { | ||||||
|                 return Err(CheckError::new() |                 return Err(CheckError::new() | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| use crate::program::run::SourceRange; | use crate::{ | ||||||
| use crate::program::{self, run::CheckError}; |     errors::{CheckError, SourceRange}, | ||||||
|  |     program::{self}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| use std::{collections::HashMap, fmt::Debug}; | use std::{collections::HashMap, fmt::Debug}; | ||||||
| 
 | 
 | ||||||
| use crate::info; | use crate::{ | ||||||
| 
 |     errors::{CheckError, SourceRange}, | ||||||
| use super::run::{CheckError, SourceRange}; |     info, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "parse")] | #[cfg(feature = "parse")] | ||||||
| pub mod assign_to; | pub mod assign_to; | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|     info, |     info, | ||||||
|     program::{ |     program::{self}, | ||||||
|         self, |  | ||||||
|         run::{CheckError, SourceRange}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
|  | |||||||
| @ -1,5 +1,8 @@ | |||||||
| use crate::program::run::{CheckError, SourceRange}; | use crate::{ | ||||||
| use crate::{data::Data, program}; |     data::Data, | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|  |     program, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|  |     errors::{error_colors, CheckError, SourceRange}, | ||||||
|     info::Local, |     info::Local, | ||||||
|     program::{ |     program::{self}, | ||||||
|         self, |  | ||||||
|         run::{error_colors, CheckError, SourceRange}, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use super::{CompInfo, MersStatement}; | use super::{CompInfo, MersStatement}; | ||||||
|  | |||||||
| @ -1,8 +1,11 @@ | |||||||
| use colored::Colorize; | use colored::Colorize; | ||||||
| 
 | 
 | ||||||
| use crate::data::{self, Data, MersType, Type}; | use crate::{ | ||||||
|  |     data::{self, Data, MersType, Type}, | ||||||
|  |     errors::{error_colors, CheckError, SourceRange}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{error_colors, CheckError, CheckInfo, MersStatement, SourceRange}; | use super::{CheckInfo, MersStatement}; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct AssignTo { | pub struct AssignTo { | ||||||
|  | |||||||
| @ -1,6 +1,9 @@ | |||||||
| use crate::data::Type; | use crate::{ | ||||||
|  |     data::Type, | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{MersStatement, SourceRange}; | use super::{CheckInfo, MersStatement}; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Block { | pub struct Block { | ||||||
| @ -10,9 +13,9 @@ pub struct Block { | |||||||
| impl MersStatement for Block { | impl MersStatement for Block { | ||||||
|     fn check_custom( |     fn check_custom( | ||||||
|         &self, |         &self, | ||||||
|         info: &mut super::CheckInfo, |         info: &mut CheckInfo, | ||||||
|         init_to: Option<&Type>, |         init_to: Option<&Type>, | ||||||
|     ) -> Result<crate::data::Type, super::CheckError> { |     ) -> Result<crate::data::Type, CheckError> { | ||||||
|         if init_to.is_some() { |         if init_to.is_some() { | ||||||
|             return Err("can't init to statement type Block".to_string().into()); |             return Err("can't init to statement type Block".to_string().into()); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -2,9 +2,12 @@ use std::sync::Arc; | |||||||
| 
 | 
 | ||||||
| use colored::Colorize; | use colored::Colorize; | ||||||
| 
 | 
 | ||||||
| use crate::data::{Data, Type}; | use crate::{ | ||||||
|  |     data::{Data, Type}, | ||||||
|  |     errors::{error_colors, CheckError, SourceRange}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{error_colors, CheckError, MersStatement, SourceRange}; | use super::MersStatement; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Chain { | pub struct Chain { | ||||||
|  | |||||||
| @ -1,8 +1,11 @@ | |||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| use crate::data::{self, Data, MersData, Type}; | use crate::{ | ||||||
|  |     data::{self, Data, MersData, Type}, | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{MersStatement, SourceRange}; | use super::MersStatement; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Function { | pub struct Function { | ||||||
| @ -15,7 +18,7 @@ impl MersStatement for Function { | |||||||
|         &self, |         &self, | ||||||
|         info: &mut super::CheckInfo, |         info: &mut super::CheckInfo, | ||||||
|         init_to: Option<&Type>, |         init_to: Option<&Type>, | ||||||
|     ) -> Result<data::Type, super::CheckError> { |     ) -> Result<data::Type, CheckError> { | ||||||
|         if init_to.is_some() { |         if init_to.is_some() { | ||||||
|             return Err("can't init to statement type Function".to_string().into()); |             return Err("can't init to statement type Function".to_string().into()); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -2,9 +2,12 @@ use std::sync::Arc; | |||||||
| 
 | 
 | ||||||
| use colored::Colorize; | use colored::Colorize; | ||||||
| 
 | 
 | ||||||
| use crate::data::{self, Data, MersType, Type}; | use crate::{ | ||||||
|  |     data::{self, Data, MersType, Type}, | ||||||
|  |     errors::{error_colors, CheckError, SourceRange}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{error_colors, CheckError, MersStatement, SourceRange}; | use super::MersStatement; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct If { | pub struct If { | ||||||
| @ -19,7 +22,7 @@ impl MersStatement for If { | |||||||
|         &self, |         &self, | ||||||
|         info: &mut super::CheckInfo, |         info: &mut super::CheckInfo, | ||||||
|         init_to: Option<&Type>, |         init_to: Option<&Type>, | ||||||
|     ) -> Result<data::Type, super::CheckError> { |     ) -> Result<data::Type, CheckError> { | ||||||
|         if init_to.is_some() { |         if init_to.is_some() { | ||||||
|             return Err("can't init to statement type If".to_string().into()); |             return Err("can't init to statement type If".to_string().into()); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,15 +1,12 @@ | |||||||
| use std::{ | use std::{ | ||||||
|     fmt::{Debug, Display}, |     fmt::Debug, | ||||||
|     sync::{Arc, RwLock}, |     sync::{Arc, RwLock}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use colored::Colorize; |  | ||||||
| use line_span::LineSpanExt; |  | ||||||
| 
 |  | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{self, Data, Type}, |     data::{self, Data, Type}, | ||||||
|  |     errors::{CheckError, SourceRange}, | ||||||
|     info, |     info, | ||||||
|     parsing::{Source, SourcePos}, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "run")] | #[cfg(feature = "run")] | ||||||
| @ -61,208 +58,6 @@ pub trait MersStatement: Debug + Send + Sync { | |||||||
|     fn source_range(&self) -> SourceRange; |     fn source_range(&self) -> SourceRange; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Copy, Debug)] |  | ||||||
| pub struct SourceRange { |  | ||||||
|     start: SourcePos, |  | ||||||
|     end: SourcePos, |  | ||||||
| } |  | ||||||
| impl From<(SourcePos, SourcePos)> for SourceRange { |  | ||||||
|     fn from(value: (SourcePos, SourcePos)) -> Self { |  | ||||||
|         SourceRange { |  | ||||||
|             start: value.0, |  | ||||||
|             end: value.1, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| impl SourceRange { |  | ||||||
|     pub fn start(&self) -> SourcePos { |  | ||||||
|         self.start |  | ||||||
|     } |  | ||||||
|     pub fn end(&self) -> SourcePos { |  | ||||||
|         self.end |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| pub struct CheckError(Vec<CheckErrorComponent>); |  | ||||||
| #[allow(non_upper_case_globals)] |  | ||||||
| pub mod error_colors { |  | ||||||
|     use colored::Color; |  | ||||||
| 
 |  | ||||||
|     pub const UnknownVariable: Color = Color::Red; |  | ||||||
| 
 |  | ||||||
|     pub const WhitespaceAfterHashtag: Color = Color::Red; |  | ||||||
|     pub const HashUnknown: Color = Color::Red; |  | ||||||
|     pub const HashIncludeCantLoadFile: Color = Color::Red; |  | ||||||
|     pub const HashIncludeNotAString: Color = Color::Red; |  | ||||||
|     pub const HashIncludeErrorInIncludedFile: Color = Color::Red; |  | ||||||
| 
 |  | ||||||
|     pub const BackslashEscapeUnknown: Color = Color::Red; |  | ||||||
|     pub const BackslashEscapeEOF: Color = Color::Red; |  | ||||||
|     pub const StringEOF: Color = Color::Red; |  | ||||||
| 
 |  | ||||||
|     pub const IfConditionNotBool: Color = Color::Red; |  | ||||||
|     pub const ChainWithNonFunction: Color = Color::Yellow; |  | ||||||
| 
 |  | ||||||
|     pub const Function: Color = Color::BrightMagenta; |  | ||||||
|     pub const FunctionArgument: Color = Color::BrightBlue; |  | ||||||
| 
 |  | ||||||
|     pub const InitFrom: Color = Color::BrightCyan; |  | ||||||
|     pub const InitTo: Color = Color::Green; |  | ||||||
|     pub const AssignFrom: Color = InitFrom; |  | ||||||
|     pub const AssignTo: Color = InitTo; |  | ||||||
|     pub const AssignTargetNonReference: Color = Color::BrightYellow; |  | ||||||
| } |  | ||||||
| #[derive(Clone, Debug)] |  | ||||||
| enum CheckErrorComponent { |  | ||||||
|     Message(String), |  | ||||||
|     Error(CheckError), |  | ||||||
|     Source(Vec<(SourceRange, Option<colored::Color>)>), |  | ||||||
| } |  | ||||||
| #[derive(Clone)] |  | ||||||
| pub struct CheckErrorHRConfig { |  | ||||||
|     indent_start: String, |  | ||||||
|     indent_default: String, |  | ||||||
|     indent_end: String, |  | ||||||
| } |  | ||||||
| pub struct CheckErrorDisplay<'a> { |  | ||||||
|     e: &'a CheckError, |  | ||||||
|     src: Option<&'a Source>, |  | ||||||
| } |  | ||||||
| impl Display for CheckErrorDisplay<'_> { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |  | ||||||
|         self.e.human_readable( |  | ||||||
|             f, |  | ||||||
|             self.src, |  | ||||||
|             &CheckErrorHRConfig { |  | ||||||
|                 indent_start: String::new(), |  | ||||||
|                 indent_default: String::new(), |  | ||||||
|                 indent_end: String::new(), |  | ||||||
|             }, |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| impl CheckError { |  | ||||||
|     pub fn new() -> Self { |  | ||||||
|         CheckError(vec![]) |  | ||||||
|     } |  | ||||||
|     fn add(mut self, v: CheckErrorComponent) -> Self { |  | ||||||
|         self.0.push(v); |  | ||||||
|         self |  | ||||||
|     } |  | ||||||
|     pub(crate) fn msg(self, s: String) -> Self { |  | ||||||
|         self.add(CheckErrorComponent::Message(s)) |  | ||||||
|     } |  | ||||||
|     pub(crate) fn err(self, e: Self) -> Self { |  | ||||||
|         self.add(CheckErrorComponent::Error(e)) |  | ||||||
|     } |  | ||||||
|     pub(crate) fn src(self, s: Vec<(SourceRange, Option<colored::Color>)>) -> Self { |  | ||||||
|         self.add(CheckErrorComponent::Source(s)) |  | ||||||
|     } |  | ||||||
|     pub fn display<'a>(&'a self, src: &'a Source) -> CheckErrorDisplay<'a> { |  | ||||||
|         CheckErrorDisplay { |  | ||||||
|             e: self, |  | ||||||
|             src: Some(src), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     pub fn display_no_src<'a>(&'a self) -> CheckErrorDisplay<'a> { |  | ||||||
|         CheckErrorDisplay { e: self, src: None } |  | ||||||
|     } |  | ||||||
|     // will, unless empty, end in a newline
 |  | ||||||
|     fn human_readable( |  | ||||||
|         &self, |  | ||||||
|         f: &mut std::fmt::Formatter<'_>, |  | ||||||
|         src: Option<&Source>, |  | ||||||
|         cfg: &CheckErrorHRConfig, |  | ||||||
|     ) -> std::fmt::Result { |  | ||||||
|         let len = self.0.len(); |  | ||||||
|         for (i, component) in self.0.iter().enumerate() { |  | ||||||
|             macro_rules! indent { |  | ||||||
|                 () => { |  | ||||||
|                     if i + 1 == len { |  | ||||||
|                         &cfg.indent_end |  | ||||||
|                     } else if i == 0 { |  | ||||||
|                         &cfg.indent_start |  | ||||||
|                     } else { |  | ||||||
|                         &cfg.indent_default |  | ||||||
|                     } |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
|             match component { |  | ||||||
|                 CheckErrorComponent::Message(msg) => writeln!(f, "{}{msg}", indent!())?, |  | ||||||
|                 CheckErrorComponent::Error(err) => { |  | ||||||
|                     let mut cfg = cfg.clone(); |  | ||||||
|                     cfg.indent_start.push_str("│"); |  | ||||||
|                     cfg.indent_default.push_str("│"); |  | ||||||
|                     cfg.indent_end.push_str("└"); |  | ||||||
|                     err.human_readable(f, src, &cfg)?; |  | ||||||
|                 } |  | ||||||
|                 CheckErrorComponent::Source(highlights) => { |  | ||||||
|                     if let Some(src) = src { |  | ||||||
|                         let start = highlights.iter().map(|v| v.0.start.pos()).min(); |  | ||||||
|                         let end = highlights.iter().map(|v| v.0.start.pos()).max(); |  | ||||||
|                         if let (Some(start), Some(end)) = (start, end) { |  | ||||||
|                             writeln!(f, "{}Line(s) [?] ({start}..{end})", indent!())?; |  | ||||||
|                             let start = src.get_line_start(start); |  | ||||||
|                             let end = src.get_line_end(end); |  | ||||||
|                             let lines = src.src()[start..end].line_spans().collect::<Vec<_>>(); |  | ||||||
|                             for line in lines { |  | ||||||
|                                 let line_start = line.start(); |  | ||||||
|                                 let line_end = line.end(); |  | ||||||
|                                 let line = line.as_str(); |  | ||||||
|                                 writeln!(f, "{} {line}", indent!())?; |  | ||||||
|                                 let mut right = 0; |  | ||||||
|                                 for (pos, color) in highlights { |  | ||||||
|                                     if let Some(color) = color { |  | ||||||
|                                         let highlight_start = pos.start.pos() - start; |  | ||||||
|                                         let highlight_end = pos.end.pos() - start; |  | ||||||
|                                         if highlight_start < line_end && highlight_end > line_start |  | ||||||
|                                         { |  | ||||||
|                                             // where the highlight starts in this line
 |  | ||||||
|                                             let hl_start = |  | ||||||
|                                                 highlight_start.saturating_sub(line_start); |  | ||||||
|                                             // highlight would be further left than cursor, so we need a new line
 |  | ||||||
|                                             if hl_start < right { |  | ||||||
|                                                 right = 0; |  | ||||||
|                                                 writeln!(f)?; |  | ||||||
|                                             } |  | ||||||
|                                             // length of the highlight
 |  | ||||||
|                                             let hl_len = highlight_end |  | ||||||
|                                                 .saturating_sub(line_start) |  | ||||||
|                                                 .saturating_sub(hl_start); |  | ||||||
|                                             let hl_space = hl_start - right; |  | ||||||
|                                             let print_indent = right == 0; |  | ||||||
|                                             let hl_len = hl_len.min(line.len() - right); |  | ||||||
|                                             right += hl_space + hl_len; |  | ||||||
|                                             if print_indent && right != 0 { |  | ||||||
|                                                 write!(f, "{} ", indent!())?; |  | ||||||
|                                             } |  | ||||||
|                                             write!( |  | ||||||
|                                                 f, |  | ||||||
|                                                 "{}{}", |  | ||||||
|                                                 " ".repeat(hl_space), |  | ||||||
|                                                 "~".repeat(hl_len).color(*color) |  | ||||||
|                                             )?; |  | ||||||
|                                         } |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                                 if right != 0 { |  | ||||||
|                                     writeln!(f)?; |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| impl From<String> for CheckError { |  | ||||||
|     fn from(value: String) -> Self { |  | ||||||
|         Self::new().msg(value) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub type Info = info::Info<Local>; | pub type Info = info::Info<Local>; | ||||||
| pub type CheckInfo = info::Info<CheckLocal>; | pub type CheckInfo = info::Info<CheckLocal>; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,9 +2,12 @@ use std::{collections::VecDeque, sync::Arc}; | |||||||
| 
 | 
 | ||||||
| use colored::Colorize; | use colored::Colorize; | ||||||
| 
 | 
 | ||||||
| use crate::data::{self, tuple::TupleT, Data, Type}; | use crate::{ | ||||||
|  |     data::{self, tuple::TupleT, Data, Type}, | ||||||
|  |     errors::{error_colors, SourceRange}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{error_colors, MersStatement, SourceRange}; | use super::MersStatement; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Tuple { | pub struct Tuple { | ||||||
|  | |||||||
| @ -1,6 +1,9 @@ | |||||||
| use crate::data::{Data, Type}; | use crate::{ | ||||||
|  |     data::{Data, Type}, | ||||||
|  |     errors::SourceRange, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{MersStatement, SourceRange}; | use super::{CheckInfo, MersStatement}; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Value { | pub struct Value { | ||||||
| @ -14,7 +17,7 @@ impl MersStatement for Value { | |||||||
|     } |     } | ||||||
|     fn check_custom( |     fn check_custom( | ||||||
|         &self, |         &self, | ||||||
|         info: &mut super::CheckInfo, |         _info: &mut CheckInfo, | ||||||
|         init_to: Option<&Type>, |         init_to: Option<&Type>, | ||||||
|     ) -> Result<crate::data::Type, super::CheckError> { |     ) -> Result<crate::data::Type, super::CheckError> { | ||||||
|         if init_to.is_some() { |         if init_to.is_some() { | ||||||
|  | |||||||
| @ -1,8 +1,11 @@ | |||||||
| use std::sync::{Arc, RwLock}; | use std::sync::{Arc, RwLock}; | ||||||
| 
 | 
 | ||||||
| use crate::data::{self, Data, Type}; | use crate::{ | ||||||
|  |     data::{self, Data, Type}, | ||||||
|  |     errors::SourceRange, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use super::{MersStatement, SourceRange}; | use super::MersStatement; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Variable { | pub struct Variable { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mark
						Mark