diff --git a/mers/Cargo.toml b/mers/Cargo.toml index 0a7696a..557d6b8 100644 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers" -version = "0.8.15" +version = "0.8.16" edition = "2021" license = "MIT OR Apache-2.0" description = "dynamically typed but type-checked programming language" @@ -12,10 +12,10 @@ repository = "https://github.com/Dummi26/mers" [features] default = ["colored-output"] -colored-output = ["mers_lib/ecolor-term", "dep:colored"] +colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"] [dependencies] -mers_lib = "0.8.15" +mers_lib = "0.8.16" # mers_lib = { path = "../mers_lib" } clap = { version = "4.3.19", features = ["derive"] } colored = { version = "2.1.0", optional = true } diff --git a/mers/src/main.rs b/mers/src/main.rs index 00a428a..c59125f 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -3,8 +3,6 @@ use mers_lib::prelude_compile::*; use std::{path::PathBuf, process::exit, sync::Arc}; mod cfg_globals; -#[cfg(feature = "colored-output")] -mod pretty_print; #[derive(Parser)] struct Args { @@ -169,7 +167,7 @@ fn main() { } #[cfg(feature = "colored-output")] Command::PrettyPrint { source } => { - pretty_print::pretty_print(get_source(source)); + mers_lib::pretty_print::pretty_print(get_source(source)); } #[cfg(not(feature = "colored-output"))] Command::PrettyPrint { source: _ } => { diff --git a/mers/src/pretty_print.rs b/mers/src/pretty_print.rs deleted file mode 100644 index d918e0b..0000000 --- a/mers/src/pretty_print.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::{process::exit, sync::Arc}; - -use colored::{Color, Colorize}; -use mers_lib::prelude_compile::{parse, Source}; - -pub fn pretty_print(mut src: Source) { - let srca = Arc::new(src.clone()); - match parse(&mut src, &srca) { - Err(e) => { - eprintln!("{e:?}"); - exit(28); - } - Ok(parsed) => { - print_parsed(&srca, parsed.as_ref()); - } - } -} - -const COLOR_COMMENT: Color = Color::BrightBlack; -const COLOR_VARIABLE: Color = Color::Green; -const COLOR_VARIABLE_REF: Color = Color::Green; -const COLOR_IF: Color = Color::Red; -const COLOR_IF_WITH_ELSE: Color = Color::Red; -const COLOR_LOOP: Color = Color::Red; -const COLOR_TUPLE: Color = Color::Blue; -const COLOR_OBJECT: Color = Color::Blue; -const COLOR_VALUE: Color = Color::Cyan; -const COLOR_AS_TYPE: Color = Color::BrightBlack; -const COLOR_CUSTOM_TYPE: Color = Color::BrightBlack; -const COLOR_UNKNOWN: Color = Color::White; - -fn print_parsed(srca: &Arc, parsed: &dyn mers_lib::program::parsed::MersStatement) { - let mut sections = vec![(COLOR_UNKNOWN, srca.src_og().len())]; - build_print(&mut sections, srca, parsed); - for (start, comment) in srca.comments() { - let end = start + comment.len(); - build_print_insert_color(&mut sections, COLOR_COMMENT, *start, end); - } - let src = srca.src_og(); - let mut i = 0; - for (clr, end) in sections { - print!("{}", src[i..end].color(clr)); - i = end; - } - println!(); -} -fn build_print( - sections: &mut Vec<(Color, usize)>, - srca: &Arc, - parsed: &dyn mers_lib::program::parsed::MersStatement, -) { - let any = parsed.as_any(); - let clr = if let Some(v) = any.downcast_ref::() { - if v.is_ref { - COLOR_VARIABLE_REF - } else { - COLOR_VARIABLE - } - } else if let Some(v) = any.downcast_ref::() { - if v.on_false.is_some() { - COLOR_IF_WITH_ELSE - } else { - COLOR_IF - } - } else if let Some(_) = any.downcast_ref::() { - COLOR_LOOP - } else if let Some(_) = any.downcast_ref::() { - COLOR_TUPLE - } else if let Some(_) = any.downcast_ref::() { - COLOR_OBJECT - } else if let Some(_) = any.downcast_ref::() { - COLOR_VALUE - } else if let Some(_) = any.downcast_ref::() { - COLOR_AS_TYPE - } else if let Some(_) = any.downcast_ref::() - { - COLOR_CUSTOM_TYPE - } else { - COLOR_UNKNOWN - }; - let range = parsed.source_range(); - let start = srca.pos_in_og(range.start().pos(), true); - let end = srca.pos_in_og(range.end().pos(), false); - build_print_insert_color(sections, clr, start, end); - for s in parsed.inner_statements() { - build_print(sections, srca, s); - } -} - -fn build_print_insert_color( - sections: &mut Vec<(Color, usize)>, - clr: Color, - start: usize, - end: usize, -) { - let thing_at_my_start = sections.iter().position(|v| v.1 > start); - let thing_at_my_end = sections.iter().position(|v| v.1 > end); - if let Some(thing_at_my_start) = thing_at_my_start { - if let Some(thing_at_my_end) = thing_at_my_end { - if thing_at_my_start == thing_at_my_end { - let (around_color, around_end) = sections[thing_at_my_start]; - if around_color != clr { - sections[thing_at_my_start].1 = start; - sections.insert(thing_at_my_start + 1, (clr, end)); - sections.insert(thing_at_my_start + 2, (around_color, around_end)); - if sections[thing_at_my_start].1 - == thing_at_my_start - .checked_sub(1) - .map(|v| sections[v].1) - .unwrap_or(0) - { - // thing at my start now ends at the same place the thing before it ends, so we can remove it - sections.remove(thing_at_my_start); - } - } - } else { - sections[thing_at_my_start].1 = start; - sections.insert(thing_at_my_start + 1, (clr, end)); - for _ in 0..(thing_at_my_end.saturating_sub(thing_at_my_start + 1)) { - sections.remove(thing_at_my_start + 2); - } - } - } else { - if sections[thing_at_my_start].0 == clr { - sections[thing_at_my_start].1 = end; - } else { - sections[thing_at_my_start].1 = start; - sections.push((clr, end)); - } - } - } else { - if let Some(last) = sections.last_mut().filter(|v| v.0 == clr) { - last.1 = end; - } else { - sections.push((clr, end)); - } - } -} diff --git a/mers_lib/Cargo.toml b/mers_lib/Cargo.toml index 39199e7..e0c710f 100755 --- a/mers_lib/Cargo.toml +++ b/mers_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mers_lib" -version = "0.8.15" +version = "0.8.16" edition = "2021" license = "MIT OR Apache-2.0" description = "library to use the mers language in other projects" @@ -17,7 +17,12 @@ run = [] # color features are used when formatting error messages. ecolor-term = ["dep:colored"] +ecolor-html = ["dep:html-escape"] + +# pretty-print requires ecolor-term, but this is not listed here because I want people to explicitly opt-in to the extra `colored` dependency via the ecolor-term feature. +pretty-print = [] [dependencies] -colored = { version = "2.1.0", optional = true } line-span = "0.1.5" +colored = { version = "2.1.0", optional = true } +html-escape = { version = "0.2.13", optional = true } diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index 777d92d..829cbac 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -8,6 +8,9 @@ use line_span::LineSpans; #[cfg(feature = "parse")] use crate::parsing::Source; +use crate::theme::ThemeGen; + +pub mod themes; #[derive(Clone, Copy, Debug)] pub struct SourcePos(pub(crate) usize); @@ -62,6 +65,9 @@ pub enum EColor { BackslashEscapeUnknown, BackslashEscapeEOF, StringEOF, + TypeEOF, + /// `&[Int/String` (notice the missing `]`) + BracketedRefTypeNoClosingBracket, IfConditionNotBool, ChainWithNonFunction, Function, @@ -92,115 +98,24 @@ pub enum EColor { InCodePositionLine, } -pub trait Theme { - fn color(&self, text: &str, color: EColor, t: &mut T); -} -pub trait ThemeTo: Theme { - fn color_to(&self, text: &str, color: EColor) -> T; -} -impl + ?Sized> ThemeTo for T { - fn color_to(&self, text: &str, color: EColor) -> String { - let mut t = String::new(); - self.color(text, color, &mut t); - t - } -} +pub trait ETheme: ThemeGen {} +impl> ETheme for T {} + pub fn colorize_str( message: &Vec<(String, Option)>, - theme: &(impl Theme + ?Sized), + theme: &(impl ETheme + ?Sized), ) -> String { let mut t = String::new(); - colorize_gen(message, &mut t, |t, a| a.push_str(t), theme); - t -} -pub fn colorize_gen( - message: &Vec<(String, Option)>, - t: &mut T, - direct: impl Fn(&str, &mut T), - theme: &(impl Theme + ?Sized), -) { for (text, color) in message { if let Some(color) = *color { - theme.color(text, color, t) + theme.color(text, color, &mut t) } else { - direct(text, t) + theme.nocolor(text, &mut t) } } + t } -pub struct NoTheme; -impl Theme for NoTheme { - fn color(&self, text: &str, _color: EColor, t: &mut String) { - t.push_str(text); - } -} - -#[cfg(feature = "ecolor-term")] -pub struct TermDefaultTheme; -#[cfg(feature = "ecolor-term")] -impl Theme for TermDefaultTheme { - fn color(&self, text: &str, color: EColor, t: &mut String) { - use colored::{Color, Colorize}; - t.push_str( - &text - .color(match color { - EColor::Indent(n) => match n % 6 { - 0 => Color::Red, - 1 => Color::Green, - 2 => Color::Yellow, - 3 => Color::Blue, - 4 => Color::Magenta, - _ => Color::Cyan, - }, - - EColor::UnknownVariable => Color::Red, - - EColor::WhitespaceAfterHashtag => Color::Red, - EColor::HashUnknown => Color::Red, - EColor::HashIncludeCantLoadFile => Color::Red, - EColor::HashIncludeNotAString => Color::Red, - EColor::HashIncludeErrorInIncludedFile => Color::Red, - - EColor::BackslashEscapeUnknown => Color::Red, - EColor::BackslashEscapeEOF => Color::Red, - EColor::StringEOF => Color::Red, - - EColor::IfConditionNotBool => Color::Red, - EColor::ChainWithNonFunction => Color::Yellow, - - EColor::Function => Color::BrightMagenta, - EColor::FunctionArgument => Color::BrightBlue, - - EColor::InitFrom - | EColor::AssignFrom - | EColor::AsTypeStatementWithTooBroadType => Color::BrightCyan, - EColor::InitTo | EColor::AssignTo | EColor::AsTypeTypeAnnotation => { - Color::Green - } - EColor::AssignTargetNonReference => Color::BrightYellow, - - EColor::BadCharInTupleType => Color::Red, - EColor::BadCharInFunctionType => Color::Red, - EColor::BadTypeFromParsed => Color::Blue, - EColor::TypeAnnotationNoClosingBracket => Color::Blue, - - EColor::TryBadSyntax => Color::Red, - EColor::TryNoFunctionFound => Color::Red, - EColor::TryNotAFunction => Color::Red, - EColor::TryUnusedFunction1 => Color::Red, - EColor::TryUnusedFunction2 => Color::BrightRed, - EColor::CustomTypeTestFailed => Color::BrightRed, - - EColor::StacktraceDescend => Color::Yellow, - EColor::StacktraceDescendHashInclude => Color::Red, - EColor::MaximumRuntimeExceeded => Color::BrightYellow, - - EColor::InCodePositionLine => Color::BrightBlack, - }) - .to_string(), - ); - } -} #[derive(Clone)] pub enum CheckErrorComponent { Message(Vec<(String, Option)>), @@ -211,7 +126,7 @@ pub enum CheckErrorComponent { pub struct CheckErrorHRConfig { color_index_ptr: Rc, color_index: u32, - theme: Rc>, + theme: Rc, is_inner: bool, style: u8, idt_start: String, @@ -335,7 +250,7 @@ impl CheckErrorHRConfig { #[cfg(feature = "parse")] pub struct CheckErrorDisplay<'a> { e: &'a CheckError, - theme: Rc>, + theme: Rc, pub show_comments: bool, } #[cfg(feature = "parse")] @@ -409,7 +324,7 @@ impl CheckError { self.add_mut(CheckErrorComponent::Source(s)) } #[cfg(feature = "parse")] - pub fn display<'a>(&'a self, theme: impl Theme + 'static) -> CheckErrorDisplay<'a> { + pub fn display<'a>(&'a self, theme: impl ETheme + 'static) -> CheckErrorDisplay<'a> { CheckErrorDisplay { e: self, theme: Rc::new(theme), @@ -419,13 +334,13 @@ impl CheckError { /// Like `display`, but doesn't use any theme (doesn't colorize its output) #[cfg(feature = "parse")] pub fn display_notheme<'a>(&'a self) -> CheckErrorDisplay<'a> { - self.display(NoTheme) + self.display(themes::NoTheme) } /// Like `display`, but uses the default terminal theme #[cfg(feature = "parse")] #[cfg(feature = "ecolor-term")] pub fn display_term<'a>(&'a self) -> CheckErrorDisplay<'a> { - self.display(TermDefaultTheme) + self.display(themes::TermDefaultTheme) } /// will, unless empty, end in a newline #[cfg(feature = "parse")] @@ -435,7 +350,7 @@ impl CheckError { cfg: &CheckErrorHRConfig, ) -> std::fmt::Result { const ADD_RIGHT_BITS: bool = false; - use crate::parsing::SourceFrom; + use crate::{parsing::SourceFrom, theme::ThemeTo}; let len = self.0.len(); for (i, component) in self.0.iter().enumerate() { diff --git a/mers_lib/src/errors/themes.rs b/mers_lib/src/errors/themes.rs new file mode 100644 index 0000000..f33ca58 --- /dev/null +++ b/mers_lib/src/errors/themes.rs @@ -0,0 +1,177 @@ +use super::{EColor, ThemeGen}; + +pub struct NoTheme; +impl ThemeGen for NoTheme { + type C = EColor; + type T = String; + fn color(&self, text: &str, _color: EColor, t: &mut String) { + self.nocolor(text, t); + } + fn nocolor(&self, text: &str, t: &mut String) { + t.push_str(text); + } +} + +/// converts an `EColor` to the color type you need for your theme. +/// This theme is optimized for ANSI terminal colors, +/// as most of mers' colored output will be printed to a terminal. +pub fn default_theme( + color: EColor, + gray: C, + red: C, + red_bright: C, + green: C, + green_bright: C, + yellow: C, + yellow_bright: C, + blue: C, + blue_bright: C, + magenta: C, + magenta_bright: C, + cyan: C, + cyan_bright: C, +) -> Option { + if let Indent(n) = color { + return Some(match n % 7 { + 1 => red, + 2 => green, + 3 => yellow, + 4 => blue, + 5 => magenta, + 6 => cyan, + _ => return None, + }); + } + let hard_err = red; + let type_right = blue; + let type_wrong = magenta; + let type_wrong_b = magenta_bright; + let function = blue_bright; // used in combination with TYPE_WRONG + let missing = cyan; + let runtime = yellow; + let runtime_b = yellow_bright; + let unused = green; + let unused_b = green_bright; + drop(red_bright); + drop(cyan_bright); + use EColor::*; + Some(match color { + Indent(_) => unreachable!(), + + // macros (#...) + WhitespaceAfterHashtag + | HashUnknown + | HashIncludeCantLoadFile + | HashIncludeNotAString + | HashIncludeErrorInIncludedFile + | StacktraceDescendHashInclude => hard_err, + + // -- bad syntax -- + UnknownVariable => hard_err, + BackslashEscapeUnknown => hard_err, + BackslashEscapeEOF | StringEOF | TypeEOF => missing, + BadCharInTupleType => hard_err, + BadCharInFunctionType => hard_err, + TryBadSyntax => hard_err, + TypeAnnotationNoClosingBracket | BracketedRefTypeNoClosingBracket => missing, + + BadTypeFromParsed => type_wrong_b, + + // -- type-errors -- + IfConditionNotBool => type_wrong, + TryNoFunctionFound => type_wrong_b, + TryNotAFunction => type_wrong, + TryUnusedFunction1 => unused, + TryUnusedFunction2 => unused_b, + + CustomTypeTestFailed => hard_err, + + ChainWithNonFunction => type_wrong, + + AssignTargetNonReference => type_wrong, + + Function => function, + FunctionArgument => type_wrong, + + InitFrom | AssignFrom | AsTypeStatementWithTooBroadType => type_wrong, + InitTo | AssignTo | AsTypeTypeAnnotation => type_right, + + // -- runtime-errors -- + StacktraceDescend => runtime, + MaximumRuntimeExceeded => runtime_b, + + InCodePositionLine => gray, + }) +} + +#[cfg(feature = "ecolor-term")] +pub struct TermDefaultTheme; +#[cfg(feature = "ecolor-term")] +impl ThemeGen for TermDefaultTheme { + type C = EColor; + type T = String; + fn color(&self, text: &str, color: EColor, t: &mut String) { + use colored::Color::*; + if let Some(color) = default_theme( + color, + BrightBlack, + Red, + BrightRed, + Green, + BrightGreen, + Yellow, + BrightYellow, + Blue, + BrightBlue, + Magenta, + BrightMagenta, + Cyan, + BrightCyan, + ) { + use colored::Colorize; + t.push_str(&text.color(color).to_string()); + } else { + self.nocolor(text, t) + } + } + fn nocolor(&self, text: &str, t: &mut String) { + t.push_str(text); + } +} + +#[cfg(feature = "ecolor-html")] +pub struct HtmlDefaultTheme; +#[cfg(feature = "ecolor-html")] +impl ThemeGen for HtmlDefaultTheme { + type C = EColor; + type T = String; + fn color(&self, text: &str, color: EColor, t: &mut String) { + if let Some(color) = default_theme( + color, + "Gray", + "Crimson", + "Red", + "Green", + "LimeGreen", + "Gold", + "Yellow", + "RoyalBlue", + "DeepSkyBlue", + "Purple", + "Orchid", + "DarkCyan", + "Turquoise", + ) { + t.push_str(""); + html_escape::encode_text_to_string(text, t); + t.push_str(""); + } else { + html_escape::encode_text_to_string(text, t); + } + } + fn nocolor(&self, text: &str, t: &mut String) { + t.push_str(text); + } +} diff --git a/mers_lib/src/lib.rs b/mers_lib/src/lib.rs index f717bb9..a7108c4 100755 --- a/mers_lib/src/lib.rs +++ b/mers_lib/src/lib.rs @@ -7,7 +7,10 @@ pub mod info; /// parser implementation. #[cfg(feature = "parse")] pub mod parsing; +#[cfg(feature = "pretty-print")] +pub mod pretty_print; pub mod program; +pub mod theme; #[cfg(feature = "parse")] pub mod prelude_compile { diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index 15d4b38..6805be8 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -308,6 +308,9 @@ impl Source { line } + pub fn get_pos_last_char(&self) -> SourcePos { + SourcePos(self.i.saturating_sub(1)) + } pub fn get_pos(&self) -> SourcePos { SourcePos(self.i) } diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 03a7d0a..71e2228 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -555,7 +555,7 @@ pub fn parse_string( srca: &Arc, double_quote: SourcePos, ) -> Result { - parse_string_custom_end(src, srca, double_quote, '"', '"') + parse_string_custom_end(src, srca, double_quote, '"', '"', "", EColor::StringEOF) } pub fn parse_string_custom_end( src: &mut Source, @@ -563,6 +563,8 @@ pub fn parse_string_custom_end( opening: SourcePos, opening_char: char, closing_char: char, + string_prefix: &str, + eof_color: EColor, ) -> Result { let mut s = String::new(); loop { @@ -602,13 +604,13 @@ pub fn parse_string_custom_end( return Err(CheckError::new() .src(vec![( (opening, src.get_pos(), srca).into(), - Some(EColor::StringEOF), + Some(eof_color), )]) .msg_str(format!( - "EOF in string literal{}", + "EOF in {string_prefix}string literal{}", if closing_char != '"' { format!( - "{opening_char}...{closing_char} (end string with '{closing_char}')" + " {opening_char}...{closing_char} (end string with '{closing_char}')" ) } else { String::new() diff --git a/mers_lib/src/parsing/types.rs b/mers_lib/src/parsing/types.rs index 9767e45..35ff7f9 100755 --- a/mers_lib/src/parsing/types.rs +++ b/mers_lib/src/parsing/types.rs @@ -24,6 +24,7 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result { + let pos_in_src = src.get_pos(); src.next_char(); if let Some('[') = src.peek_char() { src.next_char(); @@ -35,9 +36,14 @@ pub fn parse_single_type(src: &mut Source, srca: &Arc) -> Result) -> Result')?, + super::statements::parse_string_custom_end( + src, + srca, + pos, + '<', + '>', + "type-info ", + EColor::TypeEOF, + )?, ) } else { ParsedType::Type(t) } } - None => todo!(), + None => { + return Err(CheckError::new() + .src(vec![( + (src.get_pos_last_char(), src.get_pos(), srca).into(), + Some(EColor::TypeEOF), + )]) + .msg_str(format!("Expected type, got EOF"))) + } }) } diff --git a/mers_lib/src/pretty_print.rs b/mers_lib/src/pretty_print.rs new file mode 100644 index 0000000..1f678f8 --- /dev/null +++ b/mers_lib/src/pretty_print.rs @@ -0,0 +1,221 @@ +use std::{io::Write, process::exit, sync::Arc}; + +use crate::{ + prelude_compile::{parse, Source}, + theme::ThemeGen, +}; + +#[cfg(not(feature = "ecolor-term"))] +compile_error!("feature ecolor-term is required if pretty-print feature is enabled"); + +pub fn pretty_print(src: Source) { + pretty_print_to(src, &mut std::io::stdout(), DefaultTheme) +} + +pub fn pretty_print_to(mut src: Source, out: &mut O, theme: impl FTheme) { + let srca = Arc::new(src.clone()); + match parse(&mut src, &srca) { + Err(e) => { + eprintln!("{e:?}"); + exit(28); + } + Ok(parsed) => { + print_parsed(&srca, parsed.as_ref(), out, theme); + } + } +} + +pub enum AbstractColor { + Gray, + Green, + Red, + Blue, + Cyan, +} +pub fn map_color(color: FColor) -> Option { + Some(match color { + FColor::Comment => AbstractColor::Gray, + FColor::Variable => AbstractColor::Green, + FColor::VariableRef => AbstractColor::Green, + FColor::If => AbstractColor::Red, + FColor::IfWithElse => AbstractColor::Red, + FColor::Loop => AbstractColor::Red, + FColor::Tuple => AbstractColor::Blue, + FColor::Object => AbstractColor::Blue, + FColor::Value => AbstractColor::Cyan, + FColor::AsType => AbstractColor::Gray, + FColor::CustomType => AbstractColor::Gray, + FColor::Unknown => return None, + }) +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum FColor { + Comment, + Variable, + VariableRef, + If, + IfWithElse, + Loop, + Tuple, + Object, + Value, + AsType, + CustomType, + Unknown, +} + +pub struct DefaultTheme; +impl ThemeGen for DefaultTheme { + type C = FColor; + type T = std::io::Stdout; + fn color(&self, text: &str, color: Self::C, t: &mut Self::T) { + use colored::{Color, Colorize}; + if let Some(color) = map_color(color) { + let _ = write!( + t, + "{}", + text.color(match color { + AbstractColor::Gray => Color::BrightBlack, + AbstractColor::Green => Color::Green, + AbstractColor::Red => Color::Red, + AbstractColor::Blue => Color::Blue, + AbstractColor::Cyan => Color::Cyan, + }) + ); + } else { + let _ = write!(t, "{}", text); + } + } + fn nocolor(&self, text: &str, t: &mut Self::T) { + let _ = write!(t, "{}", text); + } +} + +// const FColor::Comment: Color = Color::BrightBlack; +// const FColor::Variable: Color = Color::Green; +// const FColor::Variable_Ref: Color = Color::Green; +// const FColor::If: Color = Color::Red; +// const FColor::If_With_Else: Color = Color::Red; +// const FColor::Loop: Color = Color::Red; +// const FColor::Tuple: Color = Color::Blue; +// const FColor::Object: Color = Color::Blue; +// const FColor::Value: Color = Color::Cyan; +// const FColor::As_Type: Color = Color::BrightBlack; +// const FColor::Custom_Type: Color = Color::BrightBlack; +// const FColor::Unknown: Color = Color::White; + +pub trait FTheme: ThemeGen {} +impl> FTheme for T {} + +fn print_parsed( + srca: &Arc, + parsed: &dyn crate::program::parsed::MersStatement, + out: &mut O, + theme: impl FTheme, +) { + let mut sections = vec![(FColor::Unknown, srca.src_og().len())]; + build_print(&mut sections, srca, parsed); + for (start, comment) in srca.comments() { + let end = start + comment.len(); + build_print_insert_color(&mut sections, FColor::Comment, *start, end); + } + let src = srca.src_og(); + let mut i = 0; + for (clr, end) in sections { + theme.color(&src[i..end], clr, out); + i = end; + } + let _ = writeln!(out); +} +fn build_print( + sections: &mut Vec<(FColor, usize)>, + srca: &Arc, + parsed: &dyn crate::program::parsed::MersStatement, +) { + let any = parsed.as_any(); + let clr = if let Some(v) = any.downcast_ref::() { + if v.is_ref { + FColor::VariableRef + } else { + FColor::Variable + } + } else if let Some(v) = any.downcast_ref::() { + if v.on_false.is_some() { + FColor::IfWithElse + } else { + FColor::If + } + } else if let Some(_) = any.downcast_ref::() { + FColor::Loop + } else if let Some(_) = any.downcast_ref::() { + FColor::Tuple + } else if let Some(_) = any.downcast_ref::() { + FColor::Object + } else if let Some(_) = any.downcast_ref::() { + FColor::Value + } else if let Some(_) = any.downcast_ref::() { + FColor::AsType + } else if let Some(_) = any.downcast_ref::() { + FColor::CustomType + } else { + FColor::Unknown + }; + let range = parsed.source_range(); + let start = srca.pos_in_og(range.start().pos(), true); + let end = srca.pos_in_og(range.end().pos(), false); + build_print_insert_color(sections, clr, start, end); + for s in parsed.inner_statements() { + build_print(sections, srca, s); + } +} + +fn build_print_insert_color( + sections: &mut Vec<(FColor, usize)>, + clr: FColor, + start: usize, + end: usize, +) { + let thing_at_my_start = sections.iter().position(|v| v.1 > start); + let thing_at_my_end = sections.iter().position(|v| v.1 > end); + if let Some(thing_at_my_start) = thing_at_my_start { + if let Some(thing_at_my_end) = thing_at_my_end { + if thing_at_my_start == thing_at_my_end { + let (around_color, around_end) = sections[thing_at_my_start]; + if around_color != clr { + sections[thing_at_my_start].1 = start; + sections.insert(thing_at_my_start + 1, (clr, end)); + sections.insert(thing_at_my_start + 2, (around_color, around_end)); + if sections[thing_at_my_start].1 + == thing_at_my_start + .checked_sub(1) + .map(|v| sections[v].1) + .unwrap_or(0) + { + // thing at my start now ends at the same place the thing before it ends, so we can remove it + sections.remove(thing_at_my_start); + } + } + } else { + sections[thing_at_my_start].1 = start; + sections.insert(thing_at_my_start + 1, (clr, end)); + for _ in 0..(thing_at_my_end.saturating_sub(thing_at_my_start + 1)) { + sections.remove(thing_at_my_start + 2); + } + } + } else { + if sections[thing_at_my_start].0 == clr { + sections[thing_at_my_start].1 = end; + } else { + sections[thing_at_my_start].1 = start; + sections.push((clr, end)); + } + } + } else { + if let Some(last) = sections.last_mut().filter(|v| v.0 == clr) { + last.1 = end; + } else { + sections.push((clr, end)); + } + } +} diff --git a/mers_lib/src/program/run/chain.rs b/mers_lib/src/program/run/chain.rs index 2edfe25..4591558 100755 --- a/mers_lib/src/program/run/chain.rs +++ b/mers_lib/src/program/run/chain.rs @@ -105,7 +105,12 @@ impl MersStatement for Chain { }), } } else { - todo!("err: not a function"); + Err(CheckError::new() + .msg_str("tried to chain with non-function".to_owned()) + .src(vec![( + self.chained.source_range(), + Some(EColor::ChainWithNonFunction), + )])) } } fn has_scope(&self) -> bool { diff --git a/mers_lib/src/theme.rs b/mers_lib/src/theme.rs new file mode 100644 index 0000000..2d18aec --- /dev/null +++ b/mers_lib/src/theme.rs @@ -0,0 +1,16 @@ +pub trait ThemeGen { + type C; + type T; + fn color(&self, text: &str, color: Self::C, t: &mut Self::T); + fn nocolor(&self, text: &str, t: &mut Self::T); +} +pub trait ThemeTo: ThemeGen { + fn color_to(&self, text: &str, color: ::C) -> ::T; +} +impl + ?Sized> ThemeTo for T { + fn color_to(&self, text: &str, color: ::C) -> String { + let mut t = String::new(); + self.color(text, color, &mut t); + t + } +}