-f+c will print the parsed script with colored highlighting

This commit is contained in:
mark 2023-05-19 21:25:05 +02:00
parent 79797e6718
commit b5924b5da1
15 changed files with 417 additions and 222 deletions

View File

@ -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)
}

7
mers/Cargo.lock generated
View File

@ -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",

View File

@ -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"]

View File

@ -7,6 +7,8 @@ pub mod fs_watcher {
thread::{self, JoinHandle},
};
use crate::lang::fmtgs::FormatGs;
#[derive(Debug)]
pub struct Error(String);

View File

@ -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);
std::io::stdout().flush();
}
#[cfg(feature = "nushell_plugin")]
{
eprint!("{}", arg);
std::io::stderr().flush();
}
VDataEnum::Tuple(vec![]).to()
} else {
unreachable!("print function called with non-string arg")

View File

@ -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, " ");
}
write!(f, ")")
arg.fmtgs(f, info, form, file)?;
}
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 {
// 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)?;
forced_type.fmtgs(f, info)?;
write!(f, "{}{}::", "*".repeat(*derefs), opt.with(info, file))?;
forced_type.fmtgs(f, info, form, file)?;
write!(f, " = ")?;
} else {
write!(f, "{}{} = ", "*".repeat(*derefs), opt)?;
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()))
}
}
}

View File

@ -373,7 +373,6 @@ impl RStatementEnum {
}
}
#[derive(Debug)]
pub struct RScript {
main: RFunction,
info: GSInfo,

View File

@ -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<GlobalScriptInfo>;
#[derive(Debug)]
pub struct GlobalScriptInfo {
pub libs: Vec<libs::Lib>,
pub lib_fns: HashMap<String, (usize, usize)>,
@ -24,10 +25,61 @@ pub struct GlobalScriptInfo {
pub custom_type_names: HashMap<String, usize>,
pub custom_types: Vec<VType>,
#[cfg(debug_assertions)]
pub formatter: ColorFormatter,
pub log: Logger,
}
pub struct ColorFormatter {
pub mode: ColorFormatMode,
pub bracket_colors: Vec<Color>,
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<Mutex<Vec<LogMsg>>>,
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<String>, VDataEnum, usize, usize),
VTypeFitsIn(VType, VType, Vec<VSingleType>),
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

View File

@ -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;

View File

@ -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(())
}

View File

@ -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)
}
}

View File

@ -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<VType> 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)
}
}

View File

@ -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));

View File

@ -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 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 Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.err.with_gsinfo(&self.ginfo))
}
}
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())
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<RScript, Error> {
}
/// like parse, but GlobalInfo can be something other than Default::default().
pub fn parse_custom_info(file: &mut File, mut ginfo: GlobalScriptInfo) -> Result<RScript, Error> {
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<Vec<Command>, CannotFindPathForLibrary> {
pub fn parse_step_lib_paths(
file: &mut File,
ginfo: &GlobalScriptInfo,
) -> Result<Vec<Command>, CannotFindPathForLibrary> {
let mut libs = vec![];
loop {
file.skip_whitespaces();
@ -198,14 +183,23 @@ pub fn parse_step_lib_paths(file: &mut File) -> Result<Vec<Command>, CannotFindP
Ok(libs)
}
pub fn parse_step_interpret(file: &mut File) -> Result<SFunction, ParseError> {
Ok(SFunction::new(
pub fn parse_step_interpret(
file: &mut File,
ginfo: &GlobalScriptInfo,
) -> Result<SFunction, ParseError> {
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<GlobalScriptInfo>,
}
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<ParseError>),
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}"),
}

View File

@ -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},
};