diff --git a/compiler/cl-ast/src/ast.rs b/compiler/cl-ast/src/ast.rs index 93e306c..0ee7ecd 100644 --- a/compiler/cl-ast/src/ast.rs +++ b/compiler/cl-ast/src/ast.rs @@ -413,6 +413,7 @@ pub struct Let { pub enum Pattern { Name(Sym), Literal(Literal), + Rest(Option>), Ref(Mutability, Box), Tuple(Vec), Array(Vec), @@ -505,6 +506,8 @@ pub enum UnaryKind { Deref, Neg, Not, + RangeInc, + RangeExc, /// A Loop expression: `loop` [`Block`] Loop, /// Unused diff --git a/compiler/cl-ast/src/ast_impl.rs b/compiler/cl-ast/src/ast_impl.rs index 2fbf564..1c9d29e 100644 --- a/compiler/cl-ast/src/ast_impl.rs +++ b/compiler/cl-ast/src/ast_impl.rs @@ -463,6 +463,8 @@ mod display { match self { Pattern::Name(sym) => sym.fmt(f), Pattern::Literal(literal) => literal.fmt(f), + Pattern::Rest(Some(name)) => write!(f, "..{name}"), + Pattern::Rest(None) => "..".fmt(f), Pattern::Ref(mutability, pattern) => write!(f, "&{mutability}{pattern}"), Pattern::Tuple(patterns) => separate(patterns, ", ")(f.delimit(INLINE_PARENS)), Pattern::Array(patterns) => separate(patterns, ", ")(f.delimit(INLINE_SQUARE)), @@ -590,6 +592,8 @@ mod display { UnaryKind::Deref => "*", UnaryKind::Neg => "-", UnaryKind::Not => "!", + UnaryKind::RangeExc => "..", + UnaryKind::RangeInc => "..=", UnaryKind::At => "@", UnaryKind::Tilde => "~", } @@ -894,6 +898,9 @@ mod convert { }; Pattern::TupleStruct(path, args) } + ExprKind::Unary(Unary { kind: UnaryKind::RangeExc, tail }) => { + Pattern::Rest(Some(Pattern::try_from(*tail)?.into())) + } ExprKind::Structor(Structor { to, init }) => { let fields = init .into_iter() @@ -934,9 +941,9 @@ mod path { } /// Checks whether this path ends in the given [Sym] - pub fn ends_with(&self, name: &Sym) -> bool { + pub fn ends_with(&self, name: &str) -> bool { match self.parts.as_slice() { - [.., PathPart::Ident(last)] => name == last, + [.., PathPart::Ident(last)] => name == &**last, _ => false, } } diff --git a/compiler/cl-ast/src/ast_visitor/fold.rs b/compiler/cl-ast/src/ast_visitor/fold.rs index 29939ed..8210170 100644 --- a/compiler/cl-ast/src/ast_visitor/fold.rs +++ b/compiler/cl-ast/src/ast_visitor/fold.rs @@ -246,6 +246,8 @@ pub trait Fold { match p { Pattern::Name(sym) => Pattern::Name(self.fold_sym(sym)), Pattern::Literal(literal) => Pattern::Literal(self.fold_literal(literal)), + Pattern::Rest(Some(name)) => Pattern::Rest(Some(self.fold_pattern(*name).into())), + Pattern::Rest(None) => Pattern::Rest(None), Pattern::Ref(mutability, pattern) => Pattern::Ref( self.fold_mutability(mutability), Box::new(self.fold_pattern(*pattern)), diff --git a/compiler/cl-ast/src/ast_visitor/visit.rs b/compiler/cl-ast/src/ast_visitor/visit.rs index bf159a9..df1c17b 100644 --- a/compiler/cl-ast/src/ast_visitor/visit.rs +++ b/compiler/cl-ast/src/ast_visitor/visit.rs @@ -211,6 +211,8 @@ pub trait Visit<'a>: Sized { match p { Pattern::Name(name) => self.visit_sym(name), Pattern::Literal(literal) => self.visit_literal(literal), + Pattern::Rest(Some(name)) => self.visit_pattern(name), + Pattern::Rest(None) => {} Pattern::Ref(mutability, pattern) => { self.visit_mutability(mutability); self.visit_pattern(pattern); @@ -247,7 +249,7 @@ pub trait Visit<'a>: Sized { self.visit_pattern(pat); self.visit_expr(expr); } - + fn visit_assign(&mut self, a: &'a Assign) { let Assign { parts } = a; let (head, tail) = parts.as_ref(); diff --git a/compiler/cl-interpret/src/builtin.rs b/compiler/cl-interpret/src/builtin.rs index c705719..1dec7ac 100644 --- a/compiler/cl-interpret/src/builtin.rs +++ b/compiler/cl-interpret/src/builtin.rs @@ -167,8 +167,6 @@ pub const Builtins: &[Builtin] = &builtins![ ConValue::Ref(r) => return len(env, slice::from_ref(r.as_ref())), ConValue::Array(t) => t.len() as _, ConValue::Tuple(t) => t.len() as _, - ConValue::RangeExc(start, end) => (end - start) as _, - ConValue::RangeInc(start, end) => (end - start + 1) as _, _ => Err(Error::TypeError)?, }) } @@ -279,20 +277,25 @@ pub const Math: &[Builtin] = &builtins![ }) } - /// Exclusive Range `a..b` - fn range_exc(from, to) { - let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else { - Err(Error::TypeError)? - }; - Ok(ConValue::RangeExc(from, to)) + #[allow(non_snake_case)] + fn RangeExc(start, end) { + Ok(ConValue::TupleStruct(Box::new(( + "RangeExc", Box::new([start.clone(), end.clone()]) + )))) } - /// Inclusive Range `a..=b` - fn range_inc(from, to) { - let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else { - Err(Error::TypeError)? - }; - Ok(ConValue::RangeInc(from, to)) + #[allow(non_snake_case)] + fn RangeInc(start, end) { + Ok(ConValue::TupleStruct(Box::new(( + "RangeInc", Box::new([start.clone(), end.clone()]) + )))) + } + + #[allow(non_snake_case)] + fn RangeTo(end) { + Ok(ConValue::TupleStruct(Box::new(( + "RangeInc", Box::new([end.clone()]) + )))) } /// Negates the ConValue diff --git a/compiler/cl-interpret/src/convalue.rs b/compiler/cl-interpret/src/convalue.rs index 2f6db3f..cccae7e 100644 --- a/compiler/cl-interpret/src/convalue.rs +++ b/compiler/cl-interpret/src/convalue.rs @@ -35,14 +35,10 @@ pub enum ConValue { Array(Box<[ConValue]>), /// A tuple Tuple(Box<[ConValue]>), - /// An exclusive range - RangeExc(Integer, Integer), - /// An inclusive range - RangeInc(Integer, Integer), /// A value of a product type Struct(Box<(Sym, HashMap)>), /// A value of a product type with anonymous members - TupleStruct(Box<(Sym, Box<[ConValue]>)>), + TupleStruct(Box<(&'static str, Box<[ConValue]>)>), /// An entire namespace Module(Box>>), /// A quoted expression @@ -61,18 +57,6 @@ impl ConValue { _ => Err(Error::TypeError)?, } } - pub fn range_exc(self, other: Self) -> IResult { - let (Self::Int(a), Self::Int(b)) = (self, other) else { - Err(Error::TypeError)? - }; - Ok(Self::RangeExc(a, b)) - } - pub fn range_inc(self, other: Self) -> IResult { - let (Self::Int(a), Self::Int(b)) = (self, other) else { - Err(Error::TypeError)? - }; - Ok(Self::RangeInc(a, b)) - } pub fn index(&self, index: &Self) -> IResult { let Self::Int(index) = index else { Err(Error::TypeError)? @@ -289,8 +273,6 @@ impl std::fmt::Display for ConValue { } ']'.fmt(f) } - ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1), - ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"), ConValue::Tuple(tuple) => { '('.fmt(f)?; for (idx, element) in tuple.iter().enumerate() { diff --git a/compiler/cl-interpret/src/function.rs b/compiler/cl-interpret/src/function.rs index fcd2b7f..6297c22 100644 --- a/compiler/cl-interpret/src/function.rs +++ b/compiler/cl-interpret/src/function.rs @@ -59,7 +59,7 @@ impl Callable for Function { return Err(Error::ArgNumber { want: bind.len(), got: args.len() }); } if self.is_constructor { - return Ok(ConValue::TupleStruct(Box::new((*name, args.into())))); + return Ok(ConValue::TupleStruct(Box::new((Sym::to_ref(name), args.into())))); } let Some(body) = body else { return Err(Error::NotDefined(*name)); diff --git a/compiler/cl-interpret/src/function/collect_upvars.rs b/compiler/cl-interpret/src/function/collect_upvars.rs index 77cec91..53c0039 100644 --- a/compiler/cl-interpret/src/function/collect_upvars.rs +++ b/compiler/cl-interpret/src/function/collect_upvars.rs @@ -107,6 +107,10 @@ impl<'a> Visit<'a> for CollectUpvars<'_> { self.bind_name(name); } Pattern::Literal(literal) => self.visit_literal(literal), + Pattern::Rest(Some(name)) => { + self.visit_pattern(name); + } + Pattern::Rest(None) => {} Pattern::Ref(mutability, pattern) => { self.visit_mutability(mutability); self.visit_pattern(pattern); diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 5a646fa..d899a8e 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -542,8 +542,8 @@ impl Interpret for Binary { BinaryKind::NotEq => head.neq(&tail), BinaryKind::GtEq => head.gt_eq(&tail), BinaryKind::Gt => head.gt(&tail), - BinaryKind::RangeExc => head.range_exc(tail), - BinaryKind::RangeInc => head.range_inc(tail), + BinaryKind::RangeExc => env.call("RangeExc".into(), &[head, tail]), + BinaryKind::RangeInc => env.call("RangeInc".into(), &[head, tail]), BinaryKind::BitAnd => head & tail, BinaryKind::BitOr => head | tail, BinaryKind::BitXor => head ^ tail, @@ -617,6 +617,14 @@ impl Interpret for Unary { let operand = tail.interpret(env)?; env.call("not".into(), &[operand]) } + UnaryKind::RangeExc => { + let operand = tail.interpret(env)?; + env.call("RangeTo".into(), &[operand]) + } + UnaryKind::RangeInc => { + let operand = tail.interpret(env)?; + env.call("RangeToInc".into(), &[operand]) + } UnaryKind::At => { let operand = tail.interpret(env)?; println!("{operand}"); @@ -855,8 +863,21 @@ impl Interpret for For { let cond = cond.interpret(env)?; // TODO: A better iterator model let mut bounds: Box> = match &cond { - &ConValue::RangeExc(a, b) => Box::new((a..b).map(ConValue::Int)), - &ConValue::RangeInc(a, b) => Box::new((a..=b).map(ConValue::Int)), + ConValue::TupleStruct(inner) => match &**inner { + ("RangeExc", values) => match **values { + [ConValue::Int(from), ConValue::Int(to)] => { + Box::new((from..to).map(ConValue::Int)) + } + _ => Err(Error::NotIterable)?, + }, + ("RangeInc", values) => match **values { + [ConValue::Int(from), ConValue::Int(to)] => { + Box::new((from..=to).map(ConValue::Int)) + } + _ => Err(Error::NotIterable)?, + }, + _ => Err(Error::NotIterable)?, + }, ConValue::Array(a) => Box::new(a.iter().cloned()), ConValue::String(s) => Box::new(s.chars().map(ConValue::Char)), _ => Err(Error::TypeError)?, diff --git a/compiler/cl-interpret/src/pattern.rs b/compiler/cl-interpret/src/pattern.rs index eb6d678..586d500 100644 --- a/compiler/cl-interpret/src/pattern.rs +++ b/compiler/cl-interpret/src/pattern.rs @@ -8,7 +8,10 @@ use crate::{ error::{Error, IResult}, }; use cl_ast::{Literal, Pattern, Sym}; -use std::{collections::HashMap, rc::Rc}; +use std::{ + collections::{HashMap, VecDeque}, + rc::Rc, +}; /// Gets the path variables in the given Pattern pub fn variables(pat: &Pattern) -> Vec<&Sym> { @@ -17,6 +20,8 @@ pub fn variables(pat: &Pattern) -> Vec<&Sym> { Pattern::Name(name) if &**name == "_" => {} Pattern::Name(name) => set.push(name), Pattern::Literal(_) => {} + Pattern::Rest(Some(pattern)) => patvars(set, pattern), + Pattern::Rest(None) => {} Pattern::Ref(_, pattern) => patvars(set, pattern), Pattern::Tuple(patterns) | Pattern::Array(patterns) => { patterns.iter().for_each(|pat| patvars(set, pat)) @@ -37,6 +42,41 @@ pub fn variables(pat: &Pattern) -> Vec<&Sym> { set } +fn rest_binding<'pat>( + sub: &mut HashMap<&'pat Sym, ConValue>, + mut patterns: &'pat [Pattern], + mut values: VecDeque, +) -> IResult)>> { + // Bind the head of the list + while let [pattern, tail @ ..] = patterns { + if matches!(pattern, Pattern::Rest(_)) { + break; + } + let value = values + .pop_front() + .ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?; + append_sub(sub, pattern, value)?; + patterns = tail; + } + // Bind the tail of the list + while let [head @ .., pattern] = patterns { + if matches!(pattern, Pattern::Rest(_)) { + break; + } + let value = values + .pop_back() + .ok_or_else(|| Error::PatFailed(Box::new(pattern.clone())))?; + append_sub(sub, pattern, value)?; + patterns = head; + } + // Bind the ..rest of the list + match patterns { + [] | [Pattern::Rest(None)] => Ok(None), + [Pattern::Rest(Some(pattern))] => Ok(Some((pattern.as_ref(), values))), + _ => Err(Error::PatFailed(Box::new(Pattern::Array(patterns.into())))), + } +} + /// Appends a substitution to the provided table pub fn append_sub<'pat>( sub: &mut HashMap<&'pat Sym, ConValue>, @@ -44,18 +84,6 @@ pub fn append_sub<'pat>( value: ConValue, ) -> IResult<()> { match (pat, value) { - (Pattern::Array(patterns), ConValue::Array(values)) - | (Pattern::Tuple(patterns), ConValue::Tuple(values)) => { - if patterns.len() != values.len() { - Err(Error::ArgNumber { want: patterns.len(), got: values.len() })? - } - for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) { - append_sub(sub, pat, value)?; - } - Ok(()) - } - (Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()), - (Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => { (*a == b).then_some(()).ok_or(Error::NotAssignable) } @@ -73,6 +101,25 @@ pub fn append_sub<'pat>( } (Pattern::Literal(_), _) => Err(Error::NotAssignable), + (Pattern::Rest(Some(pat)), value) => match (pat.as_ref(), value) { + (Pattern::Literal(Literal::Int(a)), ConValue::Int(b)) => { + (b < *a as _).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::Char(a)), ConValue::Char(b)) => { + (b < *a as _).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::Bool(a)), ConValue::Bool(b)) => { + (!b & *a).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::Float(a)), ConValue::Float(b)) => { + (b < *a as _).then_some(()).ok_or(Error::NotAssignable) + } + (Pattern::Literal(Literal::String(a)), ConValue::String(b)) => { + (&*b < a).then_some(()).ok_or(Error::NotAssignable) + } + _ => Err(Error::NotAssignable) + }, + (Pattern::Name(name), _) if "_".eq(&**name) => Ok(()), (Pattern::Name(name), value) => { sub.insert(name, value); @@ -81,14 +128,43 @@ pub fn append_sub<'pat>( (Pattern::Ref(_, pat), ConValue::Ref(r)) => append_sub(sub, pat, Rc::unwrap_or_clone(r)), + (Pattern::Array(patterns), ConValue::Array(values)) => { + match rest_binding(sub, patterns, values.into_vec().into())? { + Some((pattern, values)) => { + append_sub(sub, pattern, ConValue::Array(Vec::from(values).into())) + } + _ => Ok(()), + } + } + + (Pattern::Tuple(patterns), ConValue::Empty) if patterns.is_empty() => Ok(()), + (Pattern::Tuple(patterns), ConValue::Tuple(values)) => { + match rest_binding(sub, patterns, values.into_vec().into())? { + Some((pattern, values)) => { + append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into())) + } + _ => Ok(()), + } + } + + (Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => { + let (name, values) = *parts; + if !path.ends_with(name) { + Err(Error::TypeError)? + } + match rest_binding(sub, patterns, values.into_vec().into())? { + Some((pattern, values)) => { + append_sub(sub, pattern, ConValue::Tuple(Vec::from(values).into())) + } + _ => Ok(()), + } + } + (Pattern::Struct(path, patterns), ConValue::Struct(parts)) => { let (name, mut values) = *parts; if !path.ends_with(&name) { Err(Error::TypeError)? } - if patterns.len() != values.len() { - return Err(Error::ArgNumber { want: patterns.len(), got: values.len() }); - } for (name, pat) in patterns { let value = values.remove(name).ok_or(Error::TypeError)?; match pat { @@ -101,23 +177,9 @@ pub fn append_sub<'pat>( Ok(()) } - (Pattern::TupleStruct(path, patterns), ConValue::TupleStruct(parts)) => { - let (name, values) = *parts; - if !path.ends_with(&name) { - Err(Error::TypeError)? - } - if patterns.len() != values.len() { - Err(Error::ArgNumber { want: patterns.len(), got: values.len() })? - } - for (pat, value) in patterns.iter().zip(Vec::from(values).into_iter()) { - append_sub(sub, pat, value)?; - } - Ok(()) - } - (pat, value) => { eprintln!("Could not match pattern `{pat}` with value `{value}`!"); - Err(Error::NotAssignable) + Ok(()) } } } diff --git a/compiler/cl-parser/src/parser/prec.rs b/compiler/cl-parser/src/parser/prec.rs index 88cffc2..d3b20bd 100644 --- a/compiler/cl-parser/src/parser/prec.rs +++ b/compiler/cl-parser/src/parser/prec.rs @@ -330,7 +330,7 @@ impl From for Precedence { use UnaryKind as Op; match value { Op::Loop => Precedence::Assign, - Op::Deref | Op::Neg | Op::Not | Op::At | Op::Tilde => Precedence::Unary, + _ => Precedence::Unary, } } } @@ -351,6 +351,8 @@ operator! { Star => Deref, Minus => Neg, Bang => Not, + DotDot => RangeExc, + DotDotEq => RangeInc, At => At, Tilde => Tilde, }; diff --git a/compiler/cl-repl/examples/yaml.rs b/compiler/cl-repl/examples/yaml.rs index 76f6663..0dde597 100644 --- a/compiler/cl-repl/examples/yaml.rs +++ b/compiler/cl-repl/examples/yaml.rs @@ -430,6 +430,7 @@ pub mod yamlify { match self { Pattern::Name(name) => y.value(name), Pattern::Literal(literal) => y.value(literal), + Pattern::Rest(name) => y.pair("Rest", name), Pattern::Ref(mutability, pattern) => { y.pair("mutability", mutability).pair("subpattern", pattern) }