doughlang: "fix" semi elision, add "fully qualified" paths, add proper pattern prec parsing.

This actually gets some old code parsing!
This commit is contained in:
2025-10-16 05:49:02 -04:00
parent 03d9682409
commit 1998558468
5 changed files with 314 additions and 172 deletions

View File

@@ -18,9 +18,19 @@ impl<T: Annotation, A: Annotation> std::fmt::Debug for Anno<T, A> {
pub trait Annotation: Clone + std::fmt::Display + std::fmt::Debug + PartialEq + Eq {}
impl<T: Clone + std::fmt::Debug + std::fmt::Display + PartialEq + Eq> Annotation for T {}
//
// TODO: Identifier interning
//
/// A qualified identifier
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FqPath {
// TODO: Identifier interning
pub parts: Vec<String>,
// TODO:
}
impl From<&str> for FqPath {
fn from(value: &str) -> Self {
Self { parts: vec![value.to_owned()] }
}
}
/// A literal value (boolean, character, integer, string)
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -40,26 +50,37 @@ pub enum Literal {
pub enum Pat {
/// Matches anything without binding
Ignore,
/// Matches nothing; used for macro substitution.
/// Matches nothing; used for macro substitution
MetId(String),
/// Matches anything, and binds it to a name
Name(String),
/// Matches against a named const value
Path(FqPath),
/// Matches a Struct Expression `Ident { Pat }`
Struct(String, Box<Pat>),
Struct(FqPath, Box<Pat>),
/// Matches a Tuple Struct Expression `Ident ( Pat )`
TupStruct(String, Box<Pat>),
/// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`).
Rest(Option<Box<Pat>>),
TupStruct(FqPath, Box<Pat>),
/// Matches a literal value by equality comparison
Lit(Literal),
/// Matches the elements of a tuple
Tuple(Vec<Pat>),
/// Matches the elements
Slice(Vec<Pat>),
/// Matches one of the provided alternates
Alt(Vec<Pat>),
/// Matches a typed pattern
Typed(Box<Pat>, Ty),
/// Matches a compound pattern
Op(PatOp, Vec<Pat>),
}
/// Operators on lists of patterns
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PatOp {
/// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`)
Rest,
/// Matches an exclusive bounded range (`0..100`)
RangeEx,
/// Matches the elements of a tuple
Tuple,
/// Matches the elements of a slice or array
Slice,
/// Matches one of a list of alternatives
Alt,
}
/// In-universe types
@@ -68,7 +89,7 @@ pub enum Ty {
/// `_`
Infer,
/// `(Identifier :: )* Identifier`
Named(String),
Named(FqPath),
/// `(..Tys)`
Tuple(Vec<Ty>),
/// `[Ty]`
@@ -78,11 +99,6 @@ pub enum Ty {
/// `[..Args, Rety]`
Fn(Vec<Ty>),
}
impl Default for Ty {
fn default() -> Self {
Self::Tuple(vec![])
}
}
/// A `let` binding
/// ```ignore
@@ -148,7 +164,7 @@ pub struct Struct(pub Pat);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr<A: Annotation = Span> {
/// An identifier
Id(String),
Id(FqPath),
/// A meta-identifier
MetId(String),
/// A literal bool, string, char, or int
@@ -197,11 +213,7 @@ impl<A: Annotation> Expr<A> {
pub fn is_place(&self) -> bool {
matches!(
self,
Self::Id(_)
| Self::Op(Op::Index, _)
| Self::Op(Op::Dot, _)
| Self::Op(Op::Path, _)
| Self::Op(Op::Deref, _)
Self::Id(_) | Self::Op(Op::Index, _) | Self::Op(Op::Dot, _) | Self::Op(Op::Deref, _)
)
}
@@ -237,8 +249,7 @@ pub enum Op {
Break, // break Expr
Return, // return Expr
Dot, // Expr . Expr
Path, // Expr :: Expr
Dot, // Expr . Expr
RangeEx, // Expr? ..Expr
RangeIn, // Expr? ..=Expr
@@ -290,6 +301,13 @@ impl Display for Literal {
}
}
impl Display for FqPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { parts } = self;
f.list(parts, "::")
}
}
impl<T: Display + Annotation, A: Annotation> Display for Anno<T, A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
@@ -351,7 +369,7 @@ impl Display for Struct {
let Self(pat) = self;
match pat {
Pat::Struct(name, bind) => match bind.as_ref() {
Pat::Tuple(parts) => f
Pat::Op(PatOp::Tuple, parts) => f
.delimit_indented(fmt!("struct {name} {{"), "}")
.list_wrap("\n", parts, ",\n", ",\n"),
other => write!(f, "{name} {{ {other} }}"),
@@ -431,7 +449,6 @@ impl Display for Op {
Op::Break => "break ".fmt(f),
Op::Return => "return ".fmt(f),
Op::Dot => ".".fmt(f),
Op::Path => "::".fmt(f),
Op::RangeEx => "..".fmt(f),
Op::RangeIn => "..=".fmt(f),
Op::Neg => "-".fmt(f),
@@ -486,17 +503,23 @@ impl Display for Pat {
Self::Lit(literal) => literal.fmt(f),
Self::MetId(name) => write!(f, "`{name}"),
Self::Name(name) => name.fmt(f),
Self::Path(path) => path.fmt(f),
Self::Struct(name, bind) => match bind.as_ref() {
Pat::Tuple(parts) => f.delimit(fmt!("{name} {{"), "}").list(parts, ", "),
Pat::Op(PatOp::Tuple, parts) => f.delimit(fmt!("{name} {{"), "}").list(parts, ", "),
other => write!(f, "{name} {{ {other} }}"),
},
Self::TupStruct(name, bind) => write!(f, "{name} {bind}"),
Self::Rest(Some(rest)) => write!(f, "..{rest}"),
Self::Rest(None) => write!(f, ".."),
Self::Tuple(pats) => f.delimit("(", ")").list(pats, ", "),
Self::Slice(pats) => f.delimit("[", "]").list(pats, ", "),
Self::Alt(pats) => f.delimit("<", ">").list(pats, " | "),
Self::Typed(pat, ty) => write!(f, "{pat}: {ty}"),
Self::Op(PatOp::Rest, pats) => match pats.as_slice() {
[] => write!(f, ".."),
[rest] => write!(f, "..{rest}"),
[from, to] => write!(f, "{from}..{to}"),
_ => f.list(pats, "<..>"),
},
Self::Op(PatOp::RangeEx, pats) => f.delimit("(", ")").list(pats, ".."),
Self::Op(PatOp::Tuple, pats) => f.delimit("(", ")").list(pats, ", "),
Self::Op(PatOp::Slice, pats) => f.delimit("[", "]").list(pats, ", "),
Self::Op(PatOp::Alt, pats) => f.delimit("<", ">").list(pats, " | "),
}
}
}
@@ -524,21 +547,35 @@ impl<A: Annotation> TryFrom<Expr<A>> for Pat {
fn try_from(value: Expr<A>) -> Result<Self, Self::Error> {
Ok(match value {
Expr::Id(name) if name == "_" => Self::Ignore,
Expr::Id(name) => Self::Name(name),
Expr::Id(FqPath { mut parts }) if parts.len() == 1 => {
match parts.pop().expect("parts should have len 1") {
ig if ig == "_" => Self::Ignore,
name => Self::Name(name),
}
}
Expr::Id(path) => Self::Path(path),
Expr::MetId(name) => Self::MetId(name),
Expr::Lit(literal) => Self::Lit(literal),
Expr::Op(Op::RangeEx, exprs) if exprs.is_empty() => Self::Rest(None),
Expr::Op(Op::RangeEx, mut exprs) if exprs.len() == 1 => {
Self::Rest(Some(Box::new(Self::try_from(exprs.remove(0))?)))
}
Expr::Op(Op::Tuple, exprs) => Self::Tuple(
Expr::Op(Op::RangeEx, exprs) => Self::Op(
if exprs.len() > 1 {
PatOp::RangeEx
} else {
PatOp::Rest
},
exprs
.into_iter()
.map(Self::try_from)
.collect::<Result<Vec<_>, Expr<A>>>()?,
),
Expr::Op(Op::Tuple, exprs) => Self::Op(
PatOp::Tuple,
exprs
.into_iter()
.map(Self::try_from)
.collect::<Result<_, _>>()?,
),
Expr::Op(Op::Array, exprs) => Self::Slice(
Expr::Op(Op::Array, exprs) => Self::Op(
PatOp::Slice,
exprs
.into_iter()
.map(Self::try_from)