From 3d9d7fd594323d04f77c48ddc3ef2c2b6dc580b7 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 30 Apr 2024 00:07:20 +0200 Subject: rebel-parse, rebel-lang: remove array length from types Tuples are sufficient for fixed-length data. For arrays, the small increase in type safety is not worth the large increase in complexity - both for the typechecker, and the usage of the language. The Coerce parameter of unify() is preserved for now, as it may become useful again for other types like sets. --- crates/rebel-lang/examples/repl.rs | 4 +- crates/rebel-lang/src/typing.rs | 91 ++++++-------------------------- crates/rebel-lang/src/value.rs | 7 +-- crates/rebel-parse/src/ast/typ.rs | 9 +--- crates/rebel-parse/src/grammar/recipe.rs | 15 +----- 5 files changed, 23 insertions(+), 103 deletions(-) diff --git a/crates/rebel-lang/examples/repl.rs b/crates/rebel-lang/examples/repl.rs index f2c714b..aa3cbf8 100644 --- a/crates/rebel-lang/examples/repl.rs +++ b/crates/rebel-lang/examples/repl.rs @@ -1,7 +1,7 @@ use rebel_lang::{ func::{Func, FuncDef, FuncType}, scope::Context, - typing::{ArrayLen, Type, TypeFamily}, + typing::{Type, TypeFamily}, value::{EvalError, Result, Value}, }; use rebel_parse::{recipe, tokenize}; @@ -43,7 +43,7 @@ fn main() { "len", Func { typ: FuncType { - params: vec![Type::Array(Box::new(Type::Free), ArrayLen::Dynamic)], + params: vec![Type::Array(Box::new(Type::Free))], ret: Type::Int, }, def: Some(FuncDef::Intrinsic(intrinsic_array_len)), diff --git a/crates/rebel-lang/src/typing.rs b/crates/rebel-lang/src/typing.rs index aad2fb3..5eb781e 100644 --- a/crates/rebel-lang/src/typing.rs +++ b/crates/rebel-lang/src/typing.rs @@ -27,12 +27,13 @@ pub enum Type { Int, Str, Tuple(Vec), - Array(Box, ArrayLen), + Array(Box), Struct(HashMap), Fn(Box), } impl Type { + #[allow(clippy::only_used_in_recursion)] pub fn unify(self, other: Type, coerce: Coerce) -> Result { use Type::*; @@ -55,10 +56,9 @@ impl Type { .collect::>()?, ) } - (Array(self_inner, self_len), Array(other_inner, other_len)) => Array( - Box::new(self_inner.unify(*other_inner, coerce)?), - ArrayLen::unify(self_len, other_len, coerce)?, - ), + (Array(self_inner), Array(other_inner)) => { + Array(Box::new(self_inner.unify(*other_inner, coerce)?)) + } (Struct(self_entries), Struct(mut other_entries)) => { if self_entries.len() != other_entries.len() { return Err(TypeError); @@ -111,7 +111,7 @@ impl Type { .map(|elem| Self::ast_type(ctx, elem)) .collect::>()?, ), - Literal::Array(typ, len) => Array(Box::new(Self::ast_type(ctx, typ)?), (*len).into()), + Literal::Array(typ) => Array(Box::new(Self::ast_type(ctx, typ)?)), Literal::Struct(entries) => Struct( entries .iter() @@ -158,14 +158,11 @@ impl Type { Ok(match (tl, op, tr) { (Str, Add, Str) => Str, (Int, Add, Int) => Int, - (Array(t1, l1), Add, Array(t2, l2)) => Array( - Box::new(t1.unify(*t2, Coerce::Common)?), - ArrayLen::add(l1, l2)?, - ), + (Array(t1), Add, Array(t2)) => Array(Box::new(t1.unify(*t2, Coerce::Common)?)), (Int, Sub, Int) => Int, - (Array(t1, _), Sub, Array(t2, _)) => { + (Array(t1), Sub, Array(t2)) => { (*t1).clone().unify(*t2, Coerce::Compare)?; - Array(t1, ArrayLen::Dynamic) + Array(t1) } (Int, Mul, Int) => Int, (Int, Div, Int) => Int, @@ -215,7 +212,7 @@ impl Type { let expr_type = Self::ast_expr_type(ctx, expr)?; let index_type = Self::ast_expr_type(ctx, index)?; - let Array(elem_type, _) = expr_type else { + let Array(elem_type) = expr_type else { return Err(TypeError); }; if index_type != Int { @@ -326,7 +323,7 @@ impl Type { (Type::Int, _) => Ok(()), (Type::Str, _) => Ok(()), (Type::Tuple(_), expr::StrKind::Script) => Ok(()), - (Type::Array(_, _), expr::StrKind::Script) => Ok(()), + (Type::Array(_), expr::StrKind::Script) => Ok(()), _ => Err(TypeError), } } @@ -351,12 +348,11 @@ impl Type { .map(|elem| Self::ast_expr_type(ctx, elem)) .collect::>()?, ), - Literal::Array(elems) => Array( - Box::new(elems.iter().try_fold(Type::Free, |acc, elem| { + Literal::Array(elems) => Array(Box::new( + elems.iter().try_fold(Type::Free, |acc, elem| { acc.unify(Self::ast_expr_type(ctx, elem)?, Coerce::Common) - })?), - ArrayLen::Fixed(elems.len().try_into().or(Err(TypeError))?), - ), + })?, + )), Literal::Struct(entries) => Struct( entries .iter() @@ -392,7 +388,7 @@ impl Display for Type { } f.write_str(")") } - Type::Array(typ, len) => write!(f, "[{typ}{len}]"), + Type::Array(typ) => write!(f, "[{typ}]"), Type::Struct(entries) => { let mut first = true; f.write_str("{")?; @@ -410,58 +406,3 @@ impl Display for Type { } } } - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum ArrayLen { - Free, - Fixed(u32), - Dynamic, -} - -impl ArrayLen { - fn unify(self, other: Self, coerce: Coerce) -> Result { - use ArrayLen::*; - - Ok(match (self, other, coerce) { - (Free, len, _) => len, - (len, Free, _) => len, - (l1, l2, _) if l1 == l2 => l1, - (_, _, Coerce::Common) => Dynamic, - (Dynamic, Fixed(_), Coerce::Compare | Coerce::Assign) => Dynamic, - (Fixed(_), Dynamic, Coerce::Compare) => Dynamic, - _ => return Err(TypeError), - }) - } - - fn add(self, other: Self) -> Result { - use ArrayLen::*; - - Ok(match (self, other) { - (Free, _) => return Err(TypeError), - (_, Free) => return Err(TypeError), - (Dynamic, _) => Dynamic, - (_, Dynamic) => Dynamic, - (Fixed(l1), Fixed(l2)) => Fixed(l1 + l2), - }) - } -} - -impl From for ArrayLen { - fn from(value: typ::ArrayLen) -> Self { - match value { - typ::ArrayLen::Free => ArrayLen::Free, - typ::ArrayLen::Fixed(len) => ArrayLen::Fixed(len), - typ::ArrayLen::Dynamic => ArrayLen::Dynamic, - } - } -} - -impl Display for ArrayLen { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ArrayLen::Free => write!(f, "; _"), - ArrayLen::Fixed(len) => write!(f, "; {len}"), - ArrayLen::Dynamic => Ok(()), - } - } -} diff --git a/crates/rebel-lang/src/value.rs b/crates/rebel-lang/src/value.rs index fe2caa2..ab21d0b 100644 --- a/crates/rebel-lang/src/value.rs +++ b/crates/rebel-lang/src/value.rs @@ -9,7 +9,7 @@ use rebel_parse::ast::{self, expr}; use crate::{ func::{Func, FuncDef}, scope::Context, - typing::{self, ArrayLen, Coerce, Type, TypeError, TypeFamily}, + typing::{self, Coerce, Type, TypeFamily}, }; #[derive(Debug)] @@ -42,10 +42,7 @@ impl Value { .map(|elem| elem.typ()) .collect::>()?, ), - Value::Array(elems) => Type::Array( - Box::new(Self::array_elem_type(elems)?), - ArrayLen::Fixed(elems.len().try_into().or(Err(TypeError))?), - ), + Value::Array(elems) => Type::Array(Box::new(Self::array_elem_type(elems)?)), Value::Struct(entries) => Type::Struct( entries .iter() diff --git a/crates/rebel-parse/src/ast/typ.rs b/crates/rebel-parse/src/ast/typ.rs index 18ea827..939b67d 100644 --- a/crates/rebel-parse/src/ast/typ.rs +++ b/crates/rebel-parse/src/ast/typ.rs @@ -11,17 +11,10 @@ pub enum Type<'a> { pub enum Literal<'a> { Unit, Tuple(Vec>), - Array(Box>, ArrayLen), + Array(Box>), Struct(Vec>), } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum ArrayLen { - Free, - Fixed(u32), - Dynamic, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct StructField<'a> { pub name: &'a str, diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs index c9fe1e2..b1ac32f 100644 --- a/crates/rebel-parse/src/grammar/recipe.rs +++ b/crates/rebel-parse/src/grammar/recipe.rs @@ -72,24 +72,13 @@ peg::parser! { / p('(') elements:(typ() ** p(',')) p(',')? p(')') { typ::Literal::Tuple(elements) } - / p('[') typ:typ() len:array_len() p(']') { - typ::Literal::Array(Box::new(typ), len) + / p('[') typ:typ() p(']') { + typ::Literal::Array(Box::new(typ)) } / p('{') entries:delimited(, ) p('}') { typ::Literal::Struct(entries) } - rule array_len() -> typ::ArrayLen - = p(';') len:number() { ? - Ok(typ::ArrayLen::Fixed(len.try_into().or(Err("Invalid array length"))?)) - } - / p(';') [Token::Ident(name) if *name == "_"] { - typ::ArrayLen::Free - } - / { - typ::ArrayLen::Dynamic - } - rule pat() -> Pat<'a> = path:path() { Pat::Path(path) } -- cgit v1.2.3