diff --git a/src/compiler/common.rs b/src/compiler/common.rs index 47f01b1..07e83e4 100755 --- a/src/compiler/common.rs +++ b/src/compiler/common.rs @@ -180,7 +180,7 @@ impl FuncVerifier for Builtin { Builtin::Len => &[&[Type::ArrayColumn(Magma::Any)]], }; - if super::cyclic_compatible_with(expected_t, &args_t) { + if super::compatible_with_repeating(expected_t, &args_t) { Ok(()) } else { bail!(CompileError::TypeError( diff --git a/src/compiler/generator.rs b/src/compiler/generator.rs index 3833250..e1710a4 100644 --- a/src/compiler/generator.rs +++ b/src/compiler/generator.rs @@ -217,35 +217,26 @@ impl FuncVerifier for Intrinsic { // Each nested second-level list represent the acceptable typings for // the argument they type. // - // The first-level list is cycled as many times is needed to validate - // all the arguments. Therefore, for function taking homogeneous - // arguments (e.g. Add), a single second-level list is enough. + // The first-level list last element is cycled as many times is needed + // to validate all the arguments. Therefore, for function taking + // homogeneous arguments (e.g. Add), a single second-level list is + // enough. let expected_t: &[&[Type]] = match self { Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul => &[&[Type::Any(Magma::Any)]], Intrinsic::Exp => &[&[Type::Any(Magma::Any)], &[Type::Scalar(Magma::Any)]], Intrinsic::Neg => &[&[Type::Scalar(Magma::Any), Type::Column(Magma::Any)]], - Intrinsic::Inv => &[&[Type::Column(Magma::Any)]], + Intrinsic::Inv => &[&[Type::Any(Magma::Any)]], Intrinsic::Shift => &[&[Type::Column(Magma::Any)], &[Type::Scalar(Magma::Any)]], Intrinsic::IfZero | Intrinsic::IfNotZero => &[ - &[ - Type::Scalar(Magma::Any), - Type::Column(Magma::Any), - Type::List(Magma::Any), - ], - &[ - Type::Scalar(Magma::Any), - Type::Column(Magma::Any), - Type::List(Magma::Any), - ], + // condition type TODO: force to bool/loob + &[Type::Any(Magma::Any)], + // then/else arms type + &[Type::Any(Magma::Any), Type::List(Magma::Any)], ], - Intrinsic::Begin => &[&[ - Type::Scalar(Magma::Any), - Type::Column(Magma::Any), - Type::List(Magma::Any), - ]], + Intrinsic::Begin => &[&[Type::Any(Magma::Any)]], }; - if super::cyclic_compatible_with(expected_t, &args_t) { + if super::compatible_with_repeating(expected_t, &args_t) { Ok(()) } else { bail!(CompileError::TypeError( @@ -1152,7 +1143,7 @@ fn apply_intrinsic( } else { traversed_args[0].to_string() }; - error!("condition {} may overflow", pretty.bright_white().bold()); + warn!("condition {} may overflow", pretty.bright_white().bold()); } Ok(Some(r)) } diff --git a/src/compiler/node.rs b/src/compiler/node.rs index a8b3d58..334db93 100644 --- a/src/compiler/node.rs +++ b/src/compiler/node.rs @@ -402,12 +402,15 @@ impl Node { Expression::Funcall { func, args } => { let mut mine = ax; if let Intrinsic::Shift = func { - let arg_big = args[1] - .pure_eval() - .unwrap_or_else(|_| panic!("{}", args[1].to_string().as_str())); - let arg = arg_big - .to_isize() - .unwrap_or_else(|| panic!("{}", arg_big.to_string().as_str())); + let arg_big = args[1].pure_eval().unwrap_or_else(|_| { + panic!( + "{} is not a valid shift offset", + args[1].to_string().as_str() + ) + }); + let arg = arg_big.to_isize().unwrap_or_else(|| { + panic!("{} is not an isize", arg_big.to_string().as_str()) + }); mine = mine.min(mine + arg); } args.iter().map(|e| _past_span(e.e(), mine)).min().unwrap() diff --git a/src/compiler/types.rs b/src/compiler/types.rs index 1923d35..36ee63b 100755 --- a/src/compiler/types.rs +++ b/src/compiler/types.rs @@ -26,7 +26,7 @@ impl std::fmt::Display for Type { Type::Void => write!(f, "∅"), Type::Scalar(m) => write!(f, "{}", m), Type::Column(m) => write!(f, "[{}]", m), - Type::Any(m) => write!(f, "∀{}", m), + Type::Any(m) => write!(f, "∋{}", m), Type::ArrayColumn(m) => write!(f, "[[{}]]", m), Type::List(m) => write!(f, "{{{}}}", m), } @@ -290,23 +290,41 @@ impl std::fmt::Display for Magma { Magma::None => write!(f, "NONE"), Magma::Loobean => write!(f, "𝕃"), Magma::Boolean => write!(f, "𝔹"), - Magma::Nibble => write!(f, "Nib."), - Magma::Byte => write!(f, "Byte"), + Magma::Nibble => write!(f, "4×"), + Magma::Byte => write!(f, "8×"), Magma::Integer => write!(f, "Fr"), Magma::Any => write!(f, "∀"), } } } -pub fn cyclic_compatible_with(expected: &[&[Type]], found: &[Type]) -> bool { - for (es, f) in expected.iter().cycle().zip(found.iter()) { - if !es.iter().any(|e| e >= f) { +/// Checks that a given list of types `found` is compatible with a list of list +/// of types `expected`. For each position in the lists, the type in `found` +/// must match with one of the types in the corresponding nested list in +/// `expected` for the operation to be a success. +/// +/// In the case where `found` is longer than `expected`, the last element of +/// `expected` is repeated as many times as required. This allows for typing +/// variadic functions, as long as their tail of arguments is type-homogeneous. +pub fn compatible_with_repeating(expected: &[&[Type]], found: &[Type]) -> bool { + for (es, f) in expected + .iter() + .chain(std::iter::repeat(expected.last().unwrap())) + .zip(found.iter()) + { + if !es.iter().any(|e| f.can_cast_to(*e)) { return false; } } true } +/// Checks that a given list of types `found` is compatible with a list of list +/// of types `expected`. For each position in the lists, the type in `found` +/// must match with the corresponding type `expected` for the operation to be a +/// success. +/// +/// If `found` and `expected` differ in length, the operation is a failure. pub fn compatible_with(expected: &[Type], found: &[Type]) -> bool { if expected.len() != found.len() { return false; diff --git a/src/errors.rs b/src/errors.rs index 3c40773..3bfbee8 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -117,15 +117,16 @@ pub(crate) mod compiler { expected .iter() .cycle() + .chain(std::iter::repeat(expected.last().unwrap())) .zip(found.iter()) .map(|(es, f)| { let es_str = es .iter() - .map(|e| format!("{:?}", e)) + .map(|e| e.to_string()) .collect::>() .join("|"); - if es.iter().any(|e| e >= f) { - es_str.black().to_string() + if es.iter().any(|e| f.can_cast_to(*e)) { + es_str.white().to_string() } else { es_str.blue().to_string() } @@ -139,10 +140,11 @@ pub(crate) mod compiler { expected .iter() .cycle() + .chain(std::iter::repeat(expected.last().unwrap())) .zip(found.iter()) .map(|(e, f)| { - if e.iter().any(|e| e >= f) { - f.black().to_string() + if e.iter().any(|e| f.can_cast_to(*e)) { + f.white().to_string() } else { f.red().to_string() }