move pattern stuff into separate file

This commit is contained in:
Alain Zscheile 2023-09-26 11:40:05 +02:00
parent c63329d59d
commit 38b8b54104
2 changed files with 201 additions and 186 deletions

View file

@ -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<str>, parser::Offset),
Record(Record<Pattern>),
TyRecord(Record<Pattern>),
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<V> {
pub span: SourceSpan,
@ -65,35 +35,6 @@ impl<V: core::cmp::PartialEq> core::cmp::PartialEq for Record<V> {
}
}
#[derive(Clone, Debug)]
pub struct FullPattern {
pub pat: Pattern,
pub pty: Option<Box<(SourceSpan, Expr)>>,
}
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<Box<str>>) -> 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<T>(
&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<Option<Self>, 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::<Pattern>::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<Option<Self>, 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<T: Parse> MaybeParse for Record<T> {
const DFL_CTX: &'static str = "record";

View file

@ -0,0 +1,198 @@
/*
* SPDX-FileCopyrightText: 2023 Alain Zscheile <fogti+devel@ytrizja.de>
*
* 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<str>, parser::Offset),
Record(Record<Pattern>),
TyRecord(Record<Pattern>),
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<Box<(SourceSpan, Expr)>>,
}
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<Box<str>>) -> 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<T>(
&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<Option<Self>, 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::<Pattern>::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<Option<Self>, 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 }))
}
}