yanais/crates/yn-qgy4hbz-core/src/lib.rs
2023-09-25 15:56:06 +02:00

335 lines
8.8 KiB
Rust

//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<str>),
Ignore,
}
/*
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DefinFlags: u8 {
const PUBLIC = 0b00000001;
const MUTABLE = 0b00000010;
}
}
#[derive(Clone, Debug)]
pub struct Defin {
pub flags: DefinFlags,
pub value: Expr,
pub name: Box<str>,
}
#[derive(Clone, Debug)]
pub struct Block {
pub defs: Box<[Defin]>,
pub res: Box<Expr>,
}
*/
#[derive(Clone, Debug)]
pub struct Record {
pub fields: Vec<(Option<Box<str>>, Expr)>,
}
#[derive(Clone, Debug)]
pub struct FullPattern {
pub pat: Pattern,
pub pty: Option<Box<Expr>>,
}
#[derive(Clone, Debug)]
pub enum Expr {
TyTy,
Lambda {
pat: FullPattern,
exp: Box<Expr>,
},
SelfRecur {
pat: Pattern,
inner: Box<Expr>,
},
LetBind {
kvs: Vec<(FullPattern, Expr)>,
inner: Box<Expr>,
},
Apply {
lam: Box<Expr>,
args: Vec<Expr>,
},
Ref(usize),
Record(Record),
TyRecord(Record),
Select {
prim: Box<Expr>,
then: Vec<Box<str>>,
},
//Block(Block),
}
impl Pattern {
pub fn push_to(&self, env: &mut ParseEnv<'_>) {
match self {
Pattern::Ident(i) => env.names.push(i.clone()),
Pattern::Ignore => {}
}
}
pub fn pop_from(&self, env: &mut ParseEnv<'_>) {
match self {
Pattern::Ident(_) => {
env.names.pop();
}
Pattern::Ignore => {}
}
}
pub fn in_this<T>(&self, env: &mut ParseEnv<'_>, f: impl FnOnce(&mut ParseEnv<'_>) -> T) -> T {
self.push_to(env);
let height = env.names.len();
let ret = f(env);
assert_eq!(env.names.len(), height);
self.pop_from(env);
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, .. } = match env.lxr.next().transpose()? {
None => return Ok(None),
Some(x) => x,
};
Ok(match kind {
Tok::PatOut(i) => Some(Pattern::Ident(i)),
Tok::PatIgnore => Some(Pattern::Ignore),
_ => {
env.lxr = backup;
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,
};
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<Option<Self>, 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 = Expr::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<Expr, Perr> {
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<Self, Perr> {
fn parse_env_inner(env: &mut ParseEnv<'_>) -> Result<Expr, Perr> {
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
}
}