diff --git a/crates/yn-qgy4hbz-core/src/lib.rs b/crates/yn-qgy4hbz-core/src/lib.rs index 5eef29b..211544e 100644 --- a/crates/yn-qgy4hbz-core/src/lib.rs +++ b/crates/yn-qgy4hbz-core/src/lib.rs @@ -10,6 +10,9 @@ use parser::{ Env as ParseEnv, Error as Perr, ErrorKind as Pek, MaybeParse, Parse, Token, TokenKind as Tok, }; +mod pat; +pub use pat::{FullPattern, Pattern}; + mod typeck; pub use typeck::Error as TypeckError; @@ -17,39 +20,6 @@ use miette::SourceSpan; use std::collections::VecDeque; -#[derive(Clone, Debug)] -pub enum Pattern { - Ident(Box, parser::Offset), - Record(Record), - TyRecord(Record), - Ignore, -} - -impl core::cmp::PartialEq for Pattern { - fn eq(&self, rhs: &Self) -> bool { - match (self, rhs) { - (Pattern::Ident(_, _), Pattern::Ident(_, _)) => true, - (Pattern::Ignore, Pattern::Ignore) => true, - (Pattern::Record(lxs), Pattern::Record(rxs)) => lxs == rxs, - (Pattern::TyRecord(lxs), Pattern::TyRecord(rxs)) => lxs == rxs, - // TODO: should Ident(_) be = Ignore? - (_, _) => false, - } - } - fn ne(&self, rhs: &Self) -> bool { - match (self, rhs) { - (Pattern::Ident(_, _), Pattern::Ident(_, _)) => false, - (Pattern::Ignore, Pattern::Ignore) => false, - (Pattern::Record(lxs), Pattern::Record(rxs)) => lxs != rxs, - (Pattern::TyRecord(lxs), Pattern::TyRecord(rxs)) => lxs != rxs, - (Pattern::Ident(_, _), Pattern::Ignore) | (Pattern::Ignore, Pattern::Ident(_, _)) => { - false - } - (_, _) => true, - } - } -} - #[derive(Clone, Debug)] pub struct Record { pub span: SourceSpan, @@ -65,35 +35,6 @@ impl core::cmp::PartialEq for Record { } } -#[derive(Clone, Debug)] -pub struct FullPattern { - pub pat: Pattern, - pub pty: Option>, -} - -impl core::cmp::PartialEq for FullPattern { - fn eq(&self, rhs: &Self) -> bool { - if self.pat != rhs.pat { - return false; - } - match (&self.pty, &rhs.pty) { - (None, None) => true, - (Some(_), None) | (None, Some(_)) => false, - (Some(lt), Some(rt)) => lt.1 == rt.1, - } - } - fn ne(&self, rhs: &Self) -> bool { - if self.pat == rhs.pat { - return false; - } - match (&self.pty, &rhs.pty) { - (None, None) => false, - (Some(_), None) | (None, Some(_)) => true, - (Some(lt), Some(rt)) => lt.1 != rt.1, - } - } -} - #[derive(Clone, Debug, PartialEq)] pub enum Expr { TyTy, @@ -140,130 +81,6 @@ pub enum Expr { }, } -impl Pattern { - pub fn push_to_penv(&self, env: &mut ParseEnv<'_>) { - match self { - Pattern::Ident(i, _) => env.names.push(i.clone()), - Pattern::Record(xs) | Pattern::TyRecord(xs) => { - xs.fields[..].iter().for_each(|i| i.1.push_to_penv(env)); - } - Pattern::Ignore => {} - } - } - - pub fn pop_from_penv(&self, env: &mut ParseEnv<'_>) { - match self { - Pattern::Ident(..) => { - env.names.pop(); - } - Pattern::Record(xs) | Pattern::TyRecord(xs) => { - xs.fields[..].iter().for_each(|i| i.1.pop_from_penv(env)); - } - Pattern::Ignore => {} - } - } - - pub fn extract_exports(&self, outp: &mut Vec>) -> Result<(), Perr> { - match self { - Pattern::Ident(i, loc) => { - let i2 = i.clone(); - if outp.iter().find(|j| i == *j).is_some() { - Err(Perr { - offset: usize::try_from(*loc).unwrap(), - kind: Pek::PatDupIdent(i2), - }) - } else { - outp.push(i2); - Ok(()) - } - } - Pattern::Record(xs) | Pattern::TyRecord(xs) => xs.fields[..] - .iter() - .try_for_each(|i| i.1.extract_exports(outp)), - Pattern::Ignore => Ok(()), - } - } - - pub fn in_this_penv( - &self, - env: &mut ParseEnv<'_>, - f: impl FnOnce(&mut ParseEnv<'_>) -> T, - ) -> T { - let orig_height = env.names.len(); - self.push_to_penv(env); - let height = env.names.len(); - let ret = f(env); - assert_eq!(env.names.len(), height); - self.pop_from_penv(env); - assert_eq!(env.names.len(), orig_height); - ret - } -} - -impl MaybeParse for Pattern { - const DFL_CTX: &'static str = "pattern"; - - fn maybe_parse(env: &mut ParseEnv<'_>) -> Result, Perr> { - let backup = env.lxr.clone(); - let Token { - kind, - offset: tok_start, - } = match env.lxr.next().transpose()? { - None => return Ok(None), - Some(x) => x, - }; - Ok(Some(match kind { - Tok::PatOut(i) => Pattern::Ident(i, tok_start), - Tok::PatIgnore => Pattern::Ignore, - Tok::Caret | Tok::Dot => { - let is_tylvl = kind == Tok::Caret; - if let Some(r) = Record::::maybe_parse(env)? { - if is_tylvl { - Pattern::TyRecord(r) - } else { - Pattern::Record(r) - } - } else { - let Token { kind, offset } = env.lxr.next_in_noeof("[^.]pattern")?; - return Err(parser::unexpected_token(offset, kind, "[^.]pattern")); - } - } - _ => { - env.lxr = backup; - return Ok(None); - } - })) - } -} - -impl MaybeParse for FullPattern { - const DFL_CTX: &'static str = "pattern"; - - fn maybe_parse(env: &mut ParseEnv<'_>) -> Result, Perr> { - let pat = match Pattern::maybe_parse(env)? { - None => return Ok(None), - Some(x) => x, - }; - - // check for name collisions - { - let mut exports = Vec::new(); - pat.extract_exports(&mut exports)?; - } - - let pty = if env.lxr.got(Tok::DubColon).is_some() { - let start_loc = usize::try_from(env.lxr.offset()).unwrap(); - let ptyx = Expr::parse(env)?; - let end_loc = usize::try_from(env.lxr.offset()).unwrap(); - Some(Box::new(((start_loc..end_loc).into(), ptyx))) - } else { - None - }; - - Ok(Some(FullPattern { pat, pty })) - } -} - impl MaybeParse for Record { const DFL_CTX: &'static str = "record"; diff --git a/crates/yn-qgy4hbz-core/src/pat.rs b/crates/yn-qgy4hbz-core/src/pat.rs new file mode 100644 index 0000000..f97d1f9 --- /dev/null +++ b/crates/yn-qgy4hbz-core/src/pat.rs @@ -0,0 +1,198 @@ +/* + * SPDX-FileCopyrightText: 2023 Alain Zscheile + * + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::parser::{ + self, Env as ParseEnv, Error as Perr, ErrorKind as Pek, MaybeParse, Parse, Token, + TokenKind as Tok, +}; +use super::{Expr, Record}; +use miette::SourceSpan; + +#[derive(Clone, Debug)] +pub enum Pattern { + Ident(Box, parser::Offset), + Record(Record), + TyRecord(Record), + Ignore, +} + +impl core::cmp::PartialEq for Pattern { + fn eq(&self, rhs: &Self) -> bool { + match (self, rhs) { + (Pattern::Ident(_, _), Pattern::Ident(_, _)) => true, + (Pattern::Ignore, Pattern::Ignore) => true, + (Pattern::Record(lxs), Pattern::Record(rxs)) => lxs == rxs, + (Pattern::TyRecord(lxs), Pattern::TyRecord(rxs)) => lxs == rxs, + // TODO: should Ident(_) be = Ignore? + (_, _) => false, + } + } + fn ne(&self, rhs: &Self) -> bool { + match (self, rhs) { + (Pattern::Ident(_, _), Pattern::Ident(_, _)) => false, + (Pattern::Ignore, Pattern::Ignore) => false, + (Pattern::Record(lxs), Pattern::Record(rxs)) => lxs != rxs, + (Pattern::TyRecord(lxs), Pattern::TyRecord(rxs)) => lxs != rxs, + (Pattern::Ident(_, _), Pattern::Ignore) | (Pattern::Ignore, Pattern::Ident(_, _)) => { + false + } + (_, _) => true, + } + } +} + +#[derive(Clone, Debug)] +pub struct FullPattern { + pub pat: Pattern, + pub pty: Option>, +} + +impl core::cmp::PartialEq for FullPattern { + fn eq(&self, rhs: &Self) -> bool { + if self.pat != rhs.pat { + return false; + } + match (&self.pty, &rhs.pty) { + (None, None) => true, + (Some(_), None) | (None, Some(_)) => false, + (Some(lt), Some(rt)) => lt.1 == rt.1, + } + } + fn ne(&self, rhs: &Self) -> bool { + if self.pat == rhs.pat { + return false; + } + match (&self.pty, &rhs.pty) { + (None, None) => false, + (Some(_), None) | (None, Some(_)) => true, + (Some(lt), Some(rt)) => lt.1 != rt.1, + } + } +} + +impl Pattern { + pub fn push_to_penv(&self, env: &mut ParseEnv<'_>) { + match self { + Pattern::Ident(i, _) => env.names.push(i.clone()), + Pattern::Record(xs) | Pattern::TyRecord(xs) => { + xs.fields[..].iter().for_each(|i| i.1.push_to_penv(env)); + } + Pattern::Ignore => {} + } + } + + pub fn pop_from_penv(&self, env: &mut ParseEnv<'_>) { + match self { + Pattern::Ident(..) => { + env.names.pop(); + } + Pattern::Record(xs) | Pattern::TyRecord(xs) => { + xs.fields[..].iter().for_each(|i| i.1.pop_from_penv(env)); + } + Pattern::Ignore => {} + } + } + + pub fn extract_exports(&self, outp: &mut Vec>) -> Result<(), Perr> { + match self { + Pattern::Ident(i, loc) => { + let i2 = i.clone(); + if outp.iter().find(|j| i == *j).is_some() { + Err(Perr { + offset: usize::try_from(*loc).unwrap(), + kind: Pek::PatDupIdent(i2), + }) + } else { + outp.push(i2); + Ok(()) + } + } + Pattern::Record(xs) | Pattern::TyRecord(xs) => xs.fields[..] + .iter() + .try_for_each(|i| i.1.extract_exports(outp)), + Pattern::Ignore => Ok(()), + } + } + + pub fn in_this_penv( + &self, + env: &mut ParseEnv<'_>, + f: impl FnOnce(&mut ParseEnv<'_>) -> T, + ) -> T { + let orig_height = env.names.len(); + self.push_to_penv(env); + let height = env.names.len(); + let ret = f(env); + assert_eq!(env.names.len(), height); + self.pop_from_penv(env); + assert_eq!(env.names.len(), orig_height); + ret + } +} + +impl MaybeParse for Pattern { + const DFL_CTX: &'static str = "pattern"; + + fn maybe_parse(env: &mut ParseEnv<'_>) -> Result, Perr> { + let backup = env.lxr.clone(); + let Token { + kind, + offset: tok_start, + } = match env.lxr.next().transpose()? { + None => return Ok(None), + Some(x) => x, + }; + Ok(Some(match kind { + Tok::PatOut(i) => Pattern::Ident(i, tok_start), + Tok::PatIgnore => Pattern::Ignore, + Tok::Caret | Tok::Dot => { + let is_tylvl = kind == Tok::Caret; + if let Some(r) = Record::::maybe_parse(env)? { + if is_tylvl { + Pattern::TyRecord(r) + } else { + Pattern::Record(r) + } + } else { + let Token { kind, offset } = env.lxr.next_in_noeof("[^.]pattern")?; + return Err(parser::unexpected_token(offset, kind, "[^.]pattern")); + } + } + _ => { + env.lxr = backup; + return Ok(None); + } + })) + } +} + +impl MaybeParse for FullPattern { + const DFL_CTX: &'static str = "pattern"; + + fn maybe_parse(env: &mut ParseEnv<'_>) -> Result, Perr> { + let pat = match Pattern::maybe_parse(env)? { + None => return Ok(None), + Some(x) => x, + }; + + // check for name collisions + { + let mut exports = Vec::new(); + pat.extract_exports(&mut exports)?; + } + + let pty = if env.lxr.got(Tok::DubColon).is_some() { + let start_loc = usize::try_from(env.lxr.offset()).unwrap(); + let ptyx = Expr::parse(env)?; + let end_loc = usize::try_from(env.lxr.offset()).unwrap(); + Some(Box::new(((start_loc..end_loc).into(), ptyx))) + } else { + None + }; + + Ok(Some(FullPattern { pat, pty })) + } +}