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.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.1"
|
version = "2.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.1"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
@@ -45,6 +45,7 @@ dependencies = [
|
|||||||
name = "doughlang"
|
name = "doughlang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cl-arena",
|
||||||
"repline",
|
"repline",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -61,9 +62,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.174"
|
version = "0.2.179"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
@@ -79,19 +80,18 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.13"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.4"
|
version = "0.12.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
@@ -99,22 +99,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.11"
|
version = "0.9.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.17"
|
version = "0.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
@@ -130,9 +130,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
@@ -155,9 +155,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
@@ -173,67 +173,3 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"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]
|
[dependencies]
|
||||||
repline = "*"
|
repline = "*"
|
||||||
|
cl-arena = "*"
|
||||||
unicode-ident = "1.0.12"
|
unicode-ident = "1.0.12"
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ use std::{convert::Infallible, error::Error};
|
|||||||
use doughlang::{
|
use doughlang::{
|
||||||
ast::{
|
ast::{
|
||||||
fold::{Fold, Foldable},
|
fold::{Fold, Foldable},
|
||||||
|
types::{Literal, Path, Symbol},
|
||||||
visit::{Visit, Walk},
|
visit::{Visit, Walk},
|
||||||
*,
|
*,
|
||||||
},
|
},
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
parser::{Parser, expr::Prec},
|
parser::{Parser, expr::Prec},
|
||||||
|
span::Span,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CODE: &str = r#"
|
const CODE: &str = r#"
|
||||||
@@ -86,6 +88,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
struct CountNodes {
|
struct CountNodes {
|
||||||
exprs: usize,
|
exprs: usize,
|
||||||
|
macro_ids: usize,
|
||||||
idents: usize,
|
idents: usize,
|
||||||
paths: usize,
|
paths: usize,
|
||||||
literals: 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;
|
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;
|
self.exprs += 1;
|
||||||
expr.children(self)
|
expr.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> {
|
fn visit_pat(&mut self, item: &'a Pat<A>) -> 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> {
|
|
||||||
self.patterns += 1;
|
self.patterns += 1;
|
||||||
item.children(self)
|
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;
|
self.binds += 1;
|
||||||
item.children(self)
|
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;
|
self.makes += 1;
|
||||||
item.children(self)
|
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;
|
self.makearms += 1;
|
||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
@@ -153,10 +160,26 @@ impl<'a> Visit<'a> for CountNodes {
|
|||||||
|
|
||||||
struct Sevenfold;
|
struct Sevenfold;
|
||||||
|
|
||||||
impl<A: Annotation> Fold<A> for Sevenfold {
|
impl Fold<DefaultTypes> for Sevenfold {
|
||||||
type Error = ();
|
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> {
|
fn fold_literal(&mut self, _lit: Literal) -> Result<Literal, Self::Error> {
|
||||||
Ok(Literal::Int(7, 10))
|
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::{
|
use doughlang::{
|
||||||
ast::{
|
ast::{
|
||||||
visit::{Visit, Walk},
|
types::{Literal, Path}, visit::{Visit, Walk}, *
|
||||||
*,
|
|
||||||
},
|
},
|
||||||
|
intern::interned::Interned,
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
parser::{Parser, expr::Prec},
|
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 = ();
|
type Error = ();
|
||||||
|
|
||||||
fn visit(&mut self, walk: &'a impl Walk<'a>) -> Result<(), Self::Error> {
|
fn visit_expr(&mut self, expr: &'a Expr) -> Result<(), Self::Error> {
|
||||||
walk.visit_in(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Omitted => print!("()"),
|
Expr::Omitted => print!("()"),
|
||||||
Expr::Id(path) => path.visit_in(self)?,
|
Expr::Id(path) => self.visit_path(path)?,
|
||||||
Expr::MetId(id) => print!("`{id}"),
|
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::Use(import) => import.visit_in(self)?,
|
||||||
Expr::Bind(bind) => bind.visit_in(self)?,
|
Expr::Bind(bind) => bind.visit_in(self)?,
|
||||||
Expr::Make(make) => make.visit_in(self)?,
|
Expr::Make(make) => make.visit_in(self)?,
|
||||||
@@ -155,7 +151,12 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
Ok(())
|
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}");
|
print!("{name}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -165,7 +166,7 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
print!("(at");
|
print!("(at");
|
||||||
for part in parts {
|
for part in parts {
|
||||||
print!(" ");
|
print!(" ");
|
||||||
part.visit_in(self)?;
|
self.visit_symbol(part)?;
|
||||||
}
|
}
|
||||||
print!(")");
|
print!(")");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -179,7 +180,7 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||||
match item {
|
match item {
|
||||||
Use::Glob => print!("(use-glob)"),
|
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::Alias(name, alias) => print!("(use-alias {name} {alias})"),
|
||||||
Use::Path(name, tree) => {
|
Use::Path(name, tree) => {
|
||||||
print!("(use-path {name} ");
|
print!("(use-path {name} ");
|
||||||
@@ -198,7 +199,7 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
Ok(())
|
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 {
|
match item {
|
||||||
Pat::Ignore => print!("(ignore)"),
|
Pat::Ignore => print!("(ignore)"),
|
||||||
Pat::Never => print!("(never)"),
|
Pat::Never => print!("(never)"),
|
||||||
@@ -217,7 +218,7 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
Ok(())
|
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;
|
let Bind(op, generics, pattern, exprs) = item;
|
||||||
print!("({} ", self.bind_op(*op));
|
print!("({} ", self.bind_op(*op));
|
||||||
if !generics.is_empty() {
|
if !generics.is_empty() {
|
||||||
@@ -236,11 +237,11 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
Ok(())
|
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)
|
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)
|
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;
|
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);
|
self.size += size_of_val(item);
|
||||||
item.children(self)
|
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);
|
self.size += size_of_val(item);
|
||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_path(&mut self, item: &'a Path) -> Result<(), Self::Error> {
|
fn visit_pat(&mut self, item: &'a Pat<A>) -> 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> {
|
|
||||||
self.size += size_of_val(item);
|
self.size += size_of_val(item);
|
||||||
item.children(self)
|
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);
|
self.size += size_of_val(item);
|
||||||
item.children(self)
|
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);
|
self.size += size_of_val(item);
|
||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
self.size += size_of_val(item);
|
self.size += size_of_val(item);
|
||||||
item.children(self)
|
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
|
//! The Abstract Syntax Tree defines an interface between the parser and type checker
|
||||||
|
|
||||||
use crate::span::Span;
|
use std::hash::Hash;
|
||||||
|
|
||||||
pub mod macro_matcher;
|
|
||||||
|
|
||||||
pub mod visit;
|
|
||||||
|
|
||||||
pub mod fold;
|
|
||||||
|
|
||||||
mod display;
|
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 {}
|
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 {}
|
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.
|
/// A value with an annotation.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[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.
|
/// 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
|
/// performing import resolution, as imports typically depend on the order
|
||||||
/// in which names are bound.
|
/// in which names are bound.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Expr<A: Annotation = Span> {
|
pub enum Expr<A: AstTypes = DefaultTypes> {
|
||||||
/// Omitted by semicolon insertion-elision rules
|
/// Omitted by semicolon insertion-elision rules
|
||||||
Omitted,
|
Omitted,
|
||||||
/// An identifier
|
/// An identifier
|
||||||
Id(Path),
|
Id(A::Path),
|
||||||
/// An escaped token for macro binding
|
/// An escaped token for macro binding
|
||||||
MetId(String),
|
MetId(A::MacroId),
|
||||||
/// A literal bool, string, char, or int
|
/// A literal bool, string, char, or int
|
||||||
Lit(Literal),
|
Lit(A::Literal),
|
||||||
/// use Use
|
/// use Use
|
||||||
Use(Use),
|
Use(Use<A>),
|
||||||
/// `let Pat::NoTopAlt (= expr (else expr)?)?` |
|
/// `let Pat::NoTopAlt (= expr (else expr)?)?` |
|
||||||
/// `(fn | mod | impl) Pat::Fn Expr`
|
/// `(fn | mod | impl) Pat::Fn Expr`
|
||||||
Bind(Box<Bind<A>>),
|
Bind(Box<Bind<A>>),
|
||||||
@@ -198,42 +231,48 @@ pub enum Op {
|
|||||||
OrSet,
|
OrSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A qualified identifier
|
impl<A: AstTypes> Expr<A> {
|
||||||
///
|
pub const fn anno(self, annotation: A::Annotation) -> Anno<Expr<A>, A> {
|
||||||
/// TODO: qualify identifier
|
Anno(self, annotation)
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
}
|
||||||
pub struct Path {
|
|
||||||
// TODO: Identifier interning
|
|
||||||
pub parts: Vec<String>,
|
|
||||||
// TODO: generic parameters
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A literal value (boolean, character, integer, string)
|
pub fn and_do(self, annotation: A::Annotation, other: Anno<Expr<A>, A>) -> Self {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
let Self::Op(Op::Do, mut exprs) = self else {
|
||||||
pub enum Literal {
|
return Self::Op(Op::Do, vec![self.anno(annotation), other]);
|
||||||
/// A boolean literal: true | false
|
};
|
||||||
Bool(bool),
|
let Anno(Self::Op(Op::Do, mut other), _) = other else {
|
||||||
/// A character literal: 'a', '\u{1f988}'
|
exprs.push(other);
|
||||||
Char(char),
|
return Self::Op(Op::Do, exprs);
|
||||||
/// An integer literal: 0, 123, 0x10
|
};
|
||||||
Int(u128, u32),
|
exprs.append(&mut other);
|
||||||
/// A string literal:
|
Self::Op(Op::Do, exprs)
|
||||||
Str(String),
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// A compound import declaration
|
pub fn to_tuple(self, annotation: A::Annotation) -> Self {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
match self {
|
||||||
pub enum Use {
|
Self::Op(Op::Tuple, _) => self,
|
||||||
/// "*"
|
_ => Self::Op(Op::Tuple, vec![self.anno(annotation)]),
|
||||||
Glob,
|
}
|
||||||
/// Identifier
|
}
|
||||||
Name(String),
|
|
||||||
/// Identifier as Identifier
|
pub const fn is_place(&self) -> bool {
|
||||||
Alias(String, String),
|
matches!(
|
||||||
/// Identifier :: Use
|
self,
|
||||||
Path(String, Box<Use>),
|
Self::Id(_) | Self::Op(Op::Index | Op::Dot | Op::Deref, _)
|
||||||
/// { Use, * }
|
)
|
||||||
Tree(Vec<Use>),
|
}
|
||||||
|
|
||||||
|
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
|
/// A pattern binding
|
||||||
@@ -249,9 +288,9 @@ pub enum Use {
|
|||||||
/// Pat => Expr // in match
|
/// Pat => Expr // in match
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Bind<A: Annotation = Span>(
|
pub struct Bind<A: AstTypes = DefaultTypes>(
|
||||||
pub BindOp,
|
pub BindOp,
|
||||||
pub Vec<Path>,
|
pub Vec<A::Path>,
|
||||||
pub Pat<A>,
|
pub Pat<A>,
|
||||||
pub Vec<Anno<Expr<A>, A>>,
|
pub Vec<Anno<Expr<A>, A>>,
|
||||||
);
|
);
|
||||||
@@ -277,34 +316,19 @@ pub enum BindOp {
|
|||||||
/// A `Pat => Expr` binding
|
/// A `Pat => Expr` binding
|
||||||
Match,
|
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.
|
/// 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)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Pat<A: Annotation = Span> {
|
pub enum Pat<A: AstTypes = DefaultTypes> {
|
||||||
/// Matches anything without binding
|
/// Matches anything without binding
|
||||||
Ignore,
|
Ignore,
|
||||||
/// Matches nothing, ever
|
/// Matches nothing, ever
|
||||||
Never,
|
Never,
|
||||||
/// Matches nothing; used for macro substitution
|
/// Matches nothing; used for macro substitution
|
||||||
MetId(String),
|
MetId(A::MacroId),
|
||||||
/// Matches anything, and binds it to a name
|
/// Matches anything, and binds it to a name
|
||||||
Name(String),
|
Name(A::Symbol),
|
||||||
/// Matches a value by equality comparison
|
/// Matches a value by equality comparison
|
||||||
Value(Box<Anno<Expr<A>, A>>),
|
Value(Box<Anno<Expr<A>, A>>),
|
||||||
/// Matches a compound pattern
|
/// Matches a compound pattern
|
||||||
@@ -348,64 +372,7 @@ pub enum PatOp {
|
|||||||
Alt,
|
Alt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Annotation, A: Annotation> std::fmt::Debug for Anno<T, A> {
|
impl<A: AstTypes> Pat<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> {
|
|
||||||
pub fn to_tuple(self) -> Self {
|
pub fn to_tuple(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Op(PatOp::Tuple, _) => self,
|
Self::Op(PatOp::Tuple, _) => self,
|
||||||
@@ -414,8 +381,31 @@ impl<A: Annotation> Pat<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Path {
|
/// A compound import declaration
|
||||||
fn from(value: &str) -> Self {
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
Self { parts: vec![value.to_owned()] }
|
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 crate::fmt::FmtAdapter;
|
||||||
use std::{fmt::Display, format_args as fmt};
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Omitted => "/* omitted */".fmt(f),
|
Self::Omitted => "/* omitted */".fmt(f),
|
||||||
@@ -135,28 +135,7 @@ impl Display for Op {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Path {
|
impl<A: AstTypes> Display for Use<A> {
|
||||||
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 {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Glob => "*".fmt(f),
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Self(op, gens, pat, exprs) = self;
|
let Self(op, gens, pat, exprs) = self;
|
||||||
op.fmt(f)?;
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Self(expr, make_arms) = self;
|
let Self(expr, make_arms) = self;
|
||||||
f.delimit(fmt!("({expr} {{"), "})").list(make_arms, ", ")
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self(name, Some(body)) => write!(f, "{name}: {body}"),
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Ignore => "_".fmt(f),
|
Self::Ignore => "_".fmt(f),
|
||||||
|
|||||||
269
src/ast/fold.rs
269
src/ast/fold.rs
@@ -2,207 +2,201 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
#![allow(clippy::wildcard_imports, clippy::missing_errors_doc)]
|
#![allow(clippy::wildcard_imports, clippy::missing_errors_doc)]
|
||||||
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Deconstructs an entire AST, and reconstructs it from parts.
|
/// Deconstructs an entire AST, and reconstructs it from parts.
|
||||||
///
|
///
|
||||||
/// Each function acts as a customization point.
|
/// 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.
|
/// which provides double dispatch.
|
||||||
pub trait Fold<A: Annotation> {
|
pub trait Fold<From: AstTypes, To: AstTypes = From> {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
fn fold_annotation(&mut self, anno: A) -> Result<A, Self::Error> {
|
/// Consumes an Annotation in A, possibly transforms it, and produces a replacement Annotation
|
||||||
Ok(anno)
|
/// 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`]
|
/// Consumes a `MacroId` in A, possibly transforms it, and produces a replacement `MacroId` in B
|
||||||
fn fold_expr(&mut self, expr: Expr<A>) -> Result<Expr<A>, Self::Error> {
|
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)
|
expr.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a Symbol, possibly transforms it, and produces a replacement Symbol
|
/// Consumes an [`Expr`], possibly transforms it, and produces a replacement [`Expr`]
|
||||||
fn fold_ident(&mut self, name: String) -> Result<String, Self::Error> {
|
fn fold_expr(&mut self, expr: Expr<From>) -> Result<Expr<To>, Self::Error> {
|
||||||
name.children(self) // TODO: ^ this should be a symbol
|
expr.children(self)
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 a [`Use`], possibly transforms it, and produces a replacement [`Use`]
|
/// 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)
|
item.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a [`Pat`], possibly transforms it, and produces a replacement [`Pat`]
|
/// 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)
|
pat.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a [`Bind`], possibly transforms it, and produces a replacement [`Bind`]
|
/// 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)
|
bind.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a [`Make`], possibly transforms it, and produces a replacement [`Make`]
|
/// 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)
|
make.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a [`MakeArm`], possibly transforms it, and produces a replacement [`MakeArm`]
|
/// 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)
|
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)
|
/// 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,
|
/// Destructures `self`, calling [`Foldable::fold_in`] on all foldable members,
|
||||||
/// and rebuilds a `Self` out of the results.
|
/// and rebuilds a `Self` out of the results.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
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(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Foldable<A> for Expr<A> {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Expr<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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)
|
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 {
|
Ok(match self {
|
||||||
Self::Omitted => Self::Omitted,
|
Self::Omitted => Expr::Omitted,
|
||||||
Self::Id(path) => Self::Id(path.fold_in(folder)?),
|
Self::Id(path) => Expr::Id(folder.fold_path(path)?),
|
||||||
Self::MetId(id) => Self::MetId(id.fold_in(folder)?),
|
Self::MetId(id) => Expr::MetId(folder.fold_macro_id(id)?),
|
||||||
Self::Lit(literal) => Self::Lit(literal.fold_in(folder)?),
|
Self::Lit(lit) => Expr::Lit(folder.fold_literal(lit)?),
|
||||||
Self::Use(item) => Self::Use(item.fold_in(folder)?),
|
Self::Use(item) => Expr::Use(item.fold_in(folder)?),
|
||||||
Self::Bind(bind) => Self::Bind(bind.fold_in(folder)?),
|
Self::Bind(bind) => Expr::Bind(bind.fold_in(folder)?),
|
||||||
Self::Make(make) => Self::Make(make.fold_in(folder)?),
|
Self::Make(make) => Expr::Make(make.fold_in(folder)?),
|
||||||
Self::Op(op, annos) => Self::Op(op, annos.fold_in(folder)?),
|
Self::Op(op, annos) => Expr::Op(op, annos.fold_in(folder)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Foldable<A> for String {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Use<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
type Out = Use<B>;
|
||||||
folder.fold_ident(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Annotation> Foldable<A> for Path {
|
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||||
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> {
|
|
||||||
folder.fold_use(self)
|
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 {
|
Ok(match self {
|
||||||
Self::Glob => Self::Glob,
|
Self::Glob => Use::Glob,
|
||||||
Self::Name(name) => Self::Name(name),
|
Self::Name(name) => Use::Name(folder.fold_symbol(name)?),
|
||||||
Self::Alias(name, alias) => Self::Alias(name, alias),
|
Self::Alias(name, alias) => {
|
||||||
Self::Path(name, rest) => Self::Path(name, rest.fold_in(folder)?),
|
Use::Alias(folder.fold_symbol(name)?, folder.fold_symbol(alias)?)
|
||||||
Self::Tree(items) => Self::Tree(items.fold_in(folder)?),
|
}
|
||||||
|
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> {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Pat<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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)
|
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 {
|
Ok(match self {
|
||||||
Self::Ignore => Self::Ignore,
|
Self::Ignore => Pat::Ignore,
|
||||||
Self::Never => Self::Never,
|
Self::Never => Pat::Never,
|
||||||
Self::MetId(name) => Self::MetId(name.fold_in(folder)?),
|
Self::MetId(name) => Pat::MetId(folder.fold_macro_id(name)?),
|
||||||
Self::Name(name) => Self::Name(name.fold_in(folder)?),
|
Self::Name(name) => Pat::Name(folder.fold_symbol(name)?),
|
||||||
Self::Value(expr) => Self::Value(expr.fold_in(folder)?),
|
Self::Value(expr) => Pat::Value(expr.fold_in(folder)?),
|
||||||
Self::Op(op, pats) => Self::Op(op, pats.fold_in(folder)?),
|
Self::Op(op, pats) => Pat::Op(op, pats.fold_in(folder)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Foldable<A> for Bind<A> {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Bind<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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)
|
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;
|
let Self(op, gens, pat, exprs) = self;
|
||||||
Ok(Self(
|
Ok(Bind(
|
||||||
op,
|
op,
|
||||||
gens.fold_in(folder)?,
|
gens.into_iter()
|
||||||
|
.map(|g| folder.fold_path(g))
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
pat.fold_in(folder)?,
|
pat.fold_in(folder)?,
|
||||||
exprs.fold_in(folder)?,
|
exprs.fold_in(folder)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Foldable<A> for Make<A> {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Make<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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)
|
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;
|
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> {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for MakeArm<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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)
|
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;
|
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> {
|
impl<A: AstTypes, B: AstTypes> Foldable<A, B> for Anno<Expr<A>, A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
type Out = Anno<Expr<B>, B>;
|
||||||
let Self(expr, anno) = self;
|
|
||||||
let anno = folder.fold_annotation(anno)?;
|
fn fold_in<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||||
Ok(Self(expr.fold_in(folder)?, anno))
|
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;
|
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 //
|
// GENERIC IMPLEMENTATIONS ON COLLECTIONS //
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
/// Maps the value in the box across `f()` without deallocating
|
// 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> {
|
// 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.
|
// // TODO: replace with Box::take when it stabilizes.
|
||||||
let rawbox = Box::into_raw(boxed);
|
// let rawbox = Box::into_raw(boxed);
|
||||||
|
|
||||||
// Safety: `rawbox` came from a Box, so it is aligned and initialized.
|
// // Safety: `rawbox` came from a Box, so it is aligned and initialized.
|
||||||
// To prevent further reuse and deallocate on failure, rawbox is
|
// // To prevent further reuse and deallocate on failure, rawbox is
|
||||||
// shadowed by a Box<MaybeUninit<T>>.
|
// // shadowed by a Box<MaybeUninit<T>>.
|
||||||
// Safety: MaybeUninit<T> has the same size and alignment as T.
|
// // Safety: MaybeUninit<T> has the same size and alignment as T.
|
||||||
let (value, rawbox) = (unsafe { rawbox.read() }, unsafe {
|
// let (value, rawbox) = (unsafe { rawbox.read() }, unsafe {
|
||||||
Box::from_raw(rawbox.cast::<MaybeUninit<T>>())
|
// Box::from_raw(rawbox.cast::<MaybeUninit<T>>())
|
||||||
});
|
// });
|
||||||
|
|
||||||
// rawbox is reinitialized with f(value)
|
// // rawbox is reinitialized with f(value)
|
||||||
Ok(Box::write(rawbox, f(value)?))
|
// Ok(Box::write(rawbox, f(value)?))
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<T: Foldable<A>, A: Annotation> Foldable<A> for Box<T> {
|
impl<T, A, B> Foldable<A, B> for Box<T>
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
where
|
||||||
box_try_map(self, |t| t.fold_in(folder))
|
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> {
|
fn children<F: Fold<A, B> + ?Sized>(self, folder: &mut F) -> Result<Self::Out, F::Error> {
|
||||||
box_try_map(self, |t| t.children(folder))
|
let value = *self;
|
||||||
|
Ok(Box::new(value.children(folder)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Foldable<A>, A: Annotation> Foldable<A> for Vec<T> {
|
impl<T: Foldable<A, B>, A: AstTypes, B: AstTypes> Foldable<A, B> for Vec<T> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
type Out = Vec<T::Out>;
|
||||||
self.into_iter().map(|e| e.fold_in(folder)).collect()
|
|
||||||
|
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?
|
// TODO: is this correct for generic data structures?
|
||||||
self.into_iter().map(|e| e.fold_in(folder)).collect()
|
self.into_iter().map(|e| e.fold_in(folder)).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Foldable<A>, A: Annotation> Foldable<A> for Option<T> {
|
impl<T: Foldable<A, B>, A: AstTypes, B: AstTypes> Foldable<A, B> for Option<T> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
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 {
|
Ok(match self {
|
||||||
Self::Some(value) => Some(value.fold_in(folder)?),
|
Self::Some(value) => Some(value.fold_in(folder)?),
|
||||||
Self::None => None,
|
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 {
|
Ok(match self {
|
||||||
Self::Some(value) => Some(value.children(folder)?),
|
Self::Some(value) => Some(value.children(folder)?),
|
||||||
Self::None => None,
|
Self::None => None,
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
/// Stores a substitution from meta-identifiers to values
|
/// Stores a substitution from meta-identifiers to values
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Subst<A: Annotation> {
|
pub struct Subst<A: AstTypes> {
|
||||||
pub exp: HashMap<String, Expr<A>>,
|
pub exp: HashMap<A::MacroId, Expr<A>>,
|
||||||
pub pat: HashMap<String, Pat<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 {
|
fn default() -> Self {
|
||||||
Self { exp: Default::default(), pat: Default::default() }
|
Self { exp: Default::default(), pat: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<A: Annotation> Subst<A> {
|
impl<A: AstTypes> Subst<A> {
|
||||||
fn add_pat(&mut self, name: String, pat: &Pat<A>) -> bool {
|
fn add_pat(&mut self, name: A::MacroId, pat: &Pat<A>) -> bool {
|
||||||
if self.exp.contains_key(&name) {
|
if self.exp.contains_key(&name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ impl<A: Annotation> Subst<A> {
|
|||||||
}
|
}
|
||||||
self.pat.insert(name, pat.clone()).is_none()
|
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) {
|
if self.pat.contains_key(&name) {
|
||||||
return false;
|
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`
|
/// Applies a substitution rule from `pat` to `template` on `self`
|
||||||
fn apply_rule(&mut self, pat: &Self, template: &Self) -> bool
|
fn apply_rule(&mut self, pat: &Self, template: &Self) -> bool
|
||||||
where Self: Sized + Clone {
|
where Self: Sized + Clone {
|
||||||
let Some(sub) = self.construct(pat) else {
|
let Some(sub) = self.match_with(pat) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,19 +56,14 @@ pub trait Match<A: Annotation> {
|
|||||||
/// Implements recursive Subst-building for Self
|
/// Implements recursive Subst-building for Self
|
||||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool;
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool;
|
||||||
|
|
||||||
/// Constructs a Subst
|
/// Matches self against the provided pattern
|
||||||
fn construct(&self, pat: &Self) -> Option<Subst<A>> {
|
fn match_with(&self, pat: &Self) -> Option<Subst<A>> {
|
||||||
let mut sub = Subst::default();
|
let mut sub = Subst::default();
|
||||||
Match::recurse(&mut sub, pat, self).then_some(sub)
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
Match::recurse(sub, &pat.0, &expr.0)
|
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 {
|
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)) =
|
let (Self(pat_kind, _, pat_pat, pat_expr), Self(expr_kind, _, expr_pat, expr_expr)) =
|
||||||
(pat, 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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
let (Make(pat, pat_arms), Make(expr, expr_arms)) = (pat, expr);
|
let (Make(pat, pat_arms), Make(expr, expr_arms)) = (pat, expr);
|
||||||
Match::recurse(sub, pat, expr) && Match::recurse(sub, pat_arms, expr_arms)
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
match (pat, expr) {
|
match (pat, expr) {
|
||||||
(Expr::Omitted, Expr::Omitted) => true,
|
(Expr::Omitted, Expr::Omitted) => true,
|
||||||
(Expr::Omitted, _) => false,
|
(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::MetId(name), _) => sub.add_expr(name.clone(), expr),
|
||||||
(Expr::Id(pat), Expr::Id(expr)) => pat == expr,
|
(Expr::Id(pat), Expr::Id(expr)) => pat == expr,
|
||||||
(Expr::Id(_), _) => false,
|
(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.
|
// TODO: order-independent matching for MakeArm specifically.
|
||||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
pat.0 == expr.0 && Match::recurse(sub, &pat.1, &expr.1)
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
match (pat, expr) {
|
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::MetId(name), _) => sub.add_pat(name.clone(), expr),
|
||||||
(Pat::Ignore, Pat::Ignore) => true,
|
(Pat::Ignore, Pat::Ignore) => true,
|
||||||
(Pat::Ignore, _) => false,
|
(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 {
|
fn recurse(_: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
pat == expr
|
pat == expr
|
||||||
}
|
}
|
||||||
@@ -201,7 +196,7 @@ impl<A: Annotation> Match<A> for Op {
|
|||||||
fn apply(&mut self, _sub: &Subst<A>) {}
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
if pat.len() != expr.len() {
|
if pat.len() != expr.len() {
|
||||||
return false;
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
Match::recurse(sub, pat.as_ref(), expr.as_ref())
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
Match::recurse(sub, pat.as_slice(), expr.as_slice())
|
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 {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
match (pat, expr) {
|
match (pat, expr) {
|
||||||
(Some(pat), Some(expr)) => Match::recurse(sub, 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)]
|
#![allow(clippy::wildcard_imports, clippy::missing_errors_doc)]
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub trait Visit<'a> {
|
pub trait Visit<'a, A: AstTypes> {
|
||||||
type Error;
|
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)
|
walk.visit_in(self)
|
||||||
}
|
}
|
||||||
fn visit_expr<A: Annotation>(&mut self, expr: &'a Expr<A>) -> Result<(), Self::Error> {
|
fn visit_literal(&mut self, lit: &'a A::Literal) -> Result<(), Self::Error> {
|
||||||
expr.children(self)
|
let _ = lit;
|
||||||
}
|
|
||||||
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> {
|
|
||||||
Ok(())
|
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> {
|
pub trait Walk<'a, A: AstTypes> {
|
||||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
#[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 {
|
match self {
|
||||||
Self::Omitted => Ok(()),
|
Self::Omitted => Ok(()),
|
||||||
Self::Id(path) => path.visit_in(v),
|
Self::Id(path) => v.visit_path(path),
|
||||||
Self::MetId(id) => id.visit_in(v),
|
Self::MetId(id) => v.visit_macro_id(id),
|
||||||
Self::Lit(literal) => literal.visit_in(v),
|
Self::Lit(lit) => v.visit_literal(lit),
|
||||||
Self::Use(u) => u.visit_in(v),
|
Self::Use(u) => u.visit_in(v),
|
||||||
Self::Bind(bind) => bind.visit_in(v),
|
Self::Bind(bind) => bind.visit_in(v),
|
||||||
Self::Make(make) => make.visit_in(v),
|
Self::Make(make) => make.visit_in(v),
|
||||||
@@ -61,40 +68,22 @@ impl<'a, A: Annotation> Walk<'a> for Expr<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
v.visit_expr(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Walk<'a> for str {
|
impl<'a, A: AstTypes> Walk<'a, A> for Use<A> {
|
||||||
fn visit_in<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> {
|
||||||
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> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Glob => Ok(()),
|
Self::Glob => Ok(()),
|
||||||
Self::Name(name) => name.visit_in(v),
|
Self::Name(name) => v.visit_symbol(name),
|
||||||
Self::Alias(name, alias) => {
|
Self::Alias(name, alias) => {
|
||||||
name.visit_in(v)?;
|
v.visit_symbol(name)?;
|
||||||
alias.visit_in(v)
|
v.visit_symbol(alias)
|
||||||
}
|
}
|
||||||
Self::Path(name, rest) => {
|
Self::Path(name, rest) => {
|
||||||
name.visit_in(v)?;
|
v.visit_symbol(name)?;
|
||||||
rest.visit_in(v)
|
rest.visit_in(v)
|
||||||
}
|
}
|
||||||
Self::Tree(items) => items.visit_in(v),
|
Self::Tree(items) => items.visit_in(v),
|
||||||
@@ -102,76 +91,76 @@ impl<'a> Walk<'a> for Use {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
v.visit_use(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, A: Annotation> Walk<'a> for Pat<A> {
|
impl<'a, A: AstTypes> Walk<'a, A> for Pat<A> {
|
||||||
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> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ignore | Self::Never => Ok(()),
|
Self::Ignore | Self::Never => Ok(()),
|
||||||
Self::MetId(id) => id.visit_in(v),
|
Self::MetId(id) => v.visit_macro_id(id),
|
||||||
Self::Name(name) => name.visit_in(v),
|
Self::Name(name) => v.visit_symbol(name),
|
||||||
Self::Value(literal) => literal.visit_in(v),
|
Self::Value(literal) => literal.visit_in(v),
|
||||||
Self::Op(_, pats) => pats.visit_in(v),
|
Self::Op(_, pats) => pats.visit_in(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
v.visit_pat(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, A: Annotation> Walk<'a> for Bind<A> {
|
impl<'a, A: AstTypes> Walk<'a, A> for Bind<A> {
|
||||||
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> {
|
||||||
let Self(_kind, gens, pat, exprs) = self;
|
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)?;
|
pat.visit_in(v)?;
|
||||||
exprs.visit_in(v)
|
exprs.visit_in(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
v.visit_bind(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, A: Annotation> Walk<'a> for Make<A> {
|
impl<'a, A: AstTypes> Walk<'a, A> for Make<A> {
|
||||||
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> {
|
||||||
let Self(expr, arms) = self;
|
let Self(expr, arms) = self;
|
||||||
expr.visit_in(v)?;
|
expr.visit_in(v)?;
|
||||||
arms.visit_in(v)
|
arms.visit_in(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
v.visit_make(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, A: Annotation> Walk<'a> for MakeArm<A> {
|
impl<'a, A: AstTypes> Walk<'a, A> for MakeArm<A> {
|
||||||
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> {
|
||||||
let Self(name, expr) = self;
|
let Self(name, expr) = self;
|
||||||
name.visit_in(v)?;
|
v.visit_symbol(name)?;
|
||||||
expr.visit_in(v)
|
expr.visit_in(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
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]
|
#[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)
|
self.0.children(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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)
|
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 //
|
// GENERIC IMPLEMENTATIONS ON COLLECTIONS //
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
|
|
||||||
impl<'a, T: Walk<'a>> Walk<'a> for [T] {
|
impl<'a, T: Walk<'a, A>, A: AstTypes> Walk<'a, A> for [T] {
|
||||||
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> {
|
||||||
for item in self {
|
for item in self {
|
||||||
item.visit_in(v)?;
|
item.visit_in(v)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
for item in self {
|
||||||
item.visit_in(v)?;
|
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> {
|
impl<'a, T: Walk<'a, A>, A: AstTypes> Walk<'a, A> for Vec<T> {
|
||||||
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.as_slice().children(v)
|
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)
|
self.as_slice().visit_in(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Walk<'a>> Walk<'a> for Option<T> {
|
impl<'a, T: Walk<'a, A>, A: AstTypes> Walk<'a, A> for Option<T> {
|
||||||
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> {
|
||||||
match self {
|
match self {
|
||||||
Some(t) => t.children(v),
|
Some(t) => t.children(v),
|
||||||
_ => Ok(()),
|
_ => 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 {
|
match self {
|
||||||
Some(t) => t.visit_in(v),
|
Some(t) => t.visit_in(v),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Tests the lexer\
|
//! Tests the lexer\
|
||||||
use doughlang::{
|
use doughlang::{
|
||||||
ast::{
|
ast::{
|
||||||
Anno, Annotation, Bind, Expr, Literal, Pat, Path, Use,
|
Anno, Annotation, Bind, Expr, Pat, Use,
|
||||||
macro_matcher::{Match, Subst},
|
macro_matcher::{Match, Subst},
|
||||||
},
|
},
|
||||||
lexer::{EOF, LexError, Lexer},
|
lexer::{EOF, LexError, Lexer},
|
||||||
@@ -44,8 +44,6 @@ enum ParseMode {
|
|||||||
Expr,
|
Expr,
|
||||||
Pat,
|
Pat,
|
||||||
Bind,
|
Bind,
|
||||||
Path,
|
|
||||||
Literal,
|
|
||||||
Use,
|
Use,
|
||||||
Tokens,
|
Tokens,
|
||||||
}
|
}
|
||||||
@@ -55,8 +53,6 @@ impl From<&str> for ParseMode {
|
|||||||
"expr" => Self::Expr,
|
"expr" => Self::Expr,
|
||||||
"pat" => Self::Pat,
|
"pat" => Self::Pat,
|
||||||
"bind" => Self::Bind,
|
"bind" => Self::Bind,
|
||||||
"path" => Self::Path,
|
|
||||||
"literal" => Self::Literal,
|
|
||||||
"use" => Self::Use,
|
"use" => Self::Use,
|
||||||
"tokens" => Self::Tokens,
|
"tokens" => Self::Tokens,
|
||||||
_ => Default::default(),
|
_ => Default::default(),
|
||||||
@@ -69,8 +65,6 @@ impl ParseMode {
|
|||||||
Self::Expr => parse::<'a, Expr>,
|
Self::Expr => parse::<'a, Expr>,
|
||||||
Self::Pat => parse::<'a, Pat>,
|
Self::Pat => parse::<'a, Pat>,
|
||||||
Self::Bind => parse::<'a, Bind>,
|
Self::Bind => parse::<'a, Bind>,
|
||||||
Self::Path => parse::<'a, Path>,
|
|
||||||
Self::Literal => parse::<'a, Literal>,
|
|
||||||
Self::Use => parse::<'a, Use>,
|
Self::Use => parse::<'a, Use>,
|
||||||
Self::Tokens => tokens::<'a, dyn Parse<'a, Prec = ()>>,
|
Self::Tokens => tokens::<'a, dyn Parse<'a, Prec = ()>>,
|
||||||
}
|
}
|
||||||
@@ -86,7 +80,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
"" => Ok(Response::Continue),
|
"" => Ok(Response::Continue),
|
||||||
"exit" => Ok(Response::Break),
|
"exit" => Ok(Response::Break),
|
||||||
"help" => {
|
"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)");
|
println!("Verbose: {verbose:?} (pretty, debug, quiet)");
|
||||||
Ok(Response::Deny)
|
Ok(Response::Deny)
|
||||||
}
|
}
|
||||||
@@ -100,7 +94,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
Ok(Response::Accept)
|
Ok(Response::Accept)
|
||||||
}
|
}
|
||||||
line @ ("tokens" | "expr" | "pat" | "bind" | "path" | "literal" | "use") => {
|
line @ ("tokens" | "expr" | "pat" | "bind" | "use") => {
|
||||||
parsing = ParseMode::from(line);
|
parsing = ParseMode::from(line);
|
||||||
println!("Parse mode set to '{parsing:?}'");
|
println!("Parse mode set to '{parsing:?}'");
|
||||||
Ok(Response::Accept)
|
Ok(Response::Accept)
|
||||||
@@ -144,7 +138,7 @@ fn subst() -> Result<(), Box<dyn Error>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if p.next_if(TKind::Arrow).is_err() {
|
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}");
|
println!("Match failed: {exp} <- {pat}");
|
||||||
continue;
|
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) {
|
fn parse<'t, T: Parse<'t> + Annotation>(document: &'t str, verbose: Verbosity) {
|
||||||
let mut parser = Parser::new(Lexer::new(document));
|
let mut parser = Parser::new(Lexer::new(document));
|
||||||
for idx in 0.. {
|
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 _ => {
|
(Err(e @ ParseError::EOF(s)), _) if s.tail == document.len() as _ => {
|
||||||
println!(
|
println!(
|
||||||
"\x1b[92m{e} (total {} byte{}, {idx} expression{})\x1b[0m",
|
"\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 fmt;
|
||||||
|
|
||||||
|
pub mod intern;
|
||||||
|
|
||||||
pub mod span;
|
pub mod span;
|
||||||
|
|
||||||
pub mod token;
|
pub mod token;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! The parser takes a stream of [`Token`]s from the [`Lexer`], and turns them into [`crate::ast`]
|
//! The parser takes a stream of [`Token`]s from the [`Lexer`], and turns them into [`crate::ast`]
|
||||||
//! nodes.
|
//! nodes.
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::*,
|
ast::{types::{Literal, Path}, *},
|
||||||
lexer::{LexError, LexFailure, Lexer},
|
lexer::{LexError, LexFailure, Lexer},
|
||||||
span::Span,
|
span::Span,
|
||||||
token::{Lexeme, TKind, Token},
|
token::{Lexeme, TKind, Token},
|
||||||
@@ -217,7 +217,12 @@ impl<'t> Parse<'t> for Path {
|
|||||||
parts.push("".into()); // the "root"
|
parts.push("".into()); // the "root"
|
||||||
}
|
}
|
||||||
while let Ok(id) = p.next_if(TKind::Identifier)? {
|
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()? {
|
if let None | Some(Err(_)) = p.next_if(TKind::ColonColon).allow_eof()? {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -254,7 +259,7 @@ impl<'t> Parse<'t> for Use {
|
|||||||
Ok(match tok.kind {
|
Ok(match tok.kind {
|
||||||
TKind::Star => p.then(Use::Glob),
|
TKind::Star => p.then(Use::Glob),
|
||||||
TKind::Identifier => {
|
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()? {
|
match p.peek().map(Token::kind).allow_eof()? {
|
||||||
Some(TKind::ColonColon) => Use::Path(name, p.consume().parse(())?),
|
Some(TKind::ColonColon) => Use::Path(name, p.consume().parse(())?),
|
||||||
Some(TKind::As) => Use::Alias(
|
Some(TKind::As) => Use::Alias(
|
||||||
@@ -263,8 +268,9 @@ impl<'t> Parse<'t> for Use {
|
|||||||
.next_if(TKind::Identifier)?
|
.next_if(TKind::Identifier)?
|
||||||
.map_err(|e| ParseError::Expected(TKind::Identifier, e, p.span()))?
|
.map_err(|e| ParseError::Expected(TKind::Identifier, e, p.span()))?
|
||||||
.lexeme
|
.lexeme
|
||||||
.string()
|
.str()
|
||||||
.expect("Identifier should have string"),
|
.expect("Identifier should have string")
|
||||||
|
.into(),
|
||||||
),
|
),
|
||||||
_ => Use::Name(name),
|
_ => Use::Name(name),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{PResult, PResultExt, Parse, ParseError, Parser, no_eof, pat::Prec as PPrec};
|
use super::{PResult, PResultExt, Parse, ParseError, Parser, no_eof, pat::Prec as PPrec};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::*,
|
ast::{types::Literal, *},
|
||||||
token::{TKind, Token},
|
token::{TKind, Token},
|
||||||
};
|
};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@@ -223,14 +223,14 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
Ps::End => Err(ParseError::NotPrefix(kind, span))?,
|
Ps::End => Err(ParseError::NotPrefix(kind, span))?,
|
||||||
|
|
||||||
Ps::Id => Expr::Id(p.parse(())?),
|
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::Lit => Expr::Lit(p.parse(())?),
|
||||||
Ps::Use => Expr::Use(p.consume().parse(())?),
|
Ps::Use => Expr::Use(p.consume().parse(())?),
|
||||||
Ps::Def => Expr::Bind(p.parse(None)?),
|
Ps::Def => Expr::Bind(p.parse(None)?),
|
||||||
Ps::Doc => {
|
Ps::Doc => {
|
||||||
let comment = Literal::Str(p.take_lexeme()?.string().unwrap());
|
let comment = Literal::Str(p.take_lexeme()?.string().unwrap());
|
||||||
let comment = Expr::Lit(comment).anno(span);
|
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])
|
Expr::Op(Op::Meta, vec![comment, next])
|
||||||
}
|
}
|
||||||
Ps::For => parse_for(p, ())?,
|
Ps::For => parse_for(p, ())?,
|
||||||
@@ -262,7 +262,7 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
.expect(TKind::LBrack)?
|
.expect(TKind::LBrack)?
|
||||||
.opt(MIN, TKind::RBrack)?
|
.opt(MIN, TKind::RBrack)?
|
||||||
.unwrap_or_else(|| Expr::Op(Op::Tuple, vec![]).anno(span)),
|
.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(
|
Ps::Op(Op::Block) => Expr::Op(
|
||||||
@@ -525,7 +525,10 @@ impl<'t> Parse<'t> for MakeArm {
|
|||||||
.next_if(TKind::Identifier)?
|
.next_if(TKind::Identifier)?
|
||||||
.map_err(|tk| ParseError::Expected(TKind::Identifier, tk, p.span()))?;
|
.map_err(|tk| ParseError::Expected(TKind::Identifier, tk, p.span()))?;
|
||||||
Ok(MakeArm(
|
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)?,
|
p.opt_if(Prec::Body.value(), TKind::Colon)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
|
use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::*,
|
ast::{types::Path, *},
|
||||||
token::{TKind, Token},
|
token::{TKind, Token},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ impl<'t> Parse<'t> for Pat {
|
|||||||
Prefix::Consume => p.consume().parse(level)?,
|
Prefix::Consume => p.consume().parse(level)?,
|
||||||
Prefix::Underscore => p.consume().then(Pat::Ignore),
|
Prefix::Underscore => p.consume().then(Pat::Ignore),
|
||||||
Prefix::Never => p.consume().then(Pat::Never),
|
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::Constant => Pat::Value(p.parse(ExPrec::Unary.value())?),
|
||||||
Prefix::Array => parse_array_pat(p)?,
|
Prefix::Array => parse_array_pat(p)?,
|
||||||
Prefix::Id => {
|
Prefix::Id => {
|
||||||
|
|||||||
Reference in New Issue
Block a user