From b5924b5da1c1c222a1e1efc2ef321c599d61a4be Mon Sep 17 00:00:00 2001 From: mark Date: Fri, 19 May 2023 21:25:05 +0200 Subject: [PATCH] -f+c will print the parsed script with colored highlighting --- amogus.mers | 3 +- mers/Cargo.lock | 7 ++ mers/Cargo.toml | 1 + mers/src/interactive_mode.rs | 2 + mers/src/lang/builtins.rs | 11 +- mers/src/lang/code_parsed.rs | 206 +++++++++++++++++++++++---------- mers/src/lang/code_runnable.rs | 1 - mers/src/lang/global_info.rs | 75 ++++++++++-- mers/src/lang/mod.rs | 1 + mers/src/lang/to_runnable.rs | 41 ++++--- mers/src/lang/val_data.rs | 58 +++++++--- mers/src/lang/val_type.rs | 57 +++++---- mers/src/main.rs | 26 +++-- mers/src/parsing/parse.rs | 148 +++++++++++------------ mers/src/tutor/mod.rs | 2 +- 15 files changed, 417 insertions(+), 222 deletions(-) diff --git a/amogus.mers b/amogus.mers index aae534a..cbf9ee6 100644 --- a/amogus.mers +++ b/amogus.mers @@ -20,8 +20,9 @@ for word text.regex("\\S+").assume_no_enum() if words > 0 { print(word + " ") words = words - 1 + sleep(0.1) } else if words == 0 { println(word) words = rnd() - sleep(0.75) + sleep(0.5) } diff --git a/mers/Cargo.lock b/mers/Cargo.lock index aa0be0d..e6e861a 100755 --- a/mers/Cargo.lock +++ b/mers/Cargo.lock @@ -159,6 +159,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorize" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc17e449bc7854c50b943d113a98bc0e01dc6585d2c66eaa09ca645ebd8a7e62" + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -643,6 +649,7 @@ dependencies = [ name = "mers" version = "0.1.0" dependencies = [ + "colorize", "edit", "notify", "nu-plugin", diff --git a/mers/Cargo.toml b/mers/Cargo.toml index f83e6d8..9c9269c 100755 --- a/mers/Cargo.toml +++ b/mers/Cargo.toml @@ -16,6 +16,7 @@ regex = "1.7.2" static_assertions = "1.1.0" nu-plugin = { version = "0.79.0", optional = true } nu-protocol = { version = "0.79.0", features = ["plugin"], optional = true } +colorize = "0.1.0" [features] # default = ["nushell_plugin"] diff --git a/mers/src/interactive_mode.rs b/mers/src/interactive_mode.rs index 6ab3a86..3ade619 100755 --- a/mers/src/interactive_mode.rs +++ b/mers/src/interactive_mode.rs @@ -7,6 +7,8 @@ pub mod fs_watcher { thread::{self, JoinHandle}, }; + use crate::lang::fmtgs::FormatGs; + #[derive(Debug)] pub struct Error(String); diff --git a/mers/src/lang/builtins.rs b/mers/src/lang/builtins.rs index 0790dab..fe05b1a 100755 --- a/mers/src/lang/builtins.rs +++ b/mers/src/lang/builtins.rs @@ -1,4 +1,5 @@ use std::{ + io::Write, path::PathBuf, sync::{Arc, Mutex}, time::Duration, @@ -835,9 +836,15 @@ impl BuiltinFunction { BuiltinFunction::Print => args[0].run(info).operate_on_data_immut(|v| { if let VDataEnum::String(arg) = v { #[cfg(not(feature = "nushell_plugin"))] - print!("{}", arg); + { + print!("{}", arg); + std::io::stdout().flush(); + } #[cfg(feature = "nushell_plugin")] - eprint!("{}", arg); + { + eprint!("{}", arg); + std::io::stderr().flush(); + } VDataEnum::Tuple(vec![]).to() } else { unreachable!("print function called with non-string arg") diff --git a/mers/src/lang/code_parsed.rs b/mers/src/lang/code_parsed.rs index eb2f66e..ce0799a 100755 --- a/mers/src/lang/code_parsed.rs +++ b/mers/src/lang/code_parsed.rs @@ -1,6 +1,11 @@ use std::fmt::{self, Display, Formatter, Pointer}; -use super::{code_macro::Macro, global_info::GlobalScriptInfo, val_data::VData, val_type::VType}; +use crate::lang::global_info::ColorFormatMode; + +use super::{ + code_macro::Macro, fmtgs::FormatGs, global_info::GlobalScriptInfo, val_data::VData, + val_type::VType, +}; #[derive(Debug)] pub enum SStatementEnum { @@ -82,101 +87,142 @@ impl SFunction { // -impl SStatementEnum { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for SStatementEnum { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { match self { - Self::Value(v) => v.fmtgs(f, info), + Self::Value(v) => v.fmtgs(f, info, form, file), Self::Tuple(v) => { - write!(f, "[")?; + write!(f, "{}", form.open_bracket(info, "[".to_owned()))?; for (i, v) in v.iter().enumerate() { if i > 0 { write!(f, " ")?; } - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; } - write!(f, "]") + write!(f, "{}", form.close_bracket(info, "]".to_owned())) } Self::List(v) => { - write!(f, "[")?; + write!(f, "{}", form.open_bracket(info, "[".to_owned()))?; for (i, v) in v.iter().enumerate() { - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; write!(f, " ")?; } - write!(f, "...]") + write!(f, "{}", form.close_bracket(info, "...]".to_owned())) } Self::Variable(var, reference) => { if *reference { - write!(f, "&{var}") - } else { - write!(f, "{var}") + write!(f, "{}", form.variable_ref_symbol(info, "&".to_owned()))?; } + write!(f, "{}", form.variable(info, var.to_owned())) } Self::FunctionCall(func, args) => { - write!(f, "{func}(")?; - for arg in args { - arg.fmtgs(f, info)?; + write!( + f, + "{}{}", + form.fncall(info, func.to_owned()), + form.open_bracket(info, "(".to_owned()) + )?; + for (i, arg) in args.iter().enumerate() { + if i != 0 { + write!(f, " "); + } + arg.fmtgs(f, info, form, file)?; } - write!(f, ")") + write!(f, "{}", form.close_bracket(info, ")".to_owned())) } Self::FunctionDefinition(name, func) => { if let Some(name) = name { - write!(f, "{name}")?; + write!( + f, + "{} {}", + form.fndef_fn(info, "fn".to_owned()), + form.fndef_name(info, name.to_owned()) + )?; } - func.fmtgs(f, info) + func.fmtgs(f, info, form, file) } - Self::Block(b) => b.fmtgs(f, info), + Self::Block(b) => b.fmtgs(f, info, form, file), Self::If(condition, yes, no) => { - write!(f, "if ")?; - condition.fmtgs(f, info)?; + write!(f, "{} ", form.if_if(info, "if".to_owned()))?; + condition.fmtgs(f, info, form, file)?; write!(f, " ")?; - yes.fmtgs(f, info)?; + yes.fmtgs(f, info, form, file)?; if let Some(no) = no { - write!(f, " else ")?; - no.fmtgs(f, info)?; + write!(f, " {} ", form.if_else(info, "else".to_owned()))?; + no.fmtgs(f, info, form, file)?; } Ok(()) } Self::Loop(b) => { - write!(f, "loop ")?; - b.fmtgs(f, info) + write!(f, "{} ", form.loop_loop(info, "loop".to_owned()))?; + b.fmtgs(f, info, form, file) } Self::For(var, i, b) => { - write!(f, "for {} ", var)?; - i.fmtgs(f, info)?; + write!(f, "{} {} ", form.loop_for(info, "for".to_owned()), var)?; + i.fmtgs(f, info, form, file)?; write!(f, " ")?; - b.fmtgs(f, info) + b.fmtgs(f, info, form, file) } Self::Switch(var, arms, force) => { if *force { - writeln!(f, "switch! {var} {{")?; + writeln!( + f, + "{} {var} {}", + form.kw_switch(info, "switch!".to_owned()), + form.open_bracket(info, "{".to_owned()) + )?; } else { - writeln!(f, "switch {var} {{")?; + writeln!( + f, + "{} {var} {}", + form.kw_switch(info, "switch".to_owned()), + form.open_bracket(info, "{".to_owned()) + )?; } + form.go_deeper(); for (t, action) in arms { - t.fmtgs(f, info)?; + write!(f, "{}", form.line_prefix())?; + t.fmtgs(f, info, form, file)?; write!(f, " ")?; - action.fmtgs(f, info)?; + action.fmtgs(f, info, form, file)?; writeln!(f)?; } - write!(f, "}}") + form.go_shallower(); + write!(f, "{}", form.line_prefix())?; + write!(f, "{}", form.close_bracket(info, "}".to_owned())) } Self::Match(var, arms) => { - write!(f, "match {var} {{")?; + write!( + f, + "{} {var} {}", + form.kw_match(info, "match".to_owned()), + form.open_bracket(info, "{".to_owned()) + )?; + form.go_deeper(); for (condition, action) in arms { - condition.fmtgs(f, info)?; + write!(f, "{}", form.line_prefix())?; + condition.fmtgs(f, info, form, file)?; write!(f, " ")?; - action.fmtgs(f, info)?; + action.fmtgs(f, info, form, file)?; writeln!(f)?; } - write!(f, "}}") + form.go_shallower(); + write!(f, "{}", form.line_prefix())?; + write!(f, "{}", form.close_bracket(info, "}".to_owned())) } Self::IndexFixed(statement, index) => { - statement.fmtgs(f, info)?; + statement.fmtgs(f, info, form, file)?; write!(f, ".{index}") } Self::EnumVariant(variant, inner) => { write!(f, "{variant}: ")?; - inner.fmtgs(f, info) + inner.fmtgs(f, info, form, file) } Self::TypeDefinition(name, t) => write!(f, "type {name} {t}"), Self::Macro(m) => { @@ -187,32 +233,58 @@ impl SStatementEnum { } impl Display for SStatementEnum { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.fmtgs(f, None) + self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None) } } -impl SStatement { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for SStatement { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { if let Some((opt, derefs)) = &self.output_to { - if let Some(forced_type) = &self.force_output_type { - write!(f, "{}{}::", "*".repeat(*derefs), opt)?; - forced_type.fmtgs(f, info)?; - write!(f, " = ")?; - } else { - write!(f, "{}{} = ", "*".repeat(*derefs), opt)?; + // TODO! + match opt.statement.as_ref() { + SStatementEnum::Variable(name, is_ref) => { + let derefs = if !is_ref { *derefs + 1 } else { *derefs }; + write!( + f, + "{}{} = ", + "*".repeat(derefs), + SStatementEnum::Variable(name.to_owned(), false).with(info, file) + )?; + } + _ => { + if let Some(forced_type) = &self.force_output_type { + write!(f, "{}{}::", "*".repeat(*derefs), opt.with(info, file))?; + forced_type.fmtgs(f, info, form, file)?; + write!(f, " = ")?; + } else { + write!(f, "{}{} = ", "*".repeat(*derefs), opt.with(info, file))?; + } + } } } - self.statement.fmtgs(f, info) + self.statement.fmtgs(f, info, form, file) } } impl Display for SStatement { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.fmtgs(f, None) + self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None) } } -impl SFunction { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for SFunction { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { write!(f, "(")?; for (i, (name, t)) in self.inputs.iter().enumerate() { if i > 0 { @@ -220,25 +292,35 @@ impl SFunction { } else { write!(f, "{name} ")?; } - t.fmtgs(f, info)?; + t.fmtgs(f, info, form, file)?; } write!(f, ") ")?; - self.block.fmtgs(f, info) + self.block.fmtgs(f, info, form, file) } } -impl SBlock { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for SBlock { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { match self.statements.len() { 0 => write!(f, "{{}}"), - 1 => self.statements[0].fmtgs(f, info), + // 1 => self.statements[0].fmtgs(f, info, form, file), _ => { - writeln!(f, "{{")?; + writeln!(f, "{}", form.open_bracket(info, "{".to_owned()))?; + form.go_deeper(); for statement in self.statements.iter() { - statement.fmtgs(f, info)?; + write!(f, "{}", form.line_prefix())?; + statement.fmtgs(f, info, form, file)?; writeln!(f)?; } - write!(f, "}}") + form.go_shallower(); + write!(f, "{}", form.line_prefix())?; + write!(f, "{}", form.close_bracket(info, "}".to_owned())) } } } diff --git a/mers/src/lang/code_runnable.rs b/mers/src/lang/code_runnable.rs index b9b5072..5559a3c 100755 --- a/mers/src/lang/code_runnable.rs +++ b/mers/src/lang/code_runnable.rs @@ -373,7 +373,6 @@ impl RStatementEnum { } } -#[derive(Debug)] pub struct RScript { main: RFunction, info: GSInfo, diff --git a/mers/src/lang/global_info.rs b/mers/src/lang/global_info.rs index b77f2bf..d35b608 100755 --- a/mers/src/lang/global_info.rs +++ b/mers/src/lang/global_info.rs @@ -1,5 +1,6 @@ use std::{ collections::HashMap, + default, fmt::Display, sync::{Arc, Mutex}, }; @@ -8,13 +9,13 @@ use crate::libs; use super::{ builtins, + fmtgs::Color, val_data::VDataEnum, val_type::{VSingleType, VType}, }; pub type GSInfo = Arc; -#[derive(Debug)] pub struct GlobalScriptInfo { pub libs: Vec, pub lib_fns: HashMap, @@ -24,10 +25,61 @@ pub struct GlobalScriptInfo { pub custom_type_names: HashMap, pub custom_types: Vec, - #[cfg(debug_assertions)] + pub formatter: ColorFormatter, + pub log: Logger, } +pub struct ColorFormatter { + pub mode: ColorFormatMode, + pub bracket_colors: Vec, + pub value_string_quotes_color: Color, + pub value_string_content_color: Color, + pub keyword_if_color: Color, + pub keyword_else_color: Color, + pub keyword_loop_color: Color, + pub keyword_for_color: Color, + pub keyword_switch_color: Color, + pub keyword_match_color: Color, + pub function_call_color: Color, + pub function_def_fn_color: Color, + pub function_def_name_color: Color, + pub variable_color: Color, +} +impl Default for ColorFormatter { + fn default() -> Self { + Self { + mode: ColorFormatMode::Plain, + bracket_colors: vec![ + Color::Red, + Color::Yellow, + Color::Cyan, + Color::Blue, + Color::Magenta, + ], + value_string_quotes_color: Color::Grey, + value_string_content_color: Color::Cyan, + keyword_if_color: Color::Yellow, + keyword_else_color: Color::Yellow, + keyword_loop_color: Color::Yellow, + keyword_for_color: Color::Yellow, + keyword_switch_color: Color::Yellow, + keyword_match_color: Color::Yellow, + function_call_color: Color::Magenta, + function_def_fn_color: Color::Blue, + function_def_name_color: Color::Magenta, + variable_color: Color::Green, + } + } +} +#[derive(Debug)] +pub enum ColorFormatMode { + /// No color. + Plain, + /// For terminal output + Colorize, +} + impl GlobalScriptInfo { pub fn to_arc(self) -> GSInfo { Arc::new(self) @@ -42,7 +94,7 @@ impl Default for GlobalScriptInfo { enum_variants: Self::default_enum_variants(), custom_type_names: HashMap::new(), custom_types: vec![], - #[cfg(debug_assertions)] + formatter: Default::default(), log: Logger::new(), } } @@ -57,20 +109,21 @@ impl GlobalScriptInfo { } } -#[cfg(debug_assertions)] #[derive(Debug)] pub struct Logger { logs: Arc>>, + pub after_parse: LogKind, + pub vdata_clone: LogKind, pub vtype_fits_in: LogKind, pub vsingletype_fits_in: LogKind, } -#[cfg(debug_assertions)] impl Logger { pub fn new() -> Self { Self { logs: Arc::new(Mutex::new(vec![])), + after_parse: Default::default(), vdata_clone: Default::default(), vtype_fits_in: Default::default(), vsingletype_fits_in: Default::default(), @@ -78,17 +131,17 @@ impl Logger { } } -#[cfg(debug_assertions)] #[derive(Debug)] pub enum LogMsg { + AfterParse(String), VDataClone(Option, VDataEnum, usize, usize), VTypeFitsIn(VType, VType, Vec), VSingleTypeFitsIn(VSingleType, VSingleType, bool), } -#[cfg(debug_assertions)] impl Logger { pub fn log(&self, msg: LogMsg) { let kind = match msg { + LogMsg::AfterParse(..) => &self.after_parse, LogMsg::VDataClone(..) => &self.vdata_clone, LogMsg::VTypeFitsIn(..) => &self.vtype_fits_in, LogMsg::VSingleTypeFitsIn(..) => &self.vsingletype_fits_in, @@ -103,14 +156,16 @@ impl Logger { } } } -#[cfg(debug_assertions)] impl Display for LogMsg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Self::AfterParse(code) => { + write!(f, "AfterParse :: {code}") + } Self::VDataClone(varname, data, src_addr, new_addr) => { write!( f, - "VDataClone :: {data} ({}{src_addr} -> {new_addr})", + "VDataClone ::::\n{data} ({}{src_addr} -> {new_addr})", if let Some(v) = varname { format!("{v} | ") } else { @@ -126,13 +181,11 @@ impl Display for LogMsg { } } -#[cfg(debug_assertions)] #[derive(Clone, Debug, Default)] pub struct LogKind { pub stderr: bool, pub log: bool, } -#[cfg(debug_assertions)] impl LogKind { pub fn log(&self) -> bool { self.stderr || self.log diff --git a/mers/src/lang/mod.rs b/mers/src/lang/mod.rs index 6dad008..919cf4a 100644 --- a/mers/src/lang/mod.rs +++ b/mers/src/lang/mod.rs @@ -2,6 +2,7 @@ pub mod builtins; pub mod code_macro; pub mod code_parsed; pub mod code_runnable; +pub mod fmtgs; pub mod global_info; pub mod to_runnable; pub mod val_data; diff --git a/mers/src/lang/to_runnable.rs b/mers/src/lang/to_runnable.rs index 4f02c73..0e906a6 100755 --- a/mers/src/lang/to_runnable.rs +++ b/mers/src/lang/to_runnable.rs @@ -19,6 +19,7 @@ use super::{ code_macro::Macro, code_parsed::{SBlock, SFunction, SStatement, SStatementEnum}, code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, + fmtgs::FormatGs, global_info::GSInfo, }; @@ -51,18 +52,20 @@ impl Debug for ToRunnableError { } } // TODO: -// - Don't use {} to format, use .fmtgs(f, info) instead! +// - Don't use {} to format, use .fmtgs(f, info, form, file) instead! // - Show location in code where the error was found impl Display for ToRunnableError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmtgs(f, None) + self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None) } } -impl ToRunnableError { - pub fn fmtgs( +impl FormatGs for ToRunnableError { + fn fmtgs( &self, f: &mut std::fmt::Formatter, info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, ) -> std::fmt::Result { match self { Self::MainWrongInput => write!( @@ -75,9 +78,9 @@ impl ToRunnableError { Self::CannotDeclareVariableWithDereference(v) => write!(f, "Cannot declare a variable and dereference it (variable '{v}')."), Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => { write!(f, "Cannot dereference type ")?; - og_type.fmtgs(f, info)?; + og_type.fmtgs(f, info, form, file)?; write!(f, " {derefs_wanted} times (stopped at ")?; - last_valid_type.fmtgs(f, info); + last_valid_type.fmtgs(f, info, form, file); write!(f, ")")?; Ok(()) }, @@ -89,28 +92,28 @@ impl ToRunnableError { problematic, } => { write!(f, "Invalid type: Expected ")?; - expected.fmtgs(f, info)?; + expected.fmtgs(f, info, form, file)?; write!(f, " but found ")?; - found.fmtgs(f, info)?; + found.fmtgs(f, info, form, file)?; write!(f, ", which includes ")?; - problematic.fmtgs(f, info)?; + problematic.fmtgs(f, info, form, file)?; write!(f, " which is not covered.")?; Ok(()) } Self::CaseForceButTypeNotCovered(v) => { write!(f, "Switch! statement, but not all types covered. Types to cover: ")?; - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; Ok(()) } Self::MatchConditionInvalidReturn(v) => { write!(f, "match statement condition returned ")?; - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; write!(f, ", which is not necessarily a tuple of size 0 to 1.")?; Ok(()) } Self::NotIndexableFixed(t, i) => { write!(f, "Cannot use fixed-index {i} on type ")?; - t.fmtgs(f, info)?; + t.fmtgs(f, info, form, file)?; write!(f, ".")?; Ok(()) } @@ -118,7 +121,7 @@ impl ToRunnableError { write!(f, "Wrong arguments for builtin function \"{}\":", builtin_name)?; for arg in args { write!(f, " ")?; - arg.fmtgs(f, info)?; + arg.fmtgs(f, info, form, file)?; } write!(f, ".") } @@ -126,15 +129,15 @@ impl ToRunnableError { write!(f, "Wrong arguments for library function {}:", name)?; for arg in args { write!(f, " ")?; - arg.fmtgs(f, info)?; + arg.fmtgs(f, info, form, file)?; } write!(f, ".") } Self::CannotAssignTo(val, target) => { write!(f, "Cannot assign type ")?; - val.fmtgs(f, info)?; + val.fmtgs(f, info, form, file)?; write!(f, " to ")?; - target.fmtgs(f, info)?; + target.fmtgs(f, info, form, file)?; write!(f, ".")?; Ok(()) }, @@ -143,11 +146,11 @@ impl ToRunnableError { } Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => { write!(f, "the statement requires its output type to be ")?; - required.fmtgs(f, info)?; + required.fmtgs(f, info, form, file)?; write!(f, ", but its real output type is ")?; - real.fmtgs(f, info)?; + real.fmtgs(f, info, form, file)?; write!(f, ", which doesn't fit in the required type because of the problematic types ")?; - problematic.fmtgs(f, info)?; + problematic.fmtgs(f, info, form, file)?; write!(f, ".")?; Ok(()) } diff --git a/mers/src/lang/val_data.rs b/mers/src/lang/val_data.rs index 7faeb42..3191670 100755 --- a/mers/src/lang/val_data.rs +++ b/mers/src/lang/val_data.rs @@ -4,15 +4,14 @@ use std::{ sync::{Arc, Mutex, MutexGuard}, }; +use super::global_info::LogMsg; use super::{ code_runnable::RFunction, + fmtgs::FormatGs, global_info::{GSInfo, GlobalScriptInfo}, val_type::{VSingleType, VType}, }; -#[cfg(debug_assertions)] -use super::global_info::LogMsg; - #[derive(Debug)] pub enum VDataEnum { Bool(bool), @@ -117,11 +116,13 @@ impl VData { // *self doesn't modify the ::Data, it instead points the value that wraps it to a new ::Data, leaving the old one as it was. // for proof: data is untouched, only the new_data is ever modified. let new_vdata = VDataInner::Data(0, new_data).to(); - #[cfg(debug_assertions)] if info.log.vdata_clone.log() { drop(lock); info.log.log(LogMsg::VDataClone( + #[cfg(debug_assertions)] self.1.clone(), + #[cfg(not(debug_assertions))] + None, self.inner_cloned(), Arc::as_ptr(&self.0) as usize, Arc::as_ptr(&new_vdata.0) as usize, @@ -187,9 +188,15 @@ impl Clone for VData { self.clone_data() } } -impl VData { - pub fn fmtgs(&self, f: &mut Formatter<'_>, info: Option<&GlobalScriptInfo>) -> fmt::Result { - self.operate_on_data_immut(|v| v.fmtgs(f, info)) +impl FormatGs for VData { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { + self.operate_on_data_immut(|v| v.fmtgs(f, info, form, file)) } } impl Debug for VData { @@ -476,7 +483,12 @@ pub mod thread { pub struct VDataWInfo(VData, GSInfo); impl Display for VDataWInfo { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.0.fmtgs(f, Some(&self.1)) + self.0.fmtgs( + f, + Some(&self.1), + &mut super::fmtgs::FormatInfo::default(), + None, + ) } } impl VData { @@ -485,39 +497,51 @@ impl VData { } } -impl VDataEnum { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for VDataEnum { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { match self { Self::Bool(true) => write!(f, "true"), Self::Bool(false) => write!(f, "false"), Self::Int(v) => write!(f, "{v}"), Self::Float(v) => write!(f, "{v}"), - Self::String(v) => write!(f, "\"{v}\""), + Self::String(v) => write!( + f, + "{}{}{}", + form.value_string_quotes(info, "\"".to_owned()), + form.value_string_content(info, v.to_owned()), + form.value_string_quotes(info, "\"".to_owned()) + ), Self::Tuple(v) => { write!(f, "[")?; for (i, v) in v.iter().enumerate() { if i > 0 { write!(f, " ")?; } - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; } write!(f, "]") } Self::List(_t, v) => { write!(f, "[")?; for (i, v) in v.iter().enumerate() { - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; write!(f, " ")?; } write!(f, "...]") } Self::Function(func) => { - VSingleType::Function(func.input_output_map.clone()).fmtgs(f, info) + VSingleType::Function(func.input_output_map.clone()).fmtgs(f, info, form, file) } Self::Thread(..) => write!(f, "[TODO] THREAD"), Self::Reference(inner) => { write!(f, "&")?; - inner.fmtgs(f, info) + inner.fmtgs(f, info, form, file) } Self::EnumVariant(variant, inner) => { if let Some(name) = if let Some(info) = info { @@ -535,13 +559,13 @@ impl VDataEnum { } else { write!(f, "{variant}: ")?; } - inner.fmtgs(f, info) + inner.fmtgs(f, info, form, file) } } } } impl Display for VDataEnum { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.fmtgs(f, None) + self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None) } } diff --git a/mers/src/lang/val_type.rs b/mers/src/lang/val_type.rs index 61d3ebd..b6ce24a 100755 --- a/mers/src/lang/val_type.rs +++ b/mers/src/lang/val_type.rs @@ -4,9 +4,11 @@ use std::{ ops::BitOr, }; -use super::global_info::{self, GSInfo, GlobalScriptInfo}; +use super::{ + fmtgs::FormatGs, + global_info::{self, GSInfo, GlobalScriptInfo}, +}; -#[cfg(debug_assertions)] use super::global_info::LogMsg; #[derive(Clone, Debug, PartialEq, Eq)] @@ -161,7 +163,6 @@ impl VType { no.push(t.clone()) } } - #[cfg(debug_assertions)] if info.log.vtype_fits_in.log() { info.log .log(LogMsg::VTypeFitsIn(self.clone(), rhs.clone(), no.clone())) @@ -359,7 +360,6 @@ impl VSingleType { (Self::Thread(a), Self::Thread(b)) => a.fits_in(b, info).is_empty(), (Self::Thread(..), _) => false, }; - #[cfg(debug_assertions)] if info.log.vsingletype_fits_in.log() { info.log .log(LogMsg::VSingleTypeFitsIn(self.clone(), rhs.clone(), o)); @@ -393,7 +393,12 @@ impl Into for VSingleType { pub struct VTypeWInfo(VType, GSInfo); impl Display for VTypeWInfo { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.0.fmtgs(f, Some(&self.1)) + self.0.fmtgs( + f, + Some(&self.1), + &mut super::fmtgs::FormatInfo::default(), + None, + ) } } impl VType { @@ -402,8 +407,14 @@ impl VType { } } -impl VSingleType { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for VSingleType { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { match self { Self::Bool => write!(f, "bool"), Self::Int => write!(f, "int"), @@ -415,13 +426,13 @@ impl VSingleType { if i > 0 { write!(f, " ")?; } - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; } write!(f, "]") } Self::List(v) => { write!(f, "[")?; - v.fmtgs(f, info)?; + v.fmtgs(f, info, form, file)?; write!(f, " ...]") } Self::Function(func) => { @@ -429,22 +440,22 @@ impl VSingleType { for (inputs, output) in func { write!(f, "(")?; for i in inputs { - i.fmtgs(f, info)?; + i.fmtgs(f, info, form, file)?; write!(f, " "); } - output.fmtgs(f, info)?; + output.fmtgs(f, info, form, file)?; write!(f, ")")?; } write!(f, ")") } Self::Thread(out) => { write!(f, "thread(")?; - out.fmtgs(f, info)?; + out.fmtgs(f, info, form, file)?; write!(f, ")") } Self::Reference(inner) => { write!(f, "&")?; - inner.fmtgs(f, info) + inner.fmtgs(f, info, form, file) } Self::EnumVariant(variant, inner) => { if let Some(name) = if let Some(info) = info { @@ -462,12 +473,12 @@ impl VSingleType { } else { write!(f, "{variant}(")?; } - inner.fmtgs(f, info)?; + inner.fmtgs(f, info, form, file)?; write!(f, ")") } Self::EnumVariantS(name, inner) => { write!(f, "{name}(")?; - inner.fmtgs(f, info)?; + inner.fmtgs(f, info, form, file)?; write!(f, ")") } Self::CustomType(t) => { @@ -510,23 +521,29 @@ impl VSingleType { } impl Display for VSingleType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.fmtgs(f, None) + self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None) } } -impl VType { - pub fn fmtgs(&self, f: &mut Formatter, info: Option<&GlobalScriptInfo>) -> fmt::Result { +impl FormatGs for VType { + fn fmtgs( + &self, + f: &mut Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut super::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { for (i, t) in self.types.iter().enumerate() { if i > 0 { write!(f, "/")?; } - t.fmtgs(f, info)?; + t.fmtgs(f, info, form, file)?; } Ok(()) } } impl Display for VType { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.fmtgs(f, None) + self.fmtgs(f, None, &mut super::fmtgs::FormatInfo::default(), None) } } diff --git a/mers/src/main.rs b/mers/src/main.rs index 41650aa..908426d 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -3,11 +3,13 @@ use std::{fs, time::Instant}; +use lang::global_info::ColorFormatMode; use lang::global_info::GlobalScriptInfo; -#[cfg(debug_assertions)] use lang::global_info::LogKind; use notify::Watcher as FsWatcher; +use crate::lang::fmtgs::FormatGs; + mod interactive_mode; mod lang; mod libs; @@ -26,6 +28,7 @@ fn main() { fn normal_main() { let args: Vec<_> = std::env::args().skip(1).collect(); let mut info = GlobalScriptInfo::default(); + let mut run = true; let mut args_to_skip = 2; let mut file = match args.len() { 0 => { @@ -52,6 +55,10 @@ fn normal_main() { match ch { 'e' => execute = true, 'v' => verbose = true, + 'f' => { + run = false; + info.log.after_parse.stderr = true; + } 'V' => print_version = true, 'i' => interactive += 1, 't' => teachme = true, @@ -75,6 +82,11 @@ fn normal_main() { verbose_args.push(ch); } } + 'f' => match ch { + 'c' => info.formatter.mode = ColorFormatMode::Colorize, + 'C' => info.formatter.mode = ColorFormatMode::Plain, + _ => eprintln!("Ignoring f+{ch}. (unknown adv char)"), + }, _ => (), } } else { @@ -97,9 +109,6 @@ fn normal_main() { return; } if verbose { - #[cfg(not(debug_assertions))] - eprintln!("WARN: Verbose (-v) only works in debug builds!"); - #[cfg(debug_assertions)] if verbose_args.is_empty() { fn f() -> LogKind { LogKind { @@ -187,12 +196,9 @@ fn normal_main() { }; match parsing::parse::parse_custom_info(&mut file, info) { Ok(script) => { - println!(" - - - - -"); - let start = Instant::now(); - let out = script.run(std::env::args().skip(args_to_skip).collect()); - let elapsed = start.elapsed(); - println!(" - - - - -"); - println!("Output ({}s)\n{out}", elapsed.as_secs_f64()); + if run { + script.run(std::env::args().skip(args_to_skip).collect()); + } } Err(e) => { println!("Couldn't compile:\n{}", e.with_file(&file)); diff --git a/mers/src/parsing/parse.rs b/mers/src/parsing/parse.rs index ce5f3f5..203275f 100755 --- a/mers/src/parsing/parse.rs +++ b/mers/src/parsing/parse.rs @@ -5,6 +5,7 @@ use crate::{ code_macro::MacroError, code_parsed::*, code_runnable::RScript, + fmtgs::{FormatGs, FormatWithGs}, global_info::{GSInfo, GlobalScriptInfo}, to_runnable::{self, ToRunnableError}, val_data::VDataEnum, @@ -13,6 +14,8 @@ use crate::{ libs, }; +use crate::lang::global_info::LogMsg; + use super::file::File; pub enum ScriptError { @@ -51,59 +54,30 @@ impl std::fmt::Display for ScriptError { } } } -pub struct ScriptErrorWithFile<'a>(&'a ScriptError, &'a File); -pub struct ScriptErrorWithInfo<'a>(&'a ScriptError, &'a GlobalScriptInfo); -pub struct ScriptErrorWithFileAndInfo<'a>(&'a ScriptError, &'a File, &'a GlobalScriptInfo); -impl<'a> ScriptError { - pub fn with_file(&'a self, file: &'a File) -> ScriptErrorWithFile { - ScriptErrorWithFile(self, file) - } - pub fn with_gsinfo(&'a self, info: &'a GlobalScriptInfo) -> ScriptErrorWithInfo { - ScriptErrorWithInfo(self, info) - } - pub fn with_file_and_gsinfo( - &'a self, - file: &'a File, - info: &'a GlobalScriptInfo, - ) -> ScriptErrorWithFileAndInfo { - ScriptErrorWithFileAndInfo(self, file, info) - } -} -impl<'a> std::fmt::Display for ScriptErrorWithFile<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt_custom(f, Some(self.1), None) - } -} -impl<'a> std::fmt::Display for ScriptErrorWithInfo<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt_custom(f, None, Some(self.1)) - } -} -impl<'a> std::fmt::Display for ScriptErrorWithFileAndInfo<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt_custom(f, Some(self.1), Some(self.2)) - } -} - -impl ScriptError { - fn fmt_custom( +impl FormatGs for ScriptError { + fn fmtgs( &self, - f: &mut std::fmt::Formatter<'_>, - file: Option<&File>, + f: &mut std::fmt::Formatter, info: Option<&GlobalScriptInfo>, + form: &mut crate::lang::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, ) -> std::fmt::Result { match &self { ScriptError::CannotFindPathForLibrary(e) => write!(f, "{e}"), ScriptError::ParseError(e) => { write!(f, "failed while parsing: ")?; - e.fmt_custom(f, file)?; + e.fmtgs(f, info, form, file)?; Ok(()) } ScriptError::UnableToLoadLibrary(e) => write!(f, "{e}"), ScriptError::ToRunnableError(e) => { write!(f, "failed to compile: ")?; - e.fmtgs(f, info); - Ok(()) + e.fmtgs( + f, + info, + &mut crate::lang::fmtgs::FormatInfo::default(), + file, + ) } } } @@ -123,14 +97,25 @@ impl From<(ScriptError, GSInfo)> for Error { } } } -impl Debug for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.err.with_gsinfo(&self.ginfo)) +impl FormatGs for Error { + fn fmtgs( + &self, + f: &mut std::fmt::Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut crate::lang::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, + ) -> std::fmt::Result { + self.err.fmtgs(f, Some(&self.ginfo), form, file) } } -impl Error { - pub fn with_file<'a>(&'a self, file: &'a File) -> ScriptErrorWithFileAndInfo<'a> { - self.err.with_file_and_gsinfo(file, self.ginfo.as_ref()) +impl Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.err.fmtgs( + f, + Some(self.ginfo.as_ref()), + &mut crate::lang::fmtgs::FormatInfo::default(), + None, + ) } } @@ -140,19 +125,16 @@ pub fn parse(file: &mut File) -> Result { } /// like parse, but GlobalInfo can be something other than Default::default(). pub fn parse_custom_info(file: &mut File, mut ginfo: GlobalScriptInfo) -> Result { - let libs = match parse_step_lib_paths(file) { + let libs = match parse_step_lib_paths(file, &ginfo) { Ok(v) => v, Err(e) => return Err((e.into(), ginfo.to_arc()).into()), }; - let func = match parse_step_interpret(file) { + let func = match parse_step_interpret(file, &ginfo) { Ok(v) => v, Err(e) => return Err((e.into(), ginfo.to_arc()).into()), }; - #[cfg(debug_assertions)] - eprintln!("{func:#?}"); - ginfo.libs = match parse_step_libs_load(libs, &mut ginfo) { Ok(v) => v, Err(e) => return Err((e.into(), ginfo.to_arc()).into()), @@ -174,7 +156,10 @@ impl std::fmt::Display for CannotFindPathForLibrary { write!(f, "Couldn't find a path for the library with the path '{}'. Maybe set the MERS_LIB_DIR env variable?", self.0) } } -pub fn parse_step_lib_paths(file: &mut File) -> Result, CannotFindPathForLibrary> { +pub fn parse_step_lib_paths( + file: &mut File, + ginfo: &GlobalScriptInfo, +) -> Result, CannotFindPathForLibrary> { let mut libs = vec![]; loop { file.skip_whitespaces(); @@ -198,14 +183,23 @@ pub fn parse_step_lib_paths(file: &mut File) -> Result, CannotFindP Ok(libs) } -pub fn parse_step_interpret(file: &mut File) -> Result { - Ok(SFunction::new( +pub fn parse_step_interpret( + file: &mut File, + ginfo: &GlobalScriptInfo, +) -> Result { + let o = SFunction::new( vec![( "args".to_string(), VSingleType::List(VSingleType::String.into()).to(), )], parse_block_advanced(file, Some(false), true, true, false)?, - )) + ); + if ginfo.log.after_parse.log() { + ginfo.log.log(LogMsg::AfterParse( + o.with_info_and_file(ginfo, &file).to_string(), + )); + } + Ok(o) } #[derive(Debug)] @@ -242,17 +236,6 @@ pub fn parse_step_compile( to_runnable::to_runnable(main_func, ginfo) } -pub struct ParseErrorWithFile<'a>(&'a ParseError, &'a File); -impl<'a> ParseError { - pub fn with_file(&'a self, file: &'a File) -> ParseErrorWithFile { - ParseErrorWithFile(self, file) - } -} -impl<'a> std::fmt::Display for ParseErrorWithFile<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt_custom(f, Some(self.1)) - } -} pub struct ParseError { err: ParseErrors, // the location of the error @@ -264,13 +247,15 @@ pub struct ParseError { )>, info: Option, } -impl ParseError { - pub fn fmt_custom( +impl FormatGs for ParseError { + fn fmtgs( &self, - f: &mut std::fmt::Formatter<'_>, - file: Option<&super::file::File>, + f: &mut std::fmt::Formatter, + info: Option<&GlobalScriptInfo>, + form: &mut crate::lang::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, ) -> std::fmt::Result { - self.err.fmtgs(f, self.info.as_ref(), file)?; + self.err.fmtgs(f, self.info.as_ref(), form, file)?; writeln!(f); if let Some(location_end) = self.location_end { writeln!(f, " from {} to {}", self.location, location_end)?; @@ -308,7 +293,12 @@ impl ParseError { } impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.fmt_custom(f, None) + self.fmtgs( + f, + None, + &mut crate::lang::fmtgs::FormatInfo::default(), + None, + ) } } pub enum ParseErrors { @@ -327,12 +317,14 @@ pub enum ParseErrors { ErrorParsingFunctionArgs(Box), MacroError(MacroError), } -impl ParseErrors { + +impl FormatGs for ParseErrors { fn fmtgs( &self, f: &mut std::fmt::Formatter, info: Option<&GlobalScriptInfo>, - file: Option<&super::file::File>, + form: &mut crate::lang::fmtgs::FormatInfo, + file: Option<&crate::parsing::file::File>, ) -> std::fmt::Result { match self { Self::StatementCannotStartWith(ch) => { @@ -360,17 +352,17 @@ impl ParseErrors { Self::InvalidType(name) => write!(f, "\"{name}\" is not a type."), Self::CannotUseFixedIndexingWithThisType(t) => { write!(f, "cannot use fixed-indexing with type ")?; - t.fmtgs(f, info)?; + t.fmtgs(f, info, form, file)?; write!(f, ".") } Self::CannotWrapWithThisStatement(s) => { write!(f, "cannot wrap with this kind of statement: ")?; - s.fmtgs(f, info)?; + s.fmtgs(f, info, form, file)?; write!(f, ".") } Self::ErrorParsingFunctionArgs(parse_error) => { write!(f, "error parsing function args: ")?; - parse_error.fmt_custom(f, file) + parse_error.fmtgs(f, info, form, file) } Self::MacroError(e) => write!(f, "error in macro: {e}"), } diff --git a/mers/src/tutor/mod.rs b/mers/src/tutor/mod.rs index 97dc94a..1145401 100755 --- a/mers/src/tutor/mod.rs +++ b/mers/src/tutor/mod.rs @@ -1,7 +1,7 @@ use std::{path::PathBuf, thread::JoinHandle, time::Instant}; use crate::{ - lang::{code_runnable::RScript, global_info::GSInfo, val_data::VDataEnum}, + lang::{code_runnable::RScript, fmtgs::FormatGs, global_info::GSInfo, val_data::VDataEnum}, parsing::{self, file::File, parse::ScriptError}, };