From ef5874747e520e5f1c4b6c09918c28d8e57bd168 Mon Sep 17 00:00:00 2001 From: mark Date: Sat, 29 Apr 2023 19:04:38 +0200 Subject: [PATCH] added custom types that can be defined using 'type ' --- custom_type.mers | 19 ++++ mers/src/interactive_mode.rs | 2 +- mers/src/main.rs | 2 +- mers/src/parse/parse.rs | 69 ++++++++++-- mers/src/script/builtins.rs | 46 ++++---- mers/src/script/code_macro.rs | 7 +- mers/src/script/code_parsed.rs | 2 + mers/src/script/code_runnable.rs | 4 +- mers/src/script/to_runnable.rs | 187 +++++++++++++++++++++---------- mers/src/script/val_type.rs | 41 ++++++- mers/src/tutor/mod.rs | 14 +-- 11 files changed, 285 insertions(+), 108 deletions(-) create mode 100644 custom_type.mers diff --git a/custom_type.mers b/custom_type.mers new file mode 100644 index 0000000..f505cb5 --- /dev/null +++ b/custom_type.mers @@ -0,0 +1,19 @@ +// linked list +type elem [int []/elem] + +fn print_linked_list(start elem) { + loop { + println(start.0.to_string()) + elem = start.1 + switch! elem { + [] { + println("[END]") + true // break + } + elem start = elem // continue + } + } + [] +} + +[1 [2 [3 [5 [7 []]]]]].print_linked_list() \ No newline at end of file diff --git a/mers/src/interactive_mode.rs b/mers/src/interactive_mode.rs index 4df94ff..259a731 100755 --- a/mers/src/interactive_mode.rs +++ b/mers/src/interactive_mode.rs @@ -33,7 +33,7 @@ println(greeting) println!(" - - - - -"); println!("{}", output); } - Err(e) => println!("{}", e.with_file(&file)), + Err(e) => println!("{}", e.0.with_file_and_gsinfo(&file, e.1.as_ref())), } } else { println!("can't read file at {:?}!", temp_file); diff --git a/mers/src/main.rs b/mers/src/main.rs index 67cebf1..d7a174a 100755 --- a/mers/src/main.rs +++ b/mers/src/main.rs @@ -125,7 +125,7 @@ fn main() { println!("Output ({}s)\n{out}", elapsed.as_secs_f64()); } Err(e) => { - println!("Couldn't compile:\n{e}"); + println!("Couldn't compile:\n{}", e.0.with_file_and_gsinfo(&file, e.1.as_ref())); std::process::exit(99); } } diff --git a/mers/src/parse/parse.rs b/mers/src/parse/parse.rs index fcb795b..4494fd7 100755 --- a/mers/src/parse/parse.rs +++ b/mers/src/parse/parse.rs @@ -6,7 +6,7 @@ use crate::{ code_macro::MacroError, code_parsed::*, code_runnable::RScript, - global_info::{GlobalScriptInfo}, + global_info::{GlobalScriptInfo, GSInfo}, to_runnable::{self, ToRunnableError}, val_data::VDataEnum, val_type::{VSingleType, VType}, @@ -52,20 +52,50 @@ 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 { - match &self.0 { + 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(&self, f: &mut std::fmt::Formatter<'_>, file: Option<&File>, info: Option<&GlobalScriptInfo>) -> std::fmt::Result { + match &self { ScriptError::CannotFindPathForLibrary(e) => write!(f, "{e}"), ScriptError::ParseError(e) => { - write!(f, "failed while parsing: {}", e.with_file(self.1)) + write!(f, "failed while parsing: ")?; + e.fmt_custom(f, file)?; + Ok(()) } ScriptError::UnableToLoadLibrary(e) => write!(f, "{e}"), - ScriptError::ToRunnableError(e) => write!(f, "failed to compile: {e}"), + ScriptError::ToRunnableError(e) => { + write!(f, "failed to compile: ")?; + e.fmtgs(f, info); + Ok(()) + } } } } @@ -73,13 +103,28 @@ impl<'a> std::fmt::Display for ScriptErrorWithFile<'a> { pub const PARSE_VERSION: u64 = 0; /// executes the 4 parse_steps in order: lib_paths => interpret => libs_load => compile -pub fn parse(file: &mut File) -> Result { +pub fn parse(file: &mut File) -> Result { let mut ginfo = GlobalScriptInfo::default(); - let libs = parse_step_lib_paths(file)?; - let func = parse_step_interpret(file)?; - ginfo.libs = parse_step_libs_load(libs, &mut ginfo)?; - let run = parse_step_compile(func, ginfo)?; + let libs = match parse_step_lib_paths(file) { + Ok(v) => v, + Err(e) => return Err((e.into(), ginfo.to_arc())), + }; + + let func = match parse_step_interpret(file) { + Ok(v) => v, + Err(e) => return Err((e.into(), ginfo.to_arc())), + }; + + ginfo.libs = match parse_step_libs_load(libs, &mut ginfo) { + Ok(v) => v, + Err(e) => return Err((e.into(), ginfo.to_arc())), + }; + + let run = match parse_step_compile(func, ginfo) { + Ok(v) => v, + Err(e) => return Err((e.0.into(), e.1)), + }; Ok(run) } @@ -153,7 +198,7 @@ pub fn parse_step_libs_load( Ok(libs) } -pub fn parse_step_compile(main_func: SFunction, ginfo: GlobalScriptInfo) -> Result { +pub fn parse_step_compile(main_func: SFunction, ginfo: GlobalScriptInfo) -> Result { to_runnable::to_runnable(main_func, ginfo) } @@ -686,6 +731,10 @@ pub mod implementation { } break SStatementEnum::Match(match_what, cases).to(); } + "type" => { + file.skip_whitespaces(); + break SStatementEnum::TypeDefinition(file.collect_to_whitespace(), parse_type(file)?).to(); + } "true" => break SStatementEnum::Value(VDataEnum::Bool(true).to()).to(), "false" => { break SStatementEnum::Value(VDataEnum::Bool(false).to()).to() diff --git a/mers/src/script/builtins.rs b/mers/src/script/builtins.rs index 791ecdd..8f9b495 100755 --- a/mers/src/script/builtins.rs +++ b/mers/src/script/builtins.rs @@ -278,41 +278,41 @@ impl BuiltinFunction { && input[0] .fits_in(&VType { types: vec![VSingleType::Int, VSingleType::Float], - }) + }, info) .is_empty() } Self::Exit => { input.len() == 0 - || (input.len() == 1 && input[0].fits_in(&VSingleType::Int.to()).is_empty()) + || (input.len() == 1 && input[0].fits_in(&VSingleType::Int.to(), info).is_empty()) } // TODO! Self::FsList => true, Self::FsRead => { - input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty() + input.len() == 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty() } Self::FsWrite => { input.len() == 2 - && input[0].fits_in(&VSingleType::String.to()).is_empty() + && input[0].fits_in(&VSingleType::String.to(), info).is_empty() && input[1] - .fits_in(&VSingleType::List(VSingleType::Int.to()).to()) + .fits_in(&VSingleType::List(VSingleType::Int.to()).to(), info) .is_empty() } Self::BytesToString => { input.len() == 1 && input[0] - .fits_in(&VSingleType::List(VSingleType::Int.to()).to()) + .fits_in(&VSingleType::List(VSingleType::Int.to()).to(), info) .is_empty() } Self::StringToBytes => { - input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty() + input.len() == 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty() } Self::RunCommand | Self::RunCommandGetBytes => { - if input.len() >= 1 && input[0].fits_in(&VSingleType::String.to()).is_empty() { + if input.len() >= 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty() { if input.len() == 1 { true } else if input.len() == 2 { input[1] - .fits_in(&VSingleType::List(VSingleType::String.to()).to()) + .fits_in(&VSingleType::List(VSingleType::String.to()).to(), info) .is_empty() } else { false @@ -328,16 +328,16 @@ impl BuiltinFunction { types: vec![VSingleType::Int, VSingleType::Float], }; let st = VSingleType::String.to(); - (input[0].fits_in(&num).is_empty() && input[1].fits_in(&num).is_empty()) - || (input[0].fits_in(&st).is_empty() && input[1].fits_in(&st).is_empty()) + (input[0].fits_in(&num, info).is_empty() && input[1].fits_in(&num, info).is_empty()) + || (input[0].fits_in(&st, info).is_empty() && input[1].fits_in(&st, info).is_empty()) } } - Self::Not => input.len() == 1 && input[0].fits_in(&VSingleType::Bool.to()).is_empty(), + Self::Not => input.len() == 1 && input[0].fits_in(&VSingleType::Bool.to(), info).is_empty(), Self::And | Self::Or => { input.len() == 2 && input .iter() - .all(|v| v.fits_in(&VSingleType::Bool.to()).is_empty()) + .all(|v| v.fits_in(&VSingleType::Bool.to(), info).is_empty()) } Self::Sub | Self::Mul @@ -354,7 +354,7 @@ impl BuiltinFunction { let num = VType { types: vec![VSingleType::Int, VSingleType::Float], }; - input[0].fits_in(&num).is_empty() && input[1].fits_in(&num).is_empty() + input[0].fits_in(&num, info).is_empty() && input[1].fits_in(&num, info).is_empty() } } // TODO! check that we pass a reference to a list! @@ -365,7 +365,7 @@ impl BuiltinFunction { // if vec.is_reference().is_some_and(|v| v) { // unstable if let Some(true) = vec.is_reference() { if let Some(t) = vec.get_any(info) { - el.fits_in(&t).is_empty() + el.fits_in(&t, info).is_empty() } else { false } @@ -381,7 +381,7 @@ impl BuiltinFunction { let (vec, el) = (&input[0], &input[1]); if let Some(true) = vec.is_reference() { if let Some(t) = vec.get_any(info) { - el.fits_in(&t).is_empty() + el.fits_in(&t, info).is_empty() } else { false } @@ -415,7 +415,7 @@ impl BuiltinFunction { if let Some(true) = vec.is_reference() { // TODO! same issue as in pop if let Some(_) = vec.get_any(info) { - if index.fits_in(&VSingleType::Int.to()).is_empty() { + if index.fits_in(&VSingleType::Int.to(), info).is_empty() { true } else { false @@ -436,11 +436,11 @@ impl BuiltinFunction { if input.len() >= 2 && input.len() <= 3 { let (s, start) = (&input[0], &input[1]); let index_type = VSingleType::Int.to(); - if s.fits_in(&VSingleType::String.to()).is_empty() - && start.fits_in(&index_type).is_empty() + if s.fits_in(&VSingleType::String.to(), info).is_empty() + && start.fits_in(&index_type, info).is_empty() { if let Some(end) = input.get(2) { - end.fits_in(&index_type).is_empty() + end.fits_in(&index_type, info).is_empty() } else { true } @@ -456,7 +456,7 @@ impl BuiltinFunction { input.len() == 2 && input .iter() - .all(|v| v.fits_in(&VSingleType::String.to()).is_empty()) + .all(|v| v.fits_in(&VSingleType::String.to(), info).is_empty()) } // two strings or &strings Self::IndexOf => { @@ -467,12 +467,12 @@ impl BuiltinFunction { VSingleType::String, VSingleType::Reference(Box::new(VSingleType::String)), ], - }) + }, info) .is_empty() }) } Self::Trim => { - input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty() + input.len() == 1 && input[0].fits_in(&VSingleType::String.to(), info).is_empty() } } } diff --git a/mers/src/script/code_macro.rs b/mers/src/script/code_macro.rs index 808188c..8a7f736 100755 --- a/mers/src/script/code_macro.rs +++ b/mers/src/script/code_macro.rs @@ -43,7 +43,7 @@ fn parse_mers_code(file: &mut File) -> Result { _ = file.next(); match parse::parse(file) { Ok(v) => Ok(v), - Err(e) => Err(e.into()), + Err(e) => Err(e.0.into()), } } else { let path = parse_string_val(file); @@ -56,7 +56,10 @@ fn parse_mers_code(file: &mut File) -> Result { .expect("can't include mers code because the file could not be read"), path.into(), ); - Ok(parse::parse(&mut file)?) + Ok(match parse::parse(&mut file) { + Ok(v) => v, + Err(e) => return Err(e.0.into()), + }) } } diff --git a/mers/src/script/code_parsed.rs b/mers/src/script/code_parsed.rs index 51d28de..33fc065 100755 --- a/mers/src/script/code_parsed.rs +++ b/mers/src/script/code_parsed.rs @@ -17,6 +17,7 @@ pub enum SStatementEnum { Match(String, Vec<(SStatement, SStatement)>), IndexFixed(SStatement, usize), EnumVariant(String, SStatement), + TypeDefinition(String, VType), Macro(Macro), } impl SStatementEnum { @@ -173,6 +174,7 @@ impl SStatementEnum { write!(f, "{variant}: ")?; inner.fmtgs(f, info) } + Self::TypeDefinition(name, t) => write!(f, "type {name} {t}"), Self::Macro(m) => { write!(f, "!({m})") } diff --git a/mers/src/script/code_runnable.rs b/mers/src/script/code_runnable.rs index bb2a16a..605f81b 100755 --- a/mers/src/script/code_runnable.rs +++ b/mers/src/script/code_runnable.rs @@ -260,7 +260,7 @@ impl RStatementEnum { let switch_on_type = switch_on.out(); let mut out = VDataEnum::Tuple(vec![]).to(); for (case_type, case_action) in cases.iter() { - if switch_on_type.fits_in(case_type).is_empty() { + if switch_on_type.fits_in(case_type, info).is_empty() { out = case_action.run(vars, info); break; } @@ -329,7 +329,7 @@ impl RStatementEnum { let switch_on = switch_on.to(); 'search: { for (on_type, case) in cases.iter() { - if switch_on.fits_in(&on_type).is_empty() { + if switch_on.fits_in(&on_type, info).is_empty() { out = out | case.out(info); break 'search; } diff --git a/mers/src/script/to_runnable.rs b/mers/src/script/to_runnable.rs index b47de58..749b9e8 100755 --- a/mers/src/script/to_runnable.rs +++ b/mers/src/script/to_runnable.rs @@ -18,13 +18,14 @@ use super::{ builtins::BuiltinFunction, code_macro::Macro, code_parsed::{SBlock, SFunction, SStatement, SStatementEnum}, - code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, + code_runnable::{RBlock, RFunction, RScript, RStatement, RStatementEnum}, global_info::GSInfo, }; pub enum ToRunnableError { MainWrongInput, UseOfUndefinedVariable(String), UseOfUndefinedFunction(String), + UnknownType(String), CannotDeclareVariableWithDereference(String), CannotDereferenceTypeNTimes(VType, usize, VType), FunctionWrongArgCount(String, usize, usize), @@ -51,6 +52,11 @@ impl Debug for ToRunnableError { // - 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) + } +} +impl ToRunnableError { + pub fn fmtgs(&self, f: &mut std::fmt::Formatter, info: Option<&GlobalScriptInfo>) -> std::fmt::Result { match self { Self::MainWrongInput => write!( f, @@ -58,41 +64,77 @@ impl Display for ToRunnableError { ), Self::UseOfUndefinedVariable(v) => write!(f, "Cannot use variable \"{v}\" as it isn't defined (yet?)."), Self::UseOfUndefinedFunction(v) => write!(f, "Cannot use function \"{v}\" as it isn't defined (yet?)."), + Self::UnknownType(name) => write!(f, "Unknown type \"{name}\"."), 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} {derefs_wanted} times (stopped at {last_valid_type})." - ), + Self::CannotDereferenceTypeNTimes(og_type, derefs_wanted, last_valid_type) => { + write!(f, "Cannot dereference type ")?; + og_type.fmtgs(f, info)?; + write!(f, "{derefs_wanted} times (stopped at ")?; + last_valid_type.fmtgs(f, info); + write!(f, ")")?; + Ok(()) + }, Self::FunctionWrongArgCount(v, a, b) => write!(f, "Tried to call function \"{v}\", which takes {a} arguments, with {b} arguments instead."), Self::InvalidType { expected, found, problematic, } => { - write!(f, "Invalid type: Expected {expected} but found {found}, which includes {problematic}, which is not covered.") + write!(f, "Invalid type: Expected ")?; + expected.fmtgs(f, info)?; + write!(f, " but found ")?; + found.fmtgs(f, info)?; + write!(f, ", which includes ")?; + problematic.fmtgs(f, info)?; + 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)?; + Ok(()) + } + Self::MatchConditionInvalidReturn(v) => { + write!(f, "match statement condition returned ")?; + v.fmtgs(f, info)?; + 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)?; + write!(f, ".")?; + Ok(()) } - Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"), - Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."), - Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."), Self::WrongInputsForBuiltinFunction(_builtin, builtin_name, args) => { - write!(f, "Wrong arguments for builtin function {}:", builtin_name)?; + write!(f, "Wrong arguments for builtin function \"{}\":", builtin_name)?; for arg in args { - write!(f, " {arg}")?; + write!(f, " ")?; + arg.fmtgs(f, info)?; } write!(f, ".") } Self::WrongArgsForLibFunction(name, args) => { write!(f, "Wrong arguments for library function {}:", name)?; for arg in args { - write!(f, " {arg}")?; + write!(f, " ")?; + arg.fmtgs(f, info)?; } write!(f, ".") } Self::ForLoopContainerHasNoInnerTypes => { write!(f, "For loop: container had no inner types, cannot iterate.") } - Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => write!(f, - "the statement requires its output type to be {required}, but its real output type is {real}, which doesn not fit in the required type because of the problematic types {problematic}." - ), + Self::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(required, real, problematic) => { + write!(f, "the statement requires its output type to be ")?; + required.fmtgs(f, info)?; + write!(f, ", but its real output type is ")?; + real.fmtgs(f, info)?; + write!(f, ", which doesn't fit in the required type because of the problematic types ")?; + problematic.fmtgs(f, info)?; + write!(f, ".")?; + Ok(()) + } } } } @@ -104,9 +146,9 @@ struct LInfo { fns: HashMap>, } -pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result { +pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result { if s.inputs.len() != 1 || s.inputs[0].0 != "args" { - return Err(ToRunnableError::MainWrongInput); + return Err((ToRunnableError::MainWrongInput, ginfo.to_arc())); } assert_eq!( s.inputs[0].1, @@ -116,18 +158,25 @@ pub fn to_runnable(s: SFunction, mut ginfo: GlobalScriptInfo) -> Result v, + Err(e) => return Err((e, ginfo.to_arc())), + }; + let ginfo = ginfo.to_arc(); + match RScript::new( func, - ginfo.to_arc(), - )?) + ginfo.clone(), + ) { + Ok(v) => Ok(v), + Err(e) => Err((e, ginfo)), + } } // go over every possible known-type input for the given function, returning all possible RFunctions. @@ -142,7 +191,9 @@ fn get_all_functions( if s.inputs.len() > inputs.len() { let input_here = &s.inputs[inputs.len()].1; for t in &input_here.types { - inputs.push(t.clone().into()); + let mut t = t.clone(); + stype(&mut t, ginfo)?; + inputs.push(t); get_all_functions(s, ginfo, linfo, input_vars, inputs, out)?; inputs.pop(); } @@ -150,7 +201,11 @@ fn get_all_functions( } else { // set the types for (varid, vartype) in s.inputs.iter().zip(inputs.iter()) { - linfo.vars.get_mut(&varid.0).unwrap().1 = vartype.clone().into(); + linfo.vars.get_mut(&varid.0).unwrap().1 = { + let mut vartype = vartype.clone(); + stype(&mut vartype, ginfo)?; + vartype.to() + } } out.push((inputs.clone(), block(&s.block, ginfo, linfo.clone())?.out(ginfo))); Ok(()) @@ -164,11 +219,13 @@ fn function( let mut input_vars = vec![]; let mut input_types = vec![]; for (iname, itype) in &s.inputs { + let mut itype = itype.to_owned(); + stypes(&mut itype, ginfo)?; linfo .vars .insert(iname.clone(), (ginfo.vars, itype.clone())); input_vars.push(ginfo.vars); - input_types.push(itype.clone()); + input_types.push(itype); ginfo.vars += 1; } let mut all_outs = vec![]; @@ -200,18 +257,23 @@ fn block(s: &SBlock, ginfo: &mut GlobalScriptInfo, mut linfo: LInfo) -> Result Result<(), ToRunnableError> { for t in &mut t.types { - stype(t, ginfo); + stype(t, ginfo)?; } + Ok(()) } -fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) { +fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) -> Result<(), ToRunnableError> { match t { + VSingleType::Bool | VSingleType::Int | VSingleType::Float | VSingleType::String => (), VSingleType::Tuple(v) => { for t in v { - stypes(t, ginfo); + stypes(t, ginfo)?; } } + VSingleType::List(t) => stypes(t, ginfo)?, + VSingleType::Reference(t) => stype(t, ginfo)?, + VSingleType::Thread(t) => stypes(t, ginfo)?, VSingleType::EnumVariantS(e, v) => { *t = VSingleType::EnumVariant( { @@ -224,13 +286,30 @@ fn stype(t: &mut VSingleType, ginfo: &mut GlobalScriptInfo) { } }, { - stypes(v, ginfo); + stypes(v, ginfo)?; v.clone() }, ) } - _ => (), + VSingleType::Function(io_map) => { + for io_variant in io_map { + for i in &mut io_variant.0 { + stype(i, ginfo)?; + } + stypes(&mut io_variant.1, ginfo)?; + } + } + VSingleType::EnumVariant(_, t) => stypes(t, ginfo)?, + VSingleType::CustomTypeS(name) => { + *t = VSingleType::CustomType(if let Some(v) = ginfo.custom_type_names.get(name) { + *v + } else { + return Err(ToRunnableError::UnknownType(name.to_owned())); + }) + } + VSingleType::CustomType(_) => () } + Ok(()) } fn statement( s: &SStatement, @@ -253,7 +332,7 @@ fn statement( SStatementEnum::Variable(v, is_ref) => { if let Some(var) = linfo.vars.get(v) { RStatementEnum::Variable(var.0, { - let mut v = var.1.clone(); stypes(&mut v, ginfo); v }, *is_ref) + let mut v = var.1.clone(); stypes(&mut v, ginfo)?; v }, *is_ref) } else { return Err(ToRunnableError::UseOfUndefinedVariable(v.clone())); } @@ -273,7 +352,7 @@ fn statement( } for (i, rarg) in rargs.iter().enumerate() { let rarg = rarg.out(ginfo); - let out = rarg.fits_in(&func.input_types[i]); + let out = rarg.fits_in(&func.input_types[i], ginfo); if !out.is_empty() { return Err(ToRunnableError::InvalidType { expected: func.input_types[i].clone(), @@ -296,7 +375,7 @@ fn statement( // LIBRARY FUNCTION? if let Some((libid, fnid)) = ginfo.lib_fns.get(v) { let (_name, fn_in, fn_out) = &ginfo.libs[*libid].registered_fns[*fnid]; - if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out(ginfo).fits_in(fn_in).is_empty()) { + if fn_in.len() == rargs.len() && fn_in.iter().zip(rargs.iter()).all(|(fn_in, arg)| arg.out(ginfo).fits_in(fn_in, ginfo).is_empty()) { RStatementEnum::LibFunction(*libid, *fnid, rargs, fn_out.clone()) } else { // TODO! better error here @@ -332,7 +411,7 @@ fn statement( let condition = statement(&c, ginfo, linfo)?; let out = condition.out(ginfo).fits_in(&VType { types: vec![VSingleType::Bool], - }); + }, ginfo); if out.is_empty() { condition } else { @@ -374,7 +453,7 @@ fn statement( let mut ncases = Vec::with_capacity(cases.len()); let og_type = switch_on_v.1.clone(); // linfo.vars.get(switch_on).unwrap().1.clone(); for case in cases { - let case0 = { let mut v = case.0.clone(); stypes(&mut v, ginfo); v }; + let case0 = { let mut v = case.0.clone(); stypes(&mut v, ginfo)?; v }; linfo.vars.get_mut(switch_on).unwrap().1 = case0.clone(); ncases.push((case0, statement(&case.1, ginfo, linfo)?)); } @@ -391,8 +470,8 @@ fn statement( 'force: { for (case_type, _) in cases { let mut ct = case_type.clone(); - stypes(&mut ct, ginfo); - if val_type.fits_in(&ct).is_empty() { + stypes(&mut ct, ginfo)?; + if val_type.fits_in(&ct, ginfo).is_empty() { break 'force; } } @@ -493,23 +572,7 @@ fn statement( SStatementEnum::IndexFixed(st, i) => { let st = statement(st, ginfo, linfo)?; - let ok = 'ok: { - let mut one = false; - for t in st.out(ginfo).types { - one = true; - // only if all types are indexable by i - match t { - VSingleType::Tuple(v) => { - if v.len() <= *i { - break 'ok false; - } - } - _ => break 'ok false, - } - } - one - }; - if ok { + if st.out(ginfo).get_always(*i, ginfo).is_some() { RStatementEnum::IndexFixed(st, *i) } else { return Err(ToRunnableError::NotIndexableFixed(st.out(ginfo), *i)); @@ -524,6 +587,14 @@ fn statement( v } }, statement(s, ginfo, linfo)?), + SStatementEnum::TypeDefinition(name, t) => { + // insert to name map has to happen before stypes() + ginfo.custom_type_names.insert(name.to_owned(), ginfo.custom_types.len()); + let mut t = t.to_owned(); + stypes(&mut t, ginfo)?; + ginfo.custom_types.push(t); + RStatementEnum::Value(VDataEnum::Tuple(vec![]).to()) + } SStatementEnum::Macro(m) => match m { Macro::StaticMers(val) => RStatementEnum::Value(val.clone()), }, @@ -531,10 +602,12 @@ fn statement( .to(); // if force_output_type is set, verify that the real output type actually fits in the forced one. if let Some(force_opt) = &s.force_output_type { + let mut force_opt = force_opt.to_owned(); + stypes(&mut force_opt, ginfo)?; let real_output_type = statement.out(ginfo); - let problematic_types = real_output_type.fits_in(force_opt); + let problematic_types = real_output_type.fits_in(&force_opt, ginfo); if problematic_types.is_empty() { - statement.force_output_type = Some(force_opt.clone()); + statement.force_output_type = Some(force_opt); } else { return Err(ToRunnableError::StatementRequiresOutputTypeToBeAButItActuallyOutputsBWhichDoesNotFitInA(force_opt.clone(), real_output_type, VType { types: problematic_types })); } @@ -554,7 +627,7 @@ fn statement( )); } } - let inv_types = out.fits_in(&var_derefd); + let inv_types = out.fits_in(&var_derefd, ginfo); if !inv_types.is_empty() { eprintln!("Warn: shadowing variable {opt} because statement's output type {out} does not fit in the original variable's {var_out}. This might become an error in the future, or it might stop shadowing the variiable entirely - for stable scripts, avoid this by giving the variable a different name."); if *derefs != 0 { diff --git a/mers/src/script/val_type.rs b/mers/src/script/val_type.rs index e720353..53b071f 100755 --- a/mers/src/script/val_type.rs +++ b/mers/src/script/val_type.rs @@ -4,7 +4,7 @@ use std::{ ops::BitOr, }; -use super::global_info::{GlobalScriptInfo, GSInfo}; +use super::global_info::{GlobalScriptInfo, GSInfo, self}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct VType { @@ -39,7 +39,18 @@ impl VSingleType { Self::Reference(r) => r.get(i, gsinfo), Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get(i, gsinfo), Self::CustomType(t) => gsinfo.custom_types[*t].get(i, gsinfo), - &Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler error?"), + &Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler bug? [get]"), + } + } + // None => might not always return t, Some(t) => can only return t + pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option { + match self { + Self::Bool | Self::Int | Self::Float | Self::String | Self::List(_) | Self::Function(..) | Self::Thread(..) => None, + Self::Tuple(t) => t.get(i).cloned(), + Self::Reference(r) => r.get_always(i, info), + Self::EnumVariant(_, t) | Self::EnumVariantS(_, t) => t.get_always(i, info), + Self::CustomType(t) => info.custom_types[*t].get_always(i, info), + Self::CustomTypeS(_) => unreachable!("CustomTypeS instead of CustomType, compiler bug? [get_always]"), } } } @@ -54,6 +65,13 @@ impl VType { } Some(out) } + pub fn get_always(&self, i: usize, info: &GlobalScriptInfo) -> Option { + let mut out = VType { types: vec![] }; + for t in &self.types { + out = out | t.get_always(i, info)?; // if we can't use *get* on one type, we can't use it at all. + } + Some(out) + } /// returns Some(true) or Some(false) if all types are references or not references. If it is mixed or types is empty, returns None. pub fn is_reference(&self) -> Option { let mut noref = false; @@ -263,18 +281,20 @@ impl VSingleType { } } pub fn fits_in(&self, rhs: &Self, info: &GlobalScriptInfo) -> bool { + // #[cfg(debug_assertions)] + // eprintln!("{self} in {rhs}?"); match (self, rhs) { (Self::Reference(r), Self::Reference(b)) => r.fits_in(b, info), (Self::Reference(_), _) | (_, Self::Reference(_)) => false, (Self::EnumVariant(v1, t1), Self::EnumVariant(v2, t2)) => { *v1 == *v2 && t1.fits_in(&t2, info).is_empty() }, + (Self::CustomType(a), Self::CustomType(b)) => *a == *b || info.custom_types[*a].fits_in(&info.custom_types[*b], info).is_empty(), (Self::CustomType(a), b) => info.custom_types[*a].fits_in(&b.clone().to(), info).is_empty(), (a, Self::CustomType(b)) => a.clone().to().fits_in(&info.custom_types[*b], info).is_empty(), - (Self::CustomType(a), Self::CustomType(b)) => info.custom_types[*a].fits_in(&info.custom_types[*b], info).is_empty(), - (Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => unreachable!(), + (Self::CustomTypeS(_), _) | (_, Self::CustomTypeS(_)) => unreachable!("CustomTypeS instead of CustomType - compiler bug?"), (Self::EnumVariant(..), _) | (_, Self::EnumVariant(..)) => false, - (Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => unreachable!(), + (Self::EnumVariantS(..), _) | (_, Self::EnumVariantS(..)) => unreachable!("EnumVariantS instead of EnumVariant - compiler bug?"), (Self::Bool, Self::Bool) | (Self::Int, Self::Int) | (Self::Float, Self::Float) @@ -295,6 +315,7 @@ impl VSingleType { 'search: { for b in b { if a.1.fits_in(&b.1, info).is_empty() + && a.0.len() == b.0.len() && a.0.iter().zip(b.0.iter()).all(|(a, b)| *a == *b) { break 'search; @@ -396,6 +417,16 @@ impl VSingleType { inner.fmtgs(f, info)?; write!(f, ")") } + Self::CustomType(t) => if let Some(info) = info { + #[cfg(not(debug_assertions))] + write!(f, "{}", info.custom_type_names.iter().find_map(|(name, id)| if *t == *id { Some(name.to_owned()) } else { None }).unwrap())?; + #[cfg(debug_assertions)] + write!(f, "{}/*{}*/", info.custom_type_names.iter().find_map(|(name, id)| if *t == *id { Some(name.to_owned()) } else { None }).unwrap(), &info.custom_types[*t])?; + Ok(()) + } else { + write!(f, "[custom type #{t}]") + } + Self::CustomTypeS(t) => write!(f, "{t}"), } } } diff --git a/mers/src/tutor/mod.rs b/mers/src/tutor/mod.rs index 6a609a8..4ef31af 100755 --- a/mers/src/tutor/mod.rs +++ b/mers/src/tutor/mod.rs @@ -1,8 +1,8 @@ use std::{path::PathBuf, thread::JoinHandle, time::Instant}; use crate::{ - parse::{self, parse::ScriptError}, - script::{code_runnable::RScript, val_data::VDataEnum}, + parse::{self, parse::ScriptError, file::File}, + script::{code_runnable::RScript, val_data::VDataEnum, global_info::GSInfo}, }; mod base_comments; @@ -31,7 +31,7 @@ false Box::new(move |file| { let mut file = parse::file::File::new(std::fs::read_to_string(file).unwrap(), PathBuf::new()); - sender.send(parse::parse::parse(&mut file)).unwrap(); + sender.send((parse::parse::parse(&mut file), file)).unwrap(); }), ) .unwrap(); @@ -60,7 +60,7 @@ pub struct Tutor { written_status_byte_len: usize, editor_join_handle: JoinHandle<()>, file_path: PathBuf, - receiver: std::sync::mpsc::Receiver>, + receiver: std::sync::mpsc::Receiver<(Result, File)>, // i_ are inputs from the user pub i_name: Option, } @@ -70,16 +70,16 @@ impl Tutor { // eprintln!(" - - - - - - - - - - - - - - - - - - - - - - - - -"); let script = loop { match self.receiver.recv().unwrap() { - Err(e) => { + (Err((e, info)), file) => { self.current_status = format!( " - Error during build{}", - e.to_string() + e.with_file_and_gsinfo(&file, info.as_ref()).to_string() .lines() .map(|v| format!("\n// {v}")) .collect::() ) } - Ok(script) => { + (Ok(script), _) => { break script; } }