/* * SPDX-FileCopyrightText: 2023 Alain Zscheile * * SPDX-License-Identifier: Apache-2.0 */ //use bitflags::bitflags; pub mod parser; use parser::{ Env as ParseEnv, Error as Perr, ErrorKind as Pek, MaybeParse, Parse, Token, TokenKind as Tok, }; #[derive(Clone, Debug)] pub enum Pattern { Ident(Box), Record(Record), TyRecord(Record), Ignore, } #[derive(Clone, Debug)] pub struct Record { pub fields: Vec<(Option>, V)>, } #[derive(Clone, Debug)] pub struct FullPattern { pub pat: Pattern, pub pty: Option>, } #[derive(Clone, Debug)] pub enum Expr { TyTy, Lambda { pat: FullPattern, exp: Box, }, SelfRecur { pat: Pattern, inner: Box, }, LetBind { kvs: Vec<(FullPattern, Expr)>, inner: Box, }, Apply { lam: Box, args: Vec, }, Ref(usize), Record(Record), TyRecord(Record), Select { prim: Box, then: Vec>, }, } impl Pattern { pub fn push_to(&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(env)); } Pattern::Ignore => {} } } pub fn pop_from(&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(env)); } Pattern::Ignore => {} } } pub fn in_this(&self, env: &mut ParseEnv<'_>, f: impl FnOnce(&mut ParseEnv<'_>) -> T) -> T { let orig_height = env.names.len(); self.push_to(env); let height = env.names.len(); let ret = f(env); assert_eq!(env.names.len(), height); self.pop_from(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, .. } = match env.lxr.next().transpose()? { None => return Ok(None), Some(x) => x, }; Ok(Some(match kind { Tok::PatOut(i) => Pattern::Ident(i), 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, }; let pty = if env.lxr.got(Tok::DubColon).is_some() { Some(Box::new(Expr::parse(env)?)) } else { None }; Ok(Some(FullPattern { pat, pty })) } } impl MaybeParse for Record { const DFL_CTX: &'static str = "record"; fn maybe_parse(env: &mut ParseEnv<'_>) -> Result, Perr> { if env.lxr.got(Tok::LBrace).is_none() { return Ok(None); } let mut fields = Vec::new(); loop { let lxrbak = env.lxr.clone(); let name = { let Token { kind, .. } = env.lxr.next_in_noeof("record")?; if let Tok::DotIdent(i) = kind { if env.lxr.expect(Tok::Assign, "record =").is_ok() { Some(i) } else { None } } else if let Tok::RBrace = kind { env.lxr = lxrbak; break; } else { None } }; if name.is_none() { // backtrack env.lxr = lxrbak; } let expr = T::parse(env)?; env.lxr.expect(Tok::SemiColon, "record ;")?; fields.push((name, expr)); } env.lxr.expect(Tok::RBrace, "record")?; Ok(Some(Record { fields })) } } fn parse_minexpr(env: &mut ParseEnv<'_>) -> Result { let Token { offset: fi_offset, kind: fi_kind, } = env.lxr.next_in_noeof("expression")?; let mut ret = match fi_kind { Tok::Ident(i) => { if let Some(x) = env.lookup(&i) { Expr::Ref(x) } else { return Err(Perr { offset: fi_offset.try_into().unwrap(), kind: Pek::UnknownIdent(i), }); } } Tok::Lambda => { let pat = FullPattern::parse(env)?; env.lxr.expect(Tok::RArr, "lambda")?; let exp = Box::new(pat.pat.in_this(env, Expr::parse)?); return Ok(Expr::Lambda { pat, exp }); } Tok::Mu => { let pat = Pattern::parse(env)?; env.lxr.expect(Tok::RArr, "mu")?; let inner = Box::new(pat.in_this(env, Expr::parse)?); return Ok(Expr::SelfRecur { pat, inner }); } Tok::LParen => { let inner = Expr::parse(env)?; env.lxr.expect(Tok::RParen, "parens")?; return Ok(inner); } Tok::Caret => { return if let Some(r) = Record::maybe_parse(env)? { Ok(Expr::TyRecord(r)) } else { let Token { kind, offset } = env.lxr.next_in_noeof("^expression")?; Err(parser::unexpected_token(offset, kind, "^expression")) } } Tok::Dot => { return if let Some(r) = Record::maybe_parse(env)? { Ok(Expr::Record(r)) } else { let Token { kind, offset } = env.lxr.next_in_noeof(".expression")?; Err(parser::unexpected_token(offset, kind, ".expression")) } } /* Tok::LBrace => { let inner = ; env.lxr.expect(Tok::RBrace, "braces")?; return Ok(inner); } */ _ => return Err(parser::unexpected_token(fi_offset, fi_kind, "expression")), }; loop { let lxrbak = env.lxr.clone(); let Token { kind, .. } = match env.lxr.next() { Some(Ok(x)) => x, Some(Err(e)) => return Err(e), None => break, }; match kind { Tok::DotIdent(i) => { if let Expr::Select { then, .. } = &mut ret { then.push(i); } else { ret = Expr::Select { prim: Box::new(ret), then: vec![i], }; } } _ => { env.lxr = lxrbak; break; } } } Ok(ret) } impl Parse for Expr { fn parse(env: &mut ParseEnv<'_>) -> Result { fn parse_env_inner(env: &mut ParseEnv<'_>) -> Result { let mut letbinds = Vec::new(); while env.lxr.got(Tok::Let).is_some() { let key = FullPattern::parse(env)?; env.lxr.expect(Tok::Assign, "let expression =")?; let value = Expr::parse(env)?; key.pat.push_to(env); letbinds.push((key, value)); env.lxr.expect(Tok::SemiColon, "let expression ;")?; } let mut args = Vec::new(); let mut base = parse_minexpr(env)?; let reside_knamcnt = env.names.len(); loop { let lxrbak = env.lxr.clone(); let arg = parse_minexpr(env); assert_eq!(env.names.len(), reside_knamcnt); args.push(match arg { Ok(x) => x, Err(_) => { // do not eat errors without backtracking the lexer env.lxr = lxrbak; break; } }); } if !args.is_empty() { base = Expr::Apply { lam: Box::new(base), args, }; } if !letbinds.is_empty() { base = Expr::LetBind { kvs: letbinds, inner: Box::new(base), }; } Ok(base) } // make sure we don't leak names // (the assert_eq checks serve the same purpose) let knamcnt = env.names.len(); let res = parse_env_inner(env); assert!(env.names.len() >= knamcnt); env.names.truncate(knamcnt); res } }