doughlang: symbol interning and AST reparameterization
- intern: Add interners from cl-intern - ast: - Break AST into ternimals (AstTypes) and nonterminals (Expr, Pat, Bind, Make, Use) - Make AST generic over terminals (except operators, for now)
This commit is contained in:
116
Cargo.lock
generated
116
Cargo.lock
generated
@@ -2,23 +2,23 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cl-arena"
|
||||
version = "0.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b1af9ddfb0f2e11bd7f73f26ae78571e59767b2109acc2d62ef5fad6a23cf9d"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
@@ -45,6 +45,7 @@ dependencies = [
|
||||
name = "doughlang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cl-arena",
|
||||
"repline",
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -61,9 +62,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.179"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@@ -79,19 +80,18 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
@@ -99,22 +99,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -130,9 +130,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -155,9 +155,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
@@ -173,67 +173,3 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
@@ -5,4 +5,5 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
repline = "*"
|
||||
cl-arena = "*"
|
||||
unicode-ident = "1.0.12"
|
||||
|
||||
@@ -5,11 +5,13 @@ use std::{convert::Infallible, error::Error};
|
||||
use doughlang::{
|
||||
ast::{
|
||||
fold::{Fold, Foldable},
|
||||
types::{Literal, Path, Symbol},
|
||||
visit::{Visit, Walk},
|
||||
*,
|
||||
},
|
||||
lexer::Lexer,
|
||||
parser::{Parser, expr::Prec},
|
||||
span::Span,
|
||||
};
|
||||
|
||||
const CODE: &str = r#"
|
||||
@@ -86,6 +88,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
struct CountNodes {
|
||||
exprs: usize,
|
||||
macro_ids: usize,
|
||||
idents: usize,
|
||||
paths: usize,
|
||||
literals: usize,
|
||||
@@ -102,50 +105,54 @@ impl CountNodes {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for CountNodes {
|
||||
impl<'a, A: AstTypes> Visit<'a, A> for CountNodes {
|
||||
type Error = Infallible;
|
||||
|
||||
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
fn visit_macro_id(&mut self, _name: &'a A::MacroId) -> Result<(), Self::Error> {
|
||||
self.macro_ids += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_symbol(&mut self, _name: &'a A::Symbol) -> Result<(), Self::Error> {
|
||||
self.idents += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, _path: &'a A::Path) -> Result<(), Self::Error> {
|
||||
self.paths += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, _lit: &'a A::Literal) -> Result<(), Self::Error> {
|
||||
self.literals += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_use(&mut self, item: &'a Use<A>) -> Result<(), Self::Error> {
|
||||
self.uses += 1;
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_expr(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
self.exprs += 1;
|
||||
expr.children(self)
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> {
|
||||
self.idents += 1;
|
||||
name.children(self)
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, path: &'a Path) -> Result<(), Self::Error> {
|
||||
self.paths += 1;
|
||||
path.children(self)
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, lit: &'a Literal) -> Result<(), Self::Error> {
|
||||
self.literals += 1;
|
||||
lit.children(self)
|
||||
}
|
||||
|
||||
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||
self.uses += 1;
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
fn visit_pat(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
self.patterns += 1;
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
fn visit_bind(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
self.binds += 1;
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
fn visit_make(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
self.makes += 1;
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
fn visit_makearm(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
self.makearms += 1;
|
||||
item.children(self)
|
||||
}
|
||||
@@ -153,10 +160,26 @@ impl<'a> Visit<'a> for CountNodes {
|
||||
|
||||
struct Sevenfold;
|
||||
|
||||
impl<A: Annotation> Fold<A> for Sevenfold {
|
||||
impl Fold<DefaultTypes> for Sevenfold {
|
||||
type Error = ();
|
||||
|
||||
fn fold_annotation(&mut self, anno: Span) -> Result<Span, Self::Error> {
|
||||
Ok(anno)
|
||||
}
|
||||
|
||||
fn fold_literal(&mut self, _lit: Literal) -> Result<Literal, Self::Error> {
|
||||
Ok(Literal::Int(7, 10))
|
||||
}
|
||||
|
||||
fn fold_macro_id(&mut self, name: Symbol) -> Result<Symbol, Self::Error> {
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn fold_symbol(&mut self, name: Symbol) -> Result<Symbol, Self::Error> {
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn fold_path(&mut self, path: Path) -> Result<Path, Self::Error> {
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ use std::error::Error;
|
||||
|
||||
use doughlang::{
|
||||
ast::{
|
||||
visit::{Visit, Walk},
|
||||
*,
|
||||
types::{Literal, Path}, visit::{Visit, Walk}, *
|
||||
},
|
||||
intern::interned::Interned,
|
||||
lexer::Lexer,
|
||||
parser::{Parser, expr::Prec},
|
||||
};
|
||||
@@ -126,19 +126,15 @@ impl ToLisp {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for ToLisp {
|
||||
impl<'a> Visit<'a, DefaultTypes> for ToLisp {
|
||||
type Error = ();
|
||||
|
||||
fn visit(&mut self, walk: &'a impl Walk<'a>) -> Result<(), Self::Error> {
|
||||
walk.visit_in(self)
|
||||
}
|
||||
|
||||
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
fn visit_expr(&mut self, expr: &'a Expr) -> Result<(), Self::Error> {
|
||||
match expr {
|
||||
Expr::Omitted => print!("()"),
|
||||
Expr::Id(path) => path.visit_in(self)?,
|
||||
Expr::Id(path) => self.visit_path(path)?,
|
||||
Expr::MetId(id) => print!("`{id}"),
|
||||
Expr::Lit(literal) => literal.visit_in(self)?,
|
||||
Expr::Lit(literal) => self.visit_literal(literal)?,
|
||||
Expr::Use(import) => import.visit_in(self)?,
|
||||
Expr::Bind(bind) => bind.visit_in(self)?,
|
||||
Expr::Make(make) => make.visit_in(self)?,
|
||||
@@ -155,7 +151,12 @@ impl<'a> Visit<'a> for ToLisp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> {
|
||||
fn visit_macro_id(&mut self, name: &'a Interned<'static, str>) -> Result<(), Self::Error> {
|
||||
print!("{name}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_symbol(&mut self, name: &'a Interned<'static, str>) -> Result<(), Self::Error> {
|
||||
print!("{name}");
|
||||
Ok(())
|
||||
}
|
||||
@@ -165,7 +166,7 @@ impl<'a> Visit<'a> for ToLisp {
|
||||
print!("(at");
|
||||
for part in parts {
|
||||
print!(" ");
|
||||
part.visit_in(self)?;
|
||||
self.visit_symbol(part)?;
|
||||
}
|
||||
print!(")");
|
||||
Ok(())
|
||||
@@ -179,7 +180,7 @@ impl<'a> Visit<'a> for ToLisp {
|
||||
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||
match item {
|
||||
Use::Glob => print!("(use-glob)"),
|
||||
Use::Name(name) => name.visit_in(self)?,
|
||||
Use::Name(name) => self.visit_symbol(name)?,
|
||||
Use::Alias(name, alias) => print!("(use-alias {name} {alias})"),
|
||||
Use::Path(name, tree) => {
|
||||
print!("(use-path {name} ");
|
||||
@@ -198,7 +199,7 @@ impl<'a> Visit<'a> for ToLisp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
fn visit_pat(&mut self, item: &'a Pat) -> Result<(), Self::Error> {
|
||||
match item {
|
||||
Pat::Ignore => print!("(ignore)"),
|
||||
Pat::Never => print!("(never)"),
|
||||
@@ -217,7 +218,7 @@ impl<'a> Visit<'a> for ToLisp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
fn visit_bind(&mut self, item: &'a Bind) -> Result<(), Self::Error> {
|
||||
let Bind(op, generics, pattern, exprs) = item;
|
||||
print!("({} ", self.bind_op(*op));
|
||||
if !generics.is_empty() {
|
||||
@@ -236,11 +237,11 @@ impl<'a> Visit<'a> for ToLisp {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
fn visit_make(&mut self, item: &'a Make) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
fn visit_makearm(&mut self, item: &'a MakeArm) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,50 +90,50 @@ impl Weight {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for Weight {
|
||||
impl<'a, A: AstTypes> Visit<'a, A> for Weight {
|
||||
type Error = Infallible;
|
||||
|
||||
fn visit_expr<A: Annotation>(&mut self, item: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
fn visit_expr(&mut self, item: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, item: &'a str) -> Result<(), Self::Error> {
|
||||
fn visit_symbol(&mut self, item: &'a A::Symbol) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, item: &'a A::Path) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, item: &'a A::Literal) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_use(&mut self, item: &'a Use<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, item: &'a Path) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item) + size_of_val(item.parts.as_slice());
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_literal(&mut self, item: &'a Literal) -> Result<(), Self::Error> {
|
||||
fn visit_pat(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||
fn visit_bind(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
fn visit_make(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
fn visit_makearm(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
self.size += size_of_val(item);
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
252
src/ast.rs
252
src/ast.rs
@@ -1,22 +1,55 @@
|
||||
//! The Abstract Syntax Tree defines an interface between the parser and type checker
|
||||
|
||||
use crate::span::Span;
|
||||
|
||||
pub mod macro_matcher;
|
||||
|
||||
pub mod visit;
|
||||
|
||||
pub mod fold;
|
||||
use std::hash::Hash;
|
||||
|
||||
mod display;
|
||||
|
||||
/// An annotation: extra data added on to important AST nodes.
|
||||
pub mod fold;
|
||||
pub mod macro_matcher;
|
||||
pub mod types;
|
||||
pub mod visit;
|
||||
|
||||
pub use types::DefaultTypes;
|
||||
|
||||
/// An annotation: bounds on AST parameters
|
||||
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 {}
|
||||
|
||||
pub trait AstTypes: Annotation {
|
||||
/// An annotation on an arbitrary [Expr]
|
||||
type Annotation: Annotation;
|
||||
|
||||
/// A literal value
|
||||
type Literal: Annotation;
|
||||
|
||||
/// A (possibly interned) symbol or index which implements [AsRef<str>]
|
||||
type MacroId: Annotation + Hash + AsRef<str>;
|
||||
|
||||
/// A (possibly interned) symbol or index
|
||||
type Symbol: Annotation + Copy + Hash;
|
||||
|
||||
/// A (possibly compound) symbol or index
|
||||
type Path: Annotation;
|
||||
|
||||
// /// An Operator in an [Expression](Expr)
|
||||
// type ExprOp: Annotation + Copy;
|
||||
|
||||
// /// An Operator within a [Pattern](Pat)
|
||||
// type PatOp: Annotation + Copy;
|
||||
}
|
||||
|
||||
/// A value with an annotation.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Anno<T: Annotation, A: Annotation = Span>(pub T, pub A);
|
||||
pub struct Anno<T: Annotation, A: AstTypes = DefaultTypes>(pub T, pub A::Annotation);
|
||||
|
||||
impl<T: Annotation, A: AstTypes> std::fmt::Debug for Anno<T, A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
<A::Annotation as std::fmt::Debug>::fmt(&self.1, f)?;
|
||||
f.write_str(": ")?;
|
||||
<T as std::fmt::Debug>::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Expressions: The beating heart of Dough.
|
||||
///
|
||||
@@ -31,17 +64,17 @@ pub struct Anno<T: Annotation, A: Annotation = Span>(pub T, pub A);
|
||||
/// performing import resolution, as imports typically depend on the order
|
||||
/// in which names are bound.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Expr<A: Annotation = Span> {
|
||||
pub enum Expr<A: AstTypes = DefaultTypes> {
|
||||
/// Omitted by semicolon insertion-elision rules
|
||||
Omitted,
|
||||
/// An identifier
|
||||
Id(Path),
|
||||
Id(A::Path),
|
||||
/// An escaped token for macro binding
|
||||
MetId(String),
|
||||
MetId(A::MacroId),
|
||||
/// A literal bool, string, char, or int
|
||||
Lit(Literal),
|
||||
Lit(A::Literal),
|
||||
/// use Use
|
||||
Use(Use),
|
||||
Use(Use<A>),
|
||||
/// `let Pat::NoTopAlt (= expr (else expr)?)?` |
|
||||
/// `(fn | mod | impl) Pat::Fn Expr`
|
||||
Bind(Box<Bind<A>>),
|
||||
@@ -198,42 +231,48 @@ pub enum Op {
|
||||
OrSet,
|
||||
}
|
||||
|
||||
/// A qualified identifier
|
||||
///
|
||||
/// TODO: qualify identifier
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Path {
|
||||
// TODO: Identifier interning
|
||||
pub parts: Vec<String>,
|
||||
// TODO: generic parameters
|
||||
}
|
||||
impl<A: AstTypes> Expr<A> {
|
||||
pub const fn anno(self, annotation: A::Annotation) -> Anno<Expr<A>, A> {
|
||||
Anno(self, annotation)
|
||||
}
|
||||
|
||||
/// A literal value (boolean, character, integer, string)
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
/// A boolean literal: true | false
|
||||
Bool(bool),
|
||||
/// A character literal: 'a', '\u{1f988}'
|
||||
Char(char),
|
||||
/// An integer literal: 0, 123, 0x10
|
||||
Int(u128, u32),
|
||||
/// A string literal:
|
||||
Str(String),
|
||||
}
|
||||
pub fn and_do(self, annotation: A::Annotation, other: Anno<Expr<A>, A>) -> Self {
|
||||
let Self::Op(Op::Do, mut exprs) = self else {
|
||||
return Self::Op(Op::Do, vec![self.anno(annotation), other]);
|
||||
};
|
||||
let Anno(Self::Op(Op::Do, mut other), _) = other else {
|
||||
exprs.push(other);
|
||||
return Self::Op(Op::Do, exprs);
|
||||
};
|
||||
exprs.append(&mut other);
|
||||
Self::Op(Op::Do, exprs)
|
||||
}
|
||||
|
||||
/// A compound import declaration
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Use {
|
||||
/// "*"
|
||||
Glob,
|
||||
/// Identifier
|
||||
Name(String),
|
||||
/// Identifier as Identifier
|
||||
Alias(String, String),
|
||||
/// Identifier :: Use
|
||||
Path(String, Box<Use>),
|
||||
/// { Use, * }
|
||||
Tree(Vec<Use>),
|
||||
pub fn to_tuple(self, annotation: A::Annotation) -> Self {
|
||||
match self {
|
||||
Self::Op(Op::Tuple, _) => self,
|
||||
_ => Self::Op(Op::Tuple, vec![self.anno(annotation)]),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_place(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Id(_) | Self::Op(Op::Index | Op::Dot | Op::Deref, _)
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn is_value(&self) -> bool {
|
||||
!self.is_place()
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub const fn as_slice(&self) -> Option<(Op, &[Anno<Expr<A>, A>])> {
|
||||
match self {
|
||||
Expr::Op(op, args) => Some((*op, args.as_slice())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A pattern binding
|
||||
@@ -249,9 +288,9 @@ pub enum Use {
|
||||
/// Pat => Expr // in match
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Bind<A: Annotation = Span>(
|
||||
pub struct Bind<A: AstTypes = DefaultTypes>(
|
||||
pub BindOp,
|
||||
pub Vec<Path>,
|
||||
pub Vec<A::Path>,
|
||||
pub Pat<A>,
|
||||
pub Vec<Anno<Expr<A>, A>>,
|
||||
);
|
||||
@@ -277,34 +316,19 @@ pub enum BindOp {
|
||||
/// A `Pat => Expr` binding
|
||||
Match,
|
||||
}
|
||||
|
||||
/// A make (constructor) expression
|
||||
/// ```ignore
|
||||
/// Expr { (Ident (: Expr)?),* }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Make<A: Annotation = Span>(pub Anno<Expr<A>, A>, pub Vec<MakeArm<A>>);
|
||||
|
||||
/// A single "arm" of a make expression
|
||||
/// ```text
|
||||
/// Identifier (':' Expr)?
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MakeArm<A: Annotation = Span>(pub String, pub Option<Anno<Expr<A>, A>>);
|
||||
|
||||
/// Binding patterns for each kind of matchable value.
|
||||
///
|
||||
/// This covers both patterns in Match expressions, and type annotations.
|
||||
/// This covers both bindings and type annotations in [Bind] expressions.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Pat<A: Annotation = Span> {
|
||||
pub enum Pat<A: AstTypes = DefaultTypes> {
|
||||
/// Matches anything without binding
|
||||
Ignore,
|
||||
/// Matches nothing, ever
|
||||
Never,
|
||||
/// Matches nothing; used for macro substitution
|
||||
MetId(String),
|
||||
MetId(A::MacroId),
|
||||
/// Matches anything, and binds it to a name
|
||||
Name(String),
|
||||
Name(A::Symbol),
|
||||
/// Matches a value by equality comparison
|
||||
Value(Box<Anno<Expr<A>, A>>),
|
||||
/// Matches a compound pattern
|
||||
@@ -348,64 +372,7 @@ pub enum PatOp {
|
||||
Alt,
|
||||
}
|
||||
|
||||
impl<T: Annotation, A: Annotation> std::fmt::Debug for Anno<T, A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
<A as std::fmt::Debug>::fmt(&self.1, f)?;
|
||||
f.write_str(": ")?;
|
||||
<T as std::fmt::Debug>::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
impl<A: Annotation> Default for Expr<A> {
|
||||
fn default() -> Self {
|
||||
Self::Op(Op::Tuple, vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Expr<A> {
|
||||
pub const fn anno(self, annotation: A) -> Anno<Expr<A>, A> {
|
||||
Anno(self, annotation)
|
||||
}
|
||||
|
||||
pub fn and_do(self, annotation: A, other: Anno<Expr<A>, A>) -> Self {
|
||||
let Self::Op(Op::Do, mut exprs) = self else {
|
||||
return Self::Op(Op::Do, vec![self.anno(annotation), other]);
|
||||
};
|
||||
let Anno(Self::Op(Op::Do, mut other), _) = other else {
|
||||
exprs.push(other);
|
||||
return Self::Op(Op::Do, exprs);
|
||||
};
|
||||
exprs.append(&mut other);
|
||||
Self::Op(Op::Do, exprs)
|
||||
}
|
||||
|
||||
pub fn to_tuple(self, annotation: A) -> Self {
|
||||
match self {
|
||||
Self::Op(Op::Tuple, _) => self,
|
||||
_ => Self::Op(Op::Tuple, vec![self.anno(annotation)]),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_place(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Id(_) | Self::Op(Op::Index | Op::Dot | Op::Deref, _)
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn is_value(&self) -> bool {
|
||||
!self.is_place()
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub const fn as_slice(&self) -> Option<(Op, &[Anno<Expr<A>, A>])> {
|
||||
match self {
|
||||
Expr::Op(op, args) => Some((*op, args.as_slice())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Pat<A> {
|
||||
impl<A: AstTypes> Pat<A> {
|
||||
pub fn to_tuple(self) -> Self {
|
||||
match self {
|
||||
Self::Op(PatOp::Tuple, _) => self,
|
||||
@@ -414,8 +381,31 @@ impl<A: Annotation> Pat<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Path {
|
||||
fn from(value: &str) -> Self {
|
||||
Self { parts: vec![value.to_owned()] }
|
||||
}
|
||||
/// A compound import declaration
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Use<A: AstTypes = DefaultTypes> {
|
||||
/// "*"
|
||||
Glob,
|
||||
/// Identifier
|
||||
Name(A::Symbol),
|
||||
/// Identifier as Identifier
|
||||
Alias(A::Symbol, A::Symbol),
|
||||
/// Identifier :: Use
|
||||
Path(A::Symbol, Box<Use<A>>),
|
||||
/// { Use, * }
|
||||
Tree(Vec<Use<A>>),
|
||||
}
|
||||
|
||||
/// A make (constructor) expression
|
||||
/// ```ignore
|
||||
/// Expr { (Ident (: Expr)?),* }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Make<A: AstTypes = DefaultTypes>(pub Anno<Expr<A>, A>, pub Vec<MakeArm<A>>);
|
||||
|
||||
/// A single "arm" of a make expression
|
||||
/// ```text
|
||||
/// Identifier (':' Expr)?
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MakeArm<A: AstTypes = DefaultTypes>(pub A::Symbol, pub Option<Anno<Expr<A>, A>>);
|
||||
|
||||
@@ -2,13 +2,13 @@ use super::*;
|
||||
use crate::fmt::FmtAdapter;
|
||||
use std::{fmt::Display, format_args as fmt};
|
||||
|
||||
impl<T: Display + Annotation, A: Annotation> Display for Anno<T, A> {
|
||||
impl<T: Display + Annotation, A: AstTypes> Display for Anno<T, A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Display for Expr<A> {
|
||||
impl<A: AstTypes> Display for Expr<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Omitted => "/* omitted */".fmt(f),
|
||||
@@ -135,28 +135,7 @@ impl Display for Op {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Path {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { parts } = self;
|
||||
f.list(parts, "::")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Literal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Bool(v) => v.fmt(f),
|
||||
Self::Char(c) => write!(f, "'{}'", c.escape_debug()),
|
||||
Self::Int(i, 2) => write!(f, "0b{i:b}"),
|
||||
Self::Int(i, 8) => write!(f, "0o{i:o}"),
|
||||
Self::Int(i, 16) => write!(f, "0x{i:x}"),
|
||||
Self::Int(i, _) => i.fmt(f),
|
||||
Self::Str(s) => write!(f, "\"{}\"", s.escape_debug()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Use {
|
||||
impl<A: AstTypes> Display for Use<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Glob => "*".fmt(f),
|
||||
@@ -174,7 +153,7 @@ impl Display for Use {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Display for Bind<A> {
|
||||
impl<A: AstTypes> Display for Bind<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self(op, gens, pat, exprs) = self;
|
||||
op.fmt(f)?;
|
||||
@@ -238,14 +217,14 @@ impl Display for BindOp {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Display for Make<A> {
|
||||
impl<A: AstTypes> Display for Make<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self(expr, make_arms) = self;
|
||||
f.delimit(fmt!("({expr} {{"), "})").list(make_arms, ", ")
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Display for MakeArm<A> {
|
||||
impl<A: AstTypes> Display for MakeArm<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self(name, Some(body)) => write!(f, "{name}: {body}"),
|
||||
@@ -254,7 +233,7 @@ impl<A: Annotation> Display for MakeArm<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Display for Pat<A> {
|
||||
impl<A: AstTypes> Display for Pat<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Ignore => "_".fmt(f),
|
||||
|
||||
269
src/ast/fold.rs
269
src/ast/fold.rs
@@ -2,207 +2,201 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(clippy::wildcard_imports, clippy::missing_errors_doc)]
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Deconstructs an entire AST, and reconstructs it from parts.
|
||||
///
|
||||
/// Each function acts as a customization point.
|
||||
///
|
||||
/// Aside from [Annotation]s, each node in the AST implements the [`Foldable`] trait,
|
||||
/// Aside from [`AstTypes`], each node in the AST implements the [`Foldable`] trait,
|
||||
/// which provides double dispatch.
|
||||
pub trait Fold<A: Annotation> {
|
||||
pub trait Fold<From: AstTypes, To: AstTypes = From> {
|
||||
type Error;
|
||||
|
||||
fn fold_annotation(&mut self, anno: A) -> Result<A, Self::Error> {
|
||||
Ok(anno)
|
||||
}
|
||||
/// Consumes an Annotation in A, possibly transforms it, and produces a replacement Annotation
|
||||
/// in B
|
||||
fn fold_annotation(&mut self, anno: From::Annotation) -> Result<To::Annotation, Self::Error>;
|
||||
|
||||
/// Consumes an [`Expr`], possibly transforms it, and produces a replacement [`Expr`]
|
||||
fn fold_expr(&mut self, expr: Expr<A>) -> Result<Expr<A>, Self::Error> {
|
||||
/// Consumes a `MacroId` in A, possibly transforms it, and produces a replacement `MacroId` in B
|
||||
fn fold_macro_id(&mut self, name: From::MacroId) -> Result<To::MacroId, Self::Error>;
|
||||
|
||||
/// Consumes a `Symbol` in A, possibly transforms it, and produces a replacement `Symbol` in B
|
||||
fn fold_symbol(&mut self, name: From::Symbol) -> Result<To::Symbol, Self::Error>;
|
||||
|
||||
/// Consumes a `Path` in A, possibly transforms it, and produces a replacement `Path` in B
|
||||
fn fold_path(&mut self, path: From::Path) -> Result<To::Path, Self::Error>;
|
||||
|
||||
/// Consumes a `Literal` in A, possibly transforms it, and produces a replacement `Literal` in B
|
||||
fn fold_literal(&mut self, lit: From::Literal) -> Result<To::Literal, Self::Error>;
|
||||
|
||||
/// Folds an annotated expression, so the expression and annotation can be seen at once.
|
||||
fn fold_anno_expr(
|
||||
&mut self,
|
||||
expr: Anno<Expr<From>, From>,
|
||||
) -> Result<Anno<Expr<To>, To>, Self::Error> {
|
||||
expr.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a Symbol, possibly transforms it, and produces a replacement Symbol
|
||||
fn fold_ident(&mut self, name: String) -> Result<String, Self::Error> {
|
||||
name.children(self) // TODO: ^ this should be a symbol
|
||||
}
|
||||
|
||||
/// Consumes a [`Path`], possibly transforms it, and produces a replacement [`Path`]
|
||||
fn fold_path(&mut self, path: Path) -> Result<Path, Self::Error> {
|
||||
path.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a [`Literal`], possibly transforms it, and produces a replacement [`Literal`]
|
||||
fn fold_literal(&mut self, lit: Literal) -> Result<Literal, Self::Error> {
|
||||
lit.children(self)
|
||||
/// Consumes an [`Expr`], possibly transforms it, and produces a replacement [`Expr`]
|
||||
fn fold_expr(&mut self, expr: Expr<From>) -> Result<Expr<To>, Self::Error> {
|
||||
expr.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a [`Use`], possibly transforms it, and produces a replacement [`Use`]
|
||||
fn fold_use(&mut self, item: Use) -> Result<Use, Self::Error> {
|
||||
fn fold_use(&mut self, item: Use<From>) -> Result<Use<To>, Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a [`Pat`], possibly transforms it, and produces a replacement [`Pat`]
|
||||
fn fold_pat(&mut self, pat: Pat<A>) -> Result<Pat<A>, Self::Error> {
|
||||
fn fold_pat(&mut self, pat: Pat<From>) -> Result<Pat<To>, Self::Error> {
|
||||
pat.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a [`Bind`], possibly transforms it, and produces a replacement [`Bind`]
|
||||
fn fold_bind(&mut self, bind: Bind<A>) -> Result<Bind<A>, Self::Error> {
|
||||
fn fold_bind(&mut self, bind: Bind<From>) -> Result<Bind<To>, Self::Error> {
|
||||
bind.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a [`Make`], possibly transforms it, and produces a replacement [`Make`]
|
||||
fn fold_make(&mut self, make: Make<A>) -> Result<Make<A>, Self::Error> {
|
||||
fn fold_make(&mut self, make: Make<From>) -> Result<Make<To>, Self::Error> {
|
||||
make.children(self)
|
||||
}
|
||||
|
||||
/// Consumes a [`MakeArm`], possibly transforms it, and produces a replacement [`MakeArm`]
|
||||
fn fold_makearm(&mut self, arm: MakeArm<A>) -> Result<MakeArm<A>, Self::Error> {
|
||||
fn fold_makearm(&mut self, arm: MakeArm<From>) -> Result<MakeArm<To>, Self::Error> {
|
||||
arm.children(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Foldable<A: Annotation>: Sized {
|
||||
pub trait Foldable<A: AstTypes, B: AstTypes>: Sized {
|
||||
/// The return type of the associated [Fold] function
|
||||
type Out;
|
||||
|
||||
/// Calls `Self`'s appropriate [Folder] function(s)
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error>;
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error>;
|
||||
|
||||
/// Destructures `self`, calling [`Foldable::fold_in`] on all foldable members,
|
||||
/// and rebuilds a `Self` out of the results.
|
||||
#[allow(unused_variables)]
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error>;
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Expr<A> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Expr<A> {
|
||||
type Out = Expr<B>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_expr(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
Ok(match self {
|
||||
Self::Omitted => Self::Omitted,
|
||||
Self::Id(path) => Self::Id(path.fold_in(folder)?),
|
||||
Self::MetId(id) => Self::MetId(id.fold_in(folder)?),
|
||||
Self::Lit(literal) => Self::Lit(literal.fold_in(folder)?),
|
||||
Self::Use(item) => Self::Use(item.fold_in(folder)?),
|
||||
Self::Bind(bind) => Self::Bind(bind.fold_in(folder)?),
|
||||
Self::Make(make) => Self::Make(make.fold_in(folder)?),
|
||||
Self::Op(op, annos) => Self::Op(op, annos.fold_in(folder)?),
|
||||
Self::Omitted => Expr::Omitted,
|
||||
Self::Id(path) => Expr::Id(folder.fold_path(path)?),
|
||||
Self::MetId(id) => Expr::MetId(folder.fold_macro_id(id)?),
|
||||
Self::Lit(lit) => Expr::Lit(folder.fold_literal(lit)?),
|
||||
Self::Use(item) => Expr::Use(item.fold_in(folder)?),
|
||||
Self::Bind(bind) => Expr::Bind(bind.fold_in(folder)?),
|
||||
Self::Make(make) => Expr::Make(make.fold_in(folder)?),
|
||||
Self::Op(op, annos) => Expr::Op(op, annos.fold_in(folder)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for String {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
folder.fold_ident(self)
|
||||
}
|
||||
}
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Use<A> {
|
||||
type Out = Use<B>;
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Path {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
folder.fold_path(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, _folder: &mut F) -> Result<Self, F::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Literal {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
folder.fold_literal(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, _folder: &mut F) -> Result<Self, F::Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Use {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_use(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
Ok(match self {
|
||||
Self::Glob => Self::Glob,
|
||||
Self::Name(name) => Self::Name(name),
|
||||
Self::Alias(name, alias) => Self::Alias(name, alias),
|
||||
Self::Path(name, rest) => Self::Path(name, rest.fold_in(folder)?),
|
||||
Self::Tree(items) => Self::Tree(items.fold_in(folder)?),
|
||||
Self::Glob => Use::Glob,
|
||||
Self::Name(name) => Use::Name(folder.fold_symbol(name)?),
|
||||
Self::Alias(name, alias) => {
|
||||
Use::Alias(folder.fold_symbol(name)?, folder.fold_symbol(alias)?)
|
||||
}
|
||||
Self::Path(name, rest) => Use::Path(folder.fold_symbol(name)?, rest.fold_in(folder)?),
|
||||
Self::Tree(items) => Use::Tree(items.fold_in(folder)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Pat<A> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Pat<A> {
|
||||
type Out = Pat<B>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_pat(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
Ok(match self {
|
||||
Self::Ignore => Self::Ignore,
|
||||
Self::Never => Self::Never,
|
||||
Self::MetId(name) => Self::MetId(name.fold_in(folder)?),
|
||||
Self::Name(name) => Self::Name(name.fold_in(folder)?),
|
||||
Self::Value(expr) => Self::Value(expr.fold_in(folder)?),
|
||||
Self::Op(op, pats) => Self::Op(op, pats.fold_in(folder)?),
|
||||
Self::Ignore => Pat::Ignore,
|
||||
Self::Never => Pat::Never,
|
||||
Self::MetId(name) => Pat::MetId(folder.fold_macro_id(name)?),
|
||||
Self::Name(name) => Pat::Name(folder.fold_symbol(name)?),
|
||||
Self::Value(expr) => Pat::Value(expr.fold_in(folder)?),
|
||||
Self::Op(op, pats) => Pat::Op(op, pats.fold_in(folder)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Bind<A> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Bind<A> {
|
||||
type Out = Bind<B>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_bind(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
let Self(op, gens, pat, exprs) = self;
|
||||
Ok(Self(
|
||||
Ok(Bind(
|
||||
op,
|
||||
gens.fold_in(folder)?,
|
||||
gens.into_iter()
|
||||
.map(|g| folder.fold_path(g))
|
||||
.collect::<Result<_, _>>()?,
|
||||
pat.fold_in(folder)?,
|
||||
exprs.fold_in(folder)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for Make<A> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Make<A> {
|
||||
type Out = Make<B>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_make(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
let Self(expr, arms) = self;
|
||||
Ok(Self(expr.fold_in(folder)?, arms.fold_in(folder)?))
|
||||
Ok(Make(expr.fold_in(folder)?, arms.fold_in(folder)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Foldable<A> for MakeArm<A> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for MakeArm<A> {
|
||||
type Out = MakeArm<B>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_makearm(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
let Self(name, expr) = self;
|
||||
Ok(Self(name, expr.fold_in(folder)?))
|
||||
Ok(MakeArm(folder.fold_symbol(name)?, expr.fold_in(folder)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Annotation + Foldable<A>, A: Annotation> Foldable<A> for Anno<T, A> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
let Self(expr, anno) = self;
|
||||
let anno = folder.fold_annotation(anno)?;
|
||||
Ok(Self(expr.fold_in(folder)?, anno))
|
||||
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Anno<Expr<A>, A> {
|
||||
type Out = Anno<Expr<B>, B>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
folder.fold_anno_expr(self)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
let Self(expr, anno) = self;
|
||||
Ok(Self(expr.children(folder)?, anno))
|
||||
Ok(Anno(expr.children(folder)?, folder.fold_annotation(anno)?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,53 +204,66 @@ impl<T: Annotation + Foldable<A>, A: Annotation> Foldable<A> for Anno<T, A> {
|
||||
// GENERIC IMPLEMENTATIONS ON COLLECTIONS //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/// Maps the value in the box across `f()` without deallocating
|
||||
fn box_try_map<T, E>(boxed: Box<T>, f: impl FnOnce(T) -> Result<T, E>) -> Result<Box<T>, E> {
|
||||
// TODO: replace with Box::take when it stabilizes.
|
||||
let rawbox = Box::into_raw(boxed);
|
||||
// Maps the value in the box across `f()` without deallocating
|
||||
// fn box_try_map<T, E>(boxed: Box<T>, f: impl FnOnce(T) -> Result<T, E>) -> Result<Box<T>, E> {
|
||||
// // TODO: replace with Box::take when it stabilizes.
|
||||
// let rawbox = Box::into_raw(boxed);
|
||||
|
||||
// Safety: `rawbox` came from a Box, so it is aligned and initialized.
|
||||
// To prevent further reuse and deallocate on failure, rawbox is
|
||||
// shadowed by a Box<MaybeUninit<T>>.
|
||||
// Safety: MaybeUninit<T> has the same size and alignment as T.
|
||||
let (value, rawbox) = (unsafe { rawbox.read() }, unsafe {
|
||||
Box::from_raw(rawbox.cast::<MaybeUninit<T>>())
|
||||
});
|
||||
// // Safety: `rawbox` came from a Box, so it is aligned and initialized.
|
||||
// // To prevent further reuse and deallocate on failure, rawbox is
|
||||
// // shadowed by a Box<MaybeUninit<T>>.
|
||||
// // Safety: MaybeUninit<T> has the same size and alignment as T.
|
||||
// let (value, rawbox) = (unsafe { rawbox.read() }, unsafe {
|
||||
// Box::from_raw(rawbox.cast::<MaybeUninit<T>>())
|
||||
// });
|
||||
|
||||
// rawbox is reinitialized with f(value)
|
||||
Ok(Box::write(rawbox, f(value)?))
|
||||
}
|
||||
// // rawbox is reinitialized with f(value)
|
||||
// Ok(Box::write(rawbox, f(value)?))
|
||||
// }
|
||||
|
||||
impl<T: Foldable<A>, A: Annotation> Foldable<A> for Box<T> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
box_try_map(self, |t| t.fold_in(folder))
|
||||
impl<T, A, B> Foldable<A, B> for Box<T>
|
||||
where
|
||||
T: Foldable<A, B>,
|
||||
A: AstTypes,
|
||||
B: AstTypes,
|
||||
{
|
||||
type Out = Box<T::Out>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
let value = *self;
|
||||
Ok(Box::new(value.fold_in(folder)?))
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
box_try_map(self, |t| t.children(folder))
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
let value = *self;
|
||||
Ok(Box::new(value.children(folder)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Foldable<A>, A: Annotation> Foldable<A> for Vec<T> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
self.into_iter().map(|e| e.fold_in(folder)).collect()
|
||||
impl<T: Foldable<A, B>, A: AstTypes, B: AstTypes> Foldable<A, B> for Vec<T> {
|
||||
type Out = Vec<T::Out>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
self.children(folder)
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
// TODO: is this correct for generic data structures?
|
||||
self.into_iter().map(|e| e.fold_in(folder)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Foldable<A>, A: Annotation> Foldable<A> for Option<T> {
|
||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
impl<T: Foldable<A, B>, A: AstTypes, B: AstTypes> Foldable<A, B> for Option<T> {
|
||||
type Out = Option<T::Out>;
|
||||
|
||||
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
Ok(match self {
|
||||
Self::Some(value) => Some(value.fold_in(folder)?),
|
||||
Self::None => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn children<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||
Ok(match self {
|
||||
Self::Some(value) => Some(value.children(folder)?),
|
||||
Self::None => None,
|
||||
|
||||
@@ -5,18 +5,18 @@ use std::collections::HashMap;
|
||||
|
||||
/// Stores a substitution from meta-identifiers to values
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Subst<A: Annotation> {
|
||||
pub exp: HashMap<String, Expr<A>>,
|
||||
pub pat: HashMap<String, Pat<A>>,
|
||||
pub struct Subst<A: AstTypes> {
|
||||
pub exp: HashMap<A::MacroId, Expr<A>>,
|
||||
pub pat: HashMap<A::MacroId, Pat<A>>,
|
||||
}
|
||||
|
||||
impl<A: Annotation> Default for Subst<A> {
|
||||
impl<A: AstTypes> Default for Subst<A> {
|
||||
fn default() -> Self {
|
||||
Self { exp: Default::default(), pat: Default::default() }
|
||||
}
|
||||
}
|
||||
impl<A: Annotation> Subst<A> {
|
||||
fn add_pat(&mut self, name: String, pat: &Pat<A>) -> bool {
|
||||
impl<A: AstTypes> Subst<A> {
|
||||
fn add_pat(&mut self, name: A::MacroId, pat: &Pat<A>) -> bool {
|
||||
if self.exp.contains_key(&name) {
|
||||
return false;
|
||||
}
|
||||
@@ -25,7 +25,7 @@ impl<A: Annotation> Subst<A> {
|
||||
}
|
||||
self.pat.insert(name, pat.clone()).is_none()
|
||||
}
|
||||
fn add_expr(&mut self, name: String, exp: &Expr<A>) -> bool {
|
||||
fn add_expr(&mut self, name: A::MacroId, exp: &Expr<A>) -> bool {
|
||||
if self.pat.contains_key(&name) {
|
||||
return false;
|
||||
}
|
||||
@@ -36,11 +36,11 @@ impl<A: Annotation> Subst<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Match<A: Annotation> {
|
||||
pub trait Match<A: AstTypes> {
|
||||
/// Applies a substitution rule from `pat` to `template` on `self`
|
||||
fn apply_rule(&mut self, pat: &Self, template: &Self) -> bool
|
||||
where Self: Sized + Clone {
|
||||
let Some(sub) = self.construct(pat) else {
|
||||
let Some(sub) = self.match_with(pat) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -56,19 +56,14 @@ pub trait Match<A: Annotation> {
|
||||
/// Implements recursive Subst-building for Self
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool;
|
||||
|
||||
/// Constructs a Subst
|
||||
fn construct(&self, pat: &Self) -> Option<Subst<A>> {
|
||||
/// Matches self against the provided pattern
|
||||
fn match_with(&self, pat: &Self) -> Option<Subst<A>> {
|
||||
let mut sub = Subst::default();
|
||||
Match::recurse(&mut sub, pat, self).then_some(sub)
|
||||
}
|
||||
|
||||
/// Matches self against the provided pattern
|
||||
fn match_with(&self, pat: &Self, sub: &mut Subst<A>) -> bool {
|
||||
Match::recurse(sub, pat, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Match<A> + Annotation, A: Annotation> Match<A> for Anno<M, A> {
|
||||
impl<M: Match<A> + Annotation, A: AstTypes> Match<A> for Anno<M, A> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
Match::recurse(sub, &pat.0, &expr.0)
|
||||
}
|
||||
@@ -78,7 +73,7 @@ impl<M: Match<A> + Annotation, A: Annotation> Match<A> for Anno<M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Match<A> for Bind<A> {
|
||||
impl<A: AstTypes> Match<A> for Bind<A> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
let (Self(pat_kind, _, pat_pat, pat_expr), Self(expr_kind, _, expr_pat, expr_expr)) =
|
||||
(pat, expr);
|
||||
@@ -94,7 +89,7 @@ impl<A: Annotation> Match<A> for Bind<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Match<A> for crate::ast::Make<A> {
|
||||
impl<A: AstTypes> Match<A> for crate::ast::Make<A> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
let (Make(pat, pat_arms), Make(expr, expr_arms)) = (pat, expr);
|
||||
Match::recurse(sub, pat, expr) && Match::recurse(sub, pat_arms, expr_arms)
|
||||
@@ -107,12 +102,12 @@ impl<A: Annotation> Match<A> for crate::ast::Make<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Match<A> for Expr<A> {
|
||||
impl<A: AstTypes> Match<A> for Expr<A> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
match (pat, expr) {
|
||||
(Expr::Omitted, Expr::Omitted) => true,
|
||||
(Expr::Omitted, _) => false,
|
||||
(Expr::MetId(name), _) if name == "_" => true,
|
||||
(Expr::MetId(name), _) if name.as_ref() == "_" => true,
|
||||
(Expr::MetId(name), _) => sub.add_expr(name.clone(), expr),
|
||||
(Expr::Id(pat), Expr::Id(expr)) => pat == expr,
|
||||
(Expr::Id(_), _) => false,
|
||||
@@ -149,7 +144,7 @@ impl<A: Annotation> Match<A> for Expr<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Match<A> for MakeArm<A> {
|
||||
impl<A: AstTypes> Match<A> for MakeArm<A> {
|
||||
// TODO: order-independent matching for MakeArm specifically.
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
pat.0 == expr.0 && Match::recurse(sub, &pat.1, &expr.1)
|
||||
@@ -161,10 +156,10 @@ impl<A: Annotation> Match<A> for MakeArm<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Match<A> for Pat<A> {
|
||||
impl<A: AstTypes> Match<A> for Pat<A> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
match (pat, expr) {
|
||||
(Pat::MetId(name), _) if name == "_" => true,
|
||||
(Pat::MetId(name), _) if name.as_ref() == "_" => true,
|
||||
(Pat::MetId(name), _) => sub.add_pat(name.clone(), expr),
|
||||
(Pat::Ignore, Pat::Ignore) => true,
|
||||
(Pat::Ignore, _) => false,
|
||||
@@ -193,7 +188,7 @@ impl<A: Annotation> Match<A> for Pat<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation> Match<A> for Op {
|
||||
impl<A: AstTypes> Match<A> for Op {
|
||||
fn recurse(_: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
pat == expr
|
||||
}
|
||||
@@ -201,7 +196,7 @@ impl<A: Annotation> Match<A> for Op {
|
||||
fn apply(&mut self, _sub: &Subst<A>) {}
|
||||
}
|
||||
|
||||
impl<A: Annotation, T: Match<A>> Match<A> for [T] {
|
||||
impl<A: AstTypes, T: Match<A>> Match<A> for [T] {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
if pat.len() != expr.len() {
|
||||
return false;
|
||||
@@ -221,7 +216,7 @@ impl<A: Annotation, T: Match<A>> Match<A> for [T] {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation, T: Match<A>> Match<A> for Box<T> {
|
||||
impl<A: AstTypes, T: Match<A>> Match<A> for Box<T> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
Match::recurse(sub, pat.as_ref(), expr.as_ref())
|
||||
}
|
||||
@@ -231,7 +226,7 @@ impl<A: Annotation, T: Match<A>> Match<A> for Box<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation, T: Match<A>> Match<A> for Vec<T> {
|
||||
impl<A: AstTypes, T: Match<A>> Match<A> for Vec<T> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
Match::recurse(sub, pat.as_slice(), expr.as_slice())
|
||||
}
|
||||
@@ -241,7 +236,7 @@ impl<A: Annotation, T: Match<A>> Match<A> for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Annotation, T: Match<A>> Match<A> for Option<T> {
|
||||
impl<A: AstTypes, T: Match<A>> Match<A> for Option<T> {
|
||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||
match (pat, expr) {
|
||||
(Some(pat), Some(expr)) => Match::recurse(sub, pat, expr),
|
||||
|
||||
80
src/ast/types.rs
Normal file
80
src/ast/types.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{ast::AstTypes, fmt::FmtAdapter, intern::interned::Interned, span::Span};
|
||||
|
||||
/// The types emitted by the parser
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct DefaultTypes;
|
||||
|
||||
impl AstTypes for DefaultTypes {
|
||||
type Annotation = Span;
|
||||
type Literal = Literal;
|
||||
type MacroId = Symbol;
|
||||
type Symbol = Symbol;
|
||||
type Path = Path;
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DefaultTypes {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// An interned symbol (i.e. a name)
|
||||
pub type Symbol = Interned<'static, str>;
|
||||
|
||||
/// A qualified identifier
|
||||
///
|
||||
/// TODO: qualify identifier
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Path {
|
||||
// TODO: Identifier interning
|
||||
pub parts: Vec<Symbol>,
|
||||
// TODO: generic parameters
|
||||
}
|
||||
|
||||
impl From<&str> for Path {
|
||||
fn from(value: &str) -> Self {
|
||||
Self { parts: vec![value.into()] }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Symbol> for Path {
|
||||
fn from(value: Symbol) -> Self {
|
||||
Self { parts: vec![value] }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Path {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { parts } = self;
|
||||
f.list(parts, "::")
|
||||
}
|
||||
}
|
||||
|
||||
/// A literal value (boolean, character, integer, string)
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
/// A boolean literal: true | false
|
||||
Bool(bool),
|
||||
/// A character literal: 'a', '\u{1f988}'
|
||||
Char(char),
|
||||
/// An integer literal: 0, 123, 0x10
|
||||
Int(u128, u32),
|
||||
/// A string literal:
|
||||
Str(String),
|
||||
}
|
||||
|
||||
impl Display for Literal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Bool(v) => v.fmt(f),
|
||||
Self::Char(c) => write!(f, "'{}'", c.escape_debug()),
|
||||
Self::Int(i, 2) => write!(f, "0b{i:b}"),
|
||||
Self::Int(i, 8) => write!(f, "0o{i:o}"),
|
||||
Self::Int(i, 16) => write!(f, "0x{i:x}"),
|
||||
Self::Int(i, _) => i.fmt(f),
|
||||
Self::Str(s) => write!(f, "\"{}\"", s.escape_debug()),
|
||||
}
|
||||
}
|
||||
}
|
||||
177
src/ast/visit.rs
177
src/ast/visit.rs
@@ -3,56 +3,63 @@
|
||||
#![allow(clippy::wildcard_imports, clippy::missing_errors_doc)]
|
||||
use super::*;
|
||||
|
||||
pub trait Visit<'a> {
|
||||
pub trait Visit<'a, A: AstTypes> {
|
||||
type Error;
|
||||
|
||||
fn visit(&mut self, walk: &'a impl Walk<'a>) -> Result<(), Self::Error> {
|
||||
fn visit<W: Walk<'a, A> + ?Sized>(&mut self, walk: &'a W) -> Result<(), Self::Error> {
|
||||
walk.visit_in(self)
|
||||
}
|
||||
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
expr.children(self)
|
||||
}
|
||||
fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> {
|
||||
name.children(self)
|
||||
}
|
||||
fn visit_path(&mut self, path: &'a Path) -> Result<(), Self::Error> {
|
||||
path.children(self)
|
||||
}
|
||||
fn visit_literal(&mut self, lit: &'a Literal) -> Result<(), Self::Error> {
|
||||
lit.children(self)
|
||||
}
|
||||
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Walk<'a> {
|
||||
#[inline]
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, _v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_literal(&mut self, lit: &'a A::Literal) -> Result<(), Self::Error> {
|
||||
let _ = lit;
|
||||
Ok(())
|
||||
}
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error>;
|
||||
fn visit_macro_id(&mut self, name: &'a A::MacroId) -> Result<(), Self::Error> {
|
||||
let _ = name;
|
||||
Ok(())
|
||||
}
|
||||
fn visit_symbol(&mut self, name: &'a A::Symbol) -> Result<(), Self::Error> {
|
||||
let _ = name;
|
||||
Ok(())
|
||||
}
|
||||
fn visit_path(&mut self, path: &'a A::Path) -> Result<(), Self::Error> {
|
||||
let _ = path;
|
||||
Ok(())
|
||||
}
|
||||
fn visit_expr(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||
expr.children(self)
|
||||
}
|
||||
fn visit_use(&mut self, item: &'a Use<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_pat(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_bind(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_make(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
fn visit_makearm(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||
item.children(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: Annotation> Walk<'a> for Expr<A> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
pub trait Walk<'a, A: AstTypes> {
|
||||
#[inline]
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, _v: &mut V) -> Result<(), V::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error>;
|
||||
}
|
||||
|
||||
impl<'a, A: AstTypes> Walk<'a, A> for Expr<A> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
match self {
|
||||
Self::Omitted => Ok(()),
|
||||
Self::Id(path) => path.visit_in(v),
|
||||
Self::MetId(id) => id.visit_in(v),
|
||||
Self::Lit(literal) => literal.visit_in(v),
|
||||
Self::Id(path) => v.visit_path(path),
|
||||
Self::MetId(id) => v.visit_macro_id(id),
|
||||
Self::Lit(lit) => v.visit_literal(lit),
|
||||
Self::Use(u) => u.visit_in(v),
|
||||
Self::Bind(bind) => bind.visit_in(v),
|
||||
Self::Make(make) => make.visit_in(v),
|
||||
@@ -61,40 +68,22 @@ impl<'a, A: Annotation> Walk<'a> for Expr<A> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Walk<'a> for str {
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_ident(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Walk<'a> for Path {
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_path(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Walk<'a> for Literal {
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_literal(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Walk<'a> for Use {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, A: AstTypes> Walk<'a, A> for Use<A> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
match self {
|
||||
Self::Glob => Ok(()),
|
||||
Self::Name(name) => name.visit_in(v),
|
||||
Self::Name(name) => v.visit_symbol(name),
|
||||
Self::Alias(name, alias) => {
|
||||
name.visit_in(v)?;
|
||||
alias.visit_in(v)
|
||||
v.visit_symbol(name)?;
|
||||
v.visit_symbol(alias)
|
||||
}
|
||||
Self::Path(name, rest) => {
|
||||
name.visit_in(v)?;
|
||||
v.visit_symbol(name)?;
|
||||
rest.visit_in(v)
|
||||
}
|
||||
Self::Tree(items) => items.visit_in(v),
|
||||
@@ -102,76 +91,76 @@ impl<'a> Walk<'a> for Use {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_use(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: Annotation> Walk<'a> for Pat<A> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, A: AstTypes> Walk<'a, A> for Pat<A> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
match self {
|
||||
Self::Ignore | Self::Never => Ok(()),
|
||||
Self::MetId(id) => id.visit_in(v),
|
||||
Self::Name(name) => name.visit_in(v),
|
||||
Self::MetId(id) => v.visit_macro_id(id),
|
||||
Self::Name(name) => v.visit_symbol(name),
|
||||
Self::Value(literal) => literal.visit_in(v),
|
||||
Self::Op(_, pats) => pats.visit_in(v),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_pat(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: Annotation> Walk<'a> for Bind<A> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, A: AstTypes> Walk<'a, A> for Bind<A> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
let Self(_kind, gens, pat, exprs) = self;
|
||||
gens.visit_in(v)?;
|
||||
gens.iter().try_for_each(|g| v.visit_path(g))?;
|
||||
pat.visit_in(v)?;
|
||||
exprs.visit_in(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_bind(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: Annotation> Walk<'a> for Make<A> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, A: AstTypes> Walk<'a, A> for Make<A> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
let Self(expr, arms) = self;
|
||||
expr.visit_in(v)?;
|
||||
arms.visit_in(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_make(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: Annotation> Walk<'a> for MakeArm<A> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, A: AstTypes> Walk<'a, A> for MakeArm<A> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
let Self(name, expr) = self;
|
||||
name.visit_in(v)?;
|
||||
v.visit_symbol(name)?;
|
||||
expr.visit_in(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
v.visit_makearm(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Annotation + Walk<'a>, A: Annotation> Walk<'a> for Anno<T, A> {
|
||||
impl<'a, T: Annotation + Walk<'a, A>, A: AstTypes> Walk<'a, A> for Anno<T, A> {
|
||||
#[inline]
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
self.0.children(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
self.0.visit_in(v)
|
||||
}
|
||||
}
|
||||
@@ -180,15 +169,15 @@ impl<'a, T: Annotation + Walk<'a>, A: Annotation> Walk<'a> for Anno<T, A> {
|
||||
// GENERIC IMPLEMENTATIONS ON COLLECTIONS //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
impl<'a, T: Walk<'a>> Walk<'a> for [T] {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, T: Walk<'a, A>, A: AstTypes> Walk<'a, A> for [T] {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
for item in self {
|
||||
item.visit_in(v)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
for item in self {
|
||||
item.visit_in(v)?;
|
||||
}
|
||||
@@ -196,24 +185,24 @@ impl<'a, T: Walk<'a>> Walk<'a> for [T] {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Walk<'a>> Walk<'a> for Vec<T> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, T: Walk<'a, A>, A: AstTypes> Walk<'a, A> for Vec<T> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
self.as_slice().children(v)
|
||||
}
|
||||
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
self.as_slice().visit_in(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Walk<'a>> Walk<'a> for Option<T> {
|
||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
impl<'a, T: Walk<'a, A>, A: AstTypes> Walk<'a, A> for Option<T> {
|
||||
fn children<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
match self {
|
||||
Some(t) => t.children(v),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
fn visit_in<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
fn visit_in<V: Visit<'a, A> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||
match self {
|
||||
Some(t) => t.visit_in(v),
|
||||
_ => Ok(()),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Tests the lexer\
|
||||
use doughlang::{
|
||||
ast::{
|
||||
Anno, Annotation, Bind, Expr, Literal, Pat, Path, Use,
|
||||
Anno, Annotation, Bind, Expr, Pat, Use,
|
||||
macro_matcher::{Match, Subst},
|
||||
},
|
||||
lexer::{EOF, LexError, Lexer},
|
||||
@@ -44,8 +44,6 @@ enum ParseMode {
|
||||
Expr,
|
||||
Pat,
|
||||
Bind,
|
||||
Path,
|
||||
Literal,
|
||||
Use,
|
||||
Tokens,
|
||||
}
|
||||
@@ -55,8 +53,6 @@ impl From<&str> for ParseMode {
|
||||
"expr" => Self::Expr,
|
||||
"pat" => Self::Pat,
|
||||
"bind" => Self::Bind,
|
||||
"path" => Self::Path,
|
||||
"literal" => Self::Literal,
|
||||
"use" => Self::Use,
|
||||
"tokens" => Self::Tokens,
|
||||
_ => Default::default(),
|
||||
@@ -69,8 +65,6 @@ impl ParseMode {
|
||||
Self::Expr => parse::<'a, Expr>,
|
||||
Self::Pat => parse::<'a, Pat>,
|
||||
Self::Bind => parse::<'a, Bind>,
|
||||
Self::Path => parse::<'a, Path>,
|
||||
Self::Literal => parse::<'a, Literal>,
|
||||
Self::Use => parse::<'a, Use>,
|
||||
Self::Tokens => tokens::<'a, dyn Parse<'a, Prec = ()>>,
|
||||
}
|
||||
@@ -86,7 +80,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
"" => Ok(Response::Continue),
|
||||
"exit" => Ok(Response::Break),
|
||||
"help" => {
|
||||
println!("Parsing: {parsing:?} (expr, pat, bind, path, literal, use, tokens)");
|
||||
println!("Parsing: {parsing:?} (expr, pat, bind, use, tokens)");
|
||||
println!("Verbose: {verbose:?} (pretty, debug, quiet)");
|
||||
Ok(Response::Deny)
|
||||
}
|
||||
@@ -100,7 +94,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
Ok(Response::Accept)
|
||||
}
|
||||
line @ ("tokens" | "expr" | "pat" | "bind" | "path" | "literal" | "use") => {
|
||||
line @ ("tokens" | "expr" | "pat" | "bind" | "use") => {
|
||||
parsing = ParseMode::from(line);
|
||||
println!("Parse mode set to '{parsing:?}'");
|
||||
Ok(Response::Accept)
|
||||
@@ -144,7 +138,7 @@ fn subst() -> Result<(), Box<dyn Error>> {
|
||||
};
|
||||
|
||||
if p.next_if(TKind::Arrow).is_err() {
|
||||
let Some(Subst { exp, pat }) = exp.construct(&pat) else {
|
||||
let Some(Subst { exp, pat }) = exp.match_with(&pat) else {
|
||||
println!("Match failed: {exp} <- {pat}");
|
||||
continue;
|
||||
};
|
||||
@@ -203,7 +197,7 @@ fn tokens<'t, T: Parse<'t> + ?Sized>(document: &'t str, verbose: Verbosity) {
|
||||
fn parse<'t, T: Parse<'t> + Annotation>(document: &'t str, verbose: Verbosity) {
|
||||
let mut parser = Parser::new(Lexer::new(document));
|
||||
for idx in 0.. {
|
||||
match (parser.parse::<Anno<T, Span>>(T::Prec::default()), verbose) {
|
||||
match (parser.parse::<Anno<T, _>>(T::Prec::default()), verbose) {
|
||||
(Err(e @ ParseError::EOF(s)), _) if s.tail == document.len() as _ => {
|
||||
println!(
|
||||
"\x1b[92m{e} (total {} byte{}, {idx} expression{})\x1b[0m",
|
||||
354
src/intern.rs
Normal file
354
src/intern.rs
Normal file
@@ -0,0 +1,354 @@
|
||||
//! Interners for [strings](string_interner) and non-[Drop] [types](dropless_interner).
|
||||
//!
|
||||
//! An object is [Interned][1] if it is allocated within one of the interners
|
||||
//! in this module. [Interned][1] values have referential equality semantics, and
|
||||
//! [Deref](std::ops::Deref) to the value within their respective intern pool.
|
||||
//!
|
||||
//! This means, of course, that the same value interned in two different pools will be
|
||||
//! considered *not equal* by [Eq] and [Hash](std::hash::Hash).
|
||||
//!
|
||||
//! [1]: interned::Interned
|
||||
|
||||
pub mod interned {
|
||||
//! An [Interned] reference asserts its wrapped value has referential equality.
|
||||
use super::string_interner::StringInterner;
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
hash::Hash,
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
/// An [Interned] value is one that is *referentially comparable*.
|
||||
/// That is, the interned value is unique in memory, simplifying
|
||||
/// its equality and hashing implementation.
|
||||
///
|
||||
/// Comparing [Interned] values via [PartialOrd] or [Ord] will still
|
||||
/// dereference to the wrapped pointers, and as such, may produce
|
||||
/// results inconsistent with [PartialEq] or [Eq].
|
||||
#[repr(transparent)]
|
||||
pub struct Interned<'a, T: ?Sized> {
|
||||
value: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Interned<'a, T> {
|
||||
/// Gets the internal value as a pointer
|
||||
pub fn as_ptr(interned: &Self) -> *const T {
|
||||
interned.value
|
||||
}
|
||||
|
||||
/// Gets the internal value as a reference with the interner's lifetime
|
||||
pub fn to_ref(&self) -> &'a T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Interned<'a, T> {
|
||||
pub(super) fn new(value: &'a T) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Copy for Interned<'_, T> {}
|
||||
|
||||
impl<T: ?Sized> Clone for Interned<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Debug> Debug for Interned<'_, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("~")?;
|
||||
self.value.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for Interned<'_, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> PartialEq for Interned<'_, T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
std::ptr::eq(self.value, other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Eq for Interned<'_, T> {}
|
||||
|
||||
impl<T: ?Sized> Hash for Interned<'_, T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
Self::as_ptr(self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Display> Display for Interned<'_, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.value.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<str> for Interned<'a, str> {
|
||||
fn as_ref(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Interned<'static, str> {
|
||||
/// Types which implement [`AsRef<str>`] will be stored in the global [StringInterner]
|
||||
fn from(value: &str) -> Self {
|
||||
from_str(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn from_str(value: &str) -> Interned<'static, str> {
|
||||
let global_interner = StringInterner::global();
|
||||
global_interner.get_or_insert(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod string_interner {
|
||||
//! A [StringInterner] hands out [Interned] copies of each unique string given to it.
|
||||
|
||||
use super::interned::Interned;
|
||||
use cl_arena::dropless_arena::DroplessArena;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
sync::{OnceLock, RwLock},
|
||||
};
|
||||
|
||||
/// A string interner hands out [Interned] copies of each unique string given to it.
|
||||
#[derive(Default)]
|
||||
pub struct StringInterner<'a> {
|
||||
arena: DroplessArena<'a>,
|
||||
keys: RwLock<HashSet<&'a str>>,
|
||||
}
|
||||
|
||||
impl StringInterner<'static> {
|
||||
/// Gets a reference to a global string interner whose [Interned] strings are `'static`
|
||||
pub fn global() -> &'static Self {
|
||||
static GLOBAL_INTERNER: OnceLock<StringInterner<'static>> = OnceLock::new();
|
||||
|
||||
// SAFETY: The RwLock within the interner's `keys` protects the arena
|
||||
// from being modified concurrently.
|
||||
GLOBAL_INTERNER.get_or_init(|| StringInterner {
|
||||
arena: DroplessArena::new(),
|
||||
keys: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StringInterner<'a> {
|
||||
/// Creates a new [StringInterner] backed by the provided [DroplessArena]
|
||||
pub fn new(arena: DroplessArena<'a>) -> Self {
|
||||
Self { arena, keys: RwLock::new(HashSet::new()) }
|
||||
}
|
||||
|
||||
/// Returns an [Interned] copy of the given string,
|
||||
/// allocating a new one if it doesn't already exist.
|
||||
///
|
||||
/// # Blocks
|
||||
/// This function blocks when the interner is held by another thread.
|
||||
pub fn get_or_insert(&'a self, value: &str) -> Interned<'a, str> {
|
||||
let Self { arena, keys } = self;
|
||||
|
||||
// Safety: Holding this write guard for the entire duration of this
|
||||
// function enforces a safety invariant. See StringInterner::global.
|
||||
let mut keys = keys.write().expect("should not be poisoned");
|
||||
|
||||
Interned::new(match keys.get(value) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
let value = match value {
|
||||
"" => "", // Arena will panic if passed an empty string
|
||||
_ => arena.alloc_str(value),
|
||||
};
|
||||
keys.insert(value);
|
||||
value
|
||||
}
|
||||
})
|
||||
}
|
||||
/// Gets a reference to the interned copy of the given value, if it exists
|
||||
/// # Blocks
|
||||
/// This function blocks when the interner is held by another thread.
|
||||
pub fn get(&'a self, value: &str) -> Option<Interned<'a, str>> {
|
||||
let keys = self.keys.read().expect("should not be poisoned");
|
||||
keys.get(value).copied().map(Interned::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for StringInterner<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Interner")
|
||||
.field("keys", &self.keys)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for StringInterner<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Ok(keys) = self.keys.read() else {
|
||||
return write!(f, "Could not lock StringInterner key map.");
|
||||
};
|
||||
let mut keys: Vec<_> = keys.iter().collect();
|
||||
keys.sort();
|
||||
|
||||
writeln!(f, "Keys:")?;
|
||||
for (idx, key) in keys.iter().enumerate() {
|
||||
writeln!(f, "{idx}:\t\"{key}\"")?
|
||||
}
|
||||
writeln!(f, "Count: {}", keys.len())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// # Safety:
|
||||
// This is fine because StringInterner::get_or_insert(v) holds a RwLock
|
||||
// for its entire duration, and doesn't touch the non-(Send+Sync) arena
|
||||
// unless the lock is held by a write guard.
|
||||
unsafe impl Send for StringInterner<'_> {}
|
||||
unsafe impl Sync for StringInterner<'_> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::StringInterner;
|
||||
|
||||
macro_rules! ptr_eq {
|
||||
($a: expr, $b: expr $(, $($t:tt)*)?) => {
|
||||
assert_eq!(std::ptr::addr_of!($a), std::ptr::addr_of!($b) $(, $($t)*)?)
|
||||
};
|
||||
}
|
||||
macro_rules! ptr_ne {
|
||||
($a: expr, $b: expr $(, $($t:tt)*)?) => {
|
||||
assert_ne!(std::ptr::addr_of!($a), std::ptr::addr_of!($b) $(, $($t)*)?)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empties_is_unique() {
|
||||
let interner = StringInterner::global();
|
||||
let empty = interner.get_or_insert("");
|
||||
let empty2 = interner.get_or_insert("");
|
||||
ptr_eq!(*empty, *empty2);
|
||||
}
|
||||
#[test]
|
||||
fn non_empty_is_unique() {
|
||||
let interner = StringInterner::global();
|
||||
let nonempty1 = interner.get_or_insert("not empty!");
|
||||
let nonempty2 = interner.get_or_insert("not empty!");
|
||||
let different = interner.get_or_insert("different!");
|
||||
ptr_eq!(*nonempty1, *nonempty2);
|
||||
ptr_ne!(*nonempty1, *different);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod dropless_interner {
|
||||
//! A [DroplessInterner] hands out [Interned] references for arbitrary types.
|
||||
//!
|
||||
//! Note: It is a *logic error* to modify the returned reference via interior mutability
|
||||
//! in a way that changes the values produced by [Eq] and [Hash].
|
||||
//!
|
||||
//! See the standard library [HashSet] for more details.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use doughlang::intern::dropless_interner::DroplessInterner;
|
||||
//! use cl_arena::dropless_arena::DroplessArena;
|
||||
//!
|
||||
//! let da = DroplessArena::new();
|
||||
//! let di: DroplessInterner<'_, ()> = DroplessInterner::new(da);
|
||||
//! let unit1 = di.get_or_insert(());
|
||||
//! let unit2 = di.get_or_insert(());
|
||||
//! let unit3 = di.get(&()).unwrap();
|
||||
//! assert_eq!(unit1, unit3);
|
||||
//! assert_eq!(unit2, unit1);
|
||||
//! assert_eq!(unit3, unit2);
|
||||
//! ```
|
||||
//!
|
||||
//! ```rust
|
||||
//! use doughlang::intern::dropless_interner::DroplessInterner;
|
||||
//! use cl_arena::dropless_arena::DroplessArena;
|
||||
//!
|
||||
//! let da = DroplessArena::new();
|
||||
//! let di: DroplessInterner<'_, [i32; 10]> = DroplessInterner::new(da);
|
||||
//! let arr1 = di.get_or_insert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
//! let arr2 = di.get_or_insert([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
//! let arr3 = di.get(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).unwrap();
|
||||
//! assert_eq!(arr1, arr3);
|
||||
//! assert_eq!(arr2, arr1);
|
||||
//! assert_eq!(arr3, arr2);
|
||||
//!
|
||||
//! let arr4 = di.get_or_insert([10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
|
||||
//! assert_ne!(arr1, arr4);
|
||||
//! ```
|
||||
use super::interned::Interned;
|
||||
use cl_arena::dropless_arena::DroplessArena;
|
||||
use std::{collections::HashSet, hash::Hash, sync::RwLock};
|
||||
|
||||
/// A [DroplessInterner] hands out [Interned] references for arbitrary types.
|
||||
///
|
||||
/// See the [module-level documentation](self) for more information.
|
||||
pub struct DroplessInterner<'a, T: Eq + Hash> {
|
||||
arena: DroplessArena<'a>,
|
||||
keys: RwLock<HashSet<&'a T>>,
|
||||
}
|
||||
|
||||
impl<'a, T: Eq + Hash> Default for DroplessInterner<'a, T> {
|
||||
fn default() -> Self {
|
||||
Self { arena: Default::default(), keys: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Eq + Hash> DroplessInterner<'a, T> {
|
||||
/// Creates a new [DroplessInterner] backed by the provided [TypedArena]
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if T [needs drop](std::mem::needs_drop)
|
||||
pub fn new(arena: DroplessArena<'a>) -> Self {
|
||||
Self { arena, keys: RwLock::new(HashSet::new()) }
|
||||
}
|
||||
|
||||
/// Converts the given value into an [Interned] value.
|
||||
///
|
||||
/// # Blocks
|
||||
/// This function blocks when the interner is held by another thread.
|
||||
pub fn get_or_insert(&'a self, value: T) -> Interned<'a, T> {
|
||||
let Self { arena, keys } = self;
|
||||
|
||||
// Safety: Locking the keyset for the entire duration of this function
|
||||
// enforces a safety invariant when the interner is stored in a global.
|
||||
let mut keys = keys.write().expect("should not be poisoned");
|
||||
|
||||
Interned::new(match keys.get(&value) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
let value = if std::mem::size_of::<T>() == 0 {
|
||||
Box::leak(Box::new(value))
|
||||
} else {
|
||||
arena.alloc(value)
|
||||
};
|
||||
keys.insert(value);
|
||||
value
|
||||
}
|
||||
})
|
||||
}
|
||||
/// Returns the [Interned] copy of the given value, if one already exists
|
||||
///
|
||||
/// # Blocks
|
||||
/// This function blocks when the interner is being written to by another thread.
|
||||
pub fn get(&self, value: &T) -> Option<Interned<'a, T>> {
|
||||
let keys = self.keys.read().expect("should not be poisoned");
|
||||
keys.get(value).copied().map(Interned::new)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This should be safe because references yielded by
|
||||
/// [get_or_insert](DroplessInterner::get_or_insert) are unique, and the function uses
|
||||
/// the [RwLock] around the [HashSet] to ensure mutual exclusion
|
||||
unsafe impl<'a, T: Eq + Hash + Send> Send for DroplessInterner<'a, T> where &'a T: Send {}
|
||||
unsafe impl<T: Eq + Hash + Send + Sync> Sync for DroplessInterner<'_, T> {}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
pub mod fmt;
|
||||
|
||||
pub mod intern;
|
||||
|
||||
pub mod span;
|
||||
|
||||
pub mod token;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! The parser takes a stream of [`Token`]s from the [`Lexer`], and turns them into [`crate::ast`]
|
||||
//! nodes.
|
||||
use crate::{
|
||||
ast::*,
|
||||
ast::{types::{Literal, Path}, *},
|
||||
lexer::{LexError, LexFailure, Lexer},
|
||||
span::Span,
|
||||
token::{Lexeme, TKind, Token},
|
||||
@@ -217,7 +217,12 @@ impl<'t> Parse<'t> for Path {
|
||||
parts.push("".into()); // the "root"
|
||||
}
|
||||
while let Ok(id) = p.next_if(TKind::Identifier)? {
|
||||
parts.push(id.lexeme.string().expect("Identifier should have String"));
|
||||
parts.push(
|
||||
id.lexeme
|
||||
.str()
|
||||
.expect("Identifier should have String")
|
||||
.into(),
|
||||
);
|
||||
if let None | Some(Err(_)) = p.next_if(TKind::ColonColon).allow_eof()? {
|
||||
break;
|
||||
}
|
||||
@@ -254,7 +259,7 @@ impl<'t> Parse<'t> for Use {
|
||||
Ok(match tok.kind {
|
||||
TKind::Star => p.then(Use::Glob),
|
||||
TKind::Identifier => {
|
||||
let name = tok.lexeme.string().expect("should have String");
|
||||
let name = tok.lexeme.str().expect("should have String").into();
|
||||
match p.peek().map(Token::kind).allow_eof()? {
|
||||
Some(TKind::ColonColon) => Use::Path(name, p.consume().parse(())?),
|
||||
Some(TKind::As) => Use::Alias(
|
||||
@@ -263,8 +268,9 @@ impl<'t> Parse<'t> for Use {
|
||||
.next_if(TKind::Identifier)?
|
||||
.map_err(|e| ParseError::Expected(TKind::Identifier, e, p.span()))?
|
||||
.lexeme
|
||||
.string()
|
||||
.expect("Identifier should have string"),
|
||||
.str()
|
||||
.expect("Identifier should have string")
|
||||
.into(),
|
||||
),
|
||||
_ => Use::Name(name),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{PResult, PResultExt, Parse, ParseError, Parser, no_eof, pat::Prec as PPrec};
|
||||
use crate::{
|
||||
ast::*,
|
||||
ast::{types::Literal, *},
|
||||
token::{TKind, Token},
|
||||
};
|
||||
use std::iter;
|
||||
@@ -223,14 +223,14 @@ impl<'t> Parse<'t> for Expr {
|
||||
Ps::End => Err(ParseError::NotPrefix(kind, span))?,
|
||||
|
||||
Ps::Id => Expr::Id(p.parse(())?),
|
||||
Ps::Mid => Expr::MetId(p.consume().next()?.lexeme.to_string()),
|
||||
Ps::Mid => Expr::MetId(p.consume().next()?.lexeme.to_string().as_str().into()),
|
||||
Ps::Lit => Expr::Lit(p.parse(())?),
|
||||
Ps::Use => Expr::Use(p.consume().parse(())?),
|
||||
Ps::Def => Expr::Bind(p.parse(None)?),
|
||||
Ps::Doc => {
|
||||
let comment = Literal::Str(p.take_lexeme()?.string().unwrap());
|
||||
let comment = Expr::Lit(comment).anno(span);
|
||||
let next = p.parse(level)?;
|
||||
let next = p.parse(level.max(Prec::Do.next()))?;
|
||||
Expr::Op(Op::Meta, vec![comment, next])
|
||||
}
|
||||
Ps::For => parse_for(p, ())?,
|
||||
@@ -262,7 +262,7 @@ impl<'t> Parse<'t> for Expr {
|
||||
.expect(TKind::LBrack)?
|
||||
.opt(MIN, TKind::RBrack)?
|
||||
.unwrap_or_else(|| Expr::Op(Op::Tuple, vec![]).anno(span)),
|
||||
p.parse(level)?,
|
||||
p.parse(level.max(Prec::Do.next()))?,
|
||||
],
|
||||
),
|
||||
Ps::Op(Op::Block) => Expr::Op(
|
||||
@@ -525,7 +525,10 @@ impl<'t> Parse<'t> for MakeArm {
|
||||
.next_if(TKind::Identifier)?
|
||||
.map_err(|tk| ParseError::Expected(TKind::Identifier, tk, p.span()))?;
|
||||
Ok(MakeArm(
|
||||
name.lexeme.string().expect("Identifier should have String"),
|
||||
name.lexeme
|
||||
.str()
|
||||
.expect("Identifier should have String")
|
||||
.into(),
|
||||
p.opt_if(Prec::Body.value(), TKind::Colon)?,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
|
||||
use crate::{
|
||||
ast::*,
|
||||
ast::{types::Path, *},
|
||||
token::{TKind, Token},
|
||||
};
|
||||
|
||||
@@ -128,7 +128,7 @@ impl<'t> Parse<'t> for Pat {
|
||||
Prefix::Consume => p.consume().parse(level)?,
|
||||
Prefix::Underscore => p.consume().then(Pat::Ignore),
|
||||
Prefix::Never => p.consume().then(Pat::Never),
|
||||
Prefix::MetId => Pat::MetId(p.consume().next()?.lexeme.to_string()),
|
||||
Prefix::MetId => Pat::MetId(p.consume().next()?.lexeme.to_string().as_str().into()),
|
||||
Prefix::Constant => Pat::Value(p.parse(ExPrec::Unary.value())?),
|
||||
Prefix::Array => parse_array_pat(p)?,
|
||||
Prefix::Id => {
|
||||
|
||||
Reference in New Issue
Block a user